LCOV - code coverage report
Current view: top level - EnergyPlus - DaylightingManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 46.1 % 4598 2118
Test Date: 2025-05-22 16:09:37 Functions: 66.2 % 65 43

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <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            7 : 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            7 :     auto &dl = state.dataDayltg;
     175            7 :     auto &s_surf = state.dataSurface;
     176              : 
     177              :     // Total inside surface area, including windows
     178            7 :     Real64 AInsTot = 0.0;
     179              :     // Sum of products of inside surface area * vis reflectance
     180            7 :     Real64 ARHTOT = 0.0;
     181              : 
     182              :     // Area sum and area * reflectance sum for different orientations
     183            7 :     std::array<Real64, (int)FWC::Num> AR = {0.0, 0.0, 0.0};
     184            7 :     std::array<Real64, (int)FWC::Num> ARH = {0.0, 0.0, 0.0};
     185              :     // Loop over surfaces in the zone's enclosure
     186              : 
     187            7 :     auto &thisEnclosure = state.dataViewFactor->EnclSolInfo(enclNum);
     188           48 :     for (int ISurf : thisEnclosure.SurfacePtr) {
     189           41 :         auto const &surf = s_surf->Surface(ISurf);
     190              : 
     191           41 :         SurfaceClass IType = surf.Class;
     192              :         // Error if window has multiplier > 1 since this causes incorrect illuminance calc
     193           41 :         if (IType == SurfaceClass::Window && surf.Multiplier > 1.0) {
     194            0 :             if (thisEnclosure.TotalEnclosureDaylRefPoints > 0) {
     195            0 :                 ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
     196            0 :                 ShowContinueError(state, "...not allowed since it is in a zone or enclosure with daylighting.");
     197            0 :                 ShowFatalError(state, "Program terminates due to preceding conditions.");
     198              :             } else {
     199            0 :                 ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
     200            0 :                 ShowContinueError(state, "...an adjacent Zone has daylighting. Simulation cannot proceed.");
     201            0 :                 ShowFatalError(state, "Program terminates due to preceding conditions.");
     202              :             }
     203              :         }
     204           41 :         if (IType == SurfaceClass::Wall || IType == SurfaceClass::Floor || IType == SurfaceClass::Roof || IType == SurfaceClass::Window ||
     205              :             IType == SurfaceClass::Door) {
     206           41 :             Real64 AREA = surf.Area;
     207              :             // In following, FrameArea and DividerArea can be non-zero only for exterior windows
     208           41 :             AInsTot += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
     209           41 :                        s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
     210           41 :             ARHTOT +=
     211           41 :                 AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
     212           41 :                 s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
     213           41 :                 s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
     214              : 
     215           41 :             FWC fwc = FWC::Ceiling;                                     // Ceiling
     216           41 :             if (surf.Tilt > 10.0 && surf.Tilt < 170.0) fwc = FWC::Wall; // Wall
     217           41 :             if (surf.Tilt >= 170.0) fwc = FWC::Floor;                   // Floor
     218           41 :             AR[(int)fwc] += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
     219           41 :                             s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
     220           41 :             ARH[(int)fwc] +=
     221           41 :                 AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
     222           41 :                 s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
     223           41 :                 s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
     224              :         }
     225              :     }
     226              : 
     227              :     // Average inside surface reflectance of enclosure
     228            7 :     if (AInsTot <= 0.0) {
     229            0 :         ShowSevereError(state, format("DayltgAveInteriorReflectance: Total opaque surface area is <=0.0 in solar enclosure={}", thisEnclosure.Name));
     230            0 :         ShowFatalError(state, "Program terminates due to preceding conditions.");
     231              :     }
     232            7 :     dl->enclDaylight(enclNum).aveVisDiffReflect = ARHTOT / AInsTot;
     233              :     // Total inside surface area of enclosure
     234            7 :     dl->enclDaylight(enclNum).totInsSurfArea = AInsTot;
     235              :     // Average floor visible reflectance
     236            7 :     dl->enclDaylight(enclNum).floorVisRefl = ARH[iFWC_Ceiling] / (AR[iFWC_Ceiling] + 1.e-6);
     237              : 
     238           48 :     for (int ISurf : thisEnclosure.SurfacePtr) {
     239           41 :         auto const &surf = s_surf->Surface(ISurf);
     240           41 :         if (surf.Class != SurfaceClass::Wall && surf.Class != SurfaceClass::Floor && surf.Class != SurfaceClass::Roof) continue;
     241              : 
     242              :         // Remove this surface from the space inside surface area and area*reflectivity
     243              :         // The resulting areas are AP(ITILT). The resulting area*reflectivity is ARHP(ITILT).
     244              :         // Initialize gross area of surface (including subsurfaces)
     245           34 :         Real64 ATWL = surf.Area; // This is the surface area less subsurfaces
     246              :         // Area * reflectance for this surface, excluding attached windows and doors
     247           34 :         Real64 ARHTWL = surf.Area * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack;
     248              : 
     249           34 :         FWC fwc = (surf.Tilt > 45.0 && surf.Tilt < 135.0) ? FWC::Wall : ((surf.Tilt >= 135.0) ? FWC::Floor : FWC::Ceiling);
     250              : 
     251              :         // Loop over windows and doors on this wall
     252          256 :         for (int IWinDr : thisEnclosure.SurfacePtr) {
     253          222 :             auto const &surfWinDr = s_surf->Surface(IWinDr);
     254          222 :             if ((surfWinDr.Class != SurfaceClass::Window && surfWinDr.Class != SurfaceClass::Door) || surfWinDr.BaseSurf != ISurf) continue;
     255              : 
     256            7 :             ATWL += surfWinDr.Area + s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) +
     257            7 :                     s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr));
     258            7 :             ARHTWL +=
     259            7 :                 surfWinDr.Area * state.dataConstruction->Construct(surfWinDr.Construction).ReflectVisDiffBack +
     260            7 :                 s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(IWinDr)) +
     261            7 :                 s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(IWinDr));
     262              :         }
     263              : 
     264              :         std::array<Real64, (int)FWC::Num> AP;
     265              :         std::array<Real64, (int)FWC::Num> ARHP;
     266              :         // Inside surface area of floor, walls and ceilings, minus surface ISurf and its subsurfaces
     267          136 :         for (int iFWC = iFWC_Floor; iFWC < (int)FWC::Num; ++iFWC) {
     268          102 :             if (iFWC == (int)fwc) {
     269           34 :                 AP[iFWC] = AR[iFWC] - ATWL;
     270           34 :                 ARHP[iFWC] = ARH[iFWC] - ARHTWL;
     271              :             } else {
     272           68 :                 AP[iFWC] = AR[iFWC];
     273           68 :                 ARHP[iFWC] = ARH[iFWC];
     274              :             }
     275              :         }
     276           34 :         s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf = AP;
     277           34 :         s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf = ARHP;
     278              :     } // for (ISurf)
     279              : 
     280           48 :     for (int IWin : thisEnclosure.SurfacePtr) {
     281           41 :         auto const &surf = s_surf->Surface(IWin);
     282           41 :         if (surf.Class != SurfaceClass::Window) continue;
     283              : 
     284            7 :         auto &surfWin = s_surf->SurfaceWindow(IWin);
     285            7 :         auto const &zone = state.dataHeatBal->Zone(surf.Zone);
     286            7 :         int ISurf = surf.BaseSurf;
     287              : 
     288              :         // Ratio of floor-to-window-center height and average floor-to-ceiling height
     289            7 :         Real64 ETA = max(0.0, min(1.0, (surfWin.WinCenter.z - zone.OriginZ) * zone.FloorArea / zone.Volume));
     290              : 
     291            7 :         std::array<Real64, (int)FWC::Num> AP = s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf;
     292            7 :         std::array<Real64, (int)FWC::Num> ARHP = s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf;
     293              :         // Average reflectance seen by light moving up (RhoCeilingWall) and down (RhoFloorWall)
     294              :         // across horizontal plane through center of window
     295            7 :         surfWin.rhoCeilingWall = (ARHP[iFWC_Wall] * (1.0 - ETA) + ARHP[iFWC_Ceiling]) / (AP[iFWC_Wall] * (1.0 - ETA) + AP[iFWC_Ceiling] + 1.0e-5);
     296            7 :         surfWin.rhoFloorWall = (ARHP[iFWC_Wall] * ETA + ARHP[iFWC_Floor]) / (AP[iFWC_Wall] * ETA + AP[iFWC_Floor] + 1.e-9);
     297              : 
     298              :         // Angle factor for windows with diffusing shades. SurfaceWindow(IWin)%FractionUpgoing is
     299              :         // fraction of light from the shade that goes up toward ceiling and upper part of walls.
     300              :         // 1 - SurfaceWindow(IWin)%FractionUpgoing is fraction that goes down toward floor and lower part of walls.
     301            7 :         surfWin.fractionUpgoing = surf.Tilt / 180.0;
     302              : 
     303              :         // Daylighting shelf simplification:  All light goes up to the ceiling regardless of orientation of shelf
     304            7 :         if (s_surf->SurfDaylightingShelfInd(IWin) > 0) {
     305            0 :             if (state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf > 0) surfWin.fractionUpgoing = 1.0;
     306              :         }
     307              :     } // for (IWin)
     308            7 : } // DayltgAveInteriorReflectance()
     309              : 
     310         1839 : void CalcDayltgCoefficients(EnergyPlusData &state)
     311              : {
     312              : 
     313              :     // SUBROUTINE INFORMATION:
     314              :     //       AUTHOR         Fred Winkelmann
     315              :     //       DATE WRITTEN   July 1997
     316              :     //       MODIFIED       FW, Jan 2002: add variable slat angle blinds
     317              :     //                      FW, Mar 2002: add triangular windows
     318              :     //                      FW, Oct 2002: remove warning on window discretization relative to
     319              :     //                                    reference point distance to window plane
     320              :     //                      FW, Jan 2003: add between-glass shades and blinds
     321              :     //                      FW, Apr 2003: initialize shading type to 'NOSHADE' in window loop
     322              :     //                      PE, May 2003: add light pipes (tubular daylighting devices)
     323              :     //                      FW, Jul 2003: account for possible non-zero transmittance of
     324              :     //                                    shading surfaces (previously all shading surfaces were
     325              :     //                                    assumed to be opaque)
     326              :     //                      PE, Aug 2003: add daylighting shelves
     327              :     //                      FW, Sep 2003: write the bare-window overcast sky daylight factors to the eio file
     328              :     //                      FW, Nov 2003: add exterior beam and sky solar diffuse reflection from obstructions;
     329              :     //                                    add beam solar and sky solar reflection from ground with obstructions.
     330              :     //                      FW, Nov 2003: change expression for NDIVX, NDIVY (no. of window elements in X,Y) to
     331              :     //                                    round up to nearest integer rather than down
     332              :     //                      FW, Nov 2003: add specular reflection of beam solar from obstructions
     333              :     //                      RJH, Jan 2004: add alternative daylighting analysis using DElight
     334              :     //                                     All modifications demarked with RJH (Rob Hitchcock)
     335              :     //                      FW, Feb 2004: add daylighting through interior windows
     336              :     //                      FW, Apr 2004: add light well efficiency that multiplies glazing transmittance
     337              :     //                      FW, Apr 2004: add diffusing glazing
     338              :     //                      RJH, Jul 2004: add error handling for warnings/errors returned from DElight
     339              :     //                      LKL, Oct 2004: Separate "map" and "ref" point calculations -- move some input routines to
     340              :     //                                     separate routines.
     341              : 
     342              :     // PURPOSE OF THIS SUBROUTINE:
     343              :     // Calculates daylighting factors for later use in the time-step loop.
     344              : 
     345              :     // METHODOLOGY EMPLOYED:
     346              : 
     347              :     // For each combination of exterior window and reference point in a zone,
     348              :     // calculates daylighting factors (interior illuminance / exterior illuminance)
     349              :     // and glare factors for clear and overcast skies and for windows with and
     350              :     // without shading devices. These factors are calculated for each hourly
     351              :     // sun position for design days and for selected days throughout the year.
     352              : 
     353              :     // If a target zone has one or more interior windows, also calculates daylighting
     354              :     // factors for the target zone that are associated with exterior windows in adjacent
     355              :     // zones that share interior windows with the target zone.
     356              : 
     357              :     // The daylight illuminance at a reference point from a window is determined
     358              :     // by dividing the window into rectangular elements and calculating the illuminance
     359              :     // reaching the reference point directly from each element. The illumination
     360              :     // from an element can come from the sky or ground if the window is unshaded, or from
     361              :     // a shading device illuminated by solar radiation. Also considered are the
     362              :     // illuminance contribution from interreflection among the zone's interior surfaces
     363              :     // and sunlight striking the reference point.
     364              : 
     365              :     // In calculating sky-related interior illuminance and luminance quantities,
     366              :     // the sky luminance for the different sky types are determined from distributions
     367              :     // in which the zenith luminance is normalized to 1.0 cd/m2. Similarly, sun-related
     368              :     // illuminance and luminance quantities are based on beam normal solar illuminance
     369              :     // normalized to 1.0 lux.
     370              :     // The daylight and glare factors calculated in this subroutine are used in DayltgInteriorIllum
     371              :     // to get the daylight illuminance and glare at each time step.
     372              :     // Based on this information and user-input lighting setpoint and type of lighting
     373              :     // control system, DayltgElecLightingControl then determines how much the overhead electric lighting
     374              :     // can be reduced.
     375              : 
     376              :     // REFERENCES:
     377              :     // Based on DOE-2.1E subroutine DCOF.
     378              : 
     379         1839 :     auto &dl = state.dataDayltg;
     380         1839 :     auto &s_surf = state.dataSurface;
     381              : 
     382         1839 :     if (dl->CalcDayltghCoefficients_firstTime) {
     383          107 :         GetDaylightingParametersInput(state);
     384          107 :         CheckTDDsAndLightShelvesInDaylitZones(state);
     385          107 :         AssociateWindowShadingControlWithDaylighting(state);
     386          107 :         dl->CalcDayltghCoefficients_firstTime = false;
     387              :     } // End of check if firstTime
     388              : 
     389              :     // Find the total number of exterior windows associated with all Daylighting:Detailed enclosures.
     390              :     // An exterior window is associated with such a enclosure if (1) it is an exterior window in the enclosure, or
     391              :     // (2) it is an exterior window in an adjacent enclosure that shares an interior window with the enclosure.
     392              :     // Note that exterior windows in category (2) may be counted more than once if an adjacent enclosure
     393              :     // is adjacent to more than one daylit enclosure with which the adjacent enclosure shares interior windows.
     394              :     // If there are no interior windows in a building, than TotWindowsWithDayl is just the total number of
     395              :     // exterior windows in Daylighting:Detailed enclosures. Note that it is possible for a
     396              :     // Daylighting:Detailed enclosure to have zero exterior windows of its own, but it may have an interior
     397              :     // through which daylight passes from adjacent enclosures with exterior windows.
     398         1839 :     if ((int)dl->DaylRefPt.size() == 0) return;
     399         1358 :     if (state.dataGlobal->BeginSimFlag) {
     400            5 :         dl->TotWindowsWithDayl = 0;
     401           12 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     402            7 :             dl->TotWindowsWithDayl += dl->enclDaylight(enclNum).NumOfDayltgExtWins;
     403              :         }
     404              :     }
     405              : 
     406         1358 :     if (dl->TotWindowsWithDayl == 0) return;
     407              : 
     408              :     //-----------------------------------------!
     409              :     // Detailed daylighting factor calculation !
     410              :     //-----------------------------------------!
     411         1358 :     if (!state.dataSysVars->DetailedSolarTimestepIntegration && !state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) {
     412            3 :         if (state.dataGlobal->WarmupFlag) {
     413            1 :             DisplayString(state, "Calculating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
     414              :         } else {
     415            2 :             DisplayString(state, "Updating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
     416              :         }
     417              :     }
     418              : 
     419         1358 :     if (state.dataGlobal->BeginSimFlag) {
     420              : 
     421              :         // Find minimum solid angle subtended by an interior window in Daylighting:Detailed zones.
     422              :         // Used in calculating daylighting through interior windows.
     423            5 :         CalcMinIntWinSolidAngs(state);
     424              : 
     425              :         // Warning if detailed daylighting has been requested for a zone with no associated exterior windows.
     426           12 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     427            7 :             auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
     428            7 :             if (thisEnclDaylight.NumOfDayltgExtWins == 0 && thisEnclDaylight.TotalExtWindows == 0) {
     429            0 :                 for (int daylightCtrlNum : thisEnclDaylight.daylightControlIndexes) {
     430            0 :                     if (dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints > 0) {
     431            0 :                         ShowWarningError(
     432              :                             state,
     433            0 :                             format("Detailed daylighting will not be done for Daylighting:Controls={}", dl->daylightControl(daylightCtrlNum).Name));
     434            0 :                         ShowContinueError(state, "because it has no associated exterior windows.");
     435              :                     }
     436              :                 }
     437              :             }
     438              : 
     439              :             // Find area and reflectance quantities used in calculating inter-reflected illuminance.
     440              :             // TH 9/10/2009. Need to calculate for zones without daylighting controls (TotalDaylRefPoints = 0)
     441              :             // but with adjacent zones having daylighting controls.
     442            7 :             if ((thisEnclDaylight.NumOfDayltgExtWins > 0) || thisEnclDaylight.adjEnclHasDayltgCtrl) {
     443            7 :                 DayltgAveInteriorReflectance(state, enclNum);
     444              :             }
     445              :         }
     446              :     }
     447              : 
     448         1358 :     int numTDD = (int)state.dataDaylightingDevicesData->TDDPipe.size();
     449              :     // Zero daylighting factor arrays
     450         1358 :     if (numTDD > 0) {
     451            0 :         int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
     452            0 :         int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
     453            0 :         for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
     454            0 :             for (int iTDD = 1; iTDD <= numTDD; ++iTDD) {
     455            0 :                 dl->TDDTransVisBeam(iHr, iTDD) = 0.0;
     456            0 :                 dl->TDDFluxInc(iHr, iTDD) = Illums();
     457            0 :                 dl->TDDFluxTrans(iHr, iTDD) = Illums();
     458              :             }
     459              :         }
     460              :     }
     461              : 
     462         1358 :     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     463            4 :         if (state.dataGlobal->BeginDayFlag) {
     464              :             // Calculate hourly sun angles, clear sky zenith luminance, and exterior horizontal illuminance
     465            2 :             dl->sunAngles = SunAngles();
     466            2 :             dl->sunAnglesHr = {SunAngles()};
     467            2 :             dl->horIllum = {Illums()};
     468           50 :             for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
     469           48 :                 auto const &surfSunCosHr = s_surf->SurfSunCosHourly(IHR);
     470           48 :                 if (surfSunCosHr.z < DataEnvironment::SunIsUpValue)
     471           33 :                     continue; // Skip if sun is below horizon //Autodesk SurfSunCosHourly was uninitialized here
     472              : 
     473           15 :                 Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
     474           15 :                 Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
     475           15 :                 dl->sunAngles = dl->sunAnglesHr[IHR] = {phi, std::sin(phi), std::cos(phi), theta};
     476              : 
     477           15 :                 DayltgExtHorizIllum(state, dl->horIllum[IHR]);
     478              :             }
     479              :         }
     480              :     } else { // timestep integrated calculations
     481         1354 :         dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {SunAngles()};
     482         5416 :         dl->horIllum[state.dataGlobal->HourOfDay] = Illums();
     483         1354 :         auto const &surfSunCosHr = s_surf->SurfSunCosHourly(state.dataGlobal->HourOfDay);
     484         1354 :         if (!(surfSunCosHr.z < DataEnvironment::SunIsUpValue)) { // Skip if sun is below horizon
     485          536 :             Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
     486          536 :             Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
     487          536 :             dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {phi, std::sin(phi), std::cos(phi), theta};
     488          536 :             DayltgExtHorizIllum(state, dl->horIllum[state.dataGlobal->HourOfDay]);
     489              :         }
     490              :     }
     491              : 
     492         1358 :     CalcDayltgCoeffsRefMapPoints(state);
     493              : 
     494         1451 :     if (dl->doSkyReporting && (!state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) &&
     495           93 :         (dl->FirstTimeDaylFacCalc && dl->TotWindowsWithDayl > 0 &&
     496           93 :          (!state.dataSysVars->DetailedSolarTimestepIntegration || state.dataGlobal->HourOfDay == 12))) {
     497              :         // Write the bare-window four sky daylight factors at noon time to the eio file; this is done only
     498              :         // for first time that daylight factors are calculated and so is insensitive to possible variation
     499              :         // due to change in ground reflectance from month to month, or change in storm window status.
     500              :         static constexpr std::string_view Format_700("! <Sky Daylight Factors>, Sky Type, MonthAndDay, Daylighting Control Name, Enclosure Name, "
     501              :                                                      "Window Name, Reference Point, Daylight Factor\n");
     502            5 :         print(state.files.eio, Format_700);
     503           12 :         for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
     504            7 :             auto &thisDayltgCtrl = dl->daylightControl(controlNum);
     505            7 :             int enclNum = thisDayltgCtrl.enclIndex;
     506            7 :             auto &thisEnclDaylight = dl->enclDaylight(enclNum);
     507              : 
     508            7 :             if (thisEnclDaylight.NumOfDayltgExtWins == 0 || !thisEnclDaylight.hasSplitFluxDaylighting) continue;
     509           14 :             for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
     510            7 :                 int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
     511              :                 // For this report, do not include ext wins in zone adjacent to ZoneNum since the inter-reflected
     512              :                 // component will not be calculated for these windows until the time-step loop.
     513            7 :                 if (s_surf->Surface(windowSurfNum).SolarEnclIndex != enclNum) continue;
     514              :                 // Output for each reference point, for each sky. Group by sky type first
     515              : 
     516              :                 static constexpr std::array<std::string_view, (int)SkyType::Num> skyTypeStrings = {
     517              :                     "Clear Sky", "Clear Turbid Sky", "Intermediate Sky", "Overcast Sky"};
     518              : 
     519           35 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
     520           72 :                     for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
     521           44 :                         Real64 DaylFac = thisDayltgCtrl.daylFac[12](windowCounter, refPtNum)[iWinCover_Bare][iLum_Illum].sky[iSky];
     522           44 :                         print(state.files.eio,
     523              :                               " Sky Daylight Factors,{},{},{},{},{},{},{:.4R}\n",
     524           44 :                               skyTypeStrings[iSky],
     525           44 :                               state.dataEnvrn->CurMnDy,
     526           44 :                               thisDayltgCtrl.Name,
     527           44 :                               state.dataViewFactor->EnclSolInfo(thisDayltgCtrl.enclIndex).Name,
     528           44 :                               s_surf->Surface(windowSurfNum).Name,
     529           44 :                               dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
     530              :                               DaylFac);
     531              :                     } // for (refPtNum)
     532              :                 }     // for (iSky)
     533              :             }         // for (windowCounter)
     534              :         }             // for (controlNum)
     535            5 :         dl->FirstTimeDaylFacCalc = false;
     536            5 :         dl->doSkyReporting = false;
     537              :     } // if (detailedIntegration etc.)
     538              : 
     539              :     // Skip if no daylight windows
     540         1358 :     if (dl->TotWindowsWithDayl == 0) return;
     541              : 
     542              :     // Skip if no request of reporting
     543         1358 :     if ((!dl->DFSReportSizingDays) && (!dl->DFSReportAllShadowCalculationDays)) return;
     544              : 
     545              :     // Skip duplicate calls
     546            1 :     if (state.dataGlobal->KickOffSizing) return;
     547            1 :     if (state.dataGlobal->DoingSizing) return;
     548            1 :     if (state.dataGlobal->KickOffSimulation) return;
     549              : 
     550            1 :     if (dl->DFSReportSizingDays) {
     551            1 :         if (state.dataGlobal->DoWeathSim && state.dataGlobal->DoDesDaySim) {
     552            1 :             if (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) return;
     553              :         }
     554              :     }
     555              : 
     556            1 :     if (dl->DFSReportAllShadowCalculationDays) {
     557            0 :         if (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather) return;
     558              :     }
     559              : 
     560              :     // open a new file eplusout.dfs for saving the daylight factors
     561            1 :     if (dl->CreateDFSReportFile) {
     562            2 :         InputOutputFile &dfs = state.files.dfs.ensure_open(state, "CalcDayltgCoefficients", state.files.outputControl.dfs);
     563            1 :         print(dfs, "{}\n", "This file contains daylight factors for all exterior windows of daylight enclosures.");
     564            1 :         print(dfs, "{}\n", "MonthAndDay,Enclosure Name,Zone Name,Window Name,Window State");
     565            1 :         print(dfs,
     566              :               "{}\n",
     567              :               "Hour,Reference Point,Daylight Factor for Clear Sky,Daylight Factor for Clear Turbid Sky,"
     568              :               "Daylight Factor for Intermediate Sky,Daylight Factor for Overcast Sky");
     569            1 :         dl->CreateDFSReportFile = false;
     570              :     }
     571              : 
     572            3 :     for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
     573            2 :         auto &thisDayltgCtrl = dl->daylightControl(controlNum);
     574            2 :         int enclNum = thisDayltgCtrl.enclIndex;
     575            2 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
     576            2 :         if (thisEnclDaylight.NumOfDayltgExtWins == 0) continue;
     577              : 
     578            4 :         for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
     579            2 :             int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
     580            2 :             auto &surf = s_surf->Surface(windowSurfNum);
     581              :             // For this report, do not include ext wins in zone/enclosure adjacent to ZoneNum since the inter-reflected
     582              :             // component will not be calculated for these windows until the time-step loop.
     583            2 :             if (surf.SolarEnclIndex == enclNum) {
     584              : 
     585            2 :                 int numWinCover = surf.HasShadeControl ? (int)WinCover::Num : 1;
     586              : 
     587              :                 // loop over each slat angle
     588            4 :                 for (int iWinCover = 0; iWinCover < numWinCover; ++iWinCover) {
     589            2 :                     if (iWinCover == iWinCover_Bare) {
     590              :                         // base window without shades, screens, or blinds
     591            2 :                         print(state.files.dfs,
     592              :                               "{},{},{},{},Base Window\n",
     593            2 :                               state.dataEnvrn->CurMnDy,
     594            2 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     595            2 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     596            2 :                               s_surf->Surface(windowSurfNum).Name);
     597            0 :                     } else if (iWinCover == iWinCover_Shaded) {
     598              :                         // window shade or blind with fixed slat angle
     599            0 :                         print(state.files.dfs,
     600              :                               "{},{},{},{},Blind or Slat Applied\n",
     601            0 :                               state.dataEnvrn->CurMnDy,
     602            0 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     603            0 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     604            0 :                               s_surf->Surface(windowSurfNum).Name);
     605              :                     }
     606              : 
     607           50 :                     for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
     608              :                         // For each Daylight Reference Point
     609           48 :                         auto &daylFacHr = thisDayltgCtrl.daylFac[IHR];
     610          144 :                         for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
     611           96 :                             auto &illums = daylFacHr(windowCounter, refPtNum)[iWinCover][iLum_Illum];
     612              : 
     613              :                             // write daylight factors - 4 sky types for each daylight ref point
     614           96 :                             print(state.files.dfs,
     615              :                                   "{},{},{:.5R},{:.5R},{:.5R},{:.5R}\n",
     616              :                                   IHR,
     617           96 :                                   dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
     618           96 :                                   illums.sky[(int)SkyType::Clear],
     619           96 :                                   illums.sky[(int)SkyType::ClearTurbid],
     620           96 :                                   illums.sky[(int)SkyType::Intermediate],
     621           96 :                                   illums.sky[(int)SkyType::Overcast]);
     622              : 
     623              :                         } // for (refPtNum) Reference Point
     624              :                     }     // for (IHR) hour
     625              :                 }         // for (ISlatAngle) slat angle
     626              :             }             // if (SolarEnclIndex == enclNum)
     627              :         }                 // for (windowCounter) exterior windows in enclosure
     628              :     }                     // for (controlNum) daylighting control
     629              : } // CalcDayltgCoefficients()
     630              : 
     631         1358 : void CalcDayltgCoeffsRefMapPoints(EnergyPlusData &state)
     632              : {
     633              : 
     634              :     // SUBROUTINE INFORMATION:
     635              :     //       AUTHOR         Linda Lawrie
     636              :     //       DATE WRITTEN   October 2004
     637              :     //       MODIFIED       May 2006 (RR): added exterior window screens
     638              :     //                      April 2012 (LKL); change to allow multiple maps per zone
     639              : 
     640              :     // PURPOSE OF THIS SUBROUTINE:
     641              :     // This subroutine does the daylighting coefficient calculation for the
     642              :     // daylighting and illuminance map reference points.
     643         1358 :     auto &dl = state.dataDayltg;
     644         1358 :     auto const &s_surf = state.dataSurface;
     645              : 
     646         1358 :     if (dl->VeryFirstTime) {
     647              :         // make sure all necessary surfaces match to pipes
     648            5 :         bool ErrorsFound = false;
     649           12 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     650           14 :             for (int loopwin = 1; loopwin <= dl->enclDaylight(enclNum).NumOfDayltgExtWins; ++loopwin) {
     651            7 :                 int IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
     652            7 :                 if (s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Diffuser) continue;
     653              :                 // Look up the TDD:DOME object
     654            0 :                 int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
     655            0 :                 if (PipeNum == 0) {
     656            0 :                     ShowSevereError(
     657              :                         state,
     658            0 :                         format("GetTDDInput: Surface={}, TDD:Dome object does not reference a valid Diffuser object.", s_surf->Surface(IWin).Name));
     659            0 :                     ShowContinueError(state, "...needs DaylightingDevice:Tubular of same name as Surface.");
     660            0 :                     ErrorsFound = true;
     661              :                 }
     662              :             }
     663              :         }
     664              : 
     665            5 :         if (ErrorsFound) {
     666            0 :             ShowFatalError(state, "Not all TubularDaylightDome objects have corresponding DaylightingDevice:Tubular objects. Program terminates.");
     667              :         }
     668            5 :         dl->VeryFirstTime = false;
     669              :     }
     670              : 
     671              :     // Calc for daylighting reference points for daylighting controls that use SplitFlux method
     672         2718 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
     673         1360 :         if (dl->daylightControl(daylightCtrlNum).DaylightMethod != DaylightingMethod::SplitFlux) continue;
     674              :         // Skip enclosures with no exterior windows or in adjacent enclosure(s) with which an interior window is shared
     675         1360 :         if (dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex).NumOfDayltgExtWins == 0) continue;
     676         1360 :         CalcDayltgCoeffsRefPoints(state, daylightCtrlNum);
     677              :     }
     678         1358 :     if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
     679              :         // Calc for illuminance maps
     680         1351 :         if ((int)dl->illumMaps.size() > 0) {
     681            2 :             for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
     682            1 :                 int mapZoneNum = dl->illumMaps(MapNum).zoneIndex;
     683            1 :                 std::string name = format("Zone={}", state.dataHeatBal->Zone(mapZoneNum).Name);
     684            1 :                 int mapSpaceNum = dl->illumMaps(MapNum).spaceIndex;
     685            1 :                 if (mapSpaceNum > 0) {
     686            0 :                     name = format("Space={}", state.dataHeatBal->space(mapSpaceNum).Name);
     687              :                 }
     688            1 :                 if (state.dataGlobal->WarmupFlag) {
     689            1 :                     DisplayString(state, format("Calculating Daylighting Coefficients (Map Points), {}", name));
     690              :                 } else {
     691            0 :                     DisplayString(state, format("Updating Daylighting Coefficients (Map Points), {}", name));
     692              :                 }
     693            1 :                 CalcDayltgCoeffsMapPoints(state, MapNum);
     694            1 :             }
     695              :         }
     696              :     }
     697         1358 : } // CalcDayltgCoeffsRefMapPoints()
     698              : 
     699         1360 : void CalcDayltgCoeffsRefPoints(EnergyPlusData &state, int const daylightCtrlNum)
     700              : {
     701              : 
     702              :     // SUBROUTINE INFORMATION:
     703              :     //       AUTHOR         Linda Lawrie
     704              :     //       DATE WRITTEN   April 2012
     705              :     //       MODIFIED       November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
     706              : 
     707              :     // PURPOSE OF THIS SUBROUTINE:
     708              :     // Provides calculations for Daylighting Coefficients for daylighting reference points
     709         1360 :     auto &dl = state.dataDayltg;
     710         1360 :     auto const &s_surf = state.dataSurface;
     711              : 
     712              :     //  glare calculation (radians)
     713              :     int IConst;            // Construction counter
     714              :     int ICtrl;             // Window control counter
     715              :     int IWin;              // Window counter
     716              :     int IWin2;             // Secondary window counter (for TDD:DOME object, if exists)
     717              :     int InShelfSurf;       // Inside daylighting shelf surface number
     718              :     WinShadingType ShType; // Window shading type
     719              :     int BlNum;             // Window Blind Number
     720              :     int LSHCAL;            // Interior shade calculation flag: 0=not yet
     721              :     //  calculated, 1=already calculated
     722              :     int NWX;     // Number of window elements in x direction for dayltg calc
     723              :     int NWY;     // Number of window elements in y direction for dayltg calc
     724              :     int NWYlim;  // For triangle, largest NWY for a given IX
     725              :     Real64 COSB; // Cosine of angle between window outward normal and ray from
     726              :     //  reference point to window element
     727              :     Real64 PHRAY;  // Altitude of ray from reference point to window element (radians)
     728              :     Real64 THRAY;  // Azimuth of ray from reference point to window element (radians)
     729              :     Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
     730              :     Real64 TVISB;  // Visible transmittance of window for COSB angle of incidence (times light well
     731              :     //   efficiency, if appropriate)
     732              :     int ISunPos; // Sun position counter; used to avoid calculating various
     733              :     //  quantities that do not depend on sun position.
     734              :     Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
     735              :     // from reference point through a window element
     736              :     bool is_Rectangle;         // True if window is rectangular
     737              :     bool is_Triangle;          // True if window is triangular
     738              :     Real64 DWX;                // Horizontal dimension of window element (m)
     739              :     Real64 DWY;                // Vertical dimension of window element (m)
     740              :     Real64 DAXY;               // Area of window element
     741              :     Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
     742              :     ExtWinType extWinType;     // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
     743              :     int BRef;
     744              :     int ILB;
     745              :     bool hitIntObs;        // True iff interior obstruction hit
     746              :     bool hitExtObs;        // True iff ray from ref pt to ext win hits an exterior obstruction
     747              :     Real64 TVISIntWin;     // Visible transmittance of int win at COSBIntWin for light from ext win
     748              :     Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
     749              : 
     750         1360 :     Vector3<Real64> W2;
     751         1360 :     Vector3<Real64> W3;
     752         1360 :     Vector3<Real64> W21;
     753         1360 :     Vector3<Real64> W23;
     754         1360 :     Vector3<Real64> RREF2;
     755         1360 :     Vector3<Real64> RWIN;
     756         1360 :     Vector3<Real64> RWIN2;
     757         1360 :     Vector3<Real64> Ray;
     758         1360 :     Vector3<Real64> WNORM2;
     759         1360 :     Vector3<Real64> VIEWVC;
     760         1360 :     Vector3<Real64> U2;
     761         1360 :     Vector3<Real64> U21;
     762         1360 :     Vector3<Real64> U23;
     763         1360 :     Vector3<Real64> VIEWVC2;
     764              : 
     765              :     int WinEl; // Current window element
     766              : 
     767         1360 :     if (dl->refFirstTime && (dl->maxControlRefPoints > 0)) {
     768            5 :         dl->RefErrIndex.allocate(dl->maxControlRefPoints, s_surf->TotSurfaces);
     769            5 :         dl->RefErrIndex = 0;
     770            5 :         dl->refFirstTime = false;
     771              :     }
     772              : 
     773         1360 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
     774         1360 :     auto const &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
     775         1360 :     int zoneNum = thisDayltgCtrl.zoneIndex;
     776              :     // Azimuth of view vector in absolute coord sys
     777         1360 :     Real64 AZVIEW = (thisDayltgCtrl.ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth + state.dataHeatBal->BuildingAzimuth +
     778         1360 :                      state.dataHeatBal->BuildingRotationAppendixG) *
     779         1360 :                     Constant::DegToRad;
     780              :     // View vector components in absolute coord sys
     781         1360 :     VIEWVC = {std::sin(AZVIEW), std::cos(AZVIEW), 0.0};
     782              : 
     783         2725 :     for (auto &refPt : thisDayltgCtrl.refPts) {
     784         1365 :         refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
     785         1365 :         refPt.glareIndex = 0.0;       // Glare index at reference points
     786         2730 :         for (auto &extWin : refPt.extWins) {
     787         1365 :             extWin.solidAng = extWin.solidAngWtd = 0.0;
     788         1365 :             extWin.lums[iLum_Illum] = extWin.lums[iLum_Back] = extWin.lums[iLum_Source] = {0.0, 0.0};
     789              :         }
     790              :     }
     791              : 
     792         1360 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
     793         1360 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
     794         1360 :     int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
     795         1360 :     int numRefPts = thisDayltgCtrl.TotalDaylRefPoints;
     796              : 
     797         2858 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
     798         1498 :         auto &daylFacHr = thisDayltgCtrl.daylFac[iHr];
     799         2996 :         for (int iWin = 1; iWin <= numExtWins; ++iWin) {
     800         3116 :             for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
     801         4854 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
     802         3236 :                     auto &daylFac = daylFacHr(iWin, iRefPt)[iWinCover];
     803        12944 :                     daylFac[iLum_Illum] = Illums();
     804        12944 :                     daylFac[iLum_Source] = Illums();
     805        12944 :                     daylFac[iLum_Back] = Illums();
     806              :                 } // for (iSlatAng)
     807              :             }     // for (iRefPt)
     808              :         }         // for (iWin)
     809              :     }             // for (iHr)
     810              : 
     811         1360 :     BRef = 0;
     812              : 
     813         2725 :     for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
     814         1365 :         auto const &refPt = thisDayltgCtrl.refPts(IL);
     815              :         // Reference point in absolute coordinate system
     816         1365 :         Vector3<Real64> RREF = refPt.absCoords;
     817              : 
     818              :         //           -------------
     819              :         // ---------- WINDOW LOOP ----------
     820              :         //           -------------
     821         2730 :         for (int loopwin = 1; loopwin <= thisEnclDaylight.NumOfDayltgExtWins; ++loopwin) {
     822              : 
     823         1365 :             FigureDayltgCoeffsAtPointsSetupForWindow(state,
     824              :                                                      daylightCtrlNum,
     825              :                                                      IL,
     826              :                                                      loopwin,
     827              :                                                      CalledFor::RefPoint,
     828              :                                                      RREF,
     829              :                                                      VIEWVC,
     830              :                                                      IWin,
     831              :                                                      IWin2,
     832              :                                                      NWX,
     833              :                                                      NWY,
     834              :                                                      W2,
     835              :                                                      W3,
     836              :                                                      W21,
     837              :                                                      W23,
     838              :                                                      LSHCAL,
     839              :                                                      InShelfSurf,
     840              :                                                      ICtrl,
     841              :                                                      ShType,
     842              :                                                      BlNum,
     843              :                                                      WNORM2,
     844              :                                                      extWinType,
     845              :                                                      IConst,
     846              :                                                      RREF2,
     847              :                                                      DWX,
     848              :                                                      DWY,
     849              :                                                      DAXY,
     850              :                                                      U2,
     851              :                                                      U23,
     852              :                                                      U21,
     853              :                                                      VIEWVC2,
     854              :                                                      is_Rectangle,
     855              :                                                      is_Triangle);
     856              :             //           ---------------------
     857              :             // ---------- WINDOW ELEMENT LOOP ----------
     858              :             //           ---------------------
     859              : 
     860         1365 :             WinEl = 0;
     861              : 
     862        23128 :             for (int IX = 1; IX <= NWX; ++IX) {
     863        21763 :                 if (is_Rectangle) {
     864        21763 :                     NWYlim = NWY;
     865            0 :                 } else if (is_Triangle) {
     866            0 :                     NWYlim = NWY - IX + 1;
     867              :                 }
     868              : 
     869       368814 :                 for (int IY = 1; IY <= NWYlim; ++IY) {
     870              : 
     871       347051 :                     ++WinEl;
     872              : 
     873       347051 :                     FigureDayltgCoeffsAtPointsForWindowElements(state,
     874              :                                                                 daylightCtrlNum,
     875              :                                                                 IL,
     876              :                                                                 loopwin,
     877              :                                                                 CalledFor::RefPoint,
     878              :                                                                 WinEl,
     879              :                                                                 IWin,
     880              :                                                                 IWin2,
     881              :                                                                 IX,
     882              :                                                                 IY,
     883              :                                                                 SkyObstructionMult,
     884              :                                                                 W2,
     885              :                                                                 W21,
     886              :                                                                 W23,
     887              :                                                                 RREF,
     888              :                                                                 NWYlim,
     889              :                                                                 VIEWVC2,
     890              :                                                                 DWX,
     891              :                                                                 DWY,
     892              :                                                                 DAXY,
     893              :                                                                 U2,
     894              :                                                                 U23,
     895              :                                                                 U21,
     896              :                                                                 RWIN,
     897              :                                                                 RWIN2,
     898              :                                                                 Ray,
     899              :                                                                 PHRAY,
     900              :                                                                 LSHCAL,
     901              :                                                                 COSB,
     902              :                                                                 ObTrans,
     903              :                                                                 TVISB,
     904              :                                                                 DOMEGA,
     905              :                                                                 THRAY,
     906              :                                                                 hitIntObs,
     907              :                                                                 hitExtObs,
     908              :                                                                 WNORM2,
     909              :                                                                 extWinType,
     910              :                                                                 IConst,
     911              :                                                                 RREF2,
     912              :                                                                 is_Triangle,
     913              :                                                                 TVISIntWin,
     914              :                                                                 TVISIntWinDisk);
     915              : 
     916              :                     //           -------------------
     917              :                     // ---------- SUN POSITION LOOP ----------
     918              :                     //           -------------------
     919              : 
     920              :                     // Sun position counter. Used to avoid calculating various quantities
     921              :                     // that do not depend on sun position.
     922              : 
     923       347051 :                     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     924          427 :                         ISunPos = 0;
     925        10675 :                         for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
     926              : 
     927        10248 :                             FigureDayltgCoeffsAtPointsForSunPosition(state,
     928              :                                                                      daylightCtrlNum,
     929              :                                                                      IL,
     930              :                                                                      IX,
     931              :                                                                      NWX,
     932              :                                                                      IY,
     933              :                                                                      NWYlim,
     934              :                                                                      WinEl,
     935              :                                                                      IWin,
     936              :                                                                      IWin2,
     937              :                                                                      IHR,
     938              :                                                                      ISunPos,
     939              :                                                                      SkyObstructionMult,
     940              :                                                                      RWIN2,
     941              :                                                                      Ray,
     942              :                                                                      PHRAY,
     943              :                                                                      LSHCAL,
     944              :                                                                      InShelfSurf,
     945              :                                                                      COSB,
     946              :                                                                      ObTrans,
     947              :                                                                      TVISB,
     948              :                                                                      DOMEGA,
     949              :                                                                      ICtrl,
     950              :                                                                      ShType,
     951              :                                                                      BlNum,
     952              :                                                                      THRAY,
     953              :                                                                      WNORM2,
     954              :                                                                      extWinType,
     955              :                                                                      IConst,
     956              :                                                                      AZVIEW,
     957              :                                                                      RREF2,
     958              :                                                                      hitIntObs,
     959              :                                                                      hitExtObs,
     960              :                                                                      CalledFor::RefPoint,
     961              :                                                                      TVISIntWin,
     962              :                                                                      TVISIntWinDisk);
     963              : 
     964              :                         }    // End of hourly sun position loop, IHR
     965              :                     } else { // timestep integrated
     966       346624 :                         if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
     967           15 :                             ISunPos = 0;
     968           15 :                             dl->MySunIsUpFlag = true;
     969       346609 :                         } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
     970       137201 :                             ISunPos = 1;
     971       209408 :                         } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
     972           14 :                             dl->MySunIsUpFlag = false;
     973           14 :                             ISunPos = -1;
     974       209394 :                         } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
     975       209394 :                             ISunPos = -1;
     976              :                         }
     977              : 
     978       346624 :                         FigureDayltgCoeffsAtPointsForSunPosition(state,
     979              :                                                                  daylightCtrlNum,
     980              :                                                                  IL,
     981              :                                                                  IX,
     982              :                                                                  NWX,
     983              :                                                                  IY,
     984              :                                                                  NWYlim,
     985              :                                                                  WinEl,
     986              :                                                                  IWin,
     987              :                                                                  IWin2,
     988       346624 :                                                                  state.dataGlobal->HourOfDay,
     989              :                                                                  ISunPos,
     990              :                                                                  SkyObstructionMult,
     991              :                                                                  RWIN2,
     992              :                                                                  Ray,
     993              :                                                                  PHRAY,
     994              :                                                                  LSHCAL,
     995              :                                                                  InShelfSurf,
     996              :                                                                  COSB,
     997              :                                                                  ObTrans,
     998              :                                                                  TVISB,
     999              :                                                                  DOMEGA,
    1000              :                                                                  ICtrl,
    1001              :                                                                  ShType,
    1002              :                                                                  BlNum,
    1003              :                                                                  THRAY,
    1004              :                                                                  WNORM2,
    1005              :                                                                  extWinType,
    1006              :                                                                  IConst,
    1007              :                                                                  AZVIEW,
    1008              :                                                                  RREF2,
    1009              :                                                                  hitIntObs,
    1010              :                                                                  hitExtObs,
    1011              :                                                                  CalledFor::RefPoint,
    1012              :                                                                  TVISIntWin,
    1013              :                                                                  TVISIntWinDisk);
    1014              :                     }
    1015              : 
    1016              :                 } // End of window Y-element loop, IY
    1017              :             }     // End of window X-element loop, IX
    1018              : 
    1019              :             // Loop again over hourly sun positions and calculate daylight factors by adding
    1020              :             // direct and inter-reflected illum components, then dividing by exterior horiz illum.
    1021              :             // Also calculate corresponding glare factors.
    1022              : 
    1023         1365 :             ILB = BRef + IL;
    1024              : 
    1025         1365 :             if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1026           11 :                 ISunPos = 0;
    1027          275 :                 for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
    1028          264 :                     FigureRefPointDayltgFactorsToAddIllums(state, daylightCtrlNum, ILB, IHR, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
    1029              : 
    1030              :                 } // End of sun position loop, IHR
    1031              :             } else {
    1032         1354 :                 if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1033            0 :                     ISunPos = 0;
    1034            0 :                     dl->MySunIsUpFlag = true;
    1035         1354 :                 } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1036          536 :                     ISunPos = 1;
    1037          818 :                 } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1038            0 :                     dl->MySunIsUpFlag = false;
    1039            0 :                     ISunPos = -1;
    1040          818 :                 } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1041          818 :                     ISunPos = -1;
    1042              :                 }
    1043         1354 :                 FigureRefPointDayltgFactorsToAddIllums(
    1044         1354 :                     state, daylightCtrlNum, ILB, state.dataGlobal->HourOfDay, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
    1045              :             }
    1046              :         } // End of window loop, loopwin - IWin
    1047              : 
    1048         1365 :     } // End of reference point loop, IL
    1049         1360 : }
    1050              : 
    1051            1 : void CalcDayltgCoeffsMapPoints(EnergyPlusData &state, int const mapNum)
    1052              : {
    1053              : 
    1054              :     // SUBROUTINE INFORMATION:
    1055              :     //       AUTHOR         Linda Lawrie
    1056              :     //       DATE WRITTEN   April 2012
    1057              :     //       MODIFIED      November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
    1058              : 
    1059              :     // PURPOSE OF THIS SUBROUTINE:
    1060              :     // Provides calculations for Daylighting Coefficients for map illuminance points
    1061              : 
    1062              :     // METHODOLOGY EMPLOYED:
    1063              :     // Was previously part of CalcDayltgCoeffsRefMapPoints -- broken out to all multiple
    1064              :     // maps per zone
    1065            1 :     auto &dl = state.dataDayltg;
    1066            1 :     auto const &s_surf = state.dataSurface;
    1067              : 
    1068              :     //  In the following four variables, I=1 for clear sky, 2 for overcast.
    1069              :     int numRefPts; // Number of daylighting reference points in a zone
    1070              :     //  glare calculation (radians)
    1071              :     int IConst;            // Construction counter
    1072              :     int ICtrl;             // Window control counter
    1073              :     int IWin;              // Window counter
    1074              :     int IWin2;             // Secondary window counter (for TDD:DOME object, if exists)
    1075              :     int InShelfSurf;       // Inside daylighting shelf surface number
    1076              :     WinShadingType ShType; // Window shading type
    1077              :     int BlNum;             // Window Blind Number
    1078              :     int LSHCAL;            // Interior shade calculation flag: 0=not yet
    1079              :     //  calculated, 1=already calculated
    1080              :     int NWX;     // Number of window elements in x direction for dayltg calc
    1081              :     int NWY;     // Number of window elements in y direction for dayltg calc
    1082              :     int NWYlim;  // For triangle, largest NWY for a given IX
    1083              :     Real64 DWX;  // Horizontal dimension of window element (m)
    1084              :     Real64 DWY;  // Vertical dimension of window element (m)
    1085              :     Real64 COSB; // Cosine of angle between window outward normal and ray from
    1086              :     //  reference point to window element
    1087              :     Real64 PHRAY;  // Altitude of ray from reference point to window element (radians)
    1088              :     Real64 THRAY;  // Azimuth of ray from reference point to window element (radians)
    1089              :     Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
    1090              :     Real64 TVISB;  // Visible transmittance of window for COSB angle of incidence (times light well
    1091              :     //   efficiency, if appropriate)
    1092              :     int ISunPos; // Sun position counter; used to avoid calculating various
    1093              :     //  quantities that do not depend on sun position.
    1094              :     Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
    1095              :     // from reference point through a window element
    1096              :     bool is_Rectangle;         // True if window is rectangular
    1097              :     bool is_Triangle;          // True if window is triangular
    1098              :     Real64 DAXY;               // Area of window element
    1099              :     Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
    1100              :     ExtWinType extWinType;     // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1101              :     int ILB;
    1102              :     bool hitIntObs;        // True iff interior obstruction hit
    1103              :     bool hitExtObs;        // True iff ray from ref pt to ext win hits an exterior obstruction
    1104              :     Real64 TVISIntWin;     // Visible transmittance of int win at COSBIntWin for light from ext win
    1105              :     Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
    1106              :     int WinEl;             // window elements counter
    1107              : 
    1108            1 :     Vector3<Real64> W2;
    1109            1 :     Vector3<Real64> W3;
    1110            1 :     Vector3<Real64> W21;
    1111            1 :     Vector3<Real64> W23;
    1112            1 :     Vector3<Real64> RREF2;
    1113            1 :     Vector3<Real64> RWIN;
    1114            1 :     Vector3<Real64> RWIN2;
    1115            1 :     Vector3<Real64> Ray;
    1116            1 :     Vector3<Real64> WNORM2;
    1117            1 :     Vector3<Real64> VIEWVC;
    1118            1 :     Vector3<Real64> U2;
    1119            1 :     Vector3<Real64> U21;
    1120            1 :     Vector3<Real64> U23;
    1121            1 :     Vector3<Real64> VIEWVC2;
    1122              : 
    1123            1 :     if (dl->mapFirstTime && (int)dl->illumMaps.size() > 0) {
    1124            1 :         int IL = -999;
    1125            2 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    1126            1 :             IL = max(IL, dl->illumMaps(MapNum).TotalMapRefPoints);
    1127              :         }
    1128            1 :         dl->MapErrIndex.dimension(IL, s_surf->TotSurfaces, 0);
    1129            1 :         dl->mapFirstTime = false;
    1130              :     }
    1131              : 
    1132            1 :     auto &illumMap = dl->illumMaps(mapNum);
    1133            1 :     int enclNum = illumMap.enclIndex;
    1134            1 :     auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    1135              : 
    1136              :     // Azimuth of view vector in absolute coord sys - set to zero here, because glare isn't calculated for map points
    1137              :     // but these are arguments to some of the functions that are shared with regular reference points, so initalize here.
    1138            1 :     Real64 AZVIEW = 0.0;
    1139              :     // View vector components in absolute coord sys
    1140            1 :     VIEWVC = {0.0, 0.0, 0.0};
    1141              : 
    1142            1 :     numRefPts = illumMap.TotalMapRefPoints;
    1143            1 :     int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
    1144              : 
    1145          101 :     for (auto &refPt : illumMap.refPts) {
    1146          100 :         refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
    1147          200 :         for (int iExtWin = 1; iExtWin <= numExtWins; ++iExtWin) {
    1148          100 :             refPt.winLums(iExtWin) = {0.0, 0.0};
    1149              :         }
    1150              :     }
    1151              : 
    1152            1 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
    1153            1 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
    1154              : 
    1155           25 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
    1156           24 :         auto &daylFacHr = illumMap.daylFac[iHr];
    1157           48 :         for (int iWin = 1; iWin <= numExtWins; ++iWin) {
    1158         2424 :             for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
    1159         7200 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    1160        19200 :                     daylFacHr(iWin, iRefPt)[iWinCover] = Illums();
    1161              :                 }
    1162              :             }
    1163              :         }
    1164              :     }
    1165              : 
    1166          101 :     for (int IL = 1; IL <= numRefPts; ++IL) {
    1167          100 :         auto const &refPt = illumMap.refPts(IL);
    1168          100 :         Vector3<Real64> RREF = refPt.absCoords;
    1169              : 
    1170              :         //           -------------
    1171              :         // ---------- WINDOW LOOP ----------
    1172              :         //           -------------
    1173              : 
    1174          200 :         for (int loopwin = 1; loopwin <= numExtWins; ++loopwin) {
    1175              : 
    1176              :             // daylightingCtrlNum parameter is unused for map points
    1177          100 :             FigureDayltgCoeffsAtPointsSetupForWindow(state,
    1178              :                                                      0,
    1179              :                                                      IL,
    1180              :                                                      loopwin,
    1181              :                                                      CalledFor::MapPoint,
    1182              :                                                      RREF,
    1183              :                                                      VIEWVC,
    1184              :                                                      IWin,
    1185              :                                                      IWin2,
    1186              :                                                      NWX,
    1187              :                                                      NWY,
    1188              :                                                      W2,
    1189              :                                                      W3,
    1190              :                                                      W21,
    1191              :                                                      W23,
    1192              :                                                      LSHCAL,
    1193              :                                                      InShelfSurf,
    1194              :                                                      ICtrl,
    1195              :                                                      ShType,
    1196              :                                                      BlNum,
    1197              :                                                      WNORM2,
    1198              :                                                      extWinType,
    1199              :                                                      IConst,
    1200              :                                                      RREF2,
    1201              :                                                      DWX,
    1202              :                                                      DWY,
    1203              :                                                      DAXY,
    1204              :                                                      U2,
    1205              :                                                      U23,
    1206              :                                                      U21,
    1207              :                                                      VIEWVC2,
    1208              :                                                      is_Rectangle,
    1209              :                                                      is_Triangle,
    1210              :                                                      mapNum);
    1211              :             //           ---------------------
    1212              :             // ---------- WINDOW ELEMENT LOOP ----------
    1213              :             //           ---------------------
    1214          100 :             WinEl = 0;
    1215              : 
    1216         1340 :             for (int IX = 1; IX <= NWX; ++IX) {
    1217         1240 :                 if (is_Rectangle) {
    1218         1240 :                     NWYlim = NWY;
    1219            0 :                 } else if (is_Triangle) {
    1220            0 :                     NWYlim = NWY - IX + 1;
    1221              :                 }
    1222              : 
    1223        22290 :                 for (int IY = 1; IY <= NWYlim; ++IY) {
    1224              : 
    1225        21050 :                     ++WinEl;
    1226              : 
    1227              :                     // daylightingCtrlNum parameter is unused for map points
    1228        21050 :                     FigureDayltgCoeffsAtPointsForWindowElements(state,
    1229              :                                                                 0,
    1230              :                                                                 IL,
    1231              :                                                                 loopwin,
    1232              :                                                                 CalledFor::MapPoint,
    1233              :                                                                 WinEl,
    1234              :                                                                 IWin,
    1235              :                                                                 IWin2,
    1236              :                                                                 IX,
    1237              :                                                                 IY,
    1238              :                                                                 SkyObstructionMult,
    1239              :                                                                 W2,
    1240              :                                                                 W21,
    1241              :                                                                 W23,
    1242              :                                                                 RREF,
    1243              :                                                                 NWYlim,
    1244              :                                                                 VIEWVC2,
    1245              :                                                                 DWX,
    1246              :                                                                 DWY,
    1247              :                                                                 DAXY,
    1248              :                                                                 U2,
    1249              :                                                                 U23,
    1250              :                                                                 U21,
    1251              :                                                                 RWIN,
    1252              :                                                                 RWIN2,
    1253              :                                                                 Ray,
    1254              :                                                                 PHRAY,
    1255              :                                                                 LSHCAL,
    1256              :                                                                 COSB,
    1257              :                                                                 ObTrans,
    1258              :                                                                 TVISB,
    1259              :                                                                 DOMEGA,
    1260              :                                                                 THRAY,
    1261              :                                                                 hitIntObs,
    1262              :                                                                 hitExtObs,
    1263              :                                                                 WNORM2,
    1264              :                                                                 extWinType,
    1265              :                                                                 IConst,
    1266              :                                                                 RREF2,
    1267              :                                                                 is_Triangle,
    1268              :                                                                 TVISIntWin,
    1269              :                                                                 TVISIntWinDisk,
    1270              :                                                                 mapNum);
    1271              :                     //           -------------------
    1272              :                     // ---------- SUN POSITION LOOP ----------
    1273              :                     //           -------------------
    1274              : 
    1275              :                     // Sun position counter. Used to avoid calculating various quantities
    1276              :                     // that do not depend on sun position.
    1277        21050 :                     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1278        21050 :                         ISunPos = 0;
    1279       526250 :                         for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
    1280              :                             // daylightingCtrlNum parameter is unused for map points
    1281       505200 :                             FigureDayltgCoeffsAtPointsForSunPosition(state,
    1282              :                                                                      0,
    1283              :                                                                      IL,
    1284              :                                                                      IX,
    1285              :                                                                      NWX,
    1286              :                                                                      IY,
    1287              :                                                                      NWYlim,
    1288              :                                                                      WinEl,
    1289              :                                                                      IWin,
    1290              :                                                                      IWin2,
    1291              :                                                                      IHR,
    1292              :                                                                      ISunPos,
    1293              :                                                                      SkyObstructionMult,
    1294              :                                                                      RWIN2,
    1295              :                                                                      Ray,
    1296              :                                                                      PHRAY,
    1297              :                                                                      LSHCAL,
    1298              :                                                                      InShelfSurf,
    1299              :                                                                      COSB,
    1300              :                                                                      ObTrans,
    1301              :                                                                      TVISB,
    1302              :                                                                      DOMEGA,
    1303              :                                                                      ICtrl,
    1304              :                                                                      ShType,
    1305              :                                                                      BlNum,
    1306              :                                                                      THRAY,
    1307              :                                                                      WNORM2,
    1308              :                                                                      extWinType,
    1309              :                                                                      IConst,
    1310              :                                                                      AZVIEW,
    1311              :                                                                      RREF2,
    1312              :                                                                      hitIntObs,
    1313              :                                                                      hitExtObs,
    1314              :                                                                      CalledFor::MapPoint,
    1315              :                                                                      TVISIntWin,
    1316              :                                                                      TVISIntWinDisk,
    1317              :                                                                      mapNum);
    1318              :                         } // End of hourly sun position loop, IHR
    1319              :                     } else {
    1320            0 :                         if (state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1321            0 :                             ISunPos = 0;
    1322            0 :                             dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = true;
    1323            0 :                         } else if (state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1324            0 :                             ISunPos = 1;
    1325            0 :                         } else if (!state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1326            0 :                             dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = false;
    1327            0 :                             ISunPos = -1;
    1328            0 :                         } else if (!state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1329            0 :                             ISunPos = -1;
    1330              :                         }
    1331              :                         // daylightingCtrlNum parameter is unused for map points
    1332            0 :                         FigureDayltgCoeffsAtPointsForSunPosition(state,
    1333              :                                                                  0,
    1334              :                                                                  IL,
    1335              :                                                                  IX,
    1336              :                                                                  NWX,
    1337              :                                                                  IY,
    1338              :                                                                  NWYlim,
    1339              :                                                                  WinEl,
    1340              :                                                                  IWin,
    1341              :                                                                  IWin2,
    1342            0 :                                                                  state.dataGlobal->HourOfDay,
    1343              :                                                                  ISunPos,
    1344              :                                                                  SkyObstructionMult,
    1345              :                                                                  RWIN2,
    1346              :                                                                  Ray,
    1347              :                                                                  PHRAY,
    1348              :                                                                  LSHCAL,
    1349              :                                                                  InShelfSurf,
    1350              :                                                                  COSB,
    1351              :                                                                  ObTrans,
    1352              :                                                                  TVISB,
    1353              :                                                                  DOMEGA,
    1354              :                                                                  ICtrl,
    1355              :                                                                  ShType,
    1356              :                                                                  BlNum,
    1357              :                                                                  THRAY,
    1358              :                                                                  WNORM2,
    1359              :                                                                  extWinType,
    1360              :                                                                  IConst,
    1361              :                                                                  AZVIEW,
    1362              :                                                                  RREF2,
    1363              :                                                                  hitIntObs,
    1364              :                                                                  hitExtObs,
    1365              :                                                                  CalledFor::MapPoint,
    1366              :                                                                  TVISIntWin,
    1367              :                                                                  TVISIntWinDisk,
    1368              :                                                                  mapNum);
    1369              :                     }
    1370              :                 } // End of window Y-element loop, IY
    1371              :             }     // End of window X-element loop, IX
    1372              : 
    1373          100 :             if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1374              :                 // Loop again over hourly sun positions and calculate daylight factors by adding
    1375              :                 // direct and inter-reflected illum components, then dividing by exterior horiz illum.
    1376              :                 // Also calculate corresponding glare factors.
    1377          100 :                 ILB = IL;
    1378         2500 :                 for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
    1379         2400 :                     FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, IHR, IWin, loopwin, ICtrl);
    1380              :                 } // End of sun position loop, IHR
    1381              :             } else {
    1382            0 :                 ILB = IL;
    1383            0 :                 FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, state.dataGlobal->HourOfDay, IWin, loopwin, ICtrl);
    1384              :             }
    1385              : 
    1386              :         } // End of window loop, loopwin - IWin
    1387              : 
    1388          100 :     } // End of reference point loop, IL
    1389            1 : }
    1390              : 
    1391         1465 : void FigureDayltgCoeffsAtPointsSetupForWindow(EnergyPlusData &state,
    1392              :                                               int const daylightCtrlNum, // zero if called for map points
    1393              :                                               int const iRefPoint,
    1394              :                                               int const loopwin,
    1395              :                                               CalledFor const CalledFrom,    // indicate  which type of routine called this routine
    1396              :                                               Vector3<Real64> const &RREF,   // Location of a reference point in absolute coordinate system
    1397              :                                               Vector3<Real64> const &VIEWVC, // View vector in absolute coordinate system
    1398              :                                               int &IWin,
    1399              :                                               int &IWin2,
    1400              :                                               int &NWX,
    1401              :                                               int &NWY,
    1402              :                                               Vector3<Real64> &W2,     // Second vertex of window
    1403              :                                               Vector3<Real64> &W3,     // Third vertex of window
    1404              :                                               Vector3<Real64> &W21,    // Vector from window vertex 2 to window vertex 1
    1405              :                                               Vector3<Real64> &W23,    // Vector from window vertex 2 to window vertex 3
    1406              :                                               int &LSHCAL,             // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    1407              :                                               int &InShelfSurf,        // Inside daylighting shelf surface number
    1408              :                                               int &ICtrl,              // Window control counter
    1409              :                                               WinShadingType &ShType,  // Window shading type
    1410              :                                               int &BlNum,              // Window blind number
    1411              :                                               Vector3<Real64> &WNORM2, // Unit vector normal to window
    1412              :                                               ExtWinType &extWinType,  // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1413              :                                               int &IConst,             // Construction counter
    1414              :                                               Vector3<Real64> &RREF2,  // Location of virtual reference point in absolute coordinate system
    1415              :                                               Real64 &DWX,             // Horizontal dimension of window element (m)
    1416              :                                               Real64 &DWY,             // Vertical dimension of window element (m)
    1417              :                                               Real64 &DAXY,            // Area of window element
    1418              :                                               Vector3<Real64> &U2,     // Second vertex of window for TDD:DOME (if exists)
    1419              :                                               Vector3<Real64> &U23,    // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
    1420              :                                               Vector3<Real64> &U21,    // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
    1421              :                                               Vector3<Real64> &VIEWVC2, // Virtual view vector in absolute coordinate system
    1422              :                                               bool &is_Rectangle,       // True if window is rectangular
    1423              :                                               bool &is_Triangle,        // True if window is triangular
    1424              :                                               int const MapNum)
    1425              : {
    1426              :     // SUBROUTINE INFORMATION:
    1427              :     //       AUTHOR         B. Griffith
    1428              :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    1429              : 
    1430              :     // PURPOSE OF THIS SUBROUTINE:
    1431              :     // collect code to setup calculations for each window for daylighting coefficients
    1432              : 
    1433              :     // METHODOLOGY EMPLOYED:
    1434              :     // switch as need to serve both reference points and map points based on calledFrom
    1435         1465 :     auto &dl = state.dataDayltg;
    1436         1465 :     auto &s_surf = state.dataSurface;
    1437              : 
    1438              :     int ShelfNum; // Daylighting shelf object number
    1439              :     int NDIVX;    // Number of window x divisions for daylighting calc
    1440              :     int NDIVY;    // Number of window y divisions for daylighting calc
    1441              :     Real64 ALF;   // Distance from reference point to window plane (m)
    1442              :     Real64 D1a;   // Projection of vector from window origin to reference
    1443              :     //  on window X  axis (m)
    1444              :     Real64 D1b; // Projection of vector from window origin to reference
    1445              :     //  on window Y axis (m)
    1446              :     Real64 SolidAngExtWin;    // Approx. solid angle subtended by an ext. window wrt ref pt
    1447              :     Real64 SolidAngMinIntWin; // Approx. smallest solid angle subtended by an int. window wrt ref pt
    1448              :     Real64 SolidAngRatio;     // Ratio of SolidAngExtWin and SolidAngMinIntWin
    1449              :     Real64 SinCornerAng;      // For triangle, sine of corner angle of window element
    1450              : 
    1451         1465 :     int zoneNum = 0; // zone number
    1452         1465 :     int enclNum = 0; // enclosure number
    1453              : 
    1454         1465 :     Vector3<Real64> W1 = {0.0, 0.0, 0.0};
    1455         1465 :     Vector3<Real64> WC = {0.0, 0.0, 0.0};
    1456              : 
    1457         1465 :     if (CalledFrom == CalledFor::RefPoint) {
    1458         1365 :         auto &daylCtrl = dl->daylightControl(daylightCtrlNum);
    1459         1365 :         daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAng = 0.0;
    1460         1365 :         daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAngWtd = 0.0;
    1461         1365 :         zoneNum = daylCtrl.zoneIndex;
    1462         1365 :         enclNum = daylCtrl.enclIndex;
    1463          100 :     } else if (CalledFrom == CalledFor::MapPoint) {
    1464          100 :         assert(MapNum > 0);
    1465          100 :         auto const &illumMap = dl->illumMaps(MapNum);
    1466          100 :         zoneNum = illumMap.zoneIndex;
    1467          100 :         enclNum = illumMap.enclIndex;
    1468              :     }
    1469         1465 :     IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
    1470              : 
    1471         1465 :     auto &surf = s_surf->Surface(IWin);
    1472         1465 :     auto &surfWin = s_surf->SurfaceWindow(IWin);
    1473              : 
    1474         1465 :     if (s_surf->Surface(surf.BaseSurf).SolarEnclIndex == enclNum) {
    1475         1465 :         extWinType = ExtWinType::InZone;
    1476              :     } else {
    1477            0 :         extWinType = ExtWinType::AdjZone;
    1478              :     }
    1479              : 
    1480         1465 :     IConst = s_surf->SurfActiveConstruction(IWin);
    1481              : 
    1482              :     // For thermochromic windows, the daylight and glare factors are calculated for a base window cosntruction
    1483              :     //  at base TC layer temperature. During each time step calculations at DayltgInteriorIllum,
    1484              :     //  DayltgInteriorMapIllum, and DayltgGlare, the daylight and glare factors are adjusted by the visible
    1485              :     //  transmittance ratio = VT of actual TC window based on last hour TC layer temperature / VT of the base TC window
    1486         1465 :     if (state.dataConstruction->Construct(IConst).isTCWindow) {
    1487              :         // For thermochromic windows, use the base window construction at base temperature of the TC layer
    1488            0 :         IConst = state.dataConstruction->Construct(IConst).TCMasterConstrNum;
    1489              :     }
    1490              : 
    1491         1465 :     ICtrl = surf.activeWindowShadingControl;
    1492         1465 :     ShType = WinShadingType::NoShade; // 'NOSHADE'
    1493         1465 :     BlNum = 0;
    1494              :     // ScNum = 0; //Unused Set but never used
    1495         1465 :     if (surf.HasShadeControl) ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
    1496         1465 :     if (ANY_BLIND(ShType)) BlNum = s_surf->surfShades(IWin).blind.matNum;
    1497              :     // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
    1498              : 
    1499         1465 :     ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
    1500         1465 :     if (ShelfNum > 0) {
    1501            0 :         InShelfSurf =
    1502            0 :             state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf; // Inside daylighting shelf present if > 0
    1503              :     } else {
    1504         1465 :         InShelfSurf = 0;
    1505              :     }
    1506              : 
    1507         1465 :     is_Rectangle = false;
    1508         1465 :     is_Triangle = false;
    1509         1465 :     if (surf.Sides == 3) is_Triangle = true;
    1510         1465 :     if (surf.Sides == 4) is_Rectangle = true;
    1511              : 
    1512         1465 :     if (is_Rectangle) {
    1513              :         // Vertices of window (numbered counter-clockwise starting at upper left as viewed
    1514              :         // from inside of room). Assumes original vertices are numbered counter-clockwise from
    1515              :         // upper left as viewed from outside.
    1516         1465 :         W3 = surf.Vertex(2);
    1517         1465 :         W2 = surf.Vertex(3);
    1518         1465 :         W1 = surf.Vertex(4);
    1519            0 :     } else if (is_Triangle) {
    1520            0 :         W3 = surf.Vertex(2);
    1521            0 :         W2 = surf.Vertex(3);
    1522            0 :         W1 = surf.Vertex(1);
    1523              :     }
    1524              : 
    1525              :     // Shade/blind calculation flag
    1526         1465 :     LSHCAL = 0;
    1527              : 
    1528              :     // Visible transmittance at normal incidence
    1529         1465 :     s_surf->SurfWinVisTransSelected(IWin) = Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    1530              :     // For windows with switchable glazing, ratio of visible transmittance at normal
    1531              :     // incidence for fully switched (dark) state to that of unswitched state
    1532         1465 :     s_surf->SurfWinVisTransRatio(IWin) = 1.0;
    1533         1465 :     if (ICtrl > 0) {
    1534            0 :         if (ShType == WinShadingType::SwitchableGlazing) {
    1535            0 :             int IConstShaded = surf.activeShadedConstruction; // Shaded construction counter
    1536            0 :             s_surf->SurfWinVisTransRatio(IWin) =
    1537            0 :                 General::SafeDivide(Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef),
    1538            0 :                                     Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef));
    1539              :         }
    1540              :     }
    1541              : 
    1542              :     // Unit vectors from window vertex 2 to 1 and 2 to 3,
    1543              :     // center point of window, and vector from ref pt to center of window
    1544         1465 :     W21 = W1 - W2;
    1545         1465 :     W23 = W3 - W2;
    1546         1465 :     Real64 HW = W21.magnitude();
    1547         1465 :     Real64 WW = W23.magnitude();
    1548         1465 :     if (is_Rectangle) {
    1549         1465 :         WC = W2 + (W23 + W21) / 2.0;
    1550            0 :     } else if (is_Triangle) {
    1551            0 :         WC = W2 + (W23 + W21) / 3.0;
    1552              :     }
    1553         1465 :     s_surf->SurfaceWindow(IWin).WinCenter = WC;
    1554         1465 :     Vector3<Real64> REFWC = WC - RREF;
    1555              :     // Unit vectors
    1556         1465 :     W21 /= HW;
    1557         1465 :     W23 /= WW;
    1558              : 
    1559              :     // Unit vector normal to window (pointing away from room)
    1560         1465 :     Vector3<Real64> WNORM = surf.lcsz;
    1561              : 
    1562              :     // Initialize number of window elements
    1563         1465 :     NDIVX = 40; // Does this mean that windows are split into 1,600 points for daylighting? WHYYYYYY?
    1564         1465 :     NDIVY = 40;
    1565              : 
    1566              :     // Distance from ref point to window plane
    1567         1465 :     ALF = std::abs(dot(WNORM, REFWC));
    1568         1465 :     if (CalledFrom == CalledFor::RefPoint) {
    1569              :         // Check if ref point to close to window due to input error (0.1524 m below is 0.5 ft)
    1570         1365 :         if (ALF < 0.1524 && extWinType == ExtWinType::InZone) {
    1571              :             // Ref pt is close to window plane. Get vector from window
    1572              :             // origin to projection of ref pt on window plane.
    1573            0 :             Vector3<Real64> W2REF = RREF + ALF * WNORM - W2;
    1574              : 
    1575            0 :             D1a = dot(W2REF, W23);
    1576            0 :             D1b = dot(W2REF, W21);
    1577              : 
    1578              :             //            ! Error message if ref pt is too close to window.
    1579            0 :             if (D1a > 0.0 && D1b > 0.0 && D1b <= HW && D1a <= WW) {
    1580            0 :                 ShowSevereError(
    1581              :                     state,
    1582            0 :                     format("CalcDaylightCoeffRefPoints: Daylighting calculation cannot be done for Daylighting:Controls={} because reference point "
    1583              :                            "#{} is less than 0.15m (6\") from window plane {}",
    1584            0 :                            dl->daylightControl(daylightCtrlNum).Name,
    1585              :                            iRefPoint,
    1586            0 :                            surf.Name));
    1587            0 :                 ShowContinueError(state, format("Distance=[{:.5R}]. This is too close; check position of reference point.", ALF));
    1588            0 :                 ShowFatalError(state, "Program terminates due to preceding condition.");
    1589              :             }
    1590         1365 :         } else if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
    1591            0 :             if (dl->RefErrIndex(iRefPoint, IWin) == 0) { // only show error message once
    1592            0 :                 ShowWarningError(state,
    1593            0 :                                  format("CalcDaylightCoeffRefPoints: For Daylghting:Controls=\"{}\" External Window=\"{}\"in Zone=\"{}\" reference "
    1594              :                                         "point is less than 0.15m (6\") from window plane ",
    1595            0 :                                         dl->daylightControl(daylightCtrlNum).Name,
    1596            0 :                                         surf.Name,
    1597            0 :                                         state.dataHeatBal->Zone(surf.Zone).Name));
    1598            0 :                 ShowContinueError(state,
    1599            0 :                                   format("Distance=[{:.1R} m] to ref point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Daylighting Calcs may result.",
    1600              :                                          ALF,
    1601            0 :                                          RREF.x,
    1602            0 :                                          RREF.y,
    1603            0 :                                          RREF.z));
    1604            0 :                 dl->RefErrIndex(iRefPoint, IWin) = 1;
    1605              :             }
    1606              :         }
    1607          100 :     } else if (CalledFrom == CalledFor::MapPoint) {
    1608          100 :         if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
    1609            0 :             if (dl->MapErrIndex(iRefPoint, IWin) == 0) { // only show error message once
    1610            0 :                 ShowWarningError(state,
    1611            0 :                                  format("CalcDaylightCoeffMapPoints: For Zone=\"{}\" External Window=\"{}\"in Zone=\"{}\" map point is less than "
    1612              :                                         "0.15m (6\") from window plane ",
    1613            0 :                                         state.dataHeatBal->Zone(zoneNum).Name,
    1614            0 :                                         surf.Name,
    1615            0 :                                         state.dataHeatBal->Zone(surf.Zone).Name));
    1616            0 :                 ShowContinueError(
    1617              :                     state,
    1618            0 :                     format("Distance=[{:.1R} m] map point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Map Calcs may result.", ALF, RREF.x, RREF.y, RREF.z));
    1619            0 :                 dl->MapErrIndex(iRefPoint, IWin) = 1;
    1620              :             }
    1621              :         }
    1622              :     }
    1623              :     // Number of window elements in X and Y for daylighting calculation
    1624         1465 :     if (ALF > 0.1524) {
    1625         1455 :         NDIVX = 1 + int(4.0 * WW / ALF);
    1626         1455 :         NDIVY = 1 + int(4.0 * HW / ALF);
    1627              :     }
    1628              : 
    1629         1465 :     if (extWinType == ExtWinType::AdjZone) {
    1630              :         // Adjust number of exterior window elements to give acceptable number of rays through
    1631              :         // interior windows in the zone (for accuracy of interior window daylighting calculation)
    1632            0 :         SolidAngExtWin = General::SafeDivide(((surf.Area + s_surf->SurfWinDividerArea(IWin)) / surf.Multiplier), pow_2(ALF));
    1633            0 :         SolidAngMinIntWin = dl->enclDaylight(enclNum).MinIntWinSolidAng;
    1634            0 :         SolidAngRatio = max(1.0, SolidAngExtWin / SolidAngMinIntWin);
    1635            0 :         NDIVX *= std::sqrt(SolidAngRatio);
    1636            0 :         NDIVY *= std::sqrt(SolidAngRatio);
    1637              :     }
    1638              : 
    1639         1465 :     NWX = min(40, NDIVX);
    1640         1465 :     NWY = min(40, NDIVY);
    1641              : 
    1642              :     // Discretization of triangle is simpler if NWX = NWY
    1643         1465 :     if (is_Triangle) {
    1644            0 :         NWX = max(NWX, NWY);
    1645            0 :         NWY = NWX;
    1646              :     }
    1647              : 
    1648              :     // Edge lengths of window elements
    1649         1465 :     DWX = WW / NWX;
    1650         1465 :     DWY = HW / NWY;
    1651              : 
    1652              :     // Azimuth and altitude of window normal
    1653         1465 :     surfWin.phi = std::asin(WNORM.z);
    1654         1465 :     surfWin.theta = (std::abs(WNORM.x) > 1.0e-5 || std::abs(WNORM.y) > 1.0e-5) ? std::atan2(WNORM.y, WNORM.x) : 0.0;
    1655              : 
    1656              :     // Recalculation of values for TDD:DOME
    1657         1465 :     if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    1658              : 
    1659              :         // Look up the TDD:DOME object
    1660            0 :         int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
    1661            0 :         IWin2 = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
    1662              : 
    1663            0 :         auto &surf2 = s_surf->Surface(IWin2);
    1664            0 :         auto &surfWin2 = s_surf->SurfaceWindow(IWin2);
    1665              : 
    1666              :         // Calculate reference point coords relative to the diffuser coordinate system
    1667              :         // W21, W23, and WNORM are the unit vectors
    1668            0 :         Vector3<Real64> REFD = {dot(REFWC, W21), dot(REFWC, W23), dot(REFWC, WNORM)};
    1669              : 
    1670              :         // Calculate view vector coords relative to the diffuser coordinate system
    1671            0 :         Vector3<Real64> VIEWVD = {dot(VIEWVC, W21), dot(VIEWVC, W23), dot(VIEWVC, WNORM)};
    1672              : 
    1673            0 :         Vector3<Real64> U3 = surf2.Vertex(2);
    1674            0 :         U2 = surf2.Vertex(3);
    1675            0 :         Vector3<Real64> U1;
    1676              : 
    1677            0 :         if (surf2.Sides == 4) {
    1678              :             // Vertices of window (numbered counter-clockwise starting
    1679              :             // at upper left as viewed from inside of room)
    1680              :             // Assumes original vertices are numbered counter-clockwise from
    1681              :             // upper left as viewed from outside.
    1682            0 :             U3 = surf2.Vertex(2);
    1683            0 :             U2 = surf2.Vertex(3);
    1684            0 :             U1 = surf2.Vertex(4);
    1685            0 :         } else if (surf2.Sides == 3) {
    1686            0 :             U3 = surf2.Vertex(2);
    1687            0 :             U2 = surf2.Vertex(3);
    1688            0 :             U1 = surf2.Vertex(1);
    1689              :         }
    1690              : 
    1691              :         // Unit vectors from window vertex 2 to 1 and 2 to 3,
    1692              :         // center point of window, and vector from ref pt to center of window
    1693            0 :         U21 = U1 - U2;
    1694            0 :         U23 = U3 - U2;
    1695            0 :         HW = U21.magnitude();
    1696            0 :         WW = U23.magnitude();
    1697            0 :         if (surf2.Sides == 4) {
    1698            0 :             WC = U2 + (U23 + U21) / 2.0;
    1699            0 :         } else if (surf2.Sides == 3) {
    1700            0 :             WC = U2 + (U23 + U21) / 3.0;
    1701              :         }
    1702            0 :         s_surf->SurfaceWindow(IWin2).WinCenter = WC;
    1703              :         // Unit vectors
    1704            0 :         U21 /= HW;
    1705            0 :         U23 /= WW;
    1706              : 
    1707              :         // Unit vector normal to dome (pointing away from TDD)
    1708              :         // These are specific to the exterior.
    1709              :         // NOTE:  Preserve WNORM for later in the code.
    1710            0 :         WNORM2 = cross(U21, U23).normalize();
    1711              : 
    1712              :         // Azimuth and altitude of dome normal
    1713              :         // These are specific to the exterior.
    1714            0 :         surfWin2.phi = std::asin(WNORM2.z);
    1715            0 :         surfWin2.theta = (std::abs(WNORM2.x) > 1.0e-5 || std::abs(WNORM2.y) > 1.0e-5) ? std::atan2(WNORM2.y, WNORM2.x) : 0.0;
    1716              : 
    1717              :         // Calculate new virtual reference point coords relative to dome coord system
    1718              :         // W21, W23, and WNORM2 are now the unit vectors for the dome coord system
    1719            0 :         REFWC = REFD.x * U21 + REFD.y * U23 + REFD.z * WNORM2;
    1720            0 :         RREF2 = WC - REFWC;
    1721              : 
    1722              :         // Calculate new virtual view vector coords relative to dome coord system
    1723            0 :         VIEWVC2 = VIEWVD.x * U21 + VIEWVD.y * U23 + VIEWVD.z * WNORM2;
    1724              : 
    1725              :         // Copy several values from the diffuser so that DayltgInterReflectedIllum works correctly
    1726              :         // These are specific to the interior.
    1727            0 :         surfWin2.rhoCeilingWall = surfWin.rhoCeilingWall;
    1728            0 :         surfWin2.rhoFloorWall = surfWin.rhoFloorWall;
    1729            0 :         surfWin2.fractionUpgoing = surfWin.fractionUpgoing;
    1730            0 :         surfWin2.glazedFrac = surfWin.glazedFrac;
    1731              : 
    1732            0 :     } else {
    1733              :         // This is not a TDD:DIFFUSER.  Make sure nothing is messed up for a regular window.
    1734         1465 :         IWin2 = IWin;
    1735         1465 :         WNORM2 = WNORM;
    1736         1465 :         RREF2 = RREF;
    1737         1465 :         VIEWVC2 = VIEWVC;
    1738              : 
    1739         1465 :         U2 = W2;
    1740         1465 :         U21 = W21;
    1741         1465 :         U23 = W23;
    1742              :     }
    1743              : 
    1744              :     // Initialize bsdf daylighting coefficients here.  Only one time initialization
    1745         1465 :     if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
    1746            0 :         if (!state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized) {
    1747            0 :             int NRefPts = 0;
    1748            0 :             if (CalledFrom == CalledFor::MapPoint) {
    1749            0 :                 NRefPts = dl->illumMaps(MapNum).TotalMapRefPoints;
    1750            0 :             } else if (CalledFrom == CalledFor::RefPoint) {
    1751            0 :                 NRefPts = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
    1752              :             }
    1753            0 :             InitializeCFSDaylighting(state, daylightCtrlNum, IWin, NWX, NWY, RREF, NRefPts, iRefPoint, CalledFrom, MapNum);
    1754              :             // if ((WinEl == (NWX * NWY)).and.(CalledFrom == CalledForMapPoint).and.(NRefPts == iRefPoint)) then
    1755            0 :             if ((CalledFrom == CalledFor::MapPoint) && (NRefPts == iRefPoint)) {
    1756            0 :                 state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized = true;
    1757              :             }
    1758              :         }
    1759              :     }
    1760              : 
    1761         1465 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
    1762         1465 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
    1763              : 
    1764         5483 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
    1765              :         // Initialize sky and sun components of direct illuminance (arrays EDIRSK, EDIRSU, EDIRSUdisk)
    1766              :         // and average window luminance (arrays AVWLSK, AVWLSU, AVWLSUdisk), at ref pt.
    1767        16072 :         dl->dirIllum(iHr)[iWinCover_Bare] = dl->dirIllum(iHr)[iWinCover_Shaded] = Illums();
    1768        16072 :         dl->avgWinLum(iHr)[iWinCover_Bare] = dl->avgWinLum(iHr)[iWinCover_Shaded] = Illums();
    1769              :     }
    1770              : 
    1771         1465 :     if (CalledFrom == CalledFor::RefPoint) {
    1772              :         // Initialize solid angle subtended by window wrt ref pt
    1773              :         // and solid angle weighted by glare position factor
    1774         1365 :         s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAng = 0.0;
    1775         1365 :         s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd = 0.0;
    1776              :     }
    1777              :     // Area of window element
    1778         1465 :     if (is_Rectangle) {
    1779         1465 :         DAXY = DWX * DWY;
    1780            0 :     } else if (is_Triangle) {
    1781            0 :         SinCornerAng = std::sqrt(1.0 - pow_2(dot(W21, W23)));
    1782            0 :         DAXY = DWX * DWY * SinCornerAng;
    1783              :     }
    1784         1465 : }
    1785              : 
    1786       368101 : void FigureDayltgCoeffsAtPointsForWindowElements(
    1787              :     EnergyPlusData &state,
    1788              :     int const daylightCtrlNum, // Current daylighting control number (only used when called from RefPoint)
    1789              :     int const iRefPoint,
    1790              :     int const loopwin,
    1791              :     CalledFor const CalledFrom, // indicate  which type of routine called this routine
    1792              :     int const WinEl,            // Current window element number
    1793              :     int const IWin,
    1794              :     int const IWin2,
    1795              :     int const iXelement,
    1796              :     int const iYelement,
    1797              :     Real64 &SkyObstructionMult,
    1798              :     Vector3<Real64> const &W2,      // Second vertex of window
    1799              :     Vector3<Real64> const &W21,     // Vector from window vertex 2 to window vertex 1
    1800              :     Vector3<Real64> const &W23,     // Vector from window vertex 2 to window vertex 3
    1801              :     Vector3<Real64> const &RREF,    // Location of a reference point in absolute coordinate system
    1802              :     int const NWYlim,               // For triangle, largest NWY for a given IX
    1803              :     Vector3<Real64> const &VIEWVC2, // Virtual view vector in absolute coordinate system
    1804              :     Real64 const DWX,               // Horizontal dimension of window element (m)
    1805              :     Real64 const DWY,               // Vertical dimension of window element (m)
    1806              :     Real64 const DAXY,              // Area of window element
    1807              :     Vector3<Real64> const &U2,      // Second vertex of window for TDD:DOME (if exists)
    1808              :     Vector3<Real64> const &U23,     // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
    1809              :     Vector3<Real64> const &U21,     // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
    1810              :     Vector3<Real64> &RWIN,          // Center of a window element for TDD:DOME (if exists) in abs coord sys
    1811              :     Vector3<Real64> &RWIN2,         // Center of a window element for TDD:DOME (if exists) in abs coord sys
    1812              :     Vector3<Real64> &Ray,           // Unit vector along ray from reference point to window element
    1813              :     Real64 &PHRAY,                  // Altitude of ray from reference point to window element (radians)
    1814              :     int &LSHCAL,                    // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    1815              :     Real64 &COSB,                   // Cosine of angle between window outward normal and ray from reference point to window element
    1816              :     Real64 &ObTrans,                // Product of solar transmittances of exterior obstructions hit by ray
    1817              :     Real64 &TVISB,                  // Visible transmittance of window for COSB angle of incidence (times light well
    1818              :     Real64 &DOMEGA,                 // Solid angle subtended by window element wrt reference point (steradians)
    1819              :     Real64 &THRAY,                  // Azimuth of ray from reference point to window element (radians)
    1820              :     bool &hitIntObs,                // True iff interior obstruction hit
    1821              :     bool &hitExtObs,                // True iff ray from ref pt to ext win hits an exterior obstruction
    1822              :     Vector3<Real64> const &WNORM2,  // Unit vector normal to window
    1823              :     ExtWinType const extWinType,    // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1824              :     int const IConst,               // Construction counter
    1825              :     Vector3<Real64> const &RREF2,   // Location of virtual reference point in absolute coordinate system
    1826              :     bool const is_Triangle,
    1827              :     Real64 &TVISIntWin,     // Visible transmittance of int win at COSBIntWin for light from ext win
    1828              :     Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
    1829              :     int const MapNum)
    1830              : {
    1831              : 
    1832              :     // SUBROUTINE INFORMATION:
    1833              :     //       AUTHOR         B. Griffith
    1834              :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    1835              : 
    1836              :     // PURPOSE OF THIS SUBROUTINE:
    1837              :     // collect code to do calculations for each window element for daylighting coefficients
    1838              : 
    1839              :     // REFERENCES:
    1840              :     // switch as need to serve both reference points and map points based on calledFrom
    1841       368101 :     auto &dl = state.dataDayltg;
    1842       368101 :     auto &s_surf = state.dataSurface;
    1843              : 
    1844              :     Real64 RR; // Distance from ref point to intersection of view vector
    1845              :     //  and plane normal to view vector and window element (m)
    1846              :     Real64 ASQ; // Square of distance from above intersection to window element (m2)
    1847              :     Real64 YD;  // Vertical displacement of window element wrt ref point
    1848              : 
    1849              :     Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
    1850              :     //  exterior window element or between ref pt and sun
    1851              : 
    1852              :     // Local complex fenestration variables
    1853              :     Real64 TransBeam; // Obstructions transmittance for incoming BSDF rays (temporary variable)
    1854              : 
    1855       368101 :     auto &surfWin = s_surf->SurfaceWindow(IWin);
    1856              : 
    1857       368101 :     ++LSHCAL;
    1858       368101 :     SkyObstructionMult = 1.0;
    1859              : 
    1860              :     // Center of win element in absolute coord sys
    1861       368101 :     RWIN = W2 + (double(iXelement) - 0.5) * W23 * DWX + (double(iYelement) - 0.5) * W21 * DWY;
    1862              : 
    1863              :     // Center of win element on TDD:DOME in absolute coord sys
    1864              :     // If no TDD, RWIN2 = RWIN
    1865       368101 :     RWIN2 = U2 + (double(iXelement) - 0.5) * U23 * DWX + (double(iYelement) - 0.5) * U21 * DWY;
    1866              : 
    1867              :     // Distance between ref pt and window element
    1868       368101 :     Real64 DIS = distance(RWIN, RREF);
    1869              : 
    1870              :     // Unit vector along ray from ref pt to element
    1871       368101 :     Ray = (RWIN - RREF) / DIS;
    1872              : 
    1873              :     // Cosine of angle between ray and window outward normal
    1874       368101 :     COSB = dot(WNORM2, Ray);
    1875              : 
    1876              :     // If COSB > 0, direct light from window can reach ref pt. Otherwise go to loop
    1877              :     // over sun position and calculate inter-reflected component of illuminance
    1878       368101 :     if (COSB <= 0.0) return;
    1879              : 
    1880              :     // Azimuth (-pi to pi) and altitude (-pi/2 to pi/2) of ray. Azimuth = 0 is along east.
    1881       368021 :     PHRAY = std::asin(Ray.z);
    1882       368021 :     if (std::abs(Ray.x) > 1.0e-5 || std::abs(Ray.y) > 1.0e-5) {
    1883       368021 :         THRAY = std::atan2(Ray.y, Ray.x);
    1884              :     } else {
    1885            0 :         THRAY = 0.0;
    1886              :     }
    1887              : 
    1888              :     // Solid angle subtended by element wrt ref pt.
    1889       368021 :     Real64 DAXY1 = DAXY; // For triangle, area of window element at end of column
    1890              :     // For triangle, at end of Y column only one half of parallelopiped's area contributes
    1891       368021 :     if (is_Triangle && iYelement == NWYlim) DAXY1 = 0.5 * DAXY;
    1892       368021 :     DOMEGA = DAXY1 * COSB / (DIS * DIS);
    1893              : 
    1894              :     // Calculate position factor (used in glare calculation) for this
    1895              :     // win element / ref pt / view-vector combination
    1896       368021 :     Real64 POSFAC = 0.0;
    1897              : 
    1898              :     // Distance from ref pt to intersection of view vector and plane
    1899              :     // normal to view vector containing the window element
    1900              : 
    1901       368021 :     if (CalledFrom == CalledFor::RefPoint) {
    1902       346971 :         RR = DIS * dot(Ray, VIEWVC2);
    1903       346971 :         if (RR > 0.0) {
    1904              :             // Square of distance from above intersection point to win element
    1905          347 :             ASQ = DIS * DIS - RR * RR;
    1906              :             // Vertical displacement of win element wrt ref pt
    1907          347 :             YD = RWIN2.z - RREF2.z;
    1908              :             // Horizontal and vertical displacement ratio and position factor
    1909          347 :             Real64 XR = std::sqrt(std::abs(ASQ - YD * YD)) / RR;
    1910          347 :             Real64 YR = std::abs(YD / RR);
    1911          347 :             POSFAC = DayltgGlarePositionFactor(XR, YR);
    1912              :         }
    1913              :     }
    1914              : 
    1915       368021 :     hitIntObs = false;
    1916       368021 :     int IntWinHitNum = 0;   // Surface number of interior window that is intersected
    1917       368021 :     bool hitIntWin = false; // Ray from ref pt passes through interior window
    1918       368021 :     TVISIntWinDisk = 0.0;   // Init Value
    1919       368021 :     TVISIntWin = 0.0;
    1920              : 
    1921       368021 :     Vector3<Real64> HitPtIntWin = {0.0, 0.0, 0.0};
    1922       368021 :     auto const &surf = s_surf->Surface(IWin);
    1923       368021 :     if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    1924              :         // Look up the TDD:DOME object
    1925            0 :         int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
    1926              :         // Unshaded visible transmittance of TDD for a single ray from sky/ground element
    1927            0 :         TVISB = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    1928              : 
    1929              :     } else { // Regular window
    1930       368021 :         if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    1931              :             // Vis trans of glass for COSB incidence angle
    1932       368021 :             TVISB = Window::POLYF(COSB, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    1933              :         } else {
    1934              :             // Complex fenestration needs to use different equation for visible transmittance.  That will be calculated later
    1935              :             // in the code since it depends on different incoming directions.  For now, just put zero to differentiate from
    1936              :             // regular windows
    1937            0 :             TVISB = 0.0;
    1938              :         }
    1939       368021 :         if (extWinType == ExtWinType::AdjZone) {
    1940            0 :             int zoneNum = 0;
    1941            0 :             if (CalledFrom == CalledFor::RefPoint) {
    1942            0 :                 zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    1943            0 :             } else if (CalledFrom == CalledFor::MapPoint) {
    1944            0 :                 assert(MapNum > 0);
    1945            0 :                 zoneNum = dl->illumMaps(MapNum).zoneIndex;
    1946              :             }
    1947              :             // Does ray pass through an interior window in zone (ZoneNum) containing the ref point?
    1948            0 :             for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    1949            0 :                 auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    1950            0 :                 for (int IntWin = thisSpace.WindowSurfaceFirst; IntWin <= thisSpace.WindowSurfaceLast; ++IntWin) {
    1951            0 :                     auto const &surfIntWin = s_surf->Surface(IntWin);
    1952              :                     // in develop this was Surface(IntWin).Class == SurfaceClass::Window && Surface(IntWin).ExtBoundCond >= 1
    1953            0 :                     if (surfIntWin.ExtBoundCond < 1) continue;
    1954              : 
    1955            0 :                     if (s_surf->Surface(surfIntWin.ExtBoundCond).Zone != surf.Zone) continue;
    1956              : 
    1957            0 :                     hitIntWin = PierceSurface(state, IntWin, RREF, Ray, HitPtIntWin);
    1958            0 :                     if (hitIntWin) {
    1959            0 :                         IntWinHitNum = IntWin;
    1960            0 :                         COSBIntWin = dot(surfIntWin.OutNormVec, Ray);
    1961            0 :                         if (COSBIntWin <= 0.0) {
    1962            0 :                             hitIntWin = false;
    1963            0 :                             IntWinHitNum = 0;
    1964            0 :                             continue;
    1965              :                         }
    1966            0 :                         TVISIntWin = Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWin.Construction).TransVisBeamCoef);
    1967            0 :                         TVISB *= TVISIntWin;
    1968            0 :                         break; // Ray passes thru interior window; exit from DO loop
    1969              :                     }
    1970              :                 }
    1971              :             } // End of loop over surfaces in zone ZoneNum
    1972              : 
    1973            0 :             if (!hitIntWin) {
    1974              :                 // Ray does not pass through an int win in ZoneNum. Therefore, it hits the opaque part
    1975              :                 // of a surface between ref point in ZoneNum and ext win element in adjacent zone.
    1976            0 :                 hitIntObs = true;
    1977              :             }
    1978              :         } // End of check if this is an ext win in an adjacent zone
    1979              :     }     // End of check if TDD:Diffuser or regular exterior window or complex fenestration
    1980              : 
    1981              :     // Check for interior obstructions
    1982       368021 :     if (extWinType == ExtWinType::InZone && !hitIntObs) {
    1983              :         // Check for obstruction between reference point and window element
    1984              :         // Returns hitIntObs = true iff obstruction is hit
    1985              :         // (Example of interior obstruction is a wall in an L-shaped room that lies
    1986              :         // between reference point and window.)
    1987       368021 :         hitIntObs = DayltgHitInteriorObstruction(state, IWin, RREF, RWIN);
    1988              :     }
    1989              : 
    1990       368021 :     if (extWinType == ExtWinType::AdjZone && IntWinHitNum > 0 && !hitIntObs) {
    1991              :         // Check for obstruction between ref point and interior window through which ray passes
    1992            0 :         hitIntObs = DayltgHitInteriorObstruction(state, IntWinHitNum, RREF, HitPtIntWin);
    1993            0 :         if (!hitIntObs) {
    1994              :             // Check for obstruction between intersection point on int window and ext win element
    1995            0 :             hitIntObs = DayltgHitBetWinObstruction(state, IntWinHitNum, IWin, HitPtIntWin, RWIN);
    1996              :         }
    1997              :     }
    1998       368021 :     if (CalledFrom == CalledFor::RefPoint) {
    1999              :         // Glare calculations only done for regular reference points, not for maps
    2000       346971 :         if (!hitIntObs) {
    2001       346971 :             if (extWinType == ExtWinType::InZone || (extWinType == ExtWinType::AdjZone && hitIntWin)) {
    2002              :                 // Increment solid angle subtended by portion of window above ref pt
    2003       346971 :                 surfWin.refPts(iRefPoint).solidAng += DOMEGA;
    2004       346971 :                 dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAng += DOMEGA;
    2005              :                 // Increment position-factor-modified solid angle
    2006       346971 :                 surfWin.refPts(iRefPoint).solidAngWtd += DOMEGA * POSFAC;
    2007       346971 :                 dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAngWtd += DOMEGA * POSFAC;
    2008              :             }
    2009              :         }
    2010              :     }
    2011       368021 :     if (hitIntObs) ObTrans = 0.0;
    2012              : 
    2013       368021 :     hitExtObs = false;
    2014       368021 :     if (!hitIntObs) {
    2015              :         // No interior obstruction was hit.
    2016              :         // Check for exterior obstructions between window element and sky/ground.
    2017              :         // Get product of transmittances of obstructions hit by ray.
    2018              :         // ObTrans = 1.0 will be returned if no exterior obstructions are hit.
    2019              : 
    2020       368021 :         if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    2021              :             // the IHR (now HourOfDay) here is/was not correct, this is outside of hour loop
    2022              :             // the hour is used to query schedule for transmission , not sure what to do
    2023              :             // it will work for detailed and never did work correctly before.
    2024       368021 :             ObTrans = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin2, RWIN2, Ray);
    2025       368021 :             if (ObTrans < 1.0) hitExtObs = true;
    2026              :         } else {
    2027              :             // Transmittance from exterior obstruction surfaces is calculated here. This needs to be done for each timestep
    2028              :             // in order to account for changes in exterior surface transmittances
    2029            0 :             int CplxFenState = surfWin.ComplexFen.CurrentState;
    2030            0 :             auto &complexWinDayltgGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CplxFenState);
    2031            0 :             int NReflSurf = 0; // Number of blocked beams for complex fenestration
    2032            0 :             if (CalledFrom == CalledFor::RefPoint) {
    2033            0 :                 NReflSurf = complexWinDayltgGeom.RefPoint(iRefPoint).NReflSurf(WinEl);
    2034            0 :             } else if (CalledFrom == CalledFor::MapPoint) {
    2035            0 :                 NReflSurf = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).NReflSurf(WinEl);
    2036              :             }
    2037              :             int RayIndex;
    2038            0 :             for (int ICplxFen = 1; ICplxFen <= NReflSurf; ++ICplxFen) {
    2039            0 :                 if (CalledFrom == CalledFor::RefPoint) {
    2040            0 :                     RayIndex = complexWinDayltgGeom.RefPoint(iRefPoint).RefSurfIndex(ICplxFen, WinEl);
    2041            0 :                 } else if (CalledFrom == CalledFor::MapPoint) {
    2042            0 :                     RayIndex = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefSurfIndex(ICplxFen, WinEl);
    2043              :                 }
    2044            0 :                 Vector3<Real64> RayVector = state.dataBSDFWindow->ComplexWind(IWin).Geom(CplxFenState).sInc(RayIndex);
    2045              :                 // It will get product of all transmittances
    2046            0 :                 TransBeam = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin, RWIN, RayVector);
    2047              :                 // IF (TransBeam > 0.0d0) ObTrans = TransBeam
    2048            0 :                 if (CalledFrom == CalledFor::RefPoint) {
    2049            0 :                     complexWinDayltgGeom.RefPoint(iRefPoint).TransOutSurf(ICplxFen, WinEl) = TransBeam;
    2050            0 :                 } else if (CalledFrom == CalledFor::MapPoint) {
    2051            0 :                     complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).TransOutSurf(ICplxFen, WinEl) = TransBeam;
    2052              :                 }
    2053            0 :             }
    2054              :             // This will avoid obstruction multiplier calculations for non-CFS window
    2055            0 :             ObTrans = 0.0;
    2056              :         }
    2057              :     }
    2058              : 
    2059       368021 :     if (s_surf->CalcSolRefl && PHRAY < 0.0 && ObTrans > 1.0e-6) {
    2060              :         // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
    2061              :         // by the ray. This effect is given by the ratio SkyObstructionMult =
    2062              :         // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
    2063              :         // This ratio is calculated for an isotropic sky.
    2064              :         // Ground point hit by the ray:
    2065            0 :         Real64 Alfa = std::acos(-Ray.z);
    2066            0 :         Real64 Beta = std::atan2(Ray.y, Ray.x);
    2067              :         // Distance between ground hit point and proj'n of center of window element onto ground (m)
    2068            0 :         Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
    2069            0 :         Vector3<Real64> GroundHitPt = {RWIN2.x + HorDis * std::cos(Beta), RWIN2.y + HorDis * std::sin(Beta), s_surf->GroundLevelZ};
    2070              : 
    2071            0 :         SkyObstructionMult =
    2072            0 :             CalcObstrMultiplier(state, GroundHitPt, DataSurfaces::AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    2073            0 :     } // End of check if solar reflection calculation is in effect
    2074       368021 : } // FigureDayltgCoeffsAtPointsForWindowElements()
    2075              : 
    2076            0 : void InitializeCFSDaylighting(EnergyPlusData &state,
    2077              :                               int const daylightCtrlNum,       // Current daylighting control number
    2078              :                               int const IWin,                  // Complex fenestration number
    2079              :                               int const NWX,                   // Number of horizontal divisions
    2080              :                               int const NWY,                   // Number of vertical divisions
    2081              :                               Vector3<Real64> const &RefPoint, // reference point coordinates
    2082              :                               int const NRefPts,               // Number of reference points
    2083              :                               int const iRefPoint,             // Reference points counter
    2084              :                               CalledFor const CalledFrom,
    2085              :                               int const MapNum)
    2086              : {
    2087              :     // SUBROUTINE INFORMATION:
    2088              :     //       AUTHOR         Simon Vidanovic
    2089              :     //       DATE WRITTEN   April 2013
    2090              : 
    2091              :     // PURPOSE OF THIS SUBROUTINE:
    2092              :     // For incoming BSDF window direction calculates whether bin is coming from sky, ground or reflected surface.
    2093              :     // Routine also calculates intersection points with ground and exterior reflection surfaces.
    2094            0 :     auto &dl = state.dataDayltg;
    2095            0 :     auto &s_surf = state.dataSurface;
    2096              : 
    2097              :     // Object Data
    2098            0 :     DataBSDFWindow::BSDFDaylghtPosition elPos; // altitude and azimuth of intersection element
    2099            0 :     Vector Vec;                                // temporary vector variable
    2100              : 
    2101            0 :     int NumOfWinEl = NWX * NWY; // Number of window elements
    2102              : 
    2103            0 :     auto &surf = s_surf->Surface(IWin);
    2104            0 :     Real64 DWX = surf.Width / NWX;  // Window element width
    2105            0 :     Real64 DWY = surf.Height / NWY; // Window element height
    2106              : 
    2107            0 :     int zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    2108            0 :     Real64 AZVIEW = (dl->daylightControl(daylightCtrlNum).ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth +
    2109            0 :                      state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) *
    2110            0 :                     Constant::DegToRad;
    2111              : 
    2112              :     // Perform necessary calculations for window coordinates and vectors.  This will be used to calculate centroids for
    2113              :     // each window element
    2114            0 :     Vector3<Real64> W1 = {0.0, 0.0, 0.0};
    2115            0 :     Vector3<Real64> W2 = {0.0, 0.0, 0.0};
    2116            0 :     Vector3<Real64> W3 = {0.0, 0.0, 0.0};
    2117              : 
    2118            0 :     if (surf.Sides == 4) {
    2119            0 :         W3 = surf.Vertex(2);
    2120            0 :         W2 = surf.Vertex(3);
    2121            0 :         W1 = surf.Vertex(4);
    2122            0 :     } else if (surf.Sides == 3) {
    2123            0 :         W3 = surf.Vertex(2);
    2124            0 :         W2 = surf.Vertex(3);
    2125            0 :         W1 = surf.Vertex(1);
    2126              :     }
    2127              : 
    2128            0 :     Vector3<Real64> W21 = W1 - W2;
    2129            0 :     W21 /= surf.Height;
    2130            0 :     Vector3<Real64> W23 = W3 - W2;
    2131            0 :     W23 /= surf.Width;
    2132            0 :     Vector3<Real64> WNorm = surf.lcsz;
    2133              : 
    2134            0 :     Real64 WinElArea = DWX * DWY;
    2135            0 :     if (surf.Sides == 3) {
    2136            0 :         WinElArea *= std::sqrt(1.0 - pow_2(dot(W21, W23)));
    2137              :     }
    2138              : 
    2139            0 :     auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
    2140              : 
    2141            0 :     if (CalledFrom == CalledFor::MapPoint) {
    2142              : 
    2143            0 :         if (!allocated(complexWin.IlluminanceMap)) {
    2144            0 :             complexWin.IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
    2145              :         }
    2146              : 
    2147            0 :         AllocateForCFSRefPointsGeometry(complexWin.IlluminanceMap(iRefPoint, MapNum), NumOfWinEl);
    2148              : 
    2149            0 :     } else if (CalledFrom == CalledFor::RefPoint) {
    2150            0 :         if (!allocated(complexWin.RefPoint)) {
    2151            0 :             complexWin.RefPoint.allocate(NRefPts);
    2152              :         }
    2153              : 
    2154            0 :         AllocateForCFSRefPointsGeometry(complexWin.RefPoint(iRefPoint), NumOfWinEl);
    2155              :     }
    2156              : 
    2157              :     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    2158              :     //! Allocation for each complex fenestration state reference points
    2159              :     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    2160            0 :     if (!allocated(complexWin.DaylghtGeom)) {
    2161            0 :         complexWin.DaylghtGeom.allocate(state.dataBSDFWindow->ComplexWind(IWin).NumStates);
    2162              :     }
    2163              : 
    2164              :     // Calculation needs to be performed for each state
    2165            0 :     for (int CurFenState = 1; CurFenState <= complexWin.NumStates; ++CurFenState) {
    2166              :         // number of incident basis directions for current state
    2167            0 :         int NBasis = complexWin.Geom(CurFenState).Inc.NBasis;
    2168              :         // number of outgoing basis directions for current state
    2169            0 :         int NTrnBasis = complexWin.Geom(CurFenState).Trn.NBasis;
    2170              : 
    2171            0 :         if (CalledFrom == CalledFor::MapPoint) {
    2172            0 :             if ((int)dl->illumMaps.size() > 0) {
    2173              :                 // illuminance map for each state
    2174            0 :                 if (!allocated(complexWin.DaylghtGeom(CurFenState).IlluminanceMap)) {
    2175            0 :                     complexWin.DaylghtGeom(CurFenState).IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
    2176              :                 }
    2177              : 
    2178            0 :                 AllocateForCFSRefPointsState(
    2179            0 :                     state, complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum), NumOfWinEl, NBasis, NTrnBasis);
    2180              : 
    2181            0 :                 InitializeCFSStateData(state,
    2182            0 :                                        complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum),
    2183              :                                        complexWin.IlluminanceMap(iRefPoint, MapNum),
    2184              :                                        daylightCtrlNum,
    2185              :                                        IWin,
    2186              :                                        RefPoint,
    2187              :                                        CurFenState,
    2188              :                                        NBasis,
    2189              :                                        NTrnBasis,
    2190              :                                        AZVIEW,
    2191              :                                        NWX,
    2192              :                                        NWY,
    2193              :                                        W2,
    2194              :                                        W21,
    2195              :                                        W23,
    2196              :                                        DWX,
    2197              :                                        DWY,
    2198              :                                        WNorm,
    2199              :                                        WinElArea);
    2200              :             }
    2201              : 
    2202            0 :         } else if (CalledFrom == CalledFor::RefPoint) {
    2203            0 :             if (!allocated(complexWin.DaylghtGeom(CurFenState).RefPoint)) {
    2204            0 :                 complexWin.DaylghtGeom(CurFenState).RefPoint.allocate(NRefPts);
    2205              :             }
    2206              : 
    2207            0 :             AllocateForCFSRefPointsState(state, complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint), NumOfWinEl, NBasis, NTrnBasis);
    2208              : 
    2209            0 :             InitializeCFSStateData(state,
    2210            0 :                                    complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint),
    2211              :                                    complexWin.RefPoint(iRefPoint),
    2212              :                                    daylightCtrlNum,
    2213              :                                    IWin,
    2214              :                                    RefPoint,
    2215              :                                    CurFenState,
    2216              :                                    NBasis,
    2217              :                                    NTrnBasis,
    2218              :                                    AZVIEW,
    2219              :                                    NWX,
    2220              :                                    NWY,
    2221              :                                    W2,
    2222              :                                    W21,
    2223              :                                    W23,
    2224              :                                    DWX,
    2225              :                                    DWY,
    2226              :                                    WNorm,
    2227              :                                    WinElArea);
    2228              :         }
    2229              :     }
    2230            0 : } // InitializeCFSDaylighting()
    2231              : 
    2232            0 : void InitializeCFSStateData(EnergyPlusData &state,
    2233              :                             DataBSDFWindow::BSDFRefPoints &StateRefPoint,
    2234              :                             DataBSDFWindow::BSDFRefPointsGeomDescr &DaylghtGeomDescr,
    2235              :                             [[maybe_unused]] int const daylightCtrlNum, // Current daylighting control number
    2236              :                             int const iWin,
    2237              :                             Vector3<Real64> const &RefPoint, // reference point
    2238              :                             int const CurFenState,
    2239              :                             int const NBasis,
    2240              :                             int const NTrnBasis,
    2241              :                             Real64 const AZVIEW,
    2242              :                             int const NWX,
    2243              :                             int const NWY,
    2244              :                             Vector3<Real64> const &W2,
    2245              :                             Vector3<Real64> const &W21,
    2246              :                             Vector3<Real64> const &W23,
    2247              :                             Real64 const DWX,
    2248              :                             Real64 const DWY,
    2249              :                             Vector3<Real64> const &WNorm, // unit vector from window (point towards outside)
    2250              :                             Real64 const WinElArea)
    2251              : {
    2252              :     // SUBROUTINE INFORMATION:
    2253              :     //       AUTHOR         Simon Vidanovic
    2254              :     //       DATE WRITTEN   June 2013
    2255              : 
    2256              :     // PURPOSE OF THIS SUBROUTINE:
    2257              :     // Initialize daylight state data for current
    2258            0 :     auto &s_surf = state.dataSurface;
    2259              : 
    2260              :     // SUBROUTINE LOCAL VARIABLES
    2261              :     int curWinEl;
    2262              :     bool hit;
    2263              :     int TotHits;
    2264              :     Real64 DotProd; // Temporary variable for manipulating dot product .dot.
    2265              :     int NSky;
    2266              :     int NGnd;
    2267              :     int NReflSurf;
    2268              :     int MaxTotHits;
    2269              :     Real64 LeastHitDsq; // dist^2 from window element center to hit point
    2270              :     Real64 HitDsq;
    2271              :     Real64 TransRSurf;
    2272              :     int J;
    2273              : 
    2274            0 :     Vector3<Real64> RWin;
    2275            0 :     Vector3<Real64> V;
    2276            0 :     Vector3<Real64> GroundHitPt;
    2277              : 
    2278              :     // temporary arrays for surfaces
    2279              :     // Each complex fenestration state can have different number of basis elements
    2280              :     // This is the reason for making these temporary arrays local
    2281            0 :     Array1D_int TmpSkyInd(NBasis, 0);                              // Temporary sky index list
    2282            0 :     Array1D_int TmpGndInd(NBasis, 0);                              // Temporary gnd index list
    2283            0 :     Array1D<Real64> TmpGndMultiplier(NBasis, 0.0);                 // Temporary ground obstruction multiplier
    2284            0 :     Array1D_int TmpRfSfInd(NBasis, 0);                             // Temporary RefSurfIndex
    2285            0 :     Array1D_int TmpRfRyNH(NBasis, 0);                              // Temporary RefRayNHits
    2286            0 :     Array2D_int TmpHSurfNo(s_surf->TotSurfaces, NBasis, 0);        // Temporary HitSurfNo
    2287            0 :     Array2D<Real64> TmpHSurfDSq(s_surf->TotSurfaces, NBasis, 0.0); // Temporary HitSurfDSq
    2288              : 
    2289              :     // Object Data
    2290            0 :     Vector3<Real64> Centroid;                                                                       // current window element centroid
    2291            0 :     Vector3<Real64> HitPt;                                                                          // surface hit point
    2292            0 :     Array1D<Vector3<Real64>> TmpGndPt(NBasis, Vector3<Real64>(0.0, 0.0, 0.0));                      // Temporary ground intersection list
    2293            0 :     Array2D<Vector3<Real64>> TmpHitPt(s_surf->TotSurfaces, NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary HitPt
    2294              : 
    2295            0 :     CFSRefPointPosFactor(state, RefPoint, StateRefPoint, iWin, CurFenState, NTrnBasis, AZVIEW);
    2296              : 
    2297            0 :     auto const &surf = s_surf->Surface(iWin);
    2298              : 
    2299            0 :     curWinEl = 0;
    2300              :     // loop through window elements. This will calculate sky, ground and reflection bins for each window element
    2301            0 :     for (int IX = 1; IX <= NWX; ++IX) {
    2302            0 :         for (int IY = 1; IY <= NWY; ++IY) {
    2303              : 
    2304            0 :             ++curWinEl;
    2305              : 
    2306              :             // centroid coordinates for current window element
    2307            0 :             Centroid = W2 + (double(IX) - 0.5) * W23 * DWX + (double(IY) - 0.5) * W21 * DWY;
    2308            0 :             RWin = Centroid;
    2309              : 
    2310            0 :             CFSRefPointSolidAngle(state, RefPoint, RWin, WNorm, StateRefPoint, DaylghtGeomDescr, iWin, CurFenState, NTrnBasis, curWinEl, WinElArea);
    2311              : 
    2312            0 :             NSky = 0;
    2313            0 :             NGnd = 0;
    2314            0 :             NReflSurf = 0;
    2315            0 :             MaxTotHits = 0;
    2316              :             // Calculation of potential surface obstruction for each incoming direction
    2317            0 :             for (int IRay = 1; IRay <= NBasis; ++IRay) {
    2318              : 
    2319            0 :                 hit = false;
    2320            0 :                 TotHits = 0;
    2321            0 :                 for (int JSurf = 1; JSurf <= s_surf->TotSurfaces; ++JSurf) {
    2322            0 :                     auto &surf2 = s_surf->Surface(JSurf);
    2323              : 
    2324              :                     // the following test will cycle on anything except exterior surfaces and shading surfaces
    2325            0 :                     if (surf2.HeatTransSurf && surf2.ExtBoundCond != ExternalEnvironment) continue;
    2326              :                     //  skip the base surface containing the window and any other subsurfaces of that surface
    2327            0 :                     if (JSurf == surf.BaseSurf || surf2.BaseSurf == surf.BaseSurf) continue;
    2328              :                     //  skip surfaces that face away from the window
    2329            0 :                     DotProd = dot(state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), surf2.NewellSurfaceNormalVector);
    2330            0 :                     if (DotProd >= 0) continue;
    2331            0 :                     hit = PierceSurface(state, JSurf, Centroid, state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), HitPt);
    2332            0 :                     if (!hit) continue; // Miss: Try next surface
    2333            0 :                     if (TotHits == 0) {
    2334              :                         // First hit for this ray
    2335            0 :                         TotHits = 1;
    2336            0 :                         ++NReflSurf;
    2337            0 :                         TmpRfSfInd(NReflSurf) = IRay;
    2338            0 :                         TmpRfRyNH(NReflSurf) = 1;
    2339            0 :                         TmpHSurfNo(1, NReflSurf) = JSurf;
    2340            0 :                         TmpHitPt(1, NReflSurf) = HitPt;
    2341            0 :                         V = HitPt - Centroid;                // vector array from window ctr to hit pt
    2342            0 :                         LeastHitDsq = V.magnitude_squared(); // dist^2 window ctr to hit pt
    2343            0 :                         TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    2344            0 :                         if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
    2345            0 :                             TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance, treat it here as completely transparent
    2346              :                         } else {
    2347            0 :                             TransRSurf = 0.0;
    2348              :                         }
    2349              :                     } else {
    2350            0 :                         V = HitPt - Centroid;
    2351            0 :                         HitDsq = V.magnitude_squared();
    2352            0 :                         if (HitDsq >= LeastHitDsq) {
    2353            0 :                             if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
    2354            0 :                                 J = TotHits + 1;
    2355            0 :                                 if (TotHits > 1) {
    2356            0 :                                     for (int I = 2; I <= TotHits; ++I) {
    2357            0 :                                         if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
    2358            0 :                                             J = I;
    2359            0 :                                             break;
    2360              :                                         }
    2361              :                                     }
    2362            0 :                                     if (!surf2.HeatTransSurf && surf2.shadowSurfSched == nullptr) {
    2363              :                                         //  The new hit is opaque, so we can drop all the hits further away
    2364            0 :                                         TmpHSurfNo(J, NReflSurf) = JSurf;
    2365            0 :                                         TmpHitPt(J, NReflSurf) = HitPt;
    2366            0 :                                         TmpHSurfDSq(J, NReflSurf) = HitDsq;
    2367            0 :                                         TotHits = J;
    2368              :                                     } else {
    2369              :                                         //  The new hit is scheduled (presumed transparent), so keep the more distant hits
    2370              :                                         //     Note that all the hists in the list will be transparent except the last,
    2371              :                                         //       which may be either transparent or opaque
    2372            0 :                                         if (TotHits >= J) {
    2373            0 :                                             for (int I = TotHits; I >= J; --I) {
    2374            0 :                                                 TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    2375            0 :                                                 TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    2376            0 :                                                 TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    2377              :                                             }
    2378            0 :                                             TmpHSurfNo(J, NReflSurf) = JSurf;
    2379            0 :                                             TmpHitPt(J, NReflSurf) = HitPt;
    2380            0 :                                             TmpHSurfDSq(J, NReflSurf) = HitDsq;
    2381            0 :                                             ++TotHits;
    2382              :                                         }
    2383              :                                     } // if (.NOT.Surface(JSurf)%HeatTransSurf .AND. Surface(JSurf)%SchedShadowSurfIndex == 0)  then
    2384              :                                 }     // if (TotHits > 1) then
    2385              :                             }         // if (TransRSurf  > 0.0d0) then
    2386              :                         } else {      // if (HitDsq >= LeastHitDsq) then
    2387              :                             //  A new closest hit.  If it is opaque, drop the current hit list,
    2388              :                             //    otherwise add it at the front
    2389            0 :                             LeastHitDsq = HitDsq;
    2390            0 :                             if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
    2391            0 :                                 TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
    2392            0 :                                 for (int I = TotHits; I >= 1; --I) {
    2393            0 :                                     TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    2394            0 :                                     TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    2395            0 :                                     TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    2396            0 :                                     ++TotHits;
    2397              :                                 }
    2398            0 :                             } else {
    2399            0 :                                 TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
    2400            0 :                                 TotHits = 1;
    2401              :                             }
    2402            0 :                             TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
    2403            0 :                             TmpHitPt(1, NReflSurf) = HitPt;
    2404            0 :                             TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    2405              :                         }
    2406              :                     }
    2407              :                 } // do JSurf = 1, TotSurfaces
    2408            0 :                 if (TotHits <= 0) {
    2409            0 :                     auto const &sIncRay = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay);
    2410              :                     // This ray reached the sky or ground unobstructed
    2411            0 :                     if (sIncRay.z < 0.0) {
    2412              :                         // A ground ray
    2413            0 :                         ++NGnd;
    2414            0 :                         TmpGndInd(NGnd) = IRay;
    2415            0 :                         TmpGndPt(NGnd).x = Centroid.x - (sIncRay.x / sIncRay.z) * Centroid.z;
    2416            0 :                         TmpGndPt(NGnd).y = Centroid.y - (sIncRay.y / sIncRay.z) * Centroid.z;
    2417            0 :                         TmpGndPt(NGnd).z = 0.0;
    2418              : 
    2419              :                         // for solar reflectance calculations, need to precalculate obstruction multipliers
    2420            0 :                         if (s_surf->CalcSolRefl) {
    2421            0 :                             GroundHitPt = TmpGndPt(NGnd);
    2422            0 :                             TmpGndMultiplier(NGnd) =
    2423            0 :                                 CalcObstrMultiplier(state, GroundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    2424              :                         }
    2425              :                     } else {
    2426              :                         // A sky ray
    2427            0 :                         ++NSky;
    2428            0 :                         TmpSkyInd(NSky) = IRay;
    2429              :                     }
    2430              :                 } else {
    2431              :                     // Save the number of hits for this ray
    2432            0 :                     TmpRfRyNH(NReflSurf) = TotHits;
    2433              :                 }
    2434            0 :                 MaxTotHits = max(MaxTotHits, TotHits);
    2435              :             } // do IRay = 1, ComplexWind(IWin)%Geom(CurFenState)%Inc%NBasis
    2436              : 
    2437              :             // Fill up state data for current window element data
    2438            0 :             StateRefPoint.NSky(curWinEl) = NSky;
    2439            0 :             StateRefPoint.SkyIndex({1, NSky}, curWinEl) = TmpSkyInd({1, NSky});
    2440              : 
    2441            0 :             StateRefPoint.NGnd(curWinEl) = NGnd;
    2442            0 :             StateRefPoint.GndIndex({1, NGnd}, curWinEl) = TmpGndInd({1, NGnd});
    2443            0 :             StateRefPoint.GndPt({1, NGnd}, curWinEl) = TmpGndPt({1, NGnd});
    2444            0 :             StateRefPoint.GndObstrMultiplier({1, NGnd}, curWinEl) = TmpGndMultiplier({1, NGnd});
    2445              : 
    2446            0 :             StateRefPoint.NReflSurf(curWinEl) = NReflSurf;
    2447            0 :             StateRefPoint.RefSurfIndex({1, NReflSurf}, curWinEl) = TmpRfSfInd({1, NReflSurf});
    2448            0 :             StateRefPoint.RefRayNHits({1, NReflSurf}, curWinEl) = TmpRfRyNH({1, NReflSurf});
    2449            0 :             StateRefPoint.HitSurfNo({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfNo({1, MaxTotHits}, {1, NReflSurf});
    2450            0 :             StateRefPoint.HitSurfDSq({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfDSq({1, MaxTotHits}, {1, NReflSurf});
    2451            0 :             StateRefPoint.HitPt({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHitPt({1, MaxTotHits}, {1, NReflSurf});
    2452              :         } // do IY = 1, NWY
    2453              :     }     // do IX = 1, NWX
    2454            0 : }
    2455              : 
    2456            0 : void AllocateForCFSRefPointsState(
    2457              :     [[maybe_unused]] EnergyPlusData &state, DataBSDFWindow::BSDFRefPoints &StateRefPoint, int const NumOfWinEl, int const NBasis, int const NTrnBasis)
    2458              : {
    2459              :     // SUBROUTINE INFORMATION:
    2460              :     //       AUTHOR         Simon Vidanovic
    2461              :     //       DATE WRITTEN   June 2013
    2462              : 
    2463              :     // PURPOSE OF THIS SUBROUTINE:
    2464              :     // Memory allocation for complex fenestration systems reference points geometry
    2465            0 :     auto &s_surf = state.dataSurface;
    2466              : 
    2467            0 :     if (!allocated(StateRefPoint.NSky)) {
    2468            0 :         StateRefPoint.NSky.allocate(NumOfWinEl);
    2469            0 :         StateRefPoint.NSky = 0;
    2470              :     }
    2471              : 
    2472            0 :     if (!allocated(StateRefPoint.SkyIndex)) {
    2473            0 :         StateRefPoint.SkyIndex.allocate(NBasis, NumOfWinEl);
    2474            0 :         StateRefPoint.SkyIndex = 0;
    2475              :     }
    2476              : 
    2477            0 :     if (!allocated(StateRefPoint.NGnd)) {
    2478            0 :         StateRefPoint.NGnd.allocate(NumOfWinEl);
    2479            0 :         StateRefPoint.NGnd = 0;
    2480              :     }
    2481              : 
    2482            0 :     if (!allocated(StateRefPoint.GndIndex)) {
    2483            0 :         StateRefPoint.GndIndex.allocate(NBasis, NumOfWinEl);
    2484            0 :         StateRefPoint.GndIndex = 0;
    2485              :     }
    2486              : 
    2487            0 :     if (!allocated(StateRefPoint.GndPt)) {
    2488            0 :         StateRefPoint.GndPt.allocate(NBasis, NumOfWinEl);
    2489            0 :         StateRefPoint.GndPt = Vector(0.0, 0.0, 0.0);
    2490              :     }
    2491              : 
    2492            0 :     if (!allocated(StateRefPoint.GndObstrMultiplier)) {
    2493            0 :         StateRefPoint.GndObstrMultiplier.allocate(NBasis, NumOfWinEl);
    2494            0 :         StateRefPoint.GndObstrMultiplier = 0.0;
    2495              :     }
    2496              : 
    2497            0 :     if (!allocated(StateRefPoint.NReflSurf)) {
    2498            0 :         StateRefPoint.NReflSurf.allocate(NumOfWinEl);
    2499            0 :         StateRefPoint.NReflSurf = 0;
    2500              :     }
    2501              : 
    2502            0 :     if (!allocated(StateRefPoint.RefSurfIndex)) {
    2503            0 :         StateRefPoint.RefSurfIndex.allocate(NBasis, NumOfWinEl);
    2504            0 :         StateRefPoint.RefSurfIndex = 0;
    2505              :     }
    2506              : 
    2507            0 :     if (!allocated(StateRefPoint.TransOutSurf)) {
    2508            0 :         StateRefPoint.TransOutSurf.allocate(NBasis, NumOfWinEl);
    2509            0 :         StateRefPoint.TransOutSurf = 1.0;
    2510              :     }
    2511              : 
    2512            0 :     if (!allocated(StateRefPoint.RefRayNHits)) {
    2513            0 :         StateRefPoint.RefRayNHits.allocate(NBasis, NumOfWinEl);
    2514            0 :         StateRefPoint.RefRayNHits = 0;
    2515              :     }
    2516              : 
    2517            0 :     if (!allocated(StateRefPoint.HitSurfNo)) {
    2518            0 :         StateRefPoint.HitSurfNo.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
    2519            0 :         StateRefPoint.HitSurfNo = 0;
    2520              :     }
    2521              : 
    2522            0 :     if (!allocated(StateRefPoint.HitSurfDSq)) {
    2523            0 :         StateRefPoint.HitSurfDSq.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
    2524            0 :         StateRefPoint.HitSurfDSq = 0.0;
    2525              :     }
    2526              : 
    2527            0 :     if (!allocated(StateRefPoint.HitPt)) {
    2528            0 :         StateRefPoint.HitPt.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
    2529            0 :         StateRefPoint.HitPt = Vector(0.0, 0.0, 0.0);
    2530              :     }
    2531              : 
    2532            0 :     if (!allocated(StateRefPoint.RefPointIndex)) {
    2533            0 :         StateRefPoint.RefPointIndex.allocate(NumOfWinEl);
    2534            0 :         StateRefPoint.RefPointIndex = 0;
    2535              :     }
    2536              : 
    2537            0 :     if (!allocated(StateRefPoint.RefPointIntersection)) {
    2538            0 :         StateRefPoint.RefPointIntersection.allocate(NTrnBasis);
    2539            0 :         StateRefPoint.RefPointIntersection = false;
    2540              :     }
    2541              : 
    2542            0 :     if (!allocated(StateRefPoint.RefPtIntPosFac)) {
    2543            0 :         StateRefPoint.RefPtIntPosFac.allocate(NTrnBasis);
    2544            0 :         StateRefPoint.RefPtIntPosFac = 0.0;
    2545              :     }
    2546            0 : }
    2547              : 
    2548            0 : void AllocateForCFSRefPointsGeometry(DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointsGeomDescr, int const NumOfWinEl)
    2549              : {
    2550              :     // SUBROUTINE INFORMATION:
    2551              :     //       AUTHOR         Simon Vidanovic
    2552              :     //       DATE WRITTEN   June 2013
    2553              : 
    2554              :     // PURPOSE OF THIS SUBROUTINE:
    2555              :     // Memory allocation for complex fenestration systems reference points geometry
    2556              : 
    2557              :     // SUBROUTINE LOCAL VARIABLES
    2558              : 
    2559            0 :     if (!allocated(RefPointsGeomDescr.SolidAngle)) {
    2560            0 :         RefPointsGeomDescr.SolidAngle.allocate(NumOfWinEl);
    2561            0 :         RefPointsGeomDescr.SolidAngle = 0.0;
    2562              :     }
    2563              : 
    2564            0 :     if (!allocated(RefPointsGeomDescr.SolidAngleVec)) {
    2565            0 :         RefPointsGeomDescr.SolidAngleVec.allocate(NumOfWinEl);
    2566            0 :         RefPointsGeomDescr.SolidAngleVec = Vector(0.0, 0.0, 0.0);
    2567              :     }
    2568            0 : }
    2569              : 
    2570            0 : void CFSRefPointSolidAngle(EnergyPlusData &state,
    2571              :                            Vector3<Real64> const &RefPoint,
    2572              :                            Vector3<Real64> const &RWin,
    2573              :                            Vector3<Real64> const &WNorm,
    2574              :                            DataBSDFWindow::BSDFRefPoints &RefPointMap,
    2575              :                            DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointGeomMap,
    2576              :                            int const iWin,
    2577              :                            int const CurFenState,
    2578              :                            int const NTrnBasis,
    2579              :                            int const curWinEl,
    2580              :                            Real64 const WinElArea)
    2581              : {
    2582              :     // SUBROUTINE INFORMATION:
    2583              :     //       AUTHOR         Simon Vidanovic
    2584              :     //       DATE WRITTEN   June 2013
    2585              : 
    2586              :     // PURPOSE OF THIS SUBROUTINE:
    2587              :     // Calculate position factor for given reference point.
    2588              : 
    2589              :     // calculate vector from center of window element to the current reference point
    2590            0 :     Vector3<Real64> Ray = RefPoint - RWin;
    2591              : 
    2592              :     // figure out outgoing beam direction from current reference point
    2593            0 :     Real64 BestMatch = 0.0;
    2594            0 :     for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
    2595            0 :         Vector3<Real64> const &V = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn(iTrnRay);
    2596            0 :         Real64 temp = dot(Ray, V);
    2597            0 :         if (temp > BestMatch) {
    2598            0 :             BestMatch = temp;
    2599            0 :             RefPointMap.RefPointIndex(curWinEl) = iTrnRay;
    2600              :         }
    2601              :     }
    2602              : 
    2603              :     // calculate solid view angle
    2604            0 :     Real64 Dist = Ray.magnitude();
    2605            0 :     Vector3<Real64> RayNorm = Ray / (-Dist);
    2606            0 :     RefPointGeomMap.SolidAngleVec(curWinEl) = RayNorm;
    2607            0 :     Real64 CosB = dot(WNorm, RayNorm);
    2608            0 :     RefPointGeomMap.SolidAngle(curWinEl) = WinElArea * CosB / (Dist * Dist);
    2609            0 : }
    2610              : 
    2611            0 : void CFSRefPointPosFactor(EnergyPlusData &state,
    2612              :                           Vector3<Real64> const &RefPoint,
    2613              :                           DataBSDFWindow::BSDFRefPoints &RefPointMap,
    2614              :                           int const iWin,
    2615              :                           int const CurFenState,
    2616              :                           int const NTrnBasis,
    2617              :                           Real64 const AZVIEW)
    2618              : {
    2619              :     // SUBROUTINE INFORMATION:
    2620              :     //       AUTHOR         Simon Vidanovic
    2621              :     //       DATE WRITTEN   June 2013
    2622              : 
    2623              :     // PURPOSE OF THIS SUBROUTINE:
    2624              :     // Calculate position factor for given reference point.
    2625              : 
    2626            0 :     auto const &sTrn = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn;
    2627            0 :     for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
    2628            0 :         Vector3<Real64> V = sTrn(iTrnRay);
    2629            0 :         V.negate();
    2630              : 
    2631            0 :         Vector3<Real64> InterPoint;
    2632              : 
    2633            0 :         bool hit = PierceSurface(state, iWin, RefPoint, V, InterPoint);
    2634            0 :         if (hit) {
    2635            0 :             RefPointMap.RefPointIntersection(iTrnRay) = true;
    2636              : 
    2637            0 :             DataBSDFWindow::BSDFDaylghtPosition elPos = WindowComplexManager::DaylghtAltAndAzimuth(V);
    2638              : 
    2639            0 :             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - elPos.Azimuth) + 0.001);
    2640            0 :             Real64 YR = std::tan(elPos.Altitude + 0.001);
    2641            0 :             RefPointMap.RefPtIntPosFac(iTrnRay) = DayltgGlarePositionFactor(XR, YR);
    2642              :         }
    2643            0 :     }
    2644            0 : } // CFSRefPointPosFactor()
    2645              : 
    2646            0 : Real64 CalcObstrMultiplier(EnergyPlusData &state,
    2647              :                            Vector3<Real64> const &GroundHitPt, // Coordinates of point that ray hits ground (m)
    2648              :                            int const AltSteps,                 // Number of steps in altitude angle for solar reflection calc
    2649              :                            int const AzimSteps                 // Number of steps in azimuth angle of solar reflection calc
    2650              : )
    2651              : {
    2652              : 
    2653              :     // SUBROUTINE INFORMATION:
    2654              :     //       AUTHOR         Simon Vidanovic
    2655              :     //       DATE WRITTEN   April 2013, refactor from legacy code by Fred Winklemann
    2656              : 
    2657              :     // PURPOSE OF THIS SUBROUTINE:
    2658              :     // collect code to do obstruction multiplier from ground point
    2659              : 
    2660              :     // METHODOLOGY EMPLOYED:
    2661              :     // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
    2662              :     // Divide hemisphere centered at ground hit point into elements of altitude Phi and
    2663              :     // azimuth Theta and create upward-going ground ray unit vector at each Phi,Theta pair.
    2664              :     // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
    2665              : 
    2666              :     // Locals
    2667            0 :     auto const &dl = state.dataDayltg;
    2668            0 :     auto const &s_surf = state.dataSurface;
    2669              : 
    2670              :     bool hitObs; // True iff obstruction is hit
    2671              : 
    2672            0 :     Vector3<Real64> URay;     // Unit vector in (Phi,Theta) direction
    2673            0 :     Vector3<Real64> ObsHitPt; // Unit vector in (Phi,Theta) direction
    2674              : 
    2675            0 :     assert(AzimSteps <= DataSurfaces::AzimAngStepsForSolReflCalc);
    2676              : 
    2677            0 :     Real64 DPhi = Constant::PiOvr2 / (AltSteps / 2.0); // Phi increment (radians)
    2678            0 :     Real64 DTheta = Constant::Pi / AzimSteps;          // Theta increment (radians)
    2679              : 
    2680              :     // Tuned Precompute Phi trig table
    2681            0 :     if (AltSteps != dl->AltSteps_last) {
    2682            0 :         for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
    2683            0 :             Real64 Phi = (IPhi - 0.5) * DPhi;
    2684            0 :             dl->cos_Phi[IPhi] = std::cos(Phi);
    2685            0 :             dl->sin_Phi[IPhi] = std::sin(Phi);
    2686              :         }
    2687            0 :         dl->AltSteps_last = AltSteps;
    2688              :     }
    2689              :     // Tuned Precompute Theta trig table
    2690            0 :     if (AzimSteps != dl->AzimSteps_last) {
    2691            0 :         for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
    2692            0 :             Real64 Theta = (ITheta - 0.5) * DTheta;
    2693            0 :             dl->cos_Theta[ITheta] = std::cos(Theta);
    2694            0 :             dl->sin_Theta[ITheta] = std::sin(Theta);
    2695              :         }
    2696            0 :         dl->AzimSteps_last = AzimSteps;
    2697              :     }
    2698              : 
    2699            0 :     Real64 SkyGndObs = 0.0;   // Obstructed sky irradiance at a ground point
    2700            0 :     Real64 SkyGndUnObs = 0.0; // Unobstructed sky irradiance at a ground point
    2701              : 
    2702              :     // Altitude loop
    2703            0 :     for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
    2704            0 :         Real64 sinPhi = dl->sin_Phi[IPhi]; // sinPhi
    2705            0 :         Real64 cosPhi = dl->cos_Phi[IPhi]; // cosPhi
    2706              : 
    2707              :         // Third component of ground ray unit vector in (Theta,Phi) direction
    2708            0 :         URay.z = sinPhi;
    2709            0 :         Real64 dOmegaGnd = cosPhi * DTheta * DPhi; // Solid angle element of ray from ground point (steradians)
    2710              :         // Cosine of angle of incidence of ground ray on ground plane
    2711            0 :         Real64 CosIncAngURay = sinPhi;
    2712            0 :         Real64 IncAngSolidAngFac = CosIncAngURay * dOmegaGnd / Constant::Pi; // CosIncAngURay*dOmegaGnd/Pi
    2713              :         // Azimuth loop
    2714            0 :         for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
    2715            0 :             URay.x = cosPhi * dl->cos_Theta[ITheta];
    2716            0 :             URay.y = cosPhi * dl->sin_Theta[ITheta];
    2717            0 :             SkyGndUnObs += IncAngSolidAngFac;
    2718              :             // Does this ground ray hit an obstruction?
    2719            0 :             hitObs = false;
    2720            0 :             if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    2721              : 
    2722            0 :                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    2723            0 :                     hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
    2724            0 :                     if (hitObs) break;
    2725              :                 }
    2726              : 
    2727              :             } else { // Surface octree search
    2728              : 
    2729              :                 // Lambda function for the octree to test for surface hit
    2730            0 :                 auto surfaceHit = [&GroundHitPt, &hitObs, &URay, &ObsHitPt](SurfaceData const &surface) -> bool {
    2731            0 :                     if (surface.IsShadowPossibleObstruction) {
    2732            0 :                         hitObs = PierceSurface(surface, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
    2733            0 :                         return hitObs;
    2734              :                     } else {
    2735            0 :                         return false;
    2736              :                     }
    2737            0 :                 };
    2738              : 
    2739              :                 // Check octree surface candidates until a hit is found, if any
    2740            0 :                 Vector3<Real64> const URay_inv(SurfaceOctreeCube::safe_inverse(URay));
    2741            0 :                 state.dataHeatBalMgr->surfaceOctree.hasSurfaceRayIntersectsCube(GroundHitPt, URay, URay_inv, surfaceHit);
    2742            0 :             }
    2743              : 
    2744            0 :             if (hitObs) continue; // Obstruction hit
    2745              :             // Sky is hit
    2746            0 :             SkyGndObs += IncAngSolidAngFac;
    2747              :         } // End of azimuth loop
    2748              :     }     // End of altitude loop
    2749              : 
    2750              :     // in case ground point is surrounded by obstructions (SkyGndUnObs == 0), then multiplier will be equal to zero
    2751              :     // This should not happen anyway because in that case ray would not be able to reach ground point
    2752            0 :     return (SkyGndUnObs != 0.0) ? (SkyGndObs / SkyGndUnObs) : 0.0;
    2753            0 : } // CalcObstrMultiplier()
    2754              : 
    2755       862072 : void FigureDayltgCoeffsAtPointsForSunPosition(
    2756              :     EnergyPlusData &state,
    2757              :     int const daylightCtrlNum, // Daylighting control index
    2758              :     int const iRefPoint,
    2759              :     int const iXelement,
    2760              :     int const NWX, // Number of window elements in x direction for dayltg calc
    2761              :     int const iYelement,
    2762              :     int const NWY,   // Number of window elements in y direction for dayltg calc
    2763              :     int const WinEl, // Current window element counter
    2764              :     int const IWin,
    2765              :     int const IWin2,
    2766              :     int const iHour,
    2767              :     int &ISunPos,
    2768              :     Real64 const SkyObstructionMult,
    2769              :     Vector3<Real64> const &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
    2770              :     Vector3<Real64> const &Ray,   // Unit vector along ray from reference point to window element
    2771              :     Real64 const PHRAY,           // Altitude of ray from reference point to window element (radians)
    2772              :     int const LSHCAL,             // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    2773              :     int const InShelfSurf,        // Inside daylighting shelf surface number
    2774              :     Real64 const COSB,            // Cosine of angle between window outward normal and ray from reference point to window element
    2775              :     Real64 const ObTrans,         // Product of solar transmittances of exterior obstructions hit by ray from reference point through a window element
    2776              :     Real64 const TVISB,           // Visible transmittance of window for COSB angle of incidence (times light well efficiency, if appropriate)
    2777              :     Real64 const DOMEGA,          // Solid angle subtended by window element wrt reference point (steradians)
    2778              :     int const ICtrl,              // Window control counter
    2779              :     WinShadingType const ShType,  // Window shading type
    2780              :     [[maybe_unused]] int const BlNum, // Window blind number
    2781              :     Real64 const THRAY,               // Azimuth of ray from reference point to window element (radians)
    2782              :     Vector3<Real64> const &WNORM2,    // Unit vector normal to window
    2783              :     ExtWinType const extWinType,      // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    2784              :     int const IConst,                 // Construction counter
    2785              :     Real64 const AZVIEW,              // Azimuth of view vector in absolute coord system for glare calculation (radians)
    2786              :     Vector3<Real64> const &RREF2,     // Location of virtual reference point in absolute coordinate system
    2787              :     bool const hitIntObs,             // True iff interior obstruction hit
    2788              :     bool const hitExtObs,             // True iff ray from ref pt to ext win hits an exterior obstruction
    2789              :     CalledFor const CalledFrom,       // indicate  which type of routine called this routine
    2790              :     Real64 TVISIntWin,                // Visible transmittance of int win at COSBIntWin for light from ext win
    2791              :     Real64 &TVISIntWinDisk,           // Visible transmittance of int win at COSBIntWin for sun
    2792              :     int const MapNum)
    2793              : {
    2794              : 
    2795              :     // SUBROUTINE INFORMATION:
    2796              :     //       AUTHOR         B. Griffith
    2797              :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    2798              : 
    2799              :     // PURPOSE OF THIS SUBROUTINE:
    2800              :     // collect code for calculations sun position aspects for daylighting coefficients
    2801              : 
    2802              :     // METHODOLOGY EMPLOYED:
    2803              :     // switch as need to serve both reference points and map points based on calledFrom
    2804       862072 :     auto &s_surf = state.dataSurface;
    2805       862072 :     if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
    2806              : 
    2807       453596 :     auto &dl = state.dataDayltg;
    2808       453596 :     auto &s_mat = state.dataMaterial;
    2809              : 
    2810              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2811       453596 :     Vector3<Real64> RREF{0.0, 0.0, 0.0}; // Location of a reference point in absolute coordinate system //Autodesk Was used uninitialized:
    2812              : 
    2813              :     Real64 ObTransDisk;     // Product of solar transmittances of exterior obstructions hit by ray from reference point to sun
    2814              :     Real64 LumAtHitPtFrSun; // Luminance at hit point of obstruction by reflection of direct light from sun (cd/m2)
    2815              : 
    2816              :     Real64 TVISS; // Direct solar visible transmittance of window at given angle of incidence
    2817              :     //  (times light well efficiency, if appropriate)
    2818              :     Real64 XAVWL; // XAVWL*TVISS is contribution of window luminance from solar disk (cd/m2)
    2819              : 
    2820              :     bool hitObs;          // True iff obstruction is hit
    2821              :     Real64 ObsVisRefl;    // Visible reflectance of obstruction
    2822              :     Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by
    2823              : 
    2824              :     Real64 SpecReflectance; // Specular reflectance of a reflecting surface
    2825              :     Real64 TVisRefl;        // Bare window vis trans for reflected beam
    2826              :     //  (times light well efficiency, if appropriate)
    2827              :     Real64 PHSUNrefl; // Altitude angle of reflected sun (radians)
    2828              :     Real64 THSUNrefl; // Azimuth anggle of reflected sun (radians)
    2829              : 
    2830              :     Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
    2831              :     //  exterior window element or between ref pt and sun
    2832              :     Real64 TVisIntWinMult;     // Interior window vis trans multiplier for ext win in adjacent zone
    2833              :     Real64 TVisIntWinDiskMult; // Interior window vis trans solar disk multiplier for ext win in adj zone
    2834              :     Real64 WindowSolidAngleDaylightPoint;
    2835              : 
    2836       453596 :     ++ISunPos;
    2837              : 
    2838              :     // Altitude of sun (degrees)
    2839       453596 :     dl->sunAngles = dl->sunAnglesHr[iHour];
    2840              : 
    2841              :     // First time through, call routine to calculate inter-reflected illuminance
    2842              :     // at reference point and luminance of window with shade, screen or blind.
    2843              : 
    2844              :     // Rob/TH - Not sure whether this call is necessary for interior zones with interior windows only.
    2845              :     //  new code would be -
    2846              :     // IF (LSHCAL == 1 .AND. ExtWinType /= AdjZoneExtWin) CALL DayltgInterReflectedIllum(ISunPos,IHR,ZoneNum,IWin2)
    2847       453596 :     int enclNum = 0; // enclosure index
    2848       453596 :     int zoneNum = 0; // zone index
    2849       453596 :     if (CalledFrom == CalledFor::RefPoint) {
    2850       137846 :         zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    2851       137846 :         enclNum = dl->daylightControl(daylightCtrlNum).enclIndex;
    2852       315750 :     } else if (CalledFrom == CalledFor::MapPoint) {
    2853       315750 :         assert(MapNum > 0);
    2854       315750 :         zoneNum = dl->illumMaps(MapNum).zoneIndex;
    2855       315750 :         enclNum = dl->illumMaps(MapNum).enclIndex;
    2856              :     }
    2857       453596 :     if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    2858       453596 :         if (LSHCAL == 1) DayltgInterReflectedIllum(state, ISunPos, iHour, enclNum, IWin2);
    2859              :     } else {
    2860            0 :         if (LSHCAL == 1) DayltgInterReflectedIllumComplexFenestration(state, IWin2, WinEl, iHour, daylightCtrlNum, iRefPoint, CalledFrom, MapNum);
    2861            0 :         if (COSB <= 0.0) return;
    2862            0 :         DayltgDirectIllumComplexFenestration(state, IWin, WinEl, iHour, iRefPoint, CalledFrom, MapNum);
    2863              :         // Call direct sun component only once since calculation is done for entire window
    2864            0 :         if (WinEl == (NWX * NWY)) {
    2865            0 :             DayltgDirectSunDiskComplexFenestration(state, IWin2, iHour, iRefPoint, WinEl, AZVIEW, CalledFrom, MapNum);
    2866              :         }
    2867            0 :         return;
    2868              :     }
    2869              : 
    2870              :     // Daylighting shelf simplification:  The shelf completely blocks all view of the window,
    2871              :     // only interrelflected illumination is allowed (see DayltgInterReflectedIllum above).
    2872              :     // Everything else in this loop has to do with direct luminance from the window.
    2873       453596 :     if (InShelfSurf > 0) return;
    2874              : 
    2875       453596 :     if (COSB <= 0.0) return;
    2876              : 
    2877       453596 :     auto &surfWin = s_surf->SurfaceWindow(IWin);
    2878              : 
    2879       453596 :     Illums XDirIllum;
    2880       453596 :     Illums XAvgWinLum;
    2881       453596 :     Real64 const Ray_3 = Ray.z;
    2882       453596 :     Real64 const DOMEGA_Ray_3 = DOMEGA * Ray_3;
    2883              : 
    2884              :     // Add contribution of this window element to glare and to
    2885              :     // direct illuminance at reference point
    2886              : 
    2887              :     // The I,J,K indices for sky and sun components of direct illuminance
    2888              :     // (EDIRSK, EDIRSU) and average window luminance (AVWLSK, AVWLSU) are:
    2889              :     // I=1 for clear sky, =2 Clear turbid, =3 Intermediate, =4 Overcast;
    2890              :     // J=1 for bare window, =2 for window with shade or fixed slat-angle blind;
    2891              :     // K = sun position index.
    2892              : 
    2893              :     // ----- CASE I -- BARE WINDOW (no shading device)
    2894              : 
    2895              :     // Beam solar and sky solar reflected from nearest obstruction.
    2896              :     // In the following hitIntObs == false  ==> no interior obstructions hit, and
    2897              :     //                  hitExtObs == true  ==> one or more exterior obstructions hit.
    2898       453596 :     if (s_surf->CalcSolRefl && !hitIntObs && hitExtObs) {
    2899              :         int NearestHitSurfNum;        // Surface number of nearest obstruction
    2900            0 :         Vector3<Real64> NearestHitPt; // Hit point of ray on nearest obstruction
    2901              :         // One or more exterior obstructions was hit; get contribution of reflection
    2902              :         // from nearest obstruction.
    2903              :         // Find obstruction whose hit point is closest to this ray's window element
    2904            0 :         DayltgClosestObstruction(state, RWIN2, Ray, NearestHitSurfNum, NearestHitPt);
    2905            0 :         if (NearestHitSurfNum > 0) {
    2906              : 
    2907              :             // Beam solar reflected from nearest obstruction
    2908              : 
    2909            0 :             LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, iHour, Ray, NearestHitSurfNum, NearestHitPt);
    2910            0 :             dl->avgWinLum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * TVISB;
    2911            0 :             if (PHRAY >= 0.0) dl->dirIllum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * DOMEGA_Ray_3 * TVISB;
    2912              : 
    2913              :             // Sky solar reflected from nearest obstruction
    2914              : 
    2915            0 :             int const ObsConstrNum = s_surf->SurfActiveConstruction(NearestHitSurfNum);
    2916            0 :             if (ObsConstrNum > 0) {
    2917              :                 // Exterior building surface is nearest hit
    2918            0 :                 if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
    2919              :                     // Obstruction is not a window, i.e., is an opaque surface
    2920            0 :                     ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
    2921              :                 } else {
    2922              :                     // Obstruction is a window; assume it is bare
    2923            0 :                     ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
    2924              :                 }
    2925              :             } else {
    2926              :                 // Shadowing surface is nearest hit
    2927            0 :                 if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
    2928              :                     // This is a daylighting shelf, for which reflection is separately calculated
    2929            0 :                     ObsVisRefl = 0.0;
    2930              :                 } else {
    2931            0 :                     ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
    2932            0 :                     if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
    2933            0 :                         ObsVisRefl += s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
    2934            0 :                                       state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
    2935              :                 }
    2936              :             }
    2937              :             // Surface number to use when obstruction is a shadowing surface
    2938            0 :             int NearestHitSurfNumX = NearestHitSurfNum;
    2939              :             // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    2940              :             // The following gets the correct side of a shadowing surface for reflection.
    2941            0 :             if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
    2942            0 :                 if (dot(Ray, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
    2943              :             }
    2944            0 :             if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
    2945            0 :                 state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    2946            0 :                 SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    2947            0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
    2948              :             } else {
    2949            0 :                 SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    2950            0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, iHour, NearestHitSurfNumX) / Constant::Pi;
    2951              :             }
    2952            0 :             assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
    2953            0 :             auto &gilsk = dl->horIllum[iHour];
    2954            0 :             auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
    2955            0 :             auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
    2956              : 
    2957            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    2958            0 :                 XAvgWinLum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum;
    2959            0 :                 avwlsk.sky[iSky] += XAvgWinLum.sky[iSky] * TVISB;
    2960            0 :                 if (PHRAY >= 0.0) {
    2961            0 :                     XDirIllum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum * DOMEGA_Ray_3;
    2962            0 :                     edirsk.sky[iSky] += XDirIllum.sky[iSky] * TVISB;
    2963              :                 }
    2964              :             }
    2965              :         }
    2966            0 :     } // End of check if solar reflection calculation is in effect
    2967              : 
    2968       453596 :     if (ObTrans > 1.e-6) {
    2969              :         // Ray did not hit an obstruction or the transmittance product of hit obstructions is non-zero.
    2970              :         // Contribution of sky or ground luminance in cd/m2
    2971       453596 :         if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
    2972              :             // Make all transmitted light diffuse for a TDD with a bare diffuser
    2973            0 :             assert(equal_dimensions(dl->avgWinLum, dl->winLum));
    2974            0 :             assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
    2975            0 :             auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
    2976            0 :             auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
    2977            0 :             auto &wlumsk = dl->winLum(iHour)[iWinCover_Bare];
    2978            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    2979            0 :                 avwlsk.sky[iSky] += wlumsk.sky[iSky];
    2980            0 :                 if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3;
    2981              :             }
    2982              : 
    2983            0 :             dl->avgWinLum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun;
    2984            0 :             dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += dl->winLum(iHour)[iWinCover_Bare].sunDisk;
    2985              : 
    2986            0 :             if (PHRAY > 0.0) dl->dirIllum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun * DOMEGA_Ray_3;
    2987              :         } else {                         // Bare window
    2988       453596 :             Vector3<Real64> GroundHitPt; // Coordinates of point that ray hits ground (m)
    2989              :             // Tuned Hoisted operations out of loop and linear indexing
    2990       453596 :             if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
    2991            0 :                 Real64 Alfa = std::acos(-Ray_3);
    2992            0 :                 Real64 const Ray_1(Ray.x);
    2993            0 :                 Real64 const Ray_2(Ray.y);
    2994              :                 //                    Beta = std::atan2( Ray_2, Ray_1 ); //Unused Tuning below eliminated use
    2995              :                 // Distance between ground hit point and proj'n of center of window element onto ground (m)
    2996            0 :                 Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
    2997            0 :                 GroundHitPt.z = s_surf->GroundLevelZ;
    2998              :                 // Tuned Replaced by below: sqrt is faster than sincos
    2999              :                 //                    GroundHitPt( 1 ) = RWIN2( 1 ) + HorDis * std::cos( Beta );
    3000              :                 //                    GroundHitPt( 2 ) = RWIN2( 2 ) + HorDis * std::sin( Beta );
    3001            0 :                 Real64 const Ray_r(std::sqrt(square(Ray_1) + square(Ray_2)));
    3002            0 :                 if (Ray_r > 0.0) {
    3003            0 :                     HorDis /= Ray_r;
    3004            0 :                     GroundHitPt.x = RWIN2.x + HorDis * Ray_1;
    3005            0 :                     GroundHitPt.y = RWIN2.y + HorDis * Ray_2;
    3006              :                 } else { // Treat as angle==0
    3007            0 :                     GroundHitPt.x = RWIN2.x + HorDis;
    3008            0 :                     GroundHitPt.y = RWIN2.y;
    3009              :                 }
    3010              :             }
    3011       453596 :             Real64 const GILSK_mult((state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * ObTrans * SkyObstructionMult);
    3012       453596 :             Real64 const TVISB_ObTrans(TVISB * ObTrans);
    3013       453596 :             Real64 const AVWLSU_add(TVISB_ObTrans * dl->horIllum[iHour].sun * (state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi));
    3014       453596 :             Vector3<Real64> const SUNCOS_iHour(s_surf->SurfSunCosHourly(iHour));
    3015       453596 :             assert(equal_dimensions(dl->dirIllum, dl->avgWinLum));
    3016       453596 :             auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
    3017       453596 :             auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
    3018              : 
    3019      2267980 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3020      1814384 :                 if (PHRAY > 0.0) {                                                                     // Ray heads upward to sky
    3021      1531440 :                     Real64 ELUM = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), THRAY, PHRAY); // Sky or ground luminance (cd/m2)
    3022      1531440 :                     XDirIllum.sky[iSky] = ELUM * DOMEGA_Ray_3;
    3023      1531440 :                     Real64 DEDIR = XDirIllum.sky[iSky] * TVISB; // Illuminance contribution at reference point from window element (lux)
    3024      1531440 :                     edirsk.sky[iSky] += DEDIR * ObTrans;
    3025      1531440 :                     avwlsk.sky[iSky] += ELUM * TVISB_ObTrans;
    3026      1531440 :                     XAvgWinLum.sky[iSky] = ELUM * ObTrans;
    3027              :                 } else { // PHRAY <= 0.
    3028              :                     // Ray heads downward to ground.
    3029              :                     // Contribution from sky diffuse reflected from ground
    3030       282944 :                     XAvgWinLum.sky[iSky] = dl->horIllum[iHour].sky[iSky] * GILSK_mult;
    3031       282944 :                     avwlsk.sky[iSky] += TVISB * XAvgWinLum.sky[iSky];
    3032              :                     // Contribution from beam solar reflected from ground (beam reaching ground point
    3033              :                     // can be obstructed [SunObstructionMult < 1.0] if CalcSolRefl = .TRUE.)
    3034              :                 } // End of check if ray is going up or down
    3035              :             }     // for (iSky)
    3036              : 
    3037       453596 :             if (PHRAY <= 0.0) {
    3038              :                 // SunObstructionMult = 1.0; //Tuned
    3039        70736 :                 if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
    3040              :                     // Sun reaches ground point if vector from this point to the sun is unobstructed
    3041            0 :                     hitObs = false;
    3042            0 :                     Vector3<Real64> ObsHitPt; // Coordinates of hit point on an obstruction (m)
    3043            0 :                     for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    3044            0 :                         hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, SUNCOS_iHour, ObsHitPt);
    3045            0 :                         if (hitObs) break;
    3046              :                     }
    3047              :                     // if ( hitObs ) SunObstructionMult = 0.0;
    3048            0 :                     if (!hitObs) dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
    3049            0 :                 } else {
    3050        70736 :                     dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
    3051              :                 }
    3052              :             } // (PHRAY <= 0.0)
    3053       453596 :         }
    3054              :     } // End of check if bare window or TDD:DIFFUSER
    3055              : 
    3056              :     // Illuminance from beam solar (without interior reflection)
    3057              :     // Just run this once on the last pass
    3058       453596 :     if (iXelement == NWX && iYelement == NWY) { // Last pass
    3059              : 
    3060              :         // Beam solar reaching reference point directly without exterior reflection
    3061              : 
    3062              :         // Unit vector from ref. pt. to sun
    3063         2066 :         Vector3<Real64> RAYCOS;
    3064         2066 :         RAYCOS.x = dl->sunAngles.cosPhi * std::cos(dl->sunAngles.theta);
    3065         2066 :         RAYCOS.y = dl->sunAngles.cosPhi * std::sin(dl->sunAngles.theta);
    3066         2066 :         RAYCOS.z = dl->sunAngles.sinPhi;
    3067              : 
    3068              :         // Is sun on front side of exterior window?
    3069         2066 :         Real64 COSI = dot(WNORM2, RAYCOS); // Cosine of angle between direct sun and window outward normal
    3070              :         bool hit;                          // True if ray from ref point thru window element hits an obstruction
    3071              :         bool hitWin;                       // True if ray passes thru window
    3072         2066 :         Vector3<Real64> HP;
    3073         2066 :         if (COSI > 0.0) {
    3074              : 
    3075              :             // Does RAYCOS pass thru exterior window? HP is point that RAYCOS intersects window plane.
    3076         1454 :             hitWin = PierceSurface(state, IWin2, RREF2, RAYCOS, HP);
    3077              :             // True if ray from ref pt to sun hits an interior obstruction
    3078         1454 :             if (hitWin) {
    3079          173 :                 bool hitIntObsDisk = false;
    3080          173 :                 if (extWinType == ExtWinType::InZone) {
    3081              :                     // Check for interior obstructions between reference point and HP.
    3082          173 :                     hitIntObsDisk = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
    3083              :                 }
    3084          173 :                 ObTransDisk = 0.0; // Init value
    3085              :                 // Init flag for vector from RP to sun passing through interior window
    3086          173 :                 bool hitIntWinDisk = false;
    3087          173 :                 if (extWinType == ExtWinType::AdjZone) { // This block is for RPs in zones with interior windows
    3088              :                     // adjacent to zones with exterior windows
    3089              :                     // Does RAYCOS pass through interior window in zone containing RP?
    3090              :                     // Loop over zone surfaces looking for interior windows between reference point and sun
    3091              :                     // Surface number of int window intersected by ray betw ref pt and sun
    3092              :                     int IntWinDiskHitNum;
    3093              :                     // Intersection point on an interior window for ray from ref pt to sun (m)
    3094            0 :                     Vector3<Real64> HitPtIntWinDisk;
    3095            0 :                     auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
    3096            0 :                     for (int const spaceNum : thisZone.spaceIndexes) {
    3097            0 :                         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    3098            0 :                         for (int IntWinDisk = thisSpace.WindowSurfaceFirst, IntWinDisk_end = thisSpace.WindowSurfaceLast;
    3099            0 :                              IntWinDisk <= IntWinDisk_end;
    3100              :                              ++IntWinDisk) {
    3101            0 :                             auto const &surfIntWinDisk = s_surf->Surface(IntWinDisk);
    3102            0 :                             if (surfIntWinDisk.ExtBoundCond < 1) continue;
    3103              : 
    3104            0 :                             if (s_surf->Surface(surfIntWinDisk.ExtBoundCond).Zone != s_surf->Surface(IWin2).Zone) continue;
    3105              : 
    3106            0 :                             hitIntWinDisk = PierceSurface(state, IntWinDisk, RREF, RAYCOS, HitPtIntWinDisk);
    3107            0 :                             if (!hitIntWinDisk) continue;
    3108              : 
    3109            0 :                             IntWinDiskHitNum = IntWinDisk;
    3110            0 :                             COSBIntWin = dot(surfIntWinDisk.OutNormVec, RAYCOS);
    3111            0 :                             if (COSBIntWin <= 0.0) {
    3112            0 :                                 hitIntWinDisk = false;
    3113            0 :                                 IntWinDiskHitNum = 0;
    3114            0 :                                 continue;
    3115              :                             }
    3116            0 :                             TVISIntWinDisk =
    3117            0 :                                 Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWinDisk.Construction).TransVisBeamCoef);
    3118            0 :                             break;
    3119              :                         } // for (IntWinDisk)
    3120              :                     }     // for (spaceNum)
    3121              : 
    3122            0 :                     if (!hitIntWinDisk) { // Vector from RP to sun does not pass through interior window
    3123            0 :                         ObTransDisk = 0.0;
    3124            0 :                         hit = true; //! fcw Is this needed?
    3125              :                     }
    3126              : 
    3127              :                     // Check for interior obstructions between ref point and interior window
    3128            0 :                     hitIntObsDisk = false;
    3129            0 :                     if (hitIntWinDisk) {
    3130            0 :                         hitIntObsDisk = DayltgHitInteriorObstruction(state, IntWinDiskHitNum, RREF, HitPtIntWinDisk);
    3131              :                         // If no obstruction between RP and hit int win, check for obstruction
    3132              :                         // between int win and ext win
    3133            0 :                         if (!hitIntObsDisk) {
    3134            0 :                             hitIntObsDisk = DayltgHitBetWinObstruction(state, IntWinDiskHitNum, IWin2, HitPtIntWinDisk, HP);
    3135              :                         }
    3136              :                     }
    3137            0 :                     if (hitIntObsDisk) ObTransDisk = 0.0;
    3138            0 :                 } // case where RP is in zone with interior window adjacent to zone with exterior window
    3139              : 
    3140              :                 //                    hitExtObsDisk = false; //Unused Set but never used
    3141              :                 // RJH 08-25-07 hitIntWinDisk should not be reset to false here, and should be tested below.
    3142              :                 // This is to correct logic flaw causing direct solar to reach adjacent zone refpt
    3143              :                 // when vector to sun does not pass through interior window
    3144              :                 // hitIntWinDisk = false
    3145          173 :                 if (!hitIntObsDisk) { // No interior obstruction was hit
    3146              :                     // Net transmittance of exterior obstructions encountered by RAYCOS
    3147              :                     // ObTransDisk = 1.0 will be returned if no exterior obstructions are hit.
    3148          173 :                     ObTransDisk = DayltgHitObstruction(state, iHour, IWin2, RREF2, RAYCOS);
    3149              :                     //                        if ( ObTransDisk < 1.0 ) hitExtObsDisk = true; //Unused Set but never used
    3150              :                     // RJH 08-26-07 However, if this is a case of interior window
    3151              :                     // and vector to sun does not pass through interior window
    3152              :                     // then reset ObTransDisk to 0.0 since it is the key test for adding
    3153              :                     // contribution of sun to RP below.
    3154          173 :                     if ((extWinType == ExtWinType::AdjZone) && (!hitIntWinDisk)) {
    3155            0 :                         ObTransDisk = 0.0;
    3156              :                     }
    3157              :                 }
    3158              : 
    3159              :                 // PETER: need side wall mounted TDD to test this
    3160              :                 // PETER: probably need to replace RREF2 with RWIN2
    3161              :                 // PETER: need to check for interior obstructions too.
    3162              : 
    3163          173 :                 if (ObTransDisk > 1.e-6) {
    3164              : 
    3165              :                     // Sun reaches reference point;  increment illuminance.
    3166              :                     // Direct normal illuminance is normalized to 1.0
    3167              : 
    3168          173 :                     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
    3169              :                         // No beam is transmitted.  Takes care of TDD with a bare diffuser and all types of blinds.
    3170            0 :                         TVISS = 0.0;
    3171              :                     } else {
    3172              :                         // Beam transmittance for bare window and all types of blinds
    3173          173 :                         TVISS = Window::POLYF(COSI, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
    3174          173 :                                 surfWin.lightWellEff;
    3175          173 :                         if (extWinType == ExtWinType::AdjZone && hitIntWinDisk) TVISS *= TVISIntWinDisk;
    3176              :                     }
    3177              : 
    3178          173 :                     dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = RAYCOS.z * TVISS * ObTransDisk; // Bare window
    3179              : 
    3180          173 :                     Real64 transBmBmMult = 0.0;
    3181              : 
    3182          173 :                     if (ANY_BLIND(ShType)) {
    3183            0 :                         auto const &surfShade = s_surf->surfShades(IWin);
    3184            0 :                         auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    3185            0 :                         assert(matBlind != nullptr);
    3186              : 
    3187            0 :                         Real64 ProfAng = ProfileAngle(state, IWin, RAYCOS, matBlind->SlatOrientation);
    3188            0 :                         transBmBmMult = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
    3189            0 :                         dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
    3190              : 
    3191          173 :                     } else if (ShType == WinShadingType::ExtScreen) {
    3192              :                         //                          pass angle from sun to window normal here using PHSUN and THSUN from above and surface angles
    3193              :                         //                          SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
    3194              :                         //                          SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
    3195            0 :                         auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
    3196            0 :                         assert(screen != nullptr);
    3197              : 
    3198            0 :                         Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    3199            0 :                         Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    3200              :                         int ip1, ip2, it1, it2;
    3201              :                         BilinearInterpCoeffs coeffs;
    3202            0 :                         Material::NormalizePhiTheta(phi, theta);
    3203            0 :                         Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    3204            0 :                         GetBilinearInterpCoeffs(
    3205            0 :                             phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    3206            0 :                         transBmBmMult = BilinearInterp(screen->btars[ip1][it1].BmTrans,
    3207            0 :                                                        screen->btars[ip1][it2].BmTrans,
    3208            0 :                                                        screen->btars[ip2][it1].BmTrans,
    3209            0 :                                                        screen->btars[ip2][it2].BmTrans,
    3210              :                                                        coeffs);
    3211              : 
    3212            0 :                         dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
    3213              :                     }
    3214              : 
    3215          173 :                     if (CalledFrom == CalledFor::RefPoint) {
    3216              :                         // Glare from solar disk
    3217              : 
    3218              :                         // Position factor for sun (note that AZVIEW is wrt y-axis and THSUN is wrt
    3219              :                         // x-axis of absolute coordinate system.
    3220          112 :                         Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
    3221          112 :                         Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
    3222              :                         Real64 POSFAC =
    3223          112 :                             DayltgGlarePositionFactor(XR, YR); // Position factor for a window element / ref point / view vector combination
    3224              : 
    3225          112 :                         WindowSolidAngleDaylightPoint = s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd;
    3226              : 
    3227          112 :                         if (POSFAC != 0.0 && WindowSolidAngleDaylightPoint > 0.000001) {
    3228              :                             // Increment window luminance.  Luminance of solar disk (cd/m2)
    3229              :                             // is 1.47*10^4*(direct normal solar illuminance) for direct normal solar
    3230              :                             // illuminance in lux (lumens/m2). For purposes of calculating daylight factors
    3231              :                             // direct normal solar illuminance = 1.0.
    3232              :                             // Solid angle subtended by sun is 0.000068 steradians
    3233              : 
    3234            0 :                             XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) / std::pow(WindowSolidAngleDaylightPoint, 0.8);
    3235            0 :                             dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = XAVWL * TVISS * ObTransDisk; // Bare window
    3236              : 
    3237            0 :                             if (ANY_BLIND(ShType)) {
    3238            0 :                                 dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
    3239            0 :                             } else if (ShType == WinShadingType::ExtScreen) {
    3240            0 :                                 dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
    3241              :                             }
    3242              :                         } // Position Factor
    3243              :                     }     // if (calledFrom == RefPt)
    3244              :                 }         // if (ObTransDisk > 1e-6) // Beam avoids all obstructions
    3245              :             }             // if (hitWin)
    3246              :         }                 // if (COSI > 0.0) // Sun on front side
    3247              : 
    3248              :         // Beam solar reaching reference point after beam-beam (specular) reflection from
    3249              :         // an exterior surface
    3250              : 
    3251         2066 :         if (s_surf->CalcSolRefl) {
    3252              :             // Receiving surface number corresponding this window
    3253            0 :             int RecSurfNum = s_surf->SurfShadowRecSurfNum(IWin2);
    3254            0 :             if (RecSurfNum > 0) { // interior windows do not apply
    3255            0 :                 if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
    3256              :                     bool hitRefl;              // True iff ray hits reflecting surface
    3257            0 :                     Vector3<Real64> HitPtRefl; // Point that ray hits reflecting surface
    3258            0 :                     Vector3<Real64> SunVecMir; // Sun ray mirrored in reflecting surface
    3259            0 :                     Vector3<Real64> ReflNorm;  // Normal vector to reflecting surface
    3260              :                     // This window has associated obstructions that could reflect beam onto the window
    3261            0 :                     for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end;
    3262              :                          ++loop) {
    3263            0 :                         int ReflSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop);
    3264            0 :                         int ReflSurfNumX = ReflSurfNum;
    3265              :                         // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    3266              :                         // The following gets the correct side of a shadowing surface for reflection.
    3267            0 :                         if (s_surf->Surface(ReflSurfNum).IsShadowing) {
    3268            0 :                             if (dot(RAYCOS, s_surf->Surface(ReflSurfNum).OutNormVec) < 0.0) ReflSurfNumX = ReflSurfNum + 1;
    3269              :                         }
    3270              :                         // Require that the surface can have specular reflection
    3271            0 :                         if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window || s_surf->SurfShadowGlazingFrac(ReflSurfNum) > 0.0) {
    3272            0 :                             ReflNorm = s_surf->Surface(ReflSurfNumX).OutNormVec;
    3273              :                             // Vector to sun that is mirrored in obstruction
    3274            0 :                             SunVecMir = RAYCOS - 2.0 * dot(RAYCOS, ReflNorm) * ReflNorm;
    3275              :                             // Skip if reflecting surface is not sunlit
    3276            0 :                             if (state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNumX) < 0.01) continue;
    3277              :                             // Skip if altitude angle of mirrored sun is negative since reflected sun cannot
    3278              :                             // reach reference point in this case
    3279            0 :                             if (SunVecMir.z <= 0.0) continue;
    3280              :                             // Cosine of incidence angle of reflected beam on window
    3281            0 :                             Real64 CosIncAngRec = dot(s_surf->Surface(IWin2).OutNormVec, SunVecMir);
    3282            0 :                             if (CosIncAngRec <= 0.0) continue;
    3283              :                             // Does ray from ref. pt. along SunVecMir pass through window?
    3284            0 :                             hitWin = PierceSurface(state, IWin2, RREF2, SunVecMir, HP);
    3285            0 :                             if (!hitWin) continue; // Ray did not pass through window
    3286              :                             // Check if this ray hits interior obstructions
    3287            0 :                             hit = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
    3288            0 :                             if (hit) continue; // Interior obstruction was hit
    3289              :                             // Does ray hit this reflecting surface?
    3290            0 :                             hitRefl = PierceSurface(state, ReflSurfNum, RREF2, SunVecMir, HitPtRefl);
    3291            0 :                             if (!hitRefl) continue; // Ray did not hit this reflecting surface
    3292            0 :                             Real64 ReflDistanceSq = distance_squared(HitPtRefl, RREF2);
    3293            0 :                             Real64 ReflDistance = std::sqrt(ReflDistanceSq);
    3294              :                             // Is ray from ref. pt. to reflection point (HitPtRefl) obstructed?
    3295            0 :                             bool hitObsRefl = false;
    3296            0 :                             Vector3<Real64> HitPtObs; // Hit point on obstruction
    3297            0 :                             for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
    3298            0 :                                  loop2 <= loop2_end;
    3299              :                                  ++loop2) {
    3300            0 :                                 int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
    3301            0 :                                 if (ObsSurfNum == ReflSurfNum || ObsSurfNum == s_surf->Surface(ReflSurfNum).BaseSurf) continue;
    3302            0 :                                 hitObs = PierceSurface(state, ObsSurfNum, RREF2, SunVecMir, ReflDistance, HitPtObs); // ReflDistance cutoff added
    3303            0 :                                 if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
    3304            0 :                                     if (distance_squared(HitPtObs, RREF2) < ReflDistanceSq) { // Distance squared from ref pt to reflection point
    3305            0 :                                         hitObsRefl = true;
    3306            0 :                                         break;
    3307              :                                     }
    3308              :                                 }
    3309              :                             }
    3310            0 :                             if (hitObsRefl) continue; // Obstruction closer than reflection pt. was hit; go to next obstruction
    3311              :                             // There is no obstruction for this ray between ref pt and hit pt on reflecting surface.
    3312              :                             // See if ray from hit pt on reflecting surface to original (unmirrored) sun position is obstructed
    3313            0 :                             hitObs = false;
    3314            0 :                             if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    3315              :                                 // Reflecting surface is a window.
    3316              :                                 // Receiving surface number for this reflecting window.
    3317            0 :                                 int ReflSurfRecNum = s_surf->SurfShadowRecSurfNum(ReflSurfNum);
    3318            0 :                                 if (ReflSurfRecNum > 0) {
    3319              :                                     // Loop over possible obstructions for this reflecting window
    3320            0 :                                     for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
    3321            0 :                                          loop2 <= loop2_end;
    3322              :                                          ++loop2) {
    3323              :                                         int const ObsSurfNum =
    3324            0 :                                             state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
    3325            0 :                                         hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
    3326            0 :                                         if (hitObs) break;
    3327              :                                     }
    3328              :                                 }
    3329              :                             } else {
    3330              :                                 // Reflecting surface is a building shade
    3331            0 :                                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    3332            0 :                                     if (ObsSurfNum == ReflSurfNum) continue;
    3333            0 :                                     hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
    3334            0 :                                     if (hitObs) break;
    3335              :                                 }
    3336              :                             } // End of check if reflector is a window or shadowing surface
    3337              : 
    3338            0 :                             if (hitObs) continue; // Obstruction hit between reflection hit point and sun; go to next obstruction
    3339              : 
    3340              :                             // No obstructions. Calculate reflected beam illuminance at ref. pt. from this reflecting surface.
    3341            0 :                             SpecReflectance = 0.0;
    3342            0 :                             Real64 CosIncAngRefl = std::abs(dot(RAYCOS, ReflNorm)); // Cos of angle of incidence of beam on reflecting surface
    3343            0 :                             if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    3344            0 :                                 int const ConstrNumRefl = s_surf->SurfActiveConstruction(ReflSurfNum);
    3345              :                                 SpecReflectance =
    3346            0 :                                     Window::POLYF(std::abs(CosIncAngRefl), state.dataConstruction->Construct(ConstrNumRefl).ReflSolBeamFrontCoef);
    3347              :                             }
    3348            0 :                             if (s_surf->Surface(ReflSurfNum).IsShadowing && s_surf->SurfShadowGlazingConstruct(ReflSurfNum) > 0)
    3349            0 :                                 SpecReflectance =
    3350            0 :                                     s_surf->SurfShadowGlazingFrac(ReflSurfNum) *
    3351            0 :                                     Window::POLYF(
    3352              :                                         std::abs(CosIncAngRefl),
    3353            0 :                                         state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(ReflSurfNum)).ReflSolBeamFrontCoef);
    3354            0 :                             TVisRefl = Window::POLYF(CosIncAngRec, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
    3355            0 :                                        surfWin.lightWellEff;
    3356            0 :                             dl->dirIllum(iHour)[iWinCover_Bare].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl; // Bare window
    3357              : 
    3358            0 :                             Real64 TransBmBmMultRefl = 0.0;
    3359            0 :                             if (ANY_BLIND(ShType)) {
    3360            0 :                                 auto const &surfShade = s_surf->surfShades(IWin);
    3361            0 :                                 auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    3362              : 
    3363            0 :                                 Real64 ProfAng = ProfileAngle(state, IWin, SunVecMir, matBlind->SlatOrientation);
    3364            0 :                                 TransBmBmMultRefl = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
    3365            0 :                                 dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
    3366            0 :                             } else if (ShType == WinShadingType::ExtScreen) {
    3367              :                                 // pass angle from sun to window normal here using PHSUN and THSUN from above and
    3368              :                                 // surface angles SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
    3369              :                                 // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
    3370            0 :                                 auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
    3371            0 :                                 assert(screen != nullptr);
    3372              : 
    3373            0 :                                 Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    3374            0 :                                 Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    3375              :                                 int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
    3376              :                                 BilinearInterpCoeffs coeffs;
    3377            0 :                                 Material::NormalizePhiTheta(phi, theta);
    3378            0 :                                 Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    3379            0 :                                 GetBilinearInterpCoeffs(
    3380            0 :                                     phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    3381              : 
    3382            0 :                                 TransBmBmMultRefl = BilinearInterp(screen->btars[ip1][it1].BmTrans,
    3383            0 :                                                                    screen->btars[ip1][it2].BmTrans,
    3384            0 :                                                                    screen->btars[ip2][it1].BmTrans,
    3385            0 :                                                                    screen->btars[ip2][it2].BmTrans,
    3386              :                                                                    coeffs);
    3387            0 :                                 dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
    3388              :                             } // End of check if window has a blind or screen
    3389              : 
    3390              :                             // Glare from reflected solar disk
    3391              : 
    3392            0 :                             PHSUNrefl = SunVecMir.z;
    3393            0 :                             THSUNrefl = std::atan2(SunVecMir.y, SunVecMir.x);
    3394            0 :                             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - THSUNrefl) + 0.001);
    3395            0 :                             Real64 YR = std::tan(PHSUNrefl + 0.001);
    3396            0 :                             Real64 POSFAC = DayltgGlarePositionFactor(XR, YR);
    3397            0 :                             if (POSFAC != 0.0 && s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd > 0.000001) {
    3398            0 :                                 XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) /
    3399            0 :                                         std::pow(s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd, 0.8);
    3400            0 :                                 dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += XAVWL * TVisRefl * SpecReflectance; // Bare window
    3401            0 :                                 if (ANY_BLIND(ShType)) {
    3402            0 :                                     dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
    3403            0 :                                 } else if (ShType == WinShadingType::ExtScreen) {
    3404            0 :                                     dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
    3405              :                                 }
    3406              :                             }
    3407            0 :                         } // End of check that obstruction can specularly reflect
    3408              :                     }     // End of loop over obstructions associated with this window
    3409              : 
    3410            0 :                 } // End of check if this window has associated obstructions
    3411              :             }     // End of check to see if this is exterior type window
    3412              :         }         // End of check if exterior reflection calculation is in effect
    3413              : 
    3414         2066 :     } // Last pass
    3415              : 
    3416       453596 :     if ((ICtrl > 0 && (ANY_BLIND(ShType) || ANY_SHADE_SCREEN(ShType))) || s_surf->SurfWinSolarDiffusing(IWin)) {
    3417              : 
    3418              :         // ----- CASE II -- WINDOW WITH SCREEN, SHADE, BLIND, OR DIFFUSING WINDOW
    3419              :         // Interior window visible transmittance multiplier for exterior window in adjacent zone
    3420            0 :         TVisIntWinMult = 1.0;
    3421            0 :         TVisIntWinDiskMult = 1.0;
    3422            0 :         if (s_surf->Surface(IWin).SolarEnclIndex != dl->daylightControl(daylightCtrlNum).enclIndex) {
    3423            0 :             TVisIntWinMult = TVISIntWin;
    3424            0 :             TVisIntWinDiskMult = TVISIntWinDisk;
    3425              :         }
    3426              : 
    3427            0 :         Real64 const DOMEGA_Ray_3_TVisIntWinMult(DOMEGA_Ray_3 * TVisIntWinMult);
    3428              : 
    3429            0 :         auto &wlumsk = dl->winLum(iHour)[iWinCover_Shaded];
    3430            0 :         auto &edirsk = dl->dirIllum(iHour)[iWinCover_Shaded];
    3431            0 :         auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Shaded];
    3432            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3433              :             // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3434            0 :             avwlsk.sky[iSky] += wlumsk.sky[iSky] * TVisIntWinMult;
    3435            0 :             if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3_TVisIntWinMult;
    3436              :         } // for (iSky)
    3437              : 
    3438            0 :         dl->avgWinLum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * TVisIntWinMult;
    3439            0 :         dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += dl->winLum(iHour)[iWinCover_Shaded].sunDisk * TVisIntWinDiskMult;
    3440              : 
    3441            0 :         if (PHRAY > 0.0) {
    3442            0 :             dl->dirIllum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * DOMEGA_Ray_3_TVisIntWinMult;
    3443              :         }
    3444              :     }
    3445       453596 : } // FigureDayltgCoeffsAtPointsForSunPosition()
    3446              : 
    3447         1618 : void FigureRefPointDayltgFactorsToAddIllums(EnergyPlusData &state,
    3448              :                                             int const daylightCtrlNum, // Current daylighting control number
    3449              :                                             int const iRefPoint,
    3450              :                                             int const iHour,
    3451              :                                             int &ISunPos,
    3452              :                                             int const IWin,
    3453              :                                             int const loopwin,
    3454              :                                             int const NWX,  // Number of window elements in x direction for dayltg calc
    3455              :                                             int const NWY,  // Number of window elements in y direction for dayltg calc
    3456              :                                             int const ICtrl // Window control counter
    3457              : )
    3458              : {
    3459              : 
    3460              :     // SUBROUTINE INFORMATION:
    3461              :     //       AUTHOR         B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann
    3462              :     //       DATE WRITTEN   Oct. 2012
    3463              : 
    3464              :     // PURPOSE OF THIS SUBROUTINE:
    3465              :     // calculation worker routine to fill daylighting coefficients
    3466              : 
    3467              :     // METHODOLOGY EMPLOYED:
    3468              :     // this version is just for reference points.
    3469              : 
    3470              :     // SUBROUTINE PARAMETER DEFINITIONS:
    3471         1618 :     Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating the daylighting and glare factors
    3472              : 
    3473              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3474         1618 :     auto &dl = state.dataDayltg;
    3475         1618 :     auto &s_surf = state.dataSurface;
    3476              : 
    3477         1618 :     if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
    3478              : 
    3479          566 :     ++ISunPos;
    3480              : 
    3481              :     // Altitude of sun (degrees)
    3482          566 :     dl->sunAngles = dl->sunAnglesHr[iHour];
    3483              : 
    3484          566 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    3485              : 
    3486          566 :     auto &surf = s_surf->Surface(IWin);
    3487              : 
    3488          566 :     int const enclNum = surf.SolarEnclIndex;
    3489              : 
    3490              :     // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
    3491              : 
    3492              :     // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
    3493              :     //  related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
    3494              :     //  and interior surfaces with high visible reflectance.
    3495              :     // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
    3496              :     //  the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
    3497              : 
    3498          566 :     auto &daylFacHr = thisDayltgCtrl.daylFac[iHour];
    3499              : 
    3500         1698 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    3501              : 
    3502         1132 :         auto const &gilsk = dl->horIllum[iHour];
    3503         1132 :         auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
    3504         1132 :         auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
    3505         1132 :         auto const &avwlsk = dl->avgWinLum(iHour)[iWinCover];
    3506              : 
    3507         1132 :         auto &daylFac = daylFacHr(loopwin, iRefPoint)[iWinCover];
    3508         1132 :         auto &illFac = daylFac[iLum_Illum];
    3509         1132 :         auto &sourceFac = daylFac[iLum_Source];
    3510         1132 :         auto &backFac = daylFac[iLum_Back];
    3511              : 
    3512         5660 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
    3513              : 
    3514         4528 :             if (gilsk.sky[iSky] > tmpDFCalc) {
    3515         4528 :                 illFac.sky[iSky] = (edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky];
    3516         4528 :                 sourceFac.sky[iSky] = avwlsk.sky[iSky] / (NWX * NWY * gilsk.sky[iSky]);
    3517         4528 :                 backFac.sky[iSky] = eintsk.sky[iSky] * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * gilsk.sky[iSky]);
    3518              :             } else {
    3519            0 :                 illFac.sky[iSky] = 0.0;
    3520            0 :                 sourceFac.sky[iSky] = 0.0;
    3521            0 :                 backFac.sky[iSky] = 0.0;
    3522              :             }
    3523              : 
    3524              :         } // for (iSky)
    3525              : 
    3526         1132 :         if (dl->horIllum[iHour].sun > tmpDFCalc) {
    3527         1072 :             daylFac[iLum_Illum].sun = (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
    3528         2144 :             daylFac[iLum_Illum].sunDisk =
    3529         1072 :                 (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
    3530         1072 :             daylFac[iLum_Source].sun = dl->avgWinLum(iHour)[iWinCover].sun / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
    3531         1072 :             daylFac[iLum_Source].sunDisk = dl->avgWinLum(iHour)[iWinCover].sunDisk / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
    3532         1072 :             daylFac[iLum_Back].sun = dl->reflIllum(iHour)[iWinCover].sun * dl->enclDaylight(enclNum).aveVisDiffReflect /
    3533         1072 :                                      (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
    3534         2144 :             daylFac[iLum_Back].sunDisk = dl->reflIllum(iHour)[iWinCover].sunDisk * dl->enclDaylight(enclNum).aveVisDiffReflect /
    3535         1072 :                                          (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
    3536              :         } else {
    3537           60 :             daylFac[iLum_Illum].sun = 0.0;
    3538           60 :             daylFac[iLum_Illum].sunDisk = 0.0;
    3539              : 
    3540           60 :             daylFac[iLum_Source].sun = 0.0;
    3541           60 :             daylFac[iLum_Source].sunDisk = 0.0;
    3542              : 
    3543           60 :             daylFac[iLum_Back].sun = 0.0;
    3544           60 :             daylFac[iLum_Back].sunDisk = 0.0;
    3545              :         }
    3546              :     } // for (jSH)
    3547              : 
    3548              :     // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
    3549          566 :     if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
    3550              : 
    3551            0 :         Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // Ratio of Tvis of fully-switched state to that of the unswitched state
    3552            0 :         auto &daylFac2 = daylFacHr(loopwin, iRefPoint)[iWinCover_Shaded];
    3553            0 :         auto const &daylFac1 = daylFacHr(loopwin, iRefPoint)[iWinCover_Bare];
    3554              : 
    3555            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3556            0 :             daylFac2[iLum_Illum].sky[iSky] = daylFac1[iLum_Illum].sky[iSky] * VTR;
    3557            0 :             daylFac2[iLum_Source].sky[iSky] = daylFac1[iLum_Source].sky[iSky] * VTR;
    3558            0 :             daylFac2[iLum_Back].sky[iSky] = daylFac1[iLum_Back].sky[iSky] * VTR;
    3559              :         } // for (iSky)
    3560              : 
    3561            0 :         daylFac2[iLum_Illum].sun = daylFac1[iLum_Illum].sun * VTR;
    3562            0 :         daylFac2[iLum_Source].sun = daylFac1[iLum_Source].sun * VTR;
    3563            0 :         daylFac2[iLum_Back].sun = daylFac1[iLum_Back].sun * VTR;
    3564            0 :         daylFac2[iLum_Illum].sunDisk = daylFac1[iLum_Illum].sunDisk * VTR;
    3565            0 :         daylFac2[iLum_Source].sunDisk = daylFac1[iLum_Source].sunDisk * VTR;
    3566            0 :         daylFac2[iLum_Back].sunDisk = daylFac1[iLum_Back].sunDisk * VTR;
    3567              :     } // ICtrl > 0
    3568              : } // FigureRefPointDayltgFactorsToAddIllums()
    3569              : 
    3570         2400 : void FigureMapPointDayltgFactorsToAddIllums(EnergyPlusData &state,
    3571              :                                             int const MapNum,
    3572              :                                             int const iMapPoint,
    3573              :                                             int const iHour,
    3574              :                                             int const IWin,
    3575              :                                             int const loopwin,
    3576              :                                             int const ICtrl // Window control counter
    3577              : )
    3578              : {
    3579              : 
    3580              :     // SUBROUTINE INFORMATION:
    3581              :     //       AUTHOR         B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann, Peter Ellis, Linda Lawrie
    3582              :     //       DATE WRITTEN   Nov. 2012
    3583              : 
    3584              :     // PURPOSE OF THIS SUBROUTINE:
    3585              :     // calculation worker routine to fill daylighting coefficients
    3586              : 
    3587              :     // METHODOLOGY EMPLOYED:
    3588              :     // this version is just for map points.
    3589              : 
    3590              :     // SUBROUTINE PARAMETER DEFINITIONS:
    3591         2400 :     Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating
    3592              :     // the daylighting and glare factors
    3593              : 
    3594              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3595         2400 :     auto &dl = state.dataDayltg;
    3596         2400 :     auto &s_surf = state.dataSurface;
    3597              : 
    3598         2400 :     if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
    3599              : 
    3600              :     // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or blind;
    3601              : 
    3602              :     // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
    3603              :     //  related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
    3604              :     //  and interior surfaces with high visible reflectance.
    3605              :     // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
    3606              :     //  the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
    3607              : 
    3608         1500 :     auto &illumMap = dl->illumMaps(MapNum);
    3609         1500 :     auto &daylFacHr = illumMap.daylFac[iHour];
    3610         4500 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    3611              : 
    3612         3000 :         auto const &gilsk = dl->horIllum[iHour];
    3613         3000 :         auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
    3614         3000 :         auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
    3615         3000 :         auto &illSky = daylFacHr(loopwin, iMapPoint)[iWinCover];
    3616              : 
    3617        15000 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
    3618        12000 :             illSky.sky[iSky] = (gilsk.sky[iSky] > tmpDFCalc) ? ((edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky]) : 0.0;
    3619              :         } // for (iSky)
    3620              : 
    3621         3000 :         if (dl->horIllum[iHour].sun > tmpDFCalc) {
    3622         2800 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sun =
    3623         2800 :                 (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
    3624         2800 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk =
    3625         2800 :                 (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
    3626              :         } else {
    3627          200 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sun = 0.0;
    3628          200 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk = 0.0;
    3629              :         }
    3630              :     } // for (iWinCover)
    3631              : 
    3632              :     // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
    3633         1500 :     if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
    3634            0 :         Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // ratio of Tvis of switched to unswitched state
    3635            0 :         auto &illSky2 = daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded];
    3636            0 :         auto const &illSky1 = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare];
    3637            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3638            0 :             illSky2.sky[iSky] = illSky1.sky[iSky] * VTR;
    3639              :         }
    3640              : 
    3641            0 :         daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sun = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sun * VTR;
    3642            0 :         daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sunDisk = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sunDisk * VTR;
    3643              :     } // ICtrl > 0
    3644              : }
    3645              : 
    3646          111 : void GetDaylightingParametersInput(EnergyPlusData &state)
    3647              : {
    3648              : 
    3649              :     // SUBROUTINE INFORMATION:
    3650              :     //       AUTHOR         Linda Lawrie
    3651              :     //       DATE WRITTEN   Oct 2004
    3652              : 
    3653              :     // PURPOSE OF THIS SUBROUTINE:
    3654              :     // This subroutine provides a simple structure to get all daylighting
    3655              :     // parameters.
    3656          111 :     auto &dl = state.dataDayltg;
    3657          111 :     auto &s_surf = state.dataSurface;
    3658              : 
    3659          111 :     if (!dl->getDaylightingParametersInputFlag) return;
    3660          110 :     dl->getDaylightingParametersInputFlag = false;
    3661              : 
    3662          110 :     auto const &s_ipsc = state.dataIPShortCut;
    3663          110 :     s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
    3664          110 :     bool ErrorsFound = false;
    3665          110 :     int TotDaylightingControls = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    3666          110 :     if (TotDaylightingControls > 0) {
    3667            7 :         dl->enclDaylight.allocate(state.dataViewFactor->NumOfSolarEnclosures);
    3668            7 :         GetInputDayliteRefPt(state, ErrorsFound);
    3669            7 :         GetDaylightingControls(state, ErrorsFound);
    3670            7 :         GeometryTransformForDaylighting(state);
    3671            7 :         GetInputIlluminanceMap(state, ErrorsFound);
    3672            7 :         GetLightWellData(state, ErrorsFound);
    3673            7 :         if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons, related to DAYLIGHTING");
    3674            7 :         DayltgSetupAdjZoneListsAndPointers(state);
    3675              :     }
    3676              : 
    3677          110 :     dl->maxNumRefPtInAnyDaylCtrl = 0;
    3678          110 :     dl->maxNumRefPtInAnyEncl = 0;
    3679              :     // Loop through all daylighting controls to find total reference points in each enclosure
    3680          119 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    3681            9 :         int numRefPoints = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
    3682            9 :         dl->maxNumRefPtInAnyDaylCtrl = max(numRefPoints, dl->maxNumRefPtInAnyDaylCtrl);
    3683              :     }
    3684          251 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    3685          141 :         dl->maxNumRefPtInAnyEncl = max(state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints, dl->maxNumRefPtInAnyEncl);
    3686              :     }
    3687              : 
    3688          110 :     dl->maxEnclSubSurfaces = max(maxval(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::NumSubSurfaces),
    3689          110 :                                  maxval(dl->enclDaylight, &EnclDaylightCalc::NumOfDayltgExtWins));
    3690              : 
    3691          160 :     for (int SurfNum : s_surf->AllHTWindowSurfaceList) {
    3692           50 :         auto const &surf = s_surf->Surface(SurfNum);
    3693           50 :         int const surfEnclNum = surf.SolarEnclIndex;
    3694           50 :         int const numEnclRefPoints = state.dataViewFactor->EnclSolInfo(surfEnclNum).TotalEnclosureDaylRefPoints;
    3695           50 :         auto &surfWin = s_surf->SurfaceWindow(SurfNum);
    3696           50 :         if (numEnclRefPoints > 0) {
    3697            9 :             if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
    3698            9 :                 surfWin.refPts.allocate(numEnclRefPoints);
    3699           23 :                 for (auto &refPt : surfWin.refPts) {
    3700           14 :                     new (&refPt) SurfaceWindowRefPt();
    3701              :                 }
    3702              : 
    3703            9 :                 s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
    3704              :             }
    3705              :         } else {
    3706           41 :             int SurfNumAdj = surf.ExtBoundCond;
    3707           41 :             if (SurfNumAdj > 0) {
    3708            0 :                 int const adjSurfEnclNum = s_surf->Surface(SurfNumAdj).SolarEnclIndex;
    3709            0 :                 int const numAdjEnclRefPoints = state.dataViewFactor->EnclSolInfo(adjSurfEnclNum).TotalEnclosureDaylRefPoints;
    3710            0 :                 if (numAdjEnclRefPoints > 0) {
    3711            0 :                     if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
    3712            0 :                         surfWin.refPts.allocate(numAdjEnclRefPoints);
    3713            0 :                         for (auto &refPt : surfWin.refPts) {
    3714            0 :                             new (&refPt) SurfaceWindowRefPt();
    3715              :                         }
    3716            0 :                         s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
    3717              :                     }
    3718              :                 }
    3719              :             }
    3720              :         }
    3721              : 
    3722           50 :         if (surf.ExtBoundCond != ExternalEnvironment) continue;
    3723              : 
    3724           50 :         if (!surf.HasShadeControl) continue;
    3725              : 
    3726            9 :         auto &thisSurfEnclosure(state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex));
    3727            9 :         if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).GlareControlIsActive) {
    3728              :             // Error if GlareControlIsActive but window is not in a Daylighting:Detailed zone
    3729            0 :             if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
    3730            0 :                 ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3731            0 :                 ShowContinueError(state, "GlareControlIsActive = Yes but it is not in a Daylighting zone or enclosure.");
    3732            0 :                 ShowContinueError(state, format("Zone or enclosure indicated={}", state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex).Name));
    3733            0 :                 ErrorsFound = true;
    3734              :             }
    3735              :             // Error if GlareControlIsActive and window is in a Daylighting:Detailed zone/enclosure with
    3736              :             // an interior window adjacent to another Daylighting:Detailed zone/enclosure
    3737            0 :             if (thisSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3738            0 :                 for (int const intWin : thisSurfEnclosure.SurfacePtr) {
    3739            0 :                     int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
    3740            0 :                     if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
    3741            0 :                         auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
    3742            0 :                         if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3743            0 :                             ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3744            0 :                             ShowContinueError(state, "GlareControlIsActive = Yes and is in a Daylighting zone or enclosure");
    3745            0 :                             ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
    3746            0 :                             ShowContinueError(state, format("Adjacent Zone or Enclosure indicated={}", adjSurfEnclosure.Name));
    3747            0 :                             ErrorsFound = true;
    3748              :                         }
    3749              :                     }
    3750              :                 }
    3751              :             }
    3752              :         }
    3753              : 
    3754            9 :         if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) continue;
    3755              : 
    3756              :         // Error if window has shadingControlType = MeetDaylightingIlluminanceSetpoint &
    3757              :         // but is not in a Daylighting:Detailed zone
    3758            0 :         if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
    3759            0 :             ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3760            0 :             ShowContinueError(state, "MeetDaylightingIlluminanceSetpoint but it is not in a Daylighting zone or enclosure.");
    3761            0 :             ShowContinueError(state, format("Zone or enclosure indicated={}", thisSurfEnclosure.Name));
    3762            0 :             ErrorsFound = true;
    3763            0 :             continue;
    3764              :         }
    3765              : 
    3766              :         // Error if window has shadingControlType = MeetDaylightIlluminanceSetpoint and is in a &
    3767              :         // Daylighting:Detailed zone with an interior window adjacent to another Daylighting:Detailed zone
    3768            0 :         for (int const intWin : thisSurfEnclosure.SurfacePtr) {
    3769            0 :             int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
    3770            0 :             if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
    3771            0 :                 auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
    3772            0 :                 if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3773            0 :                     ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3774            0 :                     ShowContinueError(state, "MeetDaylightIlluminanceSetpoint and is in a Daylighting zone or enclosure");
    3775            0 :                     ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
    3776            0 :                     ShowContinueError(state, format("Adjacent Zone or enclosure indicated={}", adjSurfEnclosure.Name));
    3777            0 :                     ErrorsFound = true;
    3778              :                 }
    3779              :             }
    3780              :         }
    3781              :     } // for (SurfNum)
    3782              : 
    3783          110 :     if (!state.dataHeatBal->AnyAirBoundary) {
    3784          930 :         for (int SurfLoop = 1; SurfLoop <= s_surf->TotSurfaces; ++SurfLoop) {
    3785          820 :             auto const &surf = s_surf->Surface(SurfLoop);
    3786          820 :             if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
    3787              : 
    3788           48 :             int const enclOfSurf = surf.SolarEnclIndex;
    3789           48 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclOfSurf);
    3790           48 :             if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow || !dl->enclDaylight(enclOfSurf).hasSplitFluxDaylighting)
    3791           39 :                 continue;
    3792              : 
    3793            9 :             auto &surfWin = s_surf->SurfaceWindow(SurfLoop);
    3794           23 :             for (int refPtNum = 1; refPtNum <= enclSol.TotalEnclosureDaylRefPoints; ++refPtNum) {
    3795           14 :                 auto &refPt = surfWin.refPts(refPtNum);
    3796           42 :                 SetupOutputVariable(state,
    3797           28 :                                     format("Daylighting Window Reference Point {} Illuminance", refPtNum),
    3798              :                                     Constant::Units::lux,
    3799           14 :                                     refPt.illumFromWinRep,
    3800              :                                     OutputProcessor::TimeStepType::Zone,
    3801              :                                     OutputProcessor::StoreType::Average,
    3802           14 :                                     surf.Name);
    3803           42 :                 SetupOutputVariable(state,
    3804           28 :                                     format("Daylighting Window Reference Point {} View Luminance", refPtNum),
    3805              :                                     Constant::Units::cd_m2,
    3806           14 :                                     refPt.lumWinRep,
    3807              :                                     OutputProcessor::TimeStepType::Zone,
    3808              :                                     OutputProcessor::StoreType::Average,
    3809           14 :                                     surf.Name);
    3810              :             }
    3811              :         }
    3812              :     } else {
    3813            0 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    3814            0 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    3815            0 :             for (int const enclSurfNum : enclSol.SurfacePtr) {
    3816            0 :                 auto const &surf = s_surf->Surface(enclSurfNum);
    3817            0 :                 auto &surfWindow = s_surf->SurfaceWindow(enclSurfNum);
    3818              : 
    3819            0 :                 if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
    3820              : 
    3821            0 :                 if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow) continue;
    3822              : 
    3823            0 :                 auto const &enclDayltg = dl->enclDaylight(enclNum);
    3824            0 :                 if (!enclDayltg.hasSplitFluxDaylighting) continue;
    3825              : 
    3826            0 :                 int refPtCount = 0;
    3827            0 :                 for (int controlNum : enclDayltg.daylightControlIndexes) {
    3828            0 :                     auto const &control = dl->daylightControl(controlNum);
    3829            0 :                     for (int refPtNum = 1; refPtNum <= control.TotalDaylRefPoints; ++refPtNum) {
    3830            0 :                         ++refPtCount; // Count reference points across each daylighting control in the same enclosure
    3831            0 :                         auto &refPt = surfWindow.refPts(refPtCount);
    3832            0 :                         std::string varKey = format("{} to {}", surf.Name, state.dataDayltg->DaylRefPt(control.refPts(refPtNum).num).Name);
    3833            0 :                         SetupOutputVariable(state,
    3834              :                                             "Daylighting Window Reference Point Illuminance",
    3835              :                                             Constant::Units::lux,
    3836            0 :                                             refPt.illumFromWinRep,
    3837              :                                             OutputProcessor::TimeStepType::Zone,
    3838              :                                             OutputProcessor::StoreType::Average,
    3839              :                                             varKey);
    3840            0 :                         SetupOutputVariable(state,
    3841              :                                             "Daylighting Window Reference Point View Luminance",
    3842              :                                             Constant::Units::cd_m2,
    3843            0 :                                             refPt.lumWinRep,
    3844              :                                             OutputProcessor::TimeStepType::Zone,
    3845              :                                             OutputProcessor::StoreType::Average,
    3846              :                                             varKey);
    3847            0 :                     }
    3848              :                 } // for (controlNum)
    3849              :             }     // for (enclSurfNum)
    3850              :         }         // for (enclNum)
    3851              :     }
    3852              : 
    3853              :     // RJH DElight Modification Begin - Calls to DElight preprocessing subroutines
    3854          110 :     if (doesDayLightingUseDElight(state)) {
    3855            0 :         Real64 dLatitude = state.dataEnvrn->Latitude;
    3856            0 :         DisplayString(state, "Calculating DElight Daylighting Factors");
    3857            0 :         DElightManagerF::DElightInputGenerator(state);
    3858              :         // Init Error Flag to 0 (no Warnings or Errors)
    3859            0 :         DisplayString(state, "ReturnFrom DElightInputGenerator");
    3860            0 :         int iErrorFlag = 0;
    3861            0 :         DisplayString(state, "Calculating DElight DaylightCoefficients");
    3862            0 :         DElightManagerF::GenerateDElightDaylightCoefficients(dLatitude, iErrorFlag);
    3863              :         // Check Error Flag for Warnings or Errors returning from DElight
    3864              :         // RJH 2008-03-07: open file for READWRITE and DELETE file after processing
    3865            0 :         DisplayString(state, "ReturnFrom DElight DaylightCoefficients Calc");
    3866            0 :         if (iErrorFlag != 0) {
    3867              :             // Open DElight Daylight Factors Error File for reading
    3868            0 :             auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
    3869              : 
    3870              :             // Sequentially read lines in DElight Daylight Factors Error File
    3871              :             // and process them using standard EPlus warning/error handling calls
    3872              :             // Process all error/warning messages first
    3873              :             // Then, if any error has occurred, ShowFatalError to terminate processing
    3874            0 :             bool bEndofErrFile = !iDElightErrorFile.good();
    3875            0 :             std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
    3876            0 :             while (!bEndofErrFile) {
    3877            0 :                 auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
    3878            0 :                 if (cErrorLine.eof) {
    3879            0 :                     bEndofErrFile = true;
    3880            0 :                     continue;
    3881              :                 }
    3882              :                 // Is the current line a Warning message?
    3883            0 :                 if (has_prefix(cErrorLine.data, "WARNING: ")) {
    3884            0 :                     cErrorMsg = cErrorLine.data.substr(9);
    3885            0 :                     ShowWarningError(state, cErrorMsg);
    3886              :                 }
    3887              :                 // Is the current line an Error message?
    3888            0 :                 if (has_prefix(cErrorLine.data, "ERROR: ")) {
    3889            0 :                     cErrorMsg = cErrorLine.data.substr(7);
    3890            0 :                     ShowSevereError(state, cErrorMsg);
    3891            0 :                     iErrorFlag = 1;
    3892              :                 }
    3893            0 :             }
    3894              : 
    3895              :             // Close and Delete DElight Error File
    3896            0 :             if (iDElightErrorFile.is_open()) {
    3897            0 :                 iDElightErrorFile.close();
    3898            0 :                 FileSystem::removeFile(iDElightErrorFile.filePath);
    3899              :             }
    3900              : 
    3901              :             // If any DElight Error occurred then ShowFatalError to terminate
    3902            0 :             if (iErrorFlag > 0) {
    3903            0 :                 ErrorsFound = true;
    3904              :             }
    3905            0 :         } else {
    3906            0 :             if (FileSystem::fileExists(state.files.outputDelightDfdmpFilePath.filePath)) {
    3907            0 :                 FileSystem::removeFile(state.files.outputDelightDfdmpFilePath.filePath);
    3908              :             }
    3909              :         }
    3910              :     }
    3911              :     // RJH DElight Modification End - Calls to DElight preprocessing subroutines
    3912              : 
    3913              :     // TH 6/3/2010, added to report daylight factors
    3914          110 :     s_ipsc->cCurrentModuleObject = "Output:DaylightFactors";
    3915          110 :     int NumReports = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    3916          110 :     if (NumReports > 0) {
    3917              :         int NumNames;
    3918              :         int NumNumbers;
    3919              :         int IOStat;
    3920            2 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3921            1 :                                                                  s_ipsc->cCurrentModuleObject,
    3922              :                                                                  1,
    3923            1 :                                                                  s_ipsc->cAlphaArgs,
    3924              :                                                                  NumNames,
    3925            1 :                                                                  s_ipsc->rNumericArgs,
    3926              :                                                                  NumNumbers,
    3927              :                                                                  IOStat,
    3928            1 :                                                                  s_ipsc->lNumericFieldBlanks,
    3929            1 :                                                                  s_ipsc->lAlphaFieldBlanks,
    3930            1 :                                                                  s_ipsc->cAlphaFieldNames,
    3931            1 :                                                                  s_ipsc->cNumericFieldNames);
    3932            1 :         if (has_prefix(s_ipsc->cAlphaArgs(1), "SIZINGDAYS")) {
    3933            1 :             dl->DFSReportSizingDays = true;
    3934            0 :         } else if (has_prefix(s_ipsc->cAlphaArgs(1), "ALLSHADOWCALCULATIONDAYS")) {
    3935            0 :             dl->DFSReportAllShadowCalculationDays = true;
    3936              :         }
    3937              :     }
    3938              : 
    3939          110 :     if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons");
    3940              : } // FigureMapPointDayltgFactorsToAddIllums()
    3941              : 
    3942            8 : void GetInputIlluminanceMap(EnergyPlusData &state, bool &ErrorsFound)
    3943              : {
    3944              :     // Perform the GetInput function for the Output:IlluminanceMap
    3945              :     // Glazer - June 2016 (moved from GetDaylightingControls)
    3946            8 :     auto &dl = state.dataDayltg;
    3947            8 :     auto const &s_surf = state.dataSurface;
    3948              : 
    3949            8 :     Array1D_bool ZoneMsgDone;
    3950              : 
    3951            8 :     Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    3952            8 :     Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    3953              :     // these are only for Building Rotation for Appendix G when using world coordinate system
    3954            8 :     Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    3955            8 :     Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    3956              : 
    3957            8 :     bool doTransform = false;
    3958            8 :     Real64 OldAspectRatio = 1.0;
    3959            8 :     Real64 NewAspectRatio = 1.0;
    3960              : 
    3961            8 :     CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
    3962              : 
    3963            8 :     auto &ip = state.dataInputProcessing->inputProcessor;
    3964            8 :     auto const &s_ipsc = state.dataIPShortCut;
    3965            8 :     s_ipsc->cCurrentModuleObject = "Output:IlluminanceMap";
    3966            8 :     int TotIllumMaps = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    3967              : 
    3968            8 :     dl->illumMaps.allocate(TotIllumMaps);
    3969              : 
    3970            8 :     if (TotIllumMaps > 0) {
    3971              :         int IOStat;
    3972              :         int NumAlpha;
    3973              :         int NumNumber;
    3974            2 :         auto &ip = state.dataInputProcessing->inputProcessor;
    3975            4 :         for (int MapNum = 1; MapNum <= TotIllumMaps; ++MapNum) {
    3976            4 :             ip->getObjectItem(state,
    3977            2 :                               s_ipsc->cCurrentModuleObject,
    3978              :                               MapNum,
    3979            2 :                               s_ipsc->cAlphaArgs,
    3980              :                               NumAlpha,
    3981            2 :                               s_ipsc->rNumericArgs,
    3982              :                               NumNumber,
    3983              :                               IOStat,
    3984            2 :                               s_ipsc->lNumericFieldBlanks,
    3985            2 :                               s_ipsc->lAlphaFieldBlanks,
    3986            2 :                               s_ipsc->cAlphaFieldNames,
    3987            2 :                               s_ipsc->cNumericFieldNames);
    3988              : 
    3989            2 :             auto &illumMap = dl->illumMaps(MapNum);
    3990            2 :             illumMap.Name = s_ipsc->cAlphaArgs(1);
    3991            2 :             int const zoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
    3992            2 :             if (zoneNum > 0) {
    3993            2 :                 illumMap.zoneIndex = zoneNum;
    3994              :                 // set enclosure index for first space in zone
    3995            2 :                 int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
    3996            2 :                 illumMap.enclIndex = enclNum;
    3997              :                 // check that all spaces in the zone are in the same enclosure
    3998            2 :                 for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
    3999            0 :                     int spaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
    4000            0 :                     if (enclNum != state.dataHeatBal->space(spaceNum).solarEnclosureNum) {
    4001            0 :                         ShowSevereError(state,
    4002            0 :                                         format("{}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting illuminance maps.",
    4003            0 :                                                s_ipsc->cCurrentModuleObject,
    4004            0 :                                                s_ipsc->cAlphaArgs(1)));
    4005            0 :                         ShowContinueError(
    4006            0 :                             state, format("Zone=\"{}\" spans multiple enclosures. Use a Space Name instead.", state.dataHeatBal->Zone(zoneNum).Name));
    4007            0 :                         ErrorsFound = true;
    4008            0 :                         break;
    4009              :                     }
    4010              :                 }
    4011              :             } else {
    4012            0 :                 int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
    4013            0 :                 if (spaceNum == 0) {
    4014            0 :                     ShowSevereError(state,
    4015            0 :                                     format("{}=\"{}\", invalid {}=\"{}\".",
    4016            0 :                                            s_ipsc->cCurrentModuleObject,
    4017            0 :                                            s_ipsc->cAlphaArgs(1),
    4018            0 :                                            s_ipsc->cAlphaFieldNames(2),
    4019            0 :                                            s_ipsc->cAlphaArgs(2)));
    4020            0 :                     ErrorsFound = true;
    4021              :                 } else {
    4022            0 :                     illumMap.spaceIndex = spaceNum;
    4023            0 :                     illumMap.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
    4024            0 :                     illumMap.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    4025            0 :                     assert(illumMap.enclIndex > 0);
    4026              :                 }
    4027              :             }
    4028              : 
    4029            2 :             illumMap.Z = s_ipsc->rNumericArgs(1);
    4030            2 :             illumMap.Xmin = s_ipsc->rNumericArgs(2);
    4031            2 :             illumMap.Xmax = s_ipsc->rNumericArgs(3);
    4032            2 :             if (s_ipsc->rNumericArgs(2) > s_ipsc->rNumericArgs(3)) {
    4033            0 :                 ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4034            0 :                 ShowContinueError(state,
    4035            0 :                                   format("...{} {:.2R} must be <= {} {:.2R}.",
    4036            0 :                                          s_ipsc->cNumericFieldNames(2),
    4037            0 :                                          s_ipsc->rNumericArgs(2),
    4038            0 :                                          s_ipsc->cNumericFieldNames(3),
    4039            0 :                                          s_ipsc->rNumericArgs(3)));
    4040            0 :                 ErrorsFound = true;
    4041              :             }
    4042            2 :             illumMap.Xnum = s_ipsc->rNumericArgs(4);
    4043            2 :             illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
    4044              : 
    4045            2 :             illumMap.Ymin = s_ipsc->rNumericArgs(5);
    4046            2 :             illumMap.Ymax = s_ipsc->rNumericArgs(6);
    4047            2 :             if (s_ipsc->rNumericArgs(5) > s_ipsc->rNumericArgs(6)) {
    4048            0 :                 ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4049            0 :                 ShowContinueError(state,
    4050            0 :                                   format("...{} {:.2R} must be <= {} {:.2R}.",
    4051            0 :                                          s_ipsc->cNumericFieldNames(5),
    4052            0 :                                          s_ipsc->rNumericArgs(5),
    4053            0 :                                          s_ipsc->cNumericFieldNames(6),
    4054            0 :                                          s_ipsc->rNumericArgs(6)));
    4055            0 :                 ErrorsFound = true;
    4056              :             }
    4057            2 :             illumMap.Ynum = s_ipsc->rNumericArgs(7);
    4058            2 :             illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
    4059              : 
    4060            2 :             if (illumMap.Xnum * illumMap.Ynum > MaxMapRefPoints) {
    4061            0 :                 ShowSevereError(state, format("{}=\"{}\", too many map points specified.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4062            0 :                 ShowContinueError(state,
    4063            0 :                                   format("...{}[{}] * {}[{}].= [{}] must be <= [{}].",
    4064            0 :                                          s_ipsc->cNumericFieldNames(4),
    4065            0 :                                          illumMap.Xnum,
    4066            0 :                                          s_ipsc->cNumericFieldNames(7),
    4067            0 :                                          illumMap.Ynum,
    4068            0 :                                          illumMap.Xnum * illumMap.Ynum,
    4069              :                                          MaxMapRefPoints));
    4070            0 :                 ErrorsFound = true;
    4071              :             }
    4072              :         } // MapNum
    4073            2 :         s_ipsc->cCurrentModuleObject = "OutputControl:IlluminanceMap:Style";
    4074            2 :         int MapStyleIn = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4075              : 
    4076            2 :         if (MapStyleIn == 0) {
    4077            1 :             s_ipsc->cAlphaArgs(1) = "COMMA";
    4078            1 :             dl->MapColSep = DataStringGlobals::CharComma; // comma
    4079            1 :         } else if (MapStyleIn == 1) {
    4080            2 :             ip->getObjectItem(state,
    4081            1 :                               s_ipsc->cCurrentModuleObject,
    4082              :                               1,
    4083            1 :                               s_ipsc->cAlphaArgs,
    4084              :                               NumAlpha,
    4085            1 :                               s_ipsc->rNumericArgs,
    4086              :                               NumNumber,
    4087              :                               IOStat,
    4088            1 :                               s_ipsc->lNumericFieldBlanks,
    4089            1 :                               s_ipsc->lAlphaFieldBlanks,
    4090            1 :                               s_ipsc->cAlphaFieldNames,
    4091            1 :                               s_ipsc->cNumericFieldNames);
    4092            1 :             if (s_ipsc->cAlphaArgs(1) == "COMMA") {
    4093            1 :                 dl->MapColSep = DataStringGlobals::CharComma; // comma
    4094            0 :             } else if (s_ipsc->cAlphaArgs(1) == "TAB") {
    4095            0 :                 dl->MapColSep = DataStringGlobals::CharTab; // tab
    4096            0 :             } else if (s_ipsc->cAlphaArgs(1) == "FIXED" || s_ipsc->cAlphaArgs(1) == "SPACE") {
    4097            0 :                 dl->MapColSep = DataStringGlobals::CharSpace; // space
    4098              :             } else {
    4099            0 :                 dl->MapColSep = DataStringGlobals::CharComma; // comma
    4100            0 :                 ShowWarningError(state,
    4101            0 :                                  format("{}: invalid {}=\"{}\", Commas will be used to separate fields.",
    4102            0 :                                         s_ipsc->cCurrentModuleObject,
    4103            0 :                                         s_ipsc->cAlphaFieldNames(1),
    4104            0 :                                         s_ipsc->cAlphaArgs(1)));
    4105            0 :                 s_ipsc->cAlphaArgs(1) = "COMMA";
    4106              :             }
    4107              :         }
    4108            2 :         print(state.files.eio, "! <Daylighting:Illuminance Maps>,#Maps,Style\n");
    4109            2 :         ConvertCaseToLower(s_ipsc->cAlphaArgs(1), s_ipsc->cAlphaArgs(2));
    4110            2 :         s_ipsc->cAlphaArgs(1).erase(1);
    4111            2 :         s_ipsc->cAlphaArgs(1) += s_ipsc->cAlphaArgs(2).substr(1);
    4112            2 :         print(state.files.eio, "Daylighting:Illuminance Maps,{},{}\n", TotIllumMaps, s_ipsc->cAlphaArgs(1));
    4113              :     }
    4114              : 
    4115              :     // Check for illuminance maps associated with this zone
    4116           10 :     for (auto &illumMap : dl->illumMaps) {
    4117              : 
    4118            2 :         if (illumMap.zoneIndex == 0) continue;
    4119              : 
    4120            2 :         auto &zone = state.dataHeatBal->Zone(illumMap.zoneIndex);
    4121              :         // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
    4122            2 :         Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
    4123            2 :         Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
    4124              : 
    4125            2 :         if (illumMap.Xnum * illumMap.Ynum == 0) continue;
    4126              : 
    4127              :         // Add additional daylighting reference points for map
    4128            2 :         illumMap.TotalMapRefPoints = illumMap.Xnum * illumMap.Ynum;
    4129            2 :         illumMap.refPts.allocate(illumMap.TotalMapRefPoints);
    4130          212 :         for (auto &refPt : illumMap.refPts) {
    4131          210 :             new (&refPt) DaylMapPt();
    4132              :         }
    4133              : 
    4134            2 :         if (illumMap.TotalMapRefPoints > MaxMapRefPoints) {
    4135            0 :             ShowSevereError(state, "GetDaylighting Parameters: Total Map Reference points entered is greater than maximum allowed.");
    4136            0 :             ShowContinueError(state, format("Occurs in Zone={}", zone.Name));
    4137            0 :             ShowContinueError(state,
    4138            0 :                               format("Maximum reference points allowed={}, entered amount ( when error first occurred )={}",
    4139              :                                      MaxMapRefPoints,
    4140            0 :                                      illumMap.TotalMapRefPoints));
    4141            0 :             ErrorsFound = true;
    4142            0 :             break;
    4143              :         }
    4144              : 
    4145              :         // Calc cos and sin of Zone Relative North values for later use in transforming Map Point coordinates
    4146              :         // CosZoneRelNorth = std::cos( -zone.RelNorth * DegToRadians ); //Tuned These should not be changing
    4147              :         // SinZoneRelNorth = std::sin( -zone.RelNorth * DegToRadians );
    4148            2 :         illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
    4149            2 :         illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
    4150              : 
    4151              :         // Map points and increments are stored in AbsCoord and then that is operated on if relative coords entered.
    4152           23 :         for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    4153          231 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    4154          210 :                 int iRefPt = (Y - 1) * illumMap.Xnum + X;
    4155          210 :                 auto &refPt = illumMap.refPts(iRefPt);
    4156          210 :                 refPt.absCoords = {illumMap.Xmin + (X - 1) * illumMap.Xinc, illumMap.Ymin + (Y - 1) * illumMap.Yinc, illumMap.Z};
    4157              :             }
    4158              :         }
    4159              : 
    4160           23 :         for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    4161          231 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    4162          210 :                 int iRefPt = (Y - 1) * illumMap.Xnum + X;
    4163          210 :                 auto &refPt = illumMap.refPts(iRefPt);
    4164              : 
    4165          210 :                 if (!s_surf->DaylRefWorldCoordSystem) {
    4166          210 :                     Real64 Xb = refPt.absCoords.x * CosZoneRelNorth - refPt.absCoords.y * SinZoneRelNorth + zone.OriginX;
    4167          210 :                     Real64 Yb = refPt.absCoords.x * SinZoneRelNorth + refPt.absCoords.y * CosZoneRelNorth + zone.OriginY;
    4168          210 :                     refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
    4169          210 :                     refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
    4170          210 :                     refPt.absCoords.z += zone.OriginZ;
    4171          210 :                     if (doTransform) {
    4172            0 :                         Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
    4173            0 :                         Real64 Yo = refPt.absCoords.y;
    4174              :                         // next derotate the building
    4175            0 :                         Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
    4176            0 :                         Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
    4177              :                         // translate
    4178            0 :                         Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
    4179            0 :                         Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
    4180              :                         // rerotate
    4181            0 :                         refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
    4182              : 
    4183            0 :                         refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
    4184              :                     }
    4185              :                 } else {
    4186            0 :                     Real64 Xb = refPt.absCoords.x;
    4187            0 :                     Real64 Yb = refPt.absCoords.y;
    4188            0 :                     refPt.absCoords.x = Xb * CosBldgRotAppGonly - Yb * SinBldgRotAppGonly;
    4189            0 :                     refPt.absCoords.y = Xb * SinBldgRotAppGonly + Yb * CosBldgRotAppGonly;
    4190              :                 }
    4191          210 :                 if (iRefPt == 1) {
    4192            2 :                     illumMap.Xmin = refPt.absCoords.x;
    4193            2 :                     illumMap.Ymin = refPt.absCoords.y;
    4194            2 :                     illumMap.Xmax = refPt.absCoords.x;
    4195            2 :                     illumMap.Ymax = refPt.absCoords.y;
    4196            2 :                     illumMap.Z = refPt.absCoords.z;
    4197              :                 }
    4198          210 :                 illumMap.Xmin = min(illumMap.Xmin, refPt.absCoords.x);
    4199          210 :                 illumMap.Ymin = min(illumMap.Ymin, refPt.absCoords.y);
    4200          210 :                 illumMap.Xmax = max(illumMap.Xmax, refPt.absCoords.x);
    4201          210 :                 illumMap.Ymax = max(illumMap.Ymax, refPt.absCoords.y);
    4202          210 :                 if ((refPt.absCoords.x < zone.MinimumX && (zone.MinimumX - refPt.absCoords.x) > 0.001) ||
    4203          210 :                     (refPt.absCoords.x > zone.MaximumX && (refPt.absCoords.x - zone.MaximumX) > 0.001) ||
    4204          100 :                     (refPt.absCoords.y < zone.MinimumY && (zone.MinimumY - refPt.absCoords.y) > 0.001) ||
    4205          100 :                     (refPt.absCoords.y > zone.MaximumY && (refPt.absCoords.y - zone.MaximumY) > 0.001) ||
    4206          100 :                     (refPt.absCoords.z < zone.MinimumZ && (zone.MinimumZ - refPt.absCoords.z) > 0.001) ||
    4207          100 :                     (refPt.absCoords.z > zone.MaximumZ && (refPt.absCoords.z - zone.MaximumZ) > 0.001)) {
    4208          110 :                     refPt.inBounds = false;
    4209              :                 }
    4210              : 
    4211              :                 // Test extremes of Map Points against Zone Min/Max
    4212          212 :                 if (iRefPt != 1 && iRefPt != illumMap.TotalMapRefPoints) continue;
    4213              : 
    4214            4 :                 if (refPt.inBounds) continue;
    4215              : 
    4216            2 :                 if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
    4217            4 :                     ShowWarningError(
    4218              :                         state,
    4219            4 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], X Value outside Zone Min/Max X, Zone={}", iRefPt, zone.Name));
    4220            4 :                     ShowContinueError(state,
    4221            4 :                                       format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
    4222            2 :                                              refPt.absCoords.x,
    4223            2 :                                              zone.MinimumX,
    4224            2 :                                              zone.MaximumX));
    4225            4 :                     ShowContinueError(
    4226              :                         state,
    4227            4 :                         format("...X Reference Distance Outside MinimumX= {:.4R} m.",
    4228            4 :                                (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
    4229              :                 }
    4230            2 :                 if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
    4231            4 :                     ShowWarningError(
    4232              :                         state,
    4233            4 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], Y Value outside Zone Min/Max Y, Zone={}", iRefPt, zone.Name));
    4234            4 :                     ShowContinueError(state,
    4235            4 :                                       format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
    4236            2 :                                              refPt.absCoords.y,
    4237            2 :                                              zone.MinimumY,
    4238            2 :                                              zone.MaximumY));
    4239            4 :                     ShowContinueError(
    4240              :                         state,
    4241            4 :                         format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
    4242            4 :                                (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
    4243              :                 }
    4244            2 :                 if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
    4245            0 :                     ShowWarningError(
    4246              :                         state,
    4247            0 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], Z Value outside Zone Min/Max Z, Zone={}", iRefPt, zone.Name));
    4248            0 :                     ShowContinueError(state,
    4249            0 :                                       format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
    4250            0 :                                              refPt.absCoords.z,
    4251            0 :                                              zone.MinimumZ,
    4252            0 :                                              zone.MaximumZ));
    4253            0 :                     ShowContinueError(
    4254              :                         state,
    4255            0 :                         format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
    4256            0 :                                (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
    4257              :                 }
    4258              :             } // for (X)
    4259              :         }     // for (Y)
    4260              :     }         // for (MapNum)
    4261              : 
    4262            8 :     ZoneMsgDone.dimension(state.dataGlobal->NumOfZones, false);
    4263           10 :     for (auto const &illumMap : dl->illumMaps) {
    4264            2 :         if (illumMap.zoneIndex == 0) continue;
    4265            2 :         int enclNum = illumMap.enclIndex;
    4266            2 :         if (!dl->enclDaylight(enclNum).hasSplitFluxDaylighting && !ZoneMsgDone(illumMap.zoneIndex)) {
    4267            2 :             ShowSevereError(state,
    4268            2 :                             format("Zone Name in Output:IlluminanceMap is not used for Daylighting:Controls={}",
    4269            1 :                                    state.dataHeatBal->Zone(illumMap.zoneIndex).Name));
    4270            1 :             ErrorsFound = true;
    4271              :         }
    4272              :     }
    4273            8 :     ZoneMsgDone.deallocate();
    4274            8 :     if (ErrorsFound) return;
    4275              : 
    4276            7 :     if (TotIllumMaps > 0) {
    4277            1 :         print(state.files.eio,
    4278              :               "! <Daylighting:Illuminance Maps:Detail>,Name,Zone,XMin {{m}},XMax {{m}},Xinc {{m}},#X Points,YMin "
    4279              :               "{{m}},YMax {{m}},Yinc {{m}},#Y Points,Z {{m}}\n");
    4280              :     }
    4281            8 :     for (auto const &illumMap : dl->illumMaps) {
    4282            1 :         print(state.files.eio,
    4283              :               "Daylighting:Illuminance Maps:Detail,{},{},{:.2R},{:.2R},{:.2R},{},{:.2R},{:.2R},{:.2R},{},{:.2R}\n",
    4284            1 :               illumMap.Name,
    4285            1 :               state.dataHeatBal->Zone(illumMap.zoneIndex).Name,
    4286            1 :               illumMap.Xmin,
    4287            1 :               illumMap.Xmax,
    4288            1 :               illumMap.Xinc,
    4289            1 :               illumMap.Xnum,
    4290            1 :               illumMap.Ymin,
    4291            1 :               illumMap.Ymax,
    4292            1 :               illumMap.Yinc,
    4293            1 :               illumMap.Ynum,
    4294            1 :               illumMap.Z);
    4295              :     }
    4296              : 
    4297            8 : } // GetInputIlluminanceMap()
    4298              : 
    4299           11 : void GetDaylightingControls(EnergyPlusData &state, bool &ErrorsFound)
    4300              : {
    4301              :     //       AUTHOR         Fred Winkelmann
    4302              :     //       DATE WRITTEN   March 2002
    4303              :     //       MODIFIED       Glazer - July 2016 - Move geometry transformation portion, rearrange input, allow more than three reference points
    4304              :     // Obtain the user input data for Daylighting:Controls object in the input file.
    4305              : 
    4306              :     static constexpr std::string_view routineName = "GetDaylightingControls";
    4307              : 
    4308           11 :     auto &dl = state.dataDayltg;
    4309              : 
    4310              :     int IOStat;
    4311              :     int NumAlpha;
    4312              :     int NumNumber;
    4313              : 
    4314              :     // Smallest deviation from unity for the sum of all fractions
    4315              :     // Accept approx 4 to 8 ULP error (technically abs(1.0 + sumFracs) should be close to 2)
    4316              :     //   constexpr Real64 FractionTolerance(4 * std::numeric_limits<Real64>::epsilon());
    4317              :     // Instead, we use a 0.001 = 0.1% tolerance
    4318           11 :     constexpr Real64 FractionTolerance(0.001);
    4319              : 
    4320           11 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4321           11 :     auto const &s_ipsc = state.dataIPShortCut;
    4322           11 :     s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
    4323           11 :     int totDaylightingControls = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4324           11 :     dl->daylightControl.allocate(totDaylightingControls);
    4325           11 :     Array1D<bool> spaceHasDaylightingControl;
    4326           11 :     spaceHasDaylightingControl.dimension(state.dataGlobal->numSpaces, false);
    4327              :     // Reset to zero in case this is called more than once in unit tests
    4328           24 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    4329           13 :         state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints = 0;
    4330              :     }
    4331           24 :     for (int controlNum = 1; controlNum <= totDaylightingControls; ++controlNum) {
    4332           13 :         s_ipsc->cAlphaArgs = "";
    4333           13 :         s_ipsc->rNumericArgs = 0.0;
    4334           26 :         ip->getObjectItem(state,
    4335           13 :                           s_ipsc->cCurrentModuleObject,
    4336              :                           controlNum,
    4337           13 :                           s_ipsc->cAlphaArgs,
    4338              :                           NumAlpha,
    4339           13 :                           s_ipsc->rNumericArgs,
    4340              :                           NumNumber,
    4341              :                           IOStat,
    4342           13 :                           s_ipsc->lNumericFieldBlanks,
    4343           13 :                           s_ipsc->lAlphaFieldBlanks,
    4344           13 :                           s_ipsc->cAlphaFieldNames,
    4345           13 :                           s_ipsc->cNumericFieldNames);
    4346              : 
    4347           13 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    4348              : 
    4349           13 :         auto &daylightControl = dl->daylightControl(controlNum);
    4350           13 :         daylightControl.Name = s_ipsc->cAlphaArgs(1);
    4351              : 
    4352              :         // Is it a zone or space name?
    4353           13 :         int const zoneNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->Zone);
    4354           13 :         if (zoneNum > 0) {
    4355           13 :             daylightControl.zoneIndex = zoneNum;
    4356              :             // set enclosure index for first space in zone
    4357           13 :             int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
    4358           13 :             daylightControl.enclIndex = enclNum;
    4359              :             // check that all spaces in the zone are in the same enclosure
    4360           13 :             for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
    4361            0 :                 int zoneSpaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
    4362            0 :                 if (daylightControl.enclIndex != state.dataHeatBal->space(zoneSpaceNum).solarEnclosureNum) {
    4363            0 :                     ShowSevereError(state,
    4364            0 :                                     format("{}: invalid {}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting.",
    4365            0 :                                            s_ipsc->cCurrentModuleObject,
    4366            0 :                                            s_ipsc->cAlphaFieldNames(2),
    4367            0 :                                            s_ipsc->cAlphaArgs(2)));
    4368            0 :                     ErrorsFound = true;
    4369            0 :                     break;
    4370              :                 }
    4371              :             }
    4372           26 :             for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4373              :                 // Check if this is a duplicate
    4374           13 :                 if (spaceHasDaylightingControl(zoneSpaceNum)) {
    4375            0 :                     ShowWarningError(state,
    4376            0 :                                      format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
    4377            0 :                                             s_ipsc->cCurrentModuleObject,
    4378            0 :                                             daylightControl.Name,
    4379            0 :                                             state.dataHeatBal->space(zoneSpaceNum).Name,
    4380            0 :                                             s_ipsc->cCurrentModuleObject));
    4381            0 :                     ShowContinueError(state, "This control will override the lighting power factor for this space.");
    4382              :                 }
    4383           13 :                 spaceHasDaylightingControl(zoneSpaceNum) = true;
    4384              :             }
    4385              :         } else {
    4386            0 :             int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
    4387            0 :             if (spaceNum == 0) {
    4388            0 :                 ShowSevereError(state,
    4389            0 :                                 format("{}: invalid {}=\"{}\".", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
    4390            0 :                 ErrorsFound = true;
    4391            0 :                 continue;
    4392              :             } else {
    4393            0 :                 daylightControl.spaceIndex = spaceNum;
    4394            0 :                 daylightControl.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
    4395            0 :                 daylightControl.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    4396              :                 // Check if this is a duplicate
    4397            0 :                 if (spaceHasDaylightingControl(spaceNum)) {
    4398            0 :                     ShowWarningError(state,
    4399            0 :                                      format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
    4400            0 :                                             s_ipsc->cCurrentModuleObject,
    4401            0 :                                             daylightControl.Name,
    4402            0 :                                             state.dataHeatBal->space(spaceNum).Name,
    4403            0 :                                             s_ipsc->cCurrentModuleObject));
    4404            0 :                     ShowContinueError(state, "This control will override the lighting power factor for this space.");
    4405              :                 }
    4406            0 :                 spaceHasDaylightingControl(spaceNum) = true;
    4407              :             }
    4408              :         }
    4409              : 
    4410           13 :         dl->enclDaylight(daylightControl.enclIndex).daylightControlIndexes.emplace_back(controlNum);
    4411           13 :         daylightControl.ZoneName = state.dataHeatBal->Zone(daylightControl.zoneIndex).Name;
    4412              : 
    4413           13 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    4414            0 :             daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
    4415              :         } else {
    4416           13 :             daylightControl.DaylightMethod =
    4417           13 :                 static_cast<DaylightingMethod>(getEnumValue(DaylightingMethodNamesUC, Util::makeUPPER(s_ipsc->cAlphaArgs(3))));
    4418              : 
    4419           13 :             if (daylightControl.DaylightMethod == DaylightingMethod::Invalid) {
    4420            0 :                 daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
    4421            0 :                 ShowWarningError(state,
    4422            0 :                                  format("Invalid {} = {}, occurs in {}object for {}=\"{}",
    4423            0 :                                         s_ipsc->cAlphaFieldNames(3),
    4424            0 :                                         s_ipsc->cAlphaArgs(3),
    4425            0 :                                         s_ipsc->cCurrentModuleObject,
    4426            0 :                                         s_ipsc->cCurrentModuleObject,
    4427            0 :                                         s_ipsc->cAlphaArgs(1)));
    4428            0 :                 ShowContinueError(state, "SplitFlux assumed, and the simulation continues.");
    4429              :             }
    4430              :         }
    4431           13 :         dl->enclDaylight(daylightControl.enclIndex).hasSplitFluxDaylighting |= (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux);
    4432              : 
    4433           13 :         if (s_ipsc->lAlphaFieldBlanks(4)) { // Field: Availability Schedule Name
    4434           13 :             daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
    4435            0 :         } else if ((daylightControl.availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
    4436            0 :             ShowWarningItemNotFound(state,
    4437              :                                     eoh,
    4438            0 :                                     s_ipsc->cAlphaFieldNames(4),
    4439            0 :                                     s_ipsc->cAlphaArgs(4),
    4440              :                                     "Schedule was not found so controls will always be available, and the simulation continues.");
    4441            0 :             daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
    4442              :         }
    4443              : 
    4444           13 :         daylightControl.LightControlType = static_cast<LtgCtrlType>(getEnumValue(LtgCtrlTypeNamesUC, s_ipsc->cAlphaArgs(5)));
    4445           13 :         if (daylightControl.LightControlType == LtgCtrlType::Invalid) {
    4446            0 :             ShowWarningInvalidKey(
    4447            0 :                 state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), "Continuous assumed, and the simulation continues.");
    4448              :         }
    4449              : 
    4450           13 :         daylightControl.MinPowerFraction = s_ipsc->rNumericArgs(1);  // Field: Minimum Input Power Fraction for Continuous Dimming Control
    4451           13 :         daylightControl.MinLightFraction = s_ipsc->rNumericArgs(2);  // Field: Minimum Light Output Fraction for Continuous Dimming Control
    4452           13 :         daylightControl.LightControlSteps = s_ipsc->rNumericArgs(3); // Field: Number of Stepped Control Steps
    4453           13 :         daylightControl.LightControlProbability =
    4454           13 :             s_ipsc->rNumericArgs(4); // Field: Probability Lighting will be Reset When Needed in Manual Stepped Control
    4455              : 
    4456           13 :         if (!s_ipsc->lAlphaFieldBlanks(6)) { // Field: Glare Calculation Daylighting Reference Point Name
    4457           11 :             daylightControl.glareRefPtNumber = Util::FindItemInList(s_ipsc->cAlphaArgs(6),
    4458           11 :                                                                     dl->DaylRefPt,
    4459              :                                                                     &RefPointData::Name); // Field: Glare Calculation Daylighting Reference Point Name
    4460           11 :             if (daylightControl.glareRefPtNumber == 0) {
    4461            0 :                 ShowSevereError(state,
    4462            0 :                                 format("{}: invalid {}=\"{}\" for object named: {}",
    4463            0 :                                        s_ipsc->cCurrentModuleObject,
    4464            0 :                                        s_ipsc->cAlphaFieldNames(6),
    4465            0 :                                        s_ipsc->cAlphaArgs(6),
    4466            0 :                                        s_ipsc->cAlphaArgs(1)));
    4467            0 :                 ErrorsFound = true;
    4468            0 :                 continue;
    4469              :             }
    4470            2 :         } else if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4471            2 :             ShowWarningError(state, format("No {} provided for object named: {}", s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(1)));
    4472            6 :             ShowContinueError(state, "No glare calculation performed, and the simulation continues.");
    4473              :         }
    4474              : 
    4475              :         // Field: Glare Calculation Azimuth Angle of View Direction Clockwise from Zone y-Axis
    4476           13 :         daylightControl.ViewAzimuthForGlare = !s_ipsc->lNumericFieldBlanks(5) ? s_ipsc->rNumericArgs(5) : 0.0;
    4477              : 
    4478           13 :         daylightControl.MaxGlareallowed = s_ipsc->rNumericArgs(6);           // Field: Maximum Allowable Discomfort Glare Index
    4479           13 :         daylightControl.DElightGriddingResolution = s_ipsc->rNumericArgs(7); // Field: DElight Gridding Resolution
    4480              : 
    4481           13 :         int curTotalDaylRefPts = NumAlpha - 6; // first six alpha fields are not part of extensible group
    4482           13 :         daylightControl.TotalDaylRefPoints = curTotalDaylRefPts;
    4483           13 :         state.dataViewFactor->EnclSolInfo(daylightControl.enclIndex).TotalEnclosureDaylRefPoints += curTotalDaylRefPts;
    4484           13 :         dl->ZoneDaylight(daylightControl.zoneIndex).totRefPts += curTotalDaylRefPts;
    4485           13 :         dl->maxControlRefPoints = max(dl->maxControlRefPoints, curTotalDaylRefPts);
    4486           13 :         if ((NumNumber - 7) / 2 != daylightControl.TotalDaylRefPoints) {
    4487            0 :             ShowSevereError(state,
    4488            0 :                             format("{}The number of extensible numeric fields and alpha fields is inconsistent for: {}",
    4489            0 :                                    s_ipsc->cCurrentModuleObject,
    4490            0 :                                    s_ipsc->cAlphaArgs(1)));
    4491            0 :             ShowContinueError(state,
    4492            0 :                               format("For each field: {} there needs to be the following fields: Fraction Controlled by Reference Point and "
    4493              :                                      "Illuminance Setpoint at Reference Point",
    4494            0 :                                      s_ipsc->cAlphaFieldNames(NumAlpha)));
    4495            0 :             ErrorsFound = true;
    4496              :         }
    4497              : 
    4498           13 :         daylightControl.refPts.allocate(curTotalDaylRefPts);
    4499              : 
    4500           43 :         for (auto &refPt : daylightControl.refPts) {
    4501           30 :             refPt = DaylRefPt();
    4502              :         }
    4503              : 
    4504           13 :         int countRefPts = 0;
    4505           43 :         for (int refPtNum = 1; refPtNum <= curTotalDaylRefPts; ++refPtNum) {
    4506           30 :             auto &refPt = daylightControl.refPts(refPtNum);
    4507           30 :             refPt.num =
    4508           30 :                 Util::FindItemInList(s_ipsc->cAlphaArgs(6 + refPtNum), dl->DaylRefPt, &RefPointData::Name); // Field: Daylighting Reference Point Name
    4509           30 :             if (refPt.num == 0) {
    4510            0 :                 ShowSevereError(state,
    4511            0 :                                 format("{}: invalid {}=\"{}\" for object named: {}",
    4512            0 :                                        s_ipsc->cCurrentModuleObject,
    4513            0 :                                        s_ipsc->cAlphaFieldNames(6 + refPtNum),
    4514            0 :                                        s_ipsc->cAlphaArgs(6 + refPtNum),
    4515            0 :                                        s_ipsc->cAlphaArgs(1)));
    4516            0 :                 ErrorsFound = true;
    4517            0 :                 continue;
    4518              :             } else {
    4519           30 :                 ++countRefPts;
    4520              :             }
    4521           30 :             refPt.fracZoneDaylit = s_ipsc->rNumericArgs(6 + refPtNum * 2); // Field: Fraction Controlled by Reference Point
    4522           30 :             refPt.illumSetPoint = s_ipsc->rNumericArgs(7 + refPtNum * 2);  // Field: Illuminance Setpoint at Reference Point
    4523              : 
    4524           30 :             if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4525           90 :                 SetupOutputVariable(state,
    4526           60 :                                     format("Daylighting Reference Point {} Illuminance", refPtNum),
    4527              :                                     Constant::Units::lux,
    4528           30 :                                     refPt.lums[iLum_Illum],
    4529              :                                     OutputProcessor::TimeStepType::Zone,
    4530              :                                     OutputProcessor::StoreType::Average,
    4531           30 :                                     daylightControl.Name);
    4532           90 :                 SetupOutputVariable(state,
    4533           60 :                                     format("Daylighting Reference Point {} Daylight Illuminance Setpoint Exceeded Time", refPtNum),
    4534              :                                     Constant::Units::hr,
    4535           30 :                                     refPt.timeExceedingDaylightIlluminanceSetPoint,
    4536              :                                     OutputProcessor::TimeStepType::Zone,
    4537              :                                     OutputProcessor::StoreType::Sum,
    4538           30 :                                     daylightControl.Name);
    4539           90 :                 SetupOutputVariable(state,
    4540           60 :                                     format("Daylighting Reference Point {} Glare Index", refPtNum),
    4541              :                                     Constant::Units::None,
    4542           30 :                                     refPt.glareIndex,
    4543              :                                     OutputProcessor::TimeStepType::Zone,
    4544              :                                     OutputProcessor::StoreType::Average,
    4545           30 :                                     daylightControl.Name);
    4546           90 :                 SetupOutputVariable(state,
    4547           60 :                                     format("Daylighting Reference Point {} Glare Index Setpoint Exceeded Time", refPtNum),
    4548              :                                     Constant::Units::hr,
    4549           30 :                                     refPt.timeExceedingGlareIndexSetPoint,
    4550              :                                     OutputProcessor::TimeStepType::Zone,
    4551              :                                     OutputProcessor::StoreType::Sum,
    4552           30 :                                     daylightControl.Name);
    4553              :             } // if (DaylightMethod == SplitFlux)
    4554              :         }     // for (RefPtNum)
    4555              : 
    4556              :         // Register Error if 0 DElight RefPts have been input for valid DElight object
    4557           13 :         if (countRefPts < 1) {
    4558            0 :             ShowSevereError(state, format("No Reference Points input for {} zone ={}", s_ipsc->cCurrentModuleObject, daylightControl.ZoneName));
    4559            0 :             ErrorsFound = true;
    4560              :         }
    4561              : 
    4562           13 :         Real64 sumFracs = 0.0;
    4563           43 :         for (auto const &refPt : daylightControl.refPts)
    4564           30 :             sumFracs += refPt.fracZoneDaylit;
    4565              : 
    4566           13 :         daylightControl.sumFracLights = sumFracs;
    4567           13 :         if ((1.0 - sumFracs) > FractionTolerance) {
    4568            2 :             ShowWarningError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is < 1.0.");
    4569            2 :             ShowContinueError(state,
    4570            2 :                               format("..discovered in {}=\"{}\", only {:.3R} of the zone or space is controlled.",
    4571            1 :                                      s_ipsc->cCurrentModuleObject,
    4572            1 :                                      daylightControl.Name,
    4573              :                                      sumFracs));
    4574           12 :         } else if ((sumFracs - 1.0) > FractionTolerance) {
    4575            2 :             ShowSevereError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is > 1.0.");
    4576            2 :             ShowContinueError(state,
    4577            2 :                               format("..discovered in {}=\"{}\", trying to control {:.3R} of the zone or space.",
    4578            1 :                                      s_ipsc->cCurrentModuleObject,
    4579            1 :                                      daylightControl.Name,
    4580              :                                      sumFracs));
    4581            1 :             ErrorsFound = true;
    4582              :         }
    4583              : 
    4584           13 :         if (daylightControl.LightControlType == LtgCtrlType::Stepped && daylightControl.LightControlSteps <= 0) {
    4585            0 :             ShowWarningError(state, "GetDaylightingControls: For Stepped Control, the number of steps must be > 0");
    4586            0 :             ShowContinueError(state,
    4587            0 :                               format("..discovered in \"{}\" for Zone=\"{}\", will use 1", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(2)));
    4588            0 :             daylightControl.LightControlSteps = 1;
    4589              :         }
    4590           26 :         SetupOutputVariable(state,
    4591              :                             "Daylighting Lighting Power Multiplier",
    4592              :                             Constant::Units::None,
    4593           13 :                             daylightControl.PowerReductionFactor,
    4594              :                             OutputProcessor::TimeStepType::Zone,
    4595              :                             OutputProcessor::StoreType::Average,
    4596           13 :                             daylightControl.Name);
    4597              :     } // for (controlNum)
    4598           11 : } // GetDaylightingControls()
    4599              : 
    4600           11 : void GeometryTransformForDaylighting(EnergyPlusData &state)
    4601              : {
    4602              :     //       AUTHOR         Fred Winkelmann
    4603              :     //       DATE WRITTEN   March 2002
    4604              :     //       MODIFIED       Glazer - July 2016 - separated this from GetInput function
    4605              :     // For splitflux daylighting, transform the geometry
    4606           11 :     auto &dl = state.dataDayltg;
    4607           11 :     auto const &s_surf = state.dataSurface;
    4608              : 
    4609              :     // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
    4610           11 :     Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    4611           11 :     Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    4612              :     // these are only for Building Rotation for Appendix G when using world coordinate system
    4613           11 :     Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    4614           11 :     Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    4615              : 
    4616           11 :     bool doTransform = false;
    4617           11 :     Real64 OldAspectRatio = 1.0;
    4618           11 :     Real64 NewAspectRatio = 1.0;
    4619              : 
    4620           11 :     CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
    4621           28 :     for (auto &daylCntrl : dl->daylightControl) {
    4622           17 :         auto &zone = state.dataHeatBal->Zone(daylCntrl.zoneIndex);
    4623              : 
    4624              :         // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
    4625           17 :         Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
    4626           17 :         Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
    4627              : 
    4628           17 :         Real64 rLightLevel = InternalHeatGains::GetDesignLightingLevelForZone(state, daylCntrl.zoneIndex);
    4629           17 :         InternalHeatGains::CheckLightsReplaceableMinMaxForZone(state, daylCntrl.zoneIndex);
    4630              : 
    4631           45 :         for (int refPtNum = 1; refPtNum <= daylCntrl.TotalDaylRefPoints; ++refPtNum) {
    4632           28 :             auto &refPt = daylCntrl.refPts(refPtNum);
    4633           28 :             auto &curRefPt = dl->DaylRefPt(refPt.num); // get the active daylighting:referencepoint
    4634           28 :             curRefPt.indexToFracAndIllum = refPtNum;   // back reference to the index to the ZoneDaylight structure arrays related to reference points
    4635           28 :             if (s_surf->DaylRefWorldCoordSystem) {
    4636              :                 // transform only by appendix G rotation
    4637            0 :                 refPt.absCoords.x = curRefPt.coords.x * CosBldgRotAppGonly - curRefPt.coords.y * SinBldgRotAppGonly;
    4638            0 :                 refPt.absCoords.y = curRefPt.coords.x * SinBldgRotAppGonly + curRefPt.coords.y * CosBldgRotAppGonly;
    4639            0 :                 refPt.absCoords.z = curRefPt.coords.z;
    4640              :             } else {
    4641              :                 // Transform reference point coordinates into building coordinate system
    4642           28 :                 Real64 Xb = curRefPt.coords.x * CosZoneRelNorth - curRefPt.coords.y * SinZoneRelNorth + zone.OriginX;
    4643           28 :                 Real64 Yb = curRefPt.coords.x * SinZoneRelNorth + curRefPt.coords.y * CosZoneRelNorth + zone.OriginY;
    4644              :                 // Transform into World Coordinate System
    4645           28 :                 refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
    4646           28 :                 refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
    4647           28 :                 refPt.absCoords.z = curRefPt.coords.z + zone.OriginZ;
    4648           28 :                 if (doTransform) {
    4649            0 :                     Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
    4650            0 :                     Real64 Yo = refPt.absCoords.y;
    4651              :                     // next derotate the building
    4652            0 :                     Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
    4653            0 :                     Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
    4654              :                     // translate
    4655            0 :                     Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
    4656            0 :                     Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
    4657              :                     // rerotate
    4658            0 :                     refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
    4659            0 :                     refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
    4660              :                 }
    4661              :             }
    4662              : 
    4663           28 :             auto &orp = state.dataOutRptPredefined;
    4664           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtZone, curRefPt.Name, daylCntrl.ZoneName);
    4665           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlName, curRefPt.Name, daylCntrl.Name);
    4666           28 :             if (daylCntrl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4667           28 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "SplitFlux");
    4668              :             } else {
    4669            0 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "DElight");
    4670              :             }
    4671              :             // ( 1=continuous, 2=stepped, 3=continuous/off )
    4672           28 :             if (daylCntrl.LightControlType == LtgCtrlType::Continuous) {
    4673           28 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous");
    4674            0 :             } else if (daylCntrl.LightControlType == LtgCtrlType::Stepped) {
    4675            0 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Stepped");
    4676            0 :             } else if (daylCntrl.LightControlType == LtgCtrlType::ContinuousOff) {
    4677            0 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous/Off");
    4678              :             }
    4679           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtFrac, curRefPt.Name, refPt.fracZoneDaylit);
    4680           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWInst, curRefPt.Name, rLightLevel);
    4681           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWCtrl, curRefPt.Name, rLightLevel * refPt.fracZoneDaylit);
    4682              : 
    4683           28 :             if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
    4684            2 :                 refPt.inBounds = false;
    4685            4 :                 ShowWarningError(state,
    4686            4 :                                  format("GeometryTransformForDaylighting: Reference point X Value outside Zone Min/Max X, Zone={}", zone.Name));
    4687            4 :                 ShowContinueError(state,
    4688            4 :                                   format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
    4689            2 :                                          refPt.absCoords.x,
    4690            2 :                                          zone.MinimumX,
    4691            2 :                                          zone.MaximumX));
    4692            4 :                 ShowContinueError(
    4693              :                     state,
    4694            4 :                     format("...X Reference Distance Outside MinimumX= {:.4R} m.",
    4695            4 :                            (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
    4696              :             }
    4697           28 :             if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
    4698            2 :                 refPt.inBounds = false;
    4699            4 :                 ShowWarningError(state,
    4700            4 :                                  format("GeometryTransformForDaylighting: Reference point Y Value outside Zone Min/Max Y, Zone={}", zone.Name));
    4701            4 :                 ShowContinueError(state,
    4702            4 :                                   format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
    4703            2 :                                          refPt.absCoords.x,
    4704            2 :                                          zone.MinimumY,
    4705            2 :                                          zone.MaximumY));
    4706            4 :                 ShowContinueError(
    4707              :                     state,
    4708            4 :                     format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
    4709            4 :                            (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
    4710              :             }
    4711           28 :             if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
    4712            0 :                 refPt.inBounds = false;
    4713            0 :                 ShowWarningError(state,
    4714            0 :                                  format("GeometryTransformForDaylighting: Reference point Z Value outside Zone Min/Max Z, Zone={}", zone.Name));
    4715            0 :                 ShowContinueError(state,
    4716            0 :                                   format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
    4717            0 :                                          refPt.absCoords.z,
    4718            0 :                                          zone.MinimumZ,
    4719            0 :                                          zone.MaximumZ));
    4720            0 :                 ShowContinueError(
    4721              :                     state,
    4722            0 :                     format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
    4723            0 :                            (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
    4724              :             }
    4725              :         } // for (refPt)
    4726              :     }     // for (daylightCtrl)
    4727           11 : } // GeometryTransformForDaylighting()
    4728              : 
    4729           14 : void GetInputDayliteRefPt(EnergyPlusData &state, bool &ErrorsFound)
    4730              : {
    4731              :     // Perform GetInput function for the Daylighting:ReferencePoint object
    4732              :     // Glazer - July 2016
    4733           14 :     auto const &dl = state.dataDayltg;
    4734           14 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4735           14 :     auto const &s_ipsc = state.dataIPShortCut;
    4736           14 :     s_ipsc->cCurrentModuleObject = "Daylighting:ReferencePoint";
    4737              : 
    4738           14 :     int RefPtNum = 0;
    4739              :     int IOStat;
    4740              :     int NumAlpha;
    4741              :     int NumNumber;
    4742              : 
    4743           14 :     int TotRefPoints = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4744              : 
    4745           14 :     dl->DaylRefPt.allocate(TotRefPoints);
    4746           50 :     for (auto &pt : dl->DaylRefPt) {
    4747           72 :         ip->getObjectItem(state,
    4748           36 :                           s_ipsc->cCurrentModuleObject,
    4749              :                           ++RefPtNum,
    4750           36 :                           s_ipsc->cAlphaArgs,
    4751              :                           NumAlpha,
    4752           36 :                           s_ipsc->rNumericArgs,
    4753              :                           NumNumber,
    4754              :                           IOStat,
    4755           36 :                           s_ipsc->lNumericFieldBlanks,
    4756           36 :                           s_ipsc->lAlphaFieldBlanks,
    4757           36 :                           s_ipsc->cAlphaFieldNames,
    4758           36 :                           s_ipsc->cNumericFieldNames);
    4759           36 :         pt.Name = s_ipsc->cAlphaArgs(1);
    4760           36 :         pt.ZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
    4761           36 :         if (pt.ZoneNum == 0) {
    4762            0 :             int spaceNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->space);
    4763            0 :             if (spaceNum == 0) {
    4764            0 :                 ShowSevereError(state,
    4765            0 :                                 format("{}=\"{}\", invalid {}=\"{}\".",
    4766            0 :                                        s_ipsc->cCurrentModuleObject,
    4767            0 :                                        s_ipsc->cAlphaArgs(1),
    4768            0 :                                        s_ipsc->cAlphaFieldNames(2),
    4769            0 :                                        s_ipsc->cAlphaArgs(2)));
    4770            0 :                 ErrorsFound = true;
    4771              :             } else {
    4772            0 :                 pt.ZoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
    4773              :             }
    4774              :         }
    4775           36 :         pt.coords = {s_ipsc->rNumericArgs(1), s_ipsc->rNumericArgs(2), s_ipsc->rNumericArgs(3)};
    4776              :     }
    4777           14 : }
    4778              : 
    4779          134 : bool doesDayLightingUseDElight(EnergyPlusData const &state)
    4780              : {
    4781          134 :     auto const &dl = state.dataDayltg;
    4782          147 :     for (auto const &znDayl : dl->daylightControl) {
    4783           14 :         if (znDayl.DaylightMethod == DaylightingMethod::DElight) {
    4784            1 :             return true;
    4785              :         }
    4786              :     }
    4787          133 :     return false;
    4788              : }
    4789              : 
    4790          107 : void CheckTDDsAndLightShelvesInDaylitZones(EnergyPlusData &state)
    4791              : {
    4792              :     // SUBROUTINE INFORMATION:
    4793              :     //       AUTHOR         Brent Griffith
    4794              :     //       DATE WRITTEN   Dec 2007
    4795              : 
    4796              :     // PURPOSE OF THIS SUBROUTINE:
    4797              :     // This subroutine checks daylighting input for TDDs and light shelfs
    4798              :     //  which need to be checked after daylighting input has been read in (CR 7145)
    4799              :     //  (eventually this should be changed once/if implementations change to decouple from daylighting calcs so that
    4800              :     //  these devices can be used in models without daylighting controls
    4801              :     // CR 7145 was for TDDs, but also implenting check for light shelves, the other "daylighting device"
    4802              : 
    4803              :     // METHODOLOGY EMPLOYED:
    4804              :     // loop thru daylighting devices and check that their zones have daylight controls
    4805              : 
    4806              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4807          107 :     auto &s_surf = state.dataSurface;
    4808              : 
    4809          107 :     bool ErrorsFound = false;
    4810              : 
    4811          109 :     for (auto const &pipe : state.dataDaylightingDevicesData->TDDPipe) {
    4812            2 :         int SurfNum = pipe.Diffuser;
    4813            2 :         if (SurfNum > 0) {
    4814            2 :             int const pipeEnclNum = s_surf->Surface(SurfNum).SolarEnclIndex;
    4815            2 :             if (state.dataViewFactor->EnclSolInfo(pipeEnclNum).TotalEnclosureDaylRefPoints == 0) {
    4816            4 :                 ShowWarningError(state,
    4817            4 :                                  format("DaylightingDevice:Tubular = {}:  is not connected to a Zone that has Daylighting, no visible transmittance "
    4818              :                                         "will be modeled through the daylighting device.",
    4819            2 :                                         pipe.Name));
    4820              :             }
    4821              :         } else { // SurfNum == 0
    4822              :             // should not come here (would have already been caught in TDD get input), but is an error
    4823            0 :             ShowSevereError(state, format("DaylightingDevice:Tubular = {}:  Diffuser surface not found ", pipe.Name));
    4824            0 :             ErrorsFound = true;
    4825              :         }
    4826              :     } // for (pipe)
    4827              : 
    4828          107 :     for (auto const &shelf : state.dataDaylightingDevicesData->Shelf) {
    4829            0 :         if (shelf.Window == 0) {
    4830              :             // should not come here (would have already been caught in shelf get input), but is an error
    4831            0 :             ShowSevereError(state, format("DaylightingDevice:Shelf = {}:  window not found ", shelf.Name));
    4832            0 :             ErrorsFound = true;
    4833              :         }
    4834              :     } // for (shelf)
    4835              : 
    4836          107 :     if (ErrorsFound) ShowFatalError(state, "CheckTDDsAndLightShelvesInDaylitZones: Errors in DAYLIGHTING input.");
    4837          107 : }
    4838              : 
    4839          108 : void AssociateWindowShadingControlWithDaylighting(EnergyPlusData &state)
    4840              : {
    4841          108 :     auto &dl = state.dataDayltg;
    4842          108 :     auto &s_surf = state.dataSurface;
    4843              : 
    4844          112 :     for (auto &winShadeControl : s_surf->WindowShadingControl) {
    4845            4 :         if (winShadeControl.DaylightingControlName.empty()) continue;
    4846            3 :         int found = -1;
    4847            9 :         for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    4848            8 :             if (Util::SameString(winShadeControl.DaylightingControlName, dl->daylightControl(daylightCtrlNum).Name)) {
    4849            2 :                 found = daylightCtrlNum;
    4850            2 :                 break;
    4851              :             }
    4852              :         }
    4853            3 :         if (found > 0) {
    4854            2 :             winShadeControl.DaylightControlIndex = found;
    4855              :         } else {
    4856            2 :             ShowWarningError(state, "AssociateWindowShadingControlWithDaylighting: Daylighting object name used in WindowShadingControl not found.");
    4857            2 :             ShowContinueError(state,
    4858            2 :                               format("..The WindowShadingControl object=\"{}\" and referenes an object named: \"{}\"",
    4859            1 :                                      winShadeControl.Name,
    4860            1 :                                      winShadeControl.DaylightingControlName));
    4861              :         }
    4862              :     }
    4863          108 : } // AssociateWindowShadingControlWithDaylighting()
    4864              : 
    4865            7 : void GetLightWellData(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
    4866              : {
    4867              : 
    4868              :     // SUBROUTINE INFORMATION:
    4869              :     //       AUTHOR         Fred Winkelmann
    4870              :     //       DATE WRITTEN   Apr 2004
    4871              : 
    4872              :     // PURPOSE OF THIS SUBROUTINE:
    4873              :     // Gets data for a light well associated with a rectangular exterior window.
    4874              :     // Calculates light well efficiency, defined as the ratio of the amount of visible
    4875              :     // solar radiation leaving a well to the amount entering the well.
    4876              : 
    4877              :     // METHODOLOGY EMPLOYED:
    4878              :     // Based on fit to Fig. 8-21, "Efficiency factors for various depths of light wells
    4879              :     // based on well-interreflectance values," Lighting Handbook, 8th Edition, Illuminating
    4880              :     // Engineering Society of North America, 1993.
    4881              : 
    4882              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4883              : 
    4884              :     int IOStat;        // IO Status when calling get input subroutine
    4885              :     int NumAlpha;      // Number of alpha names being passed
    4886              :     int NumProp;       // Number of properties being passed
    4887              :     int TotLightWells; // Total Light Well objects
    4888              : 
    4889            7 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4890            7 :     auto &s_surf = state.dataSurface;
    4891            7 :     auto const &s_ipsc = state.dataIPShortCut;
    4892              : 
    4893              :     // Get the total number of Light Well objects
    4894            7 :     s_ipsc->cCurrentModuleObject = "DaylightingDevice:LightWell";
    4895            7 :     TotLightWells = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4896            7 :     if (TotLightWells == 0) return;
    4897              : 
    4898            0 :     for (int loop = 1; loop <= TotLightWells; ++loop) {
    4899              : 
    4900            0 :         ip->getObjectItem(state,
    4901            0 :                           s_ipsc->cCurrentModuleObject,
    4902              :                           loop,
    4903            0 :                           s_ipsc->cAlphaArgs,
    4904              :                           NumAlpha,
    4905            0 :                           s_ipsc->rNumericArgs,
    4906              :                           NumProp,
    4907              :                           IOStat,
    4908            0 :                           s_ipsc->lNumericFieldBlanks,
    4909            0 :                           s_ipsc->lAlphaFieldBlanks,
    4910            0 :                           s_ipsc->cAlphaFieldNames,
    4911            0 :                           s_ipsc->cNumericFieldNames);
    4912              : 
    4913            0 :         int SurfNum = Util::FindItemInList(s_ipsc->cAlphaArgs(1), s_surf->Surface);
    4914            0 :         if (SurfNum == 0) {
    4915            0 :             ShowSevereError(
    4916            0 :                 state, format("{}: invalid {}=\"{}\" not found.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
    4917            0 :             ErrorsFound = true;
    4918            0 :             continue;
    4919              :         }
    4920              : 
    4921            0 :         auto const &surf = s_surf->Surface(SurfNum);
    4922            0 :         auto &surfWin = s_surf->SurfaceWindow(SurfNum);
    4923              :         // Check that associated surface is an exterior window
    4924              :         // True if associated surface is not an exterior window
    4925            0 :         if (surf.Class != SurfaceClass::Window && surf.ExtBoundCond != ExternalEnvironment) {
    4926            0 :             ShowSevereError(state,
    4927            0 :                             format("{}: invalid {}=\"{}\" - not an exterior window.",
    4928            0 :                                    s_ipsc->cCurrentModuleObject,
    4929            0 :                                    s_ipsc->cAlphaFieldNames(1),
    4930            0 :                                    s_ipsc->cAlphaArgs(1)));
    4931            0 :             ErrorsFound = true;
    4932            0 :             continue;
    4933              :         }
    4934              : 
    4935              :         // Associated surface is an exterior window; calculate light well efficiency.
    4936            0 :         surfWin.lightWellEff = 1.0;
    4937            0 :         Real64 HeightWell = s_ipsc->rNumericArgs(1);  // Well height (from window to bottom of well) (m)
    4938            0 :         Real64 PerimWell = s_ipsc->rNumericArgs(2);   // Well perimeter (at bottom of well) (m)
    4939            0 :         Real64 AreaWell = s_ipsc->rNumericArgs(3);    // Well area (at bottom of well) (m2)
    4940            0 :         Real64 VisReflWell = s_ipsc->rNumericArgs(4); // Area-weighted visible reflectance of well walls
    4941              : 
    4942              :         // Warning if light well area is less than window area
    4943            0 :         if (AreaWell < (surf.Area + s_surf->SurfWinDividerArea(SurfNum) - 0.1)) {
    4944            0 :             ShowSevereError(
    4945            0 :                 state, format("{}: invalid {}=\"{}\" - Areas.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
    4946            0 :             ShowContinueError(state, format("has Area of Bottom of Well={:.1R} that is less than window area={:.1R}", surf.Area, AreaWell));
    4947              :         }
    4948              : 
    4949            0 :         if (HeightWell >= 0.0 && PerimWell > 0.0 && AreaWell > 0.0) {
    4950            0 :             Real64 WellCavRatio = 2.5 * HeightWell * PerimWell / AreaWell;
    4951            0 :             surfWin.lightWellEff = std::exp(-WellCavRatio * (0.16368 - 0.14467 * VisReflWell));
    4952              :         }
    4953              :     } // End of loop over light well objects
    4954              : } // GetLightWellData()
    4955              : 
    4956         2382 : inline WinCover findWinShadingStatus(EnergyPlusData &state, int const IWin)
    4957              : {
    4958              :     // Return the window shading status, 1=unshaded, 2=shaded
    4959              : 
    4960         2382 :     auto &s_surf = state.dataSurface;
    4961         2382 :     bool WinShadedNoGlareControl = IS_SHADED_NO_GLARE_CTRL(s_surf->SurfWinShadingFlag(IWin));
    4962              : 
    4963         4758 :     return ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) && (WinShadedNoGlareControl || s_surf->SurfWinSolarDiffusing(IWin)))
    4964         4758 :                ? WinCover::Shaded
    4965         2382 :                : WinCover::Bare;
    4966              : }
    4967              : 
    4968         1369 : Real64 DayltgGlare(EnergyPlusData &state,
    4969              :                    int IL,                   // Reference point index: 1=first ref pt, 2=second ref pt
    4970              :                    Real64 BLUM,              // Window background (surround) luminance (cd/m2)
    4971              :                    int const daylightCtrlNum // Current daylighting control number
    4972              : )
    4973              : {
    4974              : 
    4975              :     // SUBROUTINE INFORMATION:
    4976              :     //       AUTHOR         Fred Winkelmann
    4977              :     //       DATE WRITTEN   July 1997
    4978              : 
    4979              :     // PURPOSE OF THIS SUBROUTINE:
    4980              :     // CALCULATE GLARE INDEX.
    4981              : 
    4982              :     // METHODOLOGY EMPLOYED:
    4983              :     // Called from DayltgInteriorIllum.  Finds glare index at reference
    4984              :     // point no. IL in a space using the Cornell/BRS large source
    4985              :     // glare formula. BLUM is the background luminance (cd/m**2).
    4986              :     // TH comment 1/21/2010: The SurfaceWindow(IWin)%ShadingFlag has to be set
    4987              :     //  before calling this subroutine. For switchable glazings this is tricky
    4988              :     //  because the ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop)
    4989              :     //  may change every time step to represent intermediate switched state.
    4990              : 
    4991              :     // REFERENCES:
    4992              :     // Based on DOE-2.1E subroutine DGLARE.
    4993              : 
    4994         1369 :     Real64 GTOT = 0.0; // Glare constant
    4995              : 
    4996         1369 :     auto &dl = state.dataDayltg;
    4997              : 
    4998              :     // Loop over exterior windows associated with zone
    4999         1369 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5000         1369 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
    5001         2738 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5002         1369 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5003         1369 :         WinCover winCover = findWinShadingStatus(state, IWin);
    5004              :         // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
    5005              :         // below, which is (0.2936)**0.6
    5006         1369 :         auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
    5007         1369 :         Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
    5008         1369 :         Real64 GTOT2 = BLUM + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
    5009         1369 :         GTOT += GTOT1 / (GTOT2 + 0.000001);
    5010              :     }
    5011              : 
    5012              :     // Glare index (adding 0.000001 prevents LOG10 (0))
    5013         1369 :     return max(0.0, 10.0 * std::log10(GTOT + 0.000001));
    5014              : }
    5015              : 
    5016            0 : void DayltgGlareWithIntWins(EnergyPlusData &state,
    5017              :                             int const daylightCtrlNum // Current daylighting control number
    5018              : )
    5019              : {
    5020              : 
    5021              :     // SUBROUTINE INFORMATION:
    5022              :     //       AUTHOR         Fred Winkelmann
    5023              :     //       DATE WRITTEN   March 2004
    5024              : 
    5025              :     // PURPOSE OF THIS SUBROUTINE:
    5026              :     // Calculate daylighting glare index for zones with interior windows.
    5027              : 
    5028              :     // METHODOLOGY EMPLOYED:
    5029              :     // Finds glare index at reference point IL in a daylit zone using the Cornell/BRS large source
    5030              :     // glare formula. Takes into account inter-reflected illuminance from light entering
    5031              :     // the zone through interior windows
    5032              : 
    5033              :     // REFERENCES:
    5034              :     // Based on subroutine DayltgGlare.
    5035              : 
    5036            0 :     Real64 GTOT = 0.0; // Glare constant(?) // TODO: does this need to be reset for every refPt?
    5037              : 
    5038              :     // Calculate background luminance including effect of inter-reflected illuminance from light
    5039              :     // entering zone through its interior windows
    5040            0 :     auto &dl = state.dataDayltg;
    5041            0 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5042            0 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
    5043            0 :     int RefPoints = thisDayltgCtrl.TotalDaylRefPoints; // Number of daylighting reference points in zone
    5044            0 :     for (int IL = 1; IL <= RefPoints; ++IL) {
    5045            0 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    5046              : 
    5047            0 :         Real64 BackgroundLum = refPt.lums[iLum_Back] + thisEnclDaylight.InterReflIllFrIntWins * thisEnclDaylight.aveVisDiffReflect / Constant::Pi;
    5048            0 :         BackgroundLum = max(refPt.illumSetPoint * thisEnclDaylight.aveVisDiffReflect / Constant::Pi, BackgroundLum);
    5049              : 
    5050              :         // Loop over exterior windows associated with zone
    5051            0 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5052            0 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5053            0 :             WinCover winCover = findWinShadingStatus(state, IWin);
    5054              :             // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
    5055              :             // below, which is (0.2936)**0.6
    5056            0 :             auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
    5057            0 :             Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
    5058            0 :             Real64 GTOT2 = BackgroundLum + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
    5059            0 :             GTOT += GTOT1 / (GTOT2 + 0.000001);
    5060              :         }
    5061              : 
    5062              :         // Glare index
    5063            0 :         refPt.glareIndex = max(0.0, 10.0 * std::log10(GTOT + 0.000001));
    5064              :     } // for (IL)
    5065            0 : } // DaylGlareWithIntWins()
    5066              : 
    5067          551 : void DayltgExtHorizIllum(EnergyPlusData &state,
    5068              :                          Illums &HI // Horizontal illuminance from sky for different sky types
    5069              : )
    5070              : {
    5071              : 
    5072              :     // SUBROUTINE INFORMATION:
    5073              :     //       AUTHOR         Fred Winkelmann
    5074              :     //       DATE WRITTEN   July 1997
    5075              : 
    5076              :     // PURPOSE OF THIS SUBROUTINE:
    5077              :     // Calculates exterior daylight illuminance.
    5078              : 
    5079              :     // METHODOLOGY EMPLOYED:
    5080              :     // Called by CalcDayltgCoefficients. Calculates illuminance
    5081              :     // on unobstructed horizontal surface by integrating
    5082              :     // over the luminance distribution of standard CIE skies.
    5083              :     // Calculates horizontal beam illuminance.
    5084              :     // REFERENCES:
    5085              :     // Based on DOE-2.1E subroutine DHILL.
    5086              : 
    5087              :     // Argument array dimensioning
    5088              : 
    5089              :     // SUBROUTINE PARAMETER DEFINITIONS:
    5090          551 :     Real64 constexpr DTH = (2.0 * Constant::Pi) / double(NTH); // Sky integration azimuth stepsize (radians)
    5091          551 :     Real64 constexpr DPH = Constant::PiOvr2 / double(NPH);     // Sky integration altitude stepsize (radians)
    5092              : 
    5093              :     // Integrate to obtain illuminance from sky.
    5094              :     // The contribution in lumens/m2 from a patch of sky at altitude PH and azimuth TH
    5095              :     // is L(TH,PH)*SIN(PH)*COS(PH)*DTH*DPH, where L(TH,PH) is the luminance
    5096              :     // of the patch in cd/m2.
    5097          551 :     auto &dl = state.dataDayltg;
    5098              : 
    5099              :     //  Init
    5100          551 :     if (dl->DayltgExtHorizIllum_firstTime) {
    5101           27 :         for (int IPH = 1; IPH <= NPH; ++IPH) {
    5102           24 :             dl->PH[IPH] = (IPH - 0.5) * DPH;
    5103           24 :             dl->SPHCPH[IPH] = std::sin(dl->PH[IPH]) * std::cos(dl->PH[IPH]); // DA = COS(PH)*DTH*DPH
    5104              :         }
    5105           57 :         for (int ITH = 1; ITH <= NTH; ++ITH) {
    5106           54 :             dl->TH[ITH] = (ITH - 0.5) * DTH;
    5107              :         }
    5108            3 :         dl->DayltgExtHorizIllum_firstTime = false;
    5109              :     }
    5110              : 
    5111         2204 :     HI = Illums();
    5112              : 
    5113              :     // Sky integration
    5114         4959 :     for (int IPH = 1; IPH <= NPH; ++IPH) {
    5115         4408 :         Real64 const PH_IPH = dl->PH[IPH];
    5116         4408 :         Real64 const SPHCPH_IPH = dl->SPHCPH[IPH];
    5117        83752 :         for (int ITH = 1; ITH <= NTH; ++ITH) {
    5118        79344 :             Real64 const TH_ITH = dl->TH[ITH];
    5119       396720 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5120       317376 :                 HI.sky[iSky] += DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH_ITH, PH_IPH) * SPHCPH_IPH;
    5121              :             }
    5122              :         }
    5123              :     }
    5124              : 
    5125         2755 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5126         2204 :         HI.sky[iSky] *= DTH * DPH;
    5127              :     }
    5128              : 
    5129              :     // Direct solar horizontal illum (for unit direct normal illuminance)
    5130          551 :     HI.sun = dl->sunAngles.sinPhi * 1.0;
    5131          551 : } // DayltgExtHorizIllum()
    5132              : 
    5133              : // Product of solar transmittances of exterior obstructions
    5134       386914 : Real64 DayltgHitObstruction(EnergyPlusData &state,
    5135              :                             int const IHOUR,           // Hour number
    5136              :                             int const IWin,            // Window index
    5137              :                             Vector3<Real64> const &R1, // Origin of ray (m)
    5138              :                             Vector3<Real64> const &RN  // Unit vector along ray
    5139              : )
    5140              : {
    5141              : 
    5142              :     // SUBROUTINE INFORMATION:
    5143              :     //       AUTHOR         Fred Winkelmann
    5144              :     //       DATE WRITTEN   July 1997
    5145              :     //       MODIFIED       FCW, May 2003: update list of surface classes that qualify as obstructions;
    5146              :     //                        add interior surfaces as possible obstructors;
    5147              :     //                        return from DO loop over surfaces as soon as any obstruction is hit;
    5148              :     //                      FCW, July 2003: change from returning whether an obstruction is hit or not
    5149              :     //                        to product of solar transmittances of hit obstructions.
    5150              :     //                      FCW, Nov 2003: remove interior surfaces as possible obstructors since there
    5151              :     //                        is now a separate check for interior obstructions; exclude windows and
    5152              :     //                        doors as obstructors since if they are obstructors their base surfaces will
    5153              :     //                        also be obstructors
    5154              :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5155              : 
    5156              :     // PURPOSE OF THIS SUBROUTINE:
    5157              :     // Determines the product of the solar transmittances of the obstructions hit by a ray
    5158              :     // from R1 in the direction of vector RN.
    5159              : 
    5160              :     // REFERENCES:
    5161              :     // Based on DOE-2.1E subroutine DHITSH.
    5162              : 
    5163       386914 :     auto &s_surf = state.dataSurface;
    5164              :     // Local declarations
    5165              :     bool hit; // True iff a particular obstruction is hit
    5166              : 
    5167       386914 :     Real64 ObTrans = 1.0;
    5168              : 
    5169       386914 :     auto const &window = s_surf->Surface(IWin);
    5170       386914 :     int const window_iBaseSurf = window.BaseSurf;
    5171              : 
    5172       386914 :     Vector3<Real64> DayltgHitObstructionHP;
    5173              :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5174              :     // Building elements are assumed to be opaque
    5175              :     // A shadowing surface is opaque unless its transmittance schedule value is non-zero
    5176       386914 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5177              : 
    5178      1313432 :         for (int ISurf : s_surf->AllShadowPossObstrSurfaceList) {
    5179       926518 :             auto const &surface = s_surf->Surface(ISurf);
    5180       926518 :             SurfaceClass IType = surface.Class;
    5181       926518 :             if ((IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf)) {
    5182       539604 :                 hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
    5183       539604 :                 if (hit) { // Building element is hit (assumed opaque)
    5184            0 :                     ObTrans = 0.0;
    5185            0 :                     break;
    5186              :                 }
    5187       386914 :             } else if (surface.IsShadowing) {
    5188            0 :                 hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
    5189            0 :                 if (hit) { // Shading surface is hit
    5190              :                     // Get solar transmittance of the shading surface
    5191            0 :                     Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
    5192            0 :                     if (Trans < 1.e-6) {
    5193            0 :                         ObTrans = 0.0;
    5194            0 :                         break;
    5195              :                     } else {
    5196            0 :                         ObTrans *= Trans;
    5197              :                     }
    5198              :                 }
    5199              :             }
    5200              :         }
    5201              : 
    5202              :     } else { // Surface octree search
    5203              : 
    5204            0 :         auto const &window_base(window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window);
    5205            0 :         auto const *window_base_p(&window_base);
    5206              : 
    5207              :         // Lambda function for the octree to test for surface hit and update transmittance if hit
    5208            0 :         auto solarTransmittance = [=, &state, &R1, &RN, &hit, &ObTrans](SurfaceData const &surface) -> bool {
    5209            0 :             if (!surface.IsShadowPossibleObstruction) return false; // Do Consider separate octree without filtered surfaces
    5210            0 :             DataSurfaces::SurfaceClass const sClass(surface.Class);
    5211            0 :             Vector3<Real64> HP;
    5212            0 :             if ((sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && (&surface != window_base_p)) {
    5213            0 :                 hit = PierceSurface(surface, R1, RN, HP);
    5214            0 :                 if (hit) { // Building element is hit (assumed opaque)
    5215            0 :                     ObTrans = 0.0;
    5216            0 :                     return true;
    5217              :                 }
    5218            0 :             } else if (surface.IsShadowing) {
    5219            0 :                 hit = PierceSurface(surface, R1, RN, HP);
    5220            0 :                 if (hit) { // Shading surface is hit
    5221              :                     // Get solar transmittance of the shading surface
    5222            0 :                     Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
    5223            0 :                     if (Trans < 1.e-6) {
    5224            0 :                         ObTrans = 0.0;
    5225            0 :                         return true;
    5226              :                     } else {
    5227            0 :                         ObTrans *= Trans;
    5228            0 :                         return ObTrans == 0.0;
    5229              :                     }
    5230              :                 }
    5231              :             }
    5232            0 :             return false;
    5233            0 :         };
    5234              : 
    5235              :         // Check octree surface candidates for hits: short circuits if zero transmittance reached
    5236            0 :         Vector3<Real64> const RN_inv(SurfaceOctreeCube::safe_inverse(RN));
    5237            0 :         state.dataHeatBalMgr->surfaceOctree.processSomeSurfaceRayIntersectsCube(state, R1, RN, RN_inv, solarTransmittance);
    5238            0 :     }
    5239              : 
    5240       386914 :     return ObTrans;
    5241       386914 : } // DayltgHitObstruction()
    5242              : 
    5243       368194 : bool DayltgHitInteriorObstruction(EnergyPlusData &state,
    5244              :                                   int const IWin,            // Window index
    5245              :                                   Vector3<Real64> const &R1, // Origin of ray (m)
    5246              :                                   Vector3<Real64> const &R2  // Destination of ray (m)
    5247              : )
    5248              : {
    5249              : 
    5250              :     // SUBROUTINE INFORMATION:
    5251              :     //       AUTHOR         Fred Winkelmann
    5252              :     //       DATE WRITTEN   July 1997
    5253              :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5254              : 
    5255              :     // PURPOSE OF THIS SUBROUTINE:
    5256              :     // This subroutine checks for interior obstructions between reference point and window element.
    5257              : 
    5258       368194 :     auto &s_surf = state.dataSurface;
    5259              : 
    5260              :     // Preconditions
    5261       368194 :     assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
    5262              : 
    5263       368194 :     bool hit = false;
    5264       368194 :     Vector3<Real64> RN = (R2 - R1).normalize(); // Make unit vector
    5265       368194 :     Real64 const d12 = distance(R1, R2);        // Distance between R1 and R2
    5266              : 
    5267       368194 :     auto const &window = s_surf->Surface(IWin);
    5268       368194 :     int const window_Enclosure = window.SolarEnclIndex;
    5269       368194 :     int const window_iBaseSurf = window.BaseSurf;
    5270       368194 :     auto const &window_base = window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window;
    5271       368194 :     int const window_base_iExtBoundCond = window_base.ExtBoundCond;
    5272              : 
    5273              :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5274       368194 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5275              :         // Hit coordinates, if ray hits an obstruction
    5276       368194 :         Vector3<Real64> DayltgHitInteriorObstructionHP;
    5277              : 
    5278      1560449 :         for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
    5279      1192255 :             auto const &surface = s_surf->Surface(ISurf);
    5280      1192255 :             SurfaceClass IType = surface.Class;
    5281      1192255 :             if ((surface.IsShadowing) ||                         // Shadowing surface
    5282      1192255 :                 ((surface.SolarEnclIndex == window_Enclosure) && // Wall/ceiling/floor is in same zone as window
    5283      1190414 :                  (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf) &&
    5284              :                  (ISurf != window_base_iExtBoundCond))) // Exclude window's base or base-adjacent surfaces
    5285              :             {
    5286       454026 :                 hit = PierceSurface(state, ISurf, R1, RN, d12, DayltgHitInteriorObstructionHP); // Check if R2-R1 segment pierces surface
    5287       454026 :                 if (hit) break;                                                                 // Segment pierces surface: Don't check the rest
    5288              :             }
    5289              :         }
    5290              : 
    5291       368194 :     } else { // Surface octree search
    5292              : 
    5293            0 :         auto const *window_base_p = &window_base;
    5294            0 :         auto const &window_base_adjacent = window_base_iExtBoundCond > 0 ? s_surf->Surface(window_base_iExtBoundCond) : window_base;
    5295            0 :         auto const *window_base_adjacent_p = &window_base_adjacent;
    5296              : 
    5297              :         // Lambda function for the octree to test for surface hit
    5298            0 :         auto surfaceHit = [=, &R1, &hit](SurfaceData const &surface) -> bool {
    5299            0 :             DataSurfaces::SurfaceClass const sClass = surface.Class;
    5300            0 :             Vector3<Real64> HP;                                  // Hit point
    5301            0 :             if ((surface.IsShadowing) ||                         // Shadowing surface
    5302            0 :                 ((surface.SolarEnclIndex == window_Enclosure) && // Surface is in same zone as window
    5303            0 :                  (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5304            0 :                  (&surface != window_base_p) && (&surface != window_base_adjacent_p))) // Exclude window's base or base-adjacent surfaces
    5305              :             {
    5306            0 :                 hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5307            0 :                 return hit;
    5308              :             } else {
    5309            0 :                 return false;
    5310              :             }
    5311            0 :         };
    5312              : 
    5313              :         // Check octree surface candidates until a hit is found, if any
    5314            0 :         state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
    5315            0 :     }
    5316              : 
    5317       368194 :     return hit;
    5318       368194 : } // DayltgHitInteriorObstruction()
    5319              : 
    5320            0 : bool DayltgHitBetWinObstruction(EnergyPlusData &state,
    5321              :                                 int const IWin1,           // Surface number of origin window
    5322              :                                 int const IWin2,           // Surface number of destination window
    5323              :                                 Vector3<Real64> const &R1, // Origin of ray (on IWin1) (m)
    5324              :                                 Vector3<Real64> const &R2  // Destination of ray (on IWin2) (m)
    5325              : )
    5326              : {
    5327              : 
    5328              :     // SUBROUTINE INFORMATION:
    5329              :     //       AUTHOR         Fred Winkelmann
    5330              :     //       DATE WRITTEN   Feb 2004
    5331              :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5332              : 
    5333              :     // PURPOSE OF THIS SUBROUTINE:
    5334              :     // Determines if a ray from point R1 on window IWin1 to point R2
    5335              :     // on window IWin2 hits an obstruction
    5336              : 
    5337            0 :     auto &s_surf = state.dataSurface;
    5338              : 
    5339              :     // Preconditions
    5340            0 :     assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
    5341              : 
    5342              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5343              :     SurfaceClass IType; // Surface type/class
    5344              : 
    5345            0 :     bool hit = false;
    5346            0 :     Vector3<Real64> RN = (R2 - R1).normalize(); // Unit vector
    5347              : 
    5348            0 :     Real64 const d12 = distance(R1, R2); // Distance between R1 and R2 (m)
    5349              : 
    5350            0 :     auto const &window1 = s_surf->Surface(IWin1);
    5351            0 :     int const window1_iBaseSurf = window1.BaseSurf;
    5352            0 :     auto const &window1_base = window1_iBaseSurf > 0 ? s_surf->Surface(window1_iBaseSurf) : window1;
    5353            0 :     int const window1_base_iExtBoundCond = window1_base.ExtBoundCond;
    5354              : 
    5355            0 :     auto const &window2 = s_surf->Surface(IWin2);
    5356            0 :     int const window2_Enclosure = window2.SolarEnclIndex;
    5357            0 :     int const window2_iBaseSurf = window2.BaseSurf;
    5358            0 :     auto const &window2_base = window2_iBaseSurf > 0 ? s_surf->Surface(window2_iBaseSurf) : window2;
    5359            0 :     int const window2_base_iExtBoundCond = window2_base.ExtBoundCond;
    5360              : 
    5361              :     // Preconditions
    5362              :     //        assert( window1.Zone == window2_Zone ); //? This is violated in PurchAirWithDoubleFacadeDaylighting so then why the asymmetry
    5363              :     // of  only checking for wall/roof/floor for window2 zone below?
    5364              : 
    5365              :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5366            0 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5367              : 
    5368            0 :         for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
    5369            0 :             auto const &surface = s_surf->Surface(ISurf);
    5370            0 :             IType = surface.Class;
    5371            0 :             if ((surface.IsShadowing) ||                          // Shadowing surface
    5372            0 :                 ((surface.SolarEnclIndex == window2_Enclosure) && // Wall/ceiling/floor is in same zone as windows
    5373            0 :                  (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5374            0 :                  (ISurf != window1_iBaseSurf) && (ISurf != window2_iBaseSurf) &&                                 // Exclude windows' base surfaces
    5375            0 :                  (ISurf != window1_base_iExtBoundCond) && (ISurf != window2_base_iExtBoundCond))) // Exclude windows' base-adjacent surfaces
    5376              :             {
    5377            0 :                 Vector3<Real64> HP;
    5378            0 :                 hit = PierceSurface(state, ISurf, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5379            0 :                 if (hit) break;                                     // Segment pierces surface: Don't check the rest
    5380            0 :             }
    5381              :         }
    5382              : 
    5383              :     } else { // Surface octree search
    5384              : 
    5385            0 :         auto const *window1_base_p = &window1_base;
    5386            0 :         auto const &window1_base_adjacent = window1_base_iExtBoundCond > 0 ? s_surf->Surface(window1_base_iExtBoundCond) : window1_base;
    5387            0 :         auto const *window1_base_adjacent_p = &window1_base_adjacent;
    5388              : 
    5389            0 :         auto const *window2_base_p = &window2_base;
    5390            0 :         auto const &window2_base_adjacent = (window2_base_iExtBoundCond > 0) ? s_surf->Surface(window2_base_iExtBoundCond) : window2_base;
    5391            0 :         auto const *window2_base_adjacent_p = &window2_base_adjacent;
    5392              : 
    5393              :         // Lambda function for the octree to test for surface hit
    5394            0 :         auto surfaceHit = [=, &R1, &RN, &hit](SurfaceData const &surface) -> bool {
    5395            0 :             DataSurfaces::SurfaceClass const sClass = surface.Class;
    5396            0 :             Vector3<Real64> HP;
    5397            0 :             if ((surface.IsShadowing) ||                          // Shadowing surface
    5398            0 :                 ((surface.SolarEnclIndex == window2_Enclosure) && // Surface is in same zone as window
    5399            0 :                  (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5400            0 :                  (&surface != window1_base_p) && (&surface != window2_base_p) &&                                    // Exclude windows' base surfaces
    5401            0 :                  (&surface != window1_base_adjacent_p) && (&surface != window2_base_adjacent_p))) // Exclude windows' base-adjacent surfaces
    5402              :             {
    5403            0 :                 hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5404            0 :                 return hit;
    5405              :             } else {
    5406            0 :                 return false;
    5407              :             }
    5408            0 :         };
    5409              : 
    5410              :         // Check octree surface candidates until a hit is found, if any
    5411            0 :         state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
    5412              :     }
    5413              : 
    5414            0 :     return hit;
    5415            0 : } // DayltingHitBetWinObstruction()
    5416              : 
    5417       249956 : void initDaylighting(EnergyPlusData &state, bool const initSurfaceHeatBalancefirstTime)
    5418              : {
    5419              :     // For daylit zones, calculate interior daylight illuminance at reference points and
    5420              :     // simulate lighting control system to get overhead electric lighting reduction
    5421              :     // factor due to daylighting.
    5422       249956 :     auto &dl = state.dataDayltg;
    5423       249956 :     auto &s_surf = state.dataSurface;
    5424              : 
    5425       338520 :     for (int SurfNum : s_surf->AllExtSolWindowSurfaceList) {
    5426        91270 :         for (auto &refPt : s_surf->SurfaceWindow(SurfNum).refPts) {
    5427         2706 :             refPt.illumFromWinRep = refPt.lumWinRep = 0.0;
    5428              :         }
    5429              :     }
    5430              : 
    5431              :     // Reset space power reduction factors
    5432       622909 :     for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
    5433       372953 :         dl->spacePowerReductionFactor(spaceNum) = 1.0;
    5434              :     }
    5435       251986 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5436         2030 :         auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5437         2030 :         thisDayltgCtrl.PowerReductionFactor = 1.0;
    5438         2030 :         if (state.dataEnvrn->PreviousSolRadPositive) {
    5439              :             // Reset to zero only if there was solar in the previous timestep, otherwise these are already zero
    5440          941 :             dl->enclDaylight(thisDayltgCtrl.enclIndex).InterReflIllFrIntWins = 0.0; // inter-reflected illuminance from interior windows
    5441         2288 :             for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
    5442         1347 :                 auto &refPt = thisDayltgCtrl.refPts(refPtNum);
    5443         1347 :                 refPt.lums[iLum_Illum] = 0.0;
    5444         1347 :                 refPt.glareIndex = 0.0;
    5445         1347 :                 refPt.timeExceedingGlareIndexSetPoint = 0.0;
    5446         1347 :                 refPt.timeExceedingDaylightIlluminanceSetPoint = 0.0;
    5447              :             }
    5448              :         }
    5449              : 
    5450         2030 :         if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0) {
    5451          943 :             if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination");
    5452          943 :             DayltgInteriorIllum(state, daylightCtrlNum);
    5453              :         }
    5454              :     }
    5455              : 
    5456              :     // The following report variables are valid only for daylit zones/enclosures without interior windows
    5457       249956 :     if (state.dataEnvrn->SunIsUp) {
    5458       296113 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    5459       175612 :             if ((state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) ||
    5460          944 :                 (state.dataViewFactor->EnclSolInfo(enclNum).HasInterZoneWindow))
    5461       173724 :                 continue;
    5462              : 
    5463          944 :             auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    5464         1887 :             for (int extWinNum = 1; extWinNum <= thisEnclDaylight.NumOfDayltgExtWins; ++extWinNum) {
    5465          943 :                 int IWin = thisEnclDaylight.DayltgExtWinSurfNums(extWinNum);
    5466          943 :                 WinCover winCover = WinCover::Bare;
    5467         2829 :                 if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
    5468         1886 :                     (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
    5469            0 :                     winCover = WinCover::Shaded;
    5470              :                 }
    5471          943 :                 int refPtCount = 0;
    5472         1886 :                 for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    5473          943 :                     auto &daylCtrl = dl->daylightControl(controlNum);
    5474          943 :                     if (daylCtrl.DaylightMethod != DaylightingMethod::SplitFlux) continue;
    5475              : 
    5476         2293 :                     for (int refPtNum = 1; refPtNum <= daylCtrl.TotalDaylRefPoints; ++refPtNum) {
    5477         1350 :                         ++refPtCount; // Count reference points across each daylighting control in the same enclosure
    5478         1350 :                         auto &refPt = s_surf->SurfaceWindow(IWin).refPts(refPtCount);
    5479         1350 :                         auto const &daylCtrlRefPt = daylCtrl.refPts(refPtNum);
    5480         1350 :                         refPt.illumFromWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Illum][(int)winCover];
    5481         1350 :                         refPt.lumWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Source][(int)winCover];
    5482              :                     }
    5483              :                 } // for (controlNum)
    5484              :             }     // for (extWinNum)
    5485              :         }         // for (enclNum)
    5486              :     }             // if (SunIsUp)
    5487              : 
    5488       249956 :     if (state.dataEnvrn->SunIsUp && (int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
    5489            0 :         if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination for TDD pipes");
    5490            0 :         DayltgInteriorTDDIllum(state);
    5491              :     }
    5492              : 
    5493       251986 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5494         2030 :         auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5495              : 
    5496              :         // RJH DElight Modification Begin - Call to DElight electric lighting control subroutine
    5497              :         // Check if the sun is up and the current Thermal Zone hosts a Daylighting:DElight object
    5498         2030 :         if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0 && (thisDayltgCtrl.DaylightMethod == DaylightingMethod::DElight)) {
    5499            0 :             int zoneNum = thisDayltgCtrl.zoneIndex;
    5500              :             // Call DElight interior illuminance and electric lighting control subroutine
    5501            0 :             Real64 dPowerReducFac = 1.0; // Return value Electric Lighting Power Reduction Factor for current Zone and Timestep
    5502            0 :             Real64 dHISKFFC = state.dataEnvrn->HISKF * DataDElight::LUX2FC;
    5503            0 :             Real64 dHISUNFFC = state.dataEnvrn->HISUNF * DataDElight::LUX2FC;
    5504            0 :             Real64 dSOLCOS1 = state.dataEnvrn->SOLCOS.x;
    5505            0 :             Real64 dSOLCOS2 = state.dataEnvrn->SOLCOS.y;
    5506            0 :             Real64 dSOLCOS3 = state.dataEnvrn->SOLCOS.z;
    5507            0 :             Real64 dLatitude = state.dataEnvrn->Latitude;
    5508            0 :             Real64 dCloudFraction = state.dataEnvrn->CloudFraction;
    5509              :             // Init Error Flag to 0 (no Warnings or Errors) (returned from DElight)
    5510            0 :             int iErrorFlag = 0;
    5511              : 
    5512            0 :             DElightManagerF::DElightElecLtgCtrl(len(state.dataHeatBal->Zone(zoneNum).Name),
    5513            0 :                                                 state.dataHeatBal->Zone(zoneNum).Name,
    5514              :                                                 dLatitude,
    5515              :                                                 dHISKFFC,
    5516              :                                                 dHISUNFFC,
    5517              :                                                 dCloudFraction,
    5518              :                                                 dSOLCOS1,
    5519              :                                                 dSOLCOS2,
    5520              :                                                 dSOLCOS3,
    5521              :                                                 dPowerReducFac,
    5522              :                                                 iErrorFlag);
    5523              :             // Check Error Flag for Warnings or Errors returning from DElight
    5524              :             // RJH 2008-03-07: If no warnings/errors then read refpt illuminances for standard output reporting
    5525            0 :             if (iErrorFlag != 0) {
    5526            0 :                 std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
    5527              :                 // Open DElight Electric Lighting Error File for reading
    5528            0 :                 auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
    5529            0 :                 bool elOpened = iDElightErrorFile.good();
    5530              : 
    5531              :                 // Sequentially read lines in DElight Electric Lighting Error File
    5532              :                 // and process them using standard EPlus warning/error handling calls
    5533            0 :                 bool bEndofErrFile = false;
    5534            0 :                 while (!bEndofErrFile && elOpened) {
    5535            0 :                     auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
    5536            0 :                     if (cErrorLine.eof) {
    5537            0 :                         bEndofErrFile = true;
    5538            0 :                         continue;
    5539              :                     }
    5540              : 
    5541              :                     // Is the current line a Warning message?
    5542            0 :                     if (has_prefix(cErrorLine.data, "WARNING: ")) {
    5543            0 :                         cErrorMsg = cErrorLine.data.substr(9);
    5544            0 :                         ShowWarningError(state, cErrorMsg);
    5545              :                     }
    5546              :                     // Is the current line an Error message?
    5547            0 :                     if (has_prefix(cErrorLine.data, "ERROR: ")) {
    5548            0 :                         cErrorMsg = cErrorLine.data.substr(7);
    5549            0 :                         ShowSevereError(state, cErrorMsg);
    5550            0 :                         iErrorFlag = 1;
    5551              :                     }
    5552            0 :                 }
    5553              : 
    5554              :                 // Close DElight Error File and delete
    5555              : 
    5556            0 :                 if (elOpened) {
    5557            0 :                     iDElightErrorFile.close();
    5558            0 :                     FileSystem::removeFile(iDElightErrorFile.filePath);
    5559              :                 }
    5560              :                 // If any DElight Error occurred then ShowFatalError to terminate
    5561            0 :                 if (iErrorFlag > 0) {
    5562            0 :                     ShowFatalError(state, "End of DElight Error Messages");
    5563              :                 }
    5564            0 :             } else { // RJH 2008-03-07: No errors
    5565              :                      // extract reference point illuminance values from DElight Electric Lighting dump file for reporting
    5566              :                      // Open DElight Electric Lighting Dump File for reading
    5567            0 :                 auto iDElightErrorFile = state.files.outputDelightEldmpFilePath.try_open(state.files.outputControl.delighteldmp); // (THIS_AUTO_OK)
    5568            0 :                 bool elOpened = iDElightErrorFile.is_open();
    5569              : 
    5570              :                 // Sequentially read lines in DElight Electric Lighting Dump File
    5571              :                 // and extract refpt illuminances for standard EPlus output handling
    5572            0 :                 bool bEndofErrFile = false;
    5573            0 :                 int iDElightRefPt = 0; // Reference Point number for reading DElight Dump File (eplusout.delighteldmp)
    5574            0 :                 while (!bEndofErrFile && elOpened) {
    5575            0 :                     auto line = iDElightErrorFile.read<Real64>(); // (THIS_AUTO_OK)
    5576            0 :                     Real64 dRefPtIllum = line.data;               // tmp var for reading RefPt illuminance
    5577            0 :                     if (line.eof) {
    5578            0 :                         bEndofErrFile = true;
    5579            0 :                         continue;
    5580              :                     }
    5581              :                     // Increment refpt counter
    5582            0 :                     ++iDElightRefPt;
    5583              :                     // Assure refpt index does not exceed number of refpts in this zone
    5584            0 :                     if (iDElightRefPt <= thisDayltgCtrl.TotalDaylRefPoints) {
    5585            0 :                         thisDayltgCtrl.refPts(iDElightRefPt).lums[iLum_Illum] = dRefPtIllum;
    5586              :                     }
    5587              :                 }
    5588              : 
    5589              :                 // Close DElight Electric Lighting Dump File and delete
    5590            0 :                 if (elOpened) {
    5591            0 :                     iDElightErrorFile.close();
    5592            0 :                     FileSystem::removeFile(iDElightErrorFile.filePath);
    5593              :                 };
    5594            0 :             }
    5595              :             // Store the calculated total zone Power Reduction Factor due to DElight daylighting
    5596              :             // in the ZoneDaylight structure for later use
    5597            0 :             thisDayltgCtrl.PowerReductionFactor = dPowerReducFac;
    5598              :         }
    5599              :         // RJH DElight Modification End - Call to DElight electric lighting control subroutine
    5600              :     }
    5601              : 
    5602       249956 :     if (state.dataEnvrn->SunIsUp && !state.dataGlobal->DoingSizing) {
    5603        88617 :         DayltgInteriorMapIllum(state);
    5604              :     }
    5605       586606 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    5606       709603 :         for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    5607       372953 :             auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    5608       461517 :             for (int SurfNum = thisSpace.WindowSurfaceFirst; SurfNum <= thisSpace.WindowSurfaceLast; ++SurfNum) {
    5609        88564 :                 s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
    5610        88564 :                 if (IS_SHADED(s_surf->SurfWinShadingFlag(SurfNum))) {
    5611            0 :                     s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 1.0;
    5612              :                 } else {
    5613        88564 :                     s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
    5614              :                 }
    5615              :             }
    5616              :         }
    5617              :     }
    5618       249956 : }
    5619              : 
    5620       249956 : void manageDaylighting(EnergyPlusData &state)
    5621              : {
    5622       249956 :     auto &dl = state.dataDayltg;
    5623              : 
    5624       249956 :     if (state.dataEnvrn->SunIsUp && (state.dataEnvrn->BeamSolarRad + state.dataEnvrn->GndSolarRad + state.dataEnvrn->DifSolarRad > 0.0)) {
    5625       177258 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    5626       106045 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    5627       106045 :             if (enclSol.TotalEnclosureDaylRefPoints == 0 || !enclSol.HasInterZoneWindow) continue;
    5628              : 
    5629            0 :             DayltgInterReflIllFrIntWins(state, enclNum);
    5630            0 :             for (int daylightCtrlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    5631            0 :                 DayltgGlareWithIntWins(state, daylightCtrlNum);
    5632              :             }
    5633              :         }
    5634        71213 :         DayltgElecLightingControl(state);
    5635       178743 :     } else if (dl->mapResultsToReport && state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
    5636            2 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    5637            1 :             ReportIllumMap(state, MapNum);
    5638              :         }
    5639            1 :         dl->mapResultsToReport = false;
    5640              :     }
    5641       249956 : } // manageDaylighting()
    5642              : 
    5643          954 : void DayltgInteriorIllum(EnergyPlusData &state,
    5644              :                          int const daylightCtrlNum) // Daylighting:Controls number
    5645              : {
    5646              : 
    5647              :     // SUBROUTINE INFORMATION:
    5648              :     //       AUTHOR         Fred Winkelmann
    5649              :     //       DATE WRITTEN   July 1997
    5650              :     //       MODIFIED       March 2000, FCW: interpolate clear-sky daylight factors using
    5651              :     //                      HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
    5652              :     //                      only HourOfDay was used
    5653              :     //                      Jan 2001, FCW: interpolate in slat angle for windows with blinds
    5654              :     //                      that have movable slats
    5655              :     //                      Oct 2002, LKL: changed interpolation steps to HourOfDay/WeightNow
    5656              :     //                      LastHour/WeightPreviousHour
    5657              :     //                      Aug 2003, FCW: fix bug that prevented shadingControlType =
    5658              :     //                      MEETDAYLIGHTILLUMINANCESETPOINT from working
    5659              :     //                      Mar 2004, FCW: fix bug in calc of illuminance setpoint contribution
    5660              :     //                      to background luminance: now it is divided by pi to give cd/m2
    5661              :     //                      Mar 2004, FCW: modify to handle daylighting through interior windows
    5662              :     //                      June 2009, TH: modified for thermochromic windows
    5663              :     //                      Jan 2010, TH (CR 7984): added iterations for switchable windows with shading
    5664              :     //                       control of MeetDaylightIlluminanceSetpoint and glare control is active
    5665              :     //                       Also corrected bugs (CR 7988) for switchable glazings not related to CR 7984
    5666              : 
    5667              :     // PURPOSE OF THIS SUBROUTINE:
    5668              :     // Using daylighting factors and exterior illuminance, determine
    5669              :     // the current-hour interior daylight illuminance and glare index
    5670              :     // at each reference point in a space. Deploy window shading window by window
    5671              :     // if glare control is active for window and if the acceptable glare index
    5672              :     // is exceeded at both reference points.
    5673              : 
    5674              :     // Called by InitSurfaceHeatBalance.
    5675              : 
    5676              :     // REFERENCES:
    5677              :     // Based on DOE-2.1E subroutine DINTIL.
    5678          954 :     auto &dl = state.dataDayltg;
    5679          954 :     auto &s_surf = state.dataSurface;
    5680              : 
    5681          954 :     Real64 constexpr tmpSWIterStep(0.05); // step of switching factor, assuming maximum of 20 switching states
    5682              : 
    5683              :     int NREFPT; // Number of daylighting reference points
    5684              :     int iSky1;  // Sky type index values for averaging two sky types
    5685              :     int iSky2;
    5686          954 :     Array1D<Real64> SetPnt; // Illuminance setpoint at reference points (lux)
    5687          954 :     Array1D<Real64> GLRNEW; // New glare index at reference point
    5688              : 
    5689          954 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5690          954 :     int enclNum = thisDayltgCtrl.enclIndex;
    5691          954 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    5692              :     int ISWFLG; // Switchable glazing flag: =1 if one or more windows in a zone
    5693              :     //  has switchable glazing that adjusts visible transmittance to just meet
    5694              :     //  daylighting setpoint; =0 otherwise.
    5695              :     Real64 VTRAT;        // Ratio between switched and unswitched visible transmittance at normal incidence
    5696              :     Real64 BACL;         // Window background (surround) luminance for glare calc (cd/m2)
    5697              :     Real64 SkyWeight;    // Weighting factor used to average two different sky types
    5698              :     Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
    5699              :     //   luminous efficacy and horizontal illuminance from averaged sky
    5700              :     bool GlareFlag; // True if maximum glare is exceeded
    5701              : 
    5702              :     Real64 VTRatio;  // VT (visible transmittance) ratio = VTNow / VTMaster
    5703              :     Real64 VTNow;    // VT of the time step actual TC window
    5704              :     Real64 VTMaster; // VT of the base/master TC window
    5705              : 
    5706          954 :     Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> tmpDaylFromWinAtRefPt;
    5707              : 
    5708          954 :     bool breakOuterLoop(false);
    5709          954 :     bool continueOuterLoop(false);
    5710              : 
    5711              :     struct ShadeGroupLums
    5712              :     {
    5713              :         Array1D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> WDAYIL; // Illuminance from window at ref-point
    5714              :         Array1D<std::array<Real64, (int)Lum::Num>> RDAYIL; // Illuminance from window at ref-point after closing shade
    5715              :         Real64 switchedWinLum;
    5716              :         Real64 unswitchedWinLum;
    5717              :         Real64 switchedTvis;
    5718              :         Real64 unswitchedTvis;
    5719              :         Real64 lumRatio;
    5720              :     };
    5721              : 
    5722          954 :     Array1D<ShadeGroupLums> shadeGroupsLums;
    5723              : 
    5724              :     // Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, iLum_Num>> WDAYIL; // Illuminance from window at reference point
    5725              :     // (second index)
    5726              :     //   the number of shade deployment groups (third index)
    5727              :     // Array2D<std::array<Real64, (int)DataSurfaces::WinCover::Num>> WBACLU; // Background illuminance from window at reference point (second index)
    5728              :     //   the number of shade deployment groups (third index)
    5729              :     // Array2D<std::array<Real64, iLum_Num>> RDAYIL; // Illuminance from window at reference point after closing shade
    5730              :     // Array2D<Real64> RBACLU; // Background illuminance from window at reference point after closing shade
    5731              :     // Array1D<Real64> DILLSW;         // Illuminance a ref point from a group of windows that can be switched together,
    5732              :     // Array1D<Real64> DILLUN;         //  and from those that aren't (lux)
    5733              :     // Array1D<Real64> TVIS1;  // Visible transmittance at normal incidence of unswitched glazing
    5734              :     // Array1D<Real64> TVIS2;  // Visible transmittance at normal incidence of fully-switched glazing
    5735              :     // Array1D<Real64> ASETIL; // Illuminance ratio (lux)
    5736              : 
    5737          954 :     if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) return;
    5738              : 
    5739          954 :     NREFPT = thisDayltgCtrl.TotalDaylRefPoints;
    5740              : 
    5741          954 :     if (dl->DayltgInteriorIllum_firstTime) {
    5742            7 :         dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5743            7 :         dl->DayltgInteriorIllum_firstTime = false;
    5744              :     }
    5745              : 
    5746              :     // size these for the maximum of the shade deployment order
    5747          954 :     shadeGroupsLums.allocate(dl->maxShadeDeployOrderExtWins);
    5748          956 :     for (auto &shadeGroupLums : shadeGroupsLums) {
    5749            2 :         shadeGroupLums.WDAYIL.allocate(dl->maxControlRefPoints);
    5750            2 :         shadeGroupLums.RDAYIL.allocate(dl->maxControlRefPoints);
    5751              :     }
    5752              : 
    5753              :     // Three arrays to save original clear and dark (fully switched) states'
    5754              :     //  zone/window daylighting properties.
    5755          954 :     tmpDaylFromWinAtRefPt.allocate(dl->maxNumRefPtInAnyDaylCtrl, dl->maxEnclSubSurfaces);
    5756              : 
    5757          954 :     SetPnt.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5758          954 :     dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5759          954 :     GLRNEW.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5760              : 
    5761         2326 :     for (int iRefPt = 1; iRefPt <= (int)dl->maxNumRefPtInAnyDaylCtrl; ++iRefPt) {
    5762         2744 :         for (int iExtWin = 1; iExtWin <= (int)dl->maxEnclSubSurfaces; ++iExtWin) {
    5763         1372 :             auto &tmpDayl = tmpDaylFromWinAtRefPt(iRefPt, iExtWin);
    5764         1372 :             tmpDayl[iLum_Illum] = tmpDayl[iLum_Back] = tmpDayl[iLum_Source] = {0.0, 0.0};
    5765              :         }
    5766              :     }
    5767              : 
    5768              :     // Initialize reference point illuminance and window background luminance
    5769         2323 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    5770         1369 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    5771         1369 :         SetPnt(IL) = refPt.illumSetPoint;
    5772         1369 :         dl->DaylIllum(IL) = 0.0;
    5773         1369 :         refPt.lums[iLum_Back] = 0.0;
    5774              :     }
    5775              : 
    5776          954 :     if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    5777          887 :         SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    5778          887 :         iSky1 = (int)SkyType::Clear;
    5779          887 :         iSky2 = (int)SkyType::ClearTurbid;
    5780           67 :     } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    5781           56 :         SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    5782           56 :         iSky1 = (int)SkyType::ClearTurbid;
    5783           56 :         iSky2 = (int)SkyType::Intermediate;
    5784              :     } else { // Sky is average of intermediate and overcast
    5785           11 :         SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    5786           11 :         iSky1 = (int)SkyType::Intermediate;
    5787           11 :         iSky2 = (int)SkyType::Overcast;
    5788              :     }
    5789              : 
    5790              :     // First loop over exterior windows associated with this zone. The window may be an exterior window in
    5791              :     // the zone or an exterior window in an adjacent zone that shares an interior window with the zone.
    5792              :     // Find contribution of each window to the daylight illum and to the glare numerator at each reference point.
    5793              :     // Use shading flags set in WindowShadingManager.
    5794         1908 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5795          954 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5796              : 
    5797              :         // Added TH 6/29/2009 for thermochromic windows
    5798          954 :         VTRatio = 1.0;
    5799          954 :         if (NREFPT > 0) {
    5800          954 :             int const IConst = s_surf->Surface(IWin).Construction;
    5801          954 :             auto const &construction = state.dataConstruction->Construct(IConst);
    5802          954 :             if (construction.isTCWindow) {
    5803              :                 // For thermochromic windows, daylight and glare factors are always calculated
    5804              :                 //  based on the master construction. They need to be adjusted by the VTRatio, including:
    5805              :                 //  ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
    5806              :                 //  DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
    5807            0 :                 VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
    5808            0 :                 VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
    5809            0 :                 VTRatio = VTNow / VTMaster;
    5810              :             }
    5811              :         }
    5812              : 
    5813         2855 :         bool ShadedOrDiffusingGlassWin = s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
    5814         1901 :                                          (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin));
    5815              : 
    5816          954 :         Real64 wgtCurrHr = state.dataGlobal->WeightNow;
    5817          954 :         Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
    5818              : 
    5819          954 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> SFHR; // Sky source luminance factor for sky type, bare/shaded window
    5820          954 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
    5821          954 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> BFHR; // Sky background luminance factor for sky type, bare/shaded window
    5822              : 
    5823              :         // Loop over reference points
    5824         2323 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    5825              : 
    5826         1369 :             auto const &daylFacCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Bare];
    5827         1369 :             auto const &daylFacPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Bare];
    5828              :             // Daylight factors for current sun position
    5829         1369 :             auto const &illFacCurr = daylFacCurr[iLum_Illum];
    5830         1369 :             auto const &illFacPrev = daylFacPrev[iLum_Illum];
    5831         1369 :             auto &dfhr = DFHR[iWinCover_Bare];
    5832         1369 :             auto const &backFacCurr = daylFacCurr[iLum_Back];
    5833         1369 :             auto const &backFacPrev = daylFacPrev[iLum_Back];
    5834         1369 :             auto &bfhr = BFHR[iWinCover_Bare];
    5835         1369 :             auto const &sourceFacCurr = daylFacCurr[iLum_Source];
    5836         1369 :             auto const &sourceFacPrev = daylFacPrev[iLum_Source];
    5837         1369 :             auto &sfhr = SFHR[iWinCover_Bare];
    5838              : 
    5839         1369 :             auto const &daylFac2Curr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
    5840         1369 :             auto const &daylFac2Prev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
    5841              : 
    5842         1369 :             auto const &illFac2Curr = daylFac2Curr[iLum_Illum];
    5843         1369 :             auto const &illFac2Prev = daylFac2Prev[iLum_Illum];
    5844         1369 :             auto &dfhr2 = DFHR[iWinCover_Shaded];
    5845         1369 :             auto const &backFac2Curr = daylFac2Curr[iLum_Back];
    5846         1369 :             auto const &backFac2Prev = daylFac2Prev[iLum_Back];
    5847         1369 :             auto &bfhr2 = BFHR[iWinCover_Shaded];
    5848         1369 :             auto const &sourceFac2Curr = daylFac2Curr[iLum_Source];
    5849         1369 :             auto const &sourceFac2Prev = daylFac2Prev[iLum_Source];
    5850         1369 :             auto &sfhr2 = SFHR[iWinCover_Shaded];
    5851              : 
    5852              : #ifdef GET_OUT
    5853              :             auto const &daylFacShCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
    5854              :             auto const &daylFacShPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
    5855              : 
    5856              :             auto const &illFacShCurr = daylFacShCurr[iLum_Illum];
    5857              :             auto const &illFacShPrev = daylFacShPrev[iLum_Illum];
    5858              : 
    5859              :             auto const &backFacShCurr = daylFacShCurr[iLum_Back];
    5860              :             auto const &backFacShPrev = daylFacShPrev[iLum_Back];
    5861              : 
    5862              :             auto const &sourceFacShCurr = daylFacShCurr[iLum_Source];
    5863              :             auto const &sourceFacShPrev = daylFacShPrev[iLum_Source];
    5864              : #endif // GET_OUT
    5865         6845 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5866              : 
    5867              :                 // ===Bare window===
    5868              :                 // Sky daylight factor for sky type (second index), bare/shaded window (first index)
    5869         5476 :                 dfhr.sky[iSky] = VTRatio * (wgtCurrHr * illFacCurr.sky[iSky] + wgtPrevHr * illFacPrev.sky[iSky]);
    5870         5476 :                 bfhr.sky[iSky] = VTRatio * (wgtCurrHr * backFacCurr.sky[iSky] + wgtPrevHr * backFacPrev.sky[iSky]);
    5871         5476 :                 sfhr.sky[iSky] = VTRatio * (wgtCurrHr * sourceFacCurr.sky[iSky] + wgtPrevHr * sourceFacPrev.sky[iSky]);
    5872              : 
    5873         5476 :                 if (ShadedOrDiffusingGlassWin) {
    5874              : 
    5875              :                     // ===Shaded window or window with diffusing glass===
    5876              :                     // Shade, screen, blind with fixed slats, or diffusing glass
    5877           16 :                     dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illFac2Curr.sky[iSky] + wgtPrevHr * illFac2Prev.sky[iSky]);
    5878           16 :                     bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backFac2Curr.sky[iSky] + wgtPrevHr * backFac2Prev.sky[iSky]);
    5879           16 :                     sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceFac2Curr.sky[iSky] + wgtPrevHr * sourceFac2Prev.sky[iSky]);
    5880              :                 } // End of check if window is shaded or has diffusing glass
    5881              :             }     // for (iSky)
    5882              : 
    5883              :             // Sun daylight factor for bare/shaded window
    5884         2738 :             DFHR[iWinCover_Bare].sun =
    5885         1369 :                 VTRatio * (wgtCurrHr * (illFacCurr.sun + illFacCurr.sunDisk) + wgtPrevHr * (illFacPrev.sun + illFacPrev.sunDisk));
    5886              : 
    5887              :             // Sun background luminance factor for bare/shaded window
    5888         2738 :             BFHR[iWinCover_Bare].sun =
    5889         1369 :                 VTRatio * (wgtCurrHr * (backFacCurr.sun + backFacCurr.sunDisk) + wgtPrevHr * (backFacPrev.sun + backFacPrev.sunDisk));
    5890              : 
    5891              :             // Sun source luminance factor for bare/shaded window
    5892         2738 :             SFHR[iWinCover_Bare].sun =
    5893         1369 :                 VTRatio * (wgtCurrHr * (sourceFacCurr.sun + sourceFacCurr.sunDisk) + wgtPrevHr * (sourceFacPrev.sun + sourceFacPrev.sunDisk));
    5894              : 
    5895         1369 :             if (ShadedOrDiffusingGlassWin) {
    5896              : 
    5897              :                 // ===Shaded window or window with diffusing glass===
    5898              :                 // Shade, screen, blind with fixed slats, or diffusing glass
    5899            4 :                 DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * illFac2Curr.sun + wgtPrevHr * illFac2Prev.sun);
    5900            4 :                 BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * backFac2Curr.sun + wgtPrevHr * backFac2Prev.sun);
    5901            4 :                 SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * sourceFac2Curr.sun + wgtPrevHr * sourceFac2Prev.sun);
    5902              : 
    5903            4 :                 auto const &surfShade = s_surf->surfShades(IWin);
    5904            4 :                 if (!surfShade.blind.slatBlockBeam) {
    5905            4 :                     DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * illFac2Curr.sunDisk + wgtPrevHr * illFac2Prev.sunDisk);
    5906            4 :                     BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * backFac2Curr.sunDisk + wgtPrevHr * backFac2Prev.sunDisk);
    5907            4 :                     SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * sourceFac2Curr.sunDisk + wgtPrevHr * sourceFac2Prev.sunDisk);
    5908              :                 }
    5909              :             } // End of check if window is shaded or has diffusing glass
    5910              : 
    5911              :             // Get illuminance at ref point from bare and shaded window by
    5912              :             // multiplying daylight factors by exterior horizontal illuminance
    5913              : 
    5914              :             // Adding 0.001 in the following prevents zero HorIllSky in early morning or late evening when sun
    5915              :             // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
    5916         1369 :             auto const &gilskCurr = dl->horIllum[state.dataGlobal->HourOfDay];
    5917         1369 :             auto const &gilskPrev = dl->horIllum[state.dataGlobal->PreviousHour];
    5918              : 
    5919              :             // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
    5920              :             // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
    5921              :             // also calculated in DayltgLuminousEfficacy.
    5922              :             Real64 horIllSky1 =
    5923         1369 :                 state.dataGlobal->WeightNow * gilskCurr.sky[iSky1] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky1] + 0.001;
    5924              :             Real64 horIllSky2 =
    5925         1369 :                 state.dataGlobal->WeightNow * gilskCurr.sky[iSky2] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky2] + 0.001;
    5926              : 
    5927         1369 :             HorIllSkyFac = state.dataEnvrn->HISKF / ((1 - SkyWeight) * horIllSky2 + SkyWeight * horIllSky1);
    5928              : 
    5929         1369 :             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    5930         1369 :             auto &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    5931         2742 :             for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    5932         2738 :                 auto const &dfhr3 = DFHR[iWinCover];
    5933         2738 :                 auto const &bfhr3 = BFHR[iWinCover];
    5934         2738 :                 auto const &sfhr3 = SFHR[iWinCover];
    5935              : 
    5936              :                 // What is this?
    5937         2738 :                 if (iWinCover == iWinCover_Shaded && !ShadedOrDiffusingGlassWin) break;
    5938              : 
    5939         1373 :                 daylFromWinAtRefPt[iLum_Illum][iWinCover] =
    5940         1373 :                     dfhr3.sun * state.dataEnvrn->HISUNF +
    5941         1373 :                     HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * horIllSky1 + dfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    5942         1373 :                 daylFromWinAtRefPt[iLum_Back][iWinCover] =
    5943         1373 :                     bfhr3.sun * state.dataEnvrn->HISUNF +
    5944         1373 :                     HorIllSkyFac * (bfhr3.sky[iSky1] * SkyWeight * horIllSky1 + bfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    5945         1373 :                 daylFromWinAtRefPt[iLum_Source][iWinCover] =
    5946         1373 :                     sfhr3.sun * state.dataEnvrn->HISUNF +
    5947         1373 :                     HorIllSkyFac * (sfhr3.sky[iSky1] * SkyWeight * horIllSky1 + sfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    5948              : 
    5949         1373 :                 daylFromWinAtRefPt[iLum_Source][iWinCover] = max(daylFromWinAtRefPt[iLum_Source][iWinCover], 0.0);
    5950              : 
    5951              :                 // Added TH 1/21/2010 - save the original clear and dark (fully switched) states'
    5952              :                 //  zone daylighting values, needed for switachable glazings
    5953         1373 :                 tmpDayl[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
    5954         1373 :                 tmpDayl[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
    5955         1373 :                 tmpDayl[iLum_Source][iWinCover] = daylFromWinAtRefPt[iLum_Source][iWinCover];
    5956              :             } // for for (iWinCover)
    5957              : 
    5958              :         } // End of reference point loop, IL
    5959              :     }     // End of first loop over exterior windows associated with this zone
    5960              : 
    5961              :     // Initialize flag that one or more windows has switchable glazing
    5962              :     // control that adjusts visible transmittance to just meet dayltg setpoint
    5963              :     // (and the window has not already been switched)
    5964          954 :     ISWFLG = 0;
    5965              : 
    5966              :     // Second loop over windows. Find total daylight illuminance and background luminance
    5967              :     // for each ref pt from all exterior windows associated with the zone.  Use shading flags.
    5968              :     // This illuminance excludes contribution of inter-reflected illuminance produced by solar
    5969              :     // entering the zone through interior windows (which is calculated in DayltgInterReflIllFrIntWins.
    5970              : 
    5971         1908 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5972          954 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5973          954 :         int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    5974          954 :         if (s_surf->Surface(IWin).HasShadeControl && ISWFLG == 0) {
    5975            2 :             if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    5976            0 :                 s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
    5977            0 :                 ISWFLG = 1;
    5978              :         }
    5979              : 
    5980              :         // Determine if illuminance contribution is from bare or shaded window
    5981              :         //  For switchable glazings with shading control type of WSCT_MeetDaylIlumSetp,
    5982              :         //   the shading flag is initialized at GlassConditionallyLightened (20), and
    5983              :         //   the window is initialized at clear state: IS = 1
    5984              :         //  For other windows with glare control, the shading flag is initialized at >10, to be determined
    5985          954 :         WinCover winCover = findWinShadingStatus(state, IWin);
    5986              : 
    5987         2323 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    5988         1369 :             auto &refPt = thisDayltgCtrl.refPts(IL);
    5989         1369 :             dl->DaylIllum(IL) += refPt.extWins(loop).lums[iLum_Illum][(int)winCover];
    5990         1369 :             refPt.lums[iLum_Back] += refPt.extWins(loop).lums[iLum_Back][(int)winCover];
    5991              :         }
    5992              :     } // End of second window loop over exterior windows associated with this zone
    5993              : 
    5994              :     // Optical switching control (e.g. electrochromic glass) to adjust
    5995              :     // window's vis trans downward so daylight level equals or is as
    5996              :     // close as possible to the illuminance setpoint at first reference point.
    5997              :     // Assumes vis trans in the fully switched state is less than that in the
    5998              :     // unswitched state. Assumes some windows in a space may have this control and
    5999              :     // others not.
    6000              : 
    6001          954 :     int count = 0;
    6002              : 
    6003              :     // If daylight illuminance is above setpoint, allow switching
    6004          954 :     if (ISWFLG != 0 && dl->DaylIllum(1) > SetPnt(1)) {
    6005              : 
    6006              :         // array of flags to indicate that previously groups would have already shaded this window
    6007            0 :         Array1D_bool previously_shaded;
    6008            0 :         previously_shaded.dimension(dl->maxDayltgExtWins, false);
    6009              : 
    6010              :         // Third loop over windows.  Get illuminance at ref pt 1 from
    6011              :         // windows that can be switched (DILLSW) with a group and those that can't (DILLUN).
    6012              :         // Windows that can be switched are initially in the unswitched state. For subsequent
    6013              :         // groups the windows in previous groups are fully switched.
    6014            0 :         for (auto &shadeGroupLum : shadeGroupsLums) {
    6015            0 :             shadeGroupLum.switchedWinLum = shadeGroupLum.unswitchedWinLum = 0.0;
    6016              :         }
    6017              : 
    6018            0 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6019            0 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6020            0 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6021            0 :             for (const int IWin : listOfExtWin) {
    6022            0 :                 ++count;
    6023              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6024            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6025            0 :                 if (loop == 0) continue;
    6026              : 
    6027            0 :                 if (!s_surf->Surface(IWin).HasShadeControl) continue;
    6028              : 
    6029            0 :                 int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6030            0 :                 WinCover winCover = findWinShadingStatus(state, IWin);
    6031              : 
    6032            0 :                 auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Illum];
    6033            0 :                 if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened &&
    6034            0 :                     s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    6035            0 :                     !previously_shaded(loop)) {
    6036            0 :                     shadeGroupLums.switchedWinLum += daylFromWinAtRefPt[(int)winCover];
    6037            0 :                     previously_shaded(loop) = true;
    6038              :                 } else {
    6039            0 :                     shadeGroupLums.unswitchedWinLum +=
    6040            0 :                         !previously_shaded(loop) ? daylFromWinAtRefPt[(int)winCover] : daylFromWinAtRefPt[iWinCover_Shaded];
    6041              :                 }
    6042              :             } // for (IWin)
    6043              :         }     // for (igroup)
    6044              : 
    6045              :         // Transmittance multiplier
    6046            0 :         for (auto &shadeGroupLums : shadeGroupsLums) {
    6047            0 :             shadeGroupLums.lumRatio = (SetPnt(1) - shadeGroupLums.unswitchedWinLum) / (shadeGroupLums.switchedWinLum + 0.00001);
    6048              :         }
    6049              : 
    6050              :         // ASETIL < 1 means there's enough light, so check for switching
    6051              : 
    6052              :         // Fourth loop over windows to determine which to switch
    6053              :         // iterate in the order that the shades are specified in WindowShadeControl
    6054            0 :         count = 0;
    6055            0 :         breakOuterLoop = false;
    6056            0 :         continueOuterLoop = false;
    6057            0 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6058            0 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6059            0 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6060              : 
    6061            0 :             for (const int IWin : listOfExtWin) {
    6062            0 :                 ++count;
    6063            0 :                 auto const &surfWin = s_surf->SurfaceWindow(IWin);
    6064              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6065            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6066            0 :                 if (loop > 0 && shadeGroupLums.lumRatio < 1.0) {
    6067              : 
    6068            0 :                     int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6069            0 :                     if (!s_surf->Surface(IWin).HasShadeControl) {
    6070            0 :                         continueOuterLoop = true;
    6071            0 :                         continue;
    6072              :                     }
    6073            0 :                     if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::GlassConditionallyLightened ||
    6074            0 :                         s_surf->WindowShadingControl(ICtrl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
    6075            0 :                         continueOuterLoop = true;
    6076            0 :                         continue;
    6077              :                     }
    6078              : 
    6079            0 :                     int const IConst = s_surf->SurfActiveConstruction(IWin);
    6080              :                     // Vis trans at normal incidence of unswitched glass
    6081            0 :                     shadeGroupLums.unswitchedTvis =
    6082            0 :                         Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    6083              : 
    6084              :                     // Vis trans at normal incidence of fully switched glass
    6085            0 :                     int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
    6086            0 :                     shadeGroupLums.switchedTvis =
    6087            0 :                         Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    6088              : 
    6089              :                     // Reset shading flag to indicate that window is shaded by being partially or fully switched
    6090            0 :                     s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
    6091              : 
    6092              :                     // ASETIL < 0 means illuminance from non-daylight-switchable windows exceeds setpoint,
    6093              :                     // so completely switch all daylight-switchable windows to minimize solar gain
    6094            0 :                     if (shadeGroupLums.lumRatio <= 0.0) {
    6095            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
    6096            0 :                         s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
    6097              :                     } else {
    6098              :                         // Case where 0 < ASETIL < 1: darken glass in all
    6099              :                         // daylight-switchable windows to just meet illuminance setpoint
    6100              :                         // From this equation: SETPNT(1) = DILLUN + DILLSW/TVIS1 * VisTransSelected
    6101            0 :                         s_surf->SurfWinVisTransSelected(IWin) =
    6102            0 :                             max(shadeGroupLums.switchedTvis, shadeGroupLums.lumRatio * shadeGroupLums.unswitchedTvis) + 0.000001;
    6103            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = (shadeGroupLums.unswitchedTvis - s_surf->SurfWinVisTransSelected(IWin)) /
    6104            0 :                                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis + 0.000001);
    6105              :                         // bound switching factor between 0 and 1
    6106            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = min(1.0, s_surf->SurfWinSwitchingFactor(IWin));
    6107            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = max(0.0, s_surf->SurfWinSwitchingFactor(IWin));
    6108              :                     }
    6109              : 
    6110              :                     // Adjust daylight quantities based on ratio between switched and unswitched visible transmittance
    6111            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6112              :                         // DaylIllum(IL) and BacLum(IL) were calculated at the clear state:
    6113              :                         //  and need to adjusted for intermediate switched state at VisTransSelected:
    6114            0 :                         auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6115            0 :                         auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6116              : 
    6117            0 :                         VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.unswitchedTvis + 0.000001);
    6118            0 :                         dl->DaylIllum(IL) += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
    6119            0 :                         thisDayltgCtrl.refPts(IL).lums[iLum_Back] += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
    6120              : 
    6121              :                         // Adjust illum, background illum and source luminance for this window in intermediate switched state
    6122              :                         //  for later use in the DayltgGlare calc because SurfaceWindow(IWin)%ShadingFlag = WinShadingType::SwitchableGlazing = 2
    6123            0 :                         VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.switchedTvis + 0.000001);
    6124            0 :                         daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Illum][iWinCover_Shaded];
    6125            0 :                         daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Back][iWinCover_Shaded];
    6126            0 :                         daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Source][iWinCover_Shaded];
    6127              :                     } // for (IL)
    6128              :                 }     // if (loop > 0 && ASETIL < 1)
    6129              :                 // If new daylight does not exceed the illuminance setpoint, done, no more checking other groups of switchable glazings
    6130            0 :                 if (dl->DaylIllum(1) <= SetPnt(1)) {
    6131            0 :                     breakOuterLoop = true;
    6132            0 :                     break;
    6133              :                 }
    6134              :             } // for (Win)
    6135            0 :             if (breakOuterLoop) break;
    6136            0 :             if (continueOuterLoop) continue;
    6137              :         } // for (igroup)
    6138              : 
    6139            0 :     } // ISWFLG /= 0 .AND. DaylIllum(1) > SETPNT(1)
    6140              : 
    6141              :     // loop over windows to do luminance based control
    6142          954 :     count = 0;
    6143          956 :     for (int igroup = 1; igroup <= (int)thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6144            4 :         for (int const IWin : thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1]) {
    6145            2 :             ++count;
    6146            2 :             int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6147            2 :             WindowShadingControlType shCtrlType = s_surf->WindowShadingControl(ICtrl).shadingControlType;
    6148            2 :             if (!((shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffMidNight) ||
    6149              :                   (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffSunset) ||
    6150              :                   (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffNextMorning)))
    6151            0 :                 continue;
    6152              :             // need to map back to the original order of the "loop" to not change all the other data structures
    6153            2 :             int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6154            2 :             if (loop == 0) continue;
    6155              : 
    6156            2 :             WinShadingType currentFlag = s_surf->SurfWinShadingFlag(IWin);
    6157            2 :             WinShadingType ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
    6158            2 :             if ((currentFlag != WinShadingType::IntShadeConditionallyOff) && (currentFlag != WinShadingType::GlassConditionallyLightened) &&
    6159            0 :                 (currentFlag != WinShadingType::ExtShadeConditionallyOff) && (currentFlag != WinShadingType::IntBlindConditionallyOff) &&
    6160            0 :                 (currentFlag != WinShadingType::ExtBlindConditionallyOff) && (currentFlag != WinShadingType::BGShadeConditionallyOff) &&
    6161              :                 (currentFlag != WinShadingType::BGBlindConditionallyOff))
    6162            0 :                 continue;
    6163              : 
    6164            2 :             auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums;
    6165            2 :             if (daylFromWinAtRefPt[iLum_Source][iWinCover_Bare] > s_surf->WindowShadingControl(ICtrl).SetPoint2) {
    6166              :                 // shade on if luminance of this window is above setpoint
    6167            1 :                 s_surf->SurfWinShadingFlag(IWin) = ShType;
    6168              :                 // update total illuminance and background luminance
    6169            2 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6170            1 :                     dl->DaylIllum(IL) += daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
    6171            1 :                     thisDayltgCtrl.refPts(IL).lums[iLum_Back] +=
    6172            1 :                         daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
    6173              :                 }
    6174              :             } else {
    6175              :                 // shade off if luminance is below setpoint
    6176            1 :                 s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6177              :             }
    6178              :         } // for (IWin)
    6179              :     }     // for (igroup)
    6180              : 
    6181              :     // Calculate glare index at each reference point assuming the daylight illuminance setpoint is
    6182              :     //  met at both reference points, either by daylight or electric lights
    6183         2323 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    6184         1369 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    6185         1369 :         BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, refPt.lums[iLum_Back]);
    6186              :         // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,1,loop) for unshaded windows, and
    6187              :         //  ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded windows
    6188         1369 :         refPt.glareIndex = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6189              :     }
    6190              : 
    6191              :     // Check if glare level is less than maximum allowed at each ref pt.  If maximum
    6192              :     // is exceeded at either ref pt, attempt to reduce glare to acceptable level by closing
    6193              :     // shading device on windows that have shades that have not already been closed.
    6194          954 :     GlareFlag = false;
    6195         1579 :     for (auto const &refPt : thisDayltgCtrl.refPts) {
    6196          997 :         if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) {
    6197          372 :             GlareFlag = true;
    6198          372 :             break;
    6199              :         }
    6200              :     }
    6201              : 
    6202          954 :     if (GlareFlag) {
    6203              :         bool blnCycle;
    6204              :         bool GlareOK;
    6205              :         Real64 tmpMult;
    6206              :         // Glare is too high at a ref pt.  Loop through windows.
    6207          372 :         count = 0;
    6208              : 
    6209          372 :         continueOuterLoop = false;
    6210          372 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6211            0 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6212            0 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6213              : 
    6214            0 :             int countBeforeListOfExtWinLoop = count;
    6215            0 :             bool atLeastOneGlareControlIsActive = false;
    6216              : 
    6217            0 :             for (const int IWin : listOfExtWin) {
    6218            0 :                 ++count;
    6219              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6220            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6221            0 :                 if (loop == 0) continue;
    6222              : 
    6223            0 :                 auto const &surfWin = s_surf->SurfaceWindow(IWin);
    6224              :                 // Check if window is eligible for glare control
    6225              :                 // TH 1/21/2010. Switchable glazings already in partially switched state
    6226              :                 //  should be allowed to further dim to control glare
    6227              :                 // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
    6228            0 :                 if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
    6229            0 :                     ANY_BLIND(s_surf->SurfWinShadingFlag(IWin))) {
    6230            0 :                     continueOuterLoop = false;
    6231            0 :                     continue;
    6232              :                 }
    6233            0 :                 int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6234            0 :                 if (!s_surf->Surface(IWin).HasShadeControl) {
    6235            0 :                     continueOuterLoop = false;
    6236            0 :                     continue;
    6237              :                 }
    6238            0 :                 if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
    6239            0 :                     atLeastOneGlareControlIsActive = true;
    6240              : 
    6241              :                     // Illuminance (WDAYIL) and background luminance (WBACLU) contribution from this
    6242              :                     // window without shading (IS=1) and with shading (IS=2) for each ref pt
    6243              :                     //  For switchable windows, this may be partially switched rather than fully dark
    6244            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6245            0 :                         auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6246            0 :                         for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    6247            0 :                             shadeGroupLums.WDAYIL(IL)[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
    6248            0 :                             shadeGroupLums.WDAYIL(IL)[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
    6249              :                         }
    6250              :                     }
    6251              : 
    6252              :                     // Recalculate illuminance and glare with shading on this window.
    6253              :                     //  For switchable glazings, this is the fully switched (dark) state
    6254            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6255            0 :                         auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6256            0 :                         auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6257            0 :                         auto const &refPt = thisDayltgCtrl.refPts(IL);
    6258              : 
    6259            0 :                         if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::SwitchableGlazing) {
    6260              :                             // for non switchable glazings or switchable glazings not switched yet (still in clear state)
    6261              :                             //  SurfaceWindow(IWin)%ShadingFlag = WinShadingFlag::GlassConditionallyLightened
    6262            0 :                             rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Bare] + wdayil[iLum_Illum][iWinCover_Shaded];
    6263            0 :                             rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Bare] + wdayil[iLum_Back][iWinCover_Shaded];
    6264              :                         } else {
    6265              :                             // switchable glazings already in partially switched state when calc the RDAYIL(IL) & RBACLU(IL)
    6266            0 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(loop, IL);
    6267            0 :                             rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Shaded] + tmpDayl[iLum_Illum][iWinCover_Shaded];
    6268            0 :                             rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Shaded] + tmpDayl[iLum_Back][iWinCover_Shaded];
    6269              :                         }
    6270              :                     } // for (IL)
    6271              : 
    6272            0 :                     if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
    6273            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
    6274            0 :                     else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff)
    6275            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntShade;
    6276            0 :                     else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff)
    6277            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtShade;
    6278            0 :                     else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff)
    6279            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntBlind;
    6280            0 :                     else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff)
    6281            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtBlind;
    6282            0 :                     else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGShadeConditionallyOff)
    6283            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGShade;
    6284            0 :                     else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGBlindConditionallyOff)
    6285            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGBlind;
    6286              : 
    6287              :                     // For switchable glazings, it is switched to fully dark state,
    6288              :                     // update ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for use in DayltgGlare
    6289            0 :                     if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6290            0 :                         for (int IL = 1; IL <= NREFPT; ++IL) {
    6291            0 :                             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6292            0 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6293              : 
    6294            0 :                             daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6295            0 :                             daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6296            0 :                             daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6297              :                         }
    6298              : 
    6299            0 :                         int const IConst = s_surf->SurfActiveConstruction(IWin);
    6300              :                         // Vis trans at normal incidence of unswitched glass
    6301            0 :                         shadeGroupLums.unswitchedTvis =
    6302            0 :                             Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    6303              : 
    6304              :                         // Vis trans at normal incidence of fully switched glass
    6305            0 :                         int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
    6306            0 :                         shadeGroupLums.switchedTvis =
    6307            0 :                             Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    6308              :                     } // if (switchableGlazing)
    6309              :                 }     // if (GlareControlIsActive)
    6310              :             }         // for (IWin)
    6311            0 :             if (continueOuterLoop) continue;
    6312              : 
    6313            0 :             if (atLeastOneGlareControlIsActive) {
    6314              : 
    6315              :                 // Re-calc daylight and glare at shaded state. For switchable glazings, it is the fully dark state.
    6316            0 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6317            0 :                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, shadeGroupLums.RDAYIL(IL)[iLum_Back]);
    6318              :                     // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded state
    6319            0 :                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6320              :                 }
    6321              : 
    6322              :                 // Check if the shading did not improve the glare conditions
    6323              :                 //
    6324              :                 // blnCycle when true resets the specific window to its non-shaded condition. A later comment says
    6325              :                 //      Shading this window has not improved the glare situation.
    6326              :                 //      Reset shading flag to no shading condition, go to next window.
    6327              :                 //
    6328              :                 // 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
    6329              :                 // reset it. For each reference point, if the original glare was too high but ok at other reference points and the glare gets
    6330              :                 // lower at the reference and stays ok at the other reference points it is good, don't reset it.
    6331              :                 //
    6332              :                 // The old comments when there were only two reference points were:
    6333              :                 //     One ref pt;  go to next window if glare has increased.
    6334              :                 //     Two ref pts.  There are three cases depending on glare values.
    6335              :                 //         (1) Initial glare too high at both ref pts.  Deploy shading on
    6336              :                 //             this window if this decreases glare at both ref pts.
    6337              :                 //         (2) Initial glare too high only at first ref pt.  Deploy shading
    6338              :                 //             on this window if glare at first ref pt decreases and
    6339              :                 //             glare at second ref pt stays below max.
    6340              :                 //         (3) Initial glare too high at second ref pt.  Deploy shading if glare
    6341              :                 //             at second ref pt decreases and glare at first ref pt stays below max.
    6342              :                 //
    6343              :                 // The approach taken is just to count the number of reference points that fulfill the individual requirements and see if it
    6344              :                 // covers all the reference points.
    6345            0 :                 int numRefPtOldAboveMaxNewBelowOld = 0;
    6346            0 :                 int numRefPtOldBelowMaxNewBelowMax = 0;
    6347            0 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6348            0 :                     auto const &refPt = thisDayltgCtrl.refPts(IL);
    6349              : 
    6350            0 :                     if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= refPt.glareIndex)
    6351            0 :                         ++numRefPtOldAboveMaxNewBelowOld;
    6352            0 :                     else if (refPt.glareIndex <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= thisDayltgCtrl.MaxGlareallowed)
    6353            0 :                         ++numRefPtOldBelowMaxNewBelowMax;
    6354              :                 }
    6355            0 :                 blnCycle = true;
    6356            0 :                 if ((numRefPtOldAboveMaxNewBelowOld + numRefPtOldBelowMaxNewBelowMax) == NREFPT) blnCycle = false;
    6357              :             }
    6358              : 
    6359              :             // restore the count to the value prior to the last loop through the group of exterior windows
    6360            0 :             count = countBeforeListOfExtWinLoop;
    6361            0 :             breakOuterLoop = false;
    6362              : 
    6363            0 :             for (const int IWin : listOfExtWin) {
    6364            0 :                 ++count;
    6365              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6366            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6367            0 :                 if (loop == 0) continue;
    6368              : 
    6369              :                 // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
    6370            0 :                 if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
    6371            0 :                     ANY_BLIND(s_surf->SurfWinShadingFlag(IWin)))
    6372            0 :                     continue;
    6373              : 
    6374            0 :                 int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6375            0 :                 if (!s_surf->Surface(IWin).HasShadeControl) continue;
    6376            0 :                 if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
    6377              : 
    6378              :                     // Shading this window has not improved the glare situation.
    6379              :                     // Reset shading flag to no shading condition, go to next window.
    6380            0 :                     if (blnCycle) {
    6381              :                         //  for switchable glazings, reset properties to clear state or partial switched state?
    6382            0 :                         if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6383            0 :                             s_surf->SurfWinSwitchingFactor(IWin) = 0.0;
    6384            0 :                             s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.unswitchedTvis;
    6385              : 
    6386              :                             // RESET properties for fully dark state
    6387            0 :                             for (int IL = 1; IL <= NREFPT; ++IL) {
    6388            0 :                                 auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6389            0 :                                 auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6390            0 :                                 daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6391            0 :                                 daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6392            0 :                                 daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6393              :                             }
    6394              :                         }
    6395              : 
    6396            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6397            0 :                         continue;
    6398            0 :                     }
    6399              : 
    6400              :                     // Shading this window has improved the glare situation.
    6401              :                     // Reset background luminance, glare index, and daylight illuminance at each ref pt.
    6402              :                     // For switchable glazings, this is fully switched, dark state
    6403            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6404            0 :                         auto &refPt = thisDayltgCtrl.refPts(IL);
    6405            0 :                         refPt.lums[iLum_Back] = shadeGroupLums.RDAYIL(IL)[iLum_Back];
    6406            0 :                         refPt.glareIndex = GLRNEW(IL);
    6407            0 :                         dl->DaylIllum(IL) = shadeGroupLums.RDAYIL(IL)[iLum_Illum];
    6408              :                     }
    6409              : 
    6410              :                     // TH comments (5/22/2009): seems for EC windows, if the calculated glare exceeds the max setpoint,
    6411              :                     //  the EC windows will be reset to fully dark state which significantly reduces the available daylight.
    6412              :                     //  A better way is to dim the EC windows as necessary just to meet the glare index, which will still
    6413              :                     //  provide more daylight while not exceeding the max glare! The question is then how to set the
    6414              :                     //  SwitchingFactor to just meet the glare index.
    6415              :                     //  This was addressed in CR 7984 for E+ 5.0. 1/19/2010
    6416              : 
    6417              :                     // If switchable glazing, set switching factor to 1: fully switched.
    6418            0 :                     if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6419              :                         // tmpSWFactor0 = SurfaceWindow( IWin ).SwitchingFactor; // save original
    6420              :                         // switching  factor
    6421              :                         ////Unused Set but never used
    6422            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
    6423            0 :                         s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
    6424              : 
    6425              :                         // restore fully dark values
    6426            0 :                         for (int IL = 1; IL <= NREFPT; ++IL) {
    6427            0 :                             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6428            0 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6429            0 :                             auto &wdayil = shadeGroupLums.WDAYIL(IL);
    6430            0 :                             wdayil[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6431            0 :                             wdayil[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6432            0 :                             daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6433            0 :                             daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6434            0 :                             daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6435              :                         }
    6436              :                     }
    6437              : 
    6438              :                     // Check if glare now acceptable at each ref pt.
    6439            0 :                     GlareOK = false;
    6440            0 :                     if (NREFPT == 1) {
    6441            0 :                         if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
    6442            0 :                     } else if (NREFPT > 1) {
    6443            0 :                         if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed &&
    6444            0 :                             thisDayltgCtrl.refPts(2).glareIndex <= thisDayltgCtrl.MaxGlareallowed)
    6445            0 :                             GlareOK = true;
    6446              :                     }
    6447              : 
    6448            0 :                     if (GlareOK) {
    6449            0 :                         if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing &&
    6450            0 :                             s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp) {
    6451              :                             // Added TH 1/14/2010
    6452              :                             // Only for switchable glazings with MeetDaylightIlluminanceSetpoint control
    6453              :                             // The glazing is in fully dark state, it might lighten a bit to provide more daylight
    6454              :                             //  while meeting maximum discomfort glare index
    6455              :                             // Iteration to find the right switching factor meeting the glare index
    6456              : 
    6457              :                             // get fully dark state values
    6458            0 :                             Real64 tmpSWSL1 = tmpDaylFromWinAtRefPt(1, loop)[iLum_Source][iWinCover_Shaded];
    6459            0 :                             Real64 tmpSWSL2 = (NREFPT > 1) ? tmpDaylFromWinAtRefPt(2, loop)[iLum_Source][iWinCover_Shaded] : 0.0;
    6460              : 
    6461              :                             // use simple fixed step search in iteraction, can be improved in future
    6462            0 :                             Real64 tmpSWFactor = 1.0 - tmpSWIterStep;
    6463            0 :                             while (tmpSWFactor > 0) {
    6464              :                                 // calc new glare at new switching state
    6465            0 :                                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6466            0 :                                     auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6467            0 :                                     auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6468            0 :                                     auto &refPt = thisDayltgCtrl.refPts(IL);
    6469            0 :                                     rdayil[iLum_Illum] =
    6470            0 :                                         dl->DaylIllum(IL) +
    6471            0 :                                         (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6472            0 :                                     rdayil[iLum_Back] =
    6473            0 :                                         refPt.lums[iLum_Back] +
    6474            0 :                                         (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6475            0 :                                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
    6476              :                                     // needs to update SourceLumFromWinAtRefPt(IL,2,loop) before re-calc DayltgGlare
    6477            0 :                                     tmpMult = (shadeGroupLums.unswitchedTvis -
    6478            0 :                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6479            0 :                                               shadeGroupLums.switchedTvis;
    6480            0 :                                     refPt.extWins(loop).lums[iLum_Source][iWinCover_Shaded] = ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
    6481              :                                     // Calc new glare
    6482            0 :                                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6483              :                                 } // for (IL)
    6484              : 
    6485              :                                 // Check whether new glare is OK
    6486            0 :                                 GlareOK = false;
    6487            0 :                                 if (NREFPT == 1) {
    6488            0 :                                     if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
    6489            0 :                                 } else if (NREFPT > 1) {
    6490            0 :                                     if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(2) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
    6491              :                                 }
    6492              : 
    6493            0 :                                 if (GlareOK) {
    6494            0 :                                     if (tmpSWFactor >= tmpSWIterStep) {
    6495              :                                         // Continue to lighten the glazing
    6496            0 :                                         tmpSWFactor -= tmpSWIterStep;
    6497            0 :                                         continue;
    6498              :                                     } else {
    6499              :                                         // Glare still OK but glazing already in clear state, no more lighten
    6500            0 :                                         breakOuterLoop = true;
    6501            0 :                                         break;
    6502              :                                     }
    6503              :                                 } else {
    6504              :                                     // Glare too high, exit and use previous switching state
    6505            0 :                                     tmpSWFactor += tmpSWIterStep;
    6506            0 :                                     breakOuterLoop = true;
    6507            0 :                                     break;
    6508              :                                 }
    6509              :                             } // if (tmpSWFactor > 0)
    6510              : 
    6511              :                             // Final re-calculation if needed
    6512            0 :                             if (!GlareOK) {
    6513              :                                 // Glare too high, use previous state and re-calc
    6514            0 :                                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6515            0 :                                     auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6516            0 :                                     auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6517            0 :                                     rdayil[iLum_Illum] =
    6518            0 :                                         dl->DaylIllum(IL) +
    6519            0 :                                         (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6520            0 :                                     rdayil[iLum_Back] =
    6521            0 :                                         thisDayltgCtrl.refPts(IL).lums[iLum_Back] +
    6522            0 :                                         (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6523            0 :                                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
    6524              : 
    6525              :                                     // needs to update SourceLumFromWinAtRefPt(IL,2,IWin) before re-calc DayltgGlare
    6526            0 :                                     tmpMult = (shadeGroupLums.unswitchedTvis -
    6527            0 :                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6528            0 :                                               shadeGroupLums.switchedTvis;
    6529            0 :                                     thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Source][iWinCover_Shaded] =
    6530            0 :                                         ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
    6531            0 :                                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6532              :                                 }
    6533              :                             }
    6534              : 
    6535              :                             // Update final results
    6536            0 :                             for (int IL = 1; IL <= NREFPT; ++IL) {
    6537            0 :                                 auto &refPt = thisDayltgCtrl.refPts(IL);
    6538            0 :                                 auto const &rdayil = shadeGroupLums.RDAYIL(IL);
    6539            0 :                                 refPt.lums[iLum_Back] = rdayil[iLum_Back];
    6540            0 :                                 refPt.glareIndex = GLRNEW(IL);
    6541            0 :                                 dl->DaylIllum(IL) = rdayil[iLum_Illum];
    6542              : 
    6543            0 :                                 tmpMult =
    6544            0 :                                     (shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6545            0 :                                     shadeGroupLums.switchedTvis;
    6546              :                                 // update report variables
    6547            0 :                                 auto &daylFromWinAtRefPt = refPt.extWins(loop).lums;
    6548            0 :                                 auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6549            0 :                                 daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded] * tmpMult;
    6550            0 :                                 daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded] * tmpMult;
    6551              :                             }
    6552            0 :                             s_surf->SurfWinSwitchingFactor(IWin) = tmpSWFactor;
    6553            0 :                             s_surf->SurfWinVisTransSelected(IWin) =
    6554            0 :                                 shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor;
    6555              : 
    6556              :                         } else {
    6557              :                             // For un-switchable glazing or switchable glazing but not MeetDaylightIlluminaceSetpoint control,
    6558              :                             // it is in shaded state and glare is ok - job is done, exit the window loop - IWin
    6559            0 :                             breakOuterLoop = true;
    6560            0 :                             break;
    6561              :                         }
    6562              :                     } // if (glareOK)
    6563              :                 }     // if (glareControlIsActive)
    6564              :             }         // for (IWin)
    6565            0 :             if (breakOuterLoop) break;
    6566              :         } // for (igroup)
    6567              :     }     // if (GlareFlag)
    6568              : 
    6569              :     // Loop again over windows and reset remaining shading flags that
    6570              :     // are 10 or higher (i.e., conditionally off) to off
    6571         1908 :     for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6572          954 :         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    6573         1908 :         for (int IWin = thisSpace.WindowSurfaceFirst; IWin <= thisSpace.WindowSurfaceLast; ++IWin) {
    6574          954 :             if (s_surf->Surface(IWin).ExtBoundCond != ExternalEnvironment) continue;
    6575          954 :             bool anyGlareControl = (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) ||
    6576          954 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) ||
    6577          954 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) ||
    6578         2862 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) ||
    6579          954 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff);
    6580          954 :             if (anyGlareControl) {
    6581            0 :                 s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6582              :             }
    6583              :         }
    6584              :     }
    6585              : 
    6586              :     // Variables for reporting
    6587         2323 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    6588         1369 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    6589         1369 :         refPt.lums[iLum_Illum] = dl->DaylIllum(IL);
    6590              : 
    6591              :         // added TH 12/2/2008
    6592         1369 :         refPt.timeExceedingGlareIndexSetPoint = (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) ? state.dataGlobal->TimeStepZone : 0.0;
    6593              :         // added TH 7/6/2009
    6594         1369 :         refPt.timeExceedingDaylightIlluminanceSetPoint = (dl->DaylIllum(IL) > refPt.illumSetPoint) ? state.dataGlobal->TimeStepZone : 0.0;
    6595              :     }
    6596          954 : } // DayltgInteriorIllum()
    6597              : 
    6598            0 : void DayltgInteriorTDDIllum(EnergyPlusData &state)
    6599              : {
    6600              : 
    6601              :     // SUBROUTINE INFORMATION:
    6602              :     //       AUTHOR         Linda Lawrie
    6603              :     //       DATE WRITTEN   October 2006
    6604              : 
    6605              :     // PURPOSE OF THIS SUBROUTINE:
    6606              :     // Calculate the TDD Pipe illuminance values
    6607            0 :     auto &dl = state.dataDayltg;
    6608              : 
    6609              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    6610              :     int iSky1; // Sky type index values for averaging two sky types
    6611              :     int iSky2;
    6612              :     Real64 SkyWeight; // Weighting factor used to average two different sky types
    6613              : 
    6614            0 :     if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    6615            0 :         SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    6616            0 :         iSky1 = (int)SkyType::Clear;
    6617            0 :         iSky2 = (int)SkyType::ClearTurbid;
    6618            0 :     } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    6619            0 :         SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    6620            0 :         iSky1 = (int)SkyType::ClearTurbid;
    6621            0 :         iSky2 = (int)SkyType::Intermediate;
    6622              :     } else { // Sky is average of intermediate and overcast
    6623            0 :         SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    6624            0 :         iSky1 = (int)SkyType::Intermediate;
    6625            0 :         iSky2 = (int)SkyType::Overcast;
    6626              :     }
    6627              : 
    6628              :     // Calculate and report TDD visible transmittances
    6629            0 :     for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
    6630              : 
    6631            0 :         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam =
    6632            0 :             state.dataGlobal->WeightNow * dl->TDDTransVisBeam(state.dataGlobal->HourOfDay, PipeNum) +
    6633            0 :             state.dataGlobal->WeightPreviousHour * dl->TDDTransVisBeam(state.dataGlobal->PreviousHour, PipeNum);
    6634              : 
    6635            0 :         auto const &tddFluxIncCurr = dl->TDDFluxInc(state.dataGlobal->HourOfDay, PipeNum);
    6636            0 :         auto const &tddFluxIncPrev = dl->TDDFluxInc(state.dataGlobal->PreviousHour, PipeNum);
    6637              : 
    6638            0 :         auto const &tddFluxTransCurr = dl->TDDFluxTrans(state.dataGlobal->HourOfDay, PipeNum);
    6639            0 :         auto const &tddFluxTransPrev = dl->TDDFluxTrans(state.dataGlobal->PreviousHour, PipeNum);
    6640              : 
    6641            0 :         Illums TDDTransVisDiff;
    6642            0 :         for (int iSky = iSky1; iSky <= iSky2; ++iSky) {
    6643            0 :             Real64 tddTransVisDiffCurr = (tddFluxIncCurr.sky[iSky] > 0.0) ? (tddFluxTransCurr.sky[iSky] / tddFluxIncCurr.sky[iSky]) : 0.0;
    6644            0 :             Real64 tddTransVisDiffPrev = (tddFluxIncPrev.sky[iSky] > 0.0) ? (tddFluxTransPrev.sky[iSky] / tddFluxIncPrev.sky[iSky]) : 0.0;
    6645              : 
    6646            0 :             TDDTransVisDiff.sky[iSky] =
    6647            0 :                 state.dataGlobal->WeightNow * tddTransVisDiffCurr + state.dataGlobal->WeightPreviousHour * tddTransVisDiffPrev;
    6648              :         } // for (iSky)
    6649              : 
    6650            0 :         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff =
    6651            0 :             SkyWeight * TDDTransVisDiff.sky[iSky1] + (1.0 - SkyWeight) * TDDTransVisDiff.sky[iSky2];
    6652              :     } // for (PipeNum)
    6653            0 : } // DayltgInteriorTDDIllum()
    6654              : 
    6655        71219 : void DayltgElecLightingControl(EnergyPlusData &state)
    6656              : {
    6657              : 
    6658              :     // SUBROUTINE INFORMATION:
    6659              :     //       AUTHOR         Fred Winkelmann
    6660              :     //       DATE WRITTEN   July 1997
    6661              :     //       MODIFIED       Mar 2004, FCW: add inter-reflected illuminance from interior windows to DaylIllum
    6662              :     //                      Apr 2004, FCW: move CALL ReportIllumMap from DayltgInteriorIllum2 (DayltgInteriorMapIllum)
    6663              :     //                      Apr 2010, BG NREL: remove inter-reflected illuminance to stop double counting
    6664              :     //                      Aug 2012, BG NREL: added availability schedule logic
    6665              : 
    6666              :     // PURPOSE OF THIS SUBROUTINE:
    6667              :     // For a daylit space, determines lighting power reduction factor due to
    6668              :     // daylighting for different lighting control systems.
    6669              : 
    6670              :     // Called by InitSurfaceHeatBalance.
    6671              : 
    6672              :     // REFERENCES:
    6673              :     // Based on DOE-2.1E subroutine DLTSYS.
    6674        71219 :     auto &dl = state.dataDayltg;
    6675              : 
    6676        71219 :     if (dl->daylightControl.empty()) {
    6677        70270 :         return;
    6678              :     }
    6679              :     // Reset space power reduction factors
    6680         1898 :     for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
    6681          949 :         dl->spacePowerReductionFactor(spaceNum) = 1.0;
    6682              :     }
    6683              : 
    6684         1898 :     for (auto &thisDayltgCtrl : dl->daylightControl) {
    6685              : 
    6686          949 :         if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
    6687              :             // Set space power reduction factors
    6688            0 :             if (thisDayltgCtrl.PowerReductionFactor < 1.0) {
    6689            0 :                 if (thisDayltgCtrl.spaceIndex > 0) {
    6690              :                     // This is a space-level daylighting control
    6691            0 :                     dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = thisDayltgCtrl.PowerReductionFactor;
    6692              :                 } else {
    6693              :                     // This is a zone-level daylighting control
    6694            0 :                     for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6695            0 :                         dl->spacePowerReductionFactor(spaceNum) = thisDayltgCtrl.PowerReductionFactor;
    6696              :                     }
    6697              :                 }
    6698              :             }
    6699            0 :             continue;
    6700            0 :         }
    6701              : 
    6702              :         // Electric lighting power reduction factor for a given daylighting control
    6703          949 :         Real64 &TotReduction = thisDayltgCtrl.PowerReductionFactor;
    6704          949 :         TotReduction = 0.0;
    6705          949 :         Real64 ZFTOT = 0.0;
    6706              : 
    6707              :         // check if scheduled to be available
    6708          949 :         if (thisDayltgCtrl.availSched->getCurrentVal() > 0.0) {
    6709              : 
    6710              :             // Loop over reference points
    6711         2305 :             for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
    6712         1356 :                 auto &refPt = thisDayltgCtrl.refPts(IL);
    6713              :                 // Total fraction of zone that is daylit
    6714         1356 :                 ZFTOT += refPt.fracZoneDaylit;
    6715              : 
    6716         1356 :                 dl->DaylIllum(IL) = refPt.lums[iLum_Illum];
    6717         1356 :                 Real64 FL = 0.0;
    6718         1356 :                 if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
    6719          276 :                     FL = (refPt.illumSetPoint - dl->DaylIllum(IL)) / refPt.illumSetPoint;
    6720              :                 }
    6721              : 
    6722              :                 // BRANCH ON LIGHTING SYSTEM TYPE
    6723         1356 :                 LtgCtrlType LSYSTP = thisDayltgCtrl.LightControlType;
    6724         1356 :                 Real64 FP = 0.0;
    6725         1356 :                 if (LSYSTP != LtgCtrlType::Stepped) {
    6726              :                     // Continuously dimmable system with linear power curve
    6727              :                     // Fractional output power required to meet setpoint
    6728         1350 :                     FP = 1.0;
    6729              :                     // LIGHT-CTRL-TYPE = CONTINUOUS (LSYSTP = 1)
    6730         1350 :                     if (FL <= thisDayltgCtrl.MinLightFraction) {
    6731         1138 :                         FP = thisDayltgCtrl.MinPowerFraction;
    6732              :                     }
    6733              :                     // LIGHT-CTRL-TYPE = CONTINUOUS/OFF (LSYSTP = 3)
    6734         1350 :                     if (FL <= thisDayltgCtrl.MinLightFraction && LSYSTP == LtgCtrlType::ContinuousOff) {
    6735            0 :                         FP = 0.0;
    6736              :                     }
    6737         1350 :                     if (FL > thisDayltgCtrl.MinLightFraction && FL < 1.0) {
    6738          212 :                         FP = (FL + (1.0 - FL) * thisDayltgCtrl.MinPowerFraction - thisDayltgCtrl.MinLightFraction) /
    6739          212 :                              (1.0 - thisDayltgCtrl.MinLightFraction);
    6740              :                     }
    6741              : 
    6742              :                 } else { // LSYSTP = 2
    6743              :                     // Stepped system
    6744            6 :                     FP = 0.0;
    6745              :                     // #9060: Use a tolerance, otherwise at very low (< 1e-12) daylighting conditions, you can get a multiplier > 1.0
    6746            6 :                     if (dl->DaylIllum(IL) < 0.1) {
    6747            2 :                         FP = 1.0;
    6748            4 :                     } else if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
    6749            4 :                         FP = double(int(thisDayltgCtrl.LightControlSteps * FL) + 1) / double(thisDayltgCtrl.LightControlSteps);
    6750              :                     }
    6751              : 
    6752            6 :                     if (thisDayltgCtrl.LightControlProbability < 1.0) {
    6753              :                         // Manual operation.  Occupant sets lights one level too high a fraction of the time equal to
    6754              :                         // 1. - ZoneDaylight(ZoneNum)%LightControlProbability.  RANDOM_NUMBER returns a random number
    6755              :                         // between 0 and 1.
    6756              :                         Real64 XRAN;
    6757            2 :                         RANDOM_NUMBER(XRAN);
    6758            2 :                         if (XRAN >= thisDayltgCtrl.LightControlProbability) {
    6759              :                             // Set level one higher
    6760            2 :                             if (FP < 1.0) {
    6761            1 :                                 FP += (1.0 / double(thisDayltgCtrl.LightControlSteps));
    6762              :                             }
    6763              :                         } // XRAN
    6764              :                     }     // Light Control Probability < 1
    6765              :                 }         // Lighting System Type
    6766              : 
    6767         1356 :                 refPt.powerReductionFactor = FP;
    6768              : 
    6769              :                 // Accumulate net ltg power reduction factor for entire zone
    6770         1356 :                 TotReduction += refPt.powerReductionFactor * refPt.fracZoneDaylit;
    6771              : 
    6772              :             } // End of loop over reference points, IL
    6773              : 
    6774              :             // Correct for fraction of zone (1-ZFTOT) not controlled by
    6775              :             // the reference points.  For this fraction (which is usually zero),
    6776              :             // the electric lighting is unaffected and the power reduction
    6777              :             // factor is therefore 1.0.
    6778          949 :             TotReduction += (1.0 - ZFTOT);
    6779              :         } else { // controls not currently available
    6780            0 :             TotReduction = 1.0;
    6781              :         }
    6782              : 
    6783              :         // Set space power reduction factors
    6784          949 :         if (thisDayltgCtrl.spaceIndex > 0) {
    6785              :             // This is a space-level daylighting control
    6786            0 :             dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = TotReduction;
    6787              :         } else {
    6788              :             // This is a zone-level daylighting control
    6789         1898 :             for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6790          949 :                 dl->spacePowerReductionFactor(spaceNum) = TotReduction;
    6791              :             }
    6792              :         }
    6793              :     } // end daylighting control loop
    6794              : 
    6795              :     //  IF(TotIllumMaps > 0 .and. .not. DoingSizing .and. .not. WarmupFlag .and. .not. KickoffSimulation) THEN
    6796          949 :     if ((int)dl->illumMaps.size() > 0 && !state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
    6797          118 :         for (int mapNum = 1; mapNum <= (int)dl->illumMaps.size(); ++mapNum) {
    6798           59 :             auto &illumMap = dl->illumMaps(mapNum);
    6799           59 :             if (state.dataGlobal->TimeStep == 1) dl->mapResultsToReport = false;
    6800         5959 :             for (auto &refPt : illumMap.refPts) {
    6801         5900 :                 refPt.lumsHr[iLum_Illum] += refPt.lums[iLum_Illum] / double(state.dataGlobal->TimeStepsInHour);
    6802         5900 :                 if (refPt.lumsHr[iLum_Illum] > 0.0) {
    6803         5900 :                     dl->mapResultsToReport = true;
    6804         5900 :                     dl->mapResultsReported = true;
    6805              :                 }
    6806              :             }
    6807           59 :             ReportIllumMap(state, mapNum);
    6808           59 :             if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
    6809         1515 :                 for (auto &refPt : illumMap.refPts) {
    6810         1500 :                     refPt.lumsHr[iLum_Illum] = refPt.lums[iLum_Illum] = 0.0;
    6811              :                 }
    6812              :             }
    6813              :         } // for (mapNum)
    6814              :     }     // if (MapSize > 0)
    6815              : } // DayltgElecLightingControl()
    6816              : 
    6817          459 : Real64 DayltgGlarePositionFactor(Real64 X, // Lateral and vertical distance of luminous window element from
    6818              :                                  Real64 Y)
    6819              : {
    6820              : 
    6821              :     // SUBROUTINE INFORMATION:
    6822              :     //       AUTHOR         Fred Winkelmann
    6823              :     //       DATE WRITTEN   July 1997
    6824              : 
    6825              :     // PURPOSE OF THIS SUBROUTINE:
    6826              :     // by table interpolation, evaluates the
    6827              :     // Hopkinson position factor used in glare calculation
    6828              :     // (Hopkinson, Petherbridge, AND Longmore -- Daylighting,
    6829              :     // London, 1966, PP 307, 323).  X (Y) is the lateral
    6830              :     // (vertical) distance of luminous window element from
    6831              :     // horizontal line of vision, divided by horizontal distance
    6832              :     // from eye of observer. The array PF contains values of
    6833              :     // the position factor for X = 0, 0.5, 1.0, 1.5, 2.0, 2.5,
    6834              :     // and 3.0 and Y = 0, 0.5, 1.0, 1.5, 2.0. Called by CalcDayltgCoefficients.
    6835              : 
    6836              :     // REFERENCES:
    6837              :     // Based on DOE-2.1E subroutine DPFAC.
    6838              : 
    6839              :     // Position factor array
    6840              :     static constexpr std::array<std::array<Real64, 7>, 5> PF = {{
    6841              :         {1.00, 0.492, 0.226, 0.128, 0.081, 0.061, 0.057},
    6842              :         {0.123, 0.119, 0.065, 0.043, 0.029, 0.026, 0.023},
    6843              :         {0.019, 0.026, 0.019, 0.016, 0.014, 0.011, 0.011},
    6844              :         {0.008, 0.008, 0.008, 0.008, 0.008, 0.006, 0.006},
    6845              :         {0.0, 0.0, 0.003, 0.003, 0.003, 0.003, 0.003},
    6846              :     }};
    6847              : 
    6848          459 :     if (X < 0.0 || X >= 3.0) return 0.0;
    6849          451 :     if (Y < 0.0 || Y >= 2.0) return 0.0;
    6850              : 
    6851          451 :     int IX = 1 + int(2.0 * X);
    6852          451 :     int IY = 1 + int(2.0 * Y);
    6853          451 :     Real64 X1 = 0.5 * double(IX - 1);
    6854          451 :     Real64 Y1 = 0.5 * double(IY - 1);
    6855          451 :     Real64 FA = PF[IY - 1][IX - 1] + 2.0 * (X - X1) * (PF[IY - 1][IX] - PF[IY - 1][IX - 1]);
    6856          451 :     Real64 FB = PF[IY][IX - 1] + 2.0 * (X - X1) * (PF[IY][IX] - PF[IY][IX - 1]);
    6857          451 :     return FA + 2.0 * (Y - Y1) * (FB - FA);
    6858              : } // DayltgGlarePositionFactor()
    6859              : 
    6860         2066 : void DayltgInterReflectedIllum(EnergyPlusData &state,
    6861              :                                int const ISunPos, // Sun position counter; used to avoid calculating various
    6862              :                                int const IHR,     // Hour of day
    6863              :                                int const enclNum, // Daylighting enclosure index
    6864              :                                int const IWin     // Window index
    6865              : )
    6866              : {
    6867              : 
    6868              :     // SUBROUTINE INFORMATION:
    6869              :     //       AUTHOR         Fred Winkelmann
    6870              :     //       DATE WRITTEN   July 1997
    6871              :     //       MODIFIED       FCW December 1998
    6872              :     //                      FCW June 2001: Add blind calculations
    6873              :     //                      FCW Jan 2001: Add blinds with movable slats
    6874              :     //                      FCW Jan 2003: Add between-glass blinds
    6875              :     //                      FCW Jul 2003: account for transmittance of shading surfaces
    6876              :     //                       (previously these were assumed opaque even if transmittance schedule
    6877              :     //                        value was non-zero)
    6878              :     //                      FCW Aug 2003: modify initialization of WinLum from WinLum = 0. TO
    6879              :     //                        WinLum(:,:,IHR) = 0. Otherwise values calculated in previous
    6880              :     //                        call are incorrectly zeroed. Result was that window luminance with
    6881              :     //                        shade or blind included only contribution from first window element
    6882              :     //                        in window element loop in CalcDayltgCoefficients, thus seriously
    6883              :     //                        undercalculating window luminance for windows with more than one
    6884              :     //                        window element. Similarly, modified initialization of WLUMSU from
    6885              :     //                        WLUMSU = 0. to WLUMSU(:,IHR) = 0., and of WLUMSUdisk from
    6886              :     //                        WLUMSUdisk = 0. to WLUMSUdisk(:,IHR) = 0.
    6887              :     //                      PGE Aug 2003: Add daylighting shelves.
    6888              :     //                      FCW Nov 2003: Add beam solar and sky solar reflected from obstructions;
    6889              :     //                                    add beam solar reflected from ground accounting for obstructions.
    6890              :     //                      FCW Nov 2003: increase NPHMAX from 9 to 10 to avoid rays with altitude angle = 0
    6891              :     //                                    for vertical surfaces.
    6892              :     //                      FCW Nov 2003: fix the expression for min and max limits of azimuth; old expression
    6893              :     //                                    broke down for window normals with negative altitude angle
    6894              :     //                      FCW Nov 2003: add specular reflection from exterior obstructions
    6895              :     //                      FCW Apr 2004: add light well efficiency multiplying window transmittance
    6896              :     //                      FCW Apr 2004: add diffusing glazing
    6897              :     //                      RAR (FSEC)  May 2006: add exterior window screen
    6898              :     //                      B. Griffith NREL April 2010: CR7869 add adjacent zone area if window is not on this zone
    6899              :     //                                    apply interior window transmission and blocking to beam transmission from ext win
    6900              : 
    6901              :     // PURPOSE OF THIS SUBROUTINE:
    6902              :     // Called from CalcDayltgCoefficients for each window and reference point in a daylit
    6903              :     // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
    6904              :     // to internally reflected light by integrating to determine the amount of flux from
    6905              :     // sky and ground (and beam reflected from obstructions) transmitted through
    6906              :     // the center of the window and then reflecting this
    6907              :     // light from the inside surfaces of the space.  The "split-flux" method is used
    6908              :     // (Lynes, Principles of Natural Lighting, 1968).  EINT is determined for
    6909              :     // different sky types and for window with and without shades, screens or blinds.
    6910              :     // Also finds luminance (WinLum and WLUMSU) of window with shade or blind, &
    6911              :     // or with diffusing glass, for different sky types.
    6912              : 
    6913              :     // REFERENCES:
    6914              :     // Based on DOE-2.1E subroutine DREFLT.
    6915              : 
    6916              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    6917              :     // In the following I,J arrays:
    6918              :     // I = sky type;
    6919              :     // J = 1 for bare window, 2 and above for window with shade or blind.
    6920         2066 :     Illums ZSK;                   // Sky-related and sun-related illuminance on window from sky/ground
    6921         2066 :     Vector3<Real64> U;            // Unit vector in (PH,TH) direction
    6922         2066 :     Vector3<Real64> nearestHitPt; // Hit point of ray on nearest obstruction (m)
    6923         2066 :     Vector3<Real64> obsHitPt;     // Coordinates of hit point on an obstruction (m)
    6924         2066 :     Vector3<Real64> groundHitPt;  // Coordinates of point that ray from window center hits the ground (m)
    6925         2066 :     std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLCW = {Illums()}; // Sky-related upgoing luminous flux
    6926         2066 :     std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLFW = {Illums()}; // Sky-related downgoing luminous flux
    6927              : 
    6928              :     //  3=intermediate, 4=overcast
    6929              :     Real64 DPH; // Sky/ground element altitude and azimuth increments (radians)
    6930              :     Real64 DTH;
    6931              :     Real64 PH; // Sky/ground element altitude and azimuth (radians)
    6932              :     Real64 TH;
    6933              :     Real64 SPH; // Sine and cosine of PH
    6934              :     Real64 CPH;
    6935              :     Real64 PHMIN; // Limits of altitude integration (radians)
    6936              :     Real64 PHMAX;
    6937              :     Real64 ThMin; // Limits of azimuth integration (radians)
    6938              :     Real64 ThMax;
    6939              :     Real64 PhWin; // Altitude, azimuth angle of window normal (radians)
    6940              :     Real64 ThWin;
    6941              :     Real64 ACosTanTan; // ACOS(-TAN(Ph)*TAN(PhWin))
    6942              :     Real64 DA;         // CPH*DTH*DPH
    6943              :     Real64 COSB;       // Cosine of angle of incidence of light from sky or ground
    6944              :     Real64 TVISBR;     // Transmittance of window without shading at COSB
    6945              :     //  (times light well efficiency, if appropriate)
    6946              :     Real64 ZSU;
    6947              :     //  element for clear and overcast sky
    6948              :     Real64 ObTrans; // Product of solar transmittances of obstructions seen by a light ray
    6949              : 
    6950              :     // unused  REAL(r64)         :: HitPointLumFrClearSky     ! Luminance of obstruction from clear sky (cd/m2)
    6951              :     // unused  REAL(r64)         :: HitPointLumFrOvercSky     ! Luminance of obstruction from overcast sky (cd/m2)
    6952              :     // unused  REAL(r64)         :: HitPointLumFrSun          ! Luminance of obstruction from sun (cd/m2)
    6953              :     int ICtrl;       // Window control pointer
    6954              :     Real64 COSBSun;  // Cosine of angle of incidence of direct sun on window
    6955              :     Real64 TVISBSun; // Window's visible transmittance at COSBSun
    6956              :     //  (times light well efficiency, if appropriate)
    6957              :     Real64 ZSU1; // Transmitted direct normal illuminance (lux)
    6958              :     //  CHARACTER(len=32) :: ShType                    ! Window shading device type
    6959              :     bool ShadeOn;                // True if exterior or interior window shade present
    6960              :     bool BlindOn;                // True if exterior or interior window blind present
    6961              :     bool ScreenOn;               // True if exterior window screen present
    6962              :                                  //        int ScNum; // Screen number //Unused Set but never used
    6963              :     int PipeNum;                 // TDD pipe object number
    6964              :     int ShelfNum;                // Daylighting shelf object number
    6965              :     int InShelfSurf;             // Inside daylighting shelf surface number
    6966              :     int OutShelfSurf;            // Outside daylighting shelf surface number
    6967              :     Real64 TransBlBmDiffFront;   // Isolated blind vis beam-diffuse front transmittance
    6968              :     Real64 TransScBmDiffFront;   // Isolated screen vis beam-diffuse front transmittance
    6969              :     Real64 ReflGlDiffDiffBack;   // Bare glazing system vis diffuse back reflectance
    6970              :     Real64 ReflGlDiffDiffFront;  // Bare glazing system vis diffuse front reflectance
    6971              :     Real64 ReflBlBmDiffFront;    // Isolated blind vis beam-diffuse front reflectance
    6972              :     Real64 TransBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front transmittance
    6973              :     Real64 ReflBlDiffDiffFront;  // Isolated blind vis diffuse-diffuse front reflectance
    6974              :     Real64 ReflBlDiffDiffBack;   // Isolated blind vis diffuse-diffuse back reflectance
    6975              :     Real64 ReflScDiffDiffBack;   // Isolated screen vis diffuse-diffuse back reflectance
    6976              : 
    6977              :     Real64 td2; // Diffuse-diffuse vis trans of bare glass layers 2 and 3
    6978              :     Real64 td3;
    6979              :     Real64 rbd1; // Beam-diffuse back vis reflectance of bare glass layers 1 and 2
    6980              :     Real64 rbd2;
    6981              :     Real64 rfd2; // Beam-diffuse front vis reflectance of bare glass layers 2 and 3
    6982              :     Real64 rfd3;
    6983              :     Real64 tfshd;      // Diffuse-diffuse front vis trans of bare blind
    6984              :     Real64 rbshd;      // Diffuse-diffuse back vis reflectance of bare blind
    6985              :     Real64 ZSUObsRefl; // Illuminance on window from beam solar reflected by an
    6986              :     //  obstruction (for unit beam normal illuminance)
    6987              :     int NearestHitSurfNum;  // Surface number of nearest obstruction
    6988              :     int NearestHitSurfNumX; // Surface number to use when obstruction is a shadowing surface
    6989              :     Real64 LumAtHitPtFrSun; // Luminance at hit point on obstruction from solar reflection
    6990              :     //  for unit beam normal illuminance (cd/m2)
    6991              :     Real64 SunObstructionMult; // = 1 if sun hits a ground point; otherwise = 0
    6992              :     bool hitObs;               // True iff obstruction is hit
    6993              :     Real64 ObsVisRefl;         // Visible reflectance of obstruction
    6994              :     Real64 SkyReflVisLum;      // Reflected sky luminance at hit point divided by unobstructed sky
    6995              :     //  diffuse horizontal illuminance [(cd/m2)/lux]
    6996              :     Real64 dReflObsSky; // Contribution to sky-related illuminance on window due to sky diffuse
    6997              :     //  reflection from an obstruction
    6998              :     Real64 TVisSunRefl; // Diffuse vis trans of bare window for beam reflection calc
    6999              :     //  (times light well efficiency, if appropriate)
    7000              :     Real64 ZSU1refl; // Beam normal illuminance times ZSU1refl = illuminance on window
    7001              :     //  due to specular reflection from exterior surfaces
    7002              : 
    7003              :     ExtWinType extWinType;      // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    7004              :     Real64 EnclInsideSurfArea;  // temporary for calculations, total surface area of enclosure surfaces m2
    7005              :     int IntWinAdjZoneExtWinNum; // the index of the exterior window in IntWinAdjZoneExtWin nested struct
    7006              :     int IntWinNum;              // window index for interior windows associated with exterior windows
    7007              :     Real64 COSBintWin;
    7008              : 
    7009              :     WinShadingType ShType;
    7010              : 
    7011         2066 :     auto &s_mat = state.dataMaterial;
    7012         2066 :     auto &dl = state.dataDayltg;
    7013         2066 :     auto &s_surf = state.dataSurface;
    7014              : 
    7015         2066 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    7016         2066 :     auto const &surf = s_surf->Surface(IWin);
    7017         2066 :     auto const &surfWin = s_surf->SurfaceWindow(IWin);
    7018         2066 :     int const enclNumThisWin = s_surf->Surface(surf.BaseSurf).SolarEnclIndex;
    7019              :     // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea was calculated in subr DayltgAveInteriorReflectance
    7020              : 
    7021         2066 :     if (enclNumThisWin == enclNum) {
    7022         2066 :         extWinType = ExtWinType::InZone;
    7023         2066 :         EnclInsideSurfArea = dl->enclDaylight(enclNumThisWin).totInsSurfArea;
    7024         2066 :         IntWinAdjZoneExtWinNum = 0;
    7025              :     } else {
    7026            0 :         extWinType = ExtWinType::AdjZone;
    7027              :         // If window is exterior window in adjacent zone, then use areas of both enclosures
    7028            0 :         EnclInsideSurfArea = dl->enclDaylight(enclNum).totInsSurfArea + dl->enclDaylight(enclNumThisWin).totInsSurfArea;
    7029              :         // find index in IntWinAdjZoneExtWin
    7030            0 :         for (int AdjExtWinLoop = 1; AdjExtWinLoop <= thisEnclDaylight.NumOfIntWinAdjEnclExtWins; ++AdjExtWinLoop) {
    7031            0 :             if (IWin == thisEnclDaylight.IntWinAdjEnclExtWin(AdjExtWinLoop).SurfNum) { // found it
    7032            0 :                 IntWinAdjZoneExtWinNum = AdjExtWinLoop;
    7033            0 :                 break; // added TH 4/13/2010
    7034              :             }
    7035              :         }
    7036              :     }
    7037              : 
    7038              :     // Initialize window luminance and fluxes for split-flux calculation
    7039         8264 :     dl->winLum(IHR)[(int)iWinCover_Bare] = dl->winLum(IHR)[(int)iWinCover_Shaded] = Illums();
    7040              :     // dl->WLUMSU(IHR, _) = 0.0;
    7041              :     // dl->WLUMSUdisk(IHR, _) = 0.0;
    7042              : 
    7043         2066 :     int const IConst = s_surf->SurfActiveConstruction(IWin);
    7044         2066 :     auto const &construct = state.dataConstruction->Construct(IConst);
    7045              : 
    7046         2066 :     BlindOn = false;
    7047         2066 :     ShadeOn = false;
    7048         2066 :     ScreenOn = false;
    7049              : 
    7050         2066 :     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7051            0 :         PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
    7052              :     }
    7053              : 
    7054         2066 :     ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
    7055         2066 :     if (ShelfNum > 0) {
    7056            0 :         InShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf;   // Inside daylighting shelf present if > 0
    7057            0 :         OutShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf; // Outside daylighting shelf present if > 0
    7058              :     } else {
    7059         2066 :         InShelfSurf = 0;
    7060         2066 :         OutShelfSurf = 0;
    7061              :     }
    7062              : 
    7063              :     // Divide sky and ground into elements of altitude PH and
    7064              :     // azimuth TH, and add the contribution of light coming from each
    7065              :     // element to the transmitted flux at the center of the window
    7066              :     // Azimuth ranges over a maximum of 2 Pi radians.
    7067              :     // Altitude ranges over a maximum of Pi/2 radians between -Pi/2 < PH < +Pi/2, so that elements are not counted twice
    7068              :     // PH = 0 at the horizon; PH = Pi/2 at the zenith
    7069         2066 :     PHMIN = max(-Constant::PiOvr2, surfWin.phi - Constant::PiOvr2);
    7070         2066 :     PHMAX = min(Constant::PiOvr2, surfWin.phi + Constant::PiOvr2);
    7071         2066 :     DPH = (PHMAX - PHMIN) / double(NPHMAX);
    7072              : 
    7073              :     // Sky/ground element altitude integration
    7074         2066 :     Vector3<Real64> const SUNCOS_IHR(s_surf->SurfSunCosHourly(IHR));
    7075        22726 :     for (int IPH = 1; IPH <= NPHMAX; ++IPH) {
    7076        20660 :         PH = PHMIN + (double(IPH) - 0.5) * DPH;
    7077              : 
    7078        20660 :         SPH = std::sin(PH);
    7079        20660 :         CPH = std::cos(PH);
    7080              :         // Third component of unit vector in (TH,PH) direction
    7081        20660 :         U.z = SPH;
    7082              : 
    7083              :         // Limits of azimuth integration
    7084        20660 :         PhWin = surfWin.phi;
    7085        20660 :         ThWin = surfWin.theta;
    7086        20660 :         if (PhWin >= 0.0) {
    7087        20660 :             if (PH >= Constant::PiOvr2 - PhWin) {
    7088            0 :                 ThMin = -Constant::Pi;
    7089            0 :                 ThMax = Constant::Pi;
    7090              :             } else {
    7091        20660 :                 ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
    7092        20660 :                 ThMin = ThWin - std::abs(ACosTanTan);
    7093        20660 :                 ThMax = ThWin + std::abs(ACosTanTan);
    7094              :             }
    7095              : 
    7096              :         } else { // PhiSurf < 0.0
    7097            0 :             if (PH <= -PhWin - Constant::PiOvr2) {
    7098            0 :                 ThMin = -Constant::Pi;
    7099            0 :                 ThMax = Constant::Pi;
    7100              :             } else {
    7101            0 :                 ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
    7102            0 :                 ThMin = ThWin - std::abs(ACosTanTan);
    7103            0 :                 ThMax = ThWin + std::abs(ACosTanTan);
    7104              :             }
    7105              :         }
    7106              : 
    7107        20660 :         DTH = (ThMax - ThMin) / double(NTHMAX);
    7108        20660 :         DA = CPH * DTH * DPH;
    7109              : 
    7110              :         // Sky/ground element azimuth integration
    7111        20660 :         Real64 const sin_window_phi(std::sin(surfWin.phi));
    7112        20660 :         Real64 const cos_window_phi(std::cos(surfWin.phi));
    7113       351220 :         for (int ITH = 1; ITH <= NTHMAX; ++ITH) {
    7114       330560 :             TH = ThMin + (double(ITH) - 0.5) * DTH;
    7115       330560 :             U.x = CPH * std::cos(TH);
    7116       330560 :             U.y = CPH * std::sin(TH);
    7117              :             // Cosine of angle of incidence of light from sky or ground element
    7118       330560 :             COSB = SPH * sin_window_phi + CPH * cos_window_phi * std::cos(TH - surfWin.theta);
    7119       330560 :             if (COSB < 0.0) continue; // Sky/ground elements behind window (although there shouldn't be any)
    7120              : 
    7121              :             // Initialize illuminance on window for this sky/ground element
    7122      1322240 :             ZSK = Illums();
    7123       330560 :             ZSU = 0.0;
    7124              :             // Initialize illuminance on window from beam solar reflection if ray hits an obstruction
    7125       330560 :             ZSUObsRefl = 0.0;
    7126              : 
    7127       330560 :             if (ISunPos == 1) { // Intersection calculation has to be done only for first sun position
    7128              :                 // Determine net transmittance of obstructions that the ray hits. ObTrans will be 1.0
    7129              :                 // if no obstructions are hit.
    7130        18720 :                 ObTrans = DayltgHitObstruction(state, IHR, IWin, s_surf->SurfaceWindow(IWin).WinCenter, U);
    7131        18720 :                 dl->ObTransM[IPH][ITH] = ObTrans;
    7132        18720 :                 dl->SkyObstructionMult[IPH][ITH] = 1.0;
    7133              :             }
    7134              : 
    7135              :             // SKY AND GROUND RADIATION ON WINDOW
    7136              : 
    7137              :             // Contribution is from sky if PH > 0 (ray goes upward), and from ground if PH < 0 (ray goes downward)
    7138              :             // (There may also be contributions from reflection from obstructions; see 'BEAM SOLAR AND SKY SOLAR
    7139              :             // REFLECTED FROM NEAREST OBSTRUCTION,' below.)
    7140              : 
    7141       330560 :             if (PH > 0.0) { // Contribution is from sky
    7142       826400 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7143       661120 :                     ZSK.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH, PH) * COSB * DA * dl->ObTransM[IPH][ITH];
    7144              :                 }
    7145              :             } else { // PH <= 0.0; contribution is from ground
    7146       165280 :                 if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
    7147              :                     // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
    7148              :                     // by the ray. This effect is given by the ratio SkyObstructionMult =
    7149              :                     // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
    7150              :                     // This ratio is calculated for an isotropic sky.
    7151              :                     // Ground point hit by the ray:
    7152            0 :                     Real64 Alfa = std::acos(-U.z);
    7153            0 :                     Real64 Beta = std::atan2(U.y, U.x);
    7154            0 :                     Real64 HorDis = (s_surf->SurfaceWindow(IWin).WinCenter.z - s_surf->GroundLevelZ) * std::tan(Alfa);
    7155            0 :                     groundHitPt.z = s_surf->GroundLevelZ;
    7156            0 :                     groundHitPt.x = s_surf->SurfaceWindow(IWin).WinCenter.x + HorDis * std::cos(Beta);
    7157            0 :                     groundHitPt.y = s_surf->SurfaceWindow(IWin).WinCenter.y + HorDis * std::sin(Beta);
    7158              : 
    7159            0 :                     dl->SkyObstructionMult[IPH][ITH] =
    7160            0 :                         CalcObstrMultiplier(state, groundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    7161              :                 } // End of check if solar reflection calc is in effect
    7162              : 
    7163       165280 :                 auto const &gilsk = dl->horIllum[IHR];
    7164       826400 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7165              :                     // Below, luminance of ground in cd/m2 is illuminance on ground in lumens/m2
    7166              :                     // times ground reflectance, divided by pi, times obstruction multiplier.
    7167      1322240 :                     ZSK.sky[iSky] = (gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
    7168       661120 :                                     dl->SkyObstructionMult[IPH][ITH];
    7169              :                 }
    7170              :                 // Determine if sun illuminates the point that ray hits the ground. If the solar reflection
    7171              :                 // calculation has been requested (CalcSolRefl = .TRUE.) shading by obstructions, including
    7172              :                 // the building itself, is considered in determining whether sun hits the ground point.
    7173              :                 // Otherwise this shading is ignored and the sun always hits the ground point.
    7174       165280 :                 SunObstructionMult = 1.0;
    7175       165280 :                 if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
    7176              :                     // Sun reaches ground point if vector from this point to the sun is unobstructed
    7177            0 :                     hitObs = false;
    7178            0 :                     for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    7179            0 :                         hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    7180            0 :                         if (hitObs) break;
    7181              :                     }
    7182            0 :                     if (hitObs) SunObstructionMult = 0.0;
    7183              :                 }
    7184       165280 :                 ZSU = (dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
    7185              :                       SunObstructionMult;
    7186              :             }
    7187              :             // BEAM SOLAR AND SKY SOLAR REFLECTED FROM NEAREST OBSTRUCTION
    7188              : 
    7189       330560 :             if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] < 1.0) {
    7190              :                 // Find obstruction whose hit point is closest to the center of the window
    7191            0 :                 DayltgClosestObstruction(state, s_surf->SurfaceWindow(IWin).WinCenter, U, NearestHitSurfNum, nearestHitPt);
    7192            0 :                 if (NearestHitSurfNum > 0) {
    7193              : 
    7194              :                     // Beam solar reflected from nearest obstruction.
    7195            0 :                     LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, IHR, U, NearestHitSurfNum, nearestHitPt);
    7196            0 :                     ZSUObsRefl = LumAtHitPtFrSun * COSB * DA;
    7197            0 :                     ZSU += ZSUObsRefl;
    7198              : 
    7199              :                     // Sky solar reflected from nearest obstruction.
    7200            0 :                     int const ObsConstrNum = s_surf->Surface(NearestHitSurfNum).Construction;
    7201            0 :                     if (ObsConstrNum > 0) {
    7202              :                         // Exterior building surface is nearest hit
    7203            0 :                         if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
    7204              :                             // Obstruction is not a window, i.e., is an opaque surface
    7205            0 :                             ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
    7206              :                         } else {
    7207              :                             // Obstruction is a window; assume it is bare
    7208            0 :                             ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
    7209              :                         }
    7210              :                     } else {
    7211              :                         // Shadowing surface is nearest hit
    7212            0 :                         if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
    7213              :                             // Skip daylighting shelves, whose reflection is separately calculated
    7214            0 :                             ObsVisRefl = 0.0;
    7215              :                         } else {
    7216            0 :                             ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
    7217            0 :                             if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
    7218            0 :                                 ObsVisRefl +=
    7219            0 :                                     s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
    7220            0 :                                     state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
    7221              :                             // Note in the above that ShadowSurfDiffuseVisRefl is the reflectance of opaque part of
    7222              :                             // shadowing surface times (1 - ShadowSurfGlazingFrac)
    7223              :                         }
    7224              :                     }
    7225            0 :                     NearestHitSurfNumX = NearestHitSurfNum;
    7226              :                     // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    7227              :                     // The following gets the correct side of a shadowing surface for reflection.
    7228            0 :                     if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
    7229            0 :                         if (dot(U, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
    7230              :                     }
    7231            0 :                     if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
    7232            0 :                         state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    7233            0 :                         SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    7234            0 :                                         state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
    7235              :                     } else {
    7236            0 :                         SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    7237            0 :                                         state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, IHR, NearestHitSurfNumX) / Constant::Pi;
    7238              :                     }
    7239            0 :                     dReflObsSky = SkyReflVisLum * COSB * DA;
    7240              : 
    7241            0 :                     auto const &gilsk = dl->horIllum[IHR];
    7242            0 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7243            0 :                         ZSK.sky[iSky] += gilsk.sky[iSky] * dReflObsSky;
    7244              :                     }
    7245              :                 }
    7246              :             } // End of check if exterior solar reflection calculation is active
    7247              : 
    7248              :             //  ===Bare window (no shade or blind; non-diffusing glass)===
    7249              : 
    7250              :             // Increment flux entering space and window luminance (cd/m2).
    7251              :             // FLCW--(I,J) = part of incoming flux (in lumens) that goes up to ceiling and upper part of walls.
    7252              :             // FLFW--(I,J) = part that goes down to floor and lower part of walls
    7253              : 
    7254       330560 :             if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7255              :                 // Unshaded visible transmittance of TDD for a single ray from sky/ground element
    7256            0 :                 TVISBR = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    7257              : 
    7258              :                 // Make all transmitted light diffuse for a TDD with a bare diffuser
    7259            0 :                 auto &wlumsk = dl->winLum(IHR)[iWinCover_Bare];
    7260            0 :                 auto &flfwsk = FLFW[iWinCover_Bare];
    7261            0 :                 auto &flcwsk = FLCW[iWinCover_Bare];
    7262              : 
    7263            0 :                 auto &tddFluxInc = dl->TDDFluxInc(IHR, PipeNum);
    7264            0 :                 auto &tddFluxTrans = dl->TDDFluxTrans(IHR, PipeNum);
    7265            0 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7266            0 :                     wlumsk.sky[iSky] += ZSK.sky[iSky] * TVISBR / Constant::Pi;
    7267            0 :                     flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * (1.0 - surfWin.fractionUpgoing);
    7268            0 :                     flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7269              : 
    7270              :                     // For later calculation of diffuse visible transmittance
    7271            0 :                     tddFluxInc.sky[iSky] += ZSK.sky[iSky];
    7272            0 :                     tddFluxTrans.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7273              : 
    7274              :                 } // for (iSky)
    7275              : 
    7276            0 :                 tddFluxInc.sky[(int)SkyType::Clear] += ZSU;
    7277            0 :                 tddFluxTrans.sky[(int)SkyType::Clear] += ZSU * TVISBR;
    7278              : 
    7279            0 :                 dl->winLum(IHR)[iWinCover_Bare].sun += ZSU * TVISBR / Constant::Pi;
    7280            0 :                 flfwsk.sun += ZSU * TVISBR * (1.0 - surfWin.fractionUpgoing);
    7281            0 :                 flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7282              : 
    7283              :             } else { // Bare window
    7284              :                 // Transmittance of bare window for this sky/ground element
    7285       330560 :                 TVISBR = Window::POLYF(COSB, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    7286              : 
    7287       330560 :                 if (InShelfSurf > 0) { // Inside daylighting shelf
    7288              :                     // Daylighting shelf simplification:  All light is diffuse
    7289              :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7290            0 :                     auto &flcwsk = FLCW[iWinCover_Bare];
    7291            0 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7292            0 :                         flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7293              :                     }
    7294            0 :                     flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7295              : 
    7296              :                 } else { // Normal window
    7297              : 
    7298              :                     // CR 7869  correct TVISBR if disk beam passes thru interior window
    7299       330560 :                     if (extWinType == ExtWinType::AdjZone) {
    7300              :                         // modify TVISBR by second window transmission
    7301              :                         // first determine if ray from point passes thru any interior window
    7302            0 :                         hitObs = false;
    7303            0 :                         for (int IntWinLoop = 1; IntWinLoop <= thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).NumOfIntWindows;
    7304              :                              ++IntWinLoop) {
    7305            0 :                             IntWinNum = thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).IntWinNum(IntWinLoop);
    7306            0 :                             auto const &surfIntWin = s_surf->SurfaceWindow(IntWinNum);
    7307            0 :                             hitObs = PierceSurface(state, IntWinNum, surfIntWin.WinCenter, SUNCOS_IHR, obsHitPt);
    7308            0 :                             if (hitObs) { // disk passes thru
    7309              :                                 // cosine of incidence angle of light from sky or ground element for
    7310            0 :                                 COSBintWin = SPH * std::sin(surfIntWin.phi) + CPH * std::cos(surfIntWin.phi) * std::cos(TH - surfIntWin.theta);
    7311            0 :                                 TVISBR *= Window::POLYF(COSBintWin,
    7312            0 :                                                         state.dataConstruction->Construct(s_surf->Surface(IntWinNum).Construction).TransVisBeamCoef);
    7313            0 :                                 break;
    7314              :                             }
    7315              :                         }
    7316            0 :                         if (!hitObs) { // blocked by opaque parts, beam does not actually pass thru interior window to reach zone
    7317            0 :                             TVISBR = 0.0;
    7318              :                         }
    7319              :                     }
    7320              : 
    7321       330560 :                     auto &flfwsk = FLFW[iWinCover_Bare];
    7322       330560 :                     auto &flcwsk = FLCW[iWinCover_Bare];
    7323      1652800 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7324              :                         // IF (PH < 0.0d0) THEN
    7325              :                         // Fixed by FCW, Nov. 2003:
    7326      1322240 :                         if (PH > 0.0) {
    7327       661120 :                             flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7328              :                         } else {
    7329       661120 :                             flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7330              :                         }
    7331              :                     } // for (iSky)
    7332              : 
    7333       330560 :                     if (PH > 0.0) {
    7334       165280 :                         flfwsk.sun += ZSU * TVISBR;
    7335              :                     } else {
    7336       165280 :                         flcwsk.sun += ZSU * TVISBR;
    7337              :                     }
    7338              : 
    7339              :                 } // End of check if window with daylighting shelf or normal window
    7340              :             }     // End of check if TDD:DOME or bare window
    7341              : 
    7342              :             // Check if window has shade or blind
    7343       330560 :             ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    7344       330560 :             if (s_surf->Surface(IWin).HasShadeControl) {
    7345            0 :                 ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
    7346            0 :                 ShadeOn = ANY_SHADE(ShType);
    7347            0 :                 BlindOn = ANY_BLIND(ShType);
    7348            0 :                 ScreenOn = (ShType == WinShadingType::ExtScreen);
    7349              :             }
    7350              : 
    7351       330560 :             if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
    7352              : 
    7353              :                 // ===Window with interior or exterior shade or blind, exterior screen, or with diffusing glass===
    7354              : 
    7355              :                 // Increment flux entering space and window luminance. Shades and diffusing glass are
    7356              :                 // assumed to be perfect diffusers, i.e., the transmittance is independent of angle of
    7357              :                 // incidence and the transmitted light is isotropic. The transmittance of a blind is
    7358              :                 // assumed to depend on profile angle and slat angle; the diffuse light entering the room from
    7359              :                 // the slats of the blind is assumed to be isotropic. With blinds, light can also enter
    7360              :                 // the room by passing between the slats without reflection. The beam transmittance of a screen
    7361              :                 // is assumed to depend on sun azimuth and azimuth angle.
    7362              : 
    7363              :                 // For light from a shade, or from diffusing glass, or from the slats of a blind, a flux fraction,
    7364              :                 // SurfaceWindow(IWin)%FractionUpgoing (determined by window tilt), goes up toward
    7365              :                 // ceiling and upper part of walls, and 1-Surfacewindow(iwin)%FractionUpgoing
    7366              :                 // goes down toward floor and lower part of walls. For a blind, the light passing
    7367              :                 // between the slats goes either up or down depending on the altitude angle of the
    7368              :                 // element from which the light came. For a screen, the light passing
    7369              :                 // between the screen's cylinders goes either up or down depending on the altitude angle of the
    7370              :                 // element from which the light came.
    7371              : 
    7372            0 :                 int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
    7373            0 :                 if (s_surf->SurfWinSolarDiffusing(IWin)) IConstShaded = s_surf->Surface(IWin).Construction;
    7374              : 
    7375              :                 // Transmittance of window including shade, screen or blind
    7376            0 :                 Real64 transBmBmMult = 0.0;
    7377            0 :                 Real64 transMult = 0.0;
    7378              : 
    7379            0 :                 if (ShadeOn) { // Shade
    7380            0 :                     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7381              :                         // Shaded visible transmittance of TDD for a single ray from sky/ground element
    7382            0 :                         transMult = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    7383              :                     } else { // Shade only, no TDD
    7384              :                         // Calculate transmittance of the combined window and shading device for this sky/ground element
    7385            0 :                         transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7386            0 :                                     surfWin.lightWellEff;
    7387              :                     }
    7388              : 
    7389            0 :                 } else if (ScreenOn) { // Screen: get beam-beam, beam-diffuse and diffuse-diffuse vis trans/ref of screen and glazing system
    7390            0 :                     auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
    7391            0 :                     assert(screen != nullptr);
    7392              : 
    7393            0 :                     Real64 phi = std::abs(PH - surfWin.phi);
    7394            0 :                     Real64 theta = std::abs(TH - surfWin.theta);
    7395              :                     int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
    7396              :                     BilinearInterpCoeffs coeffs;
    7397              : 
    7398            0 :                     Material::NormalizePhiTheta(phi, theta);
    7399            0 :                     Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    7400            0 :                     GetBilinearInterpCoeffs(phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    7401              : 
    7402            0 :                     ReflGlDiffDiffFront = state.dataConstruction->Construct(IConst).ReflectVisDiffFront;
    7403            0 :                     ReflScDiffDiffBack = screen->DfRefVis;
    7404              : 
    7405            0 :                     auto const &b11 = screen->btars[ip1][it1];
    7406            0 :                     auto const &b12 = screen->btars[ip1][it2];
    7407            0 :                     auto const &b21 = screen->btars[ip2][it1];
    7408            0 :                     auto const &b22 = screen->btars[ip2][it2];
    7409              : 
    7410            0 :                     TransScBmDiffFront = BilinearInterp(b11.DfTransVis, b12.DfTransVis, b21.DfTransVis, b22.DfTransVis, coeffs);
    7411              : 
    7412            0 :                     transMult = TransScBmDiffFront * surfWin.glazedFrac * state.dataConstruction->Construct(IConst).TransDiffVis /
    7413            0 :                                 (1 - ReflGlDiffDiffFront * ReflScDiffDiffBack) * surfWin.lightWellEff;
    7414              : 
    7415            0 :                     transBmBmMult = BilinearInterp(b11.BmTransVis, b12.BmTransVis, b21.BmTransVis, b22.BmTransVis, coeffs);
    7416              : 
    7417            0 :                 } else if (BlindOn) { // Blind: get beam-diffuse and beam-beam vis trans of blind+glazing system
    7418              :                     // PETER:  As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
    7419              :                     //         for TDDs because it is based on TVISBR which is correctly calculated for TDDs above.
    7420            0 :                     auto const &surfShade = s_surf->surfShades(IWin);
    7421            0 :                     auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    7422            0 :                     assert(matBlind != nullptr);
    7423            0 :                     Real64 ProfAng = ProfileAngle(state, IWin, U, matBlind->SlatOrientation);
    7424              : 
    7425            0 :                     auto &btar = surfShade.blind.TAR;
    7426            0 :                     int idxLo = surfShade.blind.profAngIdxLo;
    7427            0 :                     int idxHi = std::min(Material::MaxProfAngs, idxLo + 1);
    7428            0 :                     Real64 interpFac = surfShade.blind.profAngInterpFac;
    7429            0 :                     TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7430              : 
    7431            0 :                     if (ShType == WinShadingType::IntBlind) { // Interior blind
    7432            0 :                         ReflGlDiffDiffBack = construct.ReflectVisDiffBack;
    7433            0 :                         ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7434            0 :                         ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
    7435            0 :                         TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
    7436            0 :                         transMult = TVISBR * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7437            0 :                                                                        (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7438              : 
    7439            0 :                     } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7440            0 :                         ReflGlDiffDiffFront = construct.ReflectVisDiffFront;
    7441            0 :                         ReflBlDiffDiffBack = btar.Vis.Bk.Df.Ref;
    7442            0 :                         transMult = TransBlBmDiffFront * surfWin.glazedFrac * construct.TransDiffVis /
    7443            0 :                                     (1.0 - ReflGlDiffDiffFront * ReflBlDiffDiffBack) * surfWin.lightWellEff;
    7444              : 
    7445              :                     } else { // Between-glass blind
    7446            0 :                         Real64 t1 = Window::POLYF(COSB, construct.tBareVisCoef(1));
    7447            0 :                         td2 = construct.tBareVisDiff(2);
    7448            0 :                         rbd1 = construct.rbBareVisDiff(1);
    7449            0 :                         rfd2 = construct.rfBareVisDiff(2);
    7450            0 :                         Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7451            0 :                         tfshd = btar.Vis.Ft.Df.Tra;
    7452            0 :                         Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7453            0 :                         rbshd = btar.Vis.Ft.Df.Ref;
    7454            0 :                         if (construct.TotGlassLayers == 2) { // 2 glass layers
    7455            0 :                             transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7456              :                         } else { // 3 glass layers; blind between layers 2 and 3
    7457            0 :                             Real64 t2 = Window::POLYF(COSB, construct.tBareVisCoef(2));
    7458            0 :                             td3 = construct.tBareVisDiff(3);
    7459            0 :                             rfd3 = construct.rfBareVisDiff(3);
    7460            0 :                             rbd2 = construct.rbBareVisDiff(2);
    7461            0 :                             transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7462            0 :                                         surfWin.lightWellEff;
    7463              :                         }
    7464              :                     }
    7465              : 
    7466            0 :                     transBmBmMult = TVISBR * matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
    7467              :                 } else { // Diffusing glass
    7468            0 :                     transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7469            0 :                                 surfWin.lightWellEff;
    7470              :                 } // End of check if shade, blind or diffusing glass
    7471              : 
    7472            0 :                 if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7473              :                     // No beam is transmitted.  This takes care of all types of screens and blinds.
    7474            0 :                     transBmBmMult = 0.0;
    7475              :                 }
    7476              : 
    7477              :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7478            0 :                 if (InShelfSurf > 0) { // Inside daylighting shelf
    7479            0 :                     transBmBmMult = 0.0;
    7480              :                 }
    7481              : 
    7482              :                 // DayltgInterReflectedIllumTransBmBmMult is used in the following for windows with blinds or screens to get contribution from light
    7483              :                 // passing directly between slats or between screen material without reflection.
    7484              : 
    7485            0 :                 auto &wlumsk = dl->winLum(IHR)[iWinCover_Shaded];
    7486            0 :                 auto &flfwsk = FLFW[iWinCover_Shaded];
    7487            0 :                 auto &flcwsk = FLCW[iWinCover_Shaded];
    7488              : 
    7489            0 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7490              :                     // Should these be bare or shaded?
    7491            0 :                     wlumsk.sky[iSky] += ZSK.sky[iSky] * transMult / Constant::Pi;
    7492            0 :                     flfwsk.sky[iSky] += ZSK.sky[iSky] * transMult * (1.0 - surfWin.fractionUpgoing);
    7493            0 :                     flcwsk.sky[iSky] += ZSK.sky[iSky] * transMult * surfWin.fractionUpgoing;
    7494              : 
    7495            0 :                     if (BlindOn || ScreenOn) {
    7496            0 :                         if (PH > 0.0) {
    7497            0 :                             flfwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
    7498              :                         } else {
    7499            0 :                             flcwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
    7500              :                         }
    7501              :                     }
    7502              :                 }
    7503              : 
    7504            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU * transMult / Constant::Pi;
    7505            0 :                 flfwsk.sun += ZSU * transMult * (1.0 - surfWin.fractionUpgoing);
    7506            0 :                 flcwsk.sun += ZSU * transMult * surfWin.fractionUpgoing;
    7507            0 :                 if (BlindOn || ScreenOn) {
    7508            0 :                     if (PH > 0.0) {
    7509            0 :                         flfwsk.sun += ZSU * transBmBmMult;
    7510              :                     } else {
    7511            0 :                         flcwsk.sun += ZSU * transBmBmMult;
    7512              :                     }
    7513              :                 }
    7514              :             } // End of window with shade, screen, blind or diffusing glass
    7515              : 
    7516              :         } // End of azimuth integration loop, ITH
    7517              :     }     // End of altitude integration loop, IPH
    7518              : 
    7519         2066 :     if (OutShelfSurf > 0) { // Outside daylighting shelf
    7520              :         // Add exterior diffuse illuminance due to outside shelf
    7521              :         // Since all of the illuminance is added to the zone as upgoing diffuse, it can be added as a lump sum here
    7522              : 
    7523            0 :         TVISBR = construct.TransDiffVis; // Assume diffuse transmittance for shelf illuminance
    7524              : 
    7525            0 :         auto const &gilsk = dl->horIllum[IHR];
    7526            0 :         auto &flcwsk = FLCW[iWinCover_Bare];
    7527            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7528              :             // This is only an estimate because the anisotropic sky view of the shelf is not yet taken into account.
    7529              :             // SurfAnisoSkyMult would be great to use but it is not available until the heat balance starts up.
    7530            0 :             ZSK.sky[iSky] = gilsk.sky[iSky] * 1.0 * state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis *
    7531            0 :                             state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
    7532              : 
    7533              :             // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7534            0 :             flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7535              :         } // ISKY
    7536              : 
    7537            0 :         ZSU = dl->horIllum[IHR].sun * state.dataHeatBal->SurfSunlitFracHR(IHR, OutShelfSurf) *
    7538            0 :               state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis * state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
    7539            0 :         flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7540              :     }
    7541              : 
    7542              :     // Sky-related portion of internally reflected illuminance.
    7543              :     // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea, and ZoneDaylight(ZoneNum)%aveVisDiffReflect,
    7544              :     // were calculated in subr DayltgAveInteriorReflectance.
    7545              : 
    7546         6198 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    7547         4132 :         auto &eintsk = dl->reflIllum(IHR)[iWinCover];
    7548         4132 :         auto const &flfwsk = FLFW[iWinCover];
    7549         4132 :         auto const &flcwsk = FLCW[iWinCover];
    7550              : 
    7551        20660 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7552              :             // Full area of window is used in following since effect of dividers on reducing
    7553              :             // effective window transmittance has already been accounted for in calc of FLFWSK and FLCWSK.
    7554        33056 :             eintsk.sky[iSky] = (flfwsk.sky[iSky] * surfWin.rhoFloorWall + flcwsk.sky[iSky] * surfWin.rhoCeilingWall) *
    7555        16528 :                                (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - dl->enclDaylight(enclNum).aveVisDiffReflect));
    7556              :         } // for (iSky)
    7557              :     }     // for (iWinCover)
    7558              : 
    7559              :     // BEAM SOLAR RADIATION ON WINDOW
    7560              : 
    7561              :     // Beam reaching window directly (without specular reflection from exterior obstructions)
    7562              : 
    7563         2066 :     if (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0) {
    7564              :         // Cos of angle of incidence
    7565         1454 :         COSBSun = dl->sunAngles.sinPhi * std::sin(surfWin.phi) +
    7566         1454 :                   dl->sunAngles.cosPhi * std::cos(surfWin.phi) * std::cos(dl->sunAngles.theta - surfWin.theta);
    7567              : 
    7568         1454 :         if (COSBSun > 0.0) {
    7569              :             // Multiply direct normal illuminance (normalized to 1.0 lux)
    7570              :             // by incident angle factor and by fraction of window that is sunlit.
    7571              :             // Note that in the following SurfSunlitFracHR accounts for possibly non-zero transmittance of
    7572              :             // shading surfaces.
    7573              : 
    7574         1454 :             ZSU1 = COSBSun * state.dataHeatBal->SurfSunlitFracHR(IHR, IWin);
    7575              : 
    7576              :             // Contribution to window luminance and downgoing flux
    7577              : 
    7578              :             // -- Bare window
    7579              : 
    7580         1454 :             if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7581              :                 // Unshaded visible transmittance of TDD for collimated beam from the sun
    7582            0 :                 TVISBSun = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
    7583            0 :                 dl->TDDTransVisBeam(IHR, PipeNum) = TVISBSun;
    7584              : 
    7585            0 :                 FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
    7586              : 
    7587            0 :                 dl->winLum(IHR)[iWinCover_Bare].sun += ZSU1 * TVISBSun / Constant::Pi;
    7588            0 :                 FLFW[iWinCover_Bare].sun += ZSU1 * TVISBSun * (1.0 - surfWin.fractionUpgoing);
    7589            0 :                 FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
    7590              : 
    7591              :             } else { // Bare window
    7592         1454 :                 TVISBSun = Window::POLYF(COSBSun, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    7593              : 
    7594              :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7595         1454 :                 if (InShelfSurf > 0) {                  // Inside daylighting shelf
    7596            0 :                     FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
    7597              : 
    7598              :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7599              :                     // WLUMSU(1,IHR) = WLUMSU(1,IHR) + ZSU1 * TVISBSun / PI
    7600              :                     // FLFWSU(1) = FLFWSU(1) + ZSU1 * TVISBSun * (1.0 - SurfaceWindow(IWin)%FractionUpgoing)
    7601            0 :                     FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
    7602              :                 } else { // Normal window
    7603         1454 :                     FLFW[iWinCover_Bare].sunDisk = ZSU1 * TVISBSun;
    7604              :                 }
    7605              :             }
    7606              : 
    7607              :             // -- Window with shade, screen, blind or diffusing glass
    7608         1454 :             if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
    7609            0 :                 Real64 transBmBmMult = 0.0;
    7610            0 :                 Real64 transMult = 0.0;
    7611              : 
    7612            0 :                 if (ShadeOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade or screen on or diffusing glass
    7613            0 :                     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7614              :                         // Shaded visible transmittance of TDD for collimated beam from the sun
    7615            0 :                         transMult = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
    7616              : 
    7617            0 :                     } else if (ScreenOn) {
    7618            0 :                         auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
    7619            0 :                         assert(screen != nullptr);
    7620            0 :                         Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    7621            0 :                         Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    7622              :                         int ip1, ip2, it1, it2;
    7623              :                         BilinearInterpCoeffs coeffs;
    7624            0 :                         Material::NormalizePhiTheta(phi, theta);
    7625            0 :                         Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    7626            0 :                         GetBilinearInterpCoeffs(
    7627            0 :                             phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    7628            0 :                         Real64 BmBmTransVis = BilinearInterp(screen->btars[ip1][it1].BmTransVis,
    7629            0 :                                                              screen->btars[ip1][it2].BmTransVis,
    7630            0 :                                                              screen->btars[ip2][it1].BmTransVis,
    7631            0 :                                                              screen->btars[ip2][it2].BmTransVis,
    7632              :                                                              coeffs);
    7633              : 
    7634            0 :                         transMult = BmBmTransVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7635              :                     } else {
    7636            0 :                         int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
    7637            0 :                         if (s_surf->SurfWinSolarDiffusing(IWin)) IConstShaded = surf.Construction;
    7638            0 :                         transMult = Window::POLYF(COSBSun, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7639            0 :                                     surfWin.lightWellEff;
    7640              :                     }
    7641              : 
    7642              :                 } else { // Blind on
    7643            0 :                     auto const &surfShade = s_surf->surfShades(IWin);
    7644              :                     // As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
    7645              :                     // for TDDs because it is based on TVISBSun which is correctly calculated for TDDs above.
    7646            0 :                     auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    7647            0 :                     assert(matBlind != nullptr);
    7648              : 
    7649              :                     // These are "cached" in the surfShade struct
    7650            0 :                     auto &btar = surfShade.blind.TAR;
    7651            0 :                     int idxLo = surfShade.blind.profAngIdxLo;
    7652            0 :                     int idxHi = surfShade.blind.profAngIdxHi;
    7653            0 :                     int interpFac = surfShade.blind.profAngInterpFac;
    7654            0 :                     TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7655              : 
    7656            0 :                     if (ShType == WinShadingType::IntBlind) { // Interior blind
    7657              :                         // TH CR 8121, 7/7/2010
    7658              :                         // ReflBlBmDiffFront = WindowManager::InterpProfAng(ProfAng,Blind(BlNum)%VisFrontBeamDiffRefl)
    7659            0 :                         ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7660              : 
    7661              :                         // TH added 7/12/2010 for CR 8121
    7662            0 :                         ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
    7663            0 :                         TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
    7664              : 
    7665            0 :                         transMult = TVISBSun * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7666            0 :                                                                          (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7667              : 
    7668            0 :                     } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7669            0 :                         transMult = TransBlBmDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
    7670            0 :                                     surfWin.glazedFrac * surfWin.lightWellEff;
    7671              : 
    7672              :                     } else { // Between-glass blind
    7673            0 :                         Real64 t1 = Window::POLYF(COSBSun, construct.tBareVisCoef(1));
    7674            0 :                         Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7675            0 :                         Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7676            0 :                         if (construct.TotGlassLayers == 2) { // 2 glass layers
    7677            0 :                             transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7678              :                         } else { // 3 glass layers; blind between layers 2 and 3
    7679            0 :                             Real64 t2 = Window::POLYF(COSBSun, construct.tBareVisCoef(2));
    7680            0 :                             transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7681            0 :                                         surfWin.lightWellEff;
    7682              :                         }
    7683              :                     }
    7684              : 
    7685            0 :                     transBmBmMult = TVISBSun * matBlind->BeamBeamTrans(surfShade.blind.profAng, surfShade.blind.slatAng);
    7686              :                 } // ShadeOn/ScreenOn/BlindOn/Diffusing glass
    7687              : 
    7688            0 :                 if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7689            0 :                     transBmBmMult = 0.0; // No beam, diffuse only
    7690              :                 }
    7691              : 
    7692              :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7693            0 :                 if (InShelfSurf > 0) {   // Inside daylighting shelf
    7694            0 :                     transBmBmMult = 0.0; // No beam, diffuse only
    7695              :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7696              :                 }
    7697              : 
    7698            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1 * transMult / Constant::Pi;
    7699            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult / Constant::Pi;
    7700            0 :                 FLFW[iWinCover_Shaded].sun += ZSU1 * transMult * (1.0 - surfWin.fractionUpgoing);
    7701            0 :                 FLFW[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult;
    7702            0 :                 FLCW[iWinCover_Shaded].sun += ZSU1 * transMult * surfWin.fractionUpgoing;
    7703              :             } // if (BlindOn || ShadeOn)
    7704              :         }     // if (COSBSun > 0)
    7705              :     }         // if (SurfSunlitFracHR > 0)
    7706              : 
    7707              :     // Beam reaching window after specular reflection from exterior obstruction
    7708              : 
    7709              :     // In the following, Beam normal illuminance times ZSU1refl = illuminance on window due to
    7710              :     // specular reflection from exterior surfaces
    7711              : 
    7712         2066 :     if (s_surf->CalcSolRefl && s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Dome) {
    7713              : 
    7714            0 :         ZSU1refl = s_surf->SurfReflFacBmToBmSolObs(IHR, IWin);
    7715              : 
    7716            0 :         if (ZSU1refl > 0.0) {
    7717              :             // Contribution to window luminance and downgoing flux
    7718              : 
    7719              :             // -- Bare window. We use diffuse-diffuse transmittance here rather than beam-beam to avoid
    7720              :             //    complications due to specular reflection from multiple exterior surfaces
    7721              : 
    7722            0 :             TVisSunRefl = construct.TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7723              :             // In the following it is assumed that all reflected beam is going downward, as it would be in the
    7724              :             // important case of reflection from a highly glazed facade of a neighboring building. However, in
    7725              :             // rare cases (such as upward specular reflection from a flat horizontal skylight) it may
    7726              :             // actually be going upward.
    7727            0 :             FLFW[iWinCover_Bare].sunDisk += ZSU1refl * TVisSunRefl;
    7728              : 
    7729              :             // -- Window with shade, blind or diffusing glass
    7730              : 
    7731            0 :             if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
    7732            0 :                 Real64 transMult = 0.0;
    7733              : 
    7734            0 :                 if (ShadeOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade on or diffusing glass
    7735            0 :                     int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
    7736            0 :                     if (s_surf->SurfWinSolarDiffusing(IWin)) IConstShaded = s_surf->Surface(IWin).Construction;
    7737            0 :                     transMult = state.dataConstruction->Construct(IConstShaded).TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7738              : 
    7739            0 :                 } else if (ScreenOn) { // Exterior screen on
    7740            0 :                     auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
    7741            0 :                     assert(screen != nullptr);
    7742            0 :                     Real64 TransScDiffDiffFront = screen->DfTransVis;
    7743              : 
    7744            0 :                     transMult = TransScDiffDiffFront *
    7745            0 :                                 (state.dataConstruction->Construct(IConst).TransDiffVis / (1.0 - ReflGlDiffDiffFront * ReflScDiffDiffBack)) *
    7746            0 :                                 surfWin.glazedFrac * surfWin.lightWellEff;
    7747              : 
    7748              :                 } else { // Blind on
    7749              : 
    7750            0 :                     auto const &surfShade = s_surf->surfShades(IWin);
    7751            0 :                     auto const &btar = surfShade.blind.TAR;
    7752            0 :                     auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    7753              : 
    7754            0 :                     assert(matBlind != nullptr);
    7755              : 
    7756            0 :                     TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
    7757            0 :                     if (ShType == WinShadingType::IntBlind) { // Interior blind
    7758            0 :                         ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
    7759            0 :                         transMult = TVisSunRefl * (TransBlDiffDiffFront + ReflBlDiffDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7760            0 :                                                                               (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7761              : 
    7762            0 :                     } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7763            0 :                         transMult = TransBlDiffDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
    7764            0 :                                     surfWin.glazedFrac * surfWin.lightWellEff;
    7765              : 
    7766              :                     } else { // Between-glass blind
    7767            0 :                         Real64 t1 = construct.tBareVisDiff(1);
    7768            0 :                         Real64 tfshBd = btar.Vis.Ft.Df.Tra;
    7769            0 :                         Real64 rfshB = btar.Vis.Ft.Df.Ref;
    7770            0 :                         if (construct.TotGlassLayers == 2) { // 2 glass layers
    7771            0 :                             transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7772              :                         } else { // 3 glass layers; blind between layers 2 and 3
    7773            0 :                             Real64 t2 = construct.tBareVisDiff(2);
    7774            0 :                             transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7775            0 :                                         surfWin.lightWellEff;
    7776              :                         }
    7777              :                     } // End of check of interior/exterior/between-glass blind
    7778              :                 }     // if (Blind)
    7779              : 
    7780            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1refl * transMult / Constant::Pi;
    7781            0 :                 FLFW[iWinCover_Shaded].sun += ZSU1refl * transMult * (1.0 - surfWin.fractionUpgoing);
    7782            0 :                 FLCW[iWinCover_Shaded].sun += ZSU1refl * transMult * surfWin.fractionUpgoing;
    7783              :             } // End of check if window has shade, blind or diffusing glass
    7784              :         }     // End of check if ZSU1refl > 0.0
    7785              :     }         // End of check if solar reflections are in effect
    7786              : 
    7787              :     // Sun-related portion of internally reflected illuminance
    7788              : 
    7789              :     // Full area of window is used in following since effect of dividers on reducing
    7790              :     // effective window transmittance already accounted for in calc of FLFWSU and FLCWSU
    7791              :     // CR 7869 added effect of intervening interior windows on transmittance and
    7792              :     // added inside surface area of adjacent zone
    7793         6198 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    7794         8264 :         dl->reflIllum(IHR)[iWinCover].sun = (FLFW[iWinCover].sun * surfWin.rhoFloorWall + FLCW[iWinCover].sun * surfWin.rhoCeilingWall) *
    7795         4132 :                                             (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    7796              : 
    7797         8264 :         dl->reflIllum(IHR)[iWinCover].sunDisk = FLFW[iWinCover].sunDisk * surfWin.rhoFloorWall * (surf.Area / surfWin.glazedFrac) /
    7798         4132 :                                                 (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    7799              :     }
    7800         2066 : } // DayltgInterReflectedIllum()
    7801              : 
    7802            0 : void ComplexFenestrationLuminances(EnergyPlusData &state,
    7803              :                                    int const IWin,
    7804              :                                    int const WinEl,
    7805              :                                    int const NBasis,
    7806              :                                    int const IHR,
    7807              :                                    int const iRefPoint,
    7808              :                                    Array1D<Illums> &ElementLuminance, // luminance at window element (exterior side)
    7809              :                                    CalledFor const CalledFrom,
    7810              :                                    int const MapNum)
    7811              : {
    7812              : 
    7813              :     // SUBROUTINE INFORMATION:
    7814              :     //       AUTHOR         Simon Vidanovic
    7815              :     //       DATE WRITTEN   June 2013
    7816              : 
    7817            0 :     Vector3<Real64> obsHitPt;    // Coordinates of hit point on an obstruction (m)
    7818            0 :     Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
    7819              : 
    7820            0 :     auto &dl = state.dataDayltg;
    7821            0 :     auto &s_surf = state.dataSurface;
    7822              : 
    7823            0 :     int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
    7824            0 :     auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
    7825              :     // Calculate luminance from sky and sun excluding exterior obstruction transmittances and obstruction multipliers
    7826            0 :     int SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
    7827            0 :     for (int iIncElem = 1; iIncElem <= NBasis; ++iIncElem) {
    7828            0 :         Real64 LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
    7829              :         // COSB = ComplexWind(IWin)%Geom(CurCplxFenState)%CosInc(iIncElem)
    7830              :         // DA = ComplexWind(IWin)%Geom(CurCplxFenState)%DAInc(iIncElem)
    7831            0 :         Real64 Altitude = complexWinGeom.pInc(iIncElem).Altitude;
    7832            0 :         Real64 Azimuth = complexWinGeom.pInc(iIncElem).Azimuth;
    7833            0 :         auto &elemLum = ElementLuminance(iIncElem);
    7834            0 :         auto const &gilsk = dl->horIllum[IHR];
    7835              : 
    7836            0 :         if (Altitude > 0.0) {
    7837              :             // Ray from sky element
    7838            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7839            0 :                 elemLum.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc;
    7840              :             }
    7841            0 :         } else if (Altitude < 0.0) {
    7842              :             // Ray from ground element
    7843              :             // BeamObstrMultiplier = ComplexWind(IWin)%DaylghtGeom(CurCplxFenState)%GndObstrMultiplier(WinEl, iIncElem)
    7844            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7845            0 :                 elemLum.sky[iSky] = gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7846              :             }
    7847            0 :             elemLum.sun = dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7848              :         } else {
    7849              :             // Ray from the element which is half sky and half ground
    7850            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7851              :                 // in this case half of the pach is coming from the sky and half from the ground
    7852            0 :                 elemLum.sky[iSky] = 0.5 * DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc +
    7853            0 :                                     0.5 * gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7854              :             }
    7855            0 :             elemLum.sun = 0.5 * dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    7856              :         }
    7857              :         // Sun beam calculations
    7858            0 :         if ((SolBmIndex == iIncElem) && (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0)) {
    7859            0 :             elemLum.sunDisk = 1.0;
    7860              :         }
    7861              :     }
    7862              : 
    7863            0 :     auto const &complexWinDaylightGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CurCplxFenState);
    7864              : 
    7865            0 :     if (CalledFrom == CalledFor::RefPoint) {
    7866            0 :         auto const &complexWinRefPoint = complexWinDaylightGeom.RefPoint(iRefPoint);
    7867              :         // add exterior obstructions transmittances to calculated luminances
    7868            0 :         for (int iReflElem = 1; iReflElem <= complexWinRefPoint.NReflSurf(WinEl); ++iReflElem) {
    7869            0 :             Real64 ObstrTrans = complexWinRefPoint.TransOutSurf(iReflElem, WinEl);
    7870            0 :             int iReflElemIndex = complexWinRefPoint.RefSurfIndex(iReflElem, WinEl);
    7871              : 
    7872            0 :             auto &elemLum = ElementLuminance(iReflElemIndex);
    7873            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7874            0 :                 elemLum.sky[iSky] *= ObstrTrans;
    7875              :             }
    7876            0 :             elemLum.sun *= ObstrTrans;
    7877            0 :             elemLum.sunDisk *= ObstrTrans;
    7878              :         }
    7879              : 
    7880              :         // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
    7881              :         // sun reaches the ground for that point
    7882            0 :         Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
    7883            0 :         for (int iGndElem = 1; iGndElem <= complexWinRefPoint.NGnd(WinEl); ++iGndElem) {
    7884              :             // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
    7885              :             // were hit in the process
    7886              : 
    7887            0 :             Real64 BeamObstrMultiplier = complexWinRefPoint.GndObstrMultiplier(iGndElem, WinEl);
    7888            0 :             int iGndElemIndex = complexWinRefPoint.GndIndex(iGndElem, WinEl);
    7889              : 
    7890            0 :             auto &elemLum = ElementLuminance(iGndElemIndex);
    7891            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7892            0 :                 elemLum.sky[iSky] *= BeamObstrMultiplier;
    7893              :             }
    7894              : 
    7895              :             // direct sun disk reflect off the ground
    7896            0 :             Real64 SunObstrMultiplier = 1.0;
    7897            0 :             if (s_surf->CalcSolRefl) {
    7898              :                 // Sun reaches ground point if vector from this point to the sun is unobstructed
    7899            0 :                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    7900            0 :                     groundHitPt = complexWinRefPoint.GndPt(iGndElem, WinEl);
    7901            0 :                     bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    7902            0 :                     if (hitObs) {
    7903            0 :                         SunObstrMultiplier = 0.0;
    7904            0 :                         break;
    7905              :                     }
    7906              :                 }
    7907              :             }
    7908            0 :             elemLum.sun *= SunObstrMultiplier;
    7909              :         }
    7910              : 
    7911            0 :     } else { // if (CalledFrom != RefPoint)
    7912              : 
    7913            0 :         auto const &complexWinIllumMap = complexWinDaylightGeom.IlluminanceMap(iRefPoint, MapNum);
    7914              :         // add exterior obstructions transmittances to calculated luminances
    7915            0 :         for (int iReflElem = 1; iReflElem <= complexWinIllumMap.NReflSurf(WinEl); ++iReflElem) {
    7916            0 :             Real64 ObstrTrans = complexWinIllumMap.TransOutSurf(iReflElem, WinEl);
    7917            0 :             int iReflElemIndex = complexWinIllumMap.RefSurfIndex(iReflElem, WinEl);
    7918            0 :             auto &elemLum = ElementLuminance(iReflElemIndex);
    7919              : 
    7920            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7921            0 :                 elemLum.sky[iSky] *= ObstrTrans;
    7922              :             }
    7923            0 :             elemLum.sun *= ObstrTrans;
    7924            0 :             elemLum.sunDisk *= ObstrTrans;
    7925              :         }
    7926              : 
    7927              :         // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
    7928              :         // sun reaches the ground for that point
    7929            0 :         Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
    7930            0 :         for (int iGndElem = 1; iGndElem <= complexWinIllumMap.NGnd(WinEl); ++iGndElem) {
    7931              :             // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
    7932              :             // were hit in the process
    7933            0 :             Real64 BeamObstrMultiplier = complexWinIllumMap.GndObstrMultiplier(iGndElem, WinEl);
    7934            0 :             int iGndElemIndex = complexWinIllumMap.GndIndex(iGndElem, WinEl);
    7935              : 
    7936            0 :             auto &elemLum = ElementLuminance(iGndElemIndex);
    7937            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7938            0 :                 elemLum.sky[iSky] *= BeamObstrMultiplier;
    7939              :             }
    7940              : 
    7941              :             // direct sun disk reflect off the ground
    7942            0 :             Real64 SunObstrMultiplier = 1.0;
    7943            0 :             if (s_surf->CalcSolRefl) {
    7944              :                 // Sun reaches ground point if vector from this point to the sun is unobstructed
    7945            0 :                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    7946            0 :                     groundHitPt = complexWinIllumMap.GndPt(iGndElem, WinEl);
    7947              : 
    7948            0 :                     bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    7949            0 :                     if (hitObs) {
    7950            0 :                         SunObstrMultiplier = 0.0;
    7951            0 :                         break;
    7952              :                     }
    7953              :                 }
    7954              :             }
    7955            0 :             elemLum.sun *= SunObstrMultiplier;
    7956              :         }
    7957            0 :     } // if (CalledFrom == RefPoint)
    7958            0 : } // ComplexFenestrationLuminances()
    7959              : 
    7960            0 : void DayltgInterReflectedIllumComplexFenestration(EnergyPlusData &state,
    7961              :                                                   int const IWin,            // Window index
    7962              :                                                   int const WinEl,           // Current window element counter
    7963              :                                                   int const IHR,             // Hour of day
    7964              :                                                   int const daylightCtrlNum, // Daylighting control number
    7965              :                                                   int const iRefPoint,       // reference point counter
    7966              :                                                   CalledFor const CalledFrom,
    7967              :                                                   int const MapNum)
    7968              : {
    7969              : 
    7970              :     // SUBROUTINE INFORMATION:
    7971              :     //       AUTHOR         Simon Vidanovic
    7972              :     //       DATE WRITTEN   April 2013
    7973              : 
    7974              :     // PURPOSE OF THIS SUBROUTINE:
    7975              :     // Called from CalcDayltgCoefficients for each complex (bsdf) fenestration and reference point in a daylit
    7976              :     // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
    7977              :     // to internally reflected light by integrating to determine the amount of flux from
    7978              :     // sky and ground (and beam reflected from obstructions) transmitted through
    7979              :     // the center of the window and then reflecting this
    7980              :     // light from the inside surfaces of the space.
    7981              : 
    7982            0 :     auto &dl = state.dataDayltg;
    7983            0 :     auto &s_surf = state.dataSurface;
    7984              : 
    7985            0 :     Array1D<Illums> FL; // Sky related luminous flux
    7986              :     // Array1D<Real64> FLSU;     // Sun related luminous flux, excluding entering beam
    7987              :     // Array1D<Real64> FLSUdisk; // Sun related luminous flux, due to entering beam
    7988              : 
    7989            0 :     Array1D<Illums> FirstFlux; // Sky related first reflected flux
    7990              :     // Array1D<Real64> FirstFluxSU;     // Sun related first reflected flux, excluding entering beam
    7991              :     // Array1D<Real64> FirstFluxSUdisk; // Sun related first reflected flux, due to entering beam
    7992              : 
    7993            0 :     Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
    7994              :     // Array1D<Real64> ElementLuminanceSun;     // sun related luminance at window element (exterior side), exluding beam
    7995              :     // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side), due to sun beam
    7996            0 :     Illums FLTot;
    7997              :     // Real64 FLSUTot;
    7998              :     // Real64 FLSUdiskTot;
    7999              : 
    8000              :     // Total for first relflected fluxes
    8001            0 :     Illums FFTot = Illums();
    8002              :     // Real64 FFSUTot;
    8003              :     // Real64 FFSUdiskTot;
    8004              : 
    8005              :     int NIncBasis;
    8006              :     int SolBmIndex; // index of current sun position
    8007              : 
    8008              :     Real64 LambdaInc; // current lambda value for incoming direction
    8009              :     // REAL(r64) :: LambdaTrn  ! current lambda value for incoming direction
    8010              :     Real64 dirTrans; // directional bsdf transmittance
    8011              : 
    8012            0 :     auto const &surf = s_surf->Surface(IWin);
    8013            0 :     auto const &surfWin = s_surf->SurfaceWindow(IWin);
    8014              : 
    8015            0 :     int CurCplxFenState = surfWin.ComplexFen.CurrentState;
    8016            0 :     auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
    8017            0 :     int iConst = surfWin.ComplexFen.State(CurCplxFenState).Konst;
    8018            0 :     int NTrnBasis = complexWinGeom.Trn.NBasis;
    8019              : 
    8020            0 :     if (!allocated(FL)) FL.allocate(NTrnBasis);
    8021            0 :     FL = Illums();
    8022              :     // if (!allocated(FLSU)) FLSU.dimension(NTrnBasis, 0.0);
    8023              :     // if (!allocated(FLSUdisk)) FLSUdisk.dimension(NTrnBasis, 0.0);
    8024              : 
    8025            0 :     if (!allocated(FirstFlux)) FirstFlux.allocate(NTrnBasis);
    8026            0 :     FirstFlux = Illums();
    8027              :     // if (!allocated(FirstFluxSU)) FirstFluxSU.dimension(NTrnBasis, 0.0);
    8028              :     // if (!allocated(FirstFluxSUdisk)) FirstFluxSUdisk.dimension(NTrnBasis, 0.0);
    8029              : 
    8030            0 :     NIncBasis = complexWinGeom.Inc.NBasis;
    8031            0 :     if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
    8032            0 :     ElementLuminance = Illums();
    8033              :     // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
    8034              :     // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
    8035              : 
    8036              :     // Integration over sky/ground/sun elements is done over window incoming basis element and flux is calculated for each
    8037              :     // outgoing direction. This is used to calculate first reflected flux
    8038              : 
    8039            0 :     ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
    8040              : 
    8041              :     // luminance from sun disk needs to include fraction of sunlit area
    8042            0 :     SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
    8043            0 :     Real64 COSIncSun = (SolBmIndex > 0) ? complexWinGeom.CosInc(SolBmIndex) : 0.0;
    8044              : 
    8045            0 :     for (int i = 1; i <= (int)ElementLuminance.size(); ++i)
    8046            0 :         ElementLuminance(i).sunDisk *= state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) * COSIncSun;
    8047              : 
    8048              :     //        FLSKTot = 0.0;
    8049            0 :     FLTot.sun = 0.0;
    8050            0 :     FLTot.sunDisk = 0.0;
    8051            0 :     FFTot.sun = 0.0;
    8052            0 :     FFTot.sunDisk = 0.0;
    8053              :     // now calculate flux into each outgoing direction by integrating over all incoming directions
    8054            0 :     for (int iBackElem = 1; iBackElem <= NTrnBasis; ++iBackElem) {
    8055            0 :         for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
    8056            0 :             LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
    8057            0 :             dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iBackElem, iIncElem);
    8058              : 
    8059            0 :             auto &fl = FL(iBackElem);
    8060            0 :             auto const &elemLum = ElementLuminance(iIncElem);
    8061            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8062            0 :                 fl.sky[iSky] += dirTrans * LambdaInc * elemLum.sky[iSky];
    8063              :             }
    8064              : 
    8065            0 :             fl.sun += dirTrans * LambdaInc * elemLum.sun;
    8066            0 :             fl.sunDisk += dirTrans * LambdaInc * elemLum.sunDisk;
    8067              :         }
    8068              : 
    8069            0 :         auto &firstFlux = FirstFlux(iBackElem);
    8070            0 :         auto const &fl = FL(iBackElem);
    8071            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8072            0 :             firstFlux.sky[iSky] = fl.sky[iSky] * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8073            0 :             FFTot.sky[iSky] += firstFlux.sky[iSky];
    8074              :             //                FLSKTot( iSky ) += FLSK( iSky, iBackElem );
    8075              :         }
    8076            0 :         firstFlux.sun = fl.sun * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8077            0 :         FFTot.sun += firstFlux.sun;
    8078            0 :         FLTot.sun += fl.sun;
    8079              : 
    8080            0 :         firstFlux.sunDisk = fl.sunDisk * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8081            0 :         FFTot.sunDisk += firstFlux.sunDisk;
    8082            0 :         FLTot.sunDisk += fl.sunDisk;
    8083              :     }
    8084              : 
    8085            0 :     auto const &thisEnclDaylight = dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex);
    8086            0 :     Real64 EnclInsideSurfArea = thisEnclDaylight.totInsSurfArea;
    8087              : 
    8088            0 :     auto &eintsk = dl->reflIllum(IHR)[iWinCover_Bare];
    8089            0 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8090            0 :         eintsk.sky[iSky] = FFTot.sky[iSky] * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8091              :     } // for (iSky)
    8092              : 
    8093            0 :     dl->reflIllum(IHR)[iWinCover_Bare].sun =
    8094            0 :         FFTot.sun * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8095            0 :     dl->reflIllum(IHR)[iWinCover_Bare].sunDisk =
    8096            0 :         FFTot.sunDisk * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8097              : 
    8098            0 :     if (allocated(FL)) FL.deallocate();
    8099              :     // if (allocated(FLSU)) FLSU.deallocate();
    8100              :     // if (allocated(FLSUdisk)) FLSUdisk.deallocate();
    8101              : 
    8102            0 :     if (allocated(FirstFlux)) FirstFlux.deallocate();
    8103              :     // if (allocated(FirstFluxSU)) FirstFluxSU.deallocate();
    8104              :     // if (allocated(FirstFluxSUdisk)) FirstFluxSUdisk.deallocate();
    8105              : 
    8106            0 :     if (allocated(ElementLuminance)) ElementLuminance.deallocate();
    8107              :     // if (allocated(ElementLuminanceSun)) ElementLuminanceSun.deallocate();
    8108              :     // if (allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.deallocate();
    8109            0 : }
    8110              : 
    8111            0 : void DayltgDirectIllumComplexFenestration(EnergyPlusData &state,
    8112              :                                           int const IWin,      // Window index
    8113              :                                           int const WinEl,     // Current window element counter
    8114              :                                           int const IHR,       // Hour of day
    8115              :                                           int const iRefPoint, // reference point index
    8116              :                                           CalledFor const CalledFrom,
    8117              :                                           int const MapNum)
    8118              : {
    8119              : 
    8120              :     // SUBROUTINE INFORMATION:
    8121              :     //       AUTHOR         Simon Vidanovic
    8122              :     //       DATE WRITTEN   June 2013
    8123              : 
    8124            0 :     auto &dl = state.dataDayltg;
    8125            0 :     auto &s_surf = state.dataSurface;
    8126              : 
    8127              :     // Luminances from different sources to the window
    8128            0 :     Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
    8129              :     // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side),
    8130              :     // exluding beam
    8131              :     // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side),
    8132              :     // due to sun beam
    8133              : 
    8134              :     int RefPointIndex; // reference point patch number
    8135              : 
    8136              :     Real64 dirTrans;    // directional BSDF transmittance
    8137              :     Real64 dOmega;      // solid view angle of current element
    8138              :     Real64 zProjection; // z-axe projection of solid view angle (used to calculate amount of light at horizontal surface
    8139              :     // laying at reference point)
    8140              : 
    8141            0 :     int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
    8142            0 :     auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
    8143            0 :     int iConst = s_surf->SurfaceWindow(IWin).ComplexFen.State(CurCplxFenState).Konst;
    8144            0 :     int NIncBasis = complexWin.Geom(CurCplxFenState).Inc.NBasis;
    8145              : 
    8146            0 :     if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
    8147            0 :     ElementLuminance = Illums();
    8148              :     // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
    8149              :     // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
    8150              : 
    8151            0 :     ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
    8152              : 
    8153              :     // find number of outgoing basis towards current reference point
    8154            0 :     if (CalledFrom == CalledFor::RefPoint) {
    8155            0 :         RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).RefPoint(iRefPoint).RefPointIndex(WinEl);
    8156            0 :         dOmega = complexWin.RefPoint(iRefPoint).SolidAngle(WinEl);
    8157            0 :         zProjection = complexWin.RefPoint(iRefPoint).SolidAngleVec(WinEl).z;
    8158            0 :     } else if (CalledFrom == CalledFor::MapPoint) {
    8159            0 :         assert(MapNum > 0);
    8160            0 :         RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).IlluminanceMap(iRefPoint, MapNum).RefPointIndex(WinEl);
    8161            0 :         dOmega = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngle(WinEl);
    8162            0 :         zProjection = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngleVec(WinEl).z;
    8163              :     }
    8164              : 
    8165            0 :     Illums WinLum = Illums();
    8166            0 :     Illums EDir = Illums();
    8167              : 
    8168            0 :     for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
    8169              :         // LambdaInc = ComplexWind(IWin)%Geom(CurCplxFenState)%Inc%Lamda(iIncElem)
    8170            0 :         dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(RefPointIndex, iIncElem);
    8171              : 
    8172            0 :         auto const &elemLum = ElementLuminance(iIncElem);
    8173            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8174            0 :             WinLum.sky[iSky] += dirTrans * elemLum.sky[iSky];
    8175              :         }
    8176              : 
    8177            0 :         WinLum.sun += dirTrans * elemLum.sun;
    8178              : 
    8179              :         // For sun disk need to go throug outgoing directions and see which directions actually contain reference point
    8180              :     }
    8181              : 
    8182            0 :     if (zProjection > 0.0) {
    8183            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8184            0 :             EDir.sky[iSky] = WinLum.sky[iSky] * dOmega * zProjection;
    8185              :         }
    8186            0 :         EDir.sun = WinLum.sun * dOmega * zProjection;
    8187              :     }
    8188              : 
    8189              :     // Store solution in global variables
    8190            0 :     auto &avwlsk = dl->avgWinLum(IHR)[iWinCover_Bare];
    8191            0 :     auto &edirsk = dl->dirIllum(IHR)[iWinCover_Bare];
    8192              : 
    8193            0 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8194            0 :         avwlsk.sky[iSky] += WinLum.sky[iSky];
    8195            0 :         edirsk.sky[iSky] += EDir.sky[iSky];
    8196              :     }
    8197              : 
    8198            0 :     dl->avgWinLum(IHR)[iWinCover_Bare].sun += WinLum.sun;
    8199            0 :     dl->dirIllum(IHR)[iWinCover_Bare].sun += EDir.sun;
    8200              :     // AVWLSUdisk(1,IHR) = AVWLSUdisk(1,IHR) + WinLumSUdisk
    8201            0 : } // DayltgDirectIllumComplexFenestration()
    8202              : 
    8203            0 : void DayltgDirectSunDiskComplexFenestration(EnergyPlusData &state,
    8204              :                                             int const iWin,  // Window index
    8205              :                                             int const iHour, // Hour of day
    8206              :                                             int const iRefPoint,
    8207              :                                             int const NumEl,            // Total number of window elements
    8208              :                                             Real64 const AZVIEW,        // Azimuth of view vector in absolute coord system for
    8209              :                                             CalledFor const CalledFrom, // indicate  which type of routine called this routine
    8210              :                                             int const MapNum)
    8211              : {
    8212              : 
    8213              :     // SUBROUTINE INFORMATION:
    8214              :     //       AUTHOR         Simon Vidanovic
    8215              :     //       DATE WRITTEN   June 2013
    8216              : 
    8217              :     // PURPOSE OF THIS SUBROUTINE:
    8218              :     // Calculate illuminance from sun disk for complex fenestration systems
    8219              : 
    8220              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8221            0 :     auto &dl = state.dataDayltg;
    8222            0 :     auto &s_surf = state.dataSurface;
    8223              : 
    8224            0 :     assert(CalledFrom != CalledFor::MapPoint || MapNum > 0);
    8225              : 
    8226            0 :     auto const &window = s_surf->SurfaceWindow(iWin);
    8227            0 :     int CurCplxFenState = window.ComplexFen.CurrentState;
    8228            0 :     int iConst = window.ComplexFen.State(CurCplxFenState).Konst;
    8229              : 
    8230            0 :     auto const &complexWindow = state.dataBSDFWindow->ComplexWind(iWin);
    8231            0 :     auto const &complexWindowGeom = complexWindow.Geom(CurCplxFenState);
    8232            0 :     auto const &complexWindowDayltgGeom = complexWindow.DaylghtGeom(CurCplxFenState);
    8233            0 :     int SolBmIndex = complexWindowGeom.SolBmIndex(iHour, state.dataGlobal->TimeStep);
    8234              : 
    8235            0 :     Real64 WindowSolidAngleDaylightPoint = (CalledFrom == CalledFor::RefPoint) ? window.refPts(iRefPoint).solidAngWtd : 0.0;
    8236            0 :     if (WindowSolidAngleDaylightPoint < 1e-6) return;
    8237              : 
    8238            0 :     Illums WinLum;
    8239            0 :     Illums ElemLum;
    8240              : 
    8241            0 :     int NTrnBasis = complexWindowGeom.Trn.NBasis;
    8242            0 :     for (int iTrnElem = 1; iTrnElem <= NTrnBasis; ++iTrnElem) {
    8243              :         // if ray from any part of the window can reach reference point
    8244              :         int refPointIntersect = (CalledFrom == CalledFor::RefPoint)
    8245            0 :                                     ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPointIntersection(iTrnElem)
    8246            0 :                                     : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPointIntersection(iTrnElem);
    8247              : 
    8248            0 :         if (refPointIntersect == 0) continue;
    8249              : 
    8250            0 :         Real64 PosFac = (CalledFrom == CalledFor::RefPoint) ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPtIntPosFac(iTrnElem)
    8251            0 :                                                             : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPtIntPosFac(iTrnElem);
    8252              : 
    8253            0 :         Real64 RayZ = -complexWindowGeom.sTrn(iTrnElem).z;
    8254              : 
    8255              :         // Need to recalculate position factor for dominant direction in case of specular bsdf.  Otherwise this will produce
    8256              :         // very inaccurate results because of position factor of the sun and bsdf pach can vary by lot
    8257            0 :         if (iTrnElem == SolBmIndex) {
    8258            0 :             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
    8259            0 :             Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
    8260            0 :             PosFac = DayltgGlarePositionFactor(XR, YR);
    8261            0 :             RayZ = dl->sunAngles.sinPhi;
    8262              :         }
    8263              : 
    8264            0 :         if (PosFac == 0.0) continue;
    8265              : 
    8266            0 :         Real64 dirTrans = (SolBmIndex > 0) ? state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iTrnElem, SolBmIndex) : 0.0;
    8267            0 :         Real64 LambdaTrn = complexWindowGeom.Trn.Lamda(iTrnElem);
    8268            0 :         Vector3<Real64> V = -complexWindowGeom.sTrn(iTrnElem);
    8269            0 :         Vector3<Real64> RWin = s_surf->Surface(iWin).Centroid;
    8270            0 :         Real64 TransBeam = DayltgHitObstruction(state, iHour, iWin, RWin, V);
    8271              : 
    8272            0 :         WinLum.sunDisk += (14700.0 * std::sqrt(0.000068 * PosFac) * double(NumEl) / std::pow(WindowSolidAngleDaylightPoint, 0.8)) * dirTrans *
    8273            0 :                           LambdaTrn * TransBeam;
    8274              : 
    8275            0 :         ElemLum.sunDisk += RayZ * dirTrans * LambdaTrn * TransBeam;
    8276            0 :     } // for (iTrnElem)
    8277              : 
    8278            0 :     dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = WinLum.sunDisk;
    8279            0 :     dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = ElemLum.sunDisk;
    8280              : }
    8281              : 
    8282      2509936 : Real64 DayltgSkyLuminance(EnergyPlusData const &state,
    8283              :                           SkyType sky,        // Sky type: 1=clear, 2=clear turbid, 3=intermediate, 4=overcast
    8284              :                           Real64 const THSKY, // Azimuth and altitude of sky element (radians)
    8285              :                           Real64 const PHSKY)
    8286              : {
    8287              : 
    8288              :     // SUBROUTINE INFORMATION:
    8289              :     //       AUTHOR         Fred Winkelmann
    8290              :     //       DATE WRITTEN   July 1997
    8291              : 
    8292              :     // PURPOSE OF THIS SUBROUTINE:
    8293              :     // Called by CalcDayltgCoefficients, DayltgExtHorizIllum AND DayltgInterReflectedIllum.  gives
    8294              :     // luminance in cd/m2 for four different sky types, as described in R.Perez, P.Ineichen,
    8295              :     // R.Seals, J.Michalsky and R.Stewart, "Modeling daylight availability and irradiance
    8296              :     // components from direct and global irradiance," Solar Energy 44, 1990, 271-289.
    8297              :     // The luminance distributions in this routine are normalized such that
    8298              :     // the zenith luminance is 1.0, i.e., DayltgSkyLuminance =
    8299              :     // (sky luminance at THSKY, PHSKY)/(zenith luminance), which is dimensionless.
    8300              :     // The sky types are:
    8301              :     // 1. Standard CIE clear sky
    8302              :     // 2. Standard CIE high-turbidity clear sky
    8303              :     // 3. CIE intermediate sky
    8304              :     // 4. CIE overcast sky
    8305              : 
    8306              :     // METHODOLOGY EMPLOYED:
    8307              : 
    8308              :     // REFERENCES:
    8309              :     // Based on DOE-2.1E subroutine DSKYLU, which did only clear and overcast skies.
    8310              : 
    8311              :     // OTHER NOTES:
    8312              :     // THSKY ranges from 0 to 2Pi starting with 0 directly East and rotating clockwise.
    8313              :     // PHSKY ranges from 0 to Pi starting with 0 at the horizon and Pi/2 at the zenith.
    8314              : 
    8315              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    8316      2509936 :     auto const &dl = state.dataDayltg;
    8317              : 
    8318      2509936 :     Real64 G = 0.0;    // Angle between sun and element of sky (radians)
    8319      2509936 :     Real64 COSG = 0.0; // Cosine of G
    8320              : 
    8321      2509936 :     Real64 SPHSKY = max(std::sin(PHSKY), 0.01); // Prevent floating point underflows
    8322      2509936 :     Real64 Z = Constant::PiOvr2 - dl->sunAngles.phi;
    8323      2509936 :     if (sky != SkyType::Overcast) { // Following not needed for overcast sky
    8324      1882452 :         COSG = SPHSKY * dl->sunAngles.sinPhi + std::cos(PHSKY) * dl->sunAngles.cosPhi * std::cos(THSKY - dl->sunAngles.theta);
    8325      1882452 :         COSG = max(DataPrecisionGlobals::constant_minusone, min(COSG, 1.0)); // Prevent out of range due to roundoff
    8326      1882452 :         G = std::acos(COSG);
    8327              :     }
    8328              : 
    8329      2509936 :     switch (sky) {
    8330       627484 :     case SkyType::Clear: {
    8331       627484 :         Real64 Z1 = 0.910 + 10.0 * std::exp(-3.0 * G) + 0.45 * COSG * COSG;
    8332       627484 :         Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
    8333       627484 :         Real64 Z3 = 0.27385 * (0.91 + 10.0 * std::exp(-3.0 * Z) + 0.45 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
    8334       627484 :         return Z1 * Z2 / Z3;
    8335              : 
    8336              :     } break;
    8337       627484 :     case SkyType::ClearTurbid: {
    8338       627484 :         Real64 Z1 = 0.856 + 16.0 * std::exp(-3.0 * G) + 0.3 * COSG * COSG;
    8339       627484 :         Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
    8340       627484 :         Real64 Z3 = 0.27385 * (0.856 + 16.0 * std::exp(-3.0 * Z) + 0.3 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
    8341       627484 :         return Z1 * Z2 / Z3;
    8342              : 
    8343              :     } break;
    8344              : 
    8345       627484 :     case SkyType::Intermediate: {
    8346       627484 :         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;
    8347       627484 :         Real64 Z2 = std::exp(-G * 0.563 * ((dl->sunAngles.phi - 0.008) * (PHSKY + 1.059) + 0.812));
    8348       627484 :         Real64 Z3 = 0.99224 * std::sin(2.6 * dl->sunAngles.phi + 0.316) + 2.73852;
    8349       627484 :         Real64 Z4 = std::exp(-Z * 0.563 * ((dl->sunAngles.phi - 0.008) * 2.6298 + 0.812));
    8350       627484 :         return Z1 * Z2 / (Z3 * Z4);
    8351              :     } break;
    8352       627484 :     case SkyType::Overcast: {
    8353       627484 :         return (1.0 + 2.0 * SPHSKY) / 3.0;
    8354              :     } break;
    8355            0 :     default:
    8356            0 :         assert(false);
    8357              :         return 0.0;
    8358              :     }
    8359              : }
    8360              : 
    8361          816 : Real64 ProfileAngle(EnergyPlusData &state,
    8362              :                     int const SurfNum,                                     // Surface number
    8363              :                     Vector3<Real64> const &CosDirSun,                      // Solar direction cosines
    8364              :                     DataWindowEquivalentLayer::Orientation const HorOrVert // If HORIZONTAL, calculates ProfileAngHor
    8365              : )
    8366              : {
    8367              : 
    8368              :     // SUBROUTINE INFORMATION:
    8369              :     //       AUTHOR         Fred Winkelmann
    8370              :     //       DATE WRITTEN   May 2001
    8371              : 
    8372              :     // PURPOSE OF THIS SUBROUTINE:
    8373              :     // Calculates profile angle for a surface.
    8374              : 
    8375              :     // Locals
    8376              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8377              :     // For HorOrVert = HORIZONTAL,
    8378              :     //  this is the incidence angle in a plane that is normal to the window
    8379              :     //  and parallel to the Y-axis of the window (the axis along
    8380              :     //  which the height of the window is measured).
    8381              :     //  For HorOrVert = VERTICAL,
    8382              :     //  this is the incidence angle in a plane that is normal to the window
    8383              :     //  and parallel to the X-axis of the window (the axis along
    8384              :     //  which the width of the window is measured).
    8385              :     // If VERTICAL, calculates ProfileAngVert
    8386          816 :     auto &s_surf = state.dataSurface;
    8387              : 
    8388          816 :     auto const &surf = s_surf->Surface(SurfNum);
    8389          816 :     if (HorOrVert == DataWindowEquivalentLayer::Orientation::Horizontal) {  // Profile angle for horizontal structures
    8390          646 :         Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad; // Window elevation: angle between outward normal and horizontal (radians)
    8391          646 :         Real64 AzimWin = (90.0 - surf.Azimuth) * Constant::DegToRad;        // Window azimuth (radians)
    8392          646 :         Real64 ElevSun = std::asin(CosDirSun.z);                            // Sun elevation; angle between sun and horizontal (radians)
    8393          646 :         Real64 AzimSun = std::atan2(CosDirSun.y, CosDirSun.x);              // Sun azimuth (radians)
    8394          646 :         return std::atan(std::sin(ElevSun) / std::abs(std::cos(ElevSun) * std::cos(AzimWin - AzimSun))) - ElevWin;
    8395              :     } else { // Profile angle for vertical structures
    8396          170 :         Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad;
    8397          170 :         Real64 AzimWin = surf.Azimuth * Constant::DegToRad;    // 7952
    8398          170 :         Real64 AzimSun = std::atan2(CosDirSun.x, CosDirSun.y); // 7952
    8399              : 
    8400              :         Real64 ProfileAng;
    8401          170 :         if (std::abs(ElevWin) < 0.1) {      // Near-vertical window
    8402          170 :             ProfileAng = AzimWin - AzimSun; // CR7952 allow sign changes.
    8403              :         } else {
    8404            0 :             Vector3<Real64> WinNorm = surf.OutNormVec; // Window outward normal unit vector
    8405            0 :             Real64 ThWin = AzimWin - Constant::PiOvr2;
    8406            0 :             Real64 const sin_ElevWin = std::sin(ElevWin);
    8407              :             // Cross product of WinNorm and vector along window baseline
    8408            0 :             Vector3<Real64> WinNormCrossBase = {-sin_ElevWin * std::cos(ThWin), sin_ElevWin * std::sin(ThWin), std::cos(ElevWin)};
    8409              :             // Projection of sun vector onto plane (perpendicular to window plane) determined
    8410              :             // by WinNorm and vector along baseline of window
    8411            0 :             Vector3<Real64> SunPrime = CosDirSun - WinNormCrossBase * dot(CosDirSun, WinNormCrossBase);
    8412            0 :             ProfileAng = std::abs(std::acos(dot(WinNorm, SunPrime) / SunPrime.magnitude()));
    8413              :             // CR7952 correct sign of result for vertical slats
    8414            0 :             if ((AzimWin - AzimSun) < 0.0) ProfileAng = -1.0 * ProfileAng;
    8415            0 :         }
    8416              :         // Constrain to 0 to pi
    8417          170 :         if (ProfileAng > Constant::Pi) ProfileAng = 2.0 * Constant::Pi - ProfileAng;
    8418          170 :         return ProfileAng;
    8419              :     }
    8420              : }
    8421              : 
    8422            0 : void DayltgClosestObstruction(EnergyPlusData &state,
    8423              :                               Vector3<Real64> const &RecPt,  // Point on window from which ray emanates (m)
    8424              :                               Vector3<Real64> const &RayVec, // Unit vector along ray pointing away from window (m)
    8425              :                               int &NearestHitSurfNum,        // Surface number of nearest obstruction that is hit by ray;
    8426              :                               Vector3<Real64> &NearestHitPt  // Ray's hit point on nearest obstruction (m)
    8427              : )
    8428              : {
    8429              : 
    8430              :     // SUBROUTINE INFORMATION:
    8431              :     //       AUTHOR         Fred Winkelmann
    8432              :     //       DATE WRITTEN   November 2003
    8433              : 
    8434              :     // PURPOSE OF THIS SUBROUTINE:
    8435              :     // Determines surface number and hit point of closest exterior obstruction hit
    8436              :     // by a ray from a window. If no obstruction is hit, NearestHitSurfNum = 0.
    8437              : 
    8438              :     // Locals
    8439              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8440              :     //  = 0 if no obstruction is hit.
    8441            0 :     auto &s_surf = state.dataSurface;
    8442              : 
    8443              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8444            0 :     Vector3<Real64> HitPt; // Hit point on an obstruction (m)
    8445              :     bool hit;              // True iff obstruction is hit
    8446              : 
    8447            0 :     NearestHitSurfNum = 0;
    8448            0 :     Real64 NearestHitDistance_sq(std::numeric_limits<Real64>::max()); // Distance squared from receiving point to nearest hit point for a ray (m^2)
    8449            0 :     NearestHitPt = 0.0;
    8450            0 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    8451              : 
    8452            0 :         for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    8453              :             // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
    8454            0 :             hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
    8455            0 :             if (!hit) // Ray pierces surface
    8456            0 :                 continue;
    8457              : 
    8458              :             // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
    8459              :             // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
    8460            0 :             if ((s_surf->Surface(ObsSurfNum).Class == SurfaceClass::Window) && (s_surf->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum)) {
    8461            0 :                 NearestHitSurfNum = ObsSurfNum;
    8462              :             } else {
    8463              :                 // Distance squared from receiving point to hit point
    8464            0 :                 Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
    8465              :                 // Reset NearestHitSurfNum and NearestHitDistance_sq if this hit point is closer than previous closest
    8466            0 :                 if (HitDistance_sq < NearestHitDistance_sq) {
    8467            0 :                     NearestHitDistance_sq = HitDistance_sq;
    8468            0 :                     NearestHitSurfNum = ObsSurfNum;
    8469            0 :                     NearestHitPt = HitPt;
    8470              :                 }
    8471              :             } // End of check if obstruction was hit
    8472              :         }     // for (ObsSurfNum)
    8473              : 
    8474              :     } else { // Surface octree search
    8475              : 
    8476            0 :         SurfaceData const *nearestHitSurface(nullptr);
    8477              : 
    8478              :         // Lambda function for the octree to test for surface hit
    8479            0 :         auto surfaceHit = [&s_surf, &RecPt, &RayVec, &hit, &NearestHitDistance_sq, &nearestHitSurface, &NearestHitPt](SurfaceData const &surface) {
    8480            0 :             if (surface.IsShadowPossibleObstruction) {
    8481            0 :                 Vector3<Real64> HitPt;
    8482              :                 // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
    8483            0 :                 hit = PierceSurface(surface, RecPt, RayVec, HitPt); // Check if ray pierces surface
    8484            0 :                 if (!hit) return;
    8485              : 
    8486              :                 // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
    8487              :                 // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
    8488            0 :                 if ((surface.Class == SurfaceClass::Window) && (surface.BaseSurf > 0) && (&s_surf->Surface(surface.BaseSurf) == nearestHitSurface)) {
    8489            0 :                     nearestHitSurface = &surface;
    8490              :                 } else {
    8491              :                     // Distance squared from receiving point to hit point
    8492            0 :                     Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
    8493              :                     // Reset nearestHitSurface and NearestHitDistance_sq if this hit point is closer than previous closest
    8494            0 :                     if (HitDistance_sq < NearestHitDistance_sq) {
    8495            0 :                         NearestHitDistance_sq = HitDistance_sq;
    8496            0 :                         nearestHitSurface = &surface;
    8497            0 :                         NearestHitPt = HitPt;
    8498              :                     }
    8499              :                 } // End of check if obstruction was hit
    8500            0 :             }
    8501            0 :         };
    8502              : 
    8503              :         // Process octree surface candidates
    8504            0 :         Vector3<Real64> const RayVec_inv(SurfaceOctreeCube::safe_inverse(RayVec));
    8505            0 :         state.dataHeatBalMgr->surfaceOctree.processSurfaceRayIntersectsCube(RecPt, RayVec, RayVec_inv, surfaceHit);
    8506            0 :         if (nearestHitSurface != nullptr) { // Find surface number: This is inefficient: Improve when surfaces know their own number
    8507            0 :             for (int i = 1; i <= s_surf->TotSurfaces; ++i) {
    8508            0 :                 if (&s_surf->Surface(i) == nearestHitSurface) {
    8509            0 :                     NearestHitSurfNum = i;
    8510            0 :                     break;
    8511              :                 }
    8512              :             }
    8513            0 :             assert(NearestHitSurfNum != 0);
    8514              :         }
    8515            0 :     }
    8516            0 : } // DayltgClosestObstruction()
    8517              : 
    8518            0 : Real64 DayltgSurfaceLumFromSun(EnergyPlusData &state,
    8519              :                                int const IHR,                   // Hour number
    8520              :                                Vector3<Real64> const &Ray,      // Ray from window to reflecting surface (m)
    8521              :                                int const ReflSurfNum,           // Number of surface for which luminance is being calculated
    8522              :                                Vector3<Real64> const &ReflHitPt // Point on ReflSurfNum for luminance calculation (m)
    8523              : )
    8524              : {
    8525              : 
    8526              :     // SUBROUTINE INFORMATION:
    8527              :     //       AUTHOR         Fred Winkelmann
    8528              :     //       DATE WRITTEN   November 2003
    8529              : 
    8530              :     // PURPOSE OF THIS SUBROUTINE:
    8531              :     // Calculates exterior surface luminance due to beam solar diffuse reflection.
    8532              : 
    8533              :     // Locals
    8534              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8535              :     //  beam normal illuminance (cd/m2)
    8536              : 
    8537              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8538            0 :     Vector3<Real64> SurfaceLumFromSunReflNorm; // Unit normal to reflecting surface (m)
    8539            0 :     Vector3<Real64> SurfaceLumFromSunObsHitPt; // Hit point on obstruction (m)
    8540              :     bool hitObs;                               // True iff obstruction is hit
    8541              :     Real64 DiffVisRefl;                        // Diffuse visible reflectance of ReflSurfNum
    8542              : 
    8543            0 :     auto &s_surf = state.dataSurface;
    8544              :     // Skip daylighting shelves since reflection from these is separately calculated
    8545            0 :     if (s_surf->SurfDaylightingShelfInd(ReflSurfNum) > 0) return 0.0;
    8546              : 
    8547            0 :     auto const &reflSurf = s_surf->Surface(ReflSurfNum);
    8548              : 
    8549              :     // Normal to reflecting surface in hemisphere containing window element
    8550            0 :     SurfaceLumFromSunReflNorm = reflSurf.OutNormVec;
    8551            0 :     if (reflSurf.IsShadowing) {
    8552            0 :         if (dot(SurfaceLumFromSunReflNorm, Ray) > 0.0) {
    8553            0 :             SurfaceLumFromSunReflNorm *= -1.0;
    8554              :         }
    8555              :     }
    8556              :     // Cosine of angle of incidence of sun at HitPt if sun were to reach HitPt
    8557            0 :     Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
    8558            0 :     Real64 CosIncAngAtHitPt = dot(SurfaceLumFromSunReflNorm, SUNCOS_IHR);
    8559              :     // Require that the sun be in front of this surface relative to window element
    8560            0 :     if (CosIncAngAtHitPt <= 0.0) return 0.0; // Sun is in back of reflecting surface
    8561              :     // Sun reaches ReflHitPt if vector from ReflHitPt to sun is unobstructed
    8562            0 :     hitObs = false;
    8563            0 :     for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    8564              :         // Exclude as a possible obstructor ReflSurfNum and its base surface (if it has one)
    8565            0 :         if (ObsSurfNum == ReflSurfNum || ObsSurfNum == reflSurf.BaseSurf) continue;
    8566            0 :         hitObs = PierceSurface(state, ObsSurfNum, ReflHitPt, SUNCOS_IHR, SurfaceLumFromSunObsHitPt);
    8567            0 :         if (hitObs) break;
    8568              :     }
    8569              : 
    8570            0 :     if (hitObs) return 0.0; // Obstruction was hit, blocking s auto surfaceHit = [&state, &GroundHitPtun
    8571              :     // Obstruction was not hit; sun reaches ReflHitPt.
    8572              :     // Calculate luminance at ReflHitPt due to beam solar reflection (for unit beam normal illuminance)
    8573            0 :     if (reflSurf.IsShadowing) {
    8574            0 :         DiffVisRefl = s_surf->SurfShadowDiffuseVisRefl(ReflSurfNum);
    8575              :         // Note that if the shadowing surface has a non-zero glazing fraction (e.g., neighboring bldg) that the above is
    8576              :         // (1 - glazing fraction) * (vis refl of opaque part of shadowing surface); specular reflection is
    8577              :         // excluded in this value of DiffVisRefl.
    8578              :     } else { // Exterior building surface
    8579            0 :         if (!state.dataConstruction->Construct(reflSurf.Construction).TypeIsWindow) {
    8580            0 :             DiffVisRefl = 1.0 - state.dataConstruction->Construct(reflSurf.Construction).OutsideAbsorpSolar;
    8581              :         } else {
    8582              :             // Window; assume bare so no beam-to-diffuse reflection
    8583            0 :             DiffVisRefl = 0.0;
    8584              :         }
    8585              :     }
    8586            0 :     return CosIncAngAtHitPt * DiffVisRefl / Constant::Pi;
    8587            0 : }
    8588              : 
    8589        88617 : void DayltgInteriorMapIllum(EnergyPlusData &state)
    8590              : {
    8591              : 
    8592              :     // *****super modified version of DayltgInteriorIllum by Peter Graham Ellis
    8593              :     // *****removes all control code, just calculates illum with previously determined control settings
    8594              :     // *****this should be packaged into a subroutine called from 2 places
    8595              : 
    8596              :     // SUBROUTINE INFORMATION:
    8597              :     //       AUTHOR         Fred Winkelmann
    8598              :     //       DATE WRITTEN   July 1997
    8599              :     //       MODIFIED       March 2000, FW: interpolate clear-sky daylight factors using
    8600              :     //                      HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
    8601              :     //                      only HourOfDay was used
    8602              :     //                      Jan 2001, FW: interpolate in slat angle for windows with blinds
    8603              :     //                      that have movable slats
    8604              :     //                      Dec 2003, FW: fix bug--even though between-glass shade/blind is on
    8605              :     //                        daylight illum at ref pt was calculated as though it was off
    8606              :     //                      June 2009, TH: modified for thermochromic windows
    8607              :     //                      March 2010, TH: fix bug (CR 8057) for electrochromic windows
    8608              :     //       RE-ENGINEERED  na
    8609              : 
    8610              :     // PURPOSE OF THIS SUBROUTINE:
    8611              :     // Using daylighting factors and exterior illuminance, determine
    8612              :     // the current-hour interior daylight illuminance and glare index
    8613              :     // at each reference point in a space.
    8614              : 
    8615              :     // Called by InitSurfaceHeatBalance.
    8616              : 
    8617              :     // REFERENCES:
    8618              :     // Based on DOE-2.1E subroutine DINTIL.
    8619        88617 :     auto &dl = state.dataDayltg;
    8620              : 
    8621              :     // Locals
    8622        88617 :     Array1D<Real64> daylight_illum;
    8623              : 
    8624              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8625              :     int iSky1; // Sky type index values for averaging two sky types
    8626              :     int iSky2;
    8627              :     Real64 SkyWeight;    // Weighting factor used to average two different sky types
    8628              :     Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
    8629              :     //   luminous efficacy and horizontal illuminance from averaged sky
    8630              : 
    8631        88617 :     if (state.dataGlobal->WarmupFlag) return;
    8632              : 
    8633         9388 :     auto &s_surf = state.dataSurface;
    8634              : 
    8635         9388 :     daylight_illum.allocate(MaxMapRefPoints);
    8636              : 
    8637              :     //              Initialize reference point illuminance and window background luminance
    8638              : 
    8639         9447 :     for (auto &thisMap : dl->illumMaps) {
    8640           59 :         int enclNum = thisMap.enclIndex;
    8641           59 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    8642              : 
    8643           59 :         int NREFPT = thisMap.TotalMapRefPoints; // Number of daylighting map reference points
    8644              : 
    8645           59 :         daylight_illum = 0.0;
    8646              : 
    8647           59 :         if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    8648           50 :             SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    8649           50 :             iSky1 = (int)SkyType::Clear;
    8650           50 :             iSky2 = (int)SkyType::ClearTurbid;
    8651            9 :         } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    8652            8 :             SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    8653            8 :             iSky1 = (int)SkyType::ClearTurbid;
    8654            8 :             iSky2 = (int)SkyType::Intermediate;
    8655              :         } else { // Sky is average of intermediate and overcast
    8656            1 :             SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    8657            1 :             iSky1 = (int)SkyType::Intermediate;
    8658            1 :             iSky2 = (int)SkyType::Overcast;
    8659              :         }
    8660              : 
    8661              :         //              First loop over windows in this space.
    8662              :         //              Find contribution of each window to the daylight illum
    8663              :         //              and to the glare numerator at each reference point.
    8664              :         //              Use shading flags set in WindowShadingManager.
    8665              : 
    8666           59 :         auto &daylFacHrCurr = thisMap.daylFac[state.dataGlobal->HourOfDay];
    8667           59 :         auto &daylFacHrPrev = thisMap.daylFac[state.dataGlobal->PreviousHour];
    8668              : 
    8669          118 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    8670           59 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    8671              : 
    8672              :             // Added TH 6/29/2009 for thermochromic windows
    8673           59 :             Real64 VTRatio = 1.0;
    8674           59 :             if (NREFPT > 0) {
    8675           59 :                 int IConst = s_surf->Surface(IWin).Construction;
    8676           59 :                 auto const &construction = state.dataConstruction->Construct(IConst);
    8677           59 :                 if (construction.isTCWindow) {
    8678              :                     // For thermochromic windows, daylight and glare factors are always calculated
    8679              :                     //  based on the master construction. They need to be adjusted by the VTRatio, including:
    8680              :                     //  ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
    8681              :                     //  DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
    8682            0 :                     Real64 VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
    8683            0 :                     Real64 VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
    8684            0 :                     VTRatio = VTNow / VTMaster;
    8685              :                 }
    8686              :             }
    8687              : 
    8688           59 :             Real64 wgtThisHr = state.dataGlobal->WeightNow;
    8689           59 :             Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
    8690              : 
    8691           59 :             std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
    8692              : 
    8693           59 :             auto &dfhr = DFHR[iWinCover_Bare];
    8694           59 :             auto &dfhrSh = DFHR[iWinCover_Shaded];
    8695              : 
    8696           59 :             auto &surfShade = s_surf->surfShades(IWin);
    8697              :             //              Loop over reference points
    8698         5959 :             for (int ILB = 1; ILB <= NREFPT; ++ILB) {
    8699              :                 // if (ILB != 5) continue;
    8700         5900 :                 auto const &illSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Bare];
    8701         5900 :                 auto const &illSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Bare];
    8702         5900 :                 auto const &illShSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Shaded];
    8703         5900 :                 auto const &illShSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Shaded];
    8704              : 
    8705              :                 //          Daylight factors for current sun position
    8706        29500 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8707              :                     //                                ===Bare window===
    8708        23600 :                     dfhr.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr.sky[iSky] + wgtPrevHr * illSkyPrev.sky[iSky]);
    8709              : 
    8710        70800 :                     if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    8711        47200 :                         (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
    8712              : 
    8713              :                         //                                 ===Shaded window===
    8714              :                         // Shade, screen, blind with fixed slats, or diffusing glass
    8715            0 :                         dfhrSh.sky[iSky] = VTRatio * (wgtThisHr * illShSkyCurr.sky[iSky] + wgtPrevHr * illShSkyPrev.sky[iSky]);
    8716              :                     } // End of check if window is shaded or has diffusing glass
    8717              :                 }     // for (iSky)
    8718              : 
    8719              :                 // Sun daylight factor for bare/shaded window
    8720         5900 :                 std::array<Illums, (int)DataSurfaces::WinCover::Num> tmpDFHR;
    8721        11800 :                 tmpDFHR[iWinCover_Bare].sun =
    8722         5900 :                     VTRatio * (wgtThisHr * (illSkyCurr.sun + illSkyCurr.sunDisk) + wgtPrevHr * (illSkyPrev.sun + illSkyPrev.sunDisk));
    8723              : 
    8724        17700 :                 if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    8725        11800 :                     (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
    8726              : 
    8727              :                     //                                 ===Shaded window===
    8728              :                     // Shade, screen, blind with fixed slats, or diffusing glass
    8729            0 :                     tmpDFHR[iWinCover_Shaded].sun = VTRatio * (wgtThisHr * illShSkyCurr.sun + wgtPrevHr * illShSkyPrev.sun);
    8730              : 
    8731            0 :                     if (!surfShade.blind.slatBlockBeam) {
    8732            0 :                         tmpDFHR[iWinCover_Shaded].sun += VTRatio * (wgtThisHr * illShSkyCurr.sunDisk + wgtPrevHr * illShSkyPrev.sunDisk);
    8733              :                     }
    8734              :                 } // End of check if window is shaded or has diffusing glass
    8735              : 
    8736              :                 //              Get illuminance at ref point from bare and shaded window by
    8737              :                 //              multiplying daylight factors by exterior horizontal illuminance
    8738              : 
    8739              :                 // Adding 0.001 in the following prevents zero DayltgInteriorMapIllumHorIllSky in early morning or late evening when sun
    8740              :                 // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
    8741         5900 :                 Illums tmpHorIll; // Horizontal illuminance for different sky types
    8742         5900 :                 auto const &gilCurr = dl->horIllum[state.dataGlobal->HourOfDay];
    8743         5900 :                 auto const &gilPrev = dl->horIllum[state.dataGlobal->PreviousHour];
    8744        29500 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8745        23600 :                     tmpHorIll.sky[iSky] = wgtThisHr * gilCurr.sky[iSky] + wgtPrevHr * gilPrev.sky[iSky] + 0.001;
    8746              :                 }
    8747              : 
    8748              :                 // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
    8749              :                 // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
    8750              :                 // also calculated in DayltgLuminousEfficacy.
    8751         5900 :                 HorIllSkyFac = state.dataEnvrn->HISKF / ((1.0 - SkyWeight) * tmpHorIll.sky[iSky2] + SkyWeight * tmpHorIll.sky[iSky1]);
    8752              : 
    8753        11800 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    8754        11800 :                     if (iWinCover == iWinCover_Shaded) {
    8755         5900 :                         if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) break;
    8756         5900 :                         if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) && !s_surf->SurfWinSolarDiffusing(IWin)) break;
    8757              :                     }
    8758         5900 :                     auto const &dfhr3 = DFHR[iWinCover];
    8759              : 
    8760        11800 :                     thisMap.refPts(ILB).winLums(loop)[iWinCover] = tmpDFHR[iWinCover].sun * state.dataEnvrn->HISUNF +
    8761         5900 :                                                                    HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * tmpHorIll.sky[iSky1] +
    8762         5900 :                                                                                    dfhr3.sky[iSky2] * (1.0 - SkyWeight) * tmpHorIll.sky[iSky2]);
    8763              :                 }
    8764              : 
    8765              :             } // End of reference point loop
    8766              :         }     // End of first loop over windows
    8767              : 
    8768              :         //              Second loop over windows. Find total daylight illuminance
    8769              :         //              and background luminance for each ref pt from all windows in
    8770              :         //              the space.  Use shading flags.
    8771              : 
    8772          118 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    8773           59 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    8774           59 :             auto const &surfWin = s_surf->SurfaceWindow(IWin);
    8775              : 
    8776           59 :             WinCover winCover = findWinShadingStatus(state, IWin);
    8777              : 
    8778              :             // CR 8057. 3/17/2010.
    8779              :             // Switchable windows may be in partially switched state rather than fully dark state
    8780           59 :             Real64 VTMULT = 1.0;
    8781              : 
    8782           59 :             int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    8783           59 :             if (s_surf->Surface(IWin).HasShadeControl) {
    8784            0 :                 if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    8785            0 :                     s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    8786              :                     // switchable windows in partial or fully switched state,
    8787              :                     //  get its intermediate VT calculated in DayltgInteriorIllum
    8788            0 :                     int IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
    8789            0 :                     if (IConstShaded > 0) {
    8790              :                         // Visible transmittance (VT) of electrochromic (EC) windows in fully dark state
    8791            0 :                         Real64 VTDark = Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    8792            0 :                         if (VTDark > 0) VTMULT = s_surf->SurfWinVisTransSelected(IWin) / VTDark;
    8793              :                     }
    8794              :                 }
    8795              :             }
    8796              : 
    8797         5959 :             for (int IL = 1; IL <= NREFPT; ++IL) {
    8798              :                 //              Determine if illuminance contribution is from bare or shaded window
    8799         5900 :                 daylight_illum(IL) += VTMULT * thisMap.refPts(IL).winLums(loop)[(int)winCover];
    8800              :             }
    8801              :         } // End of second window loop
    8802              : 
    8803              :         //              Variables for reporting
    8804         5959 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    8805         5900 :             thisMap.refPts(IL).lums[iLum_Illum] = max(daylight_illum(IL), 0.0);
    8806              :         }
    8807              :     } // End loop over maps
    8808        88617 : } // DayltgInteriorMapIllum()
    8809              : 
    8810           61 : void ReportIllumMap(EnergyPlusData &state, int const MapNum)
    8811              : {
    8812              : 
    8813              :     // SUBROUTINE INFORMATION:
    8814              :     //       AUTHOR         Peter Ellis
    8815              :     //       DATE WRITTEN   May 2003
    8816              : 
    8817              :     // PURPOSE OF THIS SUBROUTINE:
    8818              :     // This subroutine produces the Daylighting Illuminance Map output.  Each separate map (by zone)
    8819              :     // is placed on a temporary file and later (see CloseReportIllumMaps) coallesced into a single
    8820              :     // output file.
    8821              : 
    8822              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8823              : 
    8824           61 :     std::string MapNoString;
    8825           61 :     auto &dl = state.dataDayltg;
    8826              : 
    8827           61 :     if (dl->ReportIllumMap_firstTime) {
    8828            1 :         dl->ReportIllumMap_firstTime = false;
    8829            1 :         dl->FirstTimeMaps.dimension((int)dl->illumMaps.size(), true);
    8830            1 :         dl->EnvrnPrint.dimension((int)dl->illumMaps.size(), true);
    8831            1 :         dl->SavedMnDy.allocate((int)dl->illumMaps.size());
    8832              :     }
    8833              : 
    8834           61 :     auto &illumMap = dl->illumMaps(MapNum);
    8835              : 
    8836           61 :     if (dl->FirstTimeMaps(MapNum)) {
    8837              : 
    8838            2 :         dl->FirstTimeMaps(MapNum) = false;
    8839              : 
    8840            2 :         auto openMapFile = [&](const fs::path &filePath) -> InputOutputFile & {
    8841            2 :             auto &outputFile = *illumMap.mapFile;
    8842            2 :             outputFile.filePath = FileSystem::appendSuffixToPath(filePath, fmt::to_string(MapNum));
    8843            2 :             outputFile.ensure_open(state, "ReportIllumMap");
    8844            2 :             return outputFile;
    8845            2 :         };
    8846            2 :         if (dl->MapColSep == DataStringGlobals::CharTab) {
    8847            0 :             if (!openMapFile(state.files.outputMapTabFilePath).good()) return;
    8848              :             //                CommaDelimited = false; //Unused Set but never used
    8849            2 :         } else if (dl->MapColSep == DataStringGlobals::CharComma) {
    8850            1 :             if (!openMapFile(state.files.outputMapCsvFilePath).good()) return;
    8851              :             //                CommaDelimited = true; //Unused Set but never used
    8852              :         } else {
    8853            1 :             if (!openMapFile(state.files.outputMapTxtFilePath).good()) return;
    8854              :             //                CommaDelimited = false; //Unused Set but never used
    8855              :         }
    8856              : 
    8857            2 :         dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
    8858              : 
    8859            2 :         illumMap.Name = format("{} at {:.2R}m", illumMap.Name, illumMap.Z);
    8860              :     }
    8861           61 :     if (dl->SavedMnDy(MapNum) != state.dataEnvrn->CurMnDyHr.substr(0, 5)) {
    8862            0 :         dl->EnvrnPrint(MapNum) = true;
    8863            0 :         dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
    8864              :     }
    8865              : 
    8866           61 :     illumMap.pointsHeader = "";
    8867           61 :     int rCount = 0;
    8868          122 :     for (auto &thisDayltgCtrl : dl->daylightControl) {
    8869           61 :         if (thisDayltgCtrl.zoneIndex != illumMap.zoneIndex) continue;
    8870              : 
    8871          184 :         for (int R = 1; R <= thisDayltgCtrl.TotalDaylRefPoints; ++R) {
    8872          123 :             ++rCount;
    8873          123 :             auto const &refPt = thisDayltgCtrl.refPts(R);
    8874          123 :             illumMap.pointsHeader += format(" RefPt{}=({:.2R}:{:.2R}:{:.2R}),", rCount, refPt.absCoords.x, refPt.absCoords.y, refPt.absCoords.z);
    8875              :         }
    8876              :     }
    8877              : 
    8878           61 :     if (rCount > 0) {
    8879              :         // Remove trailing comma
    8880           61 :         illumMap.pointsHeader.pop_back();
    8881              :     }
    8882           61 :     if (dl->EnvrnPrint(MapNum)) {
    8883            1 :         WriteDaylightMapTitle(
    8884            1 :             state, MapNum, *illumMap.mapFile, illumMap.Name, state.dataEnvrn->EnvironmentName, illumMap.zoneIndex, illumMap.pointsHeader, illumMap.Z);
    8885            1 :         dl->EnvrnPrint(MapNum) = false;
    8886              :     }
    8887              : 
    8888           61 :     if (!state.dataGlobal->WarmupFlag) {
    8889           60 :         if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) { // Report only hourly
    8890              : 
    8891           16 :             int linelen = 0;
    8892              :             // Write X scale column header
    8893           16 :             std::string mapLine = format(" {} {:02}:00", dl->SavedMnDy(MapNum), state.dataGlobal->HourOfDay);
    8894           16 :             if (illumMap.HeaderXLineLengthNeeded) linelen = int(len(mapLine));
    8895           16 :             int RefPt = 1;
    8896          176 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    8897              :                 const std::string AddXorYString =
    8898          160 :                     format("{}({:.2R};{:.2R})=", dl->MapColSep, illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
    8899          160 :                 if (illumMap.HeaderXLineLengthNeeded) linelen += int(len(AddXorYString));
    8900          160 :                 mapLine += AddXorYString;
    8901          160 :                 ++RefPt;
    8902          160 :             } // X
    8903              : 
    8904           16 :             if (illumMap.HeaderXLineLengthNeeded) {
    8905            1 :                 illumMap.HeaderXLineLength = linelen;
    8906            1 :                 if (static_cast<std::string::size_type>(illumMap.HeaderXLineLength) > len(mapLine)) {
    8907            0 :                     ShowWarningError(state,
    8908            0 :                                      format("ReportIllumMap: Map=\"{}\" -- the X Header overflows buffer -- will be truncated at {} characters.",
    8909            0 :                                             illumMap.Name,
    8910            0 :                                             int(len(mapLine))));
    8911            0 :                     ShowContinueError(state, format("...needed {} characters. Please contact EnergyPlus support.", illumMap.HeaderXLineLength));
    8912              :                 }
    8913            1 :                 illumMap.HeaderXLineLengthNeeded = false;
    8914              :             }
    8915              : 
    8916           16 :             print(*illumMap.mapFile, "{}\n", mapLine);
    8917              : 
    8918              :             // Write Y scale prefix and illuminance values
    8919           16 :             RefPt = 1;
    8920          176 :             for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    8921          160 :                 mapLine = format("({:.2R};{:.2R})=", illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
    8922         1760 :                 for (int R = RefPt; R <= RefPt + illumMap.Xnum - 1; ++R) {
    8923         1600 :                     int IllumOut = nint(illumMap.refPts(R).lumsHr[iLum_Illum]);
    8924         1600 :                     std::string String = fmt::to_string(IllumOut);
    8925              :                     ;
    8926         1600 :                     if (!illumMap.refPts(R).inBounds) {
    8927            0 :                         String = "*" + String;
    8928              :                     }
    8929         1600 :                     mapLine += dl->MapColSep + String;
    8930         1600 :                 }
    8931              : 
    8932          160 :                 print(*illumMap.mapFile, "{}\n", mapLine);
    8933              : 
    8934          160 :                 RefPt += illumMap.Xnum;
    8935              :             } // X
    8936              : 
    8937           16 :             if (state.dataSQLiteProcedures->sqlite) {
    8938            0 :                 if (dl->SQFirstTime) {
    8939            0 :                     int const nX(maxval(dl->illumMaps, &IllumMap::Xnum));
    8940            0 :                     int const nY(maxval(dl->illumMaps, &IllumMap::Ynum));
    8941            0 :                     dl->XValue.allocate(nX);
    8942            0 :                     dl->YValue.allocate(nY);
    8943            0 :                     dl->IllumValue.allocate(nX, nY);
    8944            0 :                     dl->SQFirstTime = false;
    8945              :                 }
    8946              : 
    8947            0 :                 for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    8948            0 :                     dl->YValue(Y) = illumMap.Ymin + (Y - 1) * illumMap.Yinc;
    8949            0 :                     for (int X = 1; X <= illumMap.Xnum; ++X) {
    8950            0 :                         dl->XValue(X) = illumMap.Xmin + (X - 1) * illumMap.Xinc;
    8951            0 :                         int IllumIndex = X + (Y - 1) * illumMap.Xnum;
    8952            0 :                         dl->IllumValue(X, Y) = nint(illumMap.refPts(IllumIndex).lumsHr[iLum_Illum]);
    8953            0 :                         if (!illumMap.refPts(IllumIndex).inBounds) {
    8954            0 :                             dl->IllumValue(X, Y) = -dl->IllumValue(X, Y);
    8955              :                         }
    8956              :                     } // X Loop
    8957              :                 }     // Y Loop
    8958              : 
    8959              :                 // We need DataGlobals::CalendarYear, and not DataEnvironment::Year because
    8960              :                 // otherwise if you run a TMY file, you'll get for eg 1977, 1981, etc
    8961            0 :                 state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMap(MapNum,
    8962            0 :                                                                             state.dataGlobal->CalendarYear,
    8963            0 :                                                                             state.dataEnvrn->Month,
    8964            0 :                                                                             state.dataEnvrn->DayOfMonth,
    8965            0 :                                                                             state.dataGlobal->HourOfDay,
    8966              :                                                                             illumMap.Xnum,
    8967            0 :                                                                             dl->XValue,
    8968              :                                                                             illumMap.Ynum,
    8969            0 :                                                                             dl->YValue,
    8970            0 :                                                                             dl->IllumValue);
    8971              : 
    8972              :             } // WriteOutputToSQLite
    8973           16 :         }     // end time step
    8974              :     }         // not Warmup
    8975           61 : }
    8976              : 
    8977           26 : void CloseReportIllumMaps(EnergyPlusData &state)
    8978              : {
    8979              : 
    8980              :     // SUBROUTINE INFORMATION:
    8981              :     //       AUTHOR         Linda K. Lawrie
    8982              :     //       DATE WRITTEN   June 2003
    8983              : 
    8984              :     // PURPOSE OF THIS SUBROUTINE:
    8985              :     // This subroutine "closes" out the created daylight illuminance maps by merging them
    8986              :     // into the "eplusout.map" file.
    8987              : 
    8988              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8989           26 :     auto &dl = state.dataDayltg;
    8990              : 
    8991           26 :     if ((int)dl->illumMaps.size() > 0) {
    8992              :         // Write map header
    8993            0 :         if (dl->MapColSep == DataStringGlobals::CharTab) {
    8994            0 :             state.files.map.filePath = state.files.outputMapTabFilePath;
    8995            0 :         } else if (dl->MapColSep == DataStringGlobals::CharComma) {
    8996            0 :             state.files.map.filePath = state.files.outputMapCsvFilePath;
    8997              :         } else {
    8998            0 :             state.files.map.filePath = state.files.outputMapTxtFilePath;
    8999              :         }
    9000              : 
    9001            0 :         state.files.map.ensure_open(state, "CloseReportIllumMaps");
    9002              : 
    9003            0 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    9004            0 :             auto &illumMap = dl->illumMaps(MapNum);
    9005            0 :             if (!illumMap.mapFile->good()) continue; // fatal error processing
    9006              : 
    9007            0 :             const std::vector<std::string> mapLines = illumMap.mapFile->getLines();
    9008            0 :             if (mapLines.empty()) {
    9009            0 :                 ShowSevereError(state, format("CloseReportIllumMaps: IllumMap=\"{}\" is empty.", illumMap.Name));
    9010            0 :                 break;
    9011              :             }
    9012            0 :             for (const std::string &mapLine : mapLines) {
    9013            0 :                 print(state.files.map, "{}\n", mapLine);
    9014              :             }
    9015            0 :             illumMap.mapFile->del();
    9016            0 :         }
    9017              : 
    9018            0 :         if (!dl->mapResultsReported && !state.dataErrTracking->AbortProcessing) {
    9019            0 :             const std::string message = "CloseReportIllumMaps: Illuminance maps requested but no data ever reported. Likely cause is no solar.";
    9020            0 :             ShowSevereError(state, message);
    9021            0 :             print(state.files.map, "{}\n", message);
    9022            0 :         }
    9023              :     }
    9024           26 : }
    9025              : 
    9026           26 : void CloseDFSFile(EnergyPlusData &state)
    9027              : {
    9028              : 
    9029              :     // SUBROUTINE INFORMATION:
    9030              :     //       AUTHOR         Linda Lawrie
    9031              :     //       DATE WRITTEN   August 2010
    9032              : 
    9033              :     // PURPOSE OF THIS SUBROUTINE:
    9034              :     // Make sure DFSFile is closed at exit time.  Do not rely on operating system to
    9035              :     // take care of it.
    9036              : 
    9037           26 :     state.files.dfs.close();
    9038           26 : }
    9039              : 
    9040            7 : void DayltgSetupAdjZoneListsAndPointers(EnergyPlusData &state)
    9041              : {
    9042              : 
    9043              :     // SUBROUTINE INFORMATION:
    9044              :     //       AUTHOR         Fred Winkelmann
    9045              :     //       DATE WRITTEN   Feb. 2004
    9046              :     //       MODIFIED:      June 2010;LKL - Merged two routines.
    9047              : 
    9048              :     // PURPOSE OF THIS SUBROUTINE:
    9049              :     // For each Daylighting:Detailed enclosure, creates a list of adjacent enclosures,
    9050              :     // that have one or more exterior windows and that share one or more interior
    9051              :     // windows with Z. Used in calculation of daylighting through interior windows.
    9052              : 
    9053              :     // Sets the daylighting factor pointers for each Daylighting:Detailed control. The pointer
    9054              :     // may be associated with an exterior window in a daylit target zone's enclosure or an exterior window in
    9055              :     // an adjacent enclosure, daylit or not, that shares interior windows with the target zone's enclosure.
    9056              : 
    9057              :     // Count number of exterior Windows (use to allocate arrays)
    9058            7 :     auto &dl = state.dataDayltg;
    9059            7 :     auto &s_surf = state.dataSurface;
    9060              : 
    9061           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9062            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9063            9 :         thisEnclDaylight.TotalExtWindows = 0;
    9064              : 
    9065              :         // Count exterior windows in this solar enclosure
    9066           64 :         for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9067           55 :             auto const &surf = s_surf->Surface(surfNum);
    9068           55 :             if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9069           46 :                 surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9070            9 :                 ++thisEnclDaylight.TotalExtWindows;
    9071              :             }
    9072              :         }
    9073              :     } // End of primary enclosure loop
    9074              : 
    9075           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9076            9 :         int NumList = 0;
    9077            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9078            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9079            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9080              :         // This is a Daylighting:Detailed enclosure
    9081              :         // Find adjacent zones/enclosures
    9082           22 :         for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
    9083           13 :             if (adjEnclNum == enclNum) continue;
    9084              :             // Require that adjEnclNum have a least one exterior window
    9085            4 :             bool AdjEnclHasExtWins = false;
    9086           28 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9087           32 :                 if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
    9088            4 :                     (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
    9089            4 :                     AdjEnclHasExtWins = true;
    9090            4 :                     break;
    9091              :                 }
    9092              :             }
    9093            4 :             if (!AdjEnclHasExtWins) continue;
    9094              :             // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to ZoneNum
    9095           32 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9096           28 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9097           28 :                 if ((surfAdj.Class == SurfaceClass::Window) && (surfAdj.ExtBoundCond >= 1)) {
    9098              :                     // This is an interior window in ZoneNumAdj
    9099            0 :                     if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex == enclNum) {
    9100              :                         // This interior window is adjacent to ZoneNum
    9101            0 :                         ++NumList;
    9102            0 :                         break;
    9103              :                     }
    9104              :                 }
    9105              :             }
    9106              :         }
    9107            9 :         thisEnclDaylight.AdjIntWinEnclNums.allocate(NumList);
    9108            9 :         thisEnclDaylight.AdjIntWinEnclNums = 0;
    9109              :     } // End of primary enclosure loop
    9110              : 
    9111           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9112            9 :         int NumList = 0;
    9113            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9114            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9115            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9116              :         // This is a Daylighting:Detailed enclosure
    9117              :         // Find adjacent zones/enclosures
    9118           22 :         for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
    9119           13 :             if (adjEnclNum == enclNum) continue;
    9120              :             // Require that adjEnclNum have a least one exterior window
    9121            4 :             bool AdjEnclHasExtWins = false;
    9122           28 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9123           28 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9124           28 :                 if (surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) {
    9125            4 :                     AdjEnclHasExtWins = true;
    9126            4 :                     break;
    9127              :                 }
    9128              :             }
    9129            4 :             if (!AdjEnclHasExtWins) continue;
    9130              :             // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to enclNum
    9131           32 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9132           28 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9133           28 :                 if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond < 1) continue;
    9134              : 
    9135              :                 // This is an interior window in adjEnclNum
    9136            0 :                 if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex != enclNum) continue;
    9137              : 
    9138              :                 // This interior window is adjacent to ZoneNum
    9139            0 :                 ++NumList;
    9140            0 :                 int enclNumAdj = surfAdj.SolarEnclIndex;
    9141            0 :                 thisEnclDaylight.AdjIntWinEnclNums(NumList) = enclNumAdj;
    9142            0 :                 dl->enclDaylight(enclNumAdj).adjEnclHasDayltgCtrl = true;
    9143            0 :                 break;
    9144              :             }
    9145              :         }
    9146            9 :         thisEnclDaylight.NumOfIntWinAdjEncls = NumList;
    9147              :     } // End of primary enclosure loop
    9148              : 
    9149              :     // now fill out information on relationship between adjacent exterior windows and associated interior windows
    9150           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9151            9 :         auto &enclDayl = dl->enclDaylight(enclNum);
    9152              :         // first find count of exterior windows
    9153            9 :         if (enclDayl.NumOfIntWinAdjEncls <= 0) {
    9154            9 :             enclDayl.NumOfIntWinAdjEnclExtWins = 0;
    9155            9 :             continue;
    9156              :         }
    9157            0 :         for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
    9158            0 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9159            0 :                 if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
    9160            0 :                     (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
    9161            0 :                     ++enclDayl.NumOfIntWinAdjEnclExtWins;
    9162              :                 }
    9163              :             }
    9164              :         }
    9165              :         // now allocate nested struct based on exterior window count
    9166            0 :         enclDayl.IntWinAdjEnclExtWin.allocate(enclDayl.NumOfIntWinAdjEnclExtWins);
    9167              : 
    9168              :         // now fill nested structure
    9169            0 :         int ExtWinIndex = 0;
    9170            0 :         for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
    9171            0 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9172            0 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9173            0 :                 if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond != ExternalEnvironment) continue;
    9174              : 
    9175            0 :                 ++ExtWinIndex;
    9176            0 :                 auto &intWinAdjEnclExtWin = enclDayl.IntWinAdjEnclExtWin(ExtWinIndex);
    9177            0 :                 intWinAdjEnclExtWin.SurfNum = SurfNumAdj;
    9178              : 
    9179              :                 // now count interior windows shared by both zones
    9180            0 :                 int NumOfIntWindowsCount = 0;
    9181            0 :                 for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9182            0 :                     auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
    9183            0 :                     if ((surfAdj2.Class == SurfaceClass::Window) && (surfAdj2.ExtBoundCond >= 1)) {
    9184              :                         // This is an interior window in ZoneNumAdj
    9185            0 :                         if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
    9186              :                             // This interior window is adjacent to ZoneNum and associated with this
    9187            0 :                             ++NumOfIntWindowsCount;
    9188              :                         }
    9189              :                     }
    9190              :                 } // for (SurfNumAdj2)
    9191              : 
    9192              :                 // allocate nested array
    9193            0 :                 intWinAdjEnclExtWin.IntWinNum.allocate(NumOfIntWindowsCount);
    9194            0 :                 intWinAdjEnclExtWin.IntWinNum = 0;
    9195            0 :                 int IntWinIndex = 0;
    9196            0 :                 for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9197            0 :                     auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
    9198            0 :                     if (surfAdj2.Class != SurfaceClass::Window || surfAdj2.ExtBoundCond < 1) continue;
    9199              : 
    9200              :                     // This is an interior window in ZoneNumAdj
    9201            0 :                     if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
    9202              :                         // This interior window is adjacent to ZoneNum and associated with this
    9203            0 :                         intWinAdjEnclExtWin.IntWinNum(++IntWinIndex) = SurfNumAdj2;
    9204              :                     }
    9205              :                 } // for (SurfNumAdj2)
    9206              :             }     // for (SurfNumAdj)
    9207              :         }         // for (adjEnclNum)
    9208              :     }             // End of primary enclosure loop
    9209              : 
    9210            7 :     Array1D_int enclExtWin;
    9211            7 :     enclExtWin.dimension(state.dataViewFactor->NumOfSolarEnclosures, 0);
    9212              : 
    9213           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9214            9 :         enclExtWin(enclNum) = 0;
    9215            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9216            9 :         auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9217            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9218              :         // This is a Daylighting:Detailed zone
    9219              : 
    9220              :         // Get exterior windows in this solar enclosure
    9221           64 :         for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9222           55 :             auto const &surf = s_surf->Surface(surfNum);
    9223           55 :             if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9224           46 :                 surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9225            9 :                 ++enclExtWin(enclNum);
    9226              :             }
    9227              :         }
    9228              : 
    9229              :         // Get exterior windows in adjacent enclosures that share interior windows with enclNum
    9230            9 :         if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
    9231              : 
    9232            0 :         for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9233              :             // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
    9234              :             // it would not be an "AdjIntWinEncl"
    9235            0 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9236            0 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9237            0 :                 if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
    9238            0 :                     surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9239            0 :                     ++enclExtWin(enclNum);
    9240              :                 }
    9241              :             }
    9242              :         } // for (adjEnclNum)
    9243              :     }     // for (enclNum)
    9244              : 
    9245            7 :     dl->maxShadeDeployOrderExtWins = 0;
    9246           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9247            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9248            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9249            9 :         thisEnclDaylight.NumOfDayltgExtWins = 0;
    9250            9 :         int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
    9251            9 :         if (thisEnclNumRefPoints > 0) {
    9252              :             // This is a Daylighting:Detailed enclosure
    9253              : 
    9254              :             // Get exterior windows in this enclosure
    9255            9 :             if (enclExtWin(enclNum) == 0) continue;
    9256            9 :             thisEnclDaylight.DayltgExtWinSurfNums.allocate(enclExtWin(enclNum));
    9257            9 :             thisEnclDaylight.DayltgExtWinSurfNums = 0;
    9258           18 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9259            9 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9260            9 :                 thisDayltgCtrl.MapShdOrdToLoopNum.allocate(enclExtWin(enclNum));
    9261            9 :                 thisDayltgCtrl.MapShdOrdToLoopNum = 0;
    9262              : 
    9263            9 :                 assert((int)thisDayltgCtrl.refPts.size() == thisDayltgCtrl.TotalDaylRefPoints);
    9264           23 :                 for (auto &refPt : thisDayltgCtrl.refPts) {
    9265           14 :                     refPt.extWins.allocate(enclExtWin(enclNum));
    9266           28 :                     for (auto &extWin : refPt.extWins) {
    9267           14 :                         new (&extWin) DaylRefPtExtWin();
    9268              :                     }
    9269              :                 }
    9270              :             }
    9271              : 
    9272            9 :             int enclExtWinCtr = 0;
    9273              : 
    9274           64 :             for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9275           55 :                 auto const &surf = s_surf->Surface(surfNum);
    9276           55 :                 if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9277           46 :                     surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9278            9 :                     ++enclExtWinCtr;
    9279            9 :                     thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = surfNum;
    9280              :                 }
    9281              :             }
    9282              : 
    9283              :             // Get exterior windows in adjacent enclosures that share interior windows with enclNum
    9284            9 :             if (thisEnclDaylight.NumOfIntWinAdjEncls > 0) {
    9285            0 :                 for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9286              :                     // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
    9287              :                     // it would not be an "AdjIntWinEncl"
    9288            0 :                     for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9289            0 :                         auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9290            0 :                         if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
    9291            0 :                             surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9292            0 :                             ++enclExtWinCtr;
    9293            0 :                             thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = SurfNumAdj;
    9294              : 
    9295            0 :                             auto &surfWinAdj = s_surf->SurfaceWindow(SurfNumAdj);
    9296              :                             // If no daylighting in the adjacent enclosure, set up variables anyway:
    9297            0 :                             if (state.dataViewFactor->EnclSolInfo(adjEnclNum).TotalEnclosureDaylRefPoints == 0 &&
    9298            0 :                                 !s_surf->SurfWinSurfDayLightInit(SurfNumAdj)) {
    9299            0 :                                 surfWinAdj.refPts.allocate(thisEnclNumRefPoints);
    9300            0 :                                 for (auto &refPt : surfWinAdj.refPts) {
    9301            0 :                                     new (&refPt) SurfaceWindowRefPt();
    9302              :                                 }
    9303            0 :                                 s_surf->SurfWinSurfDayLightInit(SurfNumAdj) = true;
    9304              :                             }
    9305              :                         }
    9306              :                     } // for (SurfNumAdj)
    9307              :                 }     // for (adjEnclNum)
    9308              :             }         // if (thisEnclDaylight.NumOfIntWinAdjEncls > 0)
    9309              : 
    9310            9 :             thisEnclDaylight.NumOfDayltgExtWins = enclExtWin(enclNum);
    9311            9 :             int winSize = enclExtWin(enclNum);
    9312           18 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9313            9 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9314            9 :                 int refSize = thisDayltgCtrl.TotalDaylRefPoints;
    9315          225 :                 for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
    9316          216 :                     thisDayltgCtrl.daylFac[iHr].allocate(winSize, refSize);
    9317              :                 }
    9318              :             }
    9319              :         } // if (thisEncl.NumOfRefPoints > 0)
    9320              : 
    9321            9 :         if (s_surf->TotWinShadingControl > 0) {
    9322            1 :             CreateShadeDeploymentOrder(state, enclNum);
    9323              :         }
    9324              :     } // for (enclNum)
    9325              : 
    9326           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9327            9 :         auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9328            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9329            9 :         int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
    9330            9 :         if (thisEnclNumRefPoints > 0) {
    9331            9 :             if (s_surf->TotWinShadingControl > 0) {
    9332            1 :                 MapShadeDeploymentOrderToLoopNumber(state, enclNum);
    9333              :             }
    9334              :         }
    9335              :     }
    9336              : 
    9337            8 :     for (auto &illumMap : dl->illumMaps) {
    9338            1 :         assert((int)illumMap.refPts.size() == illumMap.TotalMapRefPoints);
    9339            1 :         if (illumMap.TotalMapRefPoints == 0) continue;
    9340              : 
    9341            1 :         int numExtWin = enclExtWin(illumMap.enclIndex);
    9342            1 :         if (numExtWin == 0) continue;
    9343              : 
    9344          101 :         for (auto &refPt : illumMap.refPts) {
    9345          100 :             refPt.winLums.allocate(numExtWin);
    9346          200 :             for (auto &winLums : refPt.winLums) {
    9347          100 :                 winLums = {0.0, 0.0};
    9348              :             }
    9349              :         }
    9350              : 
    9351           25 :         for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
    9352           24 :             illumMap.daylFac[iHr].allocate(numExtWin, illumMap.TotalMapRefPoints);
    9353              :         }
    9354              : 
    9355              :     } // End of map loop
    9356              : 
    9357            7 :     dl->dirIllum.allocate(Constant::iHoursInDay);
    9358            7 :     dl->reflIllum.allocate(Constant::iHoursInDay);
    9359            7 :     dl->winLum.allocate(Constant::iHoursInDay);
    9360            7 :     dl->avgWinLum.allocate(Constant::iHoursInDay);
    9361              : 
    9362              :     static constexpr std::string_view Format_700("! <Enclosure/Window Adjacency Daylighting Counts>, Enclosure Name, Number of Exterior Windows, "
    9363              :                                                  "Number of Exterior Windows in Adjacent Enclosures\n");
    9364            7 :     print(state.files.eio, Format_700);
    9365           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9366            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9367            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9368            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9369              :         static constexpr std::string_view Format_701("Enclosure/Window Adjacency Daylighting Counts, {},{},{}\n");
    9370            9 :         print(state.files.eio,
    9371              :               Format_701,
    9372            9 :               state.dataViewFactor->EnclSolInfo(enclNum).Name,
    9373            9 :               thisEnclDaylight.TotalExtWindows,
    9374           18 :               (thisEnclDaylight.NumOfDayltgExtWins - thisEnclDaylight.TotalExtWindows));
    9375              :     }
    9376              :     static constexpr std::string_view Format_702(
    9377              :         "! <Enclosure/Window Adjacency Daylighting Matrix>, Enclosure Name, Number of Adjacent Enclosures with Windows,Adjacent "
    9378              :         "Enclosure Names - 1st 100 (max)\n");
    9379            7 :     print(state.files.eio, Format_702);
    9380           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9381            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9382            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
    9383            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9384              :         static constexpr std::string_view Format_703("Enclosure/Window Adjacency Daylighting Matrix, {},{}");
    9385            9 :         print(state.files.eio, Format_703, state.dataViewFactor->EnclSolInfo(enclNum).Name, thisEnclDaylight.NumOfIntWinAdjEncls);
    9386            9 :         for (int loop = 1, loop_end = min(thisEnclDaylight.NumOfIntWinAdjEncls, 100); loop <= loop_end; ++loop) {
    9387            0 :             print(state.files.eio, ",{}", state.dataViewFactor->EnclSolInfo(thisEnclDaylight.AdjIntWinEnclNums(loop)).Name);
    9388              :         }
    9389            9 :         print(state.files.eio, "\n");
    9390              :     }
    9391              : 
    9392            7 :     enclExtWin.deallocate();
    9393            7 : }
    9394              : 
    9395            3 : void CreateShadeDeploymentOrder(EnergyPlusData &state, int const enclNum)
    9396              : {
    9397              :     // J. Glazer - 2018
    9398              :     // create sorted list for shade deployment order
    9399              :     // first step is to create a sortable list of WindowShadingControl objects by sequence
    9400            3 :     auto &dl = state.dataDayltg;
    9401            3 :     auto &s_surf = state.dataSurface;
    9402              : 
    9403            3 :     std::vector<std::pair<int, int>> shadeControlSequence; // sequence, WindowShadingControl
    9404           10 :     for (int iShadeCtrl = 1; iShadeCtrl <= s_surf->TotWinShadingControl; ++iShadeCtrl) {
    9405            7 :         auto &winShadeControl = s_surf->WindowShadingControl(iShadeCtrl);
    9406            7 :         for (int spaceNum : state.dataHeatBal->Zone(winShadeControl.ZoneIndex).spaceIndexes) {
    9407            7 :             int shadeCtrlEnclNum = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    9408            7 :             if (shadeCtrlEnclNum == enclNum) {
    9409            7 :                 shadeControlSequence.push_back(std::make_pair(winShadeControl.SequenceNumber, iShadeCtrl));
    9410            7 :                 break;
    9411              :             }
    9412              :         }
    9413              :     }
    9414              :     // sort the WindowShadingControl objects based on sequence number
    9415            3 :     sort(shadeControlSequence.begin(), shadeControlSequence.end());
    9416              :     // now make the deployment list of lists.
    9417              :     // each sublist is a group of surfaces that should be deployed together
    9418              :     // often the sublist is just a single item.
    9419            3 :     dl->maxShadeDeployOrderExtWins = 0;
    9420            6 :     for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    9421            3 :         auto &thisDaylightCtrl = dl->daylightControl(controlNum);
    9422           10 :         for (auto sequence : shadeControlSequence) { // This is an iterator (THIS_AUTO_OK)
    9423            7 :             int curShadeControlNum = sequence.second;
    9424            7 :             auto const &winShadeControl = s_surf->WindowShadingControl(curShadeControlNum);
    9425            7 :             if (winShadeControl.multiSurfaceControl == MultiSurfaceControl::Group) {
    9426              :                 // add a group of surfaces since they should be deployed as a group
    9427            5 :                 std::vector<int> group;
    9428           16 :                 for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
    9429           11 :                     group.push_back(winShadeControl.FenestrationIndex(i));
    9430              :                 }
    9431            5 :                 thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(group);
    9432            5 :             } else {
    9433              :                 // add each individual surface as a separate list so they are deployed individually
    9434           10 :                 for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
    9435            8 :                     std::vector<int> singleMemberVector;
    9436            8 :                     singleMemberVector.push_back(winShadeControl.FenestrationIndex(i));
    9437            8 :                     thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(singleMemberVector);
    9438            8 :                 }
    9439              :             }
    9440              :         }
    9441            3 :         dl->maxShadeDeployOrderExtWins = max(dl->maxShadeDeployOrderExtWins, (int)thisDaylightCtrl.ShadeDeployOrderExtWins.size());
    9442              :     }
    9443              : 
    9444            3 :     dl->maxDayltgExtWins = 0;
    9445            6 :     for (auto const &enclDayl : dl->enclDaylight) {
    9446            3 :         dl->maxDayltgExtWins = max(dl->maxDayltgExtWins, enclDayl.NumOfDayltgExtWins);
    9447              :     }
    9448              : 
    9449            3 : } // CreateShadeDeploymentOrder()
    9450              : 
    9451            2 : void MapShadeDeploymentOrderToLoopNumber(EnergyPlusData &state, int const enclNum)
    9452              : {
    9453              :     // J. Glazer - 2018
    9454              :     // Allow a way to map back to the original "loop" index that is used in many other places in the
    9455              :     // ZoneDayLight data structure when traversing the list in the order of the window shaded deployment
    9456            2 :     auto &dl = state.dataDayltg;
    9457            2 :     auto &s_surf = state.dataSurface;
    9458              : 
    9459            2 :     auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9460            2 :     auto const &thisEnclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    9461              : 
    9462            2 :     if (thisEnclSol.TotalEnclosureDaylRefPoints == 0 || thisEnclDaylight.NumOfDayltgExtWins == 0) return;
    9463              : 
    9464            4 :     for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9465            2 :         auto &thisDaylightCtrl = dl->daylightControl(controlNum);
    9466            2 :         if (thisDaylightCtrl.ShadeDeployOrderExtWins.size() == 0) continue;
    9467              : 
    9468            2 :         int count = 0;
    9469            2 :         bool showOnce = true;
    9470            9 :         for (auto const &listOfExtWin : thisDaylightCtrl.ShadeDeployOrderExtWins) {
    9471           17 :             for (int IWinShdOrd : listOfExtWin) {
    9472           10 :                 ++count;
    9473           10 :                 if (count > thisEnclDaylight.NumOfDayltgExtWins) {
    9474            0 :                     if (showOnce) {
    9475            0 :                         ShowWarningError(
    9476              :                             state,
    9477            0 :                             format("MapShadeDeploymentOrderToLoopNumber: too many controlled shaded windows in enclosure {}", thisEnclSol.Name));
    9478            0 :                         ShowContinueError(state,
    9479              :                                           "Check the Zone Name in the WindowShadingControl that references the following fenestration surfaces:");
    9480            0 :                         showOnce = false;
    9481              :                     }
    9482            0 :                     ShowContinueError(state, format("  -  {}", s_surf->Surface(IWinShdOrd).Name));
    9483              :                 }
    9484           46 :                 for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    9485           46 :                     int IWinLoop = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    9486           46 :                     if (IWinShdOrd == IWinLoop) {
    9487           10 :                         thisDaylightCtrl.MapShdOrdToLoopNum(count) = loop;
    9488           10 :                         break;
    9489              :                     }
    9490              :                 }
    9491              :             }
    9492              :         } // for (listOfExtWin)
    9493              :     }     // for (controlNum)
    9494              : } // MapShadeDeploymentOrderToLoopNumber()
    9495              : 
    9496            0 : void DayltgInterReflIllFrIntWins(EnergyPlusData &state, int const enclNum)
    9497              : {
    9498              : 
    9499              :     // SUBROUTINE INFORMATION:
    9500              :     //       AUTHOR         Fred Winkelmann
    9501              :     //       DATE WRITTEN   Mar. 2004
    9502              : 
    9503              :     // PURPOSE OF THIS SUBROUTINE:
    9504              :     // Calculates the inter-reflected illuminance in a daylit zone from beam
    9505              :     // and diffuse daylight entering the zone through interior windows. This illuminance
    9506              :     // is determined by the split-flux method and is assumed to be uniform, i.e., the same
    9507              :     // at all reference points.
    9508              : 
    9509            0 :     auto &dl = state.dataDayltg;
    9510            0 :     auto &s_surf = state.dataSurface;
    9511              : 
    9512            0 :     auto &enclDayl = dl->enclDaylight(enclNum);
    9513            0 :     auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    9514              : 
    9515            0 :     enclDayl.InterReflIllFrIntWins = 0.0;
    9516              : 
    9517            0 :     for (int const IWin : enclSol.SurfacePtr) {
    9518            0 :         auto &surf = s_surf->Surface(IWin);
    9519            0 :         if (surf.Class != SurfaceClass::Window || surf.ExtBoundCond < 1) continue;
    9520            0 :         auto const &surfWin = s_surf->SurfaceWindow(IWin);
    9521              :         // This is an interior window in ZoneNum
    9522            0 :         int const ConstrNum = surf.Construction;
    9523            0 :         int const adjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
    9524              :         // Luminous flux transmitted through an int win from adjacent zone's enclosure (lumens)
    9525            0 :         Real64 QDifTrans = state.dataHeatBal->EnclSolQSDifSol(adjEnclNum) * state.dataConstruction->Construct(ConstrNum).TransDiffVis * surf.Area *
    9526            0 :                            state.dataEnvrn->PDIFLW;
    9527            0 :         Real64 QDifTransUp = QDifTrans * surfWin.fractionUpgoing;         // Upgoing part of QDifTrans (lumens)
    9528            0 :         Real64 QDifTransDn = QDifTrans * (1.0 - surfWin.fractionUpgoing); // Downgoing part of QDifTrans (lumens)
    9529            0 :         if (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect) != 0.0) {
    9530            0 :             enclDayl.InterReflIllFrIntWins += (QDifTransDn * surfWin.rhoFloorWall + QDifTransUp * surfWin.rhoCeilingWall) /
    9531            0 :                                               (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
    9532              :         }
    9533              :     } // for (iWin)
    9534              : 
    9535              :     // Add inter-reflected illuminance from beam solar entering enclosure through interior windows
    9536              :     // TH, CR 7873, 9/17/2009
    9537            0 :     if (dl->enclDaylight(enclNum).totInsSurfArea > 0) {
    9538            0 :         enclDayl.InterReflIllFrIntWins +=
    9539            0 :             (state.dataHeatBal->EnclSolDBIntWin(enclNum) * state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW * enclDayl.floorVisRefl) /
    9540            0 :             (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
    9541              :     }
    9542            0 : } // DayltgInterReflIllFrIntWins()
    9543              : 
    9544            5 : void CalcMinIntWinSolidAngs(EnergyPlusData &state)
    9545              : {
    9546              : 
    9547              :     // SUBROUTINE INFORMATION:
    9548              :     //       AUTHOR         Fred Winkelmann
    9549              :     //       DATE WRITTEN   Feb. 2004
    9550              : 
    9551              :     // PURPOSE OF THIS SUBROUTINE:
    9552              :     // For each Daylighting:Detailed zone finds the minimum solid angle subtended
    9553              :     // by interior windows through which daylight can pass from adjacent zones with
    9554              :     // exterior windows.
    9555              : 
    9556            5 :     auto &dl = state.dataDayltg;
    9557            5 :     auto &s_surf = state.dataSurface;
    9558              : 
    9559           12 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9560            7 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9561            7 :         thisEnclDaylight.MinIntWinSolidAng = 2.0 * Constant::Pi;
    9562            7 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
    9563            7 :         if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
    9564              : 
    9565            0 :         for (int IWin : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9566            0 :             auto const &surf = s_surf->Surface(IWin);
    9567              : 
    9568            0 :             if ((surf.Class != SurfaceClass::Window) || (surf.ExtBoundCond < 1)) continue;
    9569              : 
    9570              :             // This is an interior window in enclNum
    9571            0 :             int const winAdjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
    9572            0 :             bool IntWinNextToIntWinAdjZone = false; // True if an interior window is next to a zone with one or more exterior windows
    9573            0 :             for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9574            0 :                 if (winAdjEnclNum == adjEnclNum) {
    9575            0 :                     IntWinNextToIntWinAdjZone = true;
    9576            0 :                     break;
    9577              :                 }
    9578              :             }
    9579              : 
    9580            0 :             if (!IntWinNextToIntWinAdjZone) continue;
    9581              : 
    9582            0 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9583            0 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9584            0 :                 for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
    9585              :                     // Reference point in absolute coordinate system
    9586            0 :                     Vector3<Real64> RREF = thisDayltgCtrl.refPts(IL).absCoords;
    9587            0 :                     bool is_Triangle = (surf.Sides == 3);
    9588            0 :                     bool is_Rectangle = (surf.Sides == 4);
    9589              : 
    9590            0 :                     Vector3<Real64> W1, W2, W3;
    9591            0 :                     if (is_Rectangle) {
    9592              :                         // Vertices of window numbered counter-clockwise starting at upper left as viewed
    9593              :                         // from inside of room. Assumes original vertices are numbered counter-clockwise from
    9594              :                         // upper left as viewed from outside.
    9595            0 :                         W3 = surf.Vertex(2);
    9596            0 :                         W2 = surf.Vertex(3);
    9597            0 :                         W1 = surf.Vertex(4);
    9598            0 :                     } else if (is_Triangle) {
    9599            0 :                         W3 = surf.Vertex(2);
    9600            0 :                         W2 = surf.Vertex(3);
    9601            0 :                         W1 = surf.Vertex(1);
    9602              :                     }
    9603              :                     // Unit vectors from window vertex 2 to 1 and 2 to 3, center point of window,
    9604              :                     // and vector from ref pt to center of window
    9605            0 :                     Vector3<Real64> W21 = W1 - W2;
    9606            0 :                     Vector3<Real64> W23 = W3 - W2;
    9607            0 :                     Real64 HW = W21.magnitude();
    9608            0 :                     Real64 WW = W23.magnitude();
    9609            0 :                     Vector3<Real64> WC = (is_Rectangle) ? (W2 + (W23 + W21) / 2.0) : (W2 + (W23 + W21) / 3.0);
    9610              : 
    9611              :                     // Vector from ref point to center of window
    9612            0 :                     Vector3<Real64> REFWC = WC - RREF;
    9613            0 :                     W21 /= HW;
    9614            0 :                     W23 /= WW;
    9615              :                     // Unit vector normal to window (pointing away from room)
    9616            0 :                     Vector3<Real64> WNORM = surf.OutNormVec;
    9617              :                     // Distance from ref point to center of window
    9618            0 :                     Real64 DIS = REFWC.magnitude();
    9619              :                     // Unit vector from ref point to center of window
    9620            0 :                     Vector3<Real64> Ray = REFWC / DIS;
    9621              :                     // Cosine of angle between ray from ref pt to center of window and window outward normal
    9622            0 :                     Real64 COSB = dot(WNORM, Ray);
    9623            0 :                     if (COSB > 0.01765) { // 0 <= B < 89 deg
    9624              :                         // Above test avoids case where ref point cannot receive daylight directly from the
    9625              :                         // interior window
    9626            0 :                         Real64 IntWinSolidAng = COSB * surf.Area / (pow_2(DIS) + 0.001);
    9627            0 :                         thisEnclDaylight.MinIntWinSolidAng = min(thisEnclDaylight.MinIntWinSolidAng, IntWinSolidAng);
    9628              :                     }
    9629            0 :                 } // for (IL)
    9630              :             }     // for (controlNum)
    9631              :         }         // for (IWin)
    9632              :     }             // for (enclNum)
    9633            5 : }
    9634              : 
    9635           19 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
    9636              : {
    9637              : 
    9638              :     // SUBROUTINE INFORMATION:
    9639              :     //       AUTHOR         Linda Lawrie
    9640              :     //       DATE WRITTEN   February 2009
    9641              : 
    9642              :     // PURPOSE OF THIS SUBROUTINE:
    9643              :     // check for geometrytransform in the daylighting access for reference and map points
    9644              : 
    9645              :     // METHODOLOGY EMPLOYED:
    9646              :     // once reference points  have been converted to WCS,
    9647              :     //  change them to reflect a different aspect
    9648              :     // ratio for the entire building based on user input.
    9649              : 
    9650              :     // SUBROUTINE PARAMETER DEFINITIONS:
    9651              :     static constexpr std::string_view CurrentModuleObject = "GeometryTransform";
    9652              : 
    9653              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    9654           19 :     Array1D_string cAlphas(1);
    9655           19 :     Array1D<Real64> rNumerics;
    9656              : 
    9657              :     // begin execution
    9658              :     // get user input...
    9659           19 :     doTransform = false;
    9660           19 :     OldAspectRatio = 1.0;
    9661           19 :     NewAspectRatio = 1.0;
    9662              : 
    9663           19 :     auto &ip = state.dataInputProcessing->inputProcessor;
    9664           19 :     auto const &s_ipsc = state.dataIPShortCut;
    9665           19 :     auto &s_surf = state.dataSurface;
    9666              : 
    9667           19 :     if (ip->getNumObjectsFound(state, CurrentModuleObject) == 1) {
    9668              :         int NAlphas;
    9669              :         int NNum;
    9670              :         int IOStat;
    9671            0 :         ip->getObjectItem(state,
    9672              :                           CurrentModuleObject,
    9673              :                           1,
    9674              :                           cAlphas,
    9675              :                           NAlphas,
    9676              :                           rNumerics,
    9677              :                           NNum,
    9678              :                           IOStat,
    9679            0 :                           s_ipsc->lNumericFieldBlanks,
    9680            0 :                           s_ipsc->lAlphaFieldBlanks,
    9681            0 :                           s_ipsc->cAlphaFieldNames,
    9682            0 :                           s_ipsc->cNumericFieldNames);
    9683            0 :         OldAspectRatio = rNumerics(1);
    9684            0 :         NewAspectRatio = rNumerics(2);
    9685            0 :         std::string transformPlane = cAlphas(1);
    9686            0 :         if (transformPlane != "XY") {
    9687            0 :             ShowWarningError(state, format("{}: invalid {}=\"{}...ignored.", CurrentModuleObject, s_ipsc->cAlphaFieldNames(1), cAlphas(1)));
    9688              :         }
    9689            0 :         doTransform = true;
    9690            0 :         s_surf->AspectTransform = true;
    9691            0 :     }
    9692           19 :     if (s_surf->WorldCoordSystem) {
    9693            0 :         doTransform = false;
    9694            0 :         s_surf->AspectTransform = false;
    9695              :     }
    9696           19 : }
    9697              : 
    9698            1 : void WriteDaylightMapTitle(EnergyPlusData &state,
    9699              :                            int const mapNum,
    9700              :                            InputOutputFile &mapFile,
    9701              :                            std::string const &mapName,
    9702              :                            std::string const &environmentName,
    9703              :                            int const ZoneNum,
    9704              :                            std::string const &refPts,
    9705              :                            Real64 const zcoord)
    9706              : {
    9707              :     // SUBROUTINE INFORMATION:
    9708              :     //       AUTHOR         Greg Stark
    9709              :     //       DATE WRITTEN   Sept 2008
    9710              : 
    9711              :     // PURPOSE OF THIS SUBROUTINE:
    9712              :     // The purpose of the routine is to allow the daylighting map data to be written in various formats
    9713              : 
    9714              :     // must add correct number of commas at end
    9715            1 :     auto &dl = state.dataDayltg;
    9716              : 
    9717            1 :     std::string fullmapName = fmt::format("{}:{}:{} Illuminance [lux] (Hourly)", state.dataHeatBal->Zone(ZoneNum).Name, environmentName, mapName);
    9718            1 :     print(mapFile, "Date/Time{}{}{}{}{}{}\n", dl->MapColSep, fullmapName, dl->MapColSep, refPts, dl->MapColSep, dl->MapColSep);
    9719              : 
    9720            1 :     if (state.dataSQLiteProcedures->sqlite) {
    9721            0 :         state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMapTitle(mapNum, fullmapName, environmentName, ZoneNum, refPts, zcoord);
    9722              :     }
    9723            1 : } // WritDaylightMapTitle()
    9724              : 
    9725              : } // namespace EnergyPlus::Dayltg
        

Generated by: LCOV version 2.0-1