LCOV - code coverage report
Current view: top level - EnergyPlus - DaylightingManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 45.1 % 4821 2174
Test Date: 2025-06-02 12:03:30 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) {
     217           29 :                 fwc = FWC::Wall; // Wall
     218              :             }
     219           41 :             if (surf.Tilt >= 170.0) {
     220            7 :                 fwc = FWC::Floor; // Floor
     221              :             }
     222           41 :             AR[(int)fwc] += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
     223           41 :                             s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
     224           41 :             ARH[(int)fwc] +=
     225           41 :                 AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
     226           41 :                 s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
     227           41 :                 s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
     228              :         }
     229              :     }
     230              : 
     231              :     // Average inside surface reflectance of enclosure
     232            7 :     if (AInsTot <= 0.0) {
     233            0 :         ShowSevereError(state, format("DayltgAveInteriorReflectance: Total opaque surface area is <=0.0 in solar enclosure={}", thisEnclosure.Name));
     234            0 :         ShowFatalError(state, "Program terminates due to preceding conditions.");
     235              :     }
     236            7 :     dl->enclDaylight(enclNum).aveVisDiffReflect = ARHTOT / AInsTot;
     237              :     // Total inside surface area of enclosure
     238            7 :     dl->enclDaylight(enclNum).totInsSurfArea = AInsTot;
     239              :     // Average floor visible reflectance
     240            7 :     dl->enclDaylight(enclNum).floorVisRefl = ARH[iFWC_Ceiling] / (AR[iFWC_Ceiling] + 1.e-6);
     241              : 
     242           48 :     for (int ISurf : thisEnclosure.SurfacePtr) {
     243           41 :         auto const &surf = s_surf->Surface(ISurf);
     244           41 :         if (surf.Class != SurfaceClass::Wall && surf.Class != SurfaceClass::Floor && surf.Class != SurfaceClass::Roof) {
     245            7 :             continue;
     246              :         }
     247              : 
     248              :         // Remove this surface from the space inside surface area and area*reflectivity
     249              :         // The resulting areas are AP(ITILT). The resulting area*reflectivity is ARHP(ITILT).
     250              :         // Initialize gross area of surface (including subsurfaces)
     251           34 :         Real64 ATWL = surf.Area; // This is the surface area less subsurfaces
     252              :         // Area * reflectance for this surface, excluding attached windows and doors
     253           34 :         Real64 ARHTWL = surf.Area * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack;
     254              : 
     255           34 :         FWC fwc = (surf.Tilt > 45.0 && surf.Tilt < 135.0) ? FWC::Wall : ((surf.Tilt >= 135.0) ? FWC::Floor : FWC::Ceiling);
     256              : 
     257              :         // Loop over windows and doors on this wall
     258          256 :         for (int IWinDr : thisEnclosure.SurfacePtr) {
     259          222 :             auto const &surfWinDr = s_surf->Surface(IWinDr);
     260          222 :             if ((surfWinDr.Class != SurfaceClass::Window && surfWinDr.Class != SurfaceClass::Door) || surfWinDr.BaseSurf != ISurf) {
     261          215 :                 continue;
     262              :             }
     263              : 
     264            7 :             ATWL += surfWinDr.Area + s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) +
     265            7 :                     s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr));
     266            7 :             ARHTWL +=
     267            7 :                 surfWinDr.Area * state.dataConstruction->Construct(surfWinDr.Construction).ReflectVisDiffBack +
     268            7 :                 s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(IWinDr)) +
     269            7 :                 s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(IWinDr));
     270              :         }
     271              : 
     272              :         std::array<Real64, (int)FWC::Num> AP;
     273              :         std::array<Real64, (int)FWC::Num> ARHP;
     274              :         // Inside surface area of floor, walls and ceilings, minus surface ISurf and its subsurfaces
     275          136 :         for (int iFWC = iFWC_Floor; iFWC < (int)FWC::Num; ++iFWC) {
     276          102 :             if (iFWC == (int)fwc) {
     277           34 :                 AP[iFWC] = AR[iFWC] - ATWL;
     278           34 :                 ARHP[iFWC] = ARH[iFWC] - ARHTWL;
     279              :             } else {
     280           68 :                 AP[iFWC] = AR[iFWC];
     281           68 :                 ARHP[iFWC] = ARH[iFWC];
     282              :             }
     283              :         }
     284           34 :         s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf = AP;
     285           34 :         s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf = ARHP;
     286              :     } // for (ISurf)
     287              : 
     288           48 :     for (int IWin : thisEnclosure.SurfacePtr) {
     289           41 :         auto const &surf = s_surf->Surface(IWin);
     290           41 :         if (surf.Class != SurfaceClass::Window) {
     291           34 :             continue;
     292              :         }
     293              : 
     294            7 :         auto &surfWin = s_surf->SurfaceWindow(IWin);
     295            7 :         auto const &zone = state.dataHeatBal->Zone(surf.Zone);
     296            7 :         int ISurf = surf.BaseSurf;
     297              : 
     298              :         // Ratio of floor-to-window-center height and average floor-to-ceiling height
     299            7 :         Real64 ETA = max(0.0, min(1.0, (surfWin.WinCenter.z - zone.OriginZ) * zone.FloorArea / zone.Volume));
     300              : 
     301            7 :         std::array<Real64, (int)FWC::Num> AP = s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf;
     302            7 :         std::array<Real64, (int)FWC::Num> ARHP = s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf;
     303              :         // Average reflectance seen by light moving up (RhoCeilingWall) and down (RhoFloorWall)
     304              :         // across horizontal plane through center of window
     305            7 :         surfWin.rhoCeilingWall = (ARHP[iFWC_Wall] * (1.0 - ETA) + ARHP[iFWC_Ceiling]) / (AP[iFWC_Wall] * (1.0 - ETA) + AP[iFWC_Ceiling] + 1.0e-5);
     306            7 :         surfWin.rhoFloorWall = (ARHP[iFWC_Wall] * ETA + ARHP[iFWC_Floor]) / (AP[iFWC_Wall] * ETA + AP[iFWC_Floor] + 1.e-9);
     307              : 
     308              :         // Angle factor for windows with diffusing shades. SurfaceWindow(IWin)%FractionUpgoing is
     309              :         // fraction of light from the shade that goes up toward ceiling and upper part of walls.
     310              :         // 1 - SurfaceWindow(IWin)%FractionUpgoing is fraction that goes down toward floor and lower part of walls.
     311            7 :         surfWin.fractionUpgoing = surf.Tilt / 180.0;
     312              : 
     313              :         // Daylighting shelf simplification:  All light goes up to the ceiling regardless of orientation of shelf
     314            7 :         if (s_surf->SurfDaylightingShelfInd(IWin) > 0) {
     315            0 :             if (state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf > 0) {
     316            0 :                 surfWin.fractionUpgoing = 1.0;
     317              :             }
     318              :         }
     319              :     } // for (IWin)
     320            7 : } // DayltgAveInteriorReflectance()
     321              : 
     322         1839 : void CalcDayltgCoefficients(EnergyPlusData &state)
     323              : {
     324              : 
     325              :     // SUBROUTINE INFORMATION:
     326              :     //       AUTHOR         Fred Winkelmann
     327              :     //       DATE WRITTEN   July 1997
     328              :     //       MODIFIED       FW, Jan 2002: add variable slat angle blinds
     329              :     //                      FW, Mar 2002: add triangular windows
     330              :     //                      FW, Oct 2002: remove warning on window discretization relative to
     331              :     //                                    reference point distance to window plane
     332              :     //                      FW, Jan 2003: add between-glass shades and blinds
     333              :     //                      FW, Apr 2003: initialize shading type to 'NOSHADE' in window loop
     334              :     //                      PE, May 2003: add light pipes (tubular daylighting devices)
     335              :     //                      FW, Jul 2003: account for possible non-zero transmittance of
     336              :     //                                    shading surfaces (previously all shading surfaces were
     337              :     //                                    assumed to be opaque)
     338              :     //                      PE, Aug 2003: add daylighting shelves
     339              :     //                      FW, Sep 2003: write the bare-window overcast sky daylight factors to the eio file
     340              :     //                      FW, Nov 2003: add exterior beam and sky solar diffuse reflection from obstructions;
     341              :     //                                    add beam solar and sky solar reflection from ground with obstructions.
     342              :     //                      FW, Nov 2003: change expression for NDIVX, NDIVY (no. of window elements in X,Y) to
     343              :     //                                    round up to nearest integer rather than down
     344              :     //                      FW, Nov 2003: add specular reflection of beam solar from obstructions
     345              :     //                      RJH, Jan 2004: add alternative daylighting analysis using DElight
     346              :     //                                     All modifications demarked with RJH (Rob Hitchcock)
     347              :     //                      FW, Feb 2004: add daylighting through interior windows
     348              :     //                      FW, Apr 2004: add light well efficiency that multiplies glazing transmittance
     349              :     //                      FW, Apr 2004: add diffusing glazing
     350              :     //                      RJH, Jul 2004: add error handling for warnings/errors returned from DElight
     351              :     //                      LKL, Oct 2004: Separate "map" and "ref" point calculations -- move some input routines to
     352              :     //                                     separate routines.
     353              : 
     354              :     // PURPOSE OF THIS SUBROUTINE:
     355              :     // Calculates daylighting factors for later use in the time-step loop.
     356              : 
     357              :     // METHODOLOGY EMPLOYED:
     358              : 
     359              :     // For each combination of exterior window and reference point in a zone,
     360              :     // calculates daylighting factors (interior illuminance / exterior illuminance)
     361              :     // and glare factors for clear and overcast skies and for windows with and
     362              :     // without shading devices. These factors are calculated for each hourly
     363              :     // sun position for design days and for selected days throughout the year.
     364              : 
     365              :     // If a target zone has one or more interior windows, also calculates daylighting
     366              :     // factors for the target zone that are associated with exterior windows in adjacent
     367              :     // zones that share interior windows with the target zone.
     368              : 
     369              :     // The daylight illuminance at a reference point from a window is determined
     370              :     // by dividing the window into rectangular elements and calculating the illuminance
     371              :     // reaching the reference point directly from each element. The illumination
     372              :     // from an element can come from the sky or ground if the window is unshaded, or from
     373              :     // a shading device illuminated by solar radiation. Also considered are the
     374              :     // illuminance contribution from interreflection among the zone's interior surfaces
     375              :     // and sunlight striking the reference point.
     376              : 
     377              :     // In calculating sky-related interior illuminance and luminance quantities,
     378              :     // the sky luminance for the different sky types are determined from distributions
     379              :     // in which the zenith luminance is normalized to 1.0 cd/m2. Similarly, sun-related
     380              :     // illuminance and luminance quantities are based on beam normal solar illuminance
     381              :     // normalized to 1.0 lux.
     382              :     // The daylight and glare factors calculated in this subroutine are used in DayltgInteriorIllum
     383              :     // to get the daylight illuminance and glare at each time step.
     384              :     // Based on this information and user-input lighting setpoint and type of lighting
     385              :     // control system, DayltgElecLightingControl then determines how much the overhead electric lighting
     386              :     // can be reduced.
     387              : 
     388              :     // REFERENCES:
     389              :     // Based on DOE-2.1E subroutine DCOF.
     390              : 
     391         1839 :     auto &dl = state.dataDayltg;
     392         1839 :     auto &s_surf = state.dataSurface;
     393              : 
     394         1839 :     if (dl->CalcDayltghCoefficients_firstTime) {
     395          107 :         GetDaylightingParametersInput(state);
     396          107 :         CheckTDDsAndLightShelvesInDaylitZones(state);
     397          107 :         AssociateWindowShadingControlWithDaylighting(state);
     398          107 :         dl->CalcDayltghCoefficients_firstTime = false;
     399              :     } // End of check if firstTime
     400              : 
     401              :     // Find the total number of exterior windows associated with all Daylighting:Detailed enclosures.
     402              :     // An exterior window is associated with such a enclosure if (1) it is an exterior window in the enclosure, or
     403              :     // (2) it is an exterior window in an adjacent enclosure that shares an interior window with the enclosure.
     404              :     // Note that exterior windows in category (2) may be counted more than once if an adjacent enclosure
     405              :     // is adjacent to more than one daylit enclosure with which the adjacent enclosure shares interior windows.
     406              :     // If there are no interior windows in a building, than TotWindowsWithDayl is just the total number of
     407              :     // exterior windows in Daylighting:Detailed enclosures. Note that it is possible for a
     408              :     // Daylighting:Detailed enclosure to have zero exterior windows of its own, but it may have an interior
     409              :     // through which daylight passes from adjacent enclosures with exterior windows.
     410         1839 :     if ((int)dl->DaylRefPt.size() == 0) {
     411          481 :         return;
     412              :     }
     413         1358 :     if (state.dataGlobal->BeginSimFlag) {
     414            5 :         dl->TotWindowsWithDayl = 0;
     415           12 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     416            7 :             dl->TotWindowsWithDayl += dl->enclDaylight(enclNum).NumOfDayltgExtWins;
     417              :         }
     418              :     }
     419              : 
     420         1358 :     if (dl->TotWindowsWithDayl == 0) {
     421            0 :         return;
     422              :     }
     423              : 
     424              :     //-----------------------------------------!
     425              :     // Detailed daylighting factor calculation !
     426              :     //-----------------------------------------!
     427         1358 :     if (!state.dataSysVars->DetailedSolarTimestepIntegration && !state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) {
     428            3 :         if (state.dataGlobal->WarmupFlag) {
     429            1 :             DisplayString(state, "Calculating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
     430              :         } else {
     431            2 :             DisplayString(state, "Updating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
     432              :         }
     433              :     }
     434              : 
     435         1358 :     if (state.dataGlobal->BeginSimFlag) {
     436              : 
     437              :         // Find minimum solid angle subtended by an interior window in Daylighting:Detailed zones.
     438              :         // Used in calculating daylighting through interior windows.
     439            5 :         CalcMinIntWinSolidAngs(state);
     440              : 
     441              :         // Warning if detailed daylighting has been requested for a zone with no associated exterior windows.
     442           12 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     443            7 :             auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
     444            7 :             if (thisEnclDaylight.NumOfDayltgExtWins == 0 && thisEnclDaylight.TotalExtWindows == 0) {
     445            0 :                 for (int daylightCtrlNum : thisEnclDaylight.daylightControlIndexes) {
     446            0 :                     if (dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints > 0) {
     447            0 :                         ShowWarningError(
     448              :                             state,
     449            0 :                             format("Detailed daylighting will not be done for Daylighting:Controls={}", dl->daylightControl(daylightCtrlNum).Name));
     450            0 :                         ShowContinueError(state, "because it has no associated exterior windows.");
     451              :                     }
     452            0 :                 }
     453              :             }
     454              : 
     455              :             // Find area and reflectance quantities used in calculating inter-reflected illuminance.
     456              :             // TH 9/10/2009. Need to calculate for zones without daylighting controls (TotalDaylRefPoints = 0)
     457              :             // but with adjacent zones having daylighting controls.
     458            7 :             if ((thisEnclDaylight.NumOfDayltgExtWins > 0) || thisEnclDaylight.adjEnclHasDayltgCtrl) {
     459            7 :                 DayltgAveInteriorReflectance(state, enclNum);
     460              :             }
     461              :         }
     462              :     }
     463              : 
     464         1358 :     int numTDD = (int)state.dataDaylightingDevicesData->TDDPipe.size();
     465              :     // Zero daylighting factor arrays
     466         1358 :     if (numTDD > 0) {
     467            0 :         int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
     468            0 :         int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
     469            0 :         for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
     470            0 :             for (int iTDD = 1; iTDD <= numTDD; ++iTDD) {
     471            0 :                 dl->TDDTransVisBeam(iHr, iTDD) = 0.0;
     472            0 :                 dl->TDDFluxInc(iHr, iTDD) = Illums();
     473            0 :                 dl->TDDFluxTrans(iHr, iTDD) = Illums();
     474              :             }
     475              :         }
     476              :     }
     477              : 
     478         1358 :     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     479            4 :         if (state.dataGlobal->BeginDayFlag) {
     480              :             // Calculate hourly sun angles, clear sky zenith luminance, and exterior horizontal illuminance
     481            2 :             dl->sunAngles = SunAngles();
     482            2 :             dl->sunAnglesHr = {SunAngles()};
     483            2 :             dl->horIllum = {Illums()};
     484           50 :             for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
     485           48 :                 auto const &surfSunCosHr = s_surf->SurfSunCosHourly(IHR);
     486           48 :                 if (surfSunCosHr.z < DataEnvironment::SunIsUpValue) {
     487           33 :                     continue; // Skip if sun is below horizon //Autodesk SurfSunCosHourly was uninitialized here
     488              :                 }
     489              : 
     490           15 :                 Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
     491           15 :                 Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
     492           15 :                 dl->sunAngles = dl->sunAnglesHr[IHR] = {phi, std::sin(phi), std::cos(phi), theta};
     493              : 
     494           15 :                 DayltgExtHorizIllum(state, dl->horIllum[IHR]);
     495              :             }
     496              :         }
     497              :     } else { // timestep integrated calculations
     498         1354 :         dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {SunAngles()};
     499         5416 :         dl->horIllum[state.dataGlobal->HourOfDay] = Illums();
     500         1354 :         auto const &surfSunCosHr = s_surf->SurfSunCosHourly(state.dataGlobal->HourOfDay);
     501         1354 :         if (!(surfSunCosHr.z < DataEnvironment::SunIsUpValue)) { // Skip if sun is below horizon
     502          536 :             Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
     503          536 :             Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
     504          536 :             dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {phi, std::sin(phi), std::cos(phi), theta};
     505          536 :             DayltgExtHorizIllum(state, dl->horIllum[state.dataGlobal->HourOfDay]);
     506              :         }
     507              :     }
     508              : 
     509         1358 :     CalcDayltgCoeffsRefMapPoints(state);
     510              : 
     511         1451 :     if (dl->doSkyReporting && (!state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) &&
     512           93 :         (dl->FirstTimeDaylFacCalc && dl->TotWindowsWithDayl > 0 &&
     513           93 :          (!state.dataSysVars->DetailedSolarTimestepIntegration || state.dataGlobal->HourOfDay == 12))) {
     514              :         // Write the bare-window four sky daylight factors at noon time to the eio file; this is done only
     515              :         // for first time that daylight factors are calculated and so is insensitive to possible variation
     516              :         // due to change in ground reflectance from month to month, or change in storm window status.
     517              :         static constexpr std::string_view Format_700("! <Sky Daylight Factors>, Sky Type, MonthAndDay, Daylighting Control Name, Enclosure Name, "
     518              :                                                      "Window Name, Reference Point, Daylight Factor\n");
     519            5 :         print(state.files.eio, Format_700);
     520           12 :         for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
     521            7 :             auto &thisDayltgCtrl = dl->daylightControl(controlNum);
     522            7 :             int enclNum = thisDayltgCtrl.enclIndex;
     523            7 :             auto &thisEnclDaylight = dl->enclDaylight(enclNum);
     524              : 
     525            7 :             if (thisEnclDaylight.NumOfDayltgExtWins == 0 || !thisEnclDaylight.hasSplitFluxDaylighting) {
     526            0 :                 continue;
     527              :             }
     528           14 :             for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
     529            7 :                 int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
     530              :                 // For this report, do not include ext wins in zone adjacent to ZoneNum since the inter-reflected
     531              :                 // component will not be calculated for these windows until the time-step loop.
     532            7 :                 if (s_surf->Surface(windowSurfNum).SolarEnclIndex != enclNum) {
     533            0 :                     continue;
     534              :                 }
     535              :                 // Output for each reference point, for each sky. Group by sky type first
     536              : 
     537              :                 static constexpr std::array<std::string_view, (int)SkyType::Num> skyTypeStrings = {
     538              :                     "Clear Sky", "Clear Turbid Sky", "Intermediate Sky", "Overcast Sky"};
     539              : 
     540           35 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
     541           72 :                     for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
     542           44 :                         Real64 DaylFac = thisDayltgCtrl.daylFac[12](windowCounter, refPtNum)[iWinCover_Bare][iLum_Illum].sky[iSky];
     543           44 :                         print(state.files.eio,
     544              :                               " Sky Daylight Factors,{},{},{},{},{},{},{:.4R}\n",
     545           44 :                               skyTypeStrings[iSky],
     546           44 :                               state.dataEnvrn->CurMnDy,
     547           44 :                               thisDayltgCtrl.Name,
     548           44 :                               state.dataViewFactor->EnclSolInfo(thisDayltgCtrl.enclIndex).Name,
     549           44 :                               s_surf->Surface(windowSurfNum).Name,
     550           44 :                               dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
     551              :                               DaylFac);
     552              :                     } // for (refPtNum)
     553              :                 } // for (iSky)
     554              :             } // for (windowCounter)
     555              :         } // for (controlNum)
     556            5 :         dl->FirstTimeDaylFacCalc = false;
     557            5 :         dl->doSkyReporting = false;
     558              :     } // if (detailedIntegration etc.)
     559              : 
     560              :     // Skip if no daylight windows
     561         1358 :     if (dl->TotWindowsWithDayl == 0) {
     562            0 :         return;
     563              :     }
     564              : 
     565              :     // Skip if no request of reporting
     566         1358 :     if ((!dl->DFSReportSizingDays) && (!dl->DFSReportAllShadowCalculationDays)) {
     567         1357 :         return;
     568              :     }
     569              : 
     570              :     // Skip duplicate calls
     571            1 :     if (state.dataGlobal->KickOffSizing) {
     572            0 :         return;
     573              :     }
     574            1 :     if (state.dataGlobal->DoingSizing) {
     575            0 :         return;
     576              :     }
     577            1 :     if (state.dataGlobal->KickOffSimulation) {
     578            0 :         return;
     579              :     }
     580              : 
     581            1 :     if (dl->DFSReportSizingDays) {
     582            1 :         if (state.dataGlobal->DoWeathSim && state.dataGlobal->DoDesDaySim) {
     583            1 :             if (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) {
     584            0 :                 return;
     585              :             }
     586              :         }
     587              :     }
     588              : 
     589            1 :     if (dl->DFSReportAllShadowCalculationDays) {
     590            0 :         if (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather) {
     591            0 :             return;
     592              :         }
     593              :     }
     594              : 
     595              :     // open a new file eplusout.dfs for saving the daylight factors
     596            1 :     if (dl->CreateDFSReportFile) {
     597            2 :         InputOutputFile &dfs = state.files.dfs.ensure_open(state, "CalcDayltgCoefficients", state.files.outputControl.dfs);
     598            1 :         print(dfs, "{}\n", "This file contains daylight factors for all exterior windows of daylight enclosures.");
     599            1 :         print(dfs, "{}\n", "MonthAndDay,Enclosure Name,Zone Name,Window Name,Window State");
     600            1 :         print(dfs,
     601              :               "{}\n",
     602              :               "Hour,Reference Point,Daylight Factor for Clear Sky,Daylight Factor for Clear Turbid Sky,"
     603              :               "Daylight Factor for Intermediate Sky,Daylight Factor for Overcast Sky");
     604            1 :         dl->CreateDFSReportFile = false;
     605              :     }
     606              : 
     607            3 :     for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
     608            2 :         auto &thisDayltgCtrl = dl->daylightControl(controlNum);
     609            2 :         int enclNum = thisDayltgCtrl.enclIndex;
     610            2 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
     611            2 :         if (thisEnclDaylight.NumOfDayltgExtWins == 0) {
     612            0 :             continue;
     613              :         }
     614              : 
     615            4 :         for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
     616            2 :             int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
     617            2 :             auto &surf = s_surf->Surface(windowSurfNum);
     618              :             // For this report, do not include ext wins in zone/enclosure adjacent to ZoneNum since the inter-reflected
     619              :             // component will not be calculated for these windows until the time-step loop.
     620            2 :             if (surf.SolarEnclIndex == enclNum) {
     621              : 
     622            2 :                 int numWinCover = surf.HasShadeControl ? (int)WinCover::Num : 1;
     623              : 
     624              :                 // loop over each slat angle
     625            4 :                 for (int iWinCover = 0; iWinCover < numWinCover; ++iWinCover) {
     626            2 :                     if (iWinCover == iWinCover_Bare) {
     627              :                         // base window without shades, screens, or blinds
     628            2 :                         print(state.files.dfs,
     629              :                               "{},{},{},{},Base Window\n",
     630            2 :                               state.dataEnvrn->CurMnDy,
     631            2 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     632            2 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     633            2 :                               s_surf->Surface(windowSurfNum).Name);
     634            0 :                     } else if (iWinCover == iWinCover_Shaded) {
     635              :                         // window shade or blind with fixed slat angle
     636            0 :                         print(state.files.dfs,
     637              :                               "{},{},{},{},Blind or Slat Applied\n",
     638            0 :                               state.dataEnvrn->CurMnDy,
     639            0 :                               state.dataViewFactor->EnclSolInfo(enclNum).Name,
     640            0 :                               state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
     641            0 :                               s_surf->Surface(windowSurfNum).Name);
     642              :                     }
     643              : 
     644           50 :                     for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
     645              :                         // For each Daylight Reference Point
     646           48 :                         auto &daylFacHr = thisDayltgCtrl.daylFac[IHR];
     647          144 :                         for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
     648           96 :                             auto &illums = daylFacHr(windowCounter, refPtNum)[iWinCover][iLum_Illum];
     649              : 
     650              :                             // write daylight factors - 4 sky types for each daylight ref point
     651           96 :                             print(state.files.dfs,
     652              :                                   "{},{},{:.5R},{:.5R},{:.5R},{:.5R}\n",
     653              :                                   IHR,
     654           96 :                                   dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
     655           96 :                                   illums.sky[(int)SkyType::Clear],
     656           96 :                                   illums.sky[(int)SkyType::ClearTurbid],
     657           96 :                                   illums.sky[(int)SkyType::Intermediate],
     658           96 :                                   illums.sky[(int)SkyType::Overcast]);
     659              : 
     660              :                         } // for (refPtNum) Reference Point
     661              :                     } // for (IHR) hour
     662              :                 } // for (ISlatAngle) slat angle
     663              :             } // if (SolarEnclIndex == enclNum)
     664              :         } // for (windowCounter) exterior windows in enclosure
     665              :     } // for (controlNum) daylighting control
     666              : } // CalcDayltgCoefficients()
     667              : 
     668         1358 : void CalcDayltgCoeffsRefMapPoints(EnergyPlusData &state)
     669              : {
     670              : 
     671              :     // SUBROUTINE INFORMATION:
     672              :     //       AUTHOR         Linda Lawrie
     673              :     //       DATE WRITTEN   October 2004
     674              :     //       MODIFIED       May 2006 (RR): added exterior window screens
     675              :     //                      April 2012 (LKL); change to allow multiple maps per zone
     676              : 
     677              :     // PURPOSE OF THIS SUBROUTINE:
     678              :     // This subroutine does the daylighting coefficient calculation for the
     679              :     // daylighting and illuminance map reference points.
     680         1358 :     auto &dl = state.dataDayltg;
     681         1358 :     auto const &s_surf = state.dataSurface;
     682              : 
     683         1358 :     if (dl->VeryFirstTime) {
     684              :         // make sure all necessary surfaces match to pipes
     685            5 :         bool ErrorsFound = false;
     686           12 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
     687           14 :             for (int loopwin = 1; loopwin <= dl->enclDaylight(enclNum).NumOfDayltgExtWins; ++loopwin) {
     688            7 :                 int IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
     689            7 :                 if (s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Diffuser) {
     690            7 :                     continue;
     691              :                 }
     692              :                 // Look up the TDD:DOME object
     693            0 :                 int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
     694            0 :                 if (PipeNum == 0) {
     695            0 :                     ShowSevereError(
     696              :                         state,
     697            0 :                         format("GetTDDInput: Surface={}, TDD:Dome object does not reference a valid Diffuser object.", s_surf->Surface(IWin).Name));
     698            0 :                     ShowContinueError(state, "...needs DaylightingDevice:Tubular of same name as Surface.");
     699            0 :                     ErrorsFound = true;
     700              :                 }
     701              :             }
     702              :         }
     703              : 
     704            5 :         if (ErrorsFound) {
     705            0 :             ShowFatalError(state, "Not all TubularDaylightDome objects have corresponding DaylightingDevice:Tubular objects. Program terminates.");
     706              :         }
     707            5 :         dl->VeryFirstTime = false;
     708              :     }
     709              : 
     710              :     // Calc for daylighting reference points for daylighting controls that use SplitFlux method
     711         2718 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
     712         1360 :         if (dl->daylightControl(daylightCtrlNum).DaylightMethod != DaylightingMethod::SplitFlux) {
     713            0 :             continue;
     714              :         }
     715              :         // Skip enclosures with no exterior windows or in adjacent enclosure(s) with which an interior window is shared
     716         1360 :         if (dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex).NumOfDayltgExtWins == 0) {
     717            0 :             continue;
     718              :         }
     719         1360 :         CalcDayltgCoeffsRefPoints(state, daylightCtrlNum);
     720              :     }
     721         1358 :     if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
     722              :         // Calc for illuminance maps
     723         1351 :         if ((int)dl->illumMaps.size() > 0) {
     724            2 :             for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
     725            1 :                 int mapZoneNum = dl->illumMaps(MapNum).zoneIndex;
     726            1 :                 std::string name = format("Zone={}", state.dataHeatBal->Zone(mapZoneNum).Name);
     727            1 :                 int mapSpaceNum = dl->illumMaps(MapNum).spaceIndex;
     728            1 :                 if (mapSpaceNum > 0) {
     729            0 :                     name = format("Space={}", state.dataHeatBal->space(mapSpaceNum).Name);
     730              :                 }
     731            1 :                 if (state.dataGlobal->WarmupFlag) {
     732            1 :                     DisplayString(state, format("Calculating Daylighting Coefficients (Map Points), {}", name));
     733              :                 } else {
     734            0 :                     DisplayString(state, format("Updating Daylighting Coefficients (Map Points), {}", name));
     735              :                 }
     736            1 :                 CalcDayltgCoeffsMapPoints(state, MapNum);
     737            1 :             }
     738              :         }
     739              :     }
     740         1358 : } // CalcDayltgCoeffsRefMapPoints()
     741              : 
     742         1360 : void CalcDayltgCoeffsRefPoints(EnergyPlusData &state, int const daylightCtrlNum)
     743              : {
     744              : 
     745              :     // SUBROUTINE INFORMATION:
     746              :     //       AUTHOR         Linda Lawrie
     747              :     //       DATE WRITTEN   April 2012
     748              :     //       MODIFIED       November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
     749              : 
     750              :     // PURPOSE OF THIS SUBROUTINE:
     751              :     // Provides calculations for Daylighting Coefficients for daylighting reference points
     752         1360 :     auto &dl = state.dataDayltg;
     753         1360 :     auto const &s_surf = state.dataSurface;
     754              : 
     755              :     //  glare calculation (radians)
     756              :     int IConst;            // Construction counter
     757              :     int ICtrl;             // Window control counter
     758              :     int IWin;              // Window counter
     759              :     int IWin2;             // Secondary window counter (for TDD:DOME object, if exists)
     760              :     int InShelfSurf;       // Inside daylighting shelf surface number
     761              :     WinShadingType ShType; // Window shading type
     762              :     int BlNum;             // Window Blind Number
     763              :     int LSHCAL;            // Interior shade calculation flag: 0=not yet
     764              :     //  calculated, 1=already calculated
     765              :     int NWX;     // Number of window elements in x direction for dayltg calc
     766              :     int NWY;     // Number of window elements in y direction for dayltg calc
     767              :     int NWYlim;  // For triangle, largest NWY for a given IX
     768              :     Real64 COSB; // Cosine of angle between window outward normal and ray from
     769              :     //  reference point to window element
     770              :     Real64 PHRAY;  // Altitude of ray from reference point to window element (radians)
     771              :     Real64 THRAY;  // Azimuth of ray from reference point to window element (radians)
     772              :     Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
     773              :     Real64 TVISB;  // Visible transmittance of window for COSB angle of incidence (times light well
     774              :     //   efficiency, if appropriate)
     775              :     int ISunPos; // Sun position counter; used to avoid calculating various
     776              :     //  quantities that do not depend on sun position.
     777              :     Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
     778              :     // from reference point through a window element
     779              :     bool is_Rectangle;         // True if window is rectangular
     780              :     bool is_Triangle;          // True if window is triangular
     781              :     Real64 DWX;                // Horizontal dimension of window element (m)
     782              :     Real64 DWY;                // Vertical dimension of window element (m)
     783              :     Real64 DAXY;               // Area of window element
     784              :     Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
     785              :     ExtWinType extWinType;     // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
     786              :     int BRef;
     787              :     int ILB;
     788              :     bool hitIntObs;        // True iff interior obstruction hit
     789              :     bool hitExtObs;        // True iff ray from ref pt to ext win hits an exterior obstruction
     790              :     Real64 TVISIntWin;     // Visible transmittance of int win at COSBIntWin for light from ext win
     791              :     Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
     792              : 
     793         1360 :     Vector3<Real64> W2;
     794         1360 :     Vector3<Real64> W3;
     795         1360 :     Vector3<Real64> W21;
     796         1360 :     Vector3<Real64> W23;
     797         1360 :     Vector3<Real64> RREF2;
     798         1360 :     Vector3<Real64> RWIN;
     799         1360 :     Vector3<Real64> RWIN2;
     800         1360 :     Vector3<Real64> Ray;
     801         1360 :     Vector3<Real64> WNORM2;
     802         1360 :     Vector3<Real64> VIEWVC;
     803         1360 :     Vector3<Real64> U2;
     804         1360 :     Vector3<Real64> U21;
     805         1360 :     Vector3<Real64> U23;
     806         1360 :     Vector3<Real64> VIEWVC2;
     807              : 
     808              :     int WinEl; // Current window element
     809              : 
     810         1360 :     if (dl->refFirstTime && (dl->maxControlRefPoints > 0)) {
     811            5 :         dl->RefErrIndex.allocate(dl->maxControlRefPoints, s_surf->TotSurfaces);
     812            5 :         dl->RefErrIndex = 0;
     813            5 :         dl->refFirstTime = false;
     814              :     }
     815              : 
     816         1360 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
     817         1360 :     auto const &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
     818         1360 :     int zoneNum = thisDayltgCtrl.zoneIndex;
     819              :     // Azimuth of view vector in absolute coord sys
     820         1360 :     Real64 AZVIEW = (thisDayltgCtrl.ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth + state.dataHeatBal->BuildingAzimuth +
     821         1360 :                      state.dataHeatBal->BuildingRotationAppendixG) *
     822         1360 :                     Constant::DegToRad;
     823              :     // View vector components in absolute coord sys
     824         1360 :     VIEWVC = {std::sin(AZVIEW), std::cos(AZVIEW), 0.0};
     825              : 
     826         2725 :     for (auto &refPt : thisDayltgCtrl.refPts) {
     827         1365 :         refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
     828         1365 :         refPt.glareIndex = 0.0;       // Glare index at reference points
     829         2730 :         for (auto &extWin : refPt.extWins) {
     830         1365 :             extWin.solidAng = extWin.solidAngWtd = 0.0;
     831         1365 :             extWin.lums[iLum_Illum] = extWin.lums[iLum_Back] = extWin.lums[iLum_Source] = {0.0, 0.0};
     832              :         }
     833              :     }
     834              : 
     835         1360 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
     836         1360 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
     837         1360 :     int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
     838         1360 :     int numRefPts = thisDayltgCtrl.TotalDaylRefPoints;
     839              : 
     840         2858 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
     841         1498 :         auto &daylFacHr = thisDayltgCtrl.daylFac[iHr];
     842         2996 :         for (int iWin = 1; iWin <= numExtWins; ++iWin) {
     843         3116 :             for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
     844         4854 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
     845         3236 :                     auto &daylFac = daylFacHr(iWin, iRefPt)[iWinCover];
     846        12944 :                     daylFac[iLum_Illum] = Illums();
     847        12944 :                     daylFac[iLum_Source] = Illums();
     848        12944 :                     daylFac[iLum_Back] = Illums();
     849              :                 } // for (iSlatAng)
     850              :             } // for (iRefPt)
     851              :         } // for (iWin)
     852              :     } // for (iHr)
     853              : 
     854         1360 :     BRef = 0;
     855              : 
     856         2725 :     for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
     857         1365 :         auto const &refPt = thisDayltgCtrl.refPts(IL);
     858              :         // Reference point in absolute coordinate system
     859         1365 :         Vector3<Real64> RREF = refPt.absCoords;
     860              : 
     861              :         //           -------------
     862              :         // ---------- WINDOW LOOP ----------
     863              :         //           -------------
     864         2730 :         for (int loopwin = 1; loopwin <= thisEnclDaylight.NumOfDayltgExtWins; ++loopwin) {
     865              : 
     866         1365 :             FigureDayltgCoeffsAtPointsSetupForWindow(state,
     867              :                                                      daylightCtrlNum,
     868              :                                                      IL,
     869              :                                                      loopwin,
     870              :                                                      CalledFor::RefPoint,
     871              :                                                      RREF,
     872              :                                                      VIEWVC,
     873              :                                                      IWin,
     874              :                                                      IWin2,
     875              :                                                      NWX,
     876              :                                                      NWY,
     877              :                                                      W2,
     878              :                                                      W3,
     879              :                                                      W21,
     880              :                                                      W23,
     881              :                                                      LSHCAL,
     882              :                                                      InShelfSurf,
     883              :                                                      ICtrl,
     884              :                                                      ShType,
     885              :                                                      BlNum,
     886              :                                                      WNORM2,
     887              :                                                      extWinType,
     888              :                                                      IConst,
     889              :                                                      RREF2,
     890              :                                                      DWX,
     891              :                                                      DWY,
     892              :                                                      DAXY,
     893              :                                                      U2,
     894              :                                                      U23,
     895              :                                                      U21,
     896              :                                                      VIEWVC2,
     897              :                                                      is_Rectangle,
     898              :                                                      is_Triangle);
     899              :             //           ---------------------
     900              :             // ---------- WINDOW ELEMENT LOOP ----------
     901              :             //           ---------------------
     902              : 
     903         1365 :             WinEl = 0;
     904              : 
     905        23128 :             for (int IX = 1; IX <= NWX; ++IX) {
     906        21763 :                 if (is_Rectangle) {
     907        21763 :                     NWYlim = NWY;
     908            0 :                 } else if (is_Triangle) {
     909            0 :                     NWYlim = NWY - IX + 1;
     910              :                 }
     911              : 
     912       368814 :                 for (int IY = 1; IY <= NWYlim; ++IY) {
     913              : 
     914       347051 :                     ++WinEl;
     915              : 
     916       347051 :                     FigureDayltgCoeffsAtPointsForWindowElements(state,
     917              :                                                                 daylightCtrlNum,
     918              :                                                                 IL,
     919              :                                                                 loopwin,
     920              :                                                                 CalledFor::RefPoint,
     921              :                                                                 WinEl,
     922              :                                                                 IWin,
     923              :                                                                 IWin2,
     924              :                                                                 IX,
     925              :                                                                 IY,
     926              :                                                                 SkyObstructionMult,
     927              :                                                                 W2,
     928              :                                                                 W21,
     929              :                                                                 W23,
     930              :                                                                 RREF,
     931              :                                                                 NWYlim,
     932              :                                                                 VIEWVC2,
     933              :                                                                 DWX,
     934              :                                                                 DWY,
     935              :                                                                 DAXY,
     936              :                                                                 U2,
     937              :                                                                 U23,
     938              :                                                                 U21,
     939              :                                                                 RWIN,
     940              :                                                                 RWIN2,
     941              :                                                                 Ray,
     942              :                                                                 PHRAY,
     943              :                                                                 LSHCAL,
     944              :                                                                 COSB,
     945              :                                                                 ObTrans,
     946              :                                                                 TVISB,
     947              :                                                                 DOMEGA,
     948              :                                                                 THRAY,
     949              :                                                                 hitIntObs,
     950              :                                                                 hitExtObs,
     951              :                                                                 WNORM2,
     952              :                                                                 extWinType,
     953              :                                                                 IConst,
     954              :                                                                 RREF2,
     955              :                                                                 is_Triangle,
     956              :                                                                 TVISIntWin,
     957              :                                                                 TVISIntWinDisk);
     958              : 
     959              :                     //           -------------------
     960              :                     // ---------- SUN POSITION LOOP ----------
     961              :                     //           -------------------
     962              : 
     963              :                     // Sun position counter. Used to avoid calculating various quantities
     964              :                     // that do not depend on sun position.
     965              : 
     966       347051 :                     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     967          427 :                         ISunPos = 0;
     968        10675 :                         for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
     969              : 
     970        10248 :                             FigureDayltgCoeffsAtPointsForSunPosition(state,
     971              :                                                                      daylightCtrlNum,
     972              :                                                                      IL,
     973              :                                                                      IX,
     974              :                                                                      NWX,
     975              :                                                                      IY,
     976              :                                                                      NWYlim,
     977              :                                                                      WinEl,
     978              :                                                                      IWin,
     979              :                                                                      IWin2,
     980              :                                                                      IHR,
     981              :                                                                      ISunPos,
     982              :                                                                      SkyObstructionMult,
     983              :                                                                      RWIN2,
     984              :                                                                      Ray,
     985              :                                                                      PHRAY,
     986              :                                                                      LSHCAL,
     987              :                                                                      InShelfSurf,
     988              :                                                                      COSB,
     989              :                                                                      ObTrans,
     990              :                                                                      TVISB,
     991              :                                                                      DOMEGA,
     992              :                                                                      ICtrl,
     993              :                                                                      ShType,
     994              :                                                                      BlNum,
     995              :                                                                      THRAY,
     996              :                                                                      WNORM2,
     997              :                                                                      extWinType,
     998              :                                                                      IConst,
     999              :                                                                      AZVIEW,
    1000              :                                                                      RREF2,
    1001              :                                                                      hitIntObs,
    1002              :                                                                      hitExtObs,
    1003              :                                                                      CalledFor::RefPoint,
    1004              :                                                                      TVISIntWin,
    1005              :                                                                      TVISIntWinDisk);
    1006              : 
    1007              :                         } // End of hourly sun position loop, IHR
    1008              :                     } else { // timestep integrated
    1009       346624 :                         if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1010           15 :                             ISunPos = 0;
    1011           15 :                             dl->MySunIsUpFlag = true;
    1012       346609 :                         } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1013       137201 :                             ISunPos = 1;
    1014       209408 :                         } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1015           14 :                             dl->MySunIsUpFlag = false;
    1016           14 :                             ISunPos = -1;
    1017       209394 :                         } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1018       209394 :                             ISunPos = -1;
    1019              :                         }
    1020              : 
    1021       346624 :                         FigureDayltgCoeffsAtPointsForSunPosition(state,
    1022              :                                                                  daylightCtrlNum,
    1023              :                                                                  IL,
    1024              :                                                                  IX,
    1025              :                                                                  NWX,
    1026              :                                                                  IY,
    1027              :                                                                  NWYlim,
    1028              :                                                                  WinEl,
    1029              :                                                                  IWin,
    1030              :                                                                  IWin2,
    1031       346624 :                                                                  state.dataGlobal->HourOfDay,
    1032              :                                                                  ISunPos,
    1033              :                                                                  SkyObstructionMult,
    1034              :                                                                  RWIN2,
    1035              :                                                                  Ray,
    1036              :                                                                  PHRAY,
    1037              :                                                                  LSHCAL,
    1038              :                                                                  InShelfSurf,
    1039              :                                                                  COSB,
    1040              :                                                                  ObTrans,
    1041              :                                                                  TVISB,
    1042              :                                                                  DOMEGA,
    1043              :                                                                  ICtrl,
    1044              :                                                                  ShType,
    1045              :                                                                  BlNum,
    1046              :                                                                  THRAY,
    1047              :                                                                  WNORM2,
    1048              :                                                                  extWinType,
    1049              :                                                                  IConst,
    1050              :                                                                  AZVIEW,
    1051              :                                                                  RREF2,
    1052              :                                                                  hitIntObs,
    1053              :                                                                  hitExtObs,
    1054              :                                                                  CalledFor::RefPoint,
    1055              :                                                                  TVISIntWin,
    1056              :                                                                  TVISIntWinDisk);
    1057              :                     }
    1058              : 
    1059              :                 } // End of window Y-element loop, IY
    1060              :             } // End of window X-element loop, IX
    1061              : 
    1062              :             // Loop again over hourly sun positions and calculate daylight factors by adding
    1063              :             // direct and inter-reflected illum components, then dividing by exterior horiz illum.
    1064              :             // Also calculate corresponding glare factors.
    1065              : 
    1066         1365 :             ILB = BRef + IL;
    1067              : 
    1068         1365 :             if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1069           11 :                 ISunPos = 0;
    1070          275 :                 for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
    1071          264 :                     FigureRefPointDayltgFactorsToAddIllums(state, daylightCtrlNum, ILB, IHR, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
    1072              : 
    1073              :                 } // End of sun position loop, IHR
    1074              :             } else {
    1075         1354 :                 if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1076            0 :                     ISunPos = 0;
    1077            0 :                     dl->MySunIsUpFlag = true;
    1078         1354 :                 } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1079          536 :                     ISunPos = 1;
    1080          818 :                 } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
    1081            0 :                     dl->MySunIsUpFlag = false;
    1082            0 :                     ISunPos = -1;
    1083          818 :                 } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
    1084          818 :                     ISunPos = -1;
    1085              :                 }
    1086         1354 :                 FigureRefPointDayltgFactorsToAddIllums(
    1087         1354 :                     state, daylightCtrlNum, ILB, state.dataGlobal->HourOfDay, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
    1088              :             }
    1089              :         } // End of window loop, loopwin - IWin
    1090              : 
    1091         1365 :     } // End of reference point loop, IL
    1092         1360 : }
    1093              : 
    1094            1 : void CalcDayltgCoeffsMapPoints(EnergyPlusData &state, int const mapNum)
    1095              : {
    1096              : 
    1097              :     // SUBROUTINE INFORMATION:
    1098              :     //       AUTHOR         Linda Lawrie
    1099              :     //       DATE WRITTEN   April 2012
    1100              :     //       MODIFIED      November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
    1101              : 
    1102              :     // PURPOSE OF THIS SUBROUTINE:
    1103              :     // Provides calculations for Daylighting Coefficients for map illuminance points
    1104              : 
    1105              :     // METHODOLOGY EMPLOYED:
    1106              :     // Was previously part of CalcDayltgCoeffsRefMapPoints -- broken out to all multiple
    1107              :     // maps per zone
    1108            1 :     auto &dl = state.dataDayltg;
    1109            1 :     auto const &s_surf = state.dataSurface;
    1110              : 
    1111              :     //  In the following four variables, I=1 for clear sky, 2 for overcast.
    1112              :     int numRefPts; // Number of daylighting reference points in a zone
    1113              :     //  glare calculation (radians)
    1114              :     int IConst;            // Construction counter
    1115              :     int ICtrl;             // Window control counter
    1116              :     int IWin;              // Window counter
    1117              :     int IWin2;             // Secondary window counter (for TDD:DOME object, if exists)
    1118              :     int InShelfSurf;       // Inside daylighting shelf surface number
    1119              :     WinShadingType ShType; // Window shading type
    1120              :     int BlNum;             // Window Blind Number
    1121              :     int LSHCAL;            // Interior shade calculation flag: 0=not yet
    1122              :     //  calculated, 1=already calculated
    1123              :     int NWX;     // Number of window elements in x direction for dayltg calc
    1124              :     int NWY;     // Number of window elements in y direction for dayltg calc
    1125              :     int NWYlim;  // For triangle, largest NWY for a given IX
    1126              :     Real64 DWX;  // Horizontal dimension of window element (m)
    1127              :     Real64 DWY;  // Vertical dimension of window element (m)
    1128              :     Real64 COSB; // Cosine of angle between window outward normal and ray from
    1129              :     //  reference point to window element
    1130              :     Real64 PHRAY;  // Altitude of ray from reference point to window element (radians)
    1131              :     Real64 THRAY;  // Azimuth of ray from reference point to window element (radians)
    1132              :     Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
    1133              :     Real64 TVISB;  // Visible transmittance of window for COSB angle of incidence (times light well
    1134              :     //   efficiency, if appropriate)
    1135              :     int ISunPos; // Sun position counter; used to avoid calculating various
    1136              :     //  quantities that do not depend on sun position.
    1137              :     Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
    1138              :     // from reference point through a window element
    1139              :     bool is_Rectangle;         // True if window is rectangular
    1140              :     bool is_Triangle;          // True if window is triangular
    1141              :     Real64 DAXY;               // Area of window element
    1142              :     Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
    1143              :     ExtWinType extWinType;     // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1144              :     int ILB;
    1145              :     bool hitIntObs;        // True iff interior obstruction hit
    1146              :     bool hitExtObs;        // True iff ray from ref pt to ext win hits an exterior obstruction
    1147              :     Real64 TVISIntWin;     // Visible transmittance of int win at COSBIntWin for light from ext win
    1148              :     Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
    1149              :     int WinEl;             // window elements counter
    1150              : 
    1151            1 :     Vector3<Real64> W2;
    1152            1 :     Vector3<Real64> W3;
    1153            1 :     Vector3<Real64> W21;
    1154            1 :     Vector3<Real64> W23;
    1155            1 :     Vector3<Real64> RREF2;
    1156            1 :     Vector3<Real64> RWIN;
    1157            1 :     Vector3<Real64> RWIN2;
    1158            1 :     Vector3<Real64> Ray;
    1159            1 :     Vector3<Real64> WNORM2;
    1160            1 :     Vector3<Real64> VIEWVC;
    1161            1 :     Vector3<Real64> U2;
    1162            1 :     Vector3<Real64> U21;
    1163            1 :     Vector3<Real64> U23;
    1164            1 :     Vector3<Real64> VIEWVC2;
    1165              : 
    1166            1 :     if (dl->mapFirstTime && (int)dl->illumMaps.size() > 0) {
    1167            1 :         int IL = -999;
    1168            2 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    1169            1 :             IL = max(IL, dl->illumMaps(MapNum).TotalMapRefPoints);
    1170              :         }
    1171            1 :         dl->MapErrIndex.dimension(IL, s_surf->TotSurfaces, 0);
    1172            1 :         dl->mapFirstTime = false;
    1173              :     }
    1174              : 
    1175            1 :     auto &illumMap = dl->illumMaps(mapNum);
    1176            1 :     int enclNum = illumMap.enclIndex;
    1177            1 :     auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    1178              : 
    1179              :     // Azimuth of view vector in absolute coord sys - set to zero here, because glare isn't calculated for map points
    1180              :     // but these are arguments to some of the functions that are shared with regular reference points, so initalize here.
    1181            1 :     Real64 AZVIEW = 0.0;
    1182              :     // View vector components in absolute coord sys
    1183            1 :     VIEWVC = {0.0, 0.0, 0.0};
    1184              : 
    1185            1 :     numRefPts = illumMap.TotalMapRefPoints;
    1186            1 :     int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
    1187              : 
    1188          101 :     for (auto &refPt : illumMap.refPts) {
    1189          100 :         refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
    1190          200 :         for (int iExtWin = 1; iExtWin <= numExtWins; ++iExtWin) {
    1191          100 :             refPt.winLums(iExtWin) = {0.0, 0.0};
    1192              :         }
    1193              :     }
    1194              : 
    1195            1 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
    1196            1 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
    1197              : 
    1198           25 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
    1199           24 :         auto &daylFacHr = illumMap.daylFac[iHr];
    1200           48 :         for (int iWin = 1; iWin <= numExtWins; ++iWin) {
    1201         2424 :             for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
    1202         7200 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    1203        19200 :                     daylFacHr(iWin, iRefPt)[iWinCover] = Illums();
    1204              :                 }
    1205              :             }
    1206              :         }
    1207              :     }
    1208              : 
    1209          101 :     for (int IL = 1; IL <= numRefPts; ++IL) {
    1210          100 :         auto const &refPt = illumMap.refPts(IL);
    1211          100 :         Vector3<Real64> RREF = refPt.absCoords;
    1212              : 
    1213              :         //           -------------
    1214              :         // ---------- WINDOW LOOP ----------
    1215              :         //           -------------
    1216              : 
    1217          200 :         for (int loopwin = 1; loopwin <= numExtWins; ++loopwin) {
    1218              : 
    1219              :             // daylightingCtrlNum parameter is unused for map points
    1220          100 :             FigureDayltgCoeffsAtPointsSetupForWindow(state,
    1221              :                                                      0,
    1222              :                                                      IL,
    1223              :                                                      loopwin,
    1224              :                                                      CalledFor::MapPoint,
    1225              :                                                      RREF,
    1226              :                                                      VIEWVC,
    1227              :                                                      IWin,
    1228              :                                                      IWin2,
    1229              :                                                      NWX,
    1230              :                                                      NWY,
    1231              :                                                      W2,
    1232              :                                                      W3,
    1233              :                                                      W21,
    1234              :                                                      W23,
    1235              :                                                      LSHCAL,
    1236              :                                                      InShelfSurf,
    1237              :                                                      ICtrl,
    1238              :                                                      ShType,
    1239              :                                                      BlNum,
    1240              :                                                      WNORM2,
    1241              :                                                      extWinType,
    1242              :                                                      IConst,
    1243              :                                                      RREF2,
    1244              :                                                      DWX,
    1245              :                                                      DWY,
    1246              :                                                      DAXY,
    1247              :                                                      U2,
    1248              :                                                      U23,
    1249              :                                                      U21,
    1250              :                                                      VIEWVC2,
    1251              :                                                      is_Rectangle,
    1252              :                                                      is_Triangle,
    1253              :                                                      mapNum);
    1254              :             //           ---------------------
    1255              :             // ---------- WINDOW ELEMENT LOOP ----------
    1256              :             //           ---------------------
    1257          100 :             WinEl = 0;
    1258              : 
    1259         1340 :             for (int IX = 1; IX <= NWX; ++IX) {
    1260         1240 :                 if (is_Rectangle) {
    1261         1240 :                     NWYlim = NWY;
    1262            0 :                 } else if (is_Triangle) {
    1263            0 :                     NWYlim = NWY - IX + 1;
    1264              :                 }
    1265              : 
    1266        22290 :                 for (int IY = 1; IY <= NWYlim; ++IY) {
    1267              : 
    1268        21050 :                     ++WinEl;
    1269              : 
    1270              :                     // daylightingCtrlNum parameter is unused for map points
    1271        21050 :                     FigureDayltgCoeffsAtPointsForWindowElements(state,
    1272              :                                                                 0,
    1273              :                                                                 IL,
    1274              :                                                                 loopwin,
    1275              :                                                                 CalledFor::MapPoint,
    1276              :                                                                 WinEl,
    1277              :                                                                 IWin,
    1278              :                                                                 IWin2,
    1279              :                                                                 IX,
    1280              :                                                                 IY,
    1281              :                                                                 SkyObstructionMult,
    1282              :                                                                 W2,
    1283              :                                                                 W21,
    1284              :                                                                 W23,
    1285              :                                                                 RREF,
    1286              :                                                                 NWYlim,
    1287              :                                                                 VIEWVC2,
    1288              :                                                                 DWX,
    1289              :                                                                 DWY,
    1290              :                                                                 DAXY,
    1291              :                                                                 U2,
    1292              :                                                                 U23,
    1293              :                                                                 U21,
    1294              :                                                                 RWIN,
    1295              :                                                                 RWIN2,
    1296              :                                                                 Ray,
    1297              :                                                                 PHRAY,
    1298              :                                                                 LSHCAL,
    1299              :                                                                 COSB,
    1300              :                                                                 ObTrans,
    1301              :                                                                 TVISB,
    1302              :                                                                 DOMEGA,
    1303              :                                                                 THRAY,
    1304              :                                                                 hitIntObs,
    1305              :                                                                 hitExtObs,
    1306              :                                                                 WNORM2,
    1307              :                                                                 extWinType,
    1308              :                                                                 IConst,
    1309              :                                                                 RREF2,
    1310              :                                                                 is_Triangle,
    1311              :                                                                 TVISIntWin,
    1312              :                                                                 TVISIntWinDisk,
    1313              :                                                                 mapNum);
    1314              :                     //           -------------------
    1315              :                     // ---------- SUN POSITION LOOP ----------
    1316              :                     //           -------------------
    1317              : 
    1318              :                     // Sun position counter. Used to avoid calculating various quantities
    1319              :                     // that do not depend on sun position.
    1320        21050 :                     if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1321        21050 :                         ISunPos = 0;
    1322       526250 :                         for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
    1323              :                             // daylightingCtrlNum parameter is unused for map points
    1324       505200 :                             FigureDayltgCoeffsAtPointsForSunPosition(state,
    1325              :                                                                      0,
    1326              :                                                                      IL,
    1327              :                                                                      IX,
    1328              :                                                                      NWX,
    1329              :                                                                      IY,
    1330              :                                                                      NWYlim,
    1331              :                                                                      WinEl,
    1332              :                                                                      IWin,
    1333              :                                                                      IWin2,
    1334              :                                                                      IHR,
    1335              :                                                                      ISunPos,
    1336              :                                                                      SkyObstructionMult,
    1337              :                                                                      RWIN2,
    1338              :                                                                      Ray,
    1339              :                                                                      PHRAY,
    1340              :                                                                      LSHCAL,
    1341              :                                                                      InShelfSurf,
    1342              :                                                                      COSB,
    1343              :                                                                      ObTrans,
    1344              :                                                                      TVISB,
    1345              :                                                                      DOMEGA,
    1346              :                                                                      ICtrl,
    1347              :                                                                      ShType,
    1348              :                                                                      BlNum,
    1349              :                                                                      THRAY,
    1350              :                                                                      WNORM2,
    1351              :                                                                      extWinType,
    1352              :                                                                      IConst,
    1353              :                                                                      AZVIEW,
    1354              :                                                                      RREF2,
    1355              :                                                                      hitIntObs,
    1356              :                                                                      hitExtObs,
    1357              :                                                                      CalledFor::MapPoint,
    1358              :                                                                      TVISIntWin,
    1359              :                                                                      TVISIntWinDisk,
    1360              :                                                                      mapNum);
    1361              :                         } // End of hourly sun position loop, IHR
    1362              :                     } else {
    1363            0 :                         if (state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1364            0 :                             ISunPos = 0;
    1365            0 :                             dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = true;
    1366            0 :                         } else if (state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1367            0 :                             ISunPos = 1;
    1368            0 :                         } else if (!state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1369            0 :                             dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = false;
    1370            0 :                             ISunPos = -1;
    1371            0 :                         } else if (!state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
    1372            0 :                             ISunPos = -1;
    1373              :                         }
    1374              :                         // daylightingCtrlNum parameter is unused for map points
    1375            0 :                         FigureDayltgCoeffsAtPointsForSunPosition(state,
    1376              :                                                                  0,
    1377              :                                                                  IL,
    1378              :                                                                  IX,
    1379              :                                                                  NWX,
    1380              :                                                                  IY,
    1381              :                                                                  NWYlim,
    1382              :                                                                  WinEl,
    1383              :                                                                  IWin,
    1384              :                                                                  IWin2,
    1385            0 :                                                                  state.dataGlobal->HourOfDay,
    1386              :                                                                  ISunPos,
    1387              :                                                                  SkyObstructionMult,
    1388              :                                                                  RWIN2,
    1389              :                                                                  Ray,
    1390              :                                                                  PHRAY,
    1391              :                                                                  LSHCAL,
    1392              :                                                                  InShelfSurf,
    1393              :                                                                  COSB,
    1394              :                                                                  ObTrans,
    1395              :                                                                  TVISB,
    1396              :                                                                  DOMEGA,
    1397              :                                                                  ICtrl,
    1398              :                                                                  ShType,
    1399              :                                                                  BlNum,
    1400              :                                                                  THRAY,
    1401              :                                                                  WNORM2,
    1402              :                                                                  extWinType,
    1403              :                                                                  IConst,
    1404              :                                                                  AZVIEW,
    1405              :                                                                  RREF2,
    1406              :                                                                  hitIntObs,
    1407              :                                                                  hitExtObs,
    1408              :                                                                  CalledFor::MapPoint,
    1409              :                                                                  TVISIntWin,
    1410              :                                                                  TVISIntWinDisk,
    1411              :                                                                  mapNum);
    1412              :                     }
    1413              :                 } // End of window Y-element loop, IY
    1414              :             } // End of window X-element loop, IX
    1415              : 
    1416          100 :             if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
    1417              :                 // Loop again over hourly sun positions and calculate daylight factors by adding
    1418              :                 // direct and inter-reflected illum components, then dividing by exterior horiz illum.
    1419              :                 // Also calculate corresponding glare factors.
    1420          100 :                 ILB = IL;
    1421         2500 :                 for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
    1422         2400 :                     FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, IHR, IWin, loopwin, ICtrl);
    1423              :                 } // End of sun position loop, IHR
    1424              :             } else {
    1425            0 :                 ILB = IL;
    1426            0 :                 FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, state.dataGlobal->HourOfDay, IWin, loopwin, ICtrl);
    1427              :             }
    1428              : 
    1429              :         } // End of window loop, loopwin - IWin
    1430              : 
    1431          100 :     } // End of reference point loop, IL
    1432            1 : }
    1433              : 
    1434         1465 : void FigureDayltgCoeffsAtPointsSetupForWindow(EnergyPlusData &state,
    1435              :                                               int const daylightCtrlNum, // zero if called for map points
    1436              :                                               int const iRefPoint,
    1437              :                                               int const loopwin,
    1438              :                                               CalledFor const CalledFrom,    // indicate  which type of routine called this routine
    1439              :                                               Vector3<Real64> const &RREF,   // Location of a reference point in absolute coordinate system
    1440              :                                               Vector3<Real64> const &VIEWVC, // View vector in absolute coordinate system
    1441              :                                               int &IWin,
    1442              :                                               int &IWin2,
    1443              :                                               int &NWX,
    1444              :                                               int &NWY,
    1445              :                                               Vector3<Real64> &W2,     // Second vertex of window
    1446              :                                               Vector3<Real64> &W3,     // Third vertex of window
    1447              :                                               Vector3<Real64> &W21,    // Vector from window vertex 2 to window vertex 1
    1448              :                                               Vector3<Real64> &W23,    // Vector from window vertex 2 to window vertex 3
    1449              :                                               int &LSHCAL,             // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    1450              :                                               int &InShelfSurf,        // Inside daylighting shelf surface number
    1451              :                                               int &ICtrl,              // Window control counter
    1452              :                                               WinShadingType &ShType,  // Window shading type
    1453              :                                               int &BlNum,              // Window blind number
    1454              :                                               Vector3<Real64> &WNORM2, // Unit vector normal to window
    1455              :                                               ExtWinType &extWinType,  // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1456              :                                               int &IConst,             // Construction counter
    1457              :                                               Vector3<Real64> &RREF2,  // Location of virtual reference point in absolute coordinate system
    1458              :                                               Real64 &DWX,             // Horizontal dimension of window element (m)
    1459              :                                               Real64 &DWY,             // Vertical dimension of window element (m)
    1460              :                                               Real64 &DAXY,            // Area of window element
    1461              :                                               Vector3<Real64> &U2,     // Second vertex of window for TDD:DOME (if exists)
    1462              :                                               Vector3<Real64> &U23,    // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
    1463              :                                               Vector3<Real64> &U21,    // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
    1464              :                                               Vector3<Real64> &VIEWVC2, // Virtual view vector in absolute coordinate system
    1465              :                                               bool &is_Rectangle,       // True if window is rectangular
    1466              :                                               bool &is_Triangle,        // True if window is triangular
    1467              :                                               int const MapNum)
    1468              : {
    1469              :     // SUBROUTINE INFORMATION:
    1470              :     //       AUTHOR         B. Griffith
    1471              :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    1472              : 
    1473              :     // PURPOSE OF THIS SUBROUTINE:
    1474              :     // collect code to setup calculations for each window for daylighting coefficients
    1475              : 
    1476              :     // METHODOLOGY EMPLOYED:
    1477              :     // switch as need to serve both reference points and map points based on calledFrom
    1478         1465 :     auto &dl = state.dataDayltg;
    1479         1465 :     auto &s_surf = state.dataSurface;
    1480              : 
    1481              :     int ShelfNum; // Daylighting shelf object number
    1482              :     int NDIVX;    // Number of window x divisions for daylighting calc
    1483              :     int NDIVY;    // Number of window y divisions for daylighting calc
    1484              :     Real64 ALF;   // Distance from reference point to window plane (m)
    1485              :     Real64 D1a;   // Projection of vector from window origin to reference
    1486              :     //  on window X  axis (m)
    1487              :     Real64 D1b; // Projection of vector from window origin to reference
    1488              :     //  on window Y axis (m)
    1489              :     Real64 SolidAngExtWin;    // Approx. solid angle subtended by an ext. window wrt ref pt
    1490              :     Real64 SolidAngMinIntWin; // Approx. smallest solid angle subtended by an int. window wrt ref pt
    1491              :     Real64 SolidAngRatio;     // Ratio of SolidAngExtWin and SolidAngMinIntWin
    1492              :     Real64 SinCornerAng;      // For triangle, sine of corner angle of window element
    1493              : 
    1494         1465 :     int zoneNum = 0; // zone number
    1495         1465 :     int enclNum = 0; // enclosure number
    1496              : 
    1497         1465 :     Vector3<Real64> W1 = {0.0, 0.0, 0.0};
    1498         1465 :     Vector3<Real64> WC = {0.0, 0.0, 0.0};
    1499              : 
    1500         1465 :     if (CalledFrom == CalledFor::RefPoint) {
    1501         1365 :         auto &daylCtrl = dl->daylightControl(daylightCtrlNum);
    1502         1365 :         daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAng = 0.0;
    1503         1365 :         daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAngWtd = 0.0;
    1504         1365 :         zoneNum = daylCtrl.zoneIndex;
    1505         1365 :         enclNum = daylCtrl.enclIndex;
    1506          100 :     } else if (CalledFrom == CalledFor::MapPoint) {
    1507          100 :         assert(MapNum > 0);
    1508          100 :         auto const &illumMap = dl->illumMaps(MapNum);
    1509          100 :         zoneNum = illumMap.zoneIndex;
    1510          100 :         enclNum = illumMap.enclIndex;
    1511              :     }
    1512         1465 :     IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
    1513              : 
    1514         1465 :     auto &surf = s_surf->Surface(IWin);
    1515         1465 :     auto &surfWin = s_surf->SurfaceWindow(IWin);
    1516              : 
    1517         1465 :     if (s_surf->Surface(surf.BaseSurf).SolarEnclIndex == enclNum) {
    1518         1465 :         extWinType = ExtWinType::InZone;
    1519              :     } else {
    1520            0 :         extWinType = ExtWinType::AdjZone;
    1521              :     }
    1522              : 
    1523         1465 :     IConst = s_surf->SurfActiveConstruction(IWin);
    1524              : 
    1525              :     // For thermochromic windows, the daylight and glare factors are calculated for a base window cosntruction
    1526              :     //  at base TC layer temperature. During each time step calculations at DayltgInteriorIllum,
    1527              :     //  DayltgInteriorMapIllum, and DayltgGlare, the daylight and glare factors are adjusted by the visible
    1528              :     //  transmittance ratio = VT of actual TC window based on last hour TC layer temperature / VT of the base TC window
    1529         1465 :     if (state.dataConstruction->Construct(IConst).isTCWindow) {
    1530              :         // For thermochromic windows, use the base window construction at base temperature of the TC layer
    1531            0 :         IConst = state.dataConstruction->Construct(IConst).TCMasterConstrNum;
    1532              :     }
    1533              : 
    1534         1465 :     ICtrl = surf.activeWindowShadingControl;
    1535         1465 :     ShType = WinShadingType::NoShade; // 'NOSHADE'
    1536         1465 :     BlNum = 0;
    1537              :     // ScNum = 0; //Unused Set but never used
    1538         1465 :     if (surf.HasShadeControl) {
    1539            0 :         ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
    1540              :     }
    1541         1465 :     if (ANY_BLIND(ShType)) {
    1542            0 :         BlNum = s_surf->surfShades(IWin).blind.matNum;
    1543              :     }
    1544              :     // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
    1545              : 
    1546         1465 :     ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
    1547         1465 :     if (ShelfNum > 0) {
    1548            0 :         InShelfSurf =
    1549            0 :             state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf; // Inside daylighting shelf present if > 0
    1550              :     } else {
    1551         1465 :         InShelfSurf = 0;
    1552              :     }
    1553              : 
    1554         1465 :     is_Rectangle = false;
    1555         1465 :     is_Triangle = false;
    1556         1465 :     if (surf.Sides == 3) {
    1557            0 :         is_Triangle = true;
    1558              :     }
    1559         1465 :     if (surf.Sides == 4) {
    1560         1465 :         is_Rectangle = true;
    1561              :     }
    1562              : 
    1563         1465 :     if (is_Rectangle) {
    1564              :         // Vertices of window (numbered counter-clockwise starting at upper left as viewed
    1565              :         // from inside of room). Assumes original vertices are numbered counter-clockwise from
    1566              :         // upper left as viewed from outside.
    1567         1465 :         W3 = surf.Vertex(2);
    1568         1465 :         W2 = surf.Vertex(3);
    1569         1465 :         W1 = surf.Vertex(4);
    1570            0 :     } else if (is_Triangle) {
    1571            0 :         W3 = surf.Vertex(2);
    1572            0 :         W2 = surf.Vertex(3);
    1573            0 :         W1 = surf.Vertex(1);
    1574              :     }
    1575              : 
    1576              :     // Shade/blind calculation flag
    1577         1465 :     LSHCAL = 0;
    1578              : 
    1579              :     // Visible transmittance at normal incidence
    1580         1465 :     s_surf->SurfWinVisTransSelected(IWin) = Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    1581              :     // For windows with switchable glazing, ratio of visible transmittance at normal
    1582              :     // incidence for fully switched (dark) state to that of unswitched state
    1583         1465 :     s_surf->SurfWinVisTransRatio(IWin) = 1.0;
    1584         1465 :     if (ICtrl > 0) {
    1585            0 :         if (ShType == WinShadingType::SwitchableGlazing) {
    1586            0 :             int IConstShaded = surf.activeShadedConstruction; // Shaded construction counter
    1587            0 :             s_surf->SurfWinVisTransRatio(IWin) =
    1588            0 :                 General::SafeDivide(Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef),
    1589            0 :                                     Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef));
    1590              :         }
    1591              :     }
    1592              : 
    1593              :     // Unit vectors from window vertex 2 to 1 and 2 to 3,
    1594              :     // center point of window, and vector from ref pt to center of window
    1595         1465 :     W21 = W1 - W2;
    1596         1465 :     W23 = W3 - W2;
    1597         1465 :     Real64 HW = W21.magnitude();
    1598         1465 :     Real64 WW = W23.magnitude();
    1599         1465 :     if (is_Rectangle) {
    1600         1465 :         WC = W2 + (W23 + W21) / 2.0;
    1601            0 :     } else if (is_Triangle) {
    1602            0 :         WC = W2 + (W23 + W21) / 3.0;
    1603              :     }
    1604         1465 :     s_surf->SurfaceWindow(IWin).WinCenter = WC;
    1605         1465 :     Vector3<Real64> REFWC = WC - RREF;
    1606              :     // Unit vectors
    1607         1465 :     W21 /= HW;
    1608         1465 :     W23 /= WW;
    1609              : 
    1610              :     // Unit vector normal to window (pointing away from room)
    1611         1465 :     Vector3<Real64> WNORM = surf.lcsz;
    1612              : 
    1613              :     // Initialize number of window elements
    1614         1465 :     NDIVX = 40; // Does this mean that windows are split into 1,600 points for daylighting? WHYYYYYY?
    1615         1465 :     NDIVY = 40;
    1616              : 
    1617              :     // Distance from ref point to window plane
    1618         1465 :     ALF = std::abs(dot(WNORM, REFWC));
    1619         1465 :     if (CalledFrom == CalledFor::RefPoint) {
    1620              :         // Check if ref point to close to window due to input error (0.1524 m below is 0.5 ft)
    1621         1365 :         if (ALF < 0.1524 && extWinType == ExtWinType::InZone) {
    1622              :             // Ref pt is close to window plane. Get vector from window
    1623              :             // origin to projection of ref pt on window plane.
    1624            0 :             Vector3<Real64> W2REF = RREF + ALF * WNORM - W2;
    1625              : 
    1626            0 :             D1a = dot(W2REF, W23);
    1627            0 :             D1b = dot(W2REF, W21);
    1628              : 
    1629              :             //            ! Error message if ref pt is too close to window.
    1630            0 :             if (D1a > 0.0 && D1b > 0.0 && D1b <= HW && D1a <= WW) {
    1631            0 :                 ShowSevereError(
    1632              :                     state,
    1633            0 :                     format("CalcDaylightCoeffRefPoints: Daylighting calculation cannot be done for Daylighting:Controls={} because reference point "
    1634              :                            "#{} is less than 0.15m (6\") from window plane {}",
    1635            0 :                            dl->daylightControl(daylightCtrlNum).Name,
    1636              :                            iRefPoint,
    1637            0 :                            surf.Name));
    1638            0 :                 ShowContinueError(state, format("Distance=[{:.5R}]. This is too close; check position of reference point.", ALF));
    1639            0 :                 ShowFatalError(state, "Program terminates due to preceding condition.");
    1640              :             }
    1641         1365 :         } else if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
    1642            0 :             if (dl->RefErrIndex(iRefPoint, IWin) == 0) { // only show error message once
    1643            0 :                 ShowWarningError(state,
    1644            0 :                                  format("CalcDaylightCoeffRefPoints: For Daylghting:Controls=\"{}\" External Window=\"{}\"in Zone=\"{}\" reference "
    1645              :                                         "point is less than 0.15m (6\") from window plane ",
    1646            0 :                                         dl->daylightControl(daylightCtrlNum).Name,
    1647            0 :                                         surf.Name,
    1648            0 :                                         state.dataHeatBal->Zone(surf.Zone).Name));
    1649            0 :                 ShowContinueError(state,
    1650            0 :                                   format("Distance=[{:.1R} m] to ref point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Daylighting Calcs may result.",
    1651              :                                          ALF,
    1652            0 :                                          RREF.x,
    1653            0 :                                          RREF.y,
    1654            0 :                                          RREF.z));
    1655            0 :                 dl->RefErrIndex(iRefPoint, IWin) = 1;
    1656              :             }
    1657              :         }
    1658          100 :     } else if (CalledFrom == CalledFor::MapPoint) {
    1659          100 :         if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
    1660            0 :             if (dl->MapErrIndex(iRefPoint, IWin) == 0) { // only show error message once
    1661            0 :                 ShowWarningError(state,
    1662            0 :                                  format("CalcDaylightCoeffMapPoints: For Zone=\"{}\" External Window=\"{}\"in Zone=\"{}\" map point is less than "
    1663              :                                         "0.15m (6\") from window plane ",
    1664            0 :                                         state.dataHeatBal->Zone(zoneNum).Name,
    1665            0 :                                         surf.Name,
    1666            0 :                                         state.dataHeatBal->Zone(surf.Zone).Name));
    1667            0 :                 ShowContinueError(
    1668              :                     state,
    1669            0 :                     format("Distance=[{:.1R} m] map point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Map Calcs may result.", ALF, RREF.x, RREF.y, RREF.z));
    1670            0 :                 dl->MapErrIndex(iRefPoint, IWin) = 1;
    1671              :             }
    1672              :         }
    1673              :     }
    1674              :     // Number of window elements in X and Y for daylighting calculation
    1675         1465 :     if (ALF > 0.1524) {
    1676         1455 :         NDIVX = 1 + int(4.0 * WW / ALF);
    1677         1455 :         NDIVY = 1 + int(4.0 * HW / ALF);
    1678              :     }
    1679              : 
    1680         1465 :     if (extWinType == ExtWinType::AdjZone) {
    1681              :         // Adjust number of exterior window elements to give acceptable number of rays through
    1682              :         // interior windows in the zone (for accuracy of interior window daylighting calculation)
    1683            0 :         SolidAngExtWin = General::SafeDivide(((surf.Area + s_surf->SurfWinDividerArea(IWin)) / surf.Multiplier), pow_2(ALF));
    1684            0 :         SolidAngMinIntWin = dl->enclDaylight(enclNum).MinIntWinSolidAng;
    1685            0 :         SolidAngRatio = max(1.0, SolidAngExtWin / SolidAngMinIntWin);
    1686            0 :         NDIVX *= std::sqrt(SolidAngRatio);
    1687            0 :         NDIVY *= std::sqrt(SolidAngRatio);
    1688              :     }
    1689              : 
    1690         1465 :     NWX = min(40, NDIVX);
    1691         1465 :     NWY = min(40, NDIVY);
    1692              : 
    1693              :     // Discretization of triangle is simpler if NWX = NWY
    1694         1465 :     if (is_Triangle) {
    1695            0 :         NWX = max(NWX, NWY);
    1696            0 :         NWY = NWX;
    1697              :     }
    1698              : 
    1699              :     // Edge lengths of window elements
    1700         1465 :     DWX = WW / NWX;
    1701         1465 :     DWY = HW / NWY;
    1702              : 
    1703              :     // Azimuth and altitude of window normal
    1704         1465 :     surfWin.phi = std::asin(WNORM.z);
    1705         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;
    1706              : 
    1707              :     // Recalculation of values for TDD:DOME
    1708         1465 :     if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    1709              : 
    1710              :         // Look up the TDD:DOME object
    1711            0 :         int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
    1712            0 :         IWin2 = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
    1713              : 
    1714            0 :         auto &surf2 = s_surf->Surface(IWin2);
    1715            0 :         auto &surfWin2 = s_surf->SurfaceWindow(IWin2);
    1716              : 
    1717              :         // Calculate reference point coords relative to the diffuser coordinate system
    1718              :         // W21, W23, and WNORM are the unit vectors
    1719            0 :         Vector3<Real64> REFD = {dot(REFWC, W21), dot(REFWC, W23), dot(REFWC, WNORM)};
    1720              : 
    1721              :         // Calculate view vector coords relative to the diffuser coordinate system
    1722            0 :         Vector3<Real64> VIEWVD = {dot(VIEWVC, W21), dot(VIEWVC, W23), dot(VIEWVC, WNORM)};
    1723              : 
    1724            0 :         Vector3<Real64> U3 = surf2.Vertex(2);
    1725            0 :         U2 = surf2.Vertex(3);
    1726            0 :         Vector3<Real64> U1;
    1727              : 
    1728            0 :         if (surf2.Sides == 4) {
    1729              :             // Vertices of window (numbered counter-clockwise starting
    1730              :             // at upper left as viewed from inside of room)
    1731              :             // Assumes original vertices are numbered counter-clockwise from
    1732              :             // upper left as viewed from outside.
    1733            0 :             U3 = surf2.Vertex(2);
    1734            0 :             U2 = surf2.Vertex(3);
    1735            0 :             U1 = surf2.Vertex(4);
    1736            0 :         } else if (surf2.Sides == 3) {
    1737            0 :             U3 = surf2.Vertex(2);
    1738            0 :             U2 = surf2.Vertex(3);
    1739            0 :             U1 = surf2.Vertex(1);
    1740              :         }
    1741              : 
    1742              :         // Unit vectors from window vertex 2 to 1 and 2 to 3,
    1743              :         // center point of window, and vector from ref pt to center of window
    1744            0 :         U21 = U1 - U2;
    1745            0 :         U23 = U3 - U2;
    1746            0 :         HW = U21.magnitude();
    1747            0 :         WW = U23.magnitude();
    1748            0 :         if (surf2.Sides == 4) {
    1749            0 :             WC = U2 + (U23 + U21) / 2.0;
    1750            0 :         } else if (surf2.Sides == 3) {
    1751            0 :             WC = U2 + (U23 + U21) / 3.0;
    1752              :         }
    1753            0 :         s_surf->SurfaceWindow(IWin2).WinCenter = WC;
    1754              :         // Unit vectors
    1755            0 :         U21 /= HW;
    1756            0 :         U23 /= WW;
    1757              : 
    1758              :         // Unit vector normal to dome (pointing away from TDD)
    1759              :         // These are specific to the exterior.
    1760              :         // NOTE:  Preserve WNORM for later in the code.
    1761            0 :         WNORM2 = cross(U21, U23).normalize();
    1762              : 
    1763              :         // Azimuth and altitude of dome normal
    1764              :         // These are specific to the exterior.
    1765            0 :         surfWin2.phi = std::asin(WNORM2.z);
    1766            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;
    1767              : 
    1768              :         // Calculate new virtual reference point coords relative to dome coord system
    1769              :         // W21, W23, and WNORM2 are now the unit vectors for the dome coord system
    1770            0 :         REFWC = REFD.x * U21 + REFD.y * U23 + REFD.z * WNORM2;
    1771            0 :         RREF2 = WC - REFWC;
    1772              : 
    1773              :         // Calculate new virtual view vector coords relative to dome coord system
    1774            0 :         VIEWVC2 = VIEWVD.x * U21 + VIEWVD.y * U23 + VIEWVD.z * WNORM2;
    1775              : 
    1776              :         // Copy several values from the diffuser so that DayltgInterReflectedIllum works correctly
    1777              :         // These are specific to the interior.
    1778            0 :         surfWin2.rhoCeilingWall = surfWin.rhoCeilingWall;
    1779            0 :         surfWin2.rhoFloorWall = surfWin.rhoFloorWall;
    1780            0 :         surfWin2.fractionUpgoing = surfWin.fractionUpgoing;
    1781            0 :         surfWin2.glazedFrac = surfWin.glazedFrac;
    1782              : 
    1783            0 :     } else {
    1784              :         // This is not a TDD:DIFFUSER.  Make sure nothing is messed up for a regular window.
    1785         1465 :         IWin2 = IWin;
    1786         1465 :         WNORM2 = WNORM;
    1787         1465 :         RREF2 = RREF;
    1788         1465 :         VIEWVC2 = VIEWVC;
    1789              : 
    1790         1465 :         U2 = W2;
    1791         1465 :         U21 = W21;
    1792         1465 :         U23 = W23;
    1793              :     }
    1794              : 
    1795              :     // Initialize bsdf daylighting coefficients here.  Only one time initialization
    1796         1465 :     if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
    1797            0 :         if (!state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized) {
    1798            0 :             int NRefPts = 0;
    1799            0 :             if (CalledFrom == CalledFor::MapPoint) {
    1800            0 :                 NRefPts = dl->illumMaps(MapNum).TotalMapRefPoints;
    1801            0 :             } else if (CalledFrom == CalledFor::RefPoint) {
    1802            0 :                 NRefPts = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
    1803              :             }
    1804            0 :             InitializeCFSDaylighting(state, daylightCtrlNum, IWin, NWX, NWY, RREF, NRefPts, iRefPoint, CalledFrom, MapNum);
    1805              :             // if ((WinEl == (NWX * NWY)).and.(CalledFrom == CalledForMapPoint).and.(NRefPts == iRefPoint)) then
    1806            0 :             if ((CalledFrom == CalledFor::MapPoint) && (NRefPts == iRefPoint)) {
    1807            0 :                 state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized = true;
    1808              :             }
    1809              :         }
    1810              :     }
    1811              : 
    1812         1465 :     int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
    1813         1465 :     int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
    1814              : 
    1815         5483 :     for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
    1816              :         // Initialize sky and sun components of direct illuminance (arrays EDIRSK, EDIRSU, EDIRSUdisk)
    1817              :         // and average window luminance (arrays AVWLSK, AVWLSU, AVWLSUdisk), at ref pt.
    1818        16072 :         dl->dirIllum(iHr)[iWinCover_Bare] = dl->dirIllum(iHr)[iWinCover_Shaded] = Illums();
    1819        16072 :         dl->avgWinLum(iHr)[iWinCover_Bare] = dl->avgWinLum(iHr)[iWinCover_Shaded] = Illums();
    1820              :     }
    1821              : 
    1822         1465 :     if (CalledFrom == CalledFor::RefPoint) {
    1823              :         // Initialize solid angle subtended by window wrt ref pt
    1824              :         // and solid angle weighted by glare position factor
    1825         1365 :         s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAng = 0.0;
    1826         1365 :         s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd = 0.0;
    1827              :     }
    1828              :     // Area of window element
    1829         1465 :     if (is_Rectangle) {
    1830         1465 :         DAXY = DWX * DWY;
    1831            0 :     } else if (is_Triangle) {
    1832            0 :         SinCornerAng = std::sqrt(1.0 - pow_2(dot(W21, W23)));
    1833            0 :         DAXY = DWX * DWY * SinCornerAng;
    1834              :     }
    1835         1465 : }
    1836              : 
    1837       368101 : void FigureDayltgCoeffsAtPointsForWindowElements(
    1838              :     EnergyPlusData &state,
    1839              :     int const daylightCtrlNum, // Current daylighting control number (only used when called from RefPoint)
    1840              :     int const iRefPoint,
    1841              :     int const loopwin,
    1842              :     CalledFor const CalledFrom, // indicate  which type of routine called this routine
    1843              :     int const WinEl,            // Current window element number
    1844              :     int const IWin,
    1845              :     int const IWin2,
    1846              :     int const iXelement,
    1847              :     int const iYelement,
    1848              :     Real64 &SkyObstructionMult,
    1849              :     Vector3<Real64> const &W2,      // Second vertex of window
    1850              :     Vector3<Real64> const &W21,     // Vector from window vertex 2 to window vertex 1
    1851              :     Vector3<Real64> const &W23,     // Vector from window vertex 2 to window vertex 3
    1852              :     Vector3<Real64> const &RREF,    // Location of a reference point in absolute coordinate system
    1853              :     int const NWYlim,               // For triangle, largest NWY for a given IX
    1854              :     Vector3<Real64> const &VIEWVC2, // Virtual view vector in absolute coordinate system
    1855              :     Real64 const DWX,               // Horizontal dimension of window element (m)
    1856              :     Real64 const DWY,               // Vertical dimension of window element (m)
    1857              :     Real64 const DAXY,              // Area of window element
    1858              :     Vector3<Real64> const &U2,      // Second vertex of window for TDD:DOME (if exists)
    1859              :     Vector3<Real64> const &U23,     // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
    1860              :     Vector3<Real64> const &U21,     // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
    1861              :     Vector3<Real64> &RWIN,          // Center of a window element for TDD:DOME (if exists) in abs coord sys
    1862              :     Vector3<Real64> &RWIN2,         // Center of a window element for TDD:DOME (if exists) in abs coord sys
    1863              :     Vector3<Real64> &Ray,           // Unit vector along ray from reference point to window element
    1864              :     Real64 &PHRAY,                  // Altitude of ray from reference point to window element (radians)
    1865              :     int &LSHCAL,                    // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    1866              :     Real64 &COSB,                   // Cosine of angle between window outward normal and ray from reference point to window element
    1867              :     Real64 &ObTrans,                // Product of solar transmittances of exterior obstructions hit by ray
    1868              :     Real64 &TVISB,                  // Visible transmittance of window for COSB angle of incidence (times light well
    1869              :     Real64 &DOMEGA,                 // Solid angle subtended by window element wrt reference point (steradians)
    1870              :     Real64 &THRAY,                  // Azimuth of ray from reference point to window element (radians)
    1871              :     bool &hitIntObs,                // True iff interior obstruction hit
    1872              :     bool &hitExtObs,                // True iff ray from ref pt to ext win hits an exterior obstruction
    1873              :     Vector3<Real64> const &WNORM2,  // Unit vector normal to window
    1874              :     ExtWinType const extWinType,    // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    1875              :     int const IConst,               // Construction counter
    1876              :     Vector3<Real64> const &RREF2,   // Location of virtual reference point in absolute coordinate system
    1877              :     bool const is_Triangle,
    1878              :     Real64 &TVISIntWin,     // Visible transmittance of int win at COSBIntWin for light from ext win
    1879              :     Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
    1880              :     int const MapNum)
    1881              : {
    1882              : 
    1883              :     // SUBROUTINE INFORMATION:
    1884              :     //       AUTHOR         B. Griffith
    1885              :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    1886              : 
    1887              :     // PURPOSE OF THIS SUBROUTINE:
    1888              :     // collect code to do calculations for each window element for daylighting coefficients
    1889              : 
    1890              :     // REFERENCES:
    1891              :     // switch as need to serve both reference points and map points based on calledFrom
    1892       368101 :     auto &dl = state.dataDayltg;
    1893       368101 :     auto &s_surf = state.dataSurface;
    1894              : 
    1895              :     Real64 RR; // Distance from ref point to intersection of view vector
    1896              :     //  and plane normal to view vector and window element (m)
    1897              :     Real64 ASQ; // Square of distance from above intersection to window element (m2)
    1898              :     Real64 YD;  // Vertical displacement of window element wrt ref point
    1899              : 
    1900              :     Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
    1901              :     //  exterior window element or between ref pt and sun
    1902              : 
    1903              :     // Local complex fenestration variables
    1904              :     Real64 TransBeam; // Obstructions transmittance for incoming BSDF rays (temporary variable)
    1905              : 
    1906       368101 :     auto &surfWin = s_surf->SurfaceWindow(IWin);
    1907              : 
    1908       368101 :     ++LSHCAL;
    1909       368101 :     SkyObstructionMult = 1.0;
    1910              : 
    1911              :     // Center of win element in absolute coord sys
    1912       368101 :     RWIN = W2 + (double(iXelement) - 0.5) * W23 * DWX + (double(iYelement) - 0.5) * W21 * DWY;
    1913              : 
    1914              :     // Center of win element on TDD:DOME in absolute coord sys
    1915              :     // If no TDD, RWIN2 = RWIN
    1916       368101 :     RWIN2 = U2 + (double(iXelement) - 0.5) * U23 * DWX + (double(iYelement) - 0.5) * U21 * DWY;
    1917              : 
    1918              :     // Distance between ref pt and window element
    1919       368101 :     Real64 DIS = distance(RWIN, RREF);
    1920              : 
    1921              :     // Unit vector along ray from ref pt to element
    1922       368101 :     Ray = (RWIN - RREF) / DIS;
    1923              : 
    1924              :     // Cosine of angle between ray and window outward normal
    1925       368101 :     COSB = dot(WNORM2, Ray);
    1926              : 
    1927              :     // If COSB > 0, direct light from window can reach ref pt. Otherwise go to loop
    1928              :     // over sun position and calculate inter-reflected component of illuminance
    1929       368101 :     if (COSB <= 0.0) {
    1930           80 :         return;
    1931              :     }
    1932              : 
    1933              :     // Azimuth (-pi to pi) and altitude (-pi/2 to pi/2) of ray. Azimuth = 0 is along east.
    1934       368021 :     PHRAY = std::asin(Ray.z);
    1935       368021 :     if (std::abs(Ray.x) > 1.0e-5 || std::abs(Ray.y) > 1.0e-5) {
    1936       368021 :         THRAY = std::atan2(Ray.y, Ray.x);
    1937              :     } else {
    1938            0 :         THRAY = 0.0;
    1939              :     }
    1940              : 
    1941              :     // Solid angle subtended by element wrt ref pt.
    1942       368021 :     Real64 DAXY1 = DAXY; // For triangle, area of window element at end of column
    1943              :     // For triangle, at end of Y column only one half of parallelopiped's area contributes
    1944       368021 :     if (is_Triangle && iYelement == NWYlim) {
    1945            0 :         DAXY1 = 0.5 * DAXY;
    1946              :     }
    1947       368021 :     DOMEGA = DAXY1 * COSB / (DIS * DIS);
    1948              : 
    1949              :     // Calculate position factor (used in glare calculation) for this
    1950              :     // win element / ref pt / view-vector combination
    1951       368021 :     Real64 POSFAC = 0.0;
    1952              : 
    1953              :     // Distance from ref pt to intersection of view vector and plane
    1954              :     // normal to view vector containing the window element
    1955              : 
    1956       368021 :     if (CalledFrom == CalledFor::RefPoint) {
    1957       346971 :         RR = DIS * dot(Ray, VIEWVC2);
    1958       346971 :         if (RR > 0.0) {
    1959              :             // Square of distance from above intersection point to win element
    1960          347 :             ASQ = DIS * DIS - RR * RR;
    1961              :             // Vertical displacement of win element wrt ref pt
    1962          347 :             YD = RWIN2.z - RREF2.z;
    1963              :             // Horizontal and vertical displacement ratio and position factor
    1964          347 :             Real64 XR = std::sqrt(std::abs(ASQ - YD * YD)) / RR;
    1965          347 :             Real64 YR = std::abs(YD / RR);
    1966          347 :             POSFAC = DayltgGlarePositionFactor(XR, YR);
    1967              :         }
    1968              :     }
    1969              : 
    1970       368021 :     hitIntObs = false;
    1971       368021 :     int IntWinHitNum = 0;   // Surface number of interior window that is intersected
    1972       368021 :     bool hitIntWin = false; // Ray from ref pt passes through interior window
    1973       368021 :     TVISIntWinDisk = 0.0;   // Init Value
    1974       368021 :     TVISIntWin = 0.0;
    1975              : 
    1976       368021 :     Vector3<Real64> HitPtIntWin = {0.0, 0.0, 0.0};
    1977       368021 :     auto const &surf = s_surf->Surface(IWin);
    1978       368021 :     if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    1979              :         // Look up the TDD:DOME object
    1980            0 :         int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
    1981              :         // Unshaded visible transmittance of TDD for a single ray from sky/ground element
    1982            0 :         TVISB = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    1983              : 
    1984              :     } else { // Regular window
    1985       368021 :         if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    1986              :             // Vis trans of glass for COSB incidence angle
    1987       368021 :             TVISB = Window::POLYF(COSB, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    1988              :         } else {
    1989              :             // Complex fenestration needs to use different equation for visible transmittance.  That will be calculated later
    1990              :             // in the code since it depends on different incoming directions.  For now, just put zero to differentiate from
    1991              :             // regular windows
    1992            0 :             TVISB = 0.0;
    1993              :         }
    1994       368021 :         if (extWinType == ExtWinType::AdjZone) {
    1995            0 :             int zoneNum = 0;
    1996            0 :             if (CalledFrom == CalledFor::RefPoint) {
    1997            0 :                 zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    1998            0 :             } else if (CalledFrom == CalledFor::MapPoint) {
    1999            0 :                 assert(MapNum > 0);
    2000            0 :                 zoneNum = dl->illumMaps(MapNum).zoneIndex;
    2001              :             }
    2002              :             // Does ray pass through an interior window in zone (ZoneNum) containing the ref point?
    2003            0 :             for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2004            0 :                 auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    2005            0 :                 for (int IntWin = thisSpace.WindowSurfaceFirst; IntWin <= thisSpace.WindowSurfaceLast; ++IntWin) {
    2006            0 :                     auto const &surfIntWin = s_surf->Surface(IntWin);
    2007              :                     // in develop this was Surface(IntWin).Class == SurfaceClass::Window && Surface(IntWin).ExtBoundCond >= 1
    2008            0 :                     if (surfIntWin.ExtBoundCond < 1) {
    2009            0 :                         continue;
    2010              :                     }
    2011              : 
    2012            0 :                     if (s_surf->Surface(surfIntWin.ExtBoundCond).Zone != surf.Zone) {
    2013            0 :                         continue;
    2014              :                     }
    2015              : 
    2016            0 :                     hitIntWin = PierceSurface(state, IntWin, RREF, Ray, HitPtIntWin);
    2017            0 :                     if (hitIntWin) {
    2018            0 :                         IntWinHitNum = IntWin;
    2019            0 :                         COSBIntWin = dot(surfIntWin.OutNormVec, Ray);
    2020            0 :                         if (COSBIntWin <= 0.0) {
    2021            0 :                             hitIntWin = false;
    2022            0 :                             IntWinHitNum = 0;
    2023            0 :                             continue;
    2024              :                         }
    2025            0 :                         TVISIntWin = Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWin.Construction).TransVisBeamCoef);
    2026            0 :                         TVISB *= TVISIntWin;
    2027            0 :                         break; // Ray passes thru interior window; exit from DO loop
    2028              :                     }
    2029              :                 }
    2030            0 :             } // End of loop over surfaces in zone ZoneNum
    2031              : 
    2032            0 :             if (!hitIntWin) {
    2033              :                 // Ray does not pass through an int win in ZoneNum. Therefore, it hits the opaque part
    2034              :                 // of a surface between ref point in ZoneNum and ext win element in adjacent zone.
    2035            0 :                 hitIntObs = true;
    2036              :             }
    2037              :         } // End of check if this is an ext win in an adjacent zone
    2038              :     } // End of check if TDD:Diffuser or regular exterior window or complex fenestration
    2039              : 
    2040              :     // Check for interior obstructions
    2041       368021 :     if (extWinType == ExtWinType::InZone && !hitIntObs) {
    2042              :         // Check for obstruction between reference point and window element
    2043              :         // Returns hitIntObs = true iff obstruction is hit
    2044              :         // (Example of interior obstruction is a wall in an L-shaped room that lies
    2045              :         // between reference point and window.)
    2046       368021 :         hitIntObs = DayltgHitInteriorObstruction(state, IWin, RREF, RWIN);
    2047              :     }
    2048              : 
    2049       368021 :     if (extWinType == ExtWinType::AdjZone && IntWinHitNum > 0 && !hitIntObs) {
    2050              :         // Check for obstruction between ref point and interior window through which ray passes
    2051            0 :         hitIntObs = DayltgHitInteriorObstruction(state, IntWinHitNum, RREF, HitPtIntWin);
    2052            0 :         if (!hitIntObs) {
    2053              :             // Check for obstruction between intersection point on int window and ext win element
    2054            0 :             hitIntObs = DayltgHitBetWinObstruction(state, IntWinHitNum, IWin, HitPtIntWin, RWIN);
    2055              :         }
    2056              :     }
    2057       368021 :     if (CalledFrom == CalledFor::RefPoint) {
    2058              :         // Glare calculations only done for regular reference points, not for maps
    2059       346971 :         if (!hitIntObs) {
    2060       346971 :             if (extWinType == ExtWinType::InZone || (extWinType == ExtWinType::AdjZone && hitIntWin)) {
    2061              :                 // Increment solid angle subtended by portion of window above ref pt
    2062       346971 :                 surfWin.refPts(iRefPoint).solidAng += DOMEGA;
    2063       346971 :                 dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAng += DOMEGA;
    2064              :                 // Increment position-factor-modified solid angle
    2065       346971 :                 surfWin.refPts(iRefPoint).solidAngWtd += DOMEGA * POSFAC;
    2066       346971 :                 dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAngWtd += DOMEGA * POSFAC;
    2067              :             }
    2068              :         }
    2069              :     }
    2070       368021 :     if (hitIntObs) {
    2071            0 :         ObTrans = 0.0;
    2072              :     }
    2073              : 
    2074       368021 :     hitExtObs = false;
    2075       368021 :     if (!hitIntObs) {
    2076              :         // No interior obstruction was hit.
    2077              :         // Check for exterior obstructions between window element and sky/ground.
    2078              :         // Get product of transmittances of obstructions hit by ray.
    2079              :         // ObTrans = 1.0 will be returned if no exterior obstructions are hit.
    2080              : 
    2081       368021 :         if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    2082              :             // the IHR (now HourOfDay) here is/was not correct, this is outside of hour loop
    2083              :             // the hour is used to query schedule for transmission , not sure what to do
    2084              :             // it will work for detailed and never did work correctly before.
    2085       368021 :             ObTrans = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin2, RWIN2, Ray);
    2086       368021 :             if (ObTrans < 1.0) {
    2087            0 :                 hitExtObs = true;
    2088              :             }
    2089              :         } else {
    2090              :             // Transmittance from exterior obstruction surfaces is calculated here. This needs to be done for each timestep
    2091              :             // in order to account for changes in exterior surface transmittances
    2092            0 :             int CplxFenState = surfWin.ComplexFen.CurrentState;
    2093            0 :             auto &complexWinDayltgGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CplxFenState);
    2094            0 :             int NReflSurf = 0; // Number of blocked beams for complex fenestration
    2095            0 :             if (CalledFrom == CalledFor::RefPoint) {
    2096            0 :                 NReflSurf = complexWinDayltgGeom.RefPoint(iRefPoint).NReflSurf(WinEl);
    2097            0 :             } else if (CalledFrom == CalledFor::MapPoint) {
    2098            0 :                 NReflSurf = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).NReflSurf(WinEl);
    2099              :             }
    2100              :             int RayIndex;
    2101            0 :             for (int ICplxFen = 1; ICplxFen <= NReflSurf; ++ICplxFen) {
    2102            0 :                 if (CalledFrom == CalledFor::RefPoint) {
    2103            0 :                     RayIndex = complexWinDayltgGeom.RefPoint(iRefPoint).RefSurfIndex(ICplxFen, WinEl);
    2104            0 :                 } else if (CalledFrom == CalledFor::MapPoint) {
    2105            0 :                     RayIndex = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefSurfIndex(ICplxFen, WinEl);
    2106              :                 }
    2107            0 :                 Vector3<Real64> RayVector = state.dataBSDFWindow->ComplexWind(IWin).Geom(CplxFenState).sInc(RayIndex);
    2108              :                 // It will get product of all transmittances
    2109            0 :                 TransBeam = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin, RWIN, RayVector);
    2110              :                 // IF (TransBeam > 0.0d0) ObTrans = TransBeam
    2111            0 :                 if (CalledFrom == CalledFor::RefPoint) {
    2112            0 :                     complexWinDayltgGeom.RefPoint(iRefPoint).TransOutSurf(ICplxFen, WinEl) = TransBeam;
    2113            0 :                 } else if (CalledFrom == CalledFor::MapPoint) {
    2114            0 :                     complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).TransOutSurf(ICplxFen, WinEl) = TransBeam;
    2115              :                 }
    2116            0 :             }
    2117              :             // This will avoid obstruction multiplier calculations for non-CFS window
    2118            0 :             ObTrans = 0.0;
    2119              :         }
    2120              :     }
    2121              : 
    2122       368021 :     if (s_surf->CalcSolRefl && PHRAY < 0.0 && ObTrans > 1.0e-6) {
    2123              :         // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
    2124              :         // by the ray. This effect is given by the ratio SkyObstructionMult =
    2125              :         // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
    2126              :         // This ratio is calculated for an isotropic sky.
    2127              :         // Ground point hit by the ray:
    2128            0 :         Real64 Alfa = std::acos(-Ray.z);
    2129            0 :         Real64 Beta = std::atan2(Ray.y, Ray.x);
    2130              :         // Distance between ground hit point and proj'n of center of window element onto ground (m)
    2131            0 :         Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
    2132            0 :         Vector3<Real64> GroundHitPt = {RWIN2.x + HorDis * std::cos(Beta), RWIN2.y + HorDis * std::sin(Beta), s_surf->GroundLevelZ};
    2133              : 
    2134            0 :         SkyObstructionMult =
    2135            0 :             CalcObstrMultiplier(state, GroundHitPt, DataSurfaces::AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    2136            0 :     } // End of check if solar reflection calculation is in effect
    2137       368021 : } // FigureDayltgCoeffsAtPointsForWindowElements()
    2138              : 
    2139            0 : void InitializeCFSDaylighting(EnergyPlusData &state,
    2140              :                               int const daylightCtrlNum,       // Current daylighting control number
    2141              :                               int const IWin,                  // Complex fenestration number
    2142              :                               int const NWX,                   // Number of horizontal divisions
    2143              :                               int const NWY,                   // Number of vertical divisions
    2144              :                               Vector3<Real64> const &RefPoint, // reference point coordinates
    2145              :                               int const NRefPts,               // Number of reference points
    2146              :                               int const iRefPoint,             // Reference points counter
    2147              :                               CalledFor const CalledFrom,
    2148              :                               int const MapNum)
    2149              : {
    2150              :     // SUBROUTINE INFORMATION:
    2151              :     //       AUTHOR         Simon Vidanovic
    2152              :     //       DATE WRITTEN   April 2013
    2153              : 
    2154              :     // PURPOSE OF THIS SUBROUTINE:
    2155              :     // For incoming BSDF window direction calculates whether bin is coming from sky, ground or reflected surface.
    2156              :     // Routine also calculates intersection points with ground and exterior reflection surfaces.
    2157            0 :     auto &dl = state.dataDayltg;
    2158            0 :     auto &s_surf = state.dataSurface;
    2159              : 
    2160              :     // Object Data
    2161            0 :     DataBSDFWindow::BSDFDaylghtPosition elPos; // altitude and azimuth of intersection element
    2162            0 :     Vector Vec;                                // temporary vector variable
    2163              : 
    2164            0 :     int NumOfWinEl = NWX * NWY; // Number of window elements
    2165              : 
    2166            0 :     auto &surf = s_surf->Surface(IWin);
    2167            0 :     Real64 DWX = surf.Width / NWX;  // Window element width
    2168            0 :     Real64 DWY = surf.Height / NWY; // Window element height
    2169              : 
    2170            0 :     int zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    2171            0 :     Real64 AZVIEW = (dl->daylightControl(daylightCtrlNum).ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth +
    2172            0 :                      state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) *
    2173            0 :                     Constant::DegToRad;
    2174              : 
    2175              :     // Perform necessary calculations for window coordinates and vectors.  This will be used to calculate centroids for
    2176              :     // each window element
    2177            0 :     Vector3<Real64> W1 = {0.0, 0.0, 0.0};
    2178            0 :     Vector3<Real64> W2 = {0.0, 0.0, 0.0};
    2179            0 :     Vector3<Real64> W3 = {0.0, 0.0, 0.0};
    2180              : 
    2181            0 :     if (surf.Sides == 4) {
    2182            0 :         W3 = surf.Vertex(2);
    2183            0 :         W2 = surf.Vertex(3);
    2184            0 :         W1 = surf.Vertex(4);
    2185            0 :     } else if (surf.Sides == 3) {
    2186            0 :         W3 = surf.Vertex(2);
    2187            0 :         W2 = surf.Vertex(3);
    2188            0 :         W1 = surf.Vertex(1);
    2189              :     }
    2190              : 
    2191            0 :     Vector3<Real64> W21 = W1 - W2;
    2192            0 :     W21 /= surf.Height;
    2193            0 :     Vector3<Real64> W23 = W3 - W2;
    2194            0 :     W23 /= surf.Width;
    2195            0 :     Vector3<Real64> WNorm = surf.lcsz;
    2196              : 
    2197            0 :     Real64 WinElArea = DWX * DWY;
    2198            0 :     if (surf.Sides == 3) {
    2199            0 :         WinElArea *= std::sqrt(1.0 - pow_2(dot(W21, W23)));
    2200              :     }
    2201              : 
    2202            0 :     auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
    2203              : 
    2204            0 :     if (CalledFrom == CalledFor::MapPoint) {
    2205              : 
    2206            0 :         if (!allocated(complexWin.IlluminanceMap)) {
    2207            0 :             complexWin.IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
    2208              :         }
    2209              : 
    2210            0 :         AllocateForCFSRefPointsGeometry(complexWin.IlluminanceMap(iRefPoint, MapNum), NumOfWinEl);
    2211              : 
    2212            0 :     } else if (CalledFrom == CalledFor::RefPoint) {
    2213            0 :         if (!allocated(complexWin.RefPoint)) {
    2214            0 :             complexWin.RefPoint.allocate(NRefPts);
    2215              :         }
    2216              : 
    2217            0 :         AllocateForCFSRefPointsGeometry(complexWin.RefPoint(iRefPoint), NumOfWinEl);
    2218              :     }
    2219              : 
    2220              :     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    2221              :     //! Allocation for each complex fenestration state reference points
    2222              :     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    2223            0 :     if (!allocated(complexWin.DaylghtGeom)) {
    2224            0 :         complexWin.DaylghtGeom.allocate(state.dataBSDFWindow->ComplexWind(IWin).NumStates);
    2225              :     }
    2226              : 
    2227              :     // Calculation needs to be performed for each state
    2228            0 :     for (int CurFenState = 1; CurFenState <= complexWin.NumStates; ++CurFenState) {
    2229              :         // number of incident basis directions for current state
    2230            0 :         int NBasis = complexWin.Geom(CurFenState).Inc.NBasis;
    2231              :         // number of outgoing basis directions for current state
    2232            0 :         int NTrnBasis = complexWin.Geom(CurFenState).Trn.NBasis;
    2233              : 
    2234            0 :         if (CalledFrom == CalledFor::MapPoint) {
    2235            0 :             if ((int)dl->illumMaps.size() > 0) {
    2236              :                 // illuminance map for each state
    2237            0 :                 if (!allocated(complexWin.DaylghtGeom(CurFenState).IlluminanceMap)) {
    2238            0 :                     complexWin.DaylghtGeom(CurFenState).IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
    2239              :                 }
    2240              : 
    2241            0 :                 AllocateForCFSRefPointsState(
    2242            0 :                     state, complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum), NumOfWinEl, NBasis, NTrnBasis);
    2243              : 
    2244            0 :                 InitializeCFSStateData(state,
    2245            0 :                                        complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum),
    2246              :                                        complexWin.IlluminanceMap(iRefPoint, MapNum),
    2247              :                                        daylightCtrlNum,
    2248              :                                        IWin,
    2249              :                                        RefPoint,
    2250              :                                        CurFenState,
    2251              :                                        NBasis,
    2252              :                                        NTrnBasis,
    2253              :                                        AZVIEW,
    2254              :                                        NWX,
    2255              :                                        NWY,
    2256              :                                        W2,
    2257              :                                        W21,
    2258              :                                        W23,
    2259              :                                        DWX,
    2260              :                                        DWY,
    2261              :                                        WNorm,
    2262              :                                        WinElArea);
    2263              :             }
    2264              : 
    2265            0 :         } else if (CalledFrom == CalledFor::RefPoint) {
    2266            0 :             if (!allocated(complexWin.DaylghtGeom(CurFenState).RefPoint)) {
    2267            0 :                 complexWin.DaylghtGeom(CurFenState).RefPoint.allocate(NRefPts);
    2268              :             }
    2269              : 
    2270            0 :             AllocateForCFSRefPointsState(state, complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint), NumOfWinEl, NBasis, NTrnBasis);
    2271              : 
    2272            0 :             InitializeCFSStateData(state,
    2273            0 :                                    complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint),
    2274              :                                    complexWin.RefPoint(iRefPoint),
    2275              :                                    daylightCtrlNum,
    2276              :                                    IWin,
    2277              :                                    RefPoint,
    2278              :                                    CurFenState,
    2279              :                                    NBasis,
    2280              :                                    NTrnBasis,
    2281              :                                    AZVIEW,
    2282              :                                    NWX,
    2283              :                                    NWY,
    2284              :                                    W2,
    2285              :                                    W21,
    2286              :                                    W23,
    2287              :                                    DWX,
    2288              :                                    DWY,
    2289              :                                    WNorm,
    2290              :                                    WinElArea);
    2291              :         }
    2292              :     }
    2293            0 : } // InitializeCFSDaylighting()
    2294              : 
    2295            0 : void InitializeCFSStateData(EnergyPlusData &state,
    2296              :                             DataBSDFWindow::BSDFRefPoints &StateRefPoint,
    2297              :                             DataBSDFWindow::BSDFRefPointsGeomDescr &DaylghtGeomDescr,
    2298              :                             [[maybe_unused]] int const daylightCtrlNum, // Current daylighting control number
    2299              :                             int const iWin,
    2300              :                             Vector3<Real64> const &RefPoint, // reference point
    2301              :                             int const CurFenState,
    2302              :                             int const NBasis,
    2303              :                             int const NTrnBasis,
    2304              :                             Real64 const AZVIEW,
    2305              :                             int const NWX,
    2306              :                             int const NWY,
    2307              :                             Vector3<Real64> const &W2,
    2308              :                             Vector3<Real64> const &W21,
    2309              :                             Vector3<Real64> const &W23,
    2310              :                             Real64 const DWX,
    2311              :                             Real64 const DWY,
    2312              :                             Vector3<Real64> const &WNorm, // unit vector from window (point towards outside)
    2313              :                             Real64 const WinElArea)
    2314              : {
    2315              :     // SUBROUTINE INFORMATION:
    2316              :     //       AUTHOR         Simon Vidanovic
    2317              :     //       DATE WRITTEN   June 2013
    2318              : 
    2319              :     // PURPOSE OF THIS SUBROUTINE:
    2320              :     // Initialize daylight state data for current
    2321            0 :     auto &s_surf = state.dataSurface;
    2322              : 
    2323              :     // SUBROUTINE LOCAL VARIABLES
    2324              :     int curWinEl;
    2325              :     bool hit;
    2326              :     int TotHits;
    2327              :     Real64 DotProd; // Temporary variable for manipulating dot product .dot.
    2328              :     int NSky;
    2329              :     int NGnd;
    2330              :     int NReflSurf;
    2331              :     int MaxTotHits;
    2332              :     Real64 LeastHitDsq; // dist^2 from window element center to hit point
    2333              :     Real64 HitDsq;
    2334              :     Real64 TransRSurf;
    2335              :     int J;
    2336              : 
    2337            0 :     Vector3<Real64> RWin;
    2338            0 :     Vector3<Real64> V;
    2339            0 :     Vector3<Real64> GroundHitPt;
    2340              : 
    2341              :     // temporary arrays for surfaces
    2342              :     // Each complex fenestration state can have different number of basis elements
    2343              :     // This is the reason for making these temporary arrays local
    2344            0 :     Array1D_int TmpSkyInd(NBasis, 0);                              // Temporary sky index list
    2345            0 :     Array1D_int TmpGndInd(NBasis, 0);                              // Temporary gnd index list
    2346            0 :     Array1D<Real64> TmpGndMultiplier(NBasis, 0.0);                 // Temporary ground obstruction multiplier
    2347            0 :     Array1D_int TmpRfSfInd(NBasis, 0);                             // Temporary RefSurfIndex
    2348            0 :     Array1D_int TmpRfRyNH(NBasis, 0);                              // Temporary RefRayNHits
    2349            0 :     Array2D_int TmpHSurfNo(s_surf->TotSurfaces, NBasis, 0);        // Temporary HitSurfNo
    2350            0 :     Array2D<Real64> TmpHSurfDSq(s_surf->TotSurfaces, NBasis, 0.0); // Temporary HitSurfDSq
    2351              : 
    2352              :     // Object Data
    2353            0 :     Vector3<Real64> Centroid;                                                                       // current window element centroid
    2354            0 :     Vector3<Real64> HitPt;                                                                          // surface hit point
    2355            0 :     Array1D<Vector3<Real64>> TmpGndPt(NBasis, Vector3<Real64>(0.0, 0.0, 0.0));                      // Temporary ground intersection list
    2356            0 :     Array2D<Vector3<Real64>> TmpHitPt(s_surf->TotSurfaces, NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary HitPt
    2357              : 
    2358            0 :     CFSRefPointPosFactor(state, RefPoint, StateRefPoint, iWin, CurFenState, NTrnBasis, AZVIEW);
    2359              : 
    2360            0 :     auto const &surf = s_surf->Surface(iWin);
    2361              : 
    2362            0 :     curWinEl = 0;
    2363              :     // loop through window elements. This will calculate sky, ground and reflection bins for each window element
    2364            0 :     for (int IX = 1; IX <= NWX; ++IX) {
    2365            0 :         for (int IY = 1; IY <= NWY; ++IY) {
    2366              : 
    2367            0 :             ++curWinEl;
    2368              : 
    2369              :             // centroid coordinates for current window element
    2370            0 :             Centroid = W2 + (double(IX) - 0.5) * W23 * DWX + (double(IY) - 0.5) * W21 * DWY;
    2371            0 :             RWin = Centroid;
    2372              : 
    2373            0 :             CFSRefPointSolidAngle(state, RefPoint, RWin, WNorm, StateRefPoint, DaylghtGeomDescr, iWin, CurFenState, NTrnBasis, curWinEl, WinElArea);
    2374              : 
    2375            0 :             NSky = 0;
    2376            0 :             NGnd = 0;
    2377            0 :             NReflSurf = 0;
    2378            0 :             MaxTotHits = 0;
    2379              :             // Calculation of potential surface obstruction for each incoming direction
    2380            0 :             for (int IRay = 1; IRay <= NBasis; ++IRay) {
    2381              : 
    2382            0 :                 hit = false;
    2383            0 :                 TotHits = 0;
    2384            0 :                 for (int JSurf = 1; JSurf <= s_surf->TotSurfaces; ++JSurf) {
    2385            0 :                     auto &surf2 = s_surf->Surface(JSurf);
    2386              : 
    2387              :                     // the following test will cycle on anything except exterior surfaces and shading surfaces
    2388            0 :                     if (surf2.HeatTransSurf && surf2.ExtBoundCond != ExternalEnvironment) {
    2389            0 :                         continue;
    2390              :                     }
    2391              :                     //  skip the base surface containing the window and any other subsurfaces of that surface
    2392            0 :                     if (JSurf == surf.BaseSurf || surf2.BaseSurf == surf.BaseSurf) {
    2393            0 :                         continue;
    2394              :                     }
    2395              :                     //  skip surfaces that face away from the window
    2396            0 :                     DotProd = dot(state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), surf2.NewellSurfaceNormalVector);
    2397            0 :                     if (DotProd >= 0) {
    2398            0 :                         continue;
    2399              :                     }
    2400            0 :                     hit = PierceSurface(state, JSurf, Centroid, state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), HitPt);
    2401            0 :                     if (!hit) {
    2402            0 :                         continue; // Miss: Try next surface
    2403              :                     }
    2404            0 :                     if (TotHits == 0) {
    2405              :                         // First hit for this ray
    2406            0 :                         TotHits = 1;
    2407            0 :                         ++NReflSurf;
    2408            0 :                         TmpRfSfInd(NReflSurf) = IRay;
    2409            0 :                         TmpRfRyNH(NReflSurf) = 1;
    2410            0 :                         TmpHSurfNo(1, NReflSurf) = JSurf;
    2411            0 :                         TmpHitPt(1, NReflSurf) = HitPt;
    2412            0 :                         V = HitPt - Centroid;                // vector array from window ctr to hit pt
    2413            0 :                         LeastHitDsq = V.magnitude_squared(); // dist^2 window ctr to hit pt
    2414            0 :                         TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    2415            0 :                         if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
    2416            0 :                             TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance, treat it here as completely transparent
    2417              :                         } else {
    2418            0 :                             TransRSurf = 0.0;
    2419              :                         }
    2420              :                     } else {
    2421            0 :                         V = HitPt - Centroid;
    2422            0 :                         HitDsq = V.magnitude_squared();
    2423            0 :                         if (HitDsq >= LeastHitDsq) {
    2424            0 :                             if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
    2425            0 :                                 J = TotHits + 1;
    2426            0 :                                 if (TotHits > 1) {
    2427            0 :                                     for (int I = 2; I <= TotHits; ++I) {
    2428            0 :                                         if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
    2429            0 :                                             J = I;
    2430            0 :                                             break;
    2431              :                                         }
    2432              :                                     }
    2433            0 :                                     if (!surf2.HeatTransSurf && surf2.shadowSurfSched == nullptr) {
    2434              :                                         //  The new hit is opaque, so we can drop all the hits further away
    2435            0 :                                         TmpHSurfNo(J, NReflSurf) = JSurf;
    2436            0 :                                         TmpHitPt(J, NReflSurf) = HitPt;
    2437            0 :                                         TmpHSurfDSq(J, NReflSurf) = HitDsq;
    2438            0 :                                         TotHits = J;
    2439              :                                     } else {
    2440              :                                         //  The new hit is scheduled (presumed transparent), so keep the more distant hits
    2441              :                                         //     Note that all the hists in the list will be transparent except the last,
    2442              :                                         //       which may be either transparent or opaque
    2443            0 :                                         if (TotHits >= J) {
    2444            0 :                                             for (int I = TotHits; I >= J; --I) {
    2445            0 :                                                 TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    2446            0 :                                                 TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    2447            0 :                                                 TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    2448              :                                             }
    2449            0 :                                             TmpHSurfNo(J, NReflSurf) = JSurf;
    2450            0 :                                             TmpHitPt(J, NReflSurf) = HitPt;
    2451            0 :                                             TmpHSurfDSq(J, NReflSurf) = HitDsq;
    2452            0 :                                             ++TotHits;
    2453              :                                         }
    2454              :                                     } // if (.NOT.Surface(JSurf)%HeatTransSurf .AND. Surface(JSurf)%SchedShadowSurfIndex == 0)  then
    2455              :                                 } // if (TotHits > 1) then
    2456              :                             } // if (TransRSurf  > 0.0d0) then
    2457              :                         } else { // if (HitDsq >= LeastHitDsq) then
    2458              :                             //  A new closest hit.  If it is opaque, drop the current hit list,
    2459              :                             //    otherwise add it at the front
    2460            0 :                             LeastHitDsq = HitDsq;
    2461            0 :                             if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
    2462            0 :                                 TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
    2463            0 :                                 for (int I = TotHits; I >= 1; --I) {
    2464            0 :                                     TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
    2465            0 :                                     TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
    2466            0 :                                     TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
    2467            0 :                                     ++TotHits;
    2468              :                                 }
    2469            0 :                             } else {
    2470            0 :                                 TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
    2471            0 :                                 TotHits = 1;
    2472              :                             }
    2473            0 :                             TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
    2474            0 :                             TmpHitPt(1, NReflSurf) = HitPt;
    2475            0 :                             TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
    2476              :                         }
    2477              :                     }
    2478              :                 } // do JSurf = 1, TotSurfaces
    2479            0 :                 if (TotHits <= 0) {
    2480            0 :                     auto const &sIncRay = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay);
    2481              :                     // This ray reached the sky or ground unobstructed
    2482            0 :                     if (sIncRay.z < 0.0) {
    2483              :                         // A ground ray
    2484            0 :                         ++NGnd;
    2485            0 :                         TmpGndInd(NGnd) = IRay;
    2486            0 :                         TmpGndPt(NGnd).x = Centroid.x - (sIncRay.x / sIncRay.z) * Centroid.z;
    2487            0 :                         TmpGndPt(NGnd).y = Centroid.y - (sIncRay.y / sIncRay.z) * Centroid.z;
    2488            0 :                         TmpGndPt(NGnd).z = 0.0;
    2489              : 
    2490              :                         // for solar reflectance calculations, need to precalculate obstruction multipliers
    2491            0 :                         if (s_surf->CalcSolRefl) {
    2492            0 :                             GroundHitPt = TmpGndPt(NGnd);
    2493            0 :                             TmpGndMultiplier(NGnd) =
    2494            0 :                                 CalcObstrMultiplier(state, GroundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    2495              :                         }
    2496              :                     } else {
    2497              :                         // A sky ray
    2498            0 :                         ++NSky;
    2499            0 :                         TmpSkyInd(NSky) = IRay;
    2500              :                     }
    2501              :                 } else {
    2502              :                     // Save the number of hits for this ray
    2503            0 :                     TmpRfRyNH(NReflSurf) = TotHits;
    2504              :                 }
    2505            0 :                 MaxTotHits = max(MaxTotHits, TotHits);
    2506              :             } // do IRay = 1, ComplexWind(IWin)%Geom(CurFenState)%Inc%NBasis
    2507              : 
    2508              :             // Fill up state data for current window element data
    2509            0 :             StateRefPoint.NSky(curWinEl) = NSky;
    2510            0 :             StateRefPoint.SkyIndex({1, NSky}, curWinEl) = TmpSkyInd({1, NSky});
    2511              : 
    2512            0 :             StateRefPoint.NGnd(curWinEl) = NGnd;
    2513            0 :             StateRefPoint.GndIndex({1, NGnd}, curWinEl) = TmpGndInd({1, NGnd});
    2514            0 :             StateRefPoint.GndPt({1, NGnd}, curWinEl) = TmpGndPt({1, NGnd});
    2515            0 :             StateRefPoint.GndObstrMultiplier({1, NGnd}, curWinEl) = TmpGndMultiplier({1, NGnd});
    2516              : 
    2517            0 :             StateRefPoint.NReflSurf(curWinEl) = NReflSurf;
    2518            0 :             StateRefPoint.RefSurfIndex({1, NReflSurf}, curWinEl) = TmpRfSfInd({1, NReflSurf});
    2519            0 :             StateRefPoint.RefRayNHits({1, NReflSurf}, curWinEl) = TmpRfRyNH({1, NReflSurf});
    2520            0 :             StateRefPoint.HitSurfNo({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfNo({1, MaxTotHits}, {1, NReflSurf});
    2521            0 :             StateRefPoint.HitSurfDSq({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfDSq({1, MaxTotHits}, {1, NReflSurf});
    2522            0 :             StateRefPoint.HitPt({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHitPt({1, MaxTotHits}, {1, NReflSurf});
    2523              :         } // do IY = 1, NWY
    2524              :     } // do IX = 1, NWX
    2525            0 : }
    2526              : 
    2527            0 : void AllocateForCFSRefPointsState(
    2528              :     [[maybe_unused]] EnergyPlusData &state, DataBSDFWindow::BSDFRefPoints &StateRefPoint, int const NumOfWinEl, int const NBasis, int const NTrnBasis)
    2529              : {
    2530              :     // SUBROUTINE INFORMATION:
    2531              :     //       AUTHOR         Simon Vidanovic
    2532              :     //       DATE WRITTEN   June 2013
    2533              : 
    2534              :     // PURPOSE OF THIS SUBROUTINE:
    2535              :     // Memory allocation for complex fenestration systems reference points geometry
    2536            0 :     auto &s_surf = state.dataSurface;
    2537              : 
    2538            0 :     if (!allocated(StateRefPoint.NSky)) {
    2539            0 :         StateRefPoint.NSky.allocate(NumOfWinEl);
    2540            0 :         StateRefPoint.NSky = 0;
    2541              :     }
    2542              : 
    2543            0 :     if (!allocated(StateRefPoint.SkyIndex)) {
    2544            0 :         StateRefPoint.SkyIndex.allocate(NBasis, NumOfWinEl);
    2545            0 :         StateRefPoint.SkyIndex = 0;
    2546              :     }
    2547              : 
    2548            0 :     if (!allocated(StateRefPoint.NGnd)) {
    2549            0 :         StateRefPoint.NGnd.allocate(NumOfWinEl);
    2550            0 :         StateRefPoint.NGnd = 0;
    2551              :     }
    2552              : 
    2553            0 :     if (!allocated(StateRefPoint.GndIndex)) {
    2554            0 :         StateRefPoint.GndIndex.allocate(NBasis, NumOfWinEl);
    2555            0 :         StateRefPoint.GndIndex = 0;
    2556              :     }
    2557              : 
    2558            0 :     if (!allocated(StateRefPoint.GndPt)) {
    2559            0 :         StateRefPoint.GndPt.allocate(NBasis, NumOfWinEl);
    2560            0 :         StateRefPoint.GndPt = Vector(0.0, 0.0, 0.0);
    2561              :     }
    2562              : 
    2563            0 :     if (!allocated(StateRefPoint.GndObstrMultiplier)) {
    2564            0 :         StateRefPoint.GndObstrMultiplier.allocate(NBasis, NumOfWinEl);
    2565            0 :         StateRefPoint.GndObstrMultiplier = 0.0;
    2566              :     }
    2567              : 
    2568            0 :     if (!allocated(StateRefPoint.NReflSurf)) {
    2569            0 :         StateRefPoint.NReflSurf.allocate(NumOfWinEl);
    2570            0 :         StateRefPoint.NReflSurf = 0;
    2571              :     }
    2572              : 
    2573            0 :     if (!allocated(StateRefPoint.RefSurfIndex)) {
    2574            0 :         StateRefPoint.RefSurfIndex.allocate(NBasis, NumOfWinEl);
    2575            0 :         StateRefPoint.RefSurfIndex = 0;
    2576              :     }
    2577              : 
    2578            0 :     if (!allocated(StateRefPoint.TransOutSurf)) {
    2579            0 :         StateRefPoint.TransOutSurf.allocate(NBasis, NumOfWinEl);
    2580            0 :         StateRefPoint.TransOutSurf = 1.0;
    2581              :     }
    2582              : 
    2583            0 :     if (!allocated(StateRefPoint.RefRayNHits)) {
    2584            0 :         StateRefPoint.RefRayNHits.allocate(NBasis, NumOfWinEl);
    2585            0 :         StateRefPoint.RefRayNHits = 0;
    2586              :     }
    2587              : 
    2588            0 :     if (!allocated(StateRefPoint.HitSurfNo)) {
    2589            0 :         StateRefPoint.HitSurfNo.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
    2590            0 :         StateRefPoint.HitSurfNo = 0;
    2591              :     }
    2592              : 
    2593            0 :     if (!allocated(StateRefPoint.HitSurfDSq)) {
    2594            0 :         StateRefPoint.HitSurfDSq.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
    2595            0 :         StateRefPoint.HitSurfDSq = 0.0;
    2596              :     }
    2597              : 
    2598            0 :     if (!allocated(StateRefPoint.HitPt)) {
    2599            0 :         StateRefPoint.HitPt.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
    2600            0 :         StateRefPoint.HitPt = Vector(0.0, 0.0, 0.0);
    2601              :     }
    2602              : 
    2603            0 :     if (!allocated(StateRefPoint.RefPointIndex)) {
    2604            0 :         StateRefPoint.RefPointIndex.allocate(NumOfWinEl);
    2605            0 :         StateRefPoint.RefPointIndex = 0;
    2606              :     }
    2607              : 
    2608            0 :     if (!allocated(StateRefPoint.RefPointIntersection)) {
    2609            0 :         StateRefPoint.RefPointIntersection.allocate(NTrnBasis);
    2610            0 :         StateRefPoint.RefPointIntersection = false;
    2611              :     }
    2612              : 
    2613            0 :     if (!allocated(StateRefPoint.RefPtIntPosFac)) {
    2614            0 :         StateRefPoint.RefPtIntPosFac.allocate(NTrnBasis);
    2615            0 :         StateRefPoint.RefPtIntPosFac = 0.0;
    2616              :     }
    2617            0 : }
    2618              : 
    2619            0 : void AllocateForCFSRefPointsGeometry(DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointsGeomDescr, int const NumOfWinEl)
    2620              : {
    2621              :     // SUBROUTINE INFORMATION:
    2622              :     //       AUTHOR         Simon Vidanovic
    2623              :     //       DATE WRITTEN   June 2013
    2624              : 
    2625              :     // PURPOSE OF THIS SUBROUTINE:
    2626              :     // Memory allocation for complex fenestration systems reference points geometry
    2627              : 
    2628              :     // SUBROUTINE LOCAL VARIABLES
    2629              : 
    2630            0 :     if (!allocated(RefPointsGeomDescr.SolidAngle)) {
    2631            0 :         RefPointsGeomDescr.SolidAngle.allocate(NumOfWinEl);
    2632            0 :         RefPointsGeomDescr.SolidAngle = 0.0;
    2633              :     }
    2634              : 
    2635            0 :     if (!allocated(RefPointsGeomDescr.SolidAngleVec)) {
    2636            0 :         RefPointsGeomDescr.SolidAngleVec.allocate(NumOfWinEl);
    2637            0 :         RefPointsGeomDescr.SolidAngleVec = Vector(0.0, 0.0, 0.0);
    2638              :     }
    2639            0 : }
    2640              : 
    2641            0 : void CFSRefPointSolidAngle(EnergyPlusData &state,
    2642              :                            Vector3<Real64> const &RefPoint,
    2643              :                            Vector3<Real64> const &RWin,
    2644              :                            Vector3<Real64> const &WNorm,
    2645              :                            DataBSDFWindow::BSDFRefPoints &RefPointMap,
    2646              :                            DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointGeomMap,
    2647              :                            int const iWin,
    2648              :                            int const CurFenState,
    2649              :                            int const NTrnBasis,
    2650              :                            int const curWinEl,
    2651              :                            Real64 const WinElArea)
    2652              : {
    2653              :     // SUBROUTINE INFORMATION:
    2654              :     //       AUTHOR         Simon Vidanovic
    2655              :     //       DATE WRITTEN   June 2013
    2656              : 
    2657              :     // PURPOSE OF THIS SUBROUTINE:
    2658              :     // Calculate position factor for given reference point.
    2659              : 
    2660              :     // calculate vector from center of window element to the current reference point
    2661            0 :     Vector3<Real64> Ray = RefPoint - RWin;
    2662              : 
    2663              :     // figure out outgoing beam direction from current reference point
    2664            0 :     Real64 BestMatch = 0.0;
    2665            0 :     for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
    2666            0 :         Vector3<Real64> const &V = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn(iTrnRay);
    2667            0 :         Real64 temp = dot(Ray, V);
    2668            0 :         if (temp > BestMatch) {
    2669            0 :             BestMatch = temp;
    2670            0 :             RefPointMap.RefPointIndex(curWinEl) = iTrnRay;
    2671              :         }
    2672              :     }
    2673              : 
    2674              :     // calculate solid view angle
    2675            0 :     Real64 Dist = Ray.magnitude();
    2676            0 :     Vector3<Real64> RayNorm = Ray / (-Dist);
    2677            0 :     RefPointGeomMap.SolidAngleVec(curWinEl) = RayNorm;
    2678            0 :     Real64 CosB = dot(WNorm, RayNorm);
    2679            0 :     RefPointGeomMap.SolidAngle(curWinEl) = WinElArea * CosB / (Dist * Dist);
    2680            0 : }
    2681              : 
    2682            0 : void CFSRefPointPosFactor(EnergyPlusData &state,
    2683              :                           Vector3<Real64> const &RefPoint,
    2684              :                           DataBSDFWindow::BSDFRefPoints &RefPointMap,
    2685              :                           int const iWin,
    2686              :                           int const CurFenState,
    2687              :                           int const NTrnBasis,
    2688              :                           Real64 const AZVIEW)
    2689              : {
    2690              :     // SUBROUTINE INFORMATION:
    2691              :     //       AUTHOR         Simon Vidanovic
    2692              :     //       DATE WRITTEN   June 2013
    2693              : 
    2694              :     // PURPOSE OF THIS SUBROUTINE:
    2695              :     // Calculate position factor for given reference point.
    2696              : 
    2697            0 :     auto const &sTrn = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn;
    2698            0 :     for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
    2699            0 :         Vector3<Real64> V = sTrn(iTrnRay);
    2700            0 :         V.negate();
    2701              : 
    2702            0 :         Vector3<Real64> InterPoint;
    2703              : 
    2704            0 :         bool hit = PierceSurface(state, iWin, RefPoint, V, InterPoint);
    2705            0 :         if (hit) {
    2706            0 :             RefPointMap.RefPointIntersection(iTrnRay) = true;
    2707              : 
    2708            0 :             DataBSDFWindow::BSDFDaylghtPosition elPos = WindowComplexManager::DaylghtAltAndAzimuth(V);
    2709              : 
    2710            0 :             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - elPos.Azimuth) + 0.001);
    2711            0 :             Real64 YR = std::tan(elPos.Altitude + 0.001);
    2712            0 :             RefPointMap.RefPtIntPosFac(iTrnRay) = DayltgGlarePositionFactor(XR, YR);
    2713              :         }
    2714            0 :     }
    2715            0 : } // CFSRefPointPosFactor()
    2716              : 
    2717            0 : Real64 CalcObstrMultiplier(EnergyPlusData &state,
    2718              :                            Vector3<Real64> const &GroundHitPt, // Coordinates of point that ray hits ground (m)
    2719              :                            int const AltSteps,                 // Number of steps in altitude angle for solar reflection calc
    2720              :                            int const AzimSteps                 // Number of steps in azimuth angle of solar reflection calc
    2721              : )
    2722              : {
    2723              : 
    2724              :     // SUBROUTINE INFORMATION:
    2725              :     //       AUTHOR         Simon Vidanovic
    2726              :     //       DATE WRITTEN   April 2013, refactor from legacy code by Fred Winklemann
    2727              : 
    2728              :     // PURPOSE OF THIS SUBROUTINE:
    2729              :     // collect code to do obstruction multiplier from ground point
    2730              : 
    2731              :     // METHODOLOGY EMPLOYED:
    2732              :     // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
    2733              :     // Divide hemisphere centered at ground hit point into elements of altitude Phi and
    2734              :     // azimuth Theta and create upward-going ground ray unit vector at each Phi,Theta pair.
    2735              :     // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
    2736              : 
    2737              :     // Locals
    2738            0 :     auto const &dl = state.dataDayltg;
    2739            0 :     auto const &s_surf = state.dataSurface;
    2740              : 
    2741              :     bool hitObs; // True iff obstruction is hit
    2742              : 
    2743            0 :     Vector3<Real64> URay;     // Unit vector in (Phi,Theta) direction
    2744            0 :     Vector3<Real64> ObsHitPt; // Unit vector in (Phi,Theta) direction
    2745              : 
    2746            0 :     assert(AzimSteps <= DataSurfaces::AzimAngStepsForSolReflCalc);
    2747              : 
    2748            0 :     Real64 DPhi = Constant::PiOvr2 / (AltSteps / 2.0); // Phi increment (radians)
    2749            0 :     Real64 DTheta = Constant::Pi / AzimSteps;          // Theta increment (radians)
    2750              : 
    2751              :     // Tuned Precompute Phi trig table
    2752            0 :     if (AltSteps != dl->AltSteps_last) {
    2753            0 :         for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
    2754            0 :             Real64 Phi = (IPhi - 0.5) * DPhi;
    2755            0 :             dl->cos_Phi[IPhi] = std::cos(Phi);
    2756            0 :             dl->sin_Phi[IPhi] = std::sin(Phi);
    2757              :         }
    2758            0 :         dl->AltSteps_last = AltSteps;
    2759              :     }
    2760              :     // Tuned Precompute Theta trig table
    2761            0 :     if (AzimSteps != dl->AzimSteps_last) {
    2762            0 :         for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
    2763            0 :             Real64 Theta = (ITheta - 0.5) * DTheta;
    2764            0 :             dl->cos_Theta[ITheta] = std::cos(Theta);
    2765            0 :             dl->sin_Theta[ITheta] = std::sin(Theta);
    2766              :         }
    2767            0 :         dl->AzimSteps_last = AzimSteps;
    2768              :     }
    2769              : 
    2770            0 :     Real64 SkyGndObs = 0.0;   // Obstructed sky irradiance at a ground point
    2771            0 :     Real64 SkyGndUnObs = 0.0; // Unobstructed sky irradiance at a ground point
    2772              : 
    2773              :     // Altitude loop
    2774            0 :     for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
    2775            0 :         Real64 sinPhi = dl->sin_Phi[IPhi]; // sinPhi
    2776            0 :         Real64 cosPhi = dl->cos_Phi[IPhi]; // cosPhi
    2777              : 
    2778              :         // Third component of ground ray unit vector in (Theta,Phi) direction
    2779            0 :         URay.z = sinPhi;
    2780            0 :         Real64 dOmegaGnd = cosPhi * DTheta * DPhi; // Solid angle element of ray from ground point (steradians)
    2781              :         // Cosine of angle of incidence of ground ray on ground plane
    2782            0 :         Real64 CosIncAngURay = sinPhi;
    2783            0 :         Real64 IncAngSolidAngFac = CosIncAngURay * dOmegaGnd / Constant::Pi; // CosIncAngURay*dOmegaGnd/Pi
    2784              :         // Azimuth loop
    2785            0 :         for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
    2786            0 :             URay.x = cosPhi * dl->cos_Theta[ITheta];
    2787            0 :             URay.y = cosPhi * dl->sin_Theta[ITheta];
    2788            0 :             SkyGndUnObs += IncAngSolidAngFac;
    2789              :             // Does this ground ray hit an obstruction?
    2790            0 :             hitObs = false;
    2791            0 :             if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    2792              : 
    2793            0 :                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    2794            0 :                     hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
    2795            0 :                     if (hitObs) {
    2796            0 :                         break;
    2797              :                     }
    2798            0 :                 }
    2799              : 
    2800              :             } else { // Surface octree search
    2801              : 
    2802              :                 // Lambda function for the octree to test for surface hit
    2803            0 :                 auto surfaceHit = [&GroundHitPt, &hitObs, &URay, &ObsHitPt](SurfaceData const &surface) -> bool {
    2804            0 :                     if (surface.IsShadowPossibleObstruction) {
    2805            0 :                         hitObs = PierceSurface(surface, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
    2806            0 :                         return hitObs;
    2807              :                     } else {
    2808            0 :                         return false;
    2809              :                     }
    2810            0 :                 };
    2811              : 
    2812              :                 // Check octree surface candidates until a hit is found, if any
    2813            0 :                 Vector3<Real64> const URay_inv(SurfaceOctreeCube::safe_inverse(URay));
    2814            0 :                 state.dataHeatBalMgr->surfaceOctree.hasSurfaceRayIntersectsCube(GroundHitPt, URay, URay_inv, surfaceHit);
    2815            0 :             }
    2816              : 
    2817            0 :             if (hitObs) {
    2818            0 :                 continue; // Obstruction hit
    2819              :             }
    2820              :             // Sky is hit
    2821            0 :             SkyGndObs += IncAngSolidAngFac;
    2822              :         } // End of azimuth loop
    2823              :     } // End of altitude loop
    2824              : 
    2825              :     // in case ground point is surrounded by obstructions (SkyGndUnObs == 0), then multiplier will be equal to zero
    2826              :     // This should not happen anyway because in that case ray would not be able to reach ground point
    2827            0 :     return (SkyGndUnObs != 0.0) ? (SkyGndObs / SkyGndUnObs) : 0.0;
    2828            0 : } // CalcObstrMultiplier()
    2829              : 
    2830       862072 : void FigureDayltgCoeffsAtPointsForSunPosition(
    2831              :     EnergyPlusData &state,
    2832              :     int const daylightCtrlNum, // Daylighting control index
    2833              :     int const iRefPoint,
    2834              :     int const iXelement,
    2835              :     int const NWX, // Number of window elements in x direction for dayltg calc
    2836              :     int const iYelement,
    2837              :     int const NWY,   // Number of window elements in y direction for dayltg calc
    2838              :     int const WinEl, // Current window element counter
    2839              :     int const IWin,
    2840              :     int const IWin2,
    2841              :     int const iHour,
    2842              :     int &ISunPos,
    2843              :     Real64 const SkyObstructionMult,
    2844              :     Vector3<Real64> const &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
    2845              :     Vector3<Real64> const &Ray,   // Unit vector along ray from reference point to window element
    2846              :     Real64 const PHRAY,           // Altitude of ray from reference point to window element (radians)
    2847              :     int const LSHCAL,             // Interior shade calculation flag:  0=not yet calculated, 1=already calculated
    2848              :     int const InShelfSurf,        // Inside daylighting shelf surface number
    2849              :     Real64 const COSB,            // Cosine of angle between window outward normal and ray from reference point to window element
    2850              :     Real64 const ObTrans,         // Product of solar transmittances of exterior obstructions hit by ray from reference point through a window element
    2851              :     Real64 const TVISB,           // Visible transmittance of window for COSB angle of incidence (times light well efficiency, if appropriate)
    2852              :     Real64 const DOMEGA,          // Solid angle subtended by window element wrt reference point (steradians)
    2853              :     int const ICtrl,              // Window control counter
    2854              :     WinShadingType const ShType,  // Window shading type
    2855              :     [[maybe_unused]] int const BlNum, // Window blind number
    2856              :     Real64 const THRAY,               // Azimuth of ray from reference point to window element (radians)
    2857              :     Vector3<Real64> const &WNORM2,    // Unit vector normal to window
    2858              :     ExtWinType const extWinType,      // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    2859              :     int const IConst,                 // Construction counter
    2860              :     Real64 const AZVIEW,              // Azimuth of view vector in absolute coord system for glare calculation (radians)
    2861              :     Vector3<Real64> const &RREF2,     // Location of virtual reference point in absolute coordinate system
    2862              :     bool const hitIntObs,             // True iff interior obstruction hit
    2863              :     bool const hitExtObs,             // True iff ray from ref pt to ext win hits an exterior obstruction
    2864              :     CalledFor const CalledFrom,       // indicate  which type of routine called this routine
    2865              :     Real64 TVISIntWin,                // Visible transmittance of int win at COSBIntWin for light from ext win
    2866              :     Real64 &TVISIntWinDisk,           // Visible transmittance of int win at COSBIntWin for sun
    2867              :     int const MapNum)
    2868              : {
    2869              : 
    2870              :     // SUBROUTINE INFORMATION:
    2871              :     //       AUTHOR         B. Griffith
    2872              :     //       DATE WRITTEN   November 2012, refactor from legacy code by Fred Winklemann
    2873              : 
    2874              :     // PURPOSE OF THIS SUBROUTINE:
    2875              :     // collect code for calculations sun position aspects for daylighting coefficients
    2876              : 
    2877              :     // METHODOLOGY EMPLOYED:
    2878              :     // switch as need to serve both reference points and map points based on calledFrom
    2879       862072 :     auto &s_surf = state.dataSurface;
    2880       862072 :     if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) {
    2881       408476 :         return;
    2882              :     }
    2883              : 
    2884       453596 :     auto &dl = state.dataDayltg;
    2885       453596 :     auto &s_mat = state.dataMaterial;
    2886              : 
    2887              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2888       453596 :     Vector3<Real64> RREF{0.0, 0.0, 0.0}; // Location of a reference point in absolute coordinate system //Autodesk Was used uninitialized:
    2889              : 
    2890              :     Real64 ObTransDisk;     // Product of solar transmittances of exterior obstructions hit by ray from reference point to sun
    2891              :     Real64 LumAtHitPtFrSun; // Luminance at hit point of obstruction by reflection of direct light from sun (cd/m2)
    2892              : 
    2893              :     Real64 TVISS; // Direct solar visible transmittance of window at given angle of incidence
    2894              :     //  (times light well efficiency, if appropriate)
    2895              :     Real64 XAVWL; // XAVWL*TVISS is contribution of window luminance from solar disk (cd/m2)
    2896              : 
    2897              :     bool hitObs;          // True iff obstruction is hit
    2898              :     Real64 ObsVisRefl;    // Visible reflectance of obstruction
    2899              :     Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by
    2900              : 
    2901              :     Real64 SpecReflectance; // Specular reflectance of a reflecting surface
    2902              :     Real64 TVisRefl;        // Bare window vis trans for reflected beam
    2903              :     //  (times light well efficiency, if appropriate)
    2904              :     Real64 PHSUNrefl; // Altitude angle of reflected sun (radians)
    2905              :     Real64 THSUNrefl; // Azimuth anggle of reflected sun (radians)
    2906              : 
    2907              :     Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
    2908              :     //  exterior window element or between ref pt and sun
    2909              :     Real64 TVisIntWinMult;     // Interior window vis trans multiplier for ext win in adjacent zone
    2910              :     Real64 TVisIntWinDiskMult; // Interior window vis trans solar disk multiplier for ext win in adj zone
    2911              :     Real64 WindowSolidAngleDaylightPoint;
    2912              : 
    2913       453596 :     ++ISunPos;
    2914              : 
    2915              :     // Altitude of sun (degrees)
    2916       453596 :     dl->sunAngles = dl->sunAnglesHr[iHour];
    2917              : 
    2918              :     // First time through, call routine to calculate inter-reflected illuminance
    2919              :     // at reference point and luminance of window with shade, screen or blind.
    2920              : 
    2921              :     // Rob/TH - Not sure whether this call is necessary for interior zones with interior windows only.
    2922              :     //  new code would be -
    2923              :     // IF (LSHCAL == 1 .AND. ExtWinType /= AdjZoneExtWin) CALL DayltgInterReflectedIllum(ISunPos,IHR,ZoneNum,IWin2)
    2924       453596 :     int enclNum = 0; // enclosure index
    2925       453596 :     int zoneNum = 0; // zone index
    2926       453596 :     if (CalledFrom == CalledFor::RefPoint) {
    2927       137846 :         zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
    2928       137846 :         enclNum = dl->daylightControl(daylightCtrlNum).enclIndex;
    2929       315750 :     } else if (CalledFrom == CalledFor::MapPoint) {
    2930       315750 :         assert(MapNum > 0);
    2931       315750 :         zoneNum = dl->illumMaps(MapNum).zoneIndex;
    2932       315750 :         enclNum = dl->illumMaps(MapNum).enclIndex;
    2933              :     }
    2934       453596 :     if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
    2935       453596 :         if (LSHCAL == 1) {
    2936         2066 :             DayltgInterReflectedIllum(state, ISunPos, iHour, enclNum, IWin2);
    2937              :         }
    2938              :     } else {
    2939            0 :         if (LSHCAL == 1) {
    2940            0 :             DayltgInterReflectedIllumComplexFenestration(state, IWin2, WinEl, iHour, daylightCtrlNum, iRefPoint, CalledFrom, MapNum);
    2941              :         }
    2942            0 :         if (COSB <= 0.0) {
    2943            0 :             return;
    2944              :         }
    2945            0 :         DayltgDirectIllumComplexFenestration(state, IWin, WinEl, iHour, iRefPoint, CalledFrom, MapNum);
    2946              :         // Call direct sun component only once since calculation is done for entire window
    2947            0 :         if (WinEl == (NWX * NWY)) {
    2948            0 :             DayltgDirectSunDiskComplexFenestration(state, IWin2, iHour, iRefPoint, WinEl, AZVIEW, CalledFrom, MapNum);
    2949              :         }
    2950            0 :         return;
    2951              :     }
    2952              : 
    2953              :     // Daylighting shelf simplification:  The shelf completely blocks all view of the window,
    2954              :     // only interrelflected illumination is allowed (see DayltgInterReflectedIllum above).
    2955              :     // Everything else in this loop has to do with direct luminance from the window.
    2956       453596 :     if (InShelfSurf > 0) {
    2957            0 :         return;
    2958              :     }
    2959              : 
    2960       453596 :     if (COSB <= 0.0) {
    2961            0 :         return;
    2962              :     }
    2963              : 
    2964       453596 :     auto &surfWin = s_surf->SurfaceWindow(IWin);
    2965              : 
    2966       453596 :     Illums XDirIllum;
    2967       453596 :     Illums XAvgWinLum;
    2968       453596 :     Real64 const Ray_3 = Ray.z;
    2969       453596 :     Real64 const DOMEGA_Ray_3 = DOMEGA * Ray_3;
    2970              : 
    2971              :     // Add contribution of this window element to glare and to
    2972              :     // direct illuminance at reference point
    2973              : 
    2974              :     // The I,J,K indices for sky and sun components of direct illuminance
    2975              :     // (EDIRSK, EDIRSU) and average window luminance (AVWLSK, AVWLSU) are:
    2976              :     // I=1 for clear sky, =2 Clear turbid, =3 Intermediate, =4 Overcast;
    2977              :     // J=1 for bare window, =2 for window with shade or fixed slat-angle blind;
    2978              :     // K = sun position index.
    2979              : 
    2980              :     // ----- CASE I -- BARE WINDOW (no shading device)
    2981              : 
    2982              :     // Beam solar and sky solar reflected from nearest obstruction.
    2983              :     // In the following hitIntObs == false  ==> no interior obstructions hit, and
    2984              :     //                  hitExtObs == true  ==> one or more exterior obstructions hit.
    2985       453596 :     if (s_surf->CalcSolRefl && !hitIntObs && hitExtObs) {
    2986              :         int NearestHitSurfNum;        // Surface number of nearest obstruction
    2987            0 :         Vector3<Real64> NearestHitPt; // Hit point of ray on nearest obstruction
    2988              :         // One or more exterior obstructions was hit; get contribution of reflection
    2989              :         // from nearest obstruction.
    2990              :         // Find obstruction whose hit point is closest to this ray's window element
    2991            0 :         DayltgClosestObstruction(state, RWIN2, Ray, NearestHitSurfNum, NearestHitPt);
    2992            0 :         if (NearestHitSurfNum > 0) {
    2993              : 
    2994              :             // Beam solar reflected from nearest obstruction
    2995              : 
    2996            0 :             LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, iHour, Ray, NearestHitSurfNum, NearestHitPt);
    2997            0 :             dl->avgWinLum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * TVISB;
    2998            0 :             if (PHRAY >= 0.0) {
    2999            0 :                 dl->dirIllum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * DOMEGA_Ray_3 * TVISB;
    3000              :             }
    3001              : 
    3002              :             // Sky solar reflected from nearest obstruction
    3003              : 
    3004            0 :             int const ObsConstrNum = s_surf->SurfActiveConstruction(NearestHitSurfNum);
    3005            0 :             if (ObsConstrNum > 0) {
    3006              :                 // Exterior building surface is nearest hit
    3007            0 :                 if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
    3008              :                     // Obstruction is not a window, i.e., is an opaque surface
    3009            0 :                     ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
    3010              :                 } else {
    3011              :                     // Obstruction is a window; assume it is bare
    3012            0 :                     ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
    3013              :                 }
    3014              :             } else {
    3015              :                 // Shadowing surface is nearest hit
    3016            0 :                 if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
    3017              :                     // This is a daylighting shelf, for which reflection is separately calculated
    3018            0 :                     ObsVisRefl = 0.0;
    3019              :                 } else {
    3020            0 :                     ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
    3021            0 :                     if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0) {
    3022            0 :                         ObsVisRefl += s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
    3023            0 :                                       state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
    3024              :                     }
    3025              :                 }
    3026              :             }
    3027              :             // Surface number to use when obstruction is a shadowing surface
    3028            0 :             int NearestHitSurfNumX = NearestHitSurfNum;
    3029              :             // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    3030              :             // The following gets the correct side of a shadowing surface for reflection.
    3031            0 :             if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
    3032            0 :                 if (dot(Ray, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) {
    3033            0 :                     NearestHitSurfNumX = NearestHitSurfNum + 1;
    3034              :                 }
    3035              :             }
    3036            0 :             if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
    3037            0 :                 state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    3038            0 :                 SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    3039            0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
    3040              :             } else {
    3041            0 :                 SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    3042            0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, iHour, NearestHitSurfNumX) / Constant::Pi;
    3043              :             }
    3044            0 :             assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
    3045            0 :             auto &gilsk = dl->horIllum[iHour];
    3046            0 :             auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
    3047            0 :             auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
    3048              : 
    3049            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3050            0 :                 XAvgWinLum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum;
    3051            0 :                 avwlsk.sky[iSky] += XAvgWinLum.sky[iSky] * TVISB;
    3052            0 :                 if (PHRAY >= 0.0) {
    3053            0 :                     XDirIllum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum * DOMEGA_Ray_3;
    3054            0 :                     edirsk.sky[iSky] += XDirIllum.sky[iSky] * TVISB;
    3055              :                 }
    3056              :             }
    3057              :         }
    3058            0 :     } // End of check if solar reflection calculation is in effect
    3059              : 
    3060       453596 :     if (ObTrans > 1.e-6) {
    3061              :         // Ray did not hit an obstruction or the transmittance product of hit obstructions is non-zero.
    3062              :         // Contribution of sky or ground luminance in cd/m2
    3063       453596 :         if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
    3064              :             // Make all transmitted light diffuse for a TDD with a bare diffuser
    3065            0 :             assert(equal_dimensions(dl->avgWinLum, dl->winLum));
    3066            0 :             assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
    3067            0 :             auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
    3068            0 :             auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
    3069            0 :             auto &wlumsk = dl->winLum(iHour)[iWinCover_Bare];
    3070            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3071            0 :                 avwlsk.sky[iSky] += wlumsk.sky[iSky];
    3072            0 :                 if (PHRAY > 0.0) {
    3073            0 :                     edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3;
    3074              :                 }
    3075              :             }
    3076              : 
    3077            0 :             dl->avgWinLum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun;
    3078            0 :             dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += dl->winLum(iHour)[iWinCover_Bare].sunDisk;
    3079              : 
    3080            0 :             if (PHRAY > 0.0) {
    3081            0 :                 dl->dirIllum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun * DOMEGA_Ray_3;
    3082              :             }
    3083              :         } else {                         // Bare window
    3084       453596 :             Vector3<Real64> GroundHitPt; // Coordinates of point that ray hits ground (m)
    3085              :             // Tuned Hoisted operations out of loop and linear indexing
    3086       453596 :             if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
    3087            0 :                 Real64 Alfa = std::acos(-Ray_3);
    3088            0 :                 Real64 const Ray_1(Ray.x);
    3089            0 :                 Real64 const Ray_2(Ray.y);
    3090              :                 //                    Beta = std::atan2( Ray_2, Ray_1 ); //Unused Tuning below eliminated use
    3091              :                 // Distance between ground hit point and proj'n of center of window element onto ground (m)
    3092            0 :                 Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
    3093            0 :                 GroundHitPt.z = s_surf->GroundLevelZ;
    3094              :                 // Tuned Replaced by below: sqrt is faster than sincos
    3095              :                 //                    GroundHitPt( 1 ) = RWIN2( 1 ) + HorDis * std::cos( Beta );
    3096              :                 //                    GroundHitPt( 2 ) = RWIN2( 2 ) + HorDis * std::sin( Beta );
    3097            0 :                 Real64 const Ray_r(std::sqrt(square(Ray_1) + square(Ray_2)));
    3098            0 :                 if (Ray_r > 0.0) {
    3099            0 :                     HorDis /= Ray_r;
    3100            0 :                     GroundHitPt.x = RWIN2.x + HorDis * Ray_1;
    3101            0 :                     GroundHitPt.y = RWIN2.y + HorDis * Ray_2;
    3102              :                 } else { // Treat as angle==0
    3103            0 :                     GroundHitPt.x = RWIN2.x + HorDis;
    3104            0 :                     GroundHitPt.y = RWIN2.y;
    3105              :                 }
    3106              :             }
    3107       453596 :             Real64 const GILSK_mult((state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * ObTrans * SkyObstructionMult);
    3108       453596 :             Real64 const TVISB_ObTrans(TVISB * ObTrans);
    3109       453596 :             Real64 const AVWLSU_add(TVISB_ObTrans * dl->horIllum[iHour].sun * (state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi));
    3110       453596 :             Vector3<Real64> const SUNCOS_iHour(s_surf->SurfSunCosHourly(iHour));
    3111       453596 :             assert(equal_dimensions(dl->dirIllum, dl->avgWinLum));
    3112       453596 :             auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
    3113       453596 :             auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
    3114              : 
    3115      2267980 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3116      1814384 :                 if (PHRAY > 0.0) {                                                                     // Ray heads upward to sky
    3117      1531440 :                     Real64 ELUM = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), THRAY, PHRAY); // Sky or ground luminance (cd/m2)
    3118      1531440 :                     XDirIllum.sky[iSky] = ELUM * DOMEGA_Ray_3;
    3119      1531440 :                     Real64 DEDIR = XDirIllum.sky[iSky] * TVISB; // Illuminance contribution at reference point from window element (lux)
    3120      1531440 :                     edirsk.sky[iSky] += DEDIR * ObTrans;
    3121      1531440 :                     avwlsk.sky[iSky] += ELUM * TVISB_ObTrans;
    3122      1531440 :                     XAvgWinLum.sky[iSky] = ELUM * ObTrans;
    3123              :                 } else { // PHRAY <= 0.
    3124              :                     // Ray heads downward to ground.
    3125              :                     // Contribution from sky diffuse reflected from ground
    3126       282944 :                     XAvgWinLum.sky[iSky] = dl->horIllum[iHour].sky[iSky] * GILSK_mult;
    3127       282944 :                     avwlsk.sky[iSky] += TVISB * XAvgWinLum.sky[iSky];
    3128              :                     // Contribution from beam solar reflected from ground (beam reaching ground point
    3129              :                     // can be obstructed [SunObstructionMult < 1.0] if CalcSolRefl = .TRUE.)
    3130              :                 } // End of check if ray is going up or down
    3131              :             } // for (iSky)
    3132              : 
    3133       453596 :             if (PHRAY <= 0.0) {
    3134              :                 // SunObstructionMult = 1.0; //Tuned
    3135        70736 :                 if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
    3136              :                     // Sun reaches ground point if vector from this point to the sun is unobstructed
    3137            0 :                     hitObs = false;
    3138            0 :                     Vector3<Real64> ObsHitPt; // Coordinates of hit point on an obstruction (m)
    3139            0 :                     for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    3140            0 :                         hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, SUNCOS_iHour, ObsHitPt);
    3141            0 :                         if (hitObs) {
    3142            0 :                             break;
    3143              :                         }
    3144            0 :                     }
    3145              :                     // if ( hitObs ) SunObstructionMult = 0.0;
    3146            0 :                     if (!hitObs) {
    3147            0 :                         dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
    3148              :                     }
    3149            0 :                 } else {
    3150        70736 :                     dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
    3151              :                 }
    3152              :             } // (PHRAY <= 0.0)
    3153       453596 :         }
    3154              :     } // End of check if bare window or TDD:DIFFUSER
    3155              : 
    3156              :     // Illuminance from beam solar (without interior reflection)
    3157              :     // Just run this once on the last pass
    3158       453596 :     if (iXelement == NWX && iYelement == NWY) { // Last pass
    3159              : 
    3160              :         // Beam solar reaching reference point directly without exterior reflection
    3161              : 
    3162              :         // Unit vector from ref. pt. to sun
    3163         2066 :         Vector3<Real64> RAYCOS;
    3164         2066 :         RAYCOS.x = dl->sunAngles.cosPhi * std::cos(dl->sunAngles.theta);
    3165         2066 :         RAYCOS.y = dl->sunAngles.cosPhi * std::sin(dl->sunAngles.theta);
    3166         2066 :         RAYCOS.z = dl->sunAngles.sinPhi;
    3167              : 
    3168              :         // Is sun on front side of exterior window?
    3169         2066 :         Real64 COSI = dot(WNORM2, RAYCOS); // Cosine of angle between direct sun and window outward normal
    3170              :         bool hit;                          // True if ray from ref point thru window element hits an obstruction
    3171              :         bool hitWin;                       // True if ray passes thru window
    3172         2066 :         Vector3<Real64> HP;
    3173         2066 :         if (COSI > 0.0) {
    3174              : 
    3175              :             // Does RAYCOS pass thru exterior window? HP is point that RAYCOS intersects window plane.
    3176         1454 :             hitWin = PierceSurface(state, IWin2, RREF2, RAYCOS, HP);
    3177              :             // True if ray from ref pt to sun hits an interior obstruction
    3178         1454 :             if (hitWin) {
    3179          173 :                 bool hitIntObsDisk = false;
    3180          173 :                 if (extWinType == ExtWinType::InZone) {
    3181              :                     // Check for interior obstructions between reference point and HP.
    3182          173 :                     hitIntObsDisk = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
    3183              :                 }
    3184          173 :                 ObTransDisk = 0.0; // Init value
    3185              :                 // Init flag for vector from RP to sun passing through interior window
    3186          173 :                 bool hitIntWinDisk = false;
    3187          173 :                 if (extWinType == ExtWinType::AdjZone) { // This block is for RPs in zones with interior windows
    3188              :                     // adjacent to zones with exterior windows
    3189              :                     // Does RAYCOS pass through interior window in zone containing RP?
    3190              :                     // Loop over zone surfaces looking for interior windows between reference point and sun
    3191              :                     // Surface number of int window intersected by ray betw ref pt and sun
    3192              :                     int IntWinDiskHitNum;
    3193              :                     // Intersection point on an interior window for ray from ref pt to sun (m)
    3194            0 :                     Vector3<Real64> HitPtIntWinDisk;
    3195            0 :                     auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
    3196            0 :                     for (int const spaceNum : thisZone.spaceIndexes) {
    3197            0 :                         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    3198            0 :                         for (int IntWinDisk = thisSpace.WindowSurfaceFirst, IntWinDisk_end = thisSpace.WindowSurfaceLast;
    3199            0 :                              IntWinDisk <= IntWinDisk_end;
    3200              :                              ++IntWinDisk) {
    3201            0 :                             auto const &surfIntWinDisk = s_surf->Surface(IntWinDisk);
    3202            0 :                             if (surfIntWinDisk.ExtBoundCond < 1) {
    3203            0 :                                 continue;
    3204              :                             }
    3205              : 
    3206            0 :                             if (s_surf->Surface(surfIntWinDisk.ExtBoundCond).Zone != s_surf->Surface(IWin2).Zone) {
    3207            0 :                                 continue;
    3208              :                             }
    3209              : 
    3210            0 :                             hitIntWinDisk = PierceSurface(state, IntWinDisk, RREF, RAYCOS, HitPtIntWinDisk);
    3211            0 :                             if (!hitIntWinDisk) {
    3212            0 :                                 continue;
    3213              :                             }
    3214              : 
    3215            0 :                             IntWinDiskHitNum = IntWinDisk;
    3216            0 :                             COSBIntWin = dot(surfIntWinDisk.OutNormVec, RAYCOS);
    3217            0 :                             if (COSBIntWin <= 0.0) {
    3218            0 :                                 hitIntWinDisk = false;
    3219            0 :                                 IntWinDiskHitNum = 0;
    3220            0 :                                 continue;
    3221              :                             }
    3222            0 :                             TVISIntWinDisk =
    3223            0 :                                 Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWinDisk.Construction).TransVisBeamCoef);
    3224            0 :                             break;
    3225              :                         } // for (IntWinDisk)
    3226            0 :                     } // for (spaceNum)
    3227              : 
    3228            0 :                     if (!hitIntWinDisk) { // Vector from RP to sun does not pass through interior window
    3229            0 :                         ObTransDisk = 0.0;
    3230            0 :                         hit = true; //! fcw Is this needed?
    3231              :                     }
    3232              : 
    3233              :                     // Check for interior obstructions between ref point and interior window
    3234            0 :                     hitIntObsDisk = false;
    3235            0 :                     if (hitIntWinDisk) {
    3236            0 :                         hitIntObsDisk = DayltgHitInteriorObstruction(state, IntWinDiskHitNum, RREF, HitPtIntWinDisk);
    3237              :                         // If no obstruction between RP and hit int win, check for obstruction
    3238              :                         // between int win and ext win
    3239            0 :                         if (!hitIntObsDisk) {
    3240            0 :                             hitIntObsDisk = DayltgHitBetWinObstruction(state, IntWinDiskHitNum, IWin2, HitPtIntWinDisk, HP);
    3241              :                         }
    3242              :                     }
    3243            0 :                     if (hitIntObsDisk) {
    3244            0 :                         ObTransDisk = 0.0;
    3245              :                     }
    3246            0 :                 } // case where RP is in zone with interior window adjacent to zone with exterior window
    3247              : 
    3248              :                 //                    hitExtObsDisk = false; //Unused Set but never used
    3249              :                 // RJH 08-25-07 hitIntWinDisk should not be reset to false here, and should be tested below.
    3250              :                 // This is to correct logic flaw causing direct solar to reach adjacent zone refpt
    3251              :                 // when vector to sun does not pass through interior window
    3252              :                 // hitIntWinDisk = false
    3253          173 :                 if (!hitIntObsDisk) { // No interior obstruction was hit
    3254              :                     // Net transmittance of exterior obstructions encountered by RAYCOS
    3255              :                     // ObTransDisk = 1.0 will be returned if no exterior obstructions are hit.
    3256          173 :                     ObTransDisk = DayltgHitObstruction(state, iHour, IWin2, RREF2, RAYCOS);
    3257              :                     //                        if ( ObTransDisk < 1.0 ) hitExtObsDisk = true; //Unused Set but never used
    3258              :                     // RJH 08-26-07 However, if this is a case of interior window
    3259              :                     // and vector to sun does not pass through interior window
    3260              :                     // then reset ObTransDisk to 0.0 since it is the key test for adding
    3261              :                     // contribution of sun to RP below.
    3262          173 :                     if ((extWinType == ExtWinType::AdjZone) && (!hitIntWinDisk)) {
    3263            0 :                         ObTransDisk = 0.0;
    3264              :                     }
    3265              :                 }
    3266              : 
    3267              :                 // PETER: need side wall mounted TDD to test this
    3268              :                 // PETER: probably need to replace RREF2 with RWIN2
    3269              :                 // PETER: need to check for interior obstructions too.
    3270              : 
    3271          173 :                 if (ObTransDisk > 1.e-6) {
    3272              : 
    3273              :                     // Sun reaches reference point;  increment illuminance.
    3274              :                     // Direct normal illuminance is normalized to 1.0
    3275              : 
    3276          173 :                     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
    3277              :                         // No beam is transmitted.  Takes care of TDD with a bare diffuser and all types of blinds.
    3278            0 :                         TVISS = 0.0;
    3279              :                     } else {
    3280              :                         // Beam transmittance for bare window and all types of blinds
    3281          173 :                         TVISS = Window::POLYF(COSI, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
    3282          173 :                                 surfWin.lightWellEff;
    3283          173 :                         if (extWinType == ExtWinType::AdjZone && hitIntWinDisk) {
    3284            0 :                             TVISS *= TVISIntWinDisk;
    3285              :                         }
    3286              :                     }
    3287              : 
    3288          173 :                     dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = RAYCOS.z * TVISS * ObTransDisk; // Bare window
    3289              : 
    3290          173 :                     Real64 transBmBmMult = 0.0;
    3291              : 
    3292          173 :                     if (ANY_BLIND(ShType)) {
    3293            0 :                         auto const &surfShade = s_surf->surfShades(IWin);
    3294            0 :                         auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    3295            0 :                         assert(matBlind != nullptr);
    3296              : 
    3297            0 :                         Real64 ProfAng = ProfileAngle(state, IWin, RAYCOS, matBlind->SlatOrientation);
    3298            0 :                         transBmBmMult = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
    3299            0 :                         dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
    3300              : 
    3301          173 :                     } else if (ShType == WinShadingType::ExtScreen) {
    3302              :                         //                          pass angle from sun to window normal here using PHSUN and THSUN from above and surface angles
    3303              :                         //                          SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
    3304              :                         //                          SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
    3305            0 :                         auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
    3306            0 :                         assert(screen != nullptr);
    3307              : 
    3308            0 :                         Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    3309            0 :                         Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    3310              :                         int ip1, ip2, it1, it2;
    3311              :                         BilinearInterpCoeffs coeffs;
    3312            0 :                         Material::NormalizePhiTheta(phi, theta);
    3313            0 :                         Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    3314            0 :                         GetBilinearInterpCoeffs(
    3315            0 :                             phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    3316            0 :                         transBmBmMult = BilinearInterp(screen->btars[ip1][it1].BmTrans,
    3317            0 :                                                        screen->btars[ip1][it2].BmTrans,
    3318            0 :                                                        screen->btars[ip2][it1].BmTrans,
    3319            0 :                                                        screen->btars[ip2][it2].BmTrans,
    3320              :                                                        coeffs);
    3321              : 
    3322            0 :                         dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
    3323              :                     }
    3324              : 
    3325          173 :                     if (CalledFrom == CalledFor::RefPoint) {
    3326              :                         // Glare from solar disk
    3327              : 
    3328              :                         // Position factor for sun (note that AZVIEW is wrt y-axis and THSUN is wrt
    3329              :                         // x-axis of absolute coordinate system.
    3330          112 :                         Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
    3331          112 :                         Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
    3332              :                         Real64 POSFAC =
    3333          112 :                             DayltgGlarePositionFactor(XR, YR); // Position factor for a window element / ref point / view vector combination
    3334              : 
    3335          112 :                         WindowSolidAngleDaylightPoint = s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd;
    3336              : 
    3337          112 :                         if (POSFAC != 0.0 && WindowSolidAngleDaylightPoint > 0.000001) {
    3338              :                             // Increment window luminance.  Luminance of solar disk (cd/m2)
    3339              :                             // is 1.47*10^4*(direct normal solar illuminance) for direct normal solar
    3340              :                             // illuminance in lux (lumens/m2). For purposes of calculating daylight factors
    3341              :                             // direct normal solar illuminance = 1.0.
    3342              :                             // Solid angle subtended by sun is 0.000068 steradians
    3343              : 
    3344            0 :                             XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) / std::pow(WindowSolidAngleDaylightPoint, 0.8);
    3345            0 :                             dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = XAVWL * TVISS * ObTransDisk; // Bare window
    3346              : 
    3347            0 :                             if (ANY_BLIND(ShType)) {
    3348            0 :                                 dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
    3349            0 :                             } else if (ShType == WinShadingType::ExtScreen) {
    3350            0 :                                 dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
    3351              :                             }
    3352              :                         } // Position Factor
    3353              :                     } // if (calledFrom == RefPt)
    3354              :                 } // if (ObTransDisk > 1e-6) // Beam avoids all obstructions
    3355              :             } // if (hitWin)
    3356              :         } // if (COSI > 0.0) // Sun on front side
    3357              : 
    3358              :         // Beam solar reaching reference point after beam-beam (specular) reflection from
    3359              :         // an exterior surface
    3360              : 
    3361         2066 :         if (s_surf->CalcSolRefl) {
    3362              :             // Receiving surface number corresponding this window
    3363            0 :             int RecSurfNum = s_surf->SurfShadowRecSurfNum(IWin2);
    3364            0 :             if (RecSurfNum > 0) { // interior windows do not apply
    3365            0 :                 if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
    3366              :                     bool hitRefl;              // True iff ray hits reflecting surface
    3367            0 :                     Vector3<Real64> HitPtRefl; // Point that ray hits reflecting surface
    3368            0 :                     Vector3<Real64> SunVecMir; // Sun ray mirrored in reflecting surface
    3369            0 :                     Vector3<Real64> ReflNorm;  // Normal vector to reflecting surface
    3370              :                     // This window has associated obstructions that could reflect beam onto the window
    3371            0 :                     for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end;
    3372              :                          ++loop) {
    3373            0 :                         int ReflSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop);
    3374            0 :                         int ReflSurfNumX = ReflSurfNum;
    3375              :                         // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    3376              :                         // The following gets the correct side of a shadowing surface for reflection.
    3377            0 :                         if (s_surf->Surface(ReflSurfNum).IsShadowing) {
    3378            0 :                             if (dot(RAYCOS, s_surf->Surface(ReflSurfNum).OutNormVec) < 0.0) {
    3379            0 :                                 ReflSurfNumX = ReflSurfNum + 1;
    3380              :                             }
    3381              :                         }
    3382              :                         // Require that the surface can have specular reflection
    3383            0 :                         if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window || s_surf->SurfShadowGlazingFrac(ReflSurfNum) > 0.0) {
    3384            0 :                             ReflNorm = s_surf->Surface(ReflSurfNumX).OutNormVec;
    3385              :                             // Vector to sun that is mirrored in obstruction
    3386            0 :                             SunVecMir = RAYCOS - 2.0 * dot(RAYCOS, ReflNorm) * ReflNorm;
    3387              :                             // Skip if reflecting surface is not sunlit
    3388            0 :                             if (state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNumX) < 0.01) {
    3389            0 :                                 continue;
    3390              :                             }
    3391              :                             // Skip if altitude angle of mirrored sun is negative since reflected sun cannot
    3392              :                             // reach reference point in this case
    3393            0 :                             if (SunVecMir.z <= 0.0) {
    3394            0 :                                 continue;
    3395              :                             }
    3396              :                             // Cosine of incidence angle of reflected beam on window
    3397            0 :                             Real64 CosIncAngRec = dot(s_surf->Surface(IWin2).OutNormVec, SunVecMir);
    3398            0 :                             if (CosIncAngRec <= 0.0) {
    3399            0 :                                 continue;
    3400              :                             }
    3401              :                             // Does ray from ref. pt. along SunVecMir pass through window?
    3402            0 :                             hitWin = PierceSurface(state, IWin2, RREF2, SunVecMir, HP);
    3403            0 :                             if (!hitWin) {
    3404            0 :                                 continue; // Ray did not pass through window
    3405              :                             }
    3406              :                             // Check if this ray hits interior obstructions
    3407            0 :                             hit = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
    3408            0 :                             if (hit) {
    3409            0 :                                 continue; // Interior obstruction was hit
    3410              :                             }
    3411              :                             // Does ray hit this reflecting surface?
    3412            0 :                             hitRefl = PierceSurface(state, ReflSurfNum, RREF2, SunVecMir, HitPtRefl);
    3413            0 :                             if (!hitRefl) {
    3414            0 :                                 continue; // Ray did not hit this reflecting surface
    3415              :                             }
    3416            0 :                             Real64 ReflDistanceSq = distance_squared(HitPtRefl, RREF2);
    3417            0 :                             Real64 ReflDistance = std::sqrt(ReflDistanceSq);
    3418              :                             // Is ray from ref. pt. to reflection point (HitPtRefl) obstructed?
    3419            0 :                             bool hitObsRefl = false;
    3420            0 :                             Vector3<Real64> HitPtObs; // Hit point on obstruction
    3421            0 :                             for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
    3422            0 :                                  loop2 <= loop2_end;
    3423              :                                  ++loop2) {
    3424            0 :                                 int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
    3425            0 :                                 if (ObsSurfNum == ReflSurfNum || ObsSurfNum == s_surf->Surface(ReflSurfNum).BaseSurf) {
    3426            0 :                                     continue;
    3427              :                                 }
    3428            0 :                                 hitObs = PierceSurface(state, ObsSurfNum, RREF2, SunVecMir, ReflDistance, HitPtObs); // ReflDistance cutoff added
    3429            0 :                                 if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
    3430            0 :                                     if (distance_squared(HitPtObs, RREF2) < ReflDistanceSq) { // Distance squared from ref pt to reflection point
    3431            0 :                                         hitObsRefl = true;
    3432            0 :                                         break;
    3433              :                                     }
    3434              :                                 }
    3435              :                             }
    3436            0 :                             if (hitObsRefl) {
    3437            0 :                                 continue; // Obstruction closer than reflection pt. was hit; go to next obstruction
    3438              :                             }
    3439              :                             // There is no obstruction for this ray between ref pt and hit pt on reflecting surface.
    3440              :                             // See if ray from hit pt on reflecting surface to original (unmirrored) sun position is obstructed
    3441            0 :                             hitObs = false;
    3442            0 :                             if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    3443              :                                 // Reflecting surface is a window.
    3444              :                                 // Receiving surface number for this reflecting window.
    3445            0 :                                 int ReflSurfRecNum = s_surf->SurfShadowRecSurfNum(ReflSurfNum);
    3446            0 :                                 if (ReflSurfRecNum > 0) {
    3447              :                                     // Loop over possible obstructions for this reflecting window
    3448            0 :                                     for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
    3449            0 :                                          loop2 <= loop2_end;
    3450              :                                          ++loop2) {
    3451              :                                         int const ObsSurfNum =
    3452            0 :                                             state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
    3453            0 :                                         hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
    3454            0 :                                         if (hitObs) {
    3455            0 :                                             break;
    3456              :                                         }
    3457              :                                     }
    3458              :                                 }
    3459              :                             } else {
    3460              :                                 // Reflecting surface is a building shade
    3461            0 :                                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    3462            0 :                                     if (ObsSurfNum == ReflSurfNum) {
    3463            0 :                                         continue;
    3464              :                                     }
    3465            0 :                                     hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
    3466            0 :                                     if (hitObs) {
    3467            0 :                                         break;
    3468              :                                     }
    3469            0 :                                 }
    3470              :                             } // End of check if reflector is a window or shadowing surface
    3471              : 
    3472            0 :                             if (hitObs) {
    3473            0 :                                 continue; // Obstruction hit between reflection hit point and sun; go to next obstruction
    3474              :                             }
    3475              : 
    3476              :                             // No obstructions. Calculate reflected beam illuminance at ref. pt. from this reflecting surface.
    3477            0 :                             SpecReflectance = 0.0;
    3478            0 :                             Real64 CosIncAngRefl = std::abs(dot(RAYCOS, ReflNorm)); // Cos of angle of incidence of beam on reflecting surface
    3479            0 :                             if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    3480            0 :                                 int const ConstrNumRefl = s_surf->SurfActiveConstruction(ReflSurfNum);
    3481              :                                 SpecReflectance =
    3482            0 :                                     Window::POLYF(std::abs(CosIncAngRefl), state.dataConstruction->Construct(ConstrNumRefl).ReflSolBeamFrontCoef);
    3483              :                             }
    3484            0 :                             if (s_surf->Surface(ReflSurfNum).IsShadowing && s_surf->SurfShadowGlazingConstruct(ReflSurfNum) > 0) {
    3485            0 :                                 SpecReflectance =
    3486            0 :                                     s_surf->SurfShadowGlazingFrac(ReflSurfNum) *
    3487            0 :                                     Window::POLYF(
    3488              :                                         std::abs(CosIncAngRefl),
    3489            0 :                                         state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(ReflSurfNum)).ReflSolBeamFrontCoef);
    3490              :                             }
    3491            0 :                             TVisRefl = Window::POLYF(CosIncAngRec, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
    3492            0 :                                        surfWin.lightWellEff;
    3493            0 :                             dl->dirIllum(iHour)[iWinCover_Bare].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl; // Bare window
    3494              : 
    3495            0 :                             Real64 TransBmBmMultRefl = 0.0;
    3496            0 :                             if (ANY_BLIND(ShType)) {
    3497            0 :                                 auto const &surfShade = s_surf->surfShades(IWin);
    3498            0 :                                 auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    3499              : 
    3500            0 :                                 Real64 ProfAng = ProfileAngle(state, IWin, SunVecMir, matBlind->SlatOrientation);
    3501            0 :                                 TransBmBmMultRefl = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
    3502            0 :                                 dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
    3503            0 :                             } else if (ShType == WinShadingType::ExtScreen) {
    3504              :                                 // pass angle from sun to window normal here using PHSUN and THSUN from above and
    3505              :                                 // surface angles SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
    3506              :                                 // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
    3507            0 :                                 auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
    3508            0 :                                 assert(screen != nullptr);
    3509              : 
    3510            0 :                                 Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    3511            0 :                                 Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    3512              :                                 int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
    3513              :                                 BilinearInterpCoeffs coeffs;
    3514            0 :                                 Material::NormalizePhiTheta(phi, theta);
    3515            0 :                                 Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    3516            0 :                                 GetBilinearInterpCoeffs(
    3517            0 :                                     phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    3518              : 
    3519            0 :                                 TransBmBmMultRefl = BilinearInterp(screen->btars[ip1][it1].BmTrans,
    3520            0 :                                                                    screen->btars[ip1][it2].BmTrans,
    3521            0 :                                                                    screen->btars[ip2][it1].BmTrans,
    3522            0 :                                                                    screen->btars[ip2][it2].BmTrans,
    3523              :                                                                    coeffs);
    3524            0 :                                 dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
    3525              :                             } // End of check if window has a blind or screen
    3526              : 
    3527              :                             // Glare from reflected solar disk
    3528              : 
    3529            0 :                             PHSUNrefl = SunVecMir.z;
    3530            0 :                             THSUNrefl = std::atan2(SunVecMir.y, SunVecMir.x);
    3531            0 :                             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - THSUNrefl) + 0.001);
    3532            0 :                             Real64 YR = std::tan(PHSUNrefl + 0.001);
    3533            0 :                             Real64 POSFAC = DayltgGlarePositionFactor(XR, YR);
    3534            0 :                             if (POSFAC != 0.0 && s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd > 0.000001) {
    3535            0 :                                 XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) /
    3536            0 :                                         std::pow(s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd, 0.8);
    3537            0 :                                 dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += XAVWL * TVisRefl * SpecReflectance; // Bare window
    3538            0 :                                 if (ANY_BLIND(ShType)) {
    3539            0 :                                     dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
    3540            0 :                                 } else if (ShType == WinShadingType::ExtScreen) {
    3541            0 :                                     dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
    3542              :                                 }
    3543              :                             }
    3544            0 :                         } // End of check that obstruction can specularly reflect
    3545              :                     } // End of loop over obstructions associated with this window
    3546              : 
    3547            0 :                 } // End of check if this window has associated obstructions
    3548              :             } // End of check to see if this is exterior type window
    3549              :         } // End of check if exterior reflection calculation is in effect
    3550              : 
    3551         2066 :     } // Last pass
    3552              : 
    3553       453596 :     if ((ICtrl > 0 && (ANY_BLIND(ShType) || ANY_SHADE_SCREEN(ShType))) || s_surf->SurfWinSolarDiffusing(IWin)) {
    3554              : 
    3555              :         // ----- CASE II -- WINDOW WITH SCREEN, SHADE, BLIND, OR DIFFUSING WINDOW
    3556              :         // Interior window visible transmittance multiplier for exterior window in adjacent zone
    3557            0 :         TVisIntWinMult = 1.0;
    3558            0 :         TVisIntWinDiskMult = 1.0;
    3559            0 :         if (s_surf->Surface(IWin).SolarEnclIndex != dl->daylightControl(daylightCtrlNum).enclIndex) {
    3560            0 :             TVisIntWinMult = TVISIntWin;
    3561            0 :             TVisIntWinDiskMult = TVISIntWinDisk;
    3562              :         }
    3563              : 
    3564            0 :         Real64 const DOMEGA_Ray_3_TVisIntWinMult(DOMEGA_Ray_3 * TVisIntWinMult);
    3565              : 
    3566            0 :         auto &wlumsk = dl->winLum(iHour)[iWinCover_Shaded];
    3567            0 :         auto &edirsk = dl->dirIllum(iHour)[iWinCover_Shaded];
    3568            0 :         auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Shaded];
    3569            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3570              :             // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
    3571            0 :             avwlsk.sky[iSky] += wlumsk.sky[iSky] * TVisIntWinMult;
    3572            0 :             if (PHRAY > 0.0) {
    3573            0 :                 edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3_TVisIntWinMult;
    3574              :             }
    3575              :         } // for (iSky)
    3576              : 
    3577            0 :         dl->avgWinLum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * TVisIntWinMult;
    3578            0 :         dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += dl->winLum(iHour)[iWinCover_Shaded].sunDisk * TVisIntWinDiskMult;
    3579              : 
    3580            0 :         if (PHRAY > 0.0) {
    3581            0 :             dl->dirIllum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * DOMEGA_Ray_3_TVisIntWinMult;
    3582              :         }
    3583              :     }
    3584       453596 : } // FigureDayltgCoeffsAtPointsForSunPosition()
    3585              : 
    3586         1618 : void FigureRefPointDayltgFactorsToAddIllums(EnergyPlusData &state,
    3587              :                                             int const daylightCtrlNum, // Current daylighting control number
    3588              :                                             int const iRefPoint,
    3589              :                                             int const iHour,
    3590              :                                             int &ISunPos,
    3591              :                                             int const IWin,
    3592              :                                             int const loopwin,
    3593              :                                             int const NWX,  // Number of window elements in x direction for dayltg calc
    3594              :                                             int const NWY,  // Number of window elements in y direction for dayltg calc
    3595              :                                             int const ICtrl // Window control counter
    3596              : )
    3597              : {
    3598              : 
    3599              :     // SUBROUTINE INFORMATION:
    3600              :     //       AUTHOR         B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann
    3601              :     //       DATE WRITTEN   Oct. 2012
    3602              : 
    3603              :     // PURPOSE OF THIS SUBROUTINE:
    3604              :     // calculation worker routine to fill daylighting coefficients
    3605              : 
    3606              :     // METHODOLOGY EMPLOYED:
    3607              :     // this version is just for reference points.
    3608              : 
    3609              :     // SUBROUTINE PARAMETER DEFINITIONS:
    3610         1618 :     Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating the daylighting and glare factors
    3611              : 
    3612              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3613         1618 :     auto &dl = state.dataDayltg;
    3614         1618 :     auto &s_surf = state.dataSurface;
    3615              : 
    3616         1618 :     if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) {
    3617         1052 :         return;
    3618              :     }
    3619              : 
    3620          566 :     ++ISunPos;
    3621              : 
    3622              :     // Altitude of sun (degrees)
    3623          566 :     dl->sunAngles = dl->sunAnglesHr[iHour];
    3624              : 
    3625          566 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    3626              : 
    3627          566 :     auto &surf = s_surf->Surface(IWin);
    3628              : 
    3629          566 :     int const enclNum = surf.SolarEnclIndex;
    3630              : 
    3631              :     // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
    3632              : 
    3633              :     // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
    3634              :     //  related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
    3635              :     //  and interior surfaces with high visible reflectance.
    3636              :     // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
    3637              :     //  the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
    3638              : 
    3639          566 :     auto &daylFacHr = thisDayltgCtrl.daylFac[iHour];
    3640              : 
    3641         1698 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    3642              : 
    3643         1132 :         auto const &gilsk = dl->horIllum[iHour];
    3644         1132 :         auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
    3645         1132 :         auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
    3646         1132 :         auto const &avwlsk = dl->avgWinLum(iHour)[iWinCover];
    3647              : 
    3648         1132 :         auto &daylFac = daylFacHr(loopwin, iRefPoint)[iWinCover];
    3649         1132 :         auto &illFac = daylFac[iLum_Illum];
    3650         1132 :         auto &sourceFac = daylFac[iLum_Source];
    3651         1132 :         auto &backFac = daylFac[iLum_Back];
    3652              : 
    3653         5660 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
    3654              : 
    3655         4528 :             if (gilsk.sky[iSky] > tmpDFCalc) {
    3656         4528 :                 illFac.sky[iSky] = (edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky];
    3657         4528 :                 sourceFac.sky[iSky] = avwlsk.sky[iSky] / (NWX * NWY * gilsk.sky[iSky]);
    3658         4528 :                 backFac.sky[iSky] = eintsk.sky[iSky] * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * gilsk.sky[iSky]);
    3659              :             } else {
    3660            0 :                 illFac.sky[iSky] = 0.0;
    3661            0 :                 sourceFac.sky[iSky] = 0.0;
    3662            0 :                 backFac.sky[iSky] = 0.0;
    3663              :             }
    3664              : 
    3665              :         } // for (iSky)
    3666              : 
    3667         1132 :         if (dl->horIllum[iHour].sun > tmpDFCalc) {
    3668         1072 :             daylFac[iLum_Illum].sun = (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
    3669         2144 :             daylFac[iLum_Illum].sunDisk =
    3670         1072 :                 (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
    3671         1072 :             daylFac[iLum_Source].sun = dl->avgWinLum(iHour)[iWinCover].sun / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
    3672         1072 :             daylFac[iLum_Source].sunDisk = dl->avgWinLum(iHour)[iWinCover].sunDisk / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
    3673         1072 :             daylFac[iLum_Back].sun = dl->reflIllum(iHour)[iWinCover].sun * dl->enclDaylight(enclNum).aveVisDiffReflect /
    3674         1072 :                                      (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
    3675         2144 :             daylFac[iLum_Back].sunDisk = dl->reflIllum(iHour)[iWinCover].sunDisk * dl->enclDaylight(enclNum).aveVisDiffReflect /
    3676         1072 :                                          (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
    3677              :         } else {
    3678           60 :             daylFac[iLum_Illum].sun = 0.0;
    3679           60 :             daylFac[iLum_Illum].sunDisk = 0.0;
    3680              : 
    3681           60 :             daylFac[iLum_Source].sun = 0.0;
    3682           60 :             daylFac[iLum_Source].sunDisk = 0.0;
    3683              : 
    3684           60 :             daylFac[iLum_Back].sun = 0.0;
    3685           60 :             daylFac[iLum_Back].sunDisk = 0.0;
    3686              :         }
    3687              :     } // for (jSH)
    3688              : 
    3689              :     // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
    3690          566 :     if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
    3691              : 
    3692            0 :         Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // Ratio of Tvis of fully-switched state to that of the unswitched state
    3693            0 :         auto &daylFac2 = daylFacHr(loopwin, iRefPoint)[iWinCover_Shaded];
    3694            0 :         auto const &daylFac1 = daylFacHr(loopwin, iRefPoint)[iWinCover_Bare];
    3695              : 
    3696            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3697            0 :             daylFac2[iLum_Illum].sky[iSky] = daylFac1[iLum_Illum].sky[iSky] * VTR;
    3698            0 :             daylFac2[iLum_Source].sky[iSky] = daylFac1[iLum_Source].sky[iSky] * VTR;
    3699            0 :             daylFac2[iLum_Back].sky[iSky] = daylFac1[iLum_Back].sky[iSky] * VTR;
    3700              :         } // for (iSky)
    3701              : 
    3702            0 :         daylFac2[iLum_Illum].sun = daylFac1[iLum_Illum].sun * VTR;
    3703            0 :         daylFac2[iLum_Source].sun = daylFac1[iLum_Source].sun * VTR;
    3704            0 :         daylFac2[iLum_Back].sun = daylFac1[iLum_Back].sun * VTR;
    3705            0 :         daylFac2[iLum_Illum].sunDisk = daylFac1[iLum_Illum].sunDisk * VTR;
    3706            0 :         daylFac2[iLum_Source].sunDisk = daylFac1[iLum_Source].sunDisk * VTR;
    3707            0 :         daylFac2[iLum_Back].sunDisk = daylFac1[iLum_Back].sunDisk * VTR;
    3708              :     } // ICtrl > 0
    3709              : } // FigureRefPointDayltgFactorsToAddIllums()
    3710              : 
    3711         2400 : void FigureMapPointDayltgFactorsToAddIllums(EnergyPlusData &state,
    3712              :                                             int const MapNum,
    3713              :                                             int const iMapPoint,
    3714              :                                             int const iHour,
    3715              :                                             int const IWin,
    3716              :                                             int const loopwin,
    3717              :                                             int const ICtrl // Window control counter
    3718              : )
    3719              : {
    3720              : 
    3721              :     // SUBROUTINE INFORMATION:
    3722              :     //       AUTHOR         B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann, Peter Ellis, Linda Lawrie
    3723              :     //       DATE WRITTEN   Nov. 2012
    3724              : 
    3725              :     // PURPOSE OF THIS SUBROUTINE:
    3726              :     // calculation worker routine to fill daylighting coefficients
    3727              : 
    3728              :     // METHODOLOGY EMPLOYED:
    3729              :     // this version is just for map points.
    3730              : 
    3731              :     // SUBROUTINE PARAMETER DEFINITIONS:
    3732         2400 :     Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating
    3733              :     // the daylighting and glare factors
    3734              : 
    3735              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3736         2400 :     auto &dl = state.dataDayltg;
    3737         2400 :     auto &s_surf = state.dataSurface;
    3738              : 
    3739         2400 :     if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) {
    3740          900 :         return;
    3741              :     }
    3742              : 
    3743              :     // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or blind;
    3744              : 
    3745              :     // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
    3746              :     //  related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
    3747              :     //  and interior surfaces with high visible reflectance.
    3748              :     // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
    3749              :     //  the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
    3750              : 
    3751         1500 :     auto &illumMap = dl->illumMaps(MapNum);
    3752         1500 :     auto &daylFacHr = illumMap.daylFac[iHour];
    3753         4500 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    3754              : 
    3755         3000 :         auto const &gilsk = dl->horIllum[iHour];
    3756         3000 :         auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
    3757         3000 :         auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
    3758         3000 :         auto &illSky = daylFacHr(loopwin, iMapPoint)[iWinCover];
    3759              : 
    3760        15000 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
    3761        12000 :             illSky.sky[iSky] = (gilsk.sky[iSky] > tmpDFCalc) ? ((edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky]) : 0.0;
    3762              :         } // for (iSky)
    3763              : 
    3764         3000 :         if (dl->horIllum[iHour].sun > tmpDFCalc) {
    3765         2800 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sun =
    3766         2800 :                 (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
    3767         2800 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk =
    3768         2800 :                 (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
    3769              :         } else {
    3770          200 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sun = 0.0;
    3771          200 :             daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk = 0.0;
    3772              :         }
    3773              :     } // for (iWinCover)
    3774              : 
    3775              :     // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
    3776         1500 :     if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
    3777            0 :         Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // ratio of Tvis of switched to unswitched state
    3778            0 :         auto &illSky2 = daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded];
    3779            0 :         auto const &illSky1 = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare];
    3780            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    3781            0 :             illSky2.sky[iSky] = illSky1.sky[iSky] * VTR;
    3782              :         }
    3783              : 
    3784            0 :         daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sun = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sun * VTR;
    3785            0 :         daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sunDisk = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sunDisk * VTR;
    3786              :     } // ICtrl > 0
    3787              : }
    3788              : 
    3789          111 : void GetDaylightingParametersInput(EnergyPlusData &state)
    3790              : {
    3791              : 
    3792              :     // SUBROUTINE INFORMATION:
    3793              :     //       AUTHOR         Linda Lawrie
    3794              :     //       DATE WRITTEN   Oct 2004
    3795              : 
    3796              :     // PURPOSE OF THIS SUBROUTINE:
    3797              :     // This subroutine provides a simple structure to get all daylighting
    3798              :     // parameters.
    3799          111 :     auto &dl = state.dataDayltg;
    3800          111 :     auto &s_surf = state.dataSurface;
    3801              : 
    3802          111 :     if (!dl->getDaylightingParametersInputFlag) {
    3803            1 :         return;
    3804              :     }
    3805          110 :     dl->getDaylightingParametersInputFlag = false;
    3806              : 
    3807          110 :     auto const &s_ipsc = state.dataIPShortCut;
    3808          110 :     s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
    3809          110 :     bool ErrorsFound = false;
    3810          110 :     int TotDaylightingControls = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    3811          110 :     if (TotDaylightingControls > 0) {
    3812            7 :         dl->enclDaylight.allocate(state.dataViewFactor->NumOfSolarEnclosures);
    3813            7 :         GetInputDayliteRefPt(state, ErrorsFound);
    3814            7 :         GetDaylightingControls(state, ErrorsFound);
    3815            7 :         GeometryTransformForDaylighting(state);
    3816            7 :         GetInputIlluminanceMap(state, ErrorsFound);
    3817            7 :         GetLightWellData(state, ErrorsFound);
    3818            7 :         if (ErrorsFound) {
    3819            0 :             ShowFatalError(state, "Program terminated for above reasons, related to DAYLIGHTING");
    3820              :         }
    3821            7 :         DayltgSetupAdjZoneListsAndPointers(state);
    3822              :     }
    3823              : 
    3824          110 :     dl->maxNumRefPtInAnyDaylCtrl = 0;
    3825          110 :     dl->maxNumRefPtInAnyEncl = 0;
    3826              :     // Loop through all daylighting controls to find total reference points in each enclosure
    3827          119 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    3828            9 :         int numRefPoints = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
    3829            9 :         dl->maxNumRefPtInAnyDaylCtrl = max(numRefPoints, dl->maxNumRefPtInAnyDaylCtrl);
    3830              :     }
    3831          250 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    3832          140 :         dl->maxNumRefPtInAnyEncl = max(state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints, dl->maxNumRefPtInAnyEncl);
    3833              :     }
    3834              : 
    3835          110 :     dl->maxEnclSubSurfaces = max(maxval(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::NumSubSurfaces),
    3836          110 :                                  maxval(dl->enclDaylight, &EnclDaylightCalc::NumOfDayltgExtWins));
    3837              : 
    3838          160 :     for (int SurfNum : s_surf->AllHTWindowSurfaceList) {
    3839           50 :         auto const &surf = s_surf->Surface(SurfNum);
    3840           50 :         int const surfEnclNum = surf.SolarEnclIndex;
    3841           50 :         int const numEnclRefPoints = state.dataViewFactor->EnclSolInfo(surfEnclNum).TotalEnclosureDaylRefPoints;
    3842           50 :         auto &surfWin = s_surf->SurfaceWindow(SurfNum);
    3843           50 :         if (numEnclRefPoints > 0) {
    3844            9 :             if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
    3845            9 :                 surfWin.refPts.allocate(numEnclRefPoints);
    3846           23 :                 for (auto &refPt : surfWin.refPts) {
    3847           14 :                     new (&refPt) SurfaceWindowRefPt();
    3848              :                 }
    3849              : 
    3850            9 :                 s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
    3851              :             }
    3852              :         } else {
    3853           41 :             int SurfNumAdj = surf.ExtBoundCond;
    3854           41 :             if (SurfNumAdj > 0) {
    3855            0 :                 int const adjSurfEnclNum = s_surf->Surface(SurfNumAdj).SolarEnclIndex;
    3856            0 :                 int const numAdjEnclRefPoints = state.dataViewFactor->EnclSolInfo(adjSurfEnclNum).TotalEnclosureDaylRefPoints;
    3857            0 :                 if (numAdjEnclRefPoints > 0) {
    3858            0 :                     if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
    3859            0 :                         surfWin.refPts.allocate(numAdjEnclRefPoints);
    3860            0 :                         for (auto &refPt : surfWin.refPts) {
    3861            0 :                             new (&refPt) SurfaceWindowRefPt();
    3862              :                         }
    3863            0 :                         s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
    3864              :                     }
    3865              :                 }
    3866              :             }
    3867              :         }
    3868              : 
    3869           50 :         if (surf.ExtBoundCond != ExternalEnvironment) {
    3870            0 :             continue;
    3871              :         }
    3872              : 
    3873           50 :         if (!surf.HasShadeControl) {
    3874           41 :             continue;
    3875              :         }
    3876              : 
    3877            9 :         auto &thisSurfEnclosure(state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex));
    3878            9 :         if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).GlareControlIsActive) {
    3879              :             // Error if GlareControlIsActive but window is not in a Daylighting:Detailed zone
    3880            0 :             if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
    3881            0 :                 ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3882            0 :                 ShowContinueError(state, "GlareControlIsActive = Yes but it is not in a Daylighting zone or enclosure.");
    3883            0 :                 ShowContinueError(state, format("Zone or enclosure indicated={}", state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex).Name));
    3884            0 :                 ErrorsFound = true;
    3885              :             }
    3886              :             // Error if GlareControlIsActive and window is in a Daylighting:Detailed zone/enclosure with
    3887              :             // an interior window adjacent to another Daylighting:Detailed zone/enclosure
    3888            0 :             if (thisSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3889            0 :                 for (int const intWin : thisSurfEnclosure.SurfacePtr) {
    3890            0 :                     int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
    3891            0 :                     if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
    3892            0 :                         auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
    3893            0 :                         if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3894            0 :                             ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3895            0 :                             ShowContinueError(state, "GlareControlIsActive = Yes and is in a Daylighting zone or enclosure");
    3896            0 :                             ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
    3897            0 :                             ShowContinueError(state, format("Adjacent Zone or Enclosure indicated={}", adjSurfEnclosure.Name));
    3898            0 :                             ErrorsFound = true;
    3899              :                         }
    3900              :                     }
    3901              :                 }
    3902              :             }
    3903              :         }
    3904              : 
    3905            9 :         if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
    3906            9 :             continue;
    3907              :         }
    3908              : 
    3909              :         // Error if window has shadingControlType = MeetDaylightingIlluminanceSetpoint &
    3910              :         // but is not in a Daylighting:Detailed zone
    3911            0 :         if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
    3912            0 :             ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3913            0 :             ShowContinueError(state, "MeetDaylightingIlluminanceSetpoint but it is not in a Daylighting zone or enclosure.");
    3914            0 :             ShowContinueError(state, format("Zone or enclosure indicated={}", thisSurfEnclosure.Name));
    3915            0 :             ErrorsFound = true;
    3916            0 :             continue;
    3917              :         }
    3918              : 
    3919              :         // Error if window has shadingControlType = MeetDaylightIlluminanceSetpoint and is in a &
    3920              :         // Daylighting:Detailed zone with an interior window adjacent to another Daylighting:Detailed zone
    3921            0 :         for (int const intWin : thisSurfEnclosure.SurfacePtr) {
    3922            0 :             int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
    3923            0 :             if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
    3924            0 :                 auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
    3925            0 :                 if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
    3926            0 :                     ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
    3927            0 :                     ShowContinueError(state, "MeetDaylightIlluminanceSetpoint and is in a Daylighting zone or enclosure");
    3928            0 :                     ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
    3929            0 :                     ShowContinueError(state, format("Adjacent Zone or enclosure indicated={}", adjSurfEnclosure.Name));
    3930            0 :                     ErrorsFound = true;
    3931              :                 }
    3932              :             }
    3933              :         }
    3934          110 :     } // for (SurfNum)
    3935              : 
    3936          110 :     if (!state.dataHeatBal->AnyAirBoundary) {
    3937          928 :         for (int SurfLoop = 1; SurfLoop <= s_surf->TotSurfaces; ++SurfLoop) {
    3938          818 :             auto const &surf = s_surf->Surface(SurfLoop);
    3939          818 :             if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) {
    3940          770 :                 continue;
    3941              :             }
    3942              : 
    3943           48 :             int const enclOfSurf = surf.SolarEnclIndex;
    3944           48 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclOfSurf);
    3945           48 :             if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow || !dl->enclDaylight(enclOfSurf).hasSplitFluxDaylighting) {
    3946           39 :                 continue;
    3947              :             }
    3948              : 
    3949            9 :             auto &surfWin = s_surf->SurfaceWindow(SurfLoop);
    3950           23 :             for (int refPtNum = 1; refPtNum <= enclSol.TotalEnclosureDaylRefPoints; ++refPtNum) {
    3951           14 :                 auto &refPt = surfWin.refPts(refPtNum);
    3952           42 :                 SetupOutputVariable(state,
    3953           28 :                                     format("Daylighting Window Reference Point {} Illuminance", refPtNum),
    3954              :                                     Constant::Units::lux,
    3955           14 :                                     refPt.illumFromWinRep,
    3956              :                                     OutputProcessor::TimeStepType::Zone,
    3957              :                                     OutputProcessor::StoreType::Average,
    3958           14 :                                     surf.Name);
    3959           42 :                 SetupOutputVariable(state,
    3960           28 :                                     format("Daylighting Window Reference Point {} View Luminance", refPtNum),
    3961              :                                     Constant::Units::cd_m2,
    3962           14 :                                     refPt.lumWinRep,
    3963              :                                     OutputProcessor::TimeStepType::Zone,
    3964              :                                     OutputProcessor::StoreType::Average,
    3965           14 :                                     surf.Name);
    3966              :             }
    3967              :         }
    3968              :     } else {
    3969            0 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    3970            0 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    3971            0 :             for (int const enclSurfNum : enclSol.SurfacePtr) {
    3972            0 :                 auto const &surf = s_surf->Surface(enclSurfNum);
    3973            0 :                 auto &surfWindow = s_surf->SurfaceWindow(enclSurfNum);
    3974              : 
    3975            0 :                 if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) {
    3976            0 :                     continue;
    3977              :                 }
    3978              : 
    3979            0 :                 if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow) {
    3980            0 :                     continue;
    3981              :                 }
    3982              : 
    3983            0 :                 auto const &enclDayltg = dl->enclDaylight(enclNum);
    3984            0 :                 if (!enclDayltg.hasSplitFluxDaylighting) {
    3985            0 :                     continue;
    3986              :                 }
    3987              : 
    3988            0 :                 int refPtCount = 0;
    3989            0 :                 for (int controlNum : enclDayltg.daylightControlIndexes) {
    3990            0 :                     auto const &control = dl->daylightControl(controlNum);
    3991            0 :                     for (int refPtNum = 1; refPtNum <= control.TotalDaylRefPoints; ++refPtNum) {
    3992            0 :                         ++refPtCount; // Count reference points across each daylighting control in the same enclosure
    3993            0 :                         auto &refPt = surfWindow.refPts(refPtCount);
    3994            0 :                         std::string varKey = format("{} to {}", surf.Name, state.dataDayltg->DaylRefPt(control.refPts(refPtNum).num).Name);
    3995            0 :                         SetupOutputVariable(state,
    3996              :                                             "Daylighting Window Reference Point Illuminance",
    3997              :                                             Constant::Units::lux,
    3998            0 :                                             refPt.illumFromWinRep,
    3999              :                                             OutputProcessor::TimeStepType::Zone,
    4000              :                                             OutputProcessor::StoreType::Average,
    4001              :                                             varKey);
    4002            0 :                         SetupOutputVariable(state,
    4003              :                                             "Daylighting Window Reference Point View Luminance",
    4004              :                                             Constant::Units::cd_m2,
    4005            0 :                                             refPt.lumWinRep,
    4006              :                                             OutputProcessor::TimeStepType::Zone,
    4007              :                                             OutputProcessor::StoreType::Average,
    4008              :                                             varKey);
    4009            0 :                     }
    4010            0 :                 } // for (controlNum)
    4011              :             } // for (enclSurfNum)
    4012              :         } // for (enclNum)
    4013              :     }
    4014              : 
    4015              :     // RJH DElight Modification Begin - Calls to DElight preprocessing subroutines
    4016          110 :     if (doesDayLightingUseDElight(state)) {
    4017            0 :         Real64 dLatitude = state.dataEnvrn->Latitude;
    4018            0 :         DisplayString(state, "Calculating DElight Daylighting Factors");
    4019            0 :         DElightManagerF::DElightInputGenerator(state);
    4020              :         // Init Error Flag to 0 (no Warnings or Errors)
    4021            0 :         DisplayString(state, "ReturnFrom DElightInputGenerator");
    4022            0 :         int iErrorFlag = 0;
    4023            0 :         DisplayString(state, "Calculating DElight DaylightCoefficients");
    4024            0 :         DElightManagerF::GenerateDElightDaylightCoefficients(dLatitude, iErrorFlag);
    4025              :         // Check Error Flag for Warnings or Errors returning from DElight
    4026              :         // RJH 2008-03-07: open file for READWRITE and DELETE file after processing
    4027            0 :         DisplayString(state, "ReturnFrom DElight DaylightCoefficients Calc");
    4028            0 :         if (iErrorFlag != 0) {
    4029              :             // Open DElight Daylight Factors Error File for reading
    4030            0 :             auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
    4031              : 
    4032              :             // Sequentially read lines in DElight Daylight Factors Error File
    4033              :             // and process them using standard EPlus warning/error handling calls
    4034              :             // Process all error/warning messages first
    4035              :             // Then, if any error has occurred, ShowFatalError to terminate processing
    4036            0 :             bool bEndofErrFile = !iDElightErrorFile.good();
    4037            0 :             std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
    4038            0 :             while (!bEndofErrFile) {
    4039            0 :                 auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
    4040            0 :                 if (cErrorLine.eof) {
    4041            0 :                     bEndofErrFile = true;
    4042            0 :                     continue;
    4043              :                 }
    4044              :                 // Is the current line a Warning message?
    4045            0 :                 if (has_prefix(cErrorLine.data, "WARNING: ")) {
    4046            0 :                     cErrorMsg = cErrorLine.data.substr(9);
    4047            0 :                     ShowWarningError(state, cErrorMsg);
    4048              :                 }
    4049              :                 // Is the current line an Error message?
    4050            0 :                 if (has_prefix(cErrorLine.data, "ERROR: ")) {
    4051            0 :                     cErrorMsg = cErrorLine.data.substr(7);
    4052            0 :                     ShowSevereError(state, cErrorMsg);
    4053            0 :                     iErrorFlag = 1;
    4054              :                 }
    4055            0 :             }
    4056              : 
    4057              :             // Close and Delete DElight Error File
    4058            0 :             if (iDElightErrorFile.is_open()) {
    4059            0 :                 iDElightErrorFile.close();
    4060            0 :                 FileSystem::removeFile(iDElightErrorFile.filePath);
    4061              :             }
    4062              : 
    4063              :             // If any DElight Error occurred then ShowFatalError to terminate
    4064            0 :             if (iErrorFlag > 0) {
    4065            0 :                 ErrorsFound = true;
    4066              :             }
    4067            0 :         } else {
    4068            0 :             if (FileSystem::fileExists(state.files.outputDelightDfdmpFilePath.filePath)) {
    4069            0 :                 FileSystem::removeFile(state.files.outputDelightDfdmpFilePath.filePath);
    4070              :             }
    4071              :         }
    4072              :     }
    4073              :     // RJH DElight Modification End - Calls to DElight preprocessing subroutines
    4074              : 
    4075              :     // TH 6/3/2010, added to report daylight factors
    4076          110 :     s_ipsc->cCurrentModuleObject = "Output:DaylightFactors";
    4077          110 :     int NumReports = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4078          110 :     if (NumReports > 0) {
    4079              :         int NumNames;
    4080              :         int NumNumbers;
    4081              :         int IOStat;
    4082            2 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    4083            1 :                                                                  s_ipsc->cCurrentModuleObject,
    4084              :                                                                  1,
    4085            1 :                                                                  s_ipsc->cAlphaArgs,
    4086              :                                                                  NumNames,
    4087            1 :                                                                  s_ipsc->rNumericArgs,
    4088              :                                                                  NumNumbers,
    4089              :                                                                  IOStat,
    4090            1 :                                                                  s_ipsc->lNumericFieldBlanks,
    4091            1 :                                                                  s_ipsc->lAlphaFieldBlanks,
    4092            1 :                                                                  s_ipsc->cAlphaFieldNames,
    4093            1 :                                                                  s_ipsc->cNumericFieldNames);
    4094            1 :         if (has_prefix(s_ipsc->cAlphaArgs(1), "SIZINGDAYS")) {
    4095            1 :             dl->DFSReportSizingDays = true;
    4096            0 :         } else if (has_prefix(s_ipsc->cAlphaArgs(1), "ALLSHADOWCALCULATIONDAYS")) {
    4097            0 :             dl->DFSReportAllShadowCalculationDays = true;
    4098              :         }
    4099              :     }
    4100              : 
    4101          110 :     if (ErrorsFound) {
    4102            0 :         ShowFatalError(state, "Program terminated for above reasons");
    4103              :     }
    4104              : } // FigureMapPointDayltgFactorsToAddIllums()
    4105              : 
    4106            8 : void GetInputIlluminanceMap(EnergyPlusData &state, bool &ErrorsFound)
    4107              : {
    4108              :     // Perform the GetInput function for the Output:IlluminanceMap
    4109              :     // Glazer - June 2016 (moved from GetDaylightingControls)
    4110            8 :     auto &dl = state.dataDayltg;
    4111            8 :     auto const &s_surf = state.dataSurface;
    4112              : 
    4113            8 :     Array1D_bool ZoneMsgDone;
    4114              : 
    4115            8 :     Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    4116            8 :     Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    4117              :     // these are only for Building Rotation for Appendix G when using world coordinate system
    4118            8 :     Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    4119            8 :     Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    4120              : 
    4121            8 :     bool doTransform = false;
    4122            8 :     Real64 OldAspectRatio = 1.0;
    4123            8 :     Real64 NewAspectRatio = 1.0;
    4124              : 
    4125            8 :     CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
    4126              : 
    4127            8 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4128            8 :     auto const &s_ipsc = state.dataIPShortCut;
    4129            8 :     s_ipsc->cCurrentModuleObject = "Output:IlluminanceMap";
    4130            8 :     int TotIllumMaps = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4131              : 
    4132            8 :     dl->illumMaps.allocate(TotIllumMaps);
    4133              : 
    4134            8 :     if (TotIllumMaps > 0) {
    4135              :         int IOStat;
    4136              :         int NumAlpha;
    4137              :         int NumNumber;
    4138            2 :         auto &ip = state.dataInputProcessing->inputProcessor;
    4139            4 :         for (int MapNum = 1; MapNum <= TotIllumMaps; ++MapNum) {
    4140            4 :             ip->getObjectItem(state,
    4141            2 :                               s_ipsc->cCurrentModuleObject,
    4142              :                               MapNum,
    4143            2 :                               s_ipsc->cAlphaArgs,
    4144              :                               NumAlpha,
    4145            2 :                               s_ipsc->rNumericArgs,
    4146              :                               NumNumber,
    4147              :                               IOStat,
    4148            2 :                               s_ipsc->lNumericFieldBlanks,
    4149            2 :                               s_ipsc->lAlphaFieldBlanks,
    4150            2 :                               s_ipsc->cAlphaFieldNames,
    4151            2 :                               s_ipsc->cNumericFieldNames);
    4152              : 
    4153            2 :             auto &illumMap = dl->illumMaps(MapNum);
    4154            2 :             illumMap.Name = s_ipsc->cAlphaArgs(1);
    4155            2 :             int const zoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
    4156            2 :             if (zoneNum > 0) {
    4157            2 :                 illumMap.zoneIndex = zoneNum;
    4158              :                 // set enclosure index for first space in zone
    4159            2 :                 int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
    4160            2 :                 illumMap.enclIndex = enclNum;
    4161              :                 // check that all spaces in the zone are in the same enclosure
    4162            2 :                 for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
    4163            0 :                     int spaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
    4164            0 :                     if (enclNum != state.dataHeatBal->space(spaceNum).solarEnclosureNum) {
    4165            0 :                         ShowSevereError(state,
    4166            0 :                                         format("{}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting illuminance maps.",
    4167            0 :                                                s_ipsc->cCurrentModuleObject,
    4168            0 :                                                s_ipsc->cAlphaArgs(1)));
    4169            0 :                         ShowContinueError(
    4170            0 :                             state, format("Zone=\"{}\" spans multiple enclosures. Use a Space Name instead.", state.dataHeatBal->Zone(zoneNum).Name));
    4171            0 :                         ErrorsFound = true;
    4172            0 :                         break;
    4173              :                     }
    4174              :                 }
    4175              :             } else {
    4176            0 :                 int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
    4177            0 :                 if (spaceNum == 0) {
    4178            0 :                     ShowSevereError(state,
    4179            0 :                                     format("{}=\"{}\", invalid {}=\"{}\".",
    4180            0 :                                            s_ipsc->cCurrentModuleObject,
    4181            0 :                                            s_ipsc->cAlphaArgs(1),
    4182            0 :                                            s_ipsc->cAlphaFieldNames(2),
    4183            0 :                                            s_ipsc->cAlphaArgs(2)));
    4184            0 :                     ErrorsFound = true;
    4185              :                 } else {
    4186            0 :                     illumMap.spaceIndex = spaceNum;
    4187            0 :                     illumMap.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
    4188            0 :                     illumMap.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    4189            0 :                     assert(illumMap.enclIndex > 0);
    4190              :                 }
    4191              :             }
    4192              : 
    4193            2 :             illumMap.Z = s_ipsc->rNumericArgs(1);
    4194            2 :             illumMap.Xmin = s_ipsc->rNumericArgs(2);
    4195            2 :             illumMap.Xmax = s_ipsc->rNumericArgs(3);
    4196            2 :             if (s_ipsc->rNumericArgs(2) > s_ipsc->rNumericArgs(3)) {
    4197            0 :                 ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4198            0 :                 ShowContinueError(state,
    4199            0 :                                   format("...{} {:.2R} must be <= {} {:.2R}.",
    4200            0 :                                          s_ipsc->cNumericFieldNames(2),
    4201            0 :                                          s_ipsc->rNumericArgs(2),
    4202            0 :                                          s_ipsc->cNumericFieldNames(3),
    4203            0 :                                          s_ipsc->rNumericArgs(3)));
    4204            0 :                 ErrorsFound = true;
    4205              :             }
    4206            2 :             illumMap.Xnum = s_ipsc->rNumericArgs(4);
    4207            2 :             illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
    4208              : 
    4209            2 :             illumMap.Ymin = s_ipsc->rNumericArgs(5);
    4210            2 :             illumMap.Ymax = s_ipsc->rNumericArgs(6);
    4211            2 :             if (s_ipsc->rNumericArgs(5) > s_ipsc->rNumericArgs(6)) {
    4212            0 :                 ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4213            0 :                 ShowContinueError(state,
    4214            0 :                                   format("...{} {:.2R} must be <= {} {:.2R}.",
    4215            0 :                                          s_ipsc->cNumericFieldNames(5),
    4216            0 :                                          s_ipsc->rNumericArgs(5),
    4217            0 :                                          s_ipsc->cNumericFieldNames(6),
    4218            0 :                                          s_ipsc->rNumericArgs(6)));
    4219            0 :                 ErrorsFound = true;
    4220              :             }
    4221            2 :             illumMap.Ynum = s_ipsc->rNumericArgs(7);
    4222            2 :             illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
    4223              : 
    4224            2 :             if (illumMap.Xnum * illumMap.Ynum > MaxMapRefPoints) {
    4225            0 :                 ShowSevereError(state, format("{}=\"{}\", too many map points specified.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    4226            0 :                 ShowContinueError(state,
    4227            0 :                                   format("...{}[{}] * {}[{}].= [{}] must be <= [{}].",
    4228            0 :                                          s_ipsc->cNumericFieldNames(4),
    4229            0 :                                          illumMap.Xnum,
    4230            0 :                                          s_ipsc->cNumericFieldNames(7),
    4231            0 :                                          illumMap.Ynum,
    4232            0 :                                          illumMap.Xnum * illumMap.Ynum,
    4233              :                                          MaxMapRefPoints));
    4234            0 :                 ErrorsFound = true;
    4235              :             }
    4236              :         } // MapNum
    4237            2 :         s_ipsc->cCurrentModuleObject = "OutputControl:IlluminanceMap:Style";
    4238            2 :         int MapStyleIn = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4239              : 
    4240            2 :         if (MapStyleIn == 0) {
    4241            1 :             s_ipsc->cAlphaArgs(1) = "COMMA";
    4242            1 :             dl->MapColSep = DataStringGlobals::CharComma; // comma
    4243            1 :         } else if (MapStyleIn == 1) {
    4244            2 :             ip->getObjectItem(state,
    4245            1 :                               s_ipsc->cCurrentModuleObject,
    4246              :                               1,
    4247            1 :                               s_ipsc->cAlphaArgs,
    4248              :                               NumAlpha,
    4249            1 :                               s_ipsc->rNumericArgs,
    4250              :                               NumNumber,
    4251              :                               IOStat,
    4252            1 :                               s_ipsc->lNumericFieldBlanks,
    4253            1 :                               s_ipsc->lAlphaFieldBlanks,
    4254            1 :                               s_ipsc->cAlphaFieldNames,
    4255            1 :                               s_ipsc->cNumericFieldNames);
    4256            1 :             if (s_ipsc->cAlphaArgs(1) == "COMMA") {
    4257            1 :                 dl->MapColSep = DataStringGlobals::CharComma; // comma
    4258            0 :             } else if (s_ipsc->cAlphaArgs(1) == "TAB") {
    4259            0 :                 dl->MapColSep = DataStringGlobals::CharTab; // tab
    4260            0 :             } else if (s_ipsc->cAlphaArgs(1) == "FIXED" || s_ipsc->cAlphaArgs(1) == "SPACE") {
    4261            0 :                 dl->MapColSep = DataStringGlobals::CharSpace; // space
    4262              :             } else {
    4263            0 :                 dl->MapColSep = DataStringGlobals::CharComma; // comma
    4264            0 :                 ShowWarningError(state,
    4265            0 :                                  format("{}: invalid {}=\"{}\", Commas will be used to separate fields.",
    4266            0 :                                         s_ipsc->cCurrentModuleObject,
    4267            0 :                                         s_ipsc->cAlphaFieldNames(1),
    4268            0 :                                         s_ipsc->cAlphaArgs(1)));
    4269            0 :                 s_ipsc->cAlphaArgs(1) = "COMMA";
    4270              :             }
    4271              :         }
    4272            2 :         print(state.files.eio, "! <Daylighting:Illuminance Maps>,#Maps,Style\n");
    4273            2 :         ConvertCaseToLower(s_ipsc->cAlphaArgs(1), s_ipsc->cAlphaArgs(2));
    4274            2 :         s_ipsc->cAlphaArgs(1).erase(1);
    4275            2 :         s_ipsc->cAlphaArgs(1) += s_ipsc->cAlphaArgs(2).substr(1);
    4276            2 :         print(state.files.eio, "Daylighting:Illuminance Maps,{},{}\n", TotIllumMaps, s_ipsc->cAlphaArgs(1));
    4277              :     }
    4278              : 
    4279              :     // Check for illuminance maps associated with this zone
    4280           10 :     for (auto &illumMap : dl->illumMaps) {
    4281              : 
    4282            2 :         if (illumMap.zoneIndex == 0) {
    4283            0 :             continue;
    4284              :         }
    4285              : 
    4286            2 :         auto &zone = state.dataHeatBal->Zone(illumMap.zoneIndex);
    4287              :         // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
    4288            2 :         Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
    4289            2 :         Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
    4290              : 
    4291            2 :         if (illumMap.Xnum * illumMap.Ynum == 0) {
    4292            0 :             continue;
    4293              :         }
    4294              : 
    4295              :         // Add additional daylighting reference points for map
    4296            2 :         illumMap.TotalMapRefPoints = illumMap.Xnum * illumMap.Ynum;
    4297            2 :         illumMap.refPts.allocate(illumMap.TotalMapRefPoints);
    4298          212 :         for (auto &refPt : illumMap.refPts) {
    4299          210 :             new (&refPt) DaylMapPt();
    4300              :         }
    4301              : 
    4302            2 :         if (illumMap.TotalMapRefPoints > MaxMapRefPoints) {
    4303            0 :             ShowSevereError(state, "GetDaylighting Parameters: Total Map Reference points entered is greater than maximum allowed.");
    4304            0 :             ShowContinueError(state, format("Occurs in Zone={}", zone.Name));
    4305            0 :             ShowContinueError(state,
    4306            0 :                               format("Maximum reference points allowed={}, entered amount ( when error first occurred )={}",
    4307              :                                      MaxMapRefPoints,
    4308            0 :                                      illumMap.TotalMapRefPoints));
    4309            0 :             ErrorsFound = true;
    4310            0 :             break;
    4311              :         }
    4312              : 
    4313              :         // Calc cos and sin of Zone Relative North values for later use in transforming Map Point coordinates
    4314              :         // CosZoneRelNorth = std::cos( -zone.RelNorth * DegToRadians ); //Tuned These should not be changing
    4315              :         // SinZoneRelNorth = std::sin( -zone.RelNorth * DegToRadians );
    4316            2 :         illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
    4317            2 :         illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
    4318              : 
    4319              :         // Map points and increments are stored in AbsCoord and then that is operated on if relative coords entered.
    4320           23 :         for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    4321          231 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    4322          210 :                 int iRefPt = (Y - 1) * illumMap.Xnum + X;
    4323          210 :                 auto &refPt = illumMap.refPts(iRefPt);
    4324          210 :                 refPt.absCoords = {illumMap.Xmin + (X - 1) * illumMap.Xinc, illumMap.Ymin + (Y - 1) * illumMap.Yinc, illumMap.Z};
    4325              :             }
    4326              :         }
    4327              : 
    4328           23 :         for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    4329          231 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    4330          210 :                 int iRefPt = (Y - 1) * illumMap.Xnum + X;
    4331          210 :                 auto &refPt = illumMap.refPts(iRefPt);
    4332              : 
    4333          210 :                 if (!s_surf->DaylRefWorldCoordSystem) {
    4334          210 :                     Real64 Xb = refPt.absCoords.x * CosZoneRelNorth - refPt.absCoords.y * SinZoneRelNorth + zone.OriginX;
    4335          210 :                     Real64 Yb = refPt.absCoords.x * SinZoneRelNorth + refPt.absCoords.y * CosZoneRelNorth + zone.OriginY;
    4336          210 :                     refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
    4337          210 :                     refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
    4338          210 :                     refPt.absCoords.z += zone.OriginZ;
    4339          210 :                     if (doTransform) {
    4340            0 :                         Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
    4341            0 :                         Real64 Yo = refPt.absCoords.y;
    4342              :                         // next derotate the building
    4343            0 :                         Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
    4344            0 :                         Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
    4345              :                         // translate
    4346            0 :                         Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
    4347            0 :                         Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
    4348              :                         // rerotate
    4349            0 :                         refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
    4350              : 
    4351            0 :                         refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
    4352              :                     }
    4353              :                 } else {
    4354            0 :                     Real64 Xb = refPt.absCoords.x;
    4355            0 :                     Real64 Yb = refPt.absCoords.y;
    4356            0 :                     refPt.absCoords.x = Xb * CosBldgRotAppGonly - Yb * SinBldgRotAppGonly;
    4357            0 :                     refPt.absCoords.y = Xb * SinBldgRotAppGonly + Yb * CosBldgRotAppGonly;
    4358              :                 }
    4359          210 :                 if (iRefPt == 1) {
    4360            2 :                     illumMap.Xmin = refPt.absCoords.x;
    4361            2 :                     illumMap.Ymin = refPt.absCoords.y;
    4362            2 :                     illumMap.Xmax = refPt.absCoords.x;
    4363            2 :                     illumMap.Ymax = refPt.absCoords.y;
    4364            2 :                     illumMap.Z = refPt.absCoords.z;
    4365              :                 }
    4366          210 :                 illumMap.Xmin = min(illumMap.Xmin, refPt.absCoords.x);
    4367          210 :                 illumMap.Ymin = min(illumMap.Ymin, refPt.absCoords.y);
    4368          210 :                 illumMap.Xmax = max(illumMap.Xmax, refPt.absCoords.x);
    4369          210 :                 illumMap.Ymax = max(illumMap.Ymax, refPt.absCoords.y);
    4370          210 :                 if ((refPt.absCoords.x < zone.MinimumX && (zone.MinimumX - refPt.absCoords.x) > 0.001) ||
    4371          210 :                     (refPt.absCoords.x > zone.MaximumX && (refPt.absCoords.x - zone.MaximumX) > 0.001) ||
    4372          100 :                     (refPt.absCoords.y < zone.MinimumY && (zone.MinimumY - refPt.absCoords.y) > 0.001) ||
    4373          100 :                     (refPt.absCoords.y > zone.MaximumY && (refPt.absCoords.y - zone.MaximumY) > 0.001) ||
    4374          100 :                     (refPt.absCoords.z < zone.MinimumZ && (zone.MinimumZ - refPt.absCoords.z) > 0.001) ||
    4375          100 :                     (refPt.absCoords.z > zone.MaximumZ && (refPt.absCoords.z - zone.MaximumZ) > 0.001)) {
    4376          110 :                     refPt.inBounds = false;
    4377              :                 }
    4378              : 
    4379              :                 // Test extremes of Map Points against Zone Min/Max
    4380          210 :                 if (iRefPt != 1 && iRefPt != illumMap.TotalMapRefPoints) {
    4381          208 :                     continue;
    4382              :                 }
    4383              : 
    4384            4 :                 if (refPt.inBounds) {
    4385            2 :                     continue;
    4386              :                 }
    4387              : 
    4388            2 :                 if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
    4389            4 :                     ShowWarningError(
    4390              :                         state,
    4391            4 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], X Value outside Zone Min/Max X, Zone={}", iRefPt, zone.Name));
    4392            4 :                     ShowContinueError(state,
    4393            4 :                                       format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
    4394            2 :                                              refPt.absCoords.x,
    4395            2 :                                              zone.MinimumX,
    4396            2 :                                              zone.MaximumX));
    4397            4 :                     ShowContinueError(
    4398              :                         state,
    4399            4 :                         format("...X Reference Distance Outside MinimumX= {:.4R} m.",
    4400            4 :                                (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
    4401              :                 }
    4402            2 :                 if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
    4403            4 :                     ShowWarningError(
    4404              :                         state,
    4405            4 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], Y Value outside Zone Min/Max Y, Zone={}", iRefPt, zone.Name));
    4406            4 :                     ShowContinueError(state,
    4407            4 :                                       format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
    4408            2 :                                              refPt.absCoords.y,
    4409            2 :                                              zone.MinimumY,
    4410            2 :                                              zone.MaximumY));
    4411            4 :                     ShowContinueError(
    4412              :                         state,
    4413            4 :                         format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
    4414            4 :                                (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
    4415              :                 }
    4416            2 :                 if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
    4417            0 :                     ShowWarningError(
    4418              :                         state,
    4419            0 :                         format("GetInputIlluminanceMap: Reference Map point #[{}], Z Value outside Zone Min/Max Z, Zone={}", iRefPt, zone.Name));
    4420            0 :                     ShowContinueError(state,
    4421            0 :                                       format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
    4422            0 :                                              refPt.absCoords.z,
    4423            0 :                                              zone.MinimumZ,
    4424            0 :                                              zone.MaximumZ));
    4425            0 :                     ShowContinueError(
    4426              :                         state,
    4427            0 :                         format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
    4428            0 :                                (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
    4429              :                 }
    4430              :             } // for (X)
    4431              :         } // for (Y)
    4432              :     } // for (MapNum)
    4433              : 
    4434            8 :     ZoneMsgDone.dimension(state.dataGlobal->NumOfZones, false);
    4435           10 :     for (auto const &illumMap : dl->illumMaps) {
    4436            2 :         if (illumMap.zoneIndex == 0) {
    4437            0 :             continue;
    4438              :         }
    4439            2 :         int enclNum = illumMap.enclIndex;
    4440            2 :         if (!dl->enclDaylight(enclNum).hasSplitFluxDaylighting && !ZoneMsgDone(illumMap.zoneIndex)) {
    4441            2 :             ShowSevereError(state,
    4442            2 :                             format("Zone Name in Output:IlluminanceMap is not used for Daylighting:Controls={}",
    4443            1 :                                    state.dataHeatBal->Zone(illumMap.zoneIndex).Name));
    4444            1 :             ErrorsFound = true;
    4445              :         }
    4446              :     }
    4447            8 :     ZoneMsgDone.deallocate();
    4448            8 :     if (ErrorsFound) {
    4449            1 :         return;
    4450              :     }
    4451              : 
    4452            7 :     if (TotIllumMaps > 0) {
    4453            1 :         print(state.files.eio,
    4454              :               "! <Daylighting:Illuminance Maps:Detail>,Name,Zone,XMin {{m}},XMax {{m}},Xinc {{m}},#X Points,YMin "
    4455              :               "{{m}},YMax {{m}},Yinc {{m}},#Y Points,Z {{m}}\n");
    4456              :     }
    4457            8 :     for (auto const &illumMap : dl->illumMaps) {
    4458            1 :         print(state.files.eio,
    4459              :               "Daylighting:Illuminance Maps:Detail,{},{},{:.2R},{:.2R},{:.2R},{},{:.2R},{:.2R},{:.2R},{},{:.2R}\n",
    4460            1 :               illumMap.Name,
    4461            1 :               state.dataHeatBal->Zone(illumMap.zoneIndex).Name,
    4462            1 :               illumMap.Xmin,
    4463            1 :               illumMap.Xmax,
    4464            1 :               illumMap.Xinc,
    4465            1 :               illumMap.Xnum,
    4466            1 :               illumMap.Ymin,
    4467            1 :               illumMap.Ymax,
    4468            1 :               illumMap.Yinc,
    4469            1 :               illumMap.Ynum,
    4470            1 :               illumMap.Z);
    4471              :     }
    4472              : 
    4473            8 : } // GetInputIlluminanceMap()
    4474              : 
    4475           11 : void GetDaylightingControls(EnergyPlusData &state, bool &ErrorsFound)
    4476              : {
    4477              :     //       AUTHOR         Fred Winkelmann
    4478              :     //       DATE WRITTEN   March 2002
    4479              :     //       MODIFIED       Glazer - July 2016 - Move geometry transformation portion, rearrange input, allow more than three reference points
    4480              :     // Obtain the user input data for Daylighting:Controls object in the input file.
    4481              : 
    4482              :     static constexpr std::string_view routineName = "GetDaylightingControls";
    4483              : 
    4484           11 :     auto &dl = state.dataDayltg;
    4485              : 
    4486              :     int IOStat;
    4487              :     int NumAlpha;
    4488              :     int NumNumber;
    4489              : 
    4490              :     // Smallest deviation from unity for the sum of all fractions
    4491              :     // Accept approx 4 to 8 ULP error (technically abs(1.0 + sumFracs) should be close to 2)
    4492              :     //   constexpr Real64 FractionTolerance(4 * std::numeric_limits<Real64>::epsilon());
    4493              :     // Instead, we use a 0.001 = 0.1% tolerance
    4494           11 :     constexpr Real64 FractionTolerance(0.001);
    4495              : 
    4496           11 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4497           11 :     auto const &s_ipsc = state.dataIPShortCut;
    4498           11 :     s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
    4499           11 :     int totDaylightingControls = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4500           11 :     dl->daylightControl.allocate(totDaylightingControls);
    4501           11 :     Array1D<bool> spaceHasDaylightingControl;
    4502           11 :     spaceHasDaylightingControl.dimension(state.dataGlobal->numSpaces, false);
    4503              :     // Reset to zero in case this is called more than once in unit tests
    4504           24 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    4505           13 :         state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints = 0;
    4506              :     }
    4507           24 :     for (int controlNum = 1; controlNum <= totDaylightingControls; ++controlNum) {
    4508           13 :         s_ipsc->cAlphaArgs = "";
    4509           13 :         s_ipsc->rNumericArgs = 0.0;
    4510           26 :         ip->getObjectItem(state,
    4511           13 :                           s_ipsc->cCurrentModuleObject,
    4512              :                           controlNum,
    4513           13 :                           s_ipsc->cAlphaArgs,
    4514              :                           NumAlpha,
    4515           13 :                           s_ipsc->rNumericArgs,
    4516              :                           NumNumber,
    4517              :                           IOStat,
    4518           13 :                           s_ipsc->lNumericFieldBlanks,
    4519           13 :                           s_ipsc->lAlphaFieldBlanks,
    4520           13 :                           s_ipsc->cAlphaFieldNames,
    4521           13 :                           s_ipsc->cNumericFieldNames);
    4522              : 
    4523           13 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    4524              : 
    4525           13 :         auto &daylightControl = dl->daylightControl(controlNum);
    4526           13 :         daylightControl.Name = s_ipsc->cAlphaArgs(1);
    4527              : 
    4528              :         // Is it a zone or space name?
    4529           13 :         int const zoneNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->Zone);
    4530           13 :         if (zoneNum > 0) {
    4531           13 :             daylightControl.zoneIndex = zoneNum;
    4532              :             // set enclosure index for first space in zone
    4533           13 :             int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
    4534           13 :             daylightControl.enclIndex = enclNum;
    4535              :             // check that all spaces in the zone are in the same enclosure
    4536           13 :             for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
    4537            0 :                 int zoneSpaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
    4538            0 :                 if (daylightControl.enclIndex != state.dataHeatBal->space(zoneSpaceNum).solarEnclosureNum) {
    4539            0 :                     ShowSevereError(state,
    4540            0 :                                     format("{}: invalid {}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting.",
    4541            0 :                                            s_ipsc->cCurrentModuleObject,
    4542            0 :                                            s_ipsc->cAlphaFieldNames(2),
    4543            0 :                                            s_ipsc->cAlphaArgs(2)));
    4544            0 :                     ErrorsFound = true;
    4545            0 :                     break;
    4546              :                 }
    4547              :             }
    4548           26 :             for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4549              :                 // Check if this is a duplicate
    4550           13 :                 if (spaceHasDaylightingControl(zoneSpaceNum)) {
    4551            0 :                     ShowWarningError(state,
    4552            0 :                                      format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
    4553            0 :                                             s_ipsc->cCurrentModuleObject,
    4554            0 :                                             daylightControl.Name,
    4555            0 :                                             state.dataHeatBal->space(zoneSpaceNum).Name,
    4556            0 :                                             s_ipsc->cCurrentModuleObject));
    4557            0 :                     ShowContinueError(state, "This control will override the lighting power factor for this space.");
    4558              :                 }
    4559           13 :                 spaceHasDaylightingControl(zoneSpaceNum) = true;
    4560           13 :             }
    4561              :         } else {
    4562            0 :             int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
    4563            0 :             if (spaceNum == 0) {
    4564            0 :                 ShowSevereError(state,
    4565            0 :                                 format("{}: invalid {}=\"{}\".", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
    4566            0 :                 ErrorsFound = true;
    4567            0 :                 continue;
    4568              :             } else {
    4569            0 :                 daylightControl.spaceIndex = spaceNum;
    4570            0 :                 daylightControl.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
    4571            0 :                 daylightControl.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    4572              :                 // Check if this is a duplicate
    4573            0 :                 if (spaceHasDaylightingControl(spaceNum)) {
    4574            0 :                     ShowWarningError(state,
    4575            0 :                                      format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
    4576            0 :                                             s_ipsc->cCurrentModuleObject,
    4577            0 :                                             daylightControl.Name,
    4578            0 :                                             state.dataHeatBal->space(spaceNum).Name,
    4579            0 :                                             s_ipsc->cCurrentModuleObject));
    4580            0 :                     ShowContinueError(state, "This control will override the lighting power factor for this space.");
    4581              :                 }
    4582            0 :                 spaceHasDaylightingControl(spaceNum) = true;
    4583              :             }
    4584              :         }
    4585              : 
    4586           13 :         dl->enclDaylight(daylightControl.enclIndex).daylightControlIndexes.emplace_back(controlNum);
    4587           13 :         daylightControl.ZoneName = state.dataHeatBal->Zone(daylightControl.zoneIndex).Name;
    4588              : 
    4589           13 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    4590            0 :             daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
    4591              :         } else {
    4592           13 :             daylightControl.DaylightMethod =
    4593           13 :                 static_cast<DaylightingMethod>(getEnumValue(DaylightingMethodNamesUC, Util::makeUPPER(s_ipsc->cAlphaArgs(3))));
    4594              : 
    4595           13 :             if (daylightControl.DaylightMethod == DaylightingMethod::Invalid) {
    4596            0 :                 daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
    4597            0 :                 ShowWarningError(state,
    4598            0 :                                  format("Invalid {} = {}, occurs in {}object for {}=\"{}",
    4599            0 :                                         s_ipsc->cAlphaFieldNames(3),
    4600            0 :                                         s_ipsc->cAlphaArgs(3),
    4601            0 :                                         s_ipsc->cCurrentModuleObject,
    4602            0 :                                         s_ipsc->cCurrentModuleObject,
    4603            0 :                                         s_ipsc->cAlphaArgs(1)));
    4604            0 :                 ShowContinueError(state, "SplitFlux assumed, and the simulation continues.");
    4605              :             }
    4606              :         }
    4607           13 :         dl->enclDaylight(daylightControl.enclIndex).hasSplitFluxDaylighting |= (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux);
    4608              : 
    4609           13 :         if (s_ipsc->lAlphaFieldBlanks(4)) { // Field: Availability Schedule Name
    4610           13 :             daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
    4611            0 :         } else if ((daylightControl.availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
    4612            0 :             ShowWarningItemNotFound(state,
    4613              :                                     eoh,
    4614            0 :                                     s_ipsc->cAlphaFieldNames(4),
    4615            0 :                                     s_ipsc->cAlphaArgs(4),
    4616              :                                     "Schedule was not found so controls will always be available, and the simulation continues.");
    4617            0 :             daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
    4618              :         }
    4619              : 
    4620           13 :         daylightControl.LightControlType = static_cast<LtgCtrlType>(getEnumValue(LtgCtrlTypeNamesUC, s_ipsc->cAlphaArgs(5)));
    4621           13 :         if (daylightControl.LightControlType == LtgCtrlType::Invalid) {
    4622            0 :             ShowWarningInvalidKey(
    4623            0 :                 state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), "Continuous assumed, and the simulation continues.");
    4624              :         }
    4625              : 
    4626           13 :         daylightControl.MinPowerFraction = s_ipsc->rNumericArgs(1);  // Field: Minimum Input Power Fraction for Continuous Dimming Control
    4627           13 :         daylightControl.MinLightFraction = s_ipsc->rNumericArgs(2);  // Field: Minimum Light Output Fraction for Continuous Dimming Control
    4628           13 :         daylightControl.LightControlSteps = s_ipsc->rNumericArgs(3); // Field: Number of Stepped Control Steps
    4629           13 :         daylightControl.LightControlProbability =
    4630           13 :             s_ipsc->rNumericArgs(4); // Field: Probability Lighting will be Reset When Needed in Manual Stepped Control
    4631              : 
    4632           13 :         if (!s_ipsc->lAlphaFieldBlanks(6)) { // Field: Glare Calculation Daylighting Reference Point Name
    4633           11 :             daylightControl.glareRefPtNumber = Util::FindItemInList(s_ipsc->cAlphaArgs(6),
    4634           11 :                                                                     dl->DaylRefPt,
    4635              :                                                                     &RefPointData::Name); // Field: Glare Calculation Daylighting Reference Point Name
    4636           11 :             if (daylightControl.glareRefPtNumber == 0) {
    4637            0 :                 ShowSevereError(state,
    4638            0 :                                 format("{}: invalid {}=\"{}\" for object named: {}",
    4639            0 :                                        s_ipsc->cCurrentModuleObject,
    4640            0 :                                        s_ipsc->cAlphaFieldNames(6),
    4641            0 :                                        s_ipsc->cAlphaArgs(6),
    4642            0 :                                        s_ipsc->cAlphaArgs(1)));
    4643            0 :                 ErrorsFound = true;
    4644            0 :                 continue;
    4645              :             }
    4646            2 :         } else if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4647            2 :             ShowWarningError(state, format("No {} provided for object named: {}", s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(1)));
    4648            6 :             ShowContinueError(state, "No glare calculation performed, and the simulation continues.");
    4649              :         }
    4650              : 
    4651              :         // Field: Glare Calculation Azimuth Angle of View Direction Clockwise from Zone y-Axis
    4652           13 :         daylightControl.ViewAzimuthForGlare = !s_ipsc->lNumericFieldBlanks(5) ? s_ipsc->rNumericArgs(5) : 0.0;
    4653              : 
    4654           13 :         daylightControl.MaxGlareallowed = s_ipsc->rNumericArgs(6);           // Field: Maximum Allowable Discomfort Glare Index
    4655           13 :         daylightControl.DElightGriddingResolution = s_ipsc->rNumericArgs(7); // Field: DElight Gridding Resolution
    4656              : 
    4657           13 :         int curTotalDaylRefPts = NumAlpha - 6; // first six alpha fields are not part of extensible group
    4658           13 :         daylightControl.TotalDaylRefPoints = curTotalDaylRefPts;
    4659           13 :         state.dataViewFactor->EnclSolInfo(daylightControl.enclIndex).TotalEnclosureDaylRefPoints += curTotalDaylRefPts;
    4660           13 :         dl->ZoneDaylight(daylightControl.zoneIndex).totRefPts += curTotalDaylRefPts;
    4661           13 :         dl->maxControlRefPoints = max(dl->maxControlRefPoints, curTotalDaylRefPts);
    4662           13 :         if ((NumNumber - 7) / 2 != daylightControl.TotalDaylRefPoints) {
    4663            0 :             ShowSevereError(state,
    4664            0 :                             format("{}The number of extensible numeric fields and alpha fields is inconsistent for: {}",
    4665            0 :                                    s_ipsc->cCurrentModuleObject,
    4666            0 :                                    s_ipsc->cAlphaArgs(1)));
    4667            0 :             ShowContinueError(state,
    4668            0 :                               format("For each field: {} there needs to be the following fields: Fraction Controlled by Reference Point and "
    4669              :                                      "Illuminance Setpoint at Reference Point",
    4670            0 :                                      s_ipsc->cAlphaFieldNames(NumAlpha)));
    4671            0 :             ErrorsFound = true;
    4672              :         }
    4673              : 
    4674           13 :         daylightControl.refPts.allocate(curTotalDaylRefPts);
    4675              : 
    4676           43 :         for (auto &refPt : daylightControl.refPts) {
    4677           30 :             refPt = DaylRefPt();
    4678              :         }
    4679              : 
    4680           13 :         int countRefPts = 0;
    4681           43 :         for (int refPtNum = 1; refPtNum <= curTotalDaylRefPts; ++refPtNum) {
    4682           30 :             auto &refPt = daylightControl.refPts(refPtNum);
    4683           30 :             refPt.num =
    4684           30 :                 Util::FindItemInList(s_ipsc->cAlphaArgs(6 + refPtNum), dl->DaylRefPt, &RefPointData::Name); // Field: Daylighting Reference Point Name
    4685           30 :             if (refPt.num == 0) {
    4686            0 :                 ShowSevereError(state,
    4687            0 :                                 format("{}: invalid {}=\"{}\" for object named: {}",
    4688            0 :                                        s_ipsc->cCurrentModuleObject,
    4689            0 :                                        s_ipsc->cAlphaFieldNames(6 + refPtNum),
    4690            0 :                                        s_ipsc->cAlphaArgs(6 + refPtNum),
    4691            0 :                                        s_ipsc->cAlphaArgs(1)));
    4692            0 :                 ErrorsFound = true;
    4693            0 :                 continue;
    4694              :             } else {
    4695           30 :                 ++countRefPts;
    4696              :             }
    4697           30 :             refPt.fracZoneDaylit = s_ipsc->rNumericArgs(6 + refPtNum * 2); // Field: Fraction Controlled by Reference Point
    4698           30 :             refPt.illumSetPoint = s_ipsc->rNumericArgs(7 + refPtNum * 2);  // Field: Illuminance Setpoint at Reference Point
    4699              : 
    4700           30 :             if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4701           90 :                 SetupOutputVariable(state,
    4702           60 :                                     format("Daylighting Reference Point {} Illuminance", refPtNum),
    4703              :                                     Constant::Units::lux,
    4704           30 :                                     refPt.lums[iLum_Illum],
    4705              :                                     OutputProcessor::TimeStepType::Zone,
    4706              :                                     OutputProcessor::StoreType::Average,
    4707           30 :                                     daylightControl.Name);
    4708           90 :                 SetupOutputVariable(state,
    4709           60 :                                     format("Daylighting Reference Point {} Daylight Illuminance Setpoint Exceeded Time", refPtNum),
    4710              :                                     Constant::Units::hr,
    4711           30 :                                     refPt.timeExceedingDaylightIlluminanceSetPoint,
    4712              :                                     OutputProcessor::TimeStepType::Zone,
    4713              :                                     OutputProcessor::StoreType::Sum,
    4714           30 :                                     daylightControl.Name);
    4715           90 :                 SetupOutputVariable(state,
    4716           60 :                                     format("Daylighting Reference Point {} Glare Index", refPtNum),
    4717              :                                     Constant::Units::None,
    4718           30 :                                     refPt.glareIndex,
    4719              :                                     OutputProcessor::TimeStepType::Zone,
    4720              :                                     OutputProcessor::StoreType::Average,
    4721           30 :                                     daylightControl.Name);
    4722           90 :                 SetupOutputVariable(state,
    4723           60 :                                     format("Daylighting Reference Point {} Glare Index Setpoint Exceeded Time", refPtNum),
    4724              :                                     Constant::Units::hr,
    4725           30 :                                     refPt.timeExceedingGlareIndexSetPoint,
    4726              :                                     OutputProcessor::TimeStepType::Zone,
    4727              :                                     OutputProcessor::StoreType::Sum,
    4728           30 :                                     daylightControl.Name);
    4729              :             } // if (DaylightMethod == SplitFlux)
    4730              :         } // for (RefPtNum)
    4731              : 
    4732              :         // Register Error if 0 DElight RefPts have been input for valid DElight object
    4733           13 :         if (countRefPts < 1) {
    4734            0 :             ShowSevereError(state, format("No Reference Points input for {} zone ={}", s_ipsc->cCurrentModuleObject, daylightControl.ZoneName));
    4735            0 :             ErrorsFound = true;
    4736              :         }
    4737              : 
    4738           13 :         Real64 sumFracs = 0.0;
    4739           43 :         for (auto const &refPt : daylightControl.refPts) {
    4740           30 :             sumFracs += refPt.fracZoneDaylit;
    4741              :         }
    4742              : 
    4743           13 :         daylightControl.sumFracLights = sumFracs;
    4744           13 :         if ((1.0 - sumFracs) > FractionTolerance) {
    4745            2 :             ShowWarningError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is < 1.0.");
    4746            2 :             ShowContinueError(state,
    4747            2 :                               format("..discovered in {}=\"{}\", only {:.3R} of the zone or space is controlled.",
    4748            1 :                                      s_ipsc->cCurrentModuleObject,
    4749            1 :                                      daylightControl.Name,
    4750              :                                      sumFracs));
    4751           12 :         } else if ((sumFracs - 1.0) > FractionTolerance) {
    4752            2 :             ShowSevereError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is > 1.0.");
    4753            2 :             ShowContinueError(state,
    4754            2 :                               format("..discovered in {}=\"{}\", trying to control {:.3R} of the zone or space.",
    4755            1 :                                      s_ipsc->cCurrentModuleObject,
    4756            1 :                                      daylightControl.Name,
    4757              :                                      sumFracs));
    4758            1 :             ErrorsFound = true;
    4759              :         }
    4760              : 
    4761           13 :         if (daylightControl.LightControlType == LtgCtrlType::Stepped && daylightControl.LightControlSteps <= 0) {
    4762            0 :             ShowWarningError(state, "GetDaylightingControls: For Stepped Control, the number of steps must be > 0");
    4763            0 :             ShowContinueError(state,
    4764            0 :                               format("..discovered in \"{}\" for Zone=\"{}\", will use 1", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(2)));
    4765            0 :             daylightControl.LightControlSteps = 1;
    4766              :         }
    4767           26 :         SetupOutputVariable(state,
    4768              :                             "Daylighting Lighting Power Multiplier",
    4769              :                             Constant::Units::None,
    4770           13 :                             daylightControl.PowerReductionFactor,
    4771              :                             OutputProcessor::TimeStepType::Zone,
    4772              :                             OutputProcessor::StoreType::Average,
    4773           13 :                             daylightControl.Name);
    4774              :     } // for (controlNum)
    4775           11 : } // GetDaylightingControls()
    4776              : 
    4777           11 : void GeometryTransformForDaylighting(EnergyPlusData &state)
    4778              : {
    4779              :     //       AUTHOR         Fred Winkelmann
    4780              :     //       DATE WRITTEN   March 2002
    4781              :     //       MODIFIED       Glazer - July 2016 - separated this from GetInput function
    4782              :     // For splitflux daylighting, transform the geometry
    4783           11 :     auto &dl = state.dataDayltg;
    4784           11 :     auto const &s_surf = state.dataSurface;
    4785              : 
    4786              :     // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
    4787           11 :     Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    4788           11 :     Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
    4789              :     // these are only for Building Rotation for Appendix G when using world coordinate system
    4790           11 :     Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    4791           11 :     Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
    4792              : 
    4793           11 :     bool doTransform = false;
    4794           11 :     Real64 OldAspectRatio = 1.0;
    4795           11 :     Real64 NewAspectRatio = 1.0;
    4796              : 
    4797           11 :     CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
    4798           28 :     for (auto &daylCntrl : dl->daylightControl) {
    4799           17 :         auto &zone = state.dataHeatBal->Zone(daylCntrl.zoneIndex);
    4800              : 
    4801              :         // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
    4802           17 :         Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
    4803           17 :         Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
    4804              : 
    4805           17 :         Real64 rLightLevel = InternalHeatGains::GetDesignLightingLevelForZone(state, daylCntrl.zoneIndex);
    4806           17 :         InternalHeatGains::CheckLightsReplaceableMinMaxForZone(state, daylCntrl.zoneIndex);
    4807              : 
    4808           45 :         for (int refPtNum = 1; refPtNum <= daylCntrl.TotalDaylRefPoints; ++refPtNum) {
    4809           28 :             auto &refPt = daylCntrl.refPts(refPtNum);
    4810           28 :             auto &curRefPt = dl->DaylRefPt(refPt.num); // get the active daylighting:referencepoint
    4811           28 :             curRefPt.indexToFracAndIllum = refPtNum;   // back reference to the index to the ZoneDaylight structure arrays related to reference points
    4812           28 :             if (s_surf->DaylRefWorldCoordSystem) {
    4813              :                 // transform only by appendix G rotation
    4814            0 :                 refPt.absCoords.x = curRefPt.coords.x * CosBldgRotAppGonly - curRefPt.coords.y * SinBldgRotAppGonly;
    4815            0 :                 refPt.absCoords.y = curRefPt.coords.x * SinBldgRotAppGonly + curRefPt.coords.y * CosBldgRotAppGonly;
    4816            0 :                 refPt.absCoords.z = curRefPt.coords.z;
    4817              :             } else {
    4818              :                 // Transform reference point coordinates into building coordinate system
    4819           28 :                 Real64 Xb = curRefPt.coords.x * CosZoneRelNorth - curRefPt.coords.y * SinZoneRelNorth + zone.OriginX;
    4820           28 :                 Real64 Yb = curRefPt.coords.x * SinZoneRelNorth + curRefPt.coords.y * CosZoneRelNorth + zone.OriginY;
    4821              :                 // Transform into World Coordinate System
    4822           28 :                 refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
    4823           28 :                 refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
    4824           28 :                 refPt.absCoords.z = curRefPt.coords.z + zone.OriginZ;
    4825           28 :                 if (doTransform) {
    4826            0 :                     Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
    4827            0 :                     Real64 Yo = refPt.absCoords.y;
    4828              :                     // next derotate the building
    4829            0 :                     Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
    4830            0 :                     Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
    4831              :                     // translate
    4832            0 :                     Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
    4833            0 :                     Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
    4834              :                     // rerotate
    4835            0 :                     refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
    4836            0 :                     refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
    4837              :                 }
    4838              :             }
    4839              : 
    4840           28 :             auto &orp = state.dataOutRptPredefined;
    4841           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtZone, curRefPt.Name, daylCntrl.ZoneName);
    4842           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlName, curRefPt.Name, daylCntrl.Name);
    4843           28 :             if (daylCntrl.DaylightMethod == DaylightingMethod::SplitFlux) {
    4844           28 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "SplitFlux");
    4845              :             } else {
    4846            0 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "DElight");
    4847              :             }
    4848              :             // ( 1=continuous, 2=stepped, 3=continuous/off )
    4849           28 :             if (daylCntrl.LightControlType == LtgCtrlType::Continuous) {
    4850           28 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous");
    4851            0 :             } else if (daylCntrl.LightControlType == LtgCtrlType::Stepped) {
    4852            0 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Stepped");
    4853            0 :             } else if (daylCntrl.LightControlType == LtgCtrlType::ContinuousOff) {
    4854            0 :                 OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous/Off");
    4855              :             }
    4856           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtFrac, curRefPt.Name, refPt.fracZoneDaylit);
    4857           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWInst, curRefPt.Name, rLightLevel);
    4858           28 :             OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWCtrl, curRefPt.Name, rLightLevel * refPt.fracZoneDaylit);
    4859              : 
    4860           28 :             if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
    4861            2 :                 refPt.inBounds = false;
    4862            4 :                 ShowWarningError(state,
    4863            4 :                                  format("GeometryTransformForDaylighting: Reference point X Value outside Zone Min/Max X, Zone={}", zone.Name));
    4864            4 :                 ShowContinueError(state,
    4865            4 :                                   format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
    4866            2 :                                          refPt.absCoords.x,
    4867            2 :                                          zone.MinimumX,
    4868            2 :                                          zone.MaximumX));
    4869            4 :                 ShowContinueError(
    4870              :                     state,
    4871            4 :                     format("...X Reference Distance Outside MinimumX= {:.4R} m.",
    4872            4 :                            (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
    4873              :             }
    4874           28 :             if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
    4875            2 :                 refPt.inBounds = false;
    4876            4 :                 ShowWarningError(state,
    4877            4 :                                  format("GeometryTransformForDaylighting: Reference point Y Value outside Zone Min/Max Y, Zone={}", zone.Name));
    4878            4 :                 ShowContinueError(state,
    4879            4 :                                   format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
    4880            2 :                                          refPt.absCoords.x,
    4881            2 :                                          zone.MinimumY,
    4882            2 :                                          zone.MaximumY));
    4883            4 :                 ShowContinueError(
    4884              :                     state,
    4885            4 :                     format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
    4886            4 :                            (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
    4887              :             }
    4888           28 :             if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
    4889            0 :                 refPt.inBounds = false;
    4890            0 :                 ShowWarningError(state,
    4891            0 :                                  format("GeometryTransformForDaylighting: Reference point Z Value outside Zone Min/Max Z, Zone={}", zone.Name));
    4892            0 :                 ShowContinueError(state,
    4893            0 :                                   format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
    4894            0 :                                          refPt.absCoords.z,
    4895            0 :                                          zone.MinimumZ,
    4896            0 :                                          zone.MaximumZ));
    4897            0 :                 ShowContinueError(
    4898              :                     state,
    4899            0 :                     format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
    4900            0 :                            (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
    4901              :             }
    4902              :         } // for (refPt)
    4903              :     } // for (daylightCtrl)
    4904           11 : } // GeometryTransformForDaylighting()
    4905              : 
    4906           14 : void GetInputDayliteRefPt(EnergyPlusData &state, bool &ErrorsFound)
    4907              : {
    4908              :     // Perform GetInput function for the Daylighting:ReferencePoint object
    4909              :     // Glazer - July 2016
    4910           14 :     auto const &dl = state.dataDayltg;
    4911           14 :     auto &ip = state.dataInputProcessing->inputProcessor;
    4912           14 :     auto const &s_ipsc = state.dataIPShortCut;
    4913           14 :     s_ipsc->cCurrentModuleObject = "Daylighting:ReferencePoint";
    4914              : 
    4915           14 :     int RefPtNum = 0;
    4916              :     int IOStat;
    4917              :     int NumAlpha;
    4918              :     int NumNumber;
    4919              : 
    4920           14 :     int TotRefPoints = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    4921              : 
    4922           14 :     dl->DaylRefPt.allocate(TotRefPoints);
    4923           50 :     for (auto &pt : dl->DaylRefPt) {
    4924           72 :         ip->getObjectItem(state,
    4925           36 :                           s_ipsc->cCurrentModuleObject,
    4926              :                           ++RefPtNum,
    4927           36 :                           s_ipsc->cAlphaArgs,
    4928              :                           NumAlpha,
    4929           36 :                           s_ipsc->rNumericArgs,
    4930              :                           NumNumber,
    4931              :                           IOStat,
    4932           36 :                           s_ipsc->lNumericFieldBlanks,
    4933           36 :                           s_ipsc->lAlphaFieldBlanks,
    4934           36 :                           s_ipsc->cAlphaFieldNames,
    4935           36 :                           s_ipsc->cNumericFieldNames);
    4936           36 :         pt.Name = s_ipsc->cAlphaArgs(1);
    4937           36 :         pt.ZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
    4938           36 :         if (pt.ZoneNum == 0) {
    4939            0 :             int spaceNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->space);
    4940            0 :             if (spaceNum == 0) {
    4941            0 :                 ShowSevereError(state,
    4942            0 :                                 format("{}=\"{}\", invalid {}=\"{}\".",
    4943            0 :                                        s_ipsc->cCurrentModuleObject,
    4944            0 :                                        s_ipsc->cAlphaArgs(1),
    4945            0 :                                        s_ipsc->cAlphaFieldNames(2),
    4946            0 :                                        s_ipsc->cAlphaArgs(2)));
    4947            0 :                 ErrorsFound = true;
    4948              :             } else {
    4949            0 :                 pt.ZoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
    4950              :             }
    4951              :         }
    4952           36 :         pt.coords = {s_ipsc->rNumericArgs(1), s_ipsc->rNumericArgs(2), s_ipsc->rNumericArgs(3)};
    4953              :     }
    4954           14 : }
    4955              : 
    4956          134 : bool doesDayLightingUseDElight(EnergyPlusData const &state)
    4957              : {
    4958          134 :     auto const &dl = state.dataDayltg;
    4959          147 :     for (auto const &znDayl : dl->daylightControl) {
    4960           14 :         if (znDayl.DaylightMethod == DaylightingMethod::DElight) {
    4961            1 :             return true;
    4962              :         }
    4963              :     }
    4964          133 :     return false;
    4965              : }
    4966              : 
    4967          107 : void CheckTDDsAndLightShelvesInDaylitZones(EnergyPlusData &state)
    4968              : {
    4969              :     // SUBROUTINE INFORMATION:
    4970              :     //       AUTHOR         Brent Griffith
    4971              :     //       DATE WRITTEN   Dec 2007
    4972              : 
    4973              :     // PURPOSE OF THIS SUBROUTINE:
    4974              :     // This subroutine checks daylighting input for TDDs and light shelfs
    4975              :     //  which need to be checked after daylighting input has been read in (CR 7145)
    4976              :     //  (eventually this should be changed once/if implementations change to decouple from daylighting calcs so that
    4977              :     //  these devices can be used in models without daylighting controls
    4978              :     // CR 7145 was for TDDs, but also implenting check for light shelves, the other "daylighting device"
    4979              : 
    4980              :     // METHODOLOGY EMPLOYED:
    4981              :     // loop thru daylighting devices and check that their zones have daylight controls
    4982              : 
    4983              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4984          107 :     auto &s_surf = state.dataSurface;
    4985              : 
    4986          107 :     bool ErrorsFound = false;
    4987              : 
    4988          109 :     for (auto const &pipe : state.dataDaylightingDevicesData->TDDPipe) {
    4989            2 :         int SurfNum = pipe.Diffuser;
    4990            2 :         if (SurfNum > 0) {
    4991            2 :             int const pipeEnclNum = s_surf->Surface(SurfNum).SolarEnclIndex;
    4992            2 :             if (state.dataViewFactor->EnclSolInfo(pipeEnclNum).TotalEnclosureDaylRefPoints == 0) {
    4993            4 :                 ShowWarningError(state,
    4994            4 :                                  format("DaylightingDevice:Tubular = {}:  is not connected to a Zone that has Daylighting, no visible transmittance "
    4995              :                                         "will be modeled through the daylighting device.",
    4996            2 :                                         pipe.Name));
    4997              :             }
    4998              :         } else { // SurfNum == 0
    4999              :             // should not come here (would have already been caught in TDD get input), but is an error
    5000            0 :             ShowSevereError(state, format("DaylightingDevice:Tubular = {}:  Diffuser surface not found ", pipe.Name));
    5001            0 :             ErrorsFound = true;
    5002              :         }
    5003              :     } // for (pipe)
    5004              : 
    5005          107 :     for (auto const &shelf : state.dataDaylightingDevicesData->Shelf) {
    5006            0 :         if (shelf.Window == 0) {
    5007              :             // should not come here (would have already been caught in shelf get input), but is an error
    5008            0 :             ShowSevereError(state, format("DaylightingDevice:Shelf = {}:  window not found ", shelf.Name));
    5009            0 :             ErrorsFound = true;
    5010              :         }
    5011              :     } // for (shelf)
    5012              : 
    5013          107 :     if (ErrorsFound) {
    5014            0 :         ShowFatalError(state, "CheckTDDsAndLightShelvesInDaylitZones: Errors in DAYLIGHTING input.");
    5015              :     }
    5016          107 : }
    5017              : 
    5018          108 : void AssociateWindowShadingControlWithDaylighting(EnergyPlusData &state)
    5019              : {
    5020          108 :     auto &dl = state.dataDayltg;
    5021          108 :     auto &s_surf = state.dataSurface;
    5022              : 
    5023          112 :     for (auto &winShadeControl : s_surf->WindowShadingControl) {
    5024            4 :         if (winShadeControl.DaylightingControlName.empty()) {
    5025            1 :             continue;
    5026              :         }
    5027            3 :         int found = -1;
    5028            9 :         for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5029            8 :             if (Util::SameString(winShadeControl.DaylightingControlName, dl->daylightControl(daylightCtrlNum).Name)) {
    5030            2 :                 found = daylightCtrlNum;
    5031            2 :                 break;
    5032              :             }
    5033              :         }
    5034            3 :         if (found > 0) {
    5035            2 :             winShadeControl.DaylightControlIndex = found;
    5036              :         } else {
    5037            2 :             ShowWarningError(state, "AssociateWindowShadingControlWithDaylighting: Daylighting object name used in WindowShadingControl not found.");
    5038            2 :             ShowContinueError(state,
    5039            2 :                               format("..The WindowShadingControl object=\"{}\" and referenes an object named: \"{}\"",
    5040            1 :                                      winShadeControl.Name,
    5041            1 :                                      winShadeControl.DaylightingControlName));
    5042              :         }
    5043          108 :     }
    5044          108 : } // AssociateWindowShadingControlWithDaylighting()
    5045              : 
    5046            7 : void GetLightWellData(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
    5047              : {
    5048              : 
    5049              :     // SUBROUTINE INFORMATION:
    5050              :     //       AUTHOR         Fred Winkelmann
    5051              :     //       DATE WRITTEN   Apr 2004
    5052              : 
    5053              :     // PURPOSE OF THIS SUBROUTINE:
    5054              :     // Gets data for a light well associated with a rectangular exterior window.
    5055              :     // Calculates light well efficiency, defined as the ratio of the amount of visible
    5056              :     // solar radiation leaving a well to the amount entering the well.
    5057              : 
    5058              :     // METHODOLOGY EMPLOYED:
    5059              :     // Based on fit to Fig. 8-21, "Efficiency factors for various depths of light wells
    5060              :     // based on well-interreflectance values," Lighting Handbook, 8th Edition, Illuminating
    5061              :     // Engineering Society of North America, 1993.
    5062              : 
    5063              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5064              : 
    5065              :     int IOStat;        // IO Status when calling get input subroutine
    5066              :     int NumAlpha;      // Number of alpha names being passed
    5067              :     int NumProp;       // Number of properties being passed
    5068              :     int TotLightWells; // Total Light Well objects
    5069              : 
    5070            7 :     auto &ip = state.dataInputProcessing->inputProcessor;
    5071            7 :     auto &s_surf = state.dataSurface;
    5072            7 :     auto const &s_ipsc = state.dataIPShortCut;
    5073              : 
    5074              :     // Get the total number of Light Well objects
    5075            7 :     s_ipsc->cCurrentModuleObject = "DaylightingDevice:LightWell";
    5076            7 :     TotLightWells = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    5077            7 :     if (TotLightWells == 0) {
    5078            7 :         return;
    5079              :     }
    5080              : 
    5081            0 :     for (int loop = 1; loop <= TotLightWells; ++loop) {
    5082              : 
    5083            0 :         ip->getObjectItem(state,
    5084            0 :                           s_ipsc->cCurrentModuleObject,
    5085              :                           loop,
    5086            0 :                           s_ipsc->cAlphaArgs,
    5087              :                           NumAlpha,
    5088            0 :                           s_ipsc->rNumericArgs,
    5089              :                           NumProp,
    5090              :                           IOStat,
    5091            0 :                           s_ipsc->lNumericFieldBlanks,
    5092            0 :                           s_ipsc->lAlphaFieldBlanks,
    5093            0 :                           s_ipsc->cAlphaFieldNames,
    5094            0 :                           s_ipsc->cNumericFieldNames);
    5095              : 
    5096            0 :         int SurfNum = Util::FindItemInList(s_ipsc->cAlphaArgs(1), s_surf->Surface);
    5097            0 :         if (SurfNum == 0) {
    5098            0 :             ShowSevereError(
    5099            0 :                 state, format("{}: invalid {}=\"{}\" not found.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
    5100            0 :             ErrorsFound = true;
    5101            0 :             continue;
    5102              :         }
    5103              : 
    5104            0 :         auto const &surf = s_surf->Surface(SurfNum);
    5105            0 :         auto &surfWin = s_surf->SurfaceWindow(SurfNum);
    5106              :         // Check that associated surface is an exterior window
    5107              :         // True if associated surface is not an exterior window
    5108            0 :         if (surf.Class != SurfaceClass::Window && surf.ExtBoundCond != ExternalEnvironment) {
    5109            0 :             ShowSevereError(state,
    5110            0 :                             format("{}: invalid {}=\"{}\" - not an exterior window.",
    5111            0 :                                    s_ipsc->cCurrentModuleObject,
    5112            0 :                                    s_ipsc->cAlphaFieldNames(1),
    5113            0 :                                    s_ipsc->cAlphaArgs(1)));
    5114            0 :             ErrorsFound = true;
    5115            0 :             continue;
    5116              :         }
    5117              : 
    5118              :         // Associated surface is an exterior window; calculate light well efficiency.
    5119            0 :         surfWin.lightWellEff = 1.0;
    5120            0 :         Real64 HeightWell = s_ipsc->rNumericArgs(1);  // Well height (from window to bottom of well) (m)
    5121            0 :         Real64 PerimWell = s_ipsc->rNumericArgs(2);   // Well perimeter (at bottom of well) (m)
    5122            0 :         Real64 AreaWell = s_ipsc->rNumericArgs(3);    // Well area (at bottom of well) (m2)
    5123            0 :         Real64 VisReflWell = s_ipsc->rNumericArgs(4); // Area-weighted visible reflectance of well walls
    5124              : 
    5125              :         // Warning if light well area is less than window area
    5126            0 :         if (AreaWell < (surf.Area + s_surf->SurfWinDividerArea(SurfNum) - 0.1)) {
    5127            0 :             ShowSevereError(
    5128            0 :                 state, format("{}: invalid {}=\"{}\" - Areas.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
    5129            0 :             ShowContinueError(state, format("has Area of Bottom of Well={:.1R} that is less than window area={:.1R}", surf.Area, AreaWell));
    5130              :         }
    5131              : 
    5132            0 :         if (HeightWell >= 0.0 && PerimWell > 0.0 && AreaWell > 0.0) {
    5133            0 :             Real64 WellCavRatio = 2.5 * HeightWell * PerimWell / AreaWell;
    5134            0 :             surfWin.lightWellEff = std::exp(-WellCavRatio * (0.16368 - 0.14467 * VisReflWell));
    5135              :         }
    5136              :     } // End of loop over light well objects
    5137              : } // GetLightWellData()
    5138              : 
    5139         2382 : inline WinCover findWinShadingStatus(EnergyPlusData &state, int const IWin)
    5140              : {
    5141              :     // Return the window shading status, 1=unshaded, 2=shaded
    5142              : 
    5143         2382 :     auto &s_surf = state.dataSurface;
    5144         2382 :     bool WinShadedNoGlareControl = IS_SHADED_NO_GLARE_CTRL(s_surf->SurfWinShadingFlag(IWin));
    5145              : 
    5146         4758 :     return ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) && (WinShadedNoGlareControl || s_surf->SurfWinSolarDiffusing(IWin)))
    5147         4758 :                ? WinCover::Shaded
    5148         2382 :                : WinCover::Bare;
    5149              : }
    5150              : 
    5151         1369 : Real64 DayltgGlare(EnergyPlusData &state,
    5152              :                    int IL,                   // Reference point index: 1=first ref pt, 2=second ref pt
    5153              :                    Real64 BLUM,              // Window background (surround) luminance (cd/m2)
    5154              :                    int const daylightCtrlNum // Current daylighting control number
    5155              : )
    5156              : {
    5157              : 
    5158              :     // SUBROUTINE INFORMATION:
    5159              :     //       AUTHOR         Fred Winkelmann
    5160              :     //       DATE WRITTEN   July 1997
    5161              : 
    5162              :     // PURPOSE OF THIS SUBROUTINE:
    5163              :     // CALCULATE GLARE INDEX.
    5164              : 
    5165              :     // METHODOLOGY EMPLOYED:
    5166              :     // Called from DayltgInteriorIllum.  Finds glare index at reference
    5167              :     // point no. IL in a space using the Cornell/BRS large source
    5168              :     // glare formula. BLUM is the background luminance (cd/m**2).
    5169              :     // TH comment 1/21/2010: The SurfaceWindow(IWin)%ShadingFlag has to be set
    5170              :     //  before calling this subroutine. For switchable glazings this is tricky
    5171              :     //  because the ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop)
    5172              :     //  may change every time step to represent intermediate switched state.
    5173              : 
    5174              :     // REFERENCES:
    5175              :     // Based on DOE-2.1E subroutine DGLARE.
    5176              : 
    5177         1369 :     Real64 GTOT = 0.0; // Glare constant
    5178              : 
    5179         1369 :     auto &dl = state.dataDayltg;
    5180              : 
    5181              :     // Loop over exterior windows associated with zone
    5182         1369 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5183         1369 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
    5184         2738 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5185         1369 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5186         1369 :         WinCover winCover = findWinShadingStatus(state, IWin);
    5187              :         // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
    5188              :         // below, which is (0.2936)**0.6
    5189         1369 :         auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
    5190         1369 :         Real64 GTOT1 = 0.4794 * std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6) //
    5191         1369 :                        * std::pow(extWin.solidAngWtd, 0.8);
    5192         1369 :         Real64 GTOT2 = BLUM + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
    5193         1369 :         GTOT += GTOT1 / (GTOT2 + 0.000001);
    5194              :     }
    5195              : 
    5196              :     // Glare index (adding 0.000001 prevents LOG10 (0))
    5197         1369 :     return max(0.0, 10.0 * std::log10(GTOT + 0.000001));
    5198              : }
    5199              : 
    5200            0 : void DayltgGlareWithIntWins(EnergyPlusData &state,
    5201              :                             int const daylightCtrlNum // Current daylighting control number
    5202              : )
    5203              : {
    5204              : 
    5205              :     // SUBROUTINE INFORMATION:
    5206              :     //       AUTHOR         Fred Winkelmann
    5207              :     //       DATE WRITTEN   March 2004
    5208              : 
    5209              :     // PURPOSE OF THIS SUBROUTINE:
    5210              :     // Calculate daylighting glare index for zones with interior windows.
    5211              : 
    5212              :     // METHODOLOGY EMPLOYED:
    5213              :     // Finds glare index at reference point IL in a daylit zone using the Cornell/BRS large source
    5214              :     // glare formula. Takes into account inter-reflected illuminance from light entering
    5215              :     // the zone through interior windows
    5216              : 
    5217              :     // REFERENCES:
    5218              :     // Based on subroutine DayltgGlare.
    5219              : 
    5220            0 :     Real64 GTOT = 0.0; // Glare constant(?) // TODO: does this need to be reset for every refPt?
    5221              : 
    5222              :     // Calculate background luminance including effect of inter-reflected illuminance from light
    5223              :     // entering zone through its interior windows
    5224            0 :     auto &dl = state.dataDayltg;
    5225            0 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5226            0 :     auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
    5227            0 :     int RefPoints = thisDayltgCtrl.TotalDaylRefPoints; // Number of daylighting reference points in zone
    5228            0 :     for (int IL = 1; IL <= RefPoints; ++IL) {
    5229            0 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    5230              : 
    5231            0 :         Real64 BackgroundLum = refPt.lums[iLum_Back] + thisEnclDaylight.InterReflIllFrIntWins * thisEnclDaylight.aveVisDiffReflect / Constant::Pi;
    5232            0 :         BackgroundLum = max(refPt.illumSetPoint * thisEnclDaylight.aveVisDiffReflect / Constant::Pi, BackgroundLum);
    5233              : 
    5234              :         // Loop over exterior windows associated with zone
    5235            0 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5236            0 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5237            0 :             WinCover winCover = findWinShadingStatus(state, IWin);
    5238              :             // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
    5239              :             // below, which is (0.2936)**0.6
    5240            0 :             auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
    5241            0 :             Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6)) //
    5242            0 :                            * std::pow(extWin.solidAngWtd, 0.8);
    5243            0 :             Real64 GTOT2 = BackgroundLum + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
    5244            0 :             GTOT += GTOT1 / (GTOT2 + 0.000001);
    5245              :         }
    5246              : 
    5247              :         // Glare index
    5248            0 :         refPt.glareIndex = max(0.0, 10.0 * std::log10(GTOT + 0.000001));
    5249              :     } // for (IL)
    5250            0 : } // DaylGlareWithIntWins()
    5251              : 
    5252          551 : void DayltgExtHorizIllum(EnergyPlusData &state,
    5253              :                          Illums &HI // Horizontal illuminance from sky for different sky types
    5254              : )
    5255              : {
    5256              : 
    5257              :     // SUBROUTINE INFORMATION:
    5258              :     //       AUTHOR         Fred Winkelmann
    5259              :     //       DATE WRITTEN   July 1997
    5260              : 
    5261              :     // PURPOSE OF THIS SUBROUTINE:
    5262              :     // Calculates exterior daylight illuminance.
    5263              : 
    5264              :     // METHODOLOGY EMPLOYED:
    5265              :     // Called by CalcDayltgCoefficients. Calculates illuminance
    5266              :     // on unobstructed horizontal surface by integrating
    5267              :     // over the luminance distribution of standard CIE skies.
    5268              :     // Calculates horizontal beam illuminance.
    5269              :     // REFERENCES:
    5270              :     // Based on DOE-2.1E subroutine DHILL.
    5271              : 
    5272              :     // Argument array dimensioning
    5273              : 
    5274              :     // SUBROUTINE PARAMETER DEFINITIONS:
    5275          551 :     Real64 constexpr DTH = (2.0 * Constant::Pi) / double(NTH); // Sky integration azimuth stepsize (radians)
    5276          551 :     Real64 constexpr DPH = Constant::PiOvr2 / double(NPH);     // Sky integration altitude stepsize (radians)
    5277              : 
    5278              :     // Integrate to obtain illuminance from sky.
    5279              :     // The contribution in lumens/m2 from a patch of sky at altitude PH and azimuth TH
    5280              :     // is L(TH,PH)*SIN(PH)*COS(PH)*DTH*DPH, where L(TH,PH) is the luminance
    5281              :     // of the patch in cd/m2.
    5282          551 :     auto &dl = state.dataDayltg;
    5283              : 
    5284              :     //  Init
    5285          551 :     if (dl->DayltgExtHorizIllum_firstTime) {
    5286           27 :         for (int IPH = 1; IPH <= NPH; ++IPH) {
    5287           24 :             dl->PH[IPH] = (IPH - 0.5) * DPH;
    5288           24 :             dl->SPHCPH[IPH] = std::sin(dl->PH[IPH]) * std::cos(dl->PH[IPH]); // DA = COS(PH)*DTH*DPH
    5289              :         }
    5290           57 :         for (int ITH = 1; ITH <= NTH; ++ITH) {
    5291           54 :             dl->TH[ITH] = (ITH - 0.5) * DTH;
    5292              :         }
    5293            3 :         dl->DayltgExtHorizIllum_firstTime = false;
    5294              :     }
    5295              : 
    5296         2204 :     HI = Illums();
    5297              : 
    5298              :     // Sky integration
    5299         4959 :     for (int IPH = 1; IPH <= NPH; ++IPH) {
    5300         4408 :         Real64 const PH_IPH = dl->PH[IPH];
    5301         4408 :         Real64 const SPHCPH_IPH = dl->SPHCPH[IPH];
    5302        83752 :         for (int ITH = 1; ITH <= NTH; ++ITH) {
    5303        79344 :             Real64 const TH_ITH = dl->TH[ITH];
    5304       396720 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5305       317376 :                 HI.sky[iSky] += DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH_ITH, PH_IPH) * SPHCPH_IPH;
    5306              :             }
    5307              :         }
    5308              :     }
    5309              : 
    5310         2755 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    5311         2204 :         HI.sky[iSky] *= DTH * DPH;
    5312              :     }
    5313              : 
    5314              :     // Direct solar horizontal illum (for unit direct normal illuminance)
    5315          551 :     HI.sun = dl->sunAngles.sinPhi * 1.0;
    5316          551 : } // DayltgExtHorizIllum()
    5317              : 
    5318              : // Product of solar transmittances of exterior obstructions
    5319       386914 : Real64 DayltgHitObstruction(EnergyPlusData &state,
    5320              :                             int const IHOUR,           // Hour number
    5321              :                             int const IWin,            // Window index
    5322              :                             Vector3<Real64> const &R1, // Origin of ray (m)
    5323              :                             Vector3<Real64> const &RN  // Unit vector along ray
    5324              : )
    5325              : {
    5326              : 
    5327              :     // SUBROUTINE INFORMATION:
    5328              :     //       AUTHOR         Fred Winkelmann
    5329              :     //       DATE WRITTEN   July 1997
    5330              :     //       MODIFIED       FCW, May 2003: update list of surface classes that qualify as obstructions;
    5331              :     //                        add interior surfaces as possible obstructors;
    5332              :     //                        return from DO loop over surfaces as soon as any obstruction is hit;
    5333              :     //                      FCW, July 2003: change from returning whether an obstruction is hit or not
    5334              :     //                        to product of solar transmittances of hit obstructions.
    5335              :     //                      FCW, Nov 2003: remove interior surfaces as possible obstructors since there
    5336              :     //                        is now a separate check for interior obstructions; exclude windows and
    5337              :     //                        doors as obstructors since if they are obstructors their base surfaces will
    5338              :     //                        also be obstructors
    5339              :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5340              : 
    5341              :     // PURPOSE OF THIS SUBROUTINE:
    5342              :     // Determines the product of the solar transmittances of the obstructions hit by a ray
    5343              :     // from R1 in the direction of vector RN.
    5344              : 
    5345              :     // REFERENCES:
    5346              :     // Based on DOE-2.1E subroutine DHITSH.
    5347              : 
    5348       386914 :     auto &s_surf = state.dataSurface;
    5349              :     // Local declarations
    5350              :     bool hit; // True iff a particular obstruction is hit
    5351              : 
    5352       386914 :     Real64 ObTrans = 1.0;
    5353              : 
    5354       386914 :     auto const &window = s_surf->Surface(IWin);
    5355       386914 :     int const window_iBaseSurf = window.BaseSurf;
    5356              : 
    5357       386914 :     Vector3<Real64> DayltgHitObstructionHP;
    5358              :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5359              :     // Building elements are assumed to be opaque
    5360              :     // A shadowing surface is opaque unless its transmittance schedule value is non-zero
    5361       386914 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5362              : 
    5363      1313432 :         for (int ISurf : s_surf->AllShadowPossObstrSurfaceList) {
    5364       926518 :             auto const &surface = s_surf->Surface(ISurf);
    5365       926518 :             SurfaceClass IType = surface.Class;
    5366       926518 :             if ((IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf)) {
    5367       539604 :                 hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
    5368       539604 :                 if (hit) { // Building element is hit (assumed opaque)
    5369            0 :                     ObTrans = 0.0;
    5370            0 :                     break;
    5371              :                 }
    5372       386914 :             } else if (surface.IsShadowing) {
    5373            0 :                 hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
    5374            0 :                 if (hit) { // Shading surface is hit
    5375              :                     // Get solar transmittance of the shading surface
    5376            0 :                     Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
    5377            0 :                     if (Trans < 1.e-6) {
    5378            0 :                         ObTrans = 0.0;
    5379            0 :                         break;
    5380              :                     } else {
    5381            0 :                         ObTrans *= Trans;
    5382              :                     }
    5383              :                 }
    5384              :             }
    5385       386914 :         }
    5386              : 
    5387              :     } else { // Surface octree search
    5388              : 
    5389            0 :         auto const &window_base(window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window);
    5390            0 :         auto const *window_base_p(&window_base);
    5391              : 
    5392              :         // Lambda function for the octree to test for surface hit and update transmittance if hit
    5393            0 :         auto solarTransmittance = [=, &state, &R1, &RN, &hit, &ObTrans](SurfaceData const &surface) -> bool {
    5394            0 :             if (!surface.IsShadowPossibleObstruction) {
    5395            0 :                 return false; // Do Consider separate octree without filtered surfaces
    5396              :             }
    5397            0 :             DataSurfaces::SurfaceClass const sClass(surface.Class);
    5398            0 :             Vector3<Real64> HP;
    5399            0 :             if ((sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && (&surface != window_base_p)) {
    5400            0 :                 hit = PierceSurface(surface, R1, RN, HP);
    5401            0 :                 if (hit) { // Building element is hit (assumed opaque)
    5402            0 :                     ObTrans = 0.0;
    5403            0 :                     return true;
    5404              :                 }
    5405            0 :             } else if (surface.IsShadowing) {
    5406            0 :                 hit = PierceSurface(surface, R1, RN, HP);
    5407            0 :                 if (hit) { // Shading surface is hit
    5408              :                     // Get solar transmittance of the shading surface
    5409            0 :                     Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
    5410            0 :                     if (Trans < 1.e-6) {
    5411            0 :                         ObTrans = 0.0;
    5412            0 :                         return true;
    5413              :                     } else {
    5414            0 :                         ObTrans *= Trans;
    5415            0 :                         return ObTrans == 0.0;
    5416              :                     }
    5417              :                 }
    5418              :             }
    5419            0 :             return false;
    5420            0 :         };
    5421              : 
    5422              :         // Check octree surface candidates for hits: short circuits if zero transmittance reached
    5423            0 :         Vector3<Real64> const RN_inv(SurfaceOctreeCube::safe_inverse(RN));
    5424            0 :         state.dataHeatBalMgr->surfaceOctree.processSomeSurfaceRayIntersectsCube(state, R1, RN, RN_inv, solarTransmittance);
    5425            0 :     }
    5426              : 
    5427       386914 :     return ObTrans;
    5428       386914 : } // DayltgHitObstruction()
    5429              : 
    5430       368194 : bool DayltgHitInteriorObstruction(EnergyPlusData &state,
    5431              :                                   int const IWin,            // Window index
    5432              :                                   Vector3<Real64> const &R1, // Origin of ray (m)
    5433              :                                   Vector3<Real64> const &R2  // Destination of ray (m)
    5434              : )
    5435              : {
    5436              : 
    5437              :     // SUBROUTINE INFORMATION:
    5438              :     //       AUTHOR         Fred Winkelmann
    5439              :     //       DATE WRITTEN   July 1997
    5440              :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5441              : 
    5442              :     // PURPOSE OF THIS SUBROUTINE:
    5443              :     // This subroutine checks for interior obstructions between reference point and window element.
    5444              : 
    5445       368194 :     auto &s_surf = state.dataSurface;
    5446              : 
    5447              :     // Preconditions
    5448       368194 :     assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
    5449              : 
    5450       368194 :     bool hit = false;
    5451       368194 :     Vector3<Real64> RN = (R2 - R1).normalize(); // Make unit vector
    5452       368194 :     Real64 const d12 = distance(R1, R2);        // Distance between R1 and R2
    5453              : 
    5454       368194 :     auto const &window = s_surf->Surface(IWin);
    5455       368194 :     int const window_Enclosure = window.SolarEnclIndex;
    5456       368194 :     int const window_iBaseSurf = window.BaseSurf;
    5457       368194 :     auto const &window_base = window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window;
    5458       368194 :     int const window_base_iExtBoundCond = window_base.ExtBoundCond;
    5459              : 
    5460              :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5461       368194 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5462              :         // Hit coordinates, if ray hits an obstruction
    5463       368194 :         Vector3<Real64> DayltgHitInteriorObstructionHP;
    5464              : 
    5465      1560449 :         for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
    5466      1192255 :             auto const &surface = s_surf->Surface(ISurf);
    5467      1192255 :             SurfaceClass IType = surface.Class;
    5468      1192255 :             if ((surface.IsShadowing) ||                         // Shadowing surface
    5469      1192255 :                 ((surface.SolarEnclIndex == window_Enclosure) && // Wall/ceiling/floor is in same zone as window
    5470      1190414 :                  (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf) &&
    5471              :                  (ISurf != window_base_iExtBoundCond))) // Exclude window's base or base-adjacent surfaces
    5472              :             {
    5473       454026 :                 hit = PierceSurface(state, ISurf, R1, RN, d12, DayltgHitInteriorObstructionHP); // Check if R2-R1 segment pierces surface
    5474       454026 :                 if (hit) {
    5475            0 :                     break; // Segment pierces surface: Don't check the rest
    5476              :                 }
    5477              :             }
    5478              :         }
    5479              : 
    5480       368194 :     } else { // Surface octree search
    5481              : 
    5482            0 :         auto const *window_base_p = &window_base;
    5483            0 :         auto const &window_base_adjacent = window_base_iExtBoundCond > 0 ? s_surf->Surface(window_base_iExtBoundCond) : window_base;
    5484            0 :         auto const *window_base_adjacent_p = &window_base_adjacent;
    5485              : 
    5486              :         // Lambda function for the octree to test for surface hit
    5487            0 :         auto surfaceHit = [=, &R1, &hit](SurfaceData const &surface) -> bool {
    5488            0 :             DataSurfaces::SurfaceClass const sClass = surface.Class;
    5489            0 :             Vector3<Real64> HP;                                  // Hit point
    5490            0 :             if ((surface.IsShadowing) ||                         // Shadowing surface
    5491            0 :                 ((surface.SolarEnclIndex == window_Enclosure) && // Surface is in same zone as window
    5492            0 :                  (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5493            0 :                  (&surface != window_base_p) && (&surface != window_base_adjacent_p))) // Exclude window's base or base-adjacent surfaces
    5494              :             {
    5495            0 :                 hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5496            0 :                 return hit;
    5497              :             } else {
    5498            0 :                 return false;
    5499              :             }
    5500            0 :         };
    5501              : 
    5502              :         // Check octree surface candidates until a hit is found, if any
    5503            0 :         state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
    5504            0 :     }
    5505              : 
    5506       368194 :     return hit;
    5507       368194 : } // DayltgHitInteriorObstruction()
    5508              : 
    5509            0 : bool DayltgHitBetWinObstruction(EnergyPlusData &state,
    5510              :                                 int const IWin1,           // Surface number of origin window
    5511              :                                 int const IWin2,           // Surface number of destination window
    5512              :                                 Vector3<Real64> const &R1, // Origin of ray (on IWin1) (m)
    5513              :                                 Vector3<Real64> const &R2  // Destination of ray (on IWin2) (m)
    5514              : )
    5515              : {
    5516              : 
    5517              :     // SUBROUTINE INFORMATION:
    5518              :     //       AUTHOR         Fred Winkelmann
    5519              :     //       DATE WRITTEN   Feb 2004
    5520              :     //       RE-ENGINEERED  Sept 2015. Stuart Mentzer. Octree for performance.
    5521              : 
    5522              :     // PURPOSE OF THIS SUBROUTINE:
    5523              :     // Determines if a ray from point R1 on window IWin1 to point R2
    5524              :     // on window IWin2 hits an obstruction
    5525              : 
    5526            0 :     auto &s_surf = state.dataSurface;
    5527              : 
    5528              :     // Preconditions
    5529            0 :     assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
    5530              : 
    5531              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5532              :     SurfaceClass IType; // Surface type/class
    5533              : 
    5534            0 :     bool hit = false;
    5535            0 :     Vector3<Real64> RN = (R2 - R1).normalize(); // Unit vector
    5536              : 
    5537            0 :     Real64 const d12 = distance(R1, R2); // Distance between R1 and R2 (m)
    5538              : 
    5539            0 :     auto const &window1 = s_surf->Surface(IWin1);
    5540            0 :     int const window1_iBaseSurf = window1.BaseSurf;
    5541            0 :     auto const &window1_base = window1_iBaseSurf > 0 ? s_surf->Surface(window1_iBaseSurf) : window1;
    5542            0 :     int const window1_base_iExtBoundCond = window1_base.ExtBoundCond;
    5543              : 
    5544            0 :     auto const &window2 = s_surf->Surface(IWin2);
    5545            0 :     int const window2_Enclosure = window2.SolarEnclIndex;
    5546            0 :     int const window2_iBaseSurf = window2.BaseSurf;
    5547            0 :     auto const &window2_base = window2_iBaseSurf > 0 ? s_surf->Surface(window2_iBaseSurf) : window2;
    5548            0 :     int const window2_base_iExtBoundCond = window2_base.ExtBoundCond;
    5549              : 
    5550              :     // Preconditions
    5551              :     //        assert( window1.Zone == window2_Zone ); //? This is violated in PurchAirWithDoubleFacadeDaylighting so then why the asymmetry
    5552              :     // of  only checking for wall/roof/floor for window2 zone below?
    5553              : 
    5554              :     // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
    5555            0 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    5556              : 
    5557            0 :         for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
    5558            0 :             auto const &surface = s_surf->Surface(ISurf);
    5559            0 :             IType = surface.Class;
    5560            0 :             if ((surface.IsShadowing) ||                          // Shadowing surface
    5561            0 :                 ((surface.SolarEnclIndex == window2_Enclosure) && // Wall/ceiling/floor is in same zone as windows
    5562            0 :                  (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5563            0 :                  (ISurf != window1_iBaseSurf) && (ISurf != window2_iBaseSurf) &&                                 // Exclude windows' base surfaces
    5564            0 :                  (ISurf != window1_base_iExtBoundCond) && (ISurf != window2_base_iExtBoundCond))) // Exclude windows' base-adjacent surfaces
    5565              :             {
    5566            0 :                 Vector3<Real64> HP;
    5567            0 :                 hit = PierceSurface(state, ISurf, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5568            0 :                 if (hit) {
    5569            0 :                     break; // Segment pierces surface: Don't check the rest
    5570              :                 }
    5571            0 :             }
    5572              :         }
    5573              : 
    5574              :     } else { // Surface octree search
    5575              : 
    5576            0 :         auto const *window1_base_p = &window1_base;
    5577            0 :         auto const &window1_base_adjacent = window1_base_iExtBoundCond > 0 ? s_surf->Surface(window1_base_iExtBoundCond) : window1_base;
    5578            0 :         auto const *window1_base_adjacent_p = &window1_base_adjacent;
    5579              : 
    5580            0 :         auto const *window2_base_p = &window2_base;
    5581            0 :         auto const &window2_base_adjacent = (window2_base_iExtBoundCond > 0) ? s_surf->Surface(window2_base_iExtBoundCond) : window2_base;
    5582            0 :         auto const *window2_base_adjacent_p = &window2_base_adjacent;
    5583              : 
    5584              :         // Lambda function for the octree to test for surface hit
    5585            0 :         auto surfaceHit = [=, &R1, &RN, &hit](SurfaceData const &surface) -> bool {
    5586            0 :             DataSurfaces::SurfaceClass const sClass = surface.Class;
    5587            0 :             Vector3<Real64> HP;
    5588            0 :             if ((surface.IsShadowing) ||                          // Shadowing surface
    5589            0 :                 ((surface.SolarEnclIndex == window2_Enclosure) && // Surface is in same zone as window
    5590            0 :                  (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
    5591            0 :                  (&surface != window1_base_p) && (&surface != window2_base_p) &&                                    // Exclude windows' base surfaces
    5592            0 :                  (&surface != window1_base_adjacent_p) && (&surface != window2_base_adjacent_p))) // Exclude windows' base-adjacent surfaces
    5593              :             {
    5594            0 :                 hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
    5595            0 :                 return hit;
    5596              :             } else {
    5597            0 :                 return false;
    5598              :             }
    5599            0 :         };
    5600              : 
    5601              :         // Check octree surface candidates until a hit is found, if any
    5602            0 :         state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
    5603              :     }
    5604              : 
    5605            0 :     return hit;
    5606            0 : } // DayltingHitBetWinObstruction()
    5607              : 
    5608       249956 : void initDaylighting(EnergyPlusData &state, bool const initSurfaceHeatBalancefirstTime)
    5609              : {
    5610              :     // For daylit zones, calculate interior daylight illuminance at reference points and
    5611              :     // simulate lighting control system to get overhead electric lighting reduction
    5612              :     // factor due to daylighting.
    5613       249956 :     auto &dl = state.dataDayltg;
    5614       249956 :     auto &s_surf = state.dataSurface;
    5615              : 
    5616       338520 :     for (int SurfNum : s_surf->AllExtSolWindowSurfaceList) {
    5617        91270 :         for (auto &refPt : s_surf->SurfaceWindow(SurfNum).refPts) {
    5618         2706 :             refPt.illumFromWinRep = refPt.lumWinRep = 0.0;
    5619              :         }
    5620       249956 :     }
    5621              : 
    5622              :     // Reset space power reduction factors
    5623       620209 :     for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
    5624       370253 :         dl->spacePowerReductionFactor(spaceNum) = 1.0;
    5625              :     }
    5626       251986 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5627         2030 :         auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5628         2030 :         thisDayltgCtrl.PowerReductionFactor = 1.0;
    5629         2030 :         if (state.dataEnvrn->PreviousSolRadPositive) {
    5630              :             // Reset to zero only if there was solar in the previous timestep, otherwise these are already zero
    5631          941 :             dl->enclDaylight(thisDayltgCtrl.enclIndex).InterReflIllFrIntWins = 0.0; // inter-reflected illuminance from interior windows
    5632         2288 :             for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
    5633         1347 :                 auto &refPt = thisDayltgCtrl.refPts(refPtNum);
    5634         1347 :                 refPt.lums[iLum_Illum] = 0.0;
    5635         1347 :                 refPt.glareIndex = 0.0;
    5636         1347 :                 refPt.timeExceedingGlareIndexSetPoint = 0.0;
    5637         1347 :                 refPt.timeExceedingDaylightIlluminanceSetPoint = 0.0;
    5638              :             }
    5639              :         }
    5640              : 
    5641         2030 :         if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0) {
    5642          943 :             if (initSurfaceHeatBalancefirstTime) {
    5643            0 :                 DisplayString(state, "Computing Interior Daylighting Illumination");
    5644              :             }
    5645          943 :             DayltgInteriorIllum(state, daylightCtrlNum);
    5646              :         }
    5647              :     }
    5648              : 
    5649              :     // The following report variables are valid only for daylit zones/enclosures without interior windows
    5650       249956 :     if (state.dataEnvrn->SunIsUp) {
    5651       294769 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    5652       174268 :             if ((state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) ||
    5653          944 :                 (state.dataViewFactor->EnclSolInfo(enclNum).HasInterZoneWindow)) {
    5654       172380 :                 continue;
    5655              :             }
    5656              : 
    5657          944 :             auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    5658         1887 :             for (int extWinNum = 1; extWinNum <= thisEnclDaylight.NumOfDayltgExtWins; ++extWinNum) {
    5659          943 :                 int IWin = thisEnclDaylight.DayltgExtWinSurfNums(extWinNum);
    5660          943 :                 WinCover winCover = WinCover::Bare;
    5661         2829 :                 if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
    5662         1886 :                     (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
    5663            0 :                     winCover = WinCover::Shaded;
    5664              :                 }
    5665          943 :                 int refPtCount = 0;
    5666         1886 :                 for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    5667          943 :                     auto &daylCtrl = dl->daylightControl(controlNum);
    5668          943 :                     if (daylCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
    5669            0 :                         continue;
    5670              :                     }
    5671              : 
    5672         2293 :                     for (int refPtNum = 1; refPtNum <= daylCtrl.TotalDaylRefPoints; ++refPtNum) {
    5673         1350 :                         ++refPtCount; // Count reference points across each daylighting control in the same enclosure
    5674         1350 :                         auto &refPt = s_surf->SurfaceWindow(IWin).refPts(refPtCount);
    5675         1350 :                         auto const &daylCtrlRefPt = daylCtrl.refPts(refPtNum);
    5676         1350 :                         refPt.illumFromWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Illum][(int)winCover];
    5677         1350 :                         refPt.lumWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Source][(int)winCover];
    5678              :                     }
    5679          943 :                 } // for (controlNum)
    5680              :             } // for (extWinNum)
    5681              :         } // for (enclNum)
    5682              :     } // if (SunIsUp)
    5683              : 
    5684       249956 :     if (state.dataEnvrn->SunIsUp && (int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
    5685            0 :         if (initSurfaceHeatBalancefirstTime) {
    5686            0 :             DisplayString(state, "Computing Interior Daylighting Illumination for TDD pipes");
    5687              :         }
    5688            0 :         DayltgInteriorTDDIllum(state);
    5689              :     }
    5690              : 
    5691       251986 :     for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
    5692         2030 :         auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5693              : 
    5694              :         // RJH DElight Modification Begin - Call to DElight electric lighting control subroutine
    5695              :         // Check if the sun is up and the current Thermal Zone hosts a Daylighting:DElight object
    5696         2030 :         if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0 && (thisDayltgCtrl.DaylightMethod == DaylightingMethod::DElight)) {
    5697            0 :             int zoneNum = thisDayltgCtrl.zoneIndex;
    5698              :             // Call DElight interior illuminance and electric lighting control subroutine
    5699            0 :             Real64 dPowerReducFac = 1.0; // Return value Electric Lighting Power Reduction Factor for current Zone and Timestep
    5700            0 :             Real64 dHISKFFC = state.dataEnvrn->HISKF * DataDElight::LUX2FC;
    5701            0 :             Real64 dHISUNFFC = state.dataEnvrn->HISUNF * DataDElight::LUX2FC;
    5702            0 :             Real64 dSOLCOS1 = state.dataEnvrn->SOLCOS.x;
    5703            0 :             Real64 dSOLCOS2 = state.dataEnvrn->SOLCOS.y;
    5704            0 :             Real64 dSOLCOS3 = state.dataEnvrn->SOLCOS.z;
    5705            0 :             Real64 dLatitude = state.dataEnvrn->Latitude;
    5706            0 :             Real64 dCloudFraction = state.dataEnvrn->CloudFraction;
    5707              :             // Init Error Flag to 0 (no Warnings or Errors) (returned from DElight)
    5708            0 :             int iErrorFlag = 0;
    5709              : 
    5710            0 :             DElightManagerF::DElightElecLtgCtrl(len(state.dataHeatBal->Zone(zoneNum).Name),
    5711            0 :                                                 state.dataHeatBal->Zone(zoneNum).Name,
    5712              :                                                 dLatitude,
    5713              :                                                 dHISKFFC,
    5714              :                                                 dHISUNFFC,
    5715              :                                                 dCloudFraction,
    5716              :                                                 dSOLCOS1,
    5717              :                                                 dSOLCOS2,
    5718              :                                                 dSOLCOS3,
    5719              :                                                 dPowerReducFac,
    5720              :                                                 iErrorFlag);
    5721              :             // Check Error Flag for Warnings or Errors returning from DElight
    5722              :             // RJH 2008-03-07: If no warnings/errors then read refpt illuminances for standard output reporting
    5723            0 :             if (iErrorFlag != 0) {
    5724            0 :                 std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
    5725              :                 // Open DElight Electric Lighting Error File for reading
    5726            0 :                 auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
    5727            0 :                 bool elOpened = iDElightErrorFile.good();
    5728              : 
    5729              :                 // Sequentially read lines in DElight Electric Lighting Error File
    5730              :                 // and process them using standard EPlus warning/error handling calls
    5731            0 :                 bool bEndofErrFile = false;
    5732            0 :                 while (!bEndofErrFile && elOpened) {
    5733            0 :                     auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
    5734            0 :                     if (cErrorLine.eof) {
    5735            0 :                         bEndofErrFile = true;
    5736            0 :                         continue;
    5737              :                     }
    5738              : 
    5739              :                     // Is the current line a Warning message?
    5740            0 :                     if (has_prefix(cErrorLine.data, "WARNING: ")) {
    5741            0 :                         cErrorMsg = cErrorLine.data.substr(9);
    5742            0 :                         ShowWarningError(state, cErrorMsg);
    5743              :                     }
    5744              :                     // Is the current line an Error message?
    5745            0 :                     if (has_prefix(cErrorLine.data, "ERROR: ")) {
    5746            0 :                         cErrorMsg = cErrorLine.data.substr(7);
    5747            0 :                         ShowSevereError(state, cErrorMsg);
    5748            0 :                         iErrorFlag = 1;
    5749              :                     }
    5750            0 :                 }
    5751              : 
    5752              :                 // Close DElight Error File and delete
    5753              : 
    5754            0 :                 if (elOpened) {
    5755            0 :                     iDElightErrorFile.close();
    5756            0 :                     FileSystem::removeFile(iDElightErrorFile.filePath);
    5757              :                 }
    5758              :                 // If any DElight Error occurred then ShowFatalError to terminate
    5759            0 :                 if (iErrorFlag > 0) {
    5760            0 :                     ShowFatalError(state, "End of DElight Error Messages");
    5761              :                 }
    5762            0 :             } else { // RJH 2008-03-07: No errors
    5763              :                      // extract reference point illuminance values from DElight Electric Lighting dump file for reporting
    5764              :                      // Open DElight Electric Lighting Dump File for reading
    5765            0 :                 auto iDElightErrorFile = state.files.outputDelightEldmpFilePath.try_open(state.files.outputControl.delighteldmp); // (THIS_AUTO_OK)
    5766            0 :                 bool elOpened = iDElightErrorFile.is_open();
    5767              : 
    5768              :                 // Sequentially read lines in DElight Electric Lighting Dump File
    5769              :                 // and extract refpt illuminances for standard EPlus output handling
    5770            0 :                 bool bEndofErrFile = false;
    5771            0 :                 int iDElightRefPt = 0; // Reference Point number for reading DElight Dump File (eplusout.delighteldmp)
    5772            0 :                 while (!bEndofErrFile && elOpened) {
    5773            0 :                     auto line = iDElightErrorFile.read<Real64>(); // (THIS_AUTO_OK)
    5774            0 :                     Real64 dRefPtIllum = line.data;               // tmp var for reading RefPt illuminance
    5775            0 :                     if (line.eof) {
    5776            0 :                         bEndofErrFile = true;
    5777            0 :                         continue;
    5778              :                     }
    5779              :                     // Increment refpt counter
    5780            0 :                     ++iDElightRefPt;
    5781              :                     // Assure refpt index does not exceed number of refpts in this zone
    5782            0 :                     if (iDElightRefPt <= thisDayltgCtrl.TotalDaylRefPoints) {
    5783            0 :                         thisDayltgCtrl.refPts(iDElightRefPt).lums[iLum_Illum] = dRefPtIllum;
    5784              :                     }
    5785              :                 }
    5786              : 
    5787              :                 // Close DElight Electric Lighting Dump File and delete
    5788            0 :                 if (elOpened) {
    5789            0 :                     iDElightErrorFile.close();
    5790            0 :                     FileSystem::removeFile(iDElightErrorFile.filePath);
    5791              :                 };
    5792            0 :             }
    5793              :             // Store the calculated total zone Power Reduction Factor due to DElight daylighting
    5794              :             // in the ZoneDaylight structure for later use
    5795            0 :             thisDayltgCtrl.PowerReductionFactor = dPowerReducFac;
    5796              :         }
    5797              :         // RJH DElight Modification End - Call to DElight electric lighting control subroutine
    5798              :     }
    5799              : 
    5800       249956 :     if (state.dataEnvrn->SunIsUp && !state.dataGlobal->DoingSizing) {
    5801        88617 :         DayltgInteriorMapIllum(state);
    5802              :     }
    5803       586606 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    5804       706903 :         for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    5805       370253 :             auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    5806       458817 :             for (int SurfNum = thisSpace.WindowSurfaceFirst; SurfNum <= thisSpace.WindowSurfaceLast; ++SurfNum) {
    5807        88564 :                 s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
    5808        88564 :                 if (IS_SHADED(s_surf->SurfWinShadingFlag(SurfNum))) {
    5809            0 :                     s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 1.0;
    5810              :                 } else {
    5811        88564 :                     s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
    5812              :                 }
    5813              :             }
    5814       336650 :         }
    5815              :     }
    5816       249956 : }
    5817              : 
    5818       249956 : void manageDaylighting(EnergyPlusData &state)
    5819              : {
    5820       249956 :     auto &dl = state.dataDayltg;
    5821              : 
    5822       249956 :     if (state.dataEnvrn->SunIsUp && (state.dataEnvrn->BeamSolarRad + state.dataEnvrn->GndSolarRad + state.dataEnvrn->DifSolarRad > 0.0)) {
    5823       176516 :         for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    5824       105303 :             auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    5825       105303 :             if (enclSol.TotalEnclosureDaylRefPoints == 0 || !enclSol.HasInterZoneWindow) {
    5826       105303 :                 continue;
    5827              :             }
    5828              : 
    5829            0 :             DayltgInterReflIllFrIntWins(state, enclNum);
    5830            0 :             for (int daylightCtrlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    5831            0 :                 DayltgGlareWithIntWins(state, daylightCtrlNum);
    5832            0 :             }
    5833              :         }
    5834        71213 :         DayltgElecLightingControl(state);
    5835       178743 :     } else if (dl->mapResultsToReport && state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
    5836            2 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    5837            1 :             ReportIllumMap(state, MapNum);
    5838              :         }
    5839            1 :         dl->mapResultsToReport = false;
    5840              :     }
    5841       249956 : } // manageDaylighting()
    5842              : 
    5843          954 : void DayltgInteriorIllum(EnergyPlusData &state,
    5844              :                          int const daylightCtrlNum) // Daylighting:Controls number
    5845              : {
    5846              : 
    5847              :     // SUBROUTINE INFORMATION:
    5848              :     //       AUTHOR         Fred Winkelmann
    5849              :     //       DATE WRITTEN   July 1997
    5850              :     //       MODIFIED       March 2000, FCW: interpolate clear-sky daylight factors using
    5851              :     //                      HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
    5852              :     //                      only HourOfDay was used
    5853              :     //                      Jan 2001, FCW: interpolate in slat angle for windows with blinds
    5854              :     //                      that have movable slats
    5855              :     //                      Oct 2002, LKL: changed interpolation steps to HourOfDay/WeightNow
    5856              :     //                      LastHour/WeightPreviousHour
    5857              :     //                      Aug 2003, FCW: fix bug that prevented shadingControlType =
    5858              :     //                      MEETDAYLIGHTILLUMINANCESETPOINT from working
    5859              :     //                      Mar 2004, FCW: fix bug in calc of illuminance setpoint contribution
    5860              :     //                      to background luminance: now it is divided by pi to give cd/m2
    5861              :     //                      Mar 2004, FCW: modify to handle daylighting through interior windows
    5862              :     //                      June 2009, TH: modified for thermochromic windows
    5863              :     //                      Jan 2010, TH (CR 7984): added iterations for switchable windows with shading
    5864              :     //                       control of MeetDaylightIlluminanceSetpoint and glare control is active
    5865              :     //                       Also corrected bugs (CR 7988) for switchable glazings not related to CR 7984
    5866              : 
    5867              :     // PURPOSE OF THIS SUBROUTINE:
    5868              :     // Using daylighting factors and exterior illuminance, determine
    5869              :     // the current-hour interior daylight illuminance and glare index
    5870              :     // at each reference point in a space. Deploy window shading window by window
    5871              :     // if glare control is active for window and if the acceptable glare index
    5872              :     // is exceeded at both reference points.
    5873              : 
    5874              :     // Called by InitSurfaceHeatBalance.
    5875              : 
    5876              :     // REFERENCES:
    5877              :     // Based on DOE-2.1E subroutine DINTIL.
    5878          954 :     auto &dl = state.dataDayltg;
    5879          954 :     auto &s_surf = state.dataSurface;
    5880              : 
    5881          954 :     Real64 constexpr tmpSWIterStep(0.05); // step of switching factor, assuming maximum of 20 switching states
    5882              : 
    5883              :     int NREFPT; // Number of daylighting reference points
    5884              :     int iSky1;  // Sky type index values for averaging two sky types
    5885              :     int iSky2;
    5886          954 :     Array1D<Real64> SetPnt; // Illuminance setpoint at reference points (lux)
    5887          954 :     Array1D<Real64> GLRNEW; // New glare index at reference point
    5888              : 
    5889          954 :     auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
    5890          954 :     int enclNum = thisDayltgCtrl.enclIndex;
    5891          954 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    5892              :     int ISWFLG; // Switchable glazing flag: =1 if one or more windows in a zone
    5893              :     //  has switchable glazing that adjusts visible transmittance to just meet
    5894              :     //  daylighting setpoint; =0 otherwise.
    5895              :     Real64 VTRAT;        // Ratio between switched and unswitched visible transmittance at normal incidence
    5896              :     Real64 BACL;         // Window background (surround) luminance for glare calc (cd/m2)
    5897              :     Real64 SkyWeight;    // Weighting factor used to average two different sky types
    5898              :     Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
    5899              :     //   luminous efficacy and horizontal illuminance from averaged sky
    5900              :     bool GlareFlag; // True if maximum glare is exceeded
    5901              : 
    5902              :     Real64 VTRatio;  // VT (visible transmittance) ratio = VTNow / VTMaster
    5903              :     Real64 VTNow;    // VT of the time step actual TC window
    5904              :     Real64 VTMaster; // VT of the base/master TC window
    5905              : 
    5906          954 :     Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> tmpDaylFromWinAtRefPt;
    5907              : 
    5908          954 :     bool breakOuterLoop(false);
    5909          954 :     bool continueOuterLoop(false);
    5910              : 
    5911              :     struct ShadeGroupLums
    5912              :     {
    5913              :         Array1D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> WDAYIL; // Illuminance from window at ref-point
    5914              :         Array1D<std::array<Real64, (int)Lum::Num>> RDAYIL; // Illuminance from window at ref-point after closing shade
    5915              :         Real64 switchedWinLum;
    5916              :         Real64 unswitchedWinLum;
    5917              :         Real64 switchedTvis;
    5918              :         Real64 unswitchedTvis;
    5919              :         Real64 lumRatio;
    5920              :     };
    5921              : 
    5922          954 :     Array1D<ShadeGroupLums> shadeGroupsLums;
    5923              : 
    5924              :     // Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, iLum_Num>> WDAYIL; // Illuminance from window at reference point
    5925              :     // (second index)
    5926              :     //   the number of shade deployment groups (third index)
    5927              :     // Array2D<std::array<Real64, (int)DataSurfaces::WinCover::Num>> WBACLU; // Background illuminance from window at reference point (second index)
    5928              :     //   the number of shade deployment groups (third index)
    5929              :     // Array2D<std::array<Real64, iLum_Num>> RDAYIL; // Illuminance from window at reference point after closing shade
    5930              :     // Array2D<Real64> RBACLU; // Background illuminance from window at reference point after closing shade
    5931              :     // Array1D<Real64> DILLSW;         // Illuminance a ref point from a group of windows that can be switched together,
    5932              :     // Array1D<Real64> DILLUN;         //  and from those that aren't (lux)
    5933              :     // Array1D<Real64> TVIS1;  // Visible transmittance at normal incidence of unswitched glazing
    5934              :     // Array1D<Real64> TVIS2;  // Visible transmittance at normal incidence of fully-switched glazing
    5935              :     // Array1D<Real64> ASETIL; // Illuminance ratio (lux)
    5936              : 
    5937          954 :     if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
    5938            0 :         return;
    5939              :     }
    5940              : 
    5941          954 :     NREFPT = thisDayltgCtrl.TotalDaylRefPoints;
    5942              : 
    5943          954 :     if (dl->DayltgInteriorIllum_firstTime) {
    5944            7 :         dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5945            7 :         dl->DayltgInteriorIllum_firstTime = false;
    5946              :     }
    5947              : 
    5948              :     // size these for the maximum of the shade deployment order
    5949          954 :     shadeGroupsLums.allocate(dl->maxShadeDeployOrderExtWins);
    5950          956 :     for (auto &shadeGroupLums : shadeGroupsLums) {
    5951            2 :         shadeGroupLums.WDAYIL.allocate(dl->maxControlRefPoints);
    5952            2 :         shadeGroupLums.RDAYIL.allocate(dl->maxControlRefPoints);
    5953              :     }
    5954              : 
    5955              :     // Three arrays to save original clear and dark (fully switched) states'
    5956              :     //  zone/window daylighting properties.
    5957          954 :     tmpDaylFromWinAtRefPt.allocate(dl->maxNumRefPtInAnyDaylCtrl, dl->maxEnclSubSurfaces);
    5958              : 
    5959          954 :     SetPnt.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5960          954 :     dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5961          954 :     GLRNEW.allocate(dl->maxNumRefPtInAnyDaylCtrl);
    5962              : 
    5963         2326 :     for (int iRefPt = 1; iRefPt <= (int)dl->maxNumRefPtInAnyDaylCtrl; ++iRefPt) {
    5964         2744 :         for (int iExtWin = 1; iExtWin <= (int)dl->maxEnclSubSurfaces; ++iExtWin) {
    5965         1372 :             auto &tmpDayl = tmpDaylFromWinAtRefPt(iRefPt, iExtWin);
    5966         1372 :             tmpDayl[iLum_Illum] = tmpDayl[iLum_Back] = tmpDayl[iLum_Source] = {0.0, 0.0};
    5967              :         }
    5968              :     }
    5969              : 
    5970              :     // Initialize reference point illuminance and window background luminance
    5971         2323 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    5972         1369 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    5973         1369 :         SetPnt(IL) = refPt.illumSetPoint;
    5974         1369 :         dl->DaylIllum(IL) = 0.0;
    5975         1369 :         refPt.lums[iLum_Back] = 0.0;
    5976              :     }
    5977              : 
    5978          954 :     if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    5979          887 :         SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    5980          887 :         iSky1 = (int)SkyType::Clear;
    5981          887 :         iSky2 = (int)SkyType::ClearTurbid;
    5982           67 :     } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    5983           56 :         SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    5984           56 :         iSky1 = (int)SkyType::ClearTurbid;
    5985           56 :         iSky2 = (int)SkyType::Intermediate;
    5986              :     } else { // Sky is average of intermediate and overcast
    5987           11 :         SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    5988           11 :         iSky1 = (int)SkyType::Intermediate;
    5989           11 :         iSky2 = (int)SkyType::Overcast;
    5990              :     }
    5991              : 
    5992              :     // First loop over exterior windows associated with this zone. The window may be an exterior window in
    5993              :     // the zone or an exterior window in an adjacent zone that shares an interior window with the zone.
    5994              :     // Find contribution of each window to the daylight illum and to the glare numerator at each reference point.
    5995              :     // Use shading flags set in WindowShadingManager.
    5996         1908 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    5997          954 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    5998              : 
    5999              :         // Added TH 6/29/2009 for thermochromic windows
    6000          954 :         VTRatio = 1.0;
    6001          954 :         if (NREFPT > 0) {
    6002          954 :             int const IConst = s_surf->Surface(IWin).Construction;
    6003          954 :             auto const &construction = state.dataConstruction->Construct(IConst);
    6004          954 :             if (construction.isTCWindow) {
    6005              :                 // For thermochromic windows, daylight and glare factors are always calculated
    6006              :                 //  based on the master construction. They need to be adjusted by the VTRatio, including:
    6007              :                 //  ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
    6008              :                 //  DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
    6009            0 :                 VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
    6010            0 :                 VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
    6011            0 :                 VTRatio = VTNow / VTMaster;
    6012              :             }
    6013              :         }
    6014              : 
    6015         2855 :         bool ShadedOrDiffusingGlassWin = s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
    6016         1901 :                                          (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin));
    6017              : 
    6018          954 :         Real64 wgtCurrHr = state.dataGlobal->WeightNow;
    6019          954 :         Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
    6020              : 
    6021          954 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> SFHR; // Sky source luminance factor for sky type, bare/shaded window
    6022          954 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
    6023          954 :         std::array<Illums, (int)DataSurfaces::WinCover::Num> BFHR; // Sky background luminance factor for sky type, bare/shaded window
    6024              : 
    6025              :         // Loop over reference points
    6026         2323 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    6027              : 
    6028         1369 :             auto const &daylFacCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Bare];
    6029         1369 :             auto const &daylFacPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Bare];
    6030              :             // Daylight factors for current sun position
    6031         1369 :             auto const &illFacCurr = daylFacCurr[iLum_Illum];
    6032         1369 :             auto const &illFacPrev = daylFacPrev[iLum_Illum];
    6033         1369 :             auto &dfhr = DFHR[iWinCover_Bare];
    6034         1369 :             auto const &backFacCurr = daylFacCurr[iLum_Back];
    6035         1369 :             auto const &backFacPrev = daylFacPrev[iLum_Back];
    6036         1369 :             auto &bfhr = BFHR[iWinCover_Bare];
    6037         1369 :             auto const &sourceFacCurr = daylFacCurr[iLum_Source];
    6038         1369 :             auto const &sourceFacPrev = daylFacPrev[iLum_Source];
    6039         1369 :             auto &sfhr = SFHR[iWinCover_Bare];
    6040              : 
    6041         1369 :             auto const &daylFac2Curr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
    6042         1369 :             auto const &daylFac2Prev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
    6043              : 
    6044         1369 :             auto const &illFac2Curr = daylFac2Curr[iLum_Illum];
    6045         1369 :             auto const &illFac2Prev = daylFac2Prev[iLum_Illum];
    6046         1369 :             auto &dfhr2 = DFHR[iWinCover_Shaded];
    6047         1369 :             auto const &backFac2Curr = daylFac2Curr[iLum_Back];
    6048         1369 :             auto const &backFac2Prev = daylFac2Prev[iLum_Back];
    6049         1369 :             auto &bfhr2 = BFHR[iWinCover_Shaded];
    6050         1369 :             auto const &sourceFac2Curr = daylFac2Curr[iLum_Source];
    6051         1369 :             auto const &sourceFac2Prev = daylFac2Prev[iLum_Source];
    6052         1369 :             auto &sfhr2 = SFHR[iWinCover_Shaded];
    6053              : 
    6054              : #ifdef GET_OUT
    6055              :             auto const &daylFacShCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
    6056              :             auto const &daylFacShPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
    6057              : 
    6058              :             auto const &illFacShCurr = daylFacShCurr[iLum_Illum];
    6059              :             auto const &illFacShPrev = daylFacShPrev[iLum_Illum];
    6060              : 
    6061              :             auto const &backFacShCurr = daylFacShCurr[iLum_Back];
    6062              :             auto const &backFacShPrev = daylFacShPrev[iLum_Back];
    6063              : 
    6064              :             auto const &sourceFacShCurr = daylFacShCurr[iLum_Source];
    6065              :             auto const &sourceFacShPrev = daylFacShPrev[iLum_Source];
    6066              : #endif // GET_OUT
    6067         6845 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    6068              : 
    6069              :                 // ===Bare window===
    6070              :                 // Sky daylight factor for sky type (second index), bare/shaded window (first index)
    6071         5476 :                 dfhr.sky[iSky] = VTRatio * (wgtCurrHr * illFacCurr.sky[iSky] + wgtPrevHr * illFacPrev.sky[iSky]);
    6072         5476 :                 bfhr.sky[iSky] = VTRatio * (wgtCurrHr * backFacCurr.sky[iSky] + wgtPrevHr * backFacPrev.sky[iSky]);
    6073         5476 :                 sfhr.sky[iSky] = VTRatio * (wgtCurrHr * sourceFacCurr.sky[iSky] + wgtPrevHr * sourceFacPrev.sky[iSky]);
    6074              : 
    6075         5476 :                 if (ShadedOrDiffusingGlassWin) {
    6076              : 
    6077              :                     // ===Shaded window or window with diffusing glass===
    6078              :                     // Shade, screen, blind with fixed slats, or diffusing glass
    6079           16 :                     dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illFac2Curr.sky[iSky] + wgtPrevHr * illFac2Prev.sky[iSky]);
    6080           16 :                     bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backFac2Curr.sky[iSky] + wgtPrevHr * backFac2Prev.sky[iSky]);
    6081           16 :                     sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceFac2Curr.sky[iSky] + wgtPrevHr * sourceFac2Prev.sky[iSky]);
    6082              :                 } // End of check if window is shaded or has diffusing glass
    6083              :             } // for (iSky)
    6084              : 
    6085              :             // Sun daylight factor for bare/shaded window
    6086         2738 :             DFHR[iWinCover_Bare].sun =
    6087         1369 :                 VTRatio * (wgtCurrHr * (illFacCurr.sun + illFacCurr.sunDisk) + wgtPrevHr * (illFacPrev.sun + illFacPrev.sunDisk));
    6088              : 
    6089              :             // Sun background luminance factor for bare/shaded window
    6090         2738 :             BFHR[iWinCover_Bare].sun =
    6091         1369 :                 VTRatio * (wgtCurrHr * (backFacCurr.sun + backFacCurr.sunDisk) + wgtPrevHr * (backFacPrev.sun + backFacPrev.sunDisk));
    6092              : 
    6093              :             // Sun source luminance factor for bare/shaded window
    6094         2738 :             SFHR[iWinCover_Bare].sun =
    6095         1369 :                 VTRatio * (wgtCurrHr * (sourceFacCurr.sun + sourceFacCurr.sunDisk) + wgtPrevHr * (sourceFacPrev.sun + sourceFacPrev.sunDisk));
    6096              : 
    6097         1369 :             if (ShadedOrDiffusingGlassWin) {
    6098              : 
    6099              :                 // ===Shaded window or window with diffusing glass===
    6100              :                 // Shade, screen, blind with fixed slats, or diffusing glass
    6101            4 :                 DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * illFac2Curr.sun + wgtPrevHr * illFac2Prev.sun);
    6102            4 :                 BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * backFac2Curr.sun + wgtPrevHr * backFac2Prev.sun);
    6103            4 :                 SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * sourceFac2Curr.sun + wgtPrevHr * sourceFac2Prev.sun);
    6104              : 
    6105            4 :                 auto const &surfShade = s_surf->surfShades(IWin);
    6106            4 :                 if (!surfShade.blind.slatBlockBeam) {
    6107            4 :                     DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * illFac2Curr.sunDisk + wgtPrevHr * illFac2Prev.sunDisk);
    6108            4 :                     BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * backFac2Curr.sunDisk + wgtPrevHr * backFac2Prev.sunDisk);
    6109            4 :                     SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * sourceFac2Curr.sunDisk + wgtPrevHr * sourceFac2Prev.sunDisk);
    6110              :                 }
    6111              :             } // End of check if window is shaded or has diffusing glass
    6112              : 
    6113              :             // Get illuminance at ref point from bare and shaded window by
    6114              :             // multiplying daylight factors by exterior horizontal illuminance
    6115              : 
    6116              :             // Adding 0.001 in the following prevents zero HorIllSky in early morning or late evening when sun
    6117              :             // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
    6118         1369 :             auto const &gilskCurr = dl->horIllum[state.dataGlobal->HourOfDay];
    6119         1369 :             auto const &gilskPrev = dl->horIllum[state.dataGlobal->PreviousHour];
    6120              : 
    6121              :             // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
    6122              :             // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
    6123              :             // also calculated in DayltgLuminousEfficacy.
    6124              :             Real64 horIllSky1 =
    6125         1369 :                 state.dataGlobal->WeightNow * gilskCurr.sky[iSky1] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky1] + 0.001;
    6126              :             Real64 horIllSky2 =
    6127         1369 :                 state.dataGlobal->WeightNow * gilskCurr.sky[iSky2] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky2] + 0.001;
    6128              : 
    6129         1369 :             HorIllSkyFac = state.dataEnvrn->HISKF / ((1 - SkyWeight) * horIllSky2 + SkyWeight * horIllSky1);
    6130              : 
    6131         1369 :             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6132         1369 :             auto &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6133         2742 :             for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    6134         2738 :                 auto const &dfhr3 = DFHR[iWinCover];
    6135         2738 :                 auto const &bfhr3 = BFHR[iWinCover];
    6136         2738 :                 auto const &sfhr3 = SFHR[iWinCover];
    6137              : 
    6138              :                 // What is this?
    6139         2738 :                 if (iWinCover == iWinCover_Shaded && !ShadedOrDiffusingGlassWin) {
    6140         1365 :                     break;
    6141              :                 }
    6142              : 
    6143         1373 :                 daylFromWinAtRefPt[iLum_Illum][iWinCover] =
    6144         1373 :                     dfhr3.sun * state.dataEnvrn->HISUNF +
    6145         1373 :                     HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * horIllSky1 + dfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    6146         1373 :                 daylFromWinAtRefPt[iLum_Back][iWinCover] =
    6147         1373 :                     bfhr3.sun * state.dataEnvrn->HISUNF +
    6148         1373 :                     HorIllSkyFac * (bfhr3.sky[iSky1] * SkyWeight * horIllSky1 + bfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    6149         1373 :                 daylFromWinAtRefPt[iLum_Source][iWinCover] =
    6150         1373 :                     sfhr3.sun * state.dataEnvrn->HISUNF +
    6151         1373 :                     HorIllSkyFac * (sfhr3.sky[iSky1] * SkyWeight * horIllSky1 + sfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
    6152              : 
    6153         1373 :                 daylFromWinAtRefPt[iLum_Source][iWinCover] = max(daylFromWinAtRefPt[iLum_Source][iWinCover], 0.0);
    6154              : 
    6155              :                 // Added TH 1/21/2010 - save the original clear and dark (fully switched) states'
    6156              :                 //  zone daylighting values, needed for switachable glazings
    6157         1373 :                 tmpDayl[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
    6158         1373 :                 tmpDayl[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
    6159         1373 :                 tmpDayl[iLum_Source][iWinCover] = daylFromWinAtRefPt[iLum_Source][iWinCover];
    6160              :             } // for for (iWinCover)
    6161              : 
    6162              :         } // End of reference point loop, IL
    6163              :     } // End of first loop over exterior windows associated with this zone
    6164              : 
    6165              :     // Initialize flag that one or more windows has switchable glazing
    6166              :     // control that adjusts visible transmittance to just meet dayltg setpoint
    6167              :     // (and the window has not already been switched)
    6168          954 :     ISWFLG = 0;
    6169              : 
    6170              :     // Second loop over windows. Find total daylight illuminance and background luminance
    6171              :     // for each ref pt from all exterior windows associated with the zone.  Use shading flags.
    6172              :     // This illuminance excludes contribution of inter-reflected illuminance produced by solar
    6173              :     // entering the zone through interior windows (which is calculated in DayltgInterReflIllFrIntWins.
    6174              : 
    6175         1908 :     for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    6176          954 :         int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    6177          954 :         int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6178          954 :         if (s_surf->Surface(IWin).HasShadeControl && ISWFLG == 0) {
    6179            2 :             if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    6180            0 :                 s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) {
    6181            0 :                 ISWFLG = 1;
    6182              :             }
    6183              :         }
    6184              : 
    6185              :         // Determine if illuminance contribution is from bare or shaded window
    6186              :         //  For switchable glazings with shading control type of WSCT_MeetDaylIlumSetp,
    6187              :         //   the shading flag is initialized at GlassConditionallyLightened (20), and
    6188              :         //   the window is initialized at clear state: IS = 1
    6189              :         //  For other windows with glare control, the shading flag is initialized at >10, to be determined
    6190          954 :         WinCover winCover = findWinShadingStatus(state, IWin);
    6191              : 
    6192         2323 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    6193         1369 :             auto &refPt = thisDayltgCtrl.refPts(IL);
    6194         1369 :             dl->DaylIllum(IL) += refPt.extWins(loop).lums[iLum_Illum][(int)winCover];
    6195         1369 :             refPt.lums[iLum_Back] += refPt.extWins(loop).lums[iLum_Back][(int)winCover];
    6196              :         }
    6197              :     } // End of second window loop over exterior windows associated with this zone
    6198              : 
    6199              :     // Optical switching control (e.g. electrochromic glass) to adjust
    6200              :     // window's vis trans downward so daylight level equals or is as
    6201              :     // close as possible to the illuminance setpoint at first reference point.
    6202              :     // Assumes vis trans in the fully switched state is less than that in the
    6203              :     // unswitched state. Assumes some windows in a space may have this control and
    6204              :     // others not.
    6205              : 
    6206          954 :     int count = 0;
    6207              : 
    6208              :     // If daylight illuminance is above setpoint, allow switching
    6209          954 :     if (ISWFLG != 0 && dl->DaylIllum(1) > SetPnt(1)) {
    6210              : 
    6211              :         // array of flags to indicate that previously groups would have already shaded this window
    6212            0 :         Array1D_bool previously_shaded;
    6213            0 :         previously_shaded.dimension(dl->maxDayltgExtWins, false);
    6214              : 
    6215              :         // Third loop over windows.  Get illuminance at ref pt 1 from
    6216              :         // windows that can be switched (DILLSW) with a group and those that can't (DILLUN).
    6217              :         // Windows that can be switched are initially in the unswitched state. For subsequent
    6218              :         // groups the windows in previous groups are fully switched.
    6219            0 :         for (auto &shadeGroupLum : shadeGroupsLums) {
    6220            0 :             shadeGroupLum.switchedWinLum = shadeGroupLum.unswitchedWinLum = 0.0;
    6221              :         }
    6222              : 
    6223            0 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6224            0 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6225            0 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6226            0 :             for (const int IWin : listOfExtWin) {
    6227            0 :                 ++count;
    6228              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6229            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6230            0 :                 if (loop == 0) {
    6231            0 :                     continue;
    6232              :                 }
    6233              : 
    6234            0 :                 if (!s_surf->Surface(IWin).HasShadeControl) {
    6235            0 :                     continue;
    6236              :                 }
    6237              : 
    6238            0 :                 int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6239            0 :                 WinCover winCover = findWinShadingStatus(state, IWin);
    6240              : 
    6241            0 :                 auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Illum];
    6242            0 :                 if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened &&
    6243            0 :                     s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    6244            0 :                     !previously_shaded(loop)) {
    6245            0 :                     shadeGroupLums.switchedWinLum += daylFromWinAtRefPt[(int)winCover];
    6246            0 :                     previously_shaded(loop) = true;
    6247              :                 } else {
    6248            0 :                     shadeGroupLums.unswitchedWinLum +=
    6249            0 :                         !previously_shaded(loop) ? daylFromWinAtRefPt[(int)winCover] : daylFromWinAtRefPt[iWinCover_Shaded];
    6250              :                 }
    6251            0 :             } // for (IWin)
    6252              :         } // for (igroup)
    6253              : 
    6254              :         // Transmittance multiplier
    6255            0 :         for (auto &shadeGroupLums : shadeGroupsLums) {
    6256            0 :             shadeGroupLums.lumRatio = (SetPnt(1) - shadeGroupLums.unswitchedWinLum) / (shadeGroupLums.switchedWinLum + 0.00001);
    6257              :         }
    6258              : 
    6259              :         // ASETIL < 1 means there's enough light, so check for switching
    6260              : 
    6261              :         // Fourth loop over windows to determine which to switch
    6262              :         // iterate in the order that the shades are specified in WindowShadeControl
    6263            0 :         count = 0;
    6264            0 :         breakOuterLoop = false;
    6265            0 :         continueOuterLoop = false;
    6266            0 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6267            0 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6268            0 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6269              : 
    6270            0 :             for (const int IWin : listOfExtWin) {
    6271            0 :                 ++count;
    6272            0 :                 auto const &surfWin = s_surf->SurfaceWindow(IWin);
    6273              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6274            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6275            0 :                 if (loop > 0 && shadeGroupLums.lumRatio < 1.0) {
    6276              : 
    6277            0 :                     int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6278            0 :                     if (!s_surf->Surface(IWin).HasShadeControl) {
    6279            0 :                         continueOuterLoop = true;
    6280            0 :                         continue;
    6281              :                     }
    6282            0 :                     if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::GlassConditionallyLightened ||
    6283            0 :                         s_surf->WindowShadingControl(ICtrl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
    6284            0 :                         continueOuterLoop = true;
    6285            0 :                         continue;
    6286              :                     }
    6287              : 
    6288            0 :                     int const IConst = s_surf->SurfActiveConstruction(IWin);
    6289              :                     // Vis trans at normal incidence of unswitched glass
    6290            0 :                     shadeGroupLums.unswitchedTvis =
    6291            0 :                         Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    6292              : 
    6293              :                     // Vis trans at normal incidence of fully switched glass
    6294            0 :                     int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
    6295            0 :                     shadeGroupLums.switchedTvis =
    6296            0 :                         Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    6297              : 
    6298              :                     // Reset shading flag to indicate that window is shaded by being partially or fully switched
    6299            0 :                     s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
    6300              : 
    6301              :                     // ASETIL < 0 means illuminance from non-daylight-switchable windows exceeds setpoint,
    6302              :                     // so completely switch all daylight-switchable windows to minimize solar gain
    6303            0 :                     if (shadeGroupLums.lumRatio <= 0.0) {
    6304            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
    6305            0 :                         s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
    6306              :                     } else {
    6307              :                         // Case where 0 < ASETIL < 1: darken glass in all
    6308              :                         // daylight-switchable windows to just meet illuminance setpoint
    6309              :                         // From this equation: SETPNT(1) = DILLUN + DILLSW/TVIS1 * VisTransSelected
    6310            0 :                         s_surf->SurfWinVisTransSelected(IWin) =
    6311            0 :                             max(shadeGroupLums.switchedTvis, shadeGroupLums.lumRatio * shadeGroupLums.unswitchedTvis) + 0.000001;
    6312            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = (shadeGroupLums.unswitchedTvis - s_surf->SurfWinVisTransSelected(IWin)) /
    6313            0 :                                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis + 0.000001);
    6314              :                         // bound switching factor between 0 and 1
    6315            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = min(1.0, s_surf->SurfWinSwitchingFactor(IWin));
    6316            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = max(0.0, s_surf->SurfWinSwitchingFactor(IWin));
    6317              :                     }
    6318              : 
    6319              :                     // Adjust daylight quantities based on ratio between switched and unswitched visible transmittance
    6320            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6321              :                         // DaylIllum(IL) and BacLum(IL) were calculated at the clear state:
    6322              :                         //  and need to adjusted for intermediate switched state at VisTransSelected:
    6323            0 :                         auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6324            0 :                         auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6325              : 
    6326            0 :                         VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.unswitchedTvis + 0.000001);
    6327            0 :                         dl->DaylIllum(IL) += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
    6328            0 :                         thisDayltgCtrl.refPts(IL).lums[iLum_Back] += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
    6329              : 
    6330              :                         // Adjust illum, background illum and source luminance for this window in intermediate switched state
    6331              :                         //  for later use in the DayltgGlare calc because SurfaceWindow(IWin)%ShadingFlag = WinShadingType::SwitchableGlazing = 2
    6332            0 :                         VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.switchedTvis + 0.000001);
    6333            0 :                         daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Illum][iWinCover_Shaded];
    6334            0 :                         daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Back][iWinCover_Shaded];
    6335            0 :                         daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Source][iWinCover_Shaded];
    6336              :                     } // for (IL)
    6337              :                 } // if (loop > 0 && ASETIL < 1)
    6338              :                 // If new daylight does not exceed the illuminance setpoint, done, no more checking other groups of switchable glazings
    6339            0 :                 if (dl->DaylIllum(1) <= SetPnt(1)) {
    6340            0 :                     breakOuterLoop = true;
    6341            0 :                     break;
    6342              :                 }
    6343            0 :             } // for (Win)
    6344            0 :             if (breakOuterLoop) {
    6345            0 :                 break;
    6346              :             }
    6347            0 :             if (continueOuterLoop) {
    6348            0 :                 continue;
    6349              :             }
    6350              :         } // for (igroup)
    6351              : 
    6352            0 :     } // ISWFLG /= 0 .AND. DaylIllum(1) > SETPNT(1)
    6353              : 
    6354              :     // loop over windows to do luminance based control
    6355          954 :     count = 0;
    6356          956 :     for (int igroup = 1; igroup <= (int)thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6357            4 :         for (int const IWin : thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1]) {
    6358            2 :             ++count;
    6359            2 :             int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6360            2 :             WindowShadingControlType shCtrlType = s_surf->WindowShadingControl(ICtrl).shadingControlType;
    6361            2 :             if (!((shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffMidNight) ||
    6362              :                   (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffSunset) ||
    6363              :                   (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffNextMorning))) {
    6364            0 :                 continue;
    6365              :             }
    6366              :             // need to map back to the original order of the "loop" to not change all the other data structures
    6367            2 :             int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6368            2 :             if (loop == 0) {
    6369            0 :                 continue;
    6370              :             }
    6371              : 
    6372            2 :             WinShadingType currentFlag = s_surf->SurfWinShadingFlag(IWin);
    6373            2 :             WinShadingType ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
    6374            2 :             if ((currentFlag != WinShadingType::IntShadeConditionallyOff) && (currentFlag != WinShadingType::GlassConditionallyLightened) &&
    6375            0 :                 (currentFlag != WinShadingType::ExtShadeConditionallyOff) && (currentFlag != WinShadingType::IntBlindConditionallyOff) &&
    6376            0 :                 (currentFlag != WinShadingType::ExtBlindConditionallyOff) && (currentFlag != WinShadingType::BGShadeConditionallyOff) &&
    6377              :                 (currentFlag != WinShadingType::BGBlindConditionallyOff)) {
    6378            0 :                 continue;
    6379              :             }
    6380              : 
    6381            2 :             auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums;
    6382            2 :             if (daylFromWinAtRefPt[iLum_Source][iWinCover_Bare] > s_surf->WindowShadingControl(ICtrl).SetPoint2) {
    6383              :                 // shade on if luminance of this window is above setpoint
    6384            1 :                 s_surf->SurfWinShadingFlag(IWin) = ShType;
    6385              :                 // update total illuminance and background luminance
    6386            2 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6387            1 :                     dl->DaylIllum(IL) += daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
    6388            1 :                     thisDayltgCtrl.refPts(IL).lums[iLum_Back] +=
    6389            1 :                         daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
    6390              :                 }
    6391              :             } else {
    6392              :                 // shade off if luminance is below setpoint
    6393            1 :                 s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6394              :             }
    6395            2 :         } // for (IWin)
    6396              :     } // for (igroup)
    6397              : 
    6398              :     // Calculate glare index at each reference point assuming the daylight illuminance setpoint is
    6399              :     //  met at both reference points, either by daylight or electric lights
    6400         2323 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    6401         1369 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    6402         1369 :         BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, refPt.lums[iLum_Back]);
    6403              :         // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,1,loop) for unshaded windows, and
    6404              :         //  ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded windows
    6405         1369 :         refPt.glareIndex = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6406              :     }
    6407              : 
    6408              :     // Check if glare level is less than maximum allowed at each ref pt.  If maximum
    6409              :     // is exceeded at either ref pt, attempt to reduce glare to acceptable level by closing
    6410              :     // shading device on windows that have shades that have not already been closed.
    6411          954 :     GlareFlag = false;
    6412         1579 :     for (auto const &refPt : thisDayltgCtrl.refPts) {
    6413          997 :         if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) {
    6414          372 :             GlareFlag = true;
    6415          372 :             break;
    6416              :         }
    6417              :     }
    6418              : 
    6419          954 :     if (GlareFlag) {
    6420              :         bool blnCycle;
    6421              :         bool GlareOK;
    6422              :         Real64 tmpMult;
    6423              :         // Glare is too high at a ref pt.  Loop through windows.
    6424          372 :         count = 0;
    6425              : 
    6426          372 :         continueOuterLoop = false;
    6427          372 :         for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
    6428            0 :             auto &shadeGroupLums = shadeGroupsLums(igroup);
    6429            0 :             std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
    6430              : 
    6431            0 :             int countBeforeListOfExtWinLoop = count;
    6432            0 :             bool atLeastOneGlareControlIsActive = false;
    6433              : 
    6434            0 :             for (const int IWin : listOfExtWin) {
    6435            0 :                 ++count;
    6436              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6437            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6438            0 :                 if (loop == 0) {
    6439            0 :                     continue;
    6440              :                 }
    6441              : 
    6442            0 :                 auto const &surfWin = s_surf->SurfaceWindow(IWin);
    6443              :                 // Check if window is eligible for glare control
    6444              :                 // TH 1/21/2010. Switchable glazings already in partially switched state
    6445              :                 //  should be allowed to further dim to control glare
    6446              :                 // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
    6447            0 :                 if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
    6448            0 :                     ANY_BLIND(s_surf->SurfWinShadingFlag(IWin))) {
    6449            0 :                     continueOuterLoop = false;
    6450            0 :                     continue;
    6451              :                 }
    6452            0 :                 int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6453            0 :                 if (!s_surf->Surface(IWin).HasShadeControl) {
    6454            0 :                     continueOuterLoop = false;
    6455            0 :                     continue;
    6456              :                 }
    6457            0 :                 if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
    6458            0 :                     atLeastOneGlareControlIsActive = true;
    6459              : 
    6460              :                     // Illuminance (WDAYIL) and background luminance (WBACLU) contribution from this
    6461              :                     // window without shading (IS=1) and with shading (IS=2) for each ref pt
    6462              :                     //  For switchable windows, this may be partially switched rather than fully dark
    6463            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6464            0 :                         auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6465            0 :                         for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    6466            0 :                             shadeGroupLums.WDAYIL(IL)[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
    6467            0 :                             shadeGroupLums.WDAYIL(IL)[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
    6468              :                         }
    6469              :                     }
    6470              : 
    6471              :                     // Recalculate illuminance and glare with shading on this window.
    6472              :                     //  For switchable glazings, this is the fully switched (dark) state
    6473            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6474            0 :                         auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6475            0 :                         auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6476            0 :                         auto const &refPt = thisDayltgCtrl.refPts(IL);
    6477              : 
    6478            0 :                         if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::SwitchableGlazing) {
    6479              :                             // for non switchable glazings or switchable glazings not switched yet (still in clear state)
    6480              :                             //  SurfaceWindow(IWin)%ShadingFlag = WinShadingFlag::GlassConditionallyLightened
    6481            0 :                             rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Bare] + wdayil[iLum_Illum][iWinCover_Shaded];
    6482            0 :                             rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Bare] + wdayil[iLum_Back][iWinCover_Shaded];
    6483              :                         } else {
    6484              :                             // switchable glazings already in partially switched state when calc the RDAYIL(IL) & RBACLU(IL)
    6485            0 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(loop, IL);
    6486            0 :                             rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Shaded] + tmpDayl[iLum_Illum][iWinCover_Shaded];
    6487            0 :                             rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Shaded] + tmpDayl[iLum_Back][iWinCover_Shaded];
    6488              :                         }
    6489              :                     } // for (IL)
    6490              : 
    6491            0 :                     if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) {
    6492            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
    6493            0 :                     } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) {
    6494            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntShade;
    6495            0 :                     } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) {
    6496            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtShade;
    6497            0 :                     } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) {
    6498            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntBlind;
    6499            0 :                     } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff) {
    6500            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtBlind;
    6501            0 :                     } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGShadeConditionallyOff) {
    6502            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGShade;
    6503            0 :                     } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGBlindConditionallyOff) {
    6504            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGBlind;
    6505              :                     }
    6506              : 
    6507              :                     // For switchable glazings, it is switched to fully dark state,
    6508              :                     // update ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for use in DayltgGlare
    6509            0 :                     if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6510            0 :                         for (int IL = 1; IL <= NREFPT; ++IL) {
    6511            0 :                             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6512            0 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6513              : 
    6514            0 :                             daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6515            0 :                             daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6516            0 :                             daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6517              :                         }
    6518              : 
    6519            0 :                         int const IConst = s_surf->SurfActiveConstruction(IWin);
    6520              :                         // Vis trans at normal incidence of unswitched glass
    6521            0 :                         shadeGroupLums.unswitchedTvis =
    6522            0 :                             Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
    6523              : 
    6524              :                         // Vis trans at normal incidence of fully switched glass
    6525            0 :                         int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
    6526            0 :                         shadeGroupLums.switchedTvis =
    6527            0 :                             Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    6528              :                     } // if (switchableGlazing)
    6529              :                 } // if (GlareControlIsActive)
    6530            0 :             } // for (IWin)
    6531            0 :             if (continueOuterLoop) {
    6532            0 :                 continue;
    6533              :             }
    6534              : 
    6535            0 :             if (atLeastOneGlareControlIsActive) {
    6536              : 
    6537              :                 // Re-calc daylight and glare at shaded state. For switchable glazings, it is the fully dark state.
    6538            0 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6539            0 :                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, shadeGroupLums.RDAYIL(IL)[iLum_Back]);
    6540              :                     // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded state
    6541            0 :                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6542              :                 }
    6543              : 
    6544              :                 // Check if the shading did not improve the glare conditions
    6545              :                 //
    6546              :                 // blnCycle when true resets the specific window to its non-shaded condition. A later comment says
    6547              :                 //      Shading this window has not improved the glare situation.
    6548              :                 //      Reset shading flag to no shading condition, go to next window.
    6549              :                 //
    6550              :                 // 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
    6551              :                 // reset it. For each reference point, if the original glare was too high but ok at other reference points and the glare gets
    6552              :                 // lower at the reference and stays ok at the other reference points it is good, don't reset it.
    6553              :                 //
    6554              :                 // The old comments when there were only two reference points were:
    6555              :                 //     One ref pt;  go to next window if glare has increased.
    6556              :                 //     Two ref pts.  There are three cases depending on glare values.
    6557              :                 //         (1) Initial glare too high at both ref pts.  Deploy shading on
    6558              :                 //             this window if this decreases glare at both ref pts.
    6559              :                 //         (2) Initial glare too high only at first ref pt.  Deploy shading
    6560              :                 //             on this window if glare at first ref pt decreases and
    6561              :                 //             glare at second ref pt stays below max.
    6562              :                 //         (3) Initial glare too high at second ref pt.  Deploy shading if glare
    6563              :                 //             at second ref pt decreases and glare at first ref pt stays below max.
    6564              :                 //
    6565              :                 // The approach taken is just to count the number of reference points that fulfill the individual requirements and see if it
    6566              :                 // covers all the reference points.
    6567            0 :                 int numRefPtOldAboveMaxNewBelowOld = 0;
    6568            0 :                 int numRefPtOldBelowMaxNewBelowMax = 0;
    6569            0 :                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6570            0 :                     auto const &refPt = thisDayltgCtrl.refPts(IL);
    6571              : 
    6572            0 :                     if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= refPt.glareIndex) {
    6573            0 :                         ++numRefPtOldAboveMaxNewBelowOld;
    6574            0 :                     } else if (refPt.glareIndex <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= thisDayltgCtrl.MaxGlareallowed) {
    6575            0 :                         ++numRefPtOldBelowMaxNewBelowMax;
    6576              :                     }
    6577              :                 }
    6578            0 :                 blnCycle = true;
    6579            0 :                 if ((numRefPtOldAboveMaxNewBelowOld + numRefPtOldBelowMaxNewBelowMax) == NREFPT) {
    6580            0 :                     blnCycle = false;
    6581              :                 }
    6582              :             }
    6583              : 
    6584              :             // restore the count to the value prior to the last loop through the group of exterior windows
    6585            0 :             count = countBeforeListOfExtWinLoop;
    6586            0 :             breakOuterLoop = false;
    6587              : 
    6588            0 :             for (const int IWin : listOfExtWin) {
    6589            0 :                 ++count;
    6590              :                 // need to map back to the original order of the "loop" to not change all the other data structures
    6591            0 :                 int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
    6592            0 :                 if (loop == 0) {
    6593            0 :                     continue;
    6594              :                 }
    6595              : 
    6596              :                 // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
    6597            0 :                 if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
    6598            0 :                     ANY_BLIND(s_surf->SurfWinShadingFlag(IWin))) {
    6599            0 :                     continue;
    6600              :                 }
    6601              : 
    6602            0 :                 int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    6603            0 :                 if (!s_surf->Surface(IWin).HasShadeControl) {
    6604            0 :                     continue;
    6605              :                 }
    6606            0 :                 if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
    6607              : 
    6608              :                     // Shading this window has not improved the glare situation.
    6609              :                     // Reset shading flag to no shading condition, go to next window.
    6610            0 :                     if (blnCycle) {
    6611              :                         //  for switchable glazings, reset properties to clear state or partial switched state?
    6612            0 :                         if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6613            0 :                             s_surf->SurfWinSwitchingFactor(IWin) = 0.0;
    6614            0 :                             s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.unswitchedTvis;
    6615              : 
    6616              :                             // RESET properties for fully dark state
    6617            0 :                             for (int IL = 1; IL <= NREFPT; ++IL) {
    6618            0 :                                 auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6619            0 :                                 auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6620            0 :                                 daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6621            0 :                                 daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6622            0 :                                 daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6623              :                             }
    6624              :                         }
    6625              : 
    6626            0 :                         s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6627            0 :                         continue;
    6628            0 :                     }
    6629              : 
    6630              :                     // Shading this window has improved the glare situation.
    6631              :                     // Reset background luminance, glare index, and daylight illuminance at each ref pt.
    6632              :                     // For switchable glazings, this is fully switched, dark state
    6633            0 :                     for (int IL = 1; IL <= NREFPT; ++IL) {
    6634            0 :                         auto &refPt = thisDayltgCtrl.refPts(IL);
    6635            0 :                         refPt.lums[iLum_Back] = shadeGroupLums.RDAYIL(IL)[iLum_Back];
    6636            0 :                         refPt.glareIndex = GLRNEW(IL);
    6637            0 :                         dl->DaylIllum(IL) = shadeGroupLums.RDAYIL(IL)[iLum_Illum];
    6638              :                     }
    6639              : 
    6640              :                     // TH comments (5/22/2009): seems for EC windows, if the calculated glare exceeds the max setpoint,
    6641              :                     //  the EC windows will be reset to fully dark state which significantly reduces the available daylight.
    6642              :                     //  A better way is to dim the EC windows as necessary just to meet the glare index, which will still
    6643              :                     //  provide more daylight while not exceeding the max glare! The question is then how to set the
    6644              :                     //  SwitchingFactor to just meet the glare index.
    6645              :                     //  This was addressed in CR 7984 for E+ 5.0. 1/19/2010
    6646              : 
    6647              :                     // If switchable glazing, set switching factor to 1: fully switched.
    6648            0 :                     if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    6649              :                         // tmpSWFactor0 = SurfaceWindow( IWin ).SwitchingFactor; // save original
    6650              :                         // switching  factor
    6651              :                         ////Unused Set but never used
    6652            0 :                         s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
    6653            0 :                         s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
    6654              : 
    6655              :                         // restore fully dark values
    6656            0 :                         for (int IL = 1; IL <= NREFPT; ++IL) {
    6657            0 :                             auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
    6658            0 :                             auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6659            0 :                             auto &wdayil = shadeGroupLums.WDAYIL(IL);
    6660            0 :                             wdayil[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6661            0 :                             wdayil[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6662            0 :                             daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
    6663            0 :                             daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
    6664            0 :                             daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
    6665              :                         }
    6666              :                     }
    6667              : 
    6668              :                     // Check if glare now acceptable at each ref pt.
    6669            0 :                     GlareOK = false;
    6670            0 :                     if (NREFPT == 1) {
    6671            0 :                         if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed) {
    6672            0 :                             GlareOK = true;
    6673              :                         }
    6674            0 :                     } else if (NREFPT > 1) {
    6675            0 :                         if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed &&
    6676            0 :                             thisDayltgCtrl.refPts(2).glareIndex <= thisDayltgCtrl.MaxGlareallowed) {
    6677            0 :                             GlareOK = true;
    6678              :                         }
    6679              :                     }
    6680              : 
    6681            0 :                     if (GlareOK) {
    6682            0 :                         if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing &&
    6683            0 :                             s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp) {
    6684              :                             // Added TH 1/14/2010
    6685              :                             // Only for switchable glazings with MeetDaylightIlluminanceSetpoint control
    6686              :                             // The glazing is in fully dark state, it might lighten a bit to provide more daylight
    6687              :                             //  while meeting maximum discomfort glare index
    6688              :                             // Iteration to find the right switching factor meeting the glare index
    6689              : 
    6690              :                             // get fully dark state values
    6691            0 :                             Real64 tmpSWSL1 = tmpDaylFromWinAtRefPt(1, loop)[iLum_Source][iWinCover_Shaded];
    6692            0 :                             Real64 tmpSWSL2 = (NREFPT > 1) ? tmpDaylFromWinAtRefPt(2, loop)[iLum_Source][iWinCover_Shaded] : 0.0;
    6693              : 
    6694              :                             // use simple fixed step search in iteraction, can be improved in future
    6695            0 :                             Real64 tmpSWFactor = 1.0 - tmpSWIterStep;
    6696            0 :                             while (tmpSWFactor > 0) {
    6697              :                                 // calc new glare at new switching state
    6698            0 :                                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6699            0 :                                     auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6700            0 :                                     auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6701            0 :                                     auto &refPt = thisDayltgCtrl.refPts(IL);
    6702            0 :                                     rdayil[iLum_Illum] =
    6703            0 :                                         dl->DaylIllum(IL) +
    6704            0 :                                         (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6705            0 :                                     rdayil[iLum_Back] =
    6706            0 :                                         refPt.lums[iLum_Back] +
    6707            0 :                                         (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6708            0 :                                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
    6709              :                                     // needs to update SourceLumFromWinAtRefPt(IL,2,loop) before re-calc DayltgGlare
    6710            0 :                                     tmpMult = (shadeGroupLums.unswitchedTvis -
    6711            0 :                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6712            0 :                                               shadeGroupLums.switchedTvis;
    6713            0 :                                     refPt.extWins(loop).lums[iLum_Source][iWinCover_Shaded] = ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
    6714              :                                     // Calc new glare
    6715            0 :                                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6716              :                                 } // for (IL)
    6717              : 
    6718              :                                 // Check whether new glare is OK
    6719            0 :                                 GlareOK = false;
    6720            0 :                                 if (NREFPT == 1) {
    6721            0 :                                     if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed) {
    6722            0 :                                         GlareOK = true;
    6723              :                                     }
    6724            0 :                                 } else if (NREFPT > 1) {
    6725            0 :                                     if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(2) <= thisDayltgCtrl.MaxGlareallowed) {
    6726            0 :                                         GlareOK = true;
    6727              :                                     }
    6728              :                                 }
    6729              : 
    6730            0 :                                 if (GlareOK) {
    6731            0 :                                     if (tmpSWFactor >= tmpSWIterStep) {
    6732              :                                         // Continue to lighten the glazing
    6733            0 :                                         tmpSWFactor -= tmpSWIterStep;
    6734            0 :                                         continue;
    6735              :                                     } else {
    6736              :                                         // Glare still OK but glazing already in clear state, no more lighten
    6737            0 :                                         breakOuterLoop = true;
    6738            0 :                                         break;
    6739              :                                     }
    6740              :                                 } else {
    6741              :                                     // Glare too high, exit and use previous switching state
    6742            0 :                                     tmpSWFactor += tmpSWIterStep;
    6743            0 :                                     breakOuterLoop = true;
    6744            0 :                                     break;
    6745              :                                 }
    6746              :                             } // if (tmpSWFactor > 0)
    6747              : 
    6748              :                             // Final re-calculation if needed
    6749            0 :                             if (!GlareOK) {
    6750              :                                 // Glare too high, use previous state and re-calc
    6751            0 :                                 for (int IL = 1; IL <= NREFPT; ++IL) {
    6752            0 :                                     auto &rdayil = shadeGroupLums.RDAYIL(IL);
    6753            0 :                                     auto const &wdayil = shadeGroupLums.WDAYIL(IL);
    6754            0 :                                     rdayil[iLum_Illum] =
    6755            0 :                                         dl->DaylIllum(IL) +
    6756            0 :                                         (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6757            0 :                                     rdayil[iLum_Back] =
    6758            0 :                                         thisDayltgCtrl.refPts(IL).lums[iLum_Back] +
    6759            0 :                                         (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
    6760            0 :                                     BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
    6761              : 
    6762              :                                     // needs to update SourceLumFromWinAtRefPt(IL,2,IWin) before re-calc DayltgGlare
    6763            0 :                                     tmpMult = (shadeGroupLums.unswitchedTvis -
    6764            0 :                                                (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6765            0 :                                               shadeGroupLums.switchedTvis;
    6766            0 :                                     thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Source][iWinCover_Shaded] =
    6767            0 :                                         ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
    6768            0 :                                     GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
    6769              :                                 }
    6770              :                             }
    6771              : 
    6772              :                             // Update final results
    6773            0 :                             for (int IL = 1; IL <= NREFPT; ++IL) {
    6774            0 :                                 auto &refPt = thisDayltgCtrl.refPts(IL);
    6775            0 :                                 auto const &rdayil = shadeGroupLums.RDAYIL(IL);
    6776            0 :                                 refPt.lums[iLum_Back] = rdayil[iLum_Back];
    6777            0 :                                 refPt.glareIndex = GLRNEW(IL);
    6778            0 :                                 dl->DaylIllum(IL) = rdayil[iLum_Illum];
    6779              : 
    6780            0 :                                 tmpMult =
    6781            0 :                                     (shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
    6782            0 :                                     shadeGroupLums.switchedTvis;
    6783              :                                 // update report variables
    6784            0 :                                 auto &daylFromWinAtRefPt = refPt.extWins(loop).lums;
    6785            0 :                                 auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
    6786            0 :                                 daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded] * tmpMult;
    6787            0 :                                 daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded] * tmpMult;
    6788              :                             }
    6789            0 :                             s_surf->SurfWinSwitchingFactor(IWin) = tmpSWFactor;
    6790            0 :                             s_surf->SurfWinVisTransSelected(IWin) =
    6791            0 :                                 shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor;
    6792              : 
    6793              :                         } else {
    6794              :                             // For un-switchable glazing or switchable glazing but not MeetDaylightIlluminaceSetpoint control,
    6795              :                             // it is in shaded state and glare is ok - job is done, exit the window loop - IWin
    6796            0 :                             breakOuterLoop = true;
    6797            0 :                             break;
    6798              :                         }
    6799              :                     } // if (glareOK)
    6800              :                 } // if (glareControlIsActive)
    6801            0 :             } // for (IWin)
    6802            0 :             if (breakOuterLoop) {
    6803            0 :                 break;
    6804              :             }
    6805              :         } // for (igroup)
    6806              :     } // if (GlareFlag)
    6807              : 
    6808              :     // Loop again over windows and reset remaining shading flags that
    6809              :     // are 10 or higher (i.e., conditionally off) to off
    6810         1908 :     for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6811          954 :         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    6812         1908 :         for (int IWin = thisSpace.WindowSurfaceFirst; IWin <= thisSpace.WindowSurfaceLast; ++IWin) {
    6813          954 :             if (s_surf->Surface(IWin).ExtBoundCond != ExternalEnvironment) {
    6814            0 :                 continue;
    6815              :             }
    6816          954 :             bool anyGlareControl = (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) ||
    6817          954 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) ||
    6818          954 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) ||
    6819         2862 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) ||
    6820          954 :                                    (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff);
    6821          954 :             if (anyGlareControl) {
    6822            0 :                 s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
    6823              :             }
    6824              :         }
    6825          954 :     }
    6826              : 
    6827              :     // Variables for reporting
    6828         2323 :     for (int IL = 1; IL <= NREFPT; ++IL) {
    6829         1369 :         auto &refPt = thisDayltgCtrl.refPts(IL);
    6830         1369 :         refPt.lums[iLum_Illum] = dl->DaylIllum(IL);
    6831              : 
    6832              :         // added TH 12/2/2008
    6833         1369 :         refPt.timeExceedingGlareIndexSetPoint = (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) ? state.dataGlobal->TimeStepZone : 0.0;
    6834              :         // added TH 7/6/2009
    6835         1369 :         refPt.timeExceedingDaylightIlluminanceSetPoint = (dl->DaylIllum(IL) > refPt.illumSetPoint) ? state.dataGlobal->TimeStepZone : 0.0;
    6836              :     }
    6837          954 : } // DayltgInteriorIllum()
    6838              : 
    6839            0 : void DayltgInteriorTDDIllum(EnergyPlusData &state)
    6840              : {
    6841              : 
    6842              :     // SUBROUTINE INFORMATION:
    6843              :     //       AUTHOR         Linda Lawrie
    6844              :     //       DATE WRITTEN   October 2006
    6845              : 
    6846              :     // PURPOSE OF THIS SUBROUTINE:
    6847              :     // Calculate the TDD Pipe illuminance values
    6848            0 :     auto &dl = state.dataDayltg;
    6849              : 
    6850              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    6851              :     int iSky1; // Sky type index values for averaging two sky types
    6852              :     int iSky2;
    6853              :     Real64 SkyWeight; // Weighting factor used to average two different sky types
    6854              : 
    6855            0 :     if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    6856            0 :         SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    6857            0 :         iSky1 = (int)SkyType::Clear;
    6858            0 :         iSky2 = (int)SkyType::ClearTurbid;
    6859            0 :     } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    6860            0 :         SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    6861            0 :         iSky1 = (int)SkyType::ClearTurbid;
    6862            0 :         iSky2 = (int)SkyType::Intermediate;
    6863              :     } else { // Sky is average of intermediate and overcast
    6864            0 :         SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    6865            0 :         iSky1 = (int)SkyType::Intermediate;
    6866            0 :         iSky2 = (int)SkyType::Overcast;
    6867              :     }
    6868              : 
    6869              :     // Calculate and report TDD visible transmittances
    6870            0 :     for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
    6871              : 
    6872            0 :         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam =
    6873            0 :             state.dataGlobal->WeightNow * dl->TDDTransVisBeam(state.dataGlobal->HourOfDay, PipeNum) +
    6874            0 :             state.dataGlobal->WeightPreviousHour * dl->TDDTransVisBeam(state.dataGlobal->PreviousHour, PipeNum);
    6875              : 
    6876            0 :         auto const &tddFluxIncCurr = dl->TDDFluxInc(state.dataGlobal->HourOfDay, PipeNum);
    6877            0 :         auto const &tddFluxIncPrev = dl->TDDFluxInc(state.dataGlobal->PreviousHour, PipeNum);
    6878              : 
    6879            0 :         auto const &tddFluxTransCurr = dl->TDDFluxTrans(state.dataGlobal->HourOfDay, PipeNum);
    6880            0 :         auto const &tddFluxTransPrev = dl->TDDFluxTrans(state.dataGlobal->PreviousHour, PipeNum);
    6881              : 
    6882            0 :         Illums TDDTransVisDiff;
    6883            0 :         for (int iSky = iSky1; iSky <= iSky2; ++iSky) {
    6884            0 :             Real64 tddTransVisDiffCurr = (tddFluxIncCurr.sky[iSky] > 0.0) ? (tddFluxTransCurr.sky[iSky] / tddFluxIncCurr.sky[iSky]) : 0.0;
    6885            0 :             Real64 tddTransVisDiffPrev = (tddFluxIncPrev.sky[iSky] > 0.0) ? (tddFluxTransPrev.sky[iSky] / tddFluxIncPrev.sky[iSky]) : 0.0;
    6886              : 
    6887            0 :             TDDTransVisDiff.sky[iSky] =
    6888            0 :                 state.dataGlobal->WeightNow * tddTransVisDiffCurr + state.dataGlobal->WeightPreviousHour * tddTransVisDiffPrev;
    6889              :         } // for (iSky)
    6890              : 
    6891            0 :         state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff =
    6892            0 :             SkyWeight * TDDTransVisDiff.sky[iSky1] + (1.0 - SkyWeight) * TDDTransVisDiff.sky[iSky2];
    6893              :     } // for (PipeNum)
    6894            0 : } // DayltgInteriorTDDIllum()
    6895              : 
    6896        71219 : void DayltgElecLightingControl(EnergyPlusData &state)
    6897              : {
    6898              : 
    6899              :     // SUBROUTINE INFORMATION:
    6900              :     //       AUTHOR         Fred Winkelmann
    6901              :     //       DATE WRITTEN   July 1997
    6902              :     //       MODIFIED       Mar 2004, FCW: add inter-reflected illuminance from interior windows to DaylIllum
    6903              :     //                      Apr 2004, FCW: move CALL ReportIllumMap from DayltgInteriorIllum2 (DayltgInteriorMapIllum)
    6904              :     //                      Apr 2010, BG NREL: remove inter-reflected illuminance to stop double counting
    6905              :     //                      Aug 2012, BG NREL: added availability schedule logic
    6906              : 
    6907              :     // PURPOSE OF THIS SUBROUTINE:
    6908              :     // For a daylit space, determines lighting power reduction factor due to
    6909              :     // daylighting for different lighting control systems.
    6910              : 
    6911              :     // Called by InitSurfaceHeatBalance.
    6912              : 
    6913              :     // REFERENCES:
    6914              :     // Based on DOE-2.1E subroutine DLTSYS.
    6915        71219 :     auto &dl = state.dataDayltg;
    6916              : 
    6917        71219 :     if (dl->daylightControl.empty()) {
    6918        70270 :         return;
    6919              :     }
    6920              :     // Reset space power reduction factors
    6921         1898 :     for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
    6922          949 :         dl->spacePowerReductionFactor(spaceNum) = 1.0;
    6923              :     }
    6924              : 
    6925         1898 :     for (auto &thisDayltgCtrl : dl->daylightControl) {
    6926              : 
    6927          949 :         if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
    6928              :             // Set space power reduction factors
    6929            0 :             if (thisDayltgCtrl.PowerReductionFactor < 1.0) {
    6930            0 :                 if (thisDayltgCtrl.spaceIndex > 0) {
    6931              :                     // This is a space-level daylighting control
    6932            0 :                     dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = thisDayltgCtrl.PowerReductionFactor;
    6933              :                 } else {
    6934              :                     // This is a zone-level daylighting control
    6935            0 :                     for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    6936            0 :                         dl->spacePowerReductionFactor(spaceNum) = thisDayltgCtrl.PowerReductionFactor;
    6937            0 :                     }
    6938              :                 }
    6939              :             }
    6940            0 :             continue;
    6941            0 :         }
    6942              : 
    6943              :         // Electric lighting power reduction factor for a given daylighting control
    6944          949 :         Real64 &TotReduction = thisDayltgCtrl.PowerReductionFactor;
    6945          949 :         TotReduction = 0.0;
    6946          949 :         Real64 ZFTOT = 0.0;
    6947              : 
    6948              :         // check if scheduled to be available
    6949          949 :         if (thisDayltgCtrl.availSched->getCurrentVal() > 0.0) {
    6950              : 
    6951              :             // Loop over reference points
    6952         2305 :             for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
    6953         1356 :                 auto &refPt = thisDayltgCtrl.refPts(IL);
    6954              :                 // Total fraction of zone that is daylit
    6955         1356 :                 ZFTOT += refPt.fracZoneDaylit;
    6956              : 
    6957         1356 :                 dl->DaylIllum(IL) = refPt.lums[iLum_Illum];
    6958         1356 :                 Real64 FL = 0.0;
    6959         1356 :                 if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
    6960          276 :                     FL = (refPt.illumSetPoint - dl->DaylIllum(IL)) / refPt.illumSetPoint;
    6961              :                 }
    6962              : 
    6963              :                 // BRANCH ON LIGHTING SYSTEM TYPE
    6964         1356 :                 LtgCtrlType LSYSTP = thisDayltgCtrl.LightControlType;
    6965         1356 :                 Real64 FP = 0.0;
    6966         1356 :                 if (LSYSTP != LtgCtrlType::Stepped) {
    6967              :                     // Continuously dimmable system with linear power curve
    6968              :                     // Fractional output power required to meet setpoint
    6969         1350 :                     FP = 1.0;
    6970              :                     // LIGHT-CTRL-TYPE = CONTINUOUS (LSYSTP = 1)
    6971         1350 :                     if (FL <= thisDayltgCtrl.MinLightFraction) {
    6972         1138 :                         FP = thisDayltgCtrl.MinPowerFraction;
    6973              :                     }
    6974              :                     // LIGHT-CTRL-TYPE = CONTINUOUS/OFF (LSYSTP = 3)
    6975         1350 :                     if (FL <= thisDayltgCtrl.MinLightFraction && LSYSTP == LtgCtrlType::ContinuousOff) {
    6976            0 :                         FP = 0.0;
    6977              :                     }
    6978         1350 :                     if (FL > thisDayltgCtrl.MinLightFraction && FL < 1.0) {
    6979          212 :                         FP = (FL + (1.0 - FL) * thisDayltgCtrl.MinPowerFraction - thisDayltgCtrl.MinLightFraction) /
    6980          212 :                              (1.0 - thisDayltgCtrl.MinLightFraction);
    6981              :                     }
    6982              : 
    6983              :                 } else { // LSYSTP = 2
    6984              :                     // Stepped system
    6985            6 :                     FP = 0.0;
    6986              :                     // #9060: Use a tolerance, otherwise at very low (< 1e-12) daylighting conditions, you can get a multiplier > 1.0
    6987            6 :                     if (dl->DaylIllum(IL) < 0.1) {
    6988            2 :                         FP = 1.0;
    6989            4 :                     } else if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
    6990            4 :                         FP = double(int(thisDayltgCtrl.LightControlSteps * FL) + 1) / double(thisDayltgCtrl.LightControlSteps);
    6991              :                     }
    6992              : 
    6993            6 :                     if (thisDayltgCtrl.LightControlProbability < 1.0) {
    6994              :                         // Manual operation.  Occupant sets lights one level too high a fraction of the time equal to
    6995              :                         // 1. - ZoneDaylight(ZoneNum)%LightControlProbability.  RANDOM_NUMBER returns a random number
    6996              :                         // between 0 and 1.
    6997              :                         Real64 XRAN;
    6998            2 :                         RANDOM_NUMBER(XRAN);
    6999            2 :                         if (XRAN >= thisDayltgCtrl.LightControlProbability) {
    7000              :                             // Set level one higher
    7001            2 :                             if (FP < 1.0) {
    7002            1 :                                 FP += (1.0 / double(thisDayltgCtrl.LightControlSteps));
    7003              :                             }
    7004              :                         } // XRAN
    7005              :                     } // Light Control Probability < 1
    7006              :                 } // Lighting System Type
    7007              : 
    7008         1356 :                 refPt.powerReductionFactor = FP;
    7009              : 
    7010              :                 // Accumulate net ltg power reduction factor for entire zone
    7011         1356 :                 TotReduction += refPt.powerReductionFactor * refPt.fracZoneDaylit;
    7012              : 
    7013              :             } // End of loop over reference points, IL
    7014              : 
    7015              :             // Correct for fraction of zone (1-ZFTOT) not controlled by
    7016              :             // the reference points.  For this fraction (which is usually zero),
    7017              :             // the electric lighting is unaffected and the power reduction
    7018              :             // factor is therefore 1.0.
    7019          949 :             TotReduction += (1.0 - ZFTOT);
    7020              :         } else { // controls not currently available
    7021            0 :             TotReduction = 1.0;
    7022              :         }
    7023              : 
    7024              :         // Set space power reduction factors
    7025          949 :         if (thisDayltgCtrl.spaceIndex > 0) {
    7026              :             // This is a space-level daylighting control
    7027            0 :             dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = TotReduction;
    7028              :         } else {
    7029              :             // This is a zone-level daylighting control
    7030         1898 :             for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
    7031          949 :                 dl->spacePowerReductionFactor(spaceNum) = TotReduction;
    7032          949 :             }
    7033              :         }
    7034              :     } // end daylighting control loop
    7035              : 
    7036              :     //  IF(TotIllumMaps > 0 .and. .not. DoingSizing .and. .not. WarmupFlag .and. .not. KickoffSimulation) THEN
    7037          949 :     if ((int)dl->illumMaps.size() > 0 && !state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
    7038          118 :         for (int mapNum = 1; mapNum <= (int)dl->illumMaps.size(); ++mapNum) {
    7039           59 :             auto &illumMap = dl->illumMaps(mapNum);
    7040           59 :             if (state.dataGlobal->TimeStep == 1) {
    7041           16 :                 dl->mapResultsToReport = false;
    7042              :             }
    7043         5959 :             for (auto &refPt : illumMap.refPts) {
    7044         5900 :                 refPt.lumsHr[iLum_Illum] += refPt.lums[iLum_Illum] / double(state.dataGlobal->TimeStepsInHour);
    7045         5900 :                 if (refPt.lumsHr[iLum_Illum] > 0.0) {
    7046         5900 :                     dl->mapResultsToReport = true;
    7047         5900 :                     dl->mapResultsReported = true;
    7048              :                 }
    7049              :             }
    7050           59 :             ReportIllumMap(state, mapNum);
    7051           59 :             if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
    7052         1515 :                 for (auto &refPt : illumMap.refPts) {
    7053         1500 :                     refPt.lumsHr[iLum_Illum] = refPt.lums[iLum_Illum] = 0.0;
    7054              :                 }
    7055              :             }
    7056              :         } // for (mapNum)
    7057              :     } // if (MapSize > 0)
    7058              : } // DayltgElecLightingControl()
    7059              : 
    7060          459 : Real64 DayltgGlarePositionFactor(Real64 X, // Lateral and vertical distance of luminous window element from
    7061              :                                  Real64 Y)
    7062              : {
    7063              : 
    7064              :     // SUBROUTINE INFORMATION:
    7065              :     //       AUTHOR         Fred Winkelmann
    7066              :     //       DATE WRITTEN   July 1997
    7067              : 
    7068              :     // PURPOSE OF THIS SUBROUTINE:
    7069              :     // by table interpolation, evaluates the
    7070              :     // Hopkinson position factor used in glare calculation
    7071              :     // (Hopkinson, Petherbridge, AND Longmore -- Daylighting,
    7072              :     // London, 1966, PP 307, 323).  X (Y) is the lateral
    7073              :     // (vertical) distance of luminous window element from
    7074              :     // horizontal line of vision, divided by horizontal distance
    7075              :     // from eye of observer. The array PF contains values of
    7076              :     // the position factor for X = 0, 0.5, 1.0, 1.5, 2.0, 2.5,
    7077              :     // and 3.0 and Y = 0, 0.5, 1.0, 1.5, 2.0. Called by CalcDayltgCoefficients.
    7078              : 
    7079              :     // REFERENCES:
    7080              :     // Based on DOE-2.1E subroutine DPFAC.
    7081              : 
    7082              :     // Position factor array
    7083              :     static constexpr std::array<std::array<Real64, 7>, 5> PF = {{
    7084              :         {1.00, 0.492, 0.226, 0.128, 0.081, 0.061, 0.057},
    7085              :         {0.123, 0.119, 0.065, 0.043, 0.029, 0.026, 0.023},
    7086              :         {0.019, 0.026, 0.019, 0.016, 0.014, 0.011, 0.011},
    7087              :         {0.008, 0.008, 0.008, 0.008, 0.008, 0.006, 0.006},
    7088              :         {0.0, 0.0, 0.003, 0.003, 0.003, 0.003, 0.003},
    7089              :     }};
    7090              : 
    7091          459 :     if (X < 0.0 || X >= 3.0) {
    7092            8 :         return 0.0;
    7093              :     }
    7094          451 :     if (Y < 0.0 || Y >= 2.0) {
    7095            0 :         return 0.0;
    7096              :     }
    7097              : 
    7098          451 :     int IX = 1 + int(2.0 * X);
    7099          451 :     int IY = 1 + int(2.0 * Y);
    7100          451 :     Real64 X1 = 0.5 * double(IX - 1);
    7101          451 :     Real64 Y1 = 0.5 * double(IY - 1);
    7102          451 :     Real64 FA = PF[IY - 1][IX - 1] + 2.0 * (X - X1) * (PF[IY - 1][IX] - PF[IY - 1][IX - 1]);
    7103          451 :     Real64 FB = PF[IY][IX - 1] + 2.0 * (X - X1) * (PF[IY][IX] - PF[IY][IX - 1]);
    7104          451 :     return FA + 2.0 * (Y - Y1) * (FB - FA);
    7105              : } // DayltgGlarePositionFactor()
    7106              : 
    7107         2066 : void DayltgInterReflectedIllum(EnergyPlusData &state,
    7108              :                                int const ISunPos, // Sun position counter; used to avoid calculating various
    7109              :                                int const IHR,     // Hour of day
    7110              :                                int const enclNum, // Daylighting enclosure index
    7111              :                                int const IWin     // Window index
    7112              : )
    7113              : {
    7114              : 
    7115              :     // SUBROUTINE INFORMATION:
    7116              :     //       AUTHOR         Fred Winkelmann
    7117              :     //       DATE WRITTEN   July 1997
    7118              :     //       MODIFIED       FCW December 1998
    7119              :     //                      FCW June 2001: Add blind calculations
    7120              :     //                      FCW Jan 2001: Add blinds with movable slats
    7121              :     //                      FCW Jan 2003: Add between-glass blinds
    7122              :     //                      FCW Jul 2003: account for transmittance of shading surfaces
    7123              :     //                       (previously these were assumed opaque even if transmittance schedule
    7124              :     //                        value was non-zero)
    7125              :     //                      FCW Aug 2003: modify initialization of WinLum from WinLum = 0. TO
    7126              :     //                        WinLum(:,:,IHR) = 0. Otherwise values calculated in previous
    7127              :     //                        call are incorrectly zeroed. Result was that window luminance with
    7128              :     //                        shade or blind included only contribution from first window element
    7129              :     //                        in window element loop in CalcDayltgCoefficients, thus seriously
    7130              :     //                        undercalculating window luminance for windows with more than one
    7131              :     //                        window element. Similarly, modified initialization of WLUMSU from
    7132              :     //                        WLUMSU = 0. to WLUMSU(:,IHR) = 0., and of WLUMSUdisk from
    7133              :     //                        WLUMSUdisk = 0. to WLUMSUdisk(:,IHR) = 0.
    7134              :     //                      PGE Aug 2003: Add daylighting shelves.
    7135              :     //                      FCW Nov 2003: Add beam solar and sky solar reflected from obstructions;
    7136              :     //                                    add beam solar reflected from ground accounting for obstructions.
    7137              :     //                      FCW Nov 2003: increase NPHMAX from 9 to 10 to avoid rays with altitude angle = 0
    7138              :     //                                    for vertical surfaces.
    7139              :     //                      FCW Nov 2003: fix the expression for min and max limits of azimuth; old expression
    7140              :     //                                    broke down for window normals with negative altitude angle
    7141              :     //                      FCW Nov 2003: add specular reflection from exterior obstructions
    7142              :     //                      FCW Apr 2004: add light well efficiency multiplying window transmittance
    7143              :     //                      FCW Apr 2004: add diffusing glazing
    7144              :     //                      RAR (FSEC)  May 2006: add exterior window screen
    7145              :     //                      B. Griffith NREL April 2010: CR7869 add adjacent zone area if window is not on this zone
    7146              :     //                                    apply interior window transmission and blocking to beam transmission from ext win
    7147              : 
    7148              :     // PURPOSE OF THIS SUBROUTINE:
    7149              :     // Called from CalcDayltgCoefficients for each window and reference point in a daylit
    7150              :     // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
    7151              :     // to internally reflected light by integrating to determine the amount of flux from
    7152              :     // sky and ground (and beam reflected from obstructions) transmitted through
    7153              :     // the center of the window and then reflecting this
    7154              :     // light from the inside surfaces of the space.  The "split-flux" method is used
    7155              :     // (Lynes, Principles of Natural Lighting, 1968).  EINT is determined for
    7156              :     // different sky types and for window with and without shades, screens or blinds.
    7157              :     // Also finds luminance (WinLum and WLUMSU) of window with shade or blind, &
    7158              :     // or with diffusing glass, for different sky types.
    7159              : 
    7160              :     // REFERENCES:
    7161              :     // Based on DOE-2.1E subroutine DREFLT.
    7162              : 
    7163              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    7164              :     // In the following I,J arrays:
    7165              :     // I = sky type;
    7166              :     // J = 1 for bare window, 2 and above for window with shade or blind.
    7167         2066 :     Illums ZSK;                   // Sky-related and sun-related illuminance on window from sky/ground
    7168         2066 :     Vector3<Real64> U;            // Unit vector in (PH,TH) direction
    7169         2066 :     Vector3<Real64> nearestHitPt; // Hit point of ray on nearest obstruction (m)
    7170         2066 :     Vector3<Real64> obsHitPt;     // Coordinates of hit point on an obstruction (m)
    7171         2066 :     Vector3<Real64> groundHitPt;  // Coordinates of point that ray from window center hits the ground (m)
    7172         2066 :     std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLCW = {Illums()}; // Sky-related upgoing luminous flux
    7173         2066 :     std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLFW = {Illums()}; // Sky-related downgoing luminous flux
    7174              : 
    7175              :     //  3=intermediate, 4=overcast
    7176              :     Real64 DPH; // Sky/ground element altitude and azimuth increments (radians)
    7177              :     Real64 DTH;
    7178              :     Real64 PH; // Sky/ground element altitude and azimuth (radians)
    7179              :     Real64 TH;
    7180              :     Real64 SPH; // Sine and cosine of PH
    7181              :     Real64 CPH;
    7182              :     Real64 PHMIN; // Limits of altitude integration (radians)
    7183              :     Real64 PHMAX;
    7184              :     Real64 ThMin; // Limits of azimuth integration (radians)
    7185              :     Real64 ThMax;
    7186              :     Real64 PhWin; // Altitude, azimuth angle of window normal (radians)
    7187              :     Real64 ThWin;
    7188              :     Real64 ACosTanTan; // ACOS(-TAN(Ph)*TAN(PhWin))
    7189              :     Real64 DA;         // CPH*DTH*DPH
    7190              :     Real64 COSB;       // Cosine of angle of incidence of light from sky or ground
    7191              :     Real64 TVISBR;     // Transmittance of window without shading at COSB
    7192              :     //  (times light well efficiency, if appropriate)
    7193              :     Real64 ZSU;
    7194              :     //  element for clear and overcast sky
    7195              :     Real64 ObTrans; // Product of solar transmittances of obstructions seen by a light ray
    7196              : 
    7197              :     // unused  REAL(r64)         :: HitPointLumFrClearSky     ! Luminance of obstruction from clear sky (cd/m2)
    7198              :     // unused  REAL(r64)         :: HitPointLumFrOvercSky     ! Luminance of obstruction from overcast sky (cd/m2)
    7199              :     // unused  REAL(r64)         :: HitPointLumFrSun          ! Luminance of obstruction from sun (cd/m2)
    7200              :     int ICtrl;       // Window control pointer
    7201              :     Real64 COSBSun;  // Cosine of angle of incidence of direct sun on window
    7202              :     Real64 TVISBSun; // Window's visible transmittance at COSBSun
    7203              :     //  (times light well efficiency, if appropriate)
    7204              :     Real64 ZSU1; // Transmitted direct normal illuminance (lux)
    7205              :     //  CHARACTER(len=32) :: ShType                    ! Window shading device type
    7206              :     bool ShadeOn;                // True if exterior or interior window shade present
    7207              :     bool BlindOn;                // True if exterior or interior window blind present
    7208              :     bool ScreenOn;               // True if exterior window screen present
    7209              :                                  //        int ScNum; // Screen number //Unused Set but never used
    7210              :     int PipeNum;                 // TDD pipe object number
    7211              :     int ShelfNum;                // Daylighting shelf object number
    7212              :     int InShelfSurf;             // Inside daylighting shelf surface number
    7213              :     int OutShelfSurf;            // Outside daylighting shelf surface number
    7214              :     Real64 TransBlBmDiffFront;   // Isolated blind vis beam-diffuse front transmittance
    7215              :     Real64 TransScBmDiffFront;   // Isolated screen vis beam-diffuse front transmittance
    7216              :     Real64 ReflGlDiffDiffBack;   // Bare glazing system vis diffuse back reflectance
    7217              :     Real64 ReflGlDiffDiffFront;  // Bare glazing system vis diffuse front reflectance
    7218              :     Real64 ReflBlBmDiffFront;    // Isolated blind vis beam-diffuse front reflectance
    7219              :     Real64 TransBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front transmittance
    7220              :     Real64 ReflBlDiffDiffFront;  // Isolated blind vis diffuse-diffuse front reflectance
    7221              :     Real64 ReflBlDiffDiffBack;   // Isolated blind vis diffuse-diffuse back reflectance
    7222              :     Real64 ReflScDiffDiffBack;   // Isolated screen vis diffuse-diffuse back reflectance
    7223              : 
    7224              :     Real64 td2; // Diffuse-diffuse vis trans of bare glass layers 2 and 3
    7225              :     Real64 td3;
    7226              :     Real64 rbd1; // Beam-diffuse back vis reflectance of bare glass layers 1 and 2
    7227              :     Real64 rbd2;
    7228              :     Real64 rfd2; // Beam-diffuse front vis reflectance of bare glass layers 2 and 3
    7229              :     Real64 rfd3;
    7230              :     Real64 tfshd;      // Diffuse-diffuse front vis trans of bare blind
    7231              :     Real64 rbshd;      // Diffuse-diffuse back vis reflectance of bare blind
    7232              :     Real64 ZSUObsRefl; // Illuminance on window from beam solar reflected by an
    7233              :     //  obstruction (for unit beam normal illuminance)
    7234              :     int NearestHitSurfNum;  // Surface number of nearest obstruction
    7235              :     int NearestHitSurfNumX; // Surface number to use when obstruction is a shadowing surface
    7236              :     Real64 LumAtHitPtFrSun; // Luminance at hit point on obstruction from solar reflection
    7237              :     //  for unit beam normal illuminance (cd/m2)
    7238              :     Real64 SunObstructionMult; // = 1 if sun hits a ground point; otherwise = 0
    7239              :     bool hitObs;               // True iff obstruction is hit
    7240              :     Real64 ObsVisRefl;         // Visible reflectance of obstruction
    7241              :     Real64 SkyReflVisLum;      // Reflected sky luminance at hit point divided by unobstructed sky
    7242              :     //  diffuse horizontal illuminance [(cd/m2)/lux]
    7243              :     Real64 dReflObsSky; // Contribution to sky-related illuminance on window due to sky diffuse
    7244              :     //  reflection from an obstruction
    7245              :     Real64 TVisSunRefl; // Diffuse vis trans of bare window for beam reflection calc
    7246              :     //  (times light well efficiency, if appropriate)
    7247              :     Real64 ZSU1refl; // Beam normal illuminance times ZSU1refl = illuminance on window
    7248              :     //  due to specular reflection from exterior surfaces
    7249              : 
    7250              :     ExtWinType extWinType;      // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
    7251              :     Real64 EnclInsideSurfArea;  // temporary for calculations, total surface area of enclosure surfaces m2
    7252              :     int IntWinAdjZoneExtWinNum; // the index of the exterior window in IntWinAdjZoneExtWin nested struct
    7253              :     int IntWinNum;              // window index for interior windows associated with exterior windows
    7254              :     Real64 COSBintWin;
    7255              : 
    7256              :     WinShadingType ShType;
    7257              : 
    7258         2066 :     auto &s_mat = state.dataMaterial;
    7259         2066 :     auto &dl = state.dataDayltg;
    7260         2066 :     auto &s_surf = state.dataSurface;
    7261              : 
    7262         2066 :     auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    7263         2066 :     auto const &surf = s_surf->Surface(IWin);
    7264         2066 :     auto const &surfWin = s_surf->SurfaceWindow(IWin);
    7265         2066 :     int const enclNumThisWin = s_surf->Surface(surf.BaseSurf).SolarEnclIndex;
    7266              :     // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea was calculated in subr DayltgAveInteriorReflectance
    7267              : 
    7268         2066 :     if (enclNumThisWin == enclNum) {
    7269         2066 :         extWinType = ExtWinType::InZone;
    7270         2066 :         EnclInsideSurfArea = dl->enclDaylight(enclNumThisWin).totInsSurfArea;
    7271         2066 :         IntWinAdjZoneExtWinNum = 0;
    7272              :     } else {
    7273            0 :         extWinType = ExtWinType::AdjZone;
    7274              :         // If window is exterior window in adjacent zone, then use areas of both enclosures
    7275            0 :         EnclInsideSurfArea = dl->enclDaylight(enclNum).totInsSurfArea + dl->enclDaylight(enclNumThisWin).totInsSurfArea;
    7276              :         // find index in IntWinAdjZoneExtWin
    7277            0 :         for (int AdjExtWinLoop = 1; AdjExtWinLoop <= thisEnclDaylight.NumOfIntWinAdjEnclExtWins; ++AdjExtWinLoop) {
    7278            0 :             if (IWin == thisEnclDaylight.IntWinAdjEnclExtWin(AdjExtWinLoop).SurfNum) { // found it
    7279            0 :                 IntWinAdjZoneExtWinNum = AdjExtWinLoop;
    7280            0 :                 break; // added TH 4/13/2010
    7281              :             }
    7282              :         }
    7283              :     }
    7284              : 
    7285              :     // Initialize window luminance and fluxes for split-flux calculation
    7286         8264 :     dl->winLum(IHR)[(int)iWinCover_Bare] = dl->winLum(IHR)[(int)iWinCover_Shaded] = Illums();
    7287              :     // dl->WLUMSU(IHR, _) = 0.0;
    7288              :     // dl->WLUMSUdisk(IHR, _) = 0.0;
    7289              : 
    7290         2066 :     int const IConst = s_surf->SurfActiveConstruction(IWin);
    7291         2066 :     auto const &construct = state.dataConstruction->Construct(IConst);
    7292              : 
    7293         2066 :     BlindOn = false;
    7294         2066 :     ShadeOn = false;
    7295         2066 :     ScreenOn = false;
    7296              : 
    7297         2066 :     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7298            0 :         PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
    7299              :     }
    7300              : 
    7301         2066 :     ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
    7302         2066 :     if (ShelfNum > 0) {
    7303            0 :         InShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf;   // Inside daylighting shelf present if > 0
    7304            0 :         OutShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf; // Outside daylighting shelf present if > 0
    7305              :     } else {
    7306         2066 :         InShelfSurf = 0;
    7307         2066 :         OutShelfSurf = 0;
    7308              :     }
    7309              : 
    7310              :     // Divide sky and ground into elements of altitude PH and
    7311              :     // azimuth TH, and add the contribution of light coming from each
    7312              :     // element to the transmitted flux at the center of the window
    7313              :     // Azimuth ranges over a maximum of 2 Pi radians.
    7314              :     // Altitude ranges over a maximum of Pi/2 radians between -Pi/2 < PH < +Pi/2, so that elements are not counted twice
    7315              :     // PH = 0 at the horizon; PH = Pi/2 at the zenith
    7316         2066 :     PHMIN = max(-Constant::PiOvr2, surfWin.phi - Constant::PiOvr2);
    7317         2066 :     PHMAX = min(Constant::PiOvr2, surfWin.phi + Constant::PiOvr2);
    7318         2066 :     DPH = (PHMAX - PHMIN) / double(NPHMAX);
    7319              : 
    7320              :     // Sky/ground element altitude integration
    7321         2066 :     Vector3<Real64> const SUNCOS_IHR(s_surf->SurfSunCosHourly(IHR));
    7322        22726 :     for (int IPH = 1; IPH <= NPHMAX; ++IPH) {
    7323        20660 :         PH = PHMIN + (double(IPH) - 0.5) * DPH;
    7324              : 
    7325        20660 :         SPH = std::sin(PH);
    7326        20660 :         CPH = std::cos(PH);
    7327              :         // Third component of unit vector in (TH,PH) direction
    7328        20660 :         U.z = SPH;
    7329              : 
    7330              :         // Limits of azimuth integration
    7331        20660 :         PhWin = surfWin.phi;
    7332        20660 :         ThWin = surfWin.theta;
    7333        20660 :         if (PhWin >= 0.0) {
    7334        20660 :             if (PH >= Constant::PiOvr2 - PhWin) {
    7335            0 :                 ThMin = -Constant::Pi;
    7336            0 :                 ThMax = Constant::Pi;
    7337              :             } else {
    7338        20660 :                 ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
    7339        20660 :                 ThMin = ThWin - std::abs(ACosTanTan);
    7340        20660 :                 ThMax = ThWin + std::abs(ACosTanTan);
    7341              :             }
    7342              : 
    7343              :         } else { // PhiSurf < 0.0
    7344            0 :             if (PH <= -PhWin - Constant::PiOvr2) {
    7345            0 :                 ThMin = -Constant::Pi;
    7346            0 :                 ThMax = Constant::Pi;
    7347              :             } else {
    7348            0 :                 ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
    7349            0 :                 ThMin = ThWin - std::abs(ACosTanTan);
    7350            0 :                 ThMax = ThWin + std::abs(ACosTanTan);
    7351              :             }
    7352              :         }
    7353              : 
    7354        20660 :         DTH = (ThMax - ThMin) / double(NTHMAX);
    7355        20660 :         DA = CPH * DTH * DPH;
    7356              : 
    7357              :         // Sky/ground element azimuth integration
    7358        20660 :         Real64 const sin_window_phi(std::sin(surfWin.phi));
    7359        20660 :         Real64 const cos_window_phi(std::cos(surfWin.phi));
    7360       351220 :         for (int ITH = 1; ITH <= NTHMAX; ++ITH) {
    7361       330560 :             TH = ThMin + (double(ITH) - 0.5) * DTH;
    7362       330560 :             U.x = CPH * std::cos(TH);
    7363       330560 :             U.y = CPH * std::sin(TH);
    7364              :             // Cosine of angle of incidence of light from sky or ground element
    7365       330560 :             COSB = SPH * sin_window_phi + CPH * cos_window_phi * std::cos(TH - surfWin.theta);
    7366       330560 :             if (COSB < 0.0) {
    7367            0 :                 continue; // Sky/ground elements behind window (although there shouldn't be any)
    7368              :             }
    7369              : 
    7370              :             // Initialize illuminance on window for this sky/ground element
    7371      1322240 :             ZSK = Illums();
    7372       330560 :             ZSU = 0.0;
    7373              :             // Initialize illuminance on window from beam solar reflection if ray hits an obstruction
    7374       330560 :             ZSUObsRefl = 0.0;
    7375              : 
    7376       330560 :             if (ISunPos == 1) { // Intersection calculation has to be done only for first sun position
    7377              :                 // Determine net transmittance of obstructions that the ray hits. ObTrans will be 1.0
    7378              :                 // if no obstructions are hit.
    7379        18720 :                 ObTrans = DayltgHitObstruction(state, IHR, IWin, s_surf->SurfaceWindow(IWin).WinCenter, U);
    7380        18720 :                 dl->ObTransM[IPH][ITH] = ObTrans;
    7381        18720 :                 dl->SkyObstructionMult[IPH][ITH] = 1.0;
    7382              :             }
    7383              : 
    7384              :             // SKY AND GROUND RADIATION ON WINDOW
    7385              : 
    7386              :             // Contribution is from sky if PH > 0 (ray goes upward), and from ground if PH < 0 (ray goes downward)
    7387              :             // (There may also be contributions from reflection from obstructions; see 'BEAM SOLAR AND SKY SOLAR
    7388              :             // REFLECTED FROM NEAREST OBSTRUCTION,' below.)
    7389              : 
    7390       330560 :             if (PH > 0.0) { // Contribution is from sky
    7391       826400 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7392       661120 :                     ZSK.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH, PH) * COSB * DA * dl->ObTransM[IPH][ITH];
    7393              :                 }
    7394              :             } else { // PH <= 0.0; contribution is from ground
    7395       165280 :                 if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
    7396              :                     // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
    7397              :                     // by the ray. This effect is given by the ratio SkyObstructionMult =
    7398              :                     // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
    7399              :                     // This ratio is calculated for an isotropic sky.
    7400              :                     // Ground point hit by the ray:
    7401            0 :                     Real64 Alfa = std::acos(-U.z);
    7402            0 :                     Real64 Beta = std::atan2(U.y, U.x);
    7403            0 :                     Real64 HorDis = (s_surf->SurfaceWindow(IWin).WinCenter.z - s_surf->GroundLevelZ) * std::tan(Alfa);
    7404            0 :                     groundHitPt.z = s_surf->GroundLevelZ;
    7405            0 :                     groundHitPt.x = s_surf->SurfaceWindow(IWin).WinCenter.x + HorDis * std::cos(Beta);
    7406            0 :                     groundHitPt.y = s_surf->SurfaceWindow(IWin).WinCenter.y + HorDis * std::sin(Beta);
    7407              : 
    7408            0 :                     dl->SkyObstructionMult[IPH][ITH] =
    7409            0 :                         CalcObstrMultiplier(state, groundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
    7410              :                 } // End of check if solar reflection calc is in effect
    7411              : 
    7412       165280 :                 auto const &gilsk = dl->horIllum[IHR];
    7413       826400 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7414              :                     // Below, luminance of ground in cd/m2 is illuminance on ground in lumens/m2
    7415              :                     // times ground reflectance, divided by pi, times obstruction multiplier.
    7416      1322240 :                     ZSK.sky[iSky] = (gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
    7417       661120 :                                     dl->SkyObstructionMult[IPH][ITH];
    7418              :                 }
    7419              :                 // Determine if sun illuminates the point that ray hits the ground. If the solar reflection
    7420              :                 // calculation has been requested (CalcSolRefl = .TRUE.) shading by obstructions, including
    7421              :                 // the building itself, is considered in determining whether sun hits the ground point.
    7422              :                 // Otherwise this shading is ignored and the sun always hits the ground point.
    7423       165280 :                 SunObstructionMult = 1.0;
    7424       165280 :                 if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
    7425              :                     // Sun reaches ground point if vector from this point to the sun is unobstructed
    7426            0 :                     hitObs = false;
    7427            0 :                     for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    7428            0 :                         hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    7429            0 :                         if (hitObs) {
    7430            0 :                             break;
    7431              :                         }
    7432            0 :                     }
    7433            0 :                     if (hitObs) {
    7434            0 :                         SunObstructionMult = 0.0;
    7435              :                     }
    7436              :                 }
    7437       165280 :                 ZSU = (dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
    7438              :                       SunObstructionMult;
    7439              :             }
    7440              :             // BEAM SOLAR AND SKY SOLAR REFLECTED FROM NEAREST OBSTRUCTION
    7441              : 
    7442       330560 :             if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] < 1.0) {
    7443              :                 // Find obstruction whose hit point is closest to the center of the window
    7444            0 :                 DayltgClosestObstruction(state, s_surf->SurfaceWindow(IWin).WinCenter, U, NearestHitSurfNum, nearestHitPt);
    7445            0 :                 if (NearestHitSurfNum > 0) {
    7446              : 
    7447              :                     // Beam solar reflected from nearest obstruction.
    7448            0 :                     LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, IHR, U, NearestHitSurfNum, nearestHitPt);
    7449            0 :                     ZSUObsRefl = LumAtHitPtFrSun * COSB * DA;
    7450            0 :                     ZSU += ZSUObsRefl;
    7451              : 
    7452              :                     // Sky solar reflected from nearest obstruction.
    7453            0 :                     int const ObsConstrNum = s_surf->Surface(NearestHitSurfNum).Construction;
    7454            0 :                     if (ObsConstrNum > 0) {
    7455              :                         // Exterior building surface is nearest hit
    7456            0 :                         if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
    7457              :                             // Obstruction is not a window, i.e., is an opaque surface
    7458            0 :                             ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
    7459              :                         } else {
    7460              :                             // Obstruction is a window; assume it is bare
    7461            0 :                             ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
    7462              :                         }
    7463              :                     } else {
    7464              :                         // Shadowing surface is nearest hit
    7465            0 :                         if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
    7466              :                             // Skip daylighting shelves, whose reflection is separately calculated
    7467            0 :                             ObsVisRefl = 0.0;
    7468              :                         } else {
    7469            0 :                             ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
    7470            0 :                             if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0) {
    7471            0 :                                 ObsVisRefl +=
    7472            0 :                                     s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
    7473            0 :                                     state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
    7474              :                             }
    7475              :                             // Note in the above that ShadowSurfDiffuseVisRefl is the reflectance of opaque part of
    7476              :                             // shadowing surface times (1 - ShadowSurfGlazingFrac)
    7477              :                         }
    7478              :                     }
    7479            0 :                     NearestHitSurfNumX = NearestHitSurfNum;
    7480              :                     // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
    7481              :                     // The following gets the correct side of a shadowing surface for reflection.
    7482            0 :                     if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
    7483            0 :                         if (dot(U, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) {
    7484            0 :                             NearestHitSurfNumX = NearestHitSurfNum + 1;
    7485              :                         }
    7486              :                     }
    7487            0 :                     if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
    7488            0 :                         state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    7489            0 :                         SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    7490            0 :                                         state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
    7491              :                     } else {
    7492            0 :                         SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
    7493            0 :                                         state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, IHR, NearestHitSurfNumX) / Constant::Pi;
    7494              :                     }
    7495            0 :                     dReflObsSky = SkyReflVisLum * COSB * DA;
    7496              : 
    7497            0 :                     auto const &gilsk = dl->horIllum[IHR];
    7498            0 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7499            0 :                         ZSK.sky[iSky] += gilsk.sky[iSky] * dReflObsSky;
    7500              :                     }
    7501              :                 }
    7502              :             } // End of check if exterior solar reflection calculation is active
    7503              : 
    7504              :             //  ===Bare window (no shade or blind; non-diffusing glass)===
    7505              : 
    7506              :             // Increment flux entering space and window luminance (cd/m2).
    7507              :             // FLCW--(I,J) = part of incoming flux (in lumens) that goes up to ceiling and upper part of walls.
    7508              :             // FLFW--(I,J) = part that goes down to floor and lower part of walls
    7509              : 
    7510       330560 :             if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7511              :                 // Unshaded visible transmittance of TDD for a single ray from sky/ground element
    7512            0 :                 TVISBR = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    7513              : 
    7514              :                 // Make all transmitted light diffuse for a TDD with a bare diffuser
    7515            0 :                 auto &wlumsk = dl->winLum(IHR)[iWinCover_Bare];
    7516            0 :                 auto &flfwsk = FLFW[iWinCover_Bare];
    7517            0 :                 auto &flcwsk = FLCW[iWinCover_Bare];
    7518              : 
    7519            0 :                 auto &tddFluxInc = dl->TDDFluxInc(IHR, PipeNum);
    7520            0 :                 auto &tddFluxTrans = dl->TDDFluxTrans(IHR, PipeNum);
    7521            0 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7522            0 :                     wlumsk.sky[iSky] += ZSK.sky[iSky] * TVISBR / Constant::Pi;
    7523            0 :                     flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * (1.0 - surfWin.fractionUpgoing);
    7524            0 :                     flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7525              : 
    7526              :                     // For later calculation of diffuse visible transmittance
    7527            0 :                     tddFluxInc.sky[iSky] += ZSK.sky[iSky];
    7528            0 :                     tddFluxTrans.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7529              : 
    7530              :                 } // for (iSky)
    7531              : 
    7532            0 :                 tddFluxInc.sky[(int)SkyType::Clear] += ZSU;
    7533            0 :                 tddFluxTrans.sky[(int)SkyType::Clear] += ZSU * TVISBR;
    7534              : 
    7535            0 :                 dl->winLum(IHR)[iWinCover_Bare].sun += ZSU * TVISBR / Constant::Pi;
    7536            0 :                 flfwsk.sun += ZSU * TVISBR * (1.0 - surfWin.fractionUpgoing);
    7537            0 :                 flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7538              : 
    7539              :             } else { // Bare window
    7540              :                 // Transmittance of bare window for this sky/ground element
    7541       330560 :                 TVISBR = Window::POLYF(COSB, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    7542              : 
    7543       330560 :                 if (InShelfSurf > 0) { // Inside daylighting shelf
    7544              :                     // Daylighting shelf simplification:  All light is diffuse
    7545              :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7546            0 :                     auto &flcwsk = FLCW[iWinCover_Bare];
    7547            0 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7548            0 :                         flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7549              :                     }
    7550            0 :                     flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7551              : 
    7552              :                 } else { // Normal window
    7553              : 
    7554              :                     // CR 7869  correct TVISBR if disk beam passes thru interior window
    7555       330560 :                     if (extWinType == ExtWinType::AdjZone) {
    7556              :                         // modify TVISBR by second window transmission
    7557              :                         // first determine if ray from point passes thru any interior window
    7558            0 :                         hitObs = false;
    7559            0 :                         for (int IntWinLoop = 1; IntWinLoop <= thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).NumOfIntWindows;
    7560              :                              ++IntWinLoop) {
    7561            0 :                             IntWinNum = thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).IntWinNum(IntWinLoop);
    7562            0 :                             auto const &surfIntWin = s_surf->SurfaceWindow(IntWinNum);
    7563            0 :                             hitObs = PierceSurface(state, IntWinNum, surfIntWin.WinCenter, SUNCOS_IHR, obsHitPt);
    7564            0 :                             if (hitObs) { // disk passes thru
    7565              :                                 // cosine of incidence angle of light from sky or ground element for
    7566            0 :                                 COSBintWin = SPH * std::sin(surfIntWin.phi) + CPH * std::cos(surfIntWin.phi) * std::cos(TH - surfIntWin.theta);
    7567            0 :                                 TVISBR *= Window::POLYF(COSBintWin,
    7568            0 :                                                         state.dataConstruction->Construct(s_surf->Surface(IntWinNum).Construction).TransVisBeamCoef);
    7569            0 :                                 break;
    7570              :                             }
    7571              :                         }
    7572            0 :                         if (!hitObs) { // blocked by opaque parts, beam does not actually pass thru interior window to reach zone
    7573            0 :                             TVISBR = 0.0;
    7574              :                         }
    7575              :                     }
    7576              : 
    7577       330560 :                     auto &flfwsk = FLFW[iWinCover_Bare];
    7578       330560 :                     auto &flcwsk = FLCW[iWinCover_Bare];
    7579      1652800 :                     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7580              :                         // IF (PH < 0.0d0) THEN
    7581              :                         // Fixed by FCW, Nov. 2003:
    7582      1322240 :                         if (PH > 0.0) {
    7583       661120 :                             flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7584              :                         } else {
    7585       661120 :                             flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
    7586              :                         }
    7587              :                     } // for (iSky)
    7588              : 
    7589       330560 :                     if (PH > 0.0) {
    7590       165280 :                         flfwsk.sun += ZSU * TVISBR;
    7591              :                     } else {
    7592       165280 :                         flcwsk.sun += ZSU * TVISBR;
    7593              :                     }
    7594              : 
    7595              :                 } // End of check if window with daylighting shelf or normal window
    7596              :             } // End of check if TDD:DOME or bare window
    7597              : 
    7598              :             // Check if window has shade or blind
    7599       330560 :             ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    7600       330560 :             if (s_surf->Surface(IWin).HasShadeControl) {
    7601            0 :                 ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
    7602            0 :                 ShadeOn = ANY_SHADE(ShType);
    7603            0 :                 BlindOn = ANY_BLIND(ShType);
    7604            0 :                 ScreenOn = (ShType == WinShadingType::ExtScreen);
    7605              :             }
    7606              : 
    7607       330560 :             if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
    7608              : 
    7609              :                 // ===Window with interior or exterior shade or blind, exterior screen, or with diffusing glass===
    7610              : 
    7611              :                 // Increment flux entering space and window luminance. Shades and diffusing glass are
    7612              :                 // assumed to be perfect diffusers, i.e., the transmittance is independent of angle of
    7613              :                 // incidence and the transmitted light is isotropic. The transmittance of a blind is
    7614              :                 // assumed to depend on profile angle and slat angle; the diffuse light entering the room from
    7615              :                 // the slats of the blind is assumed to be isotropic. With blinds, light can also enter
    7616              :                 // the room by passing between the slats without reflection. The beam transmittance of a screen
    7617              :                 // is assumed to depend on sun azimuth and azimuth angle.
    7618              : 
    7619              :                 // For light from a shade, or from diffusing glass, or from the slats of a blind, a flux fraction,
    7620              :                 // SurfaceWindow(IWin)%FractionUpgoing (determined by window tilt), goes up toward
    7621              :                 // ceiling and upper part of walls, and 1-Surfacewindow(iwin)%FractionUpgoing
    7622              :                 // goes down toward floor and lower part of walls. For a blind, the light passing
    7623              :                 // between the slats goes either up or down depending on the altitude angle of the
    7624              :                 // element from which the light came. For a screen, the light passing
    7625              :                 // between the screen's cylinders goes either up or down depending on the altitude angle of the
    7626              :                 // element from which the light came.
    7627              : 
    7628            0 :                 int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
    7629            0 :                 if (s_surf->SurfWinSolarDiffusing(IWin)) {
    7630            0 :                     IConstShaded = s_surf->Surface(IWin).Construction;
    7631              :                 }
    7632              : 
    7633              :                 // Transmittance of window including shade, screen or blind
    7634            0 :                 Real64 transBmBmMult = 0.0;
    7635            0 :                 Real64 transMult = 0.0;
    7636              : 
    7637            0 :                 if (ShadeOn) { // Shade
    7638            0 :                     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7639              :                         // Shaded visible transmittance of TDD for a single ray from sky/ground element
    7640            0 :                         transMult = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
    7641              :                     } else { // Shade only, no TDD
    7642              :                         // Calculate transmittance of the combined window and shading device for this sky/ground element
    7643            0 :                         transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7644            0 :                                     surfWin.lightWellEff;
    7645              :                     }
    7646              : 
    7647            0 :                 } else if (ScreenOn) { // Screen: get beam-beam, beam-diffuse and diffuse-diffuse vis trans/ref of screen and glazing system
    7648            0 :                     auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
    7649            0 :                     assert(screen != nullptr);
    7650              : 
    7651            0 :                     Real64 phi = std::abs(PH - surfWin.phi);
    7652            0 :                     Real64 theta = std::abs(TH - surfWin.theta);
    7653              :                     int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
    7654              :                     BilinearInterpCoeffs coeffs;
    7655              : 
    7656            0 :                     Material::NormalizePhiTheta(phi, theta);
    7657            0 :                     Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    7658            0 :                     GetBilinearInterpCoeffs(phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    7659              : 
    7660            0 :                     ReflGlDiffDiffFront = state.dataConstruction->Construct(IConst).ReflectVisDiffFront;
    7661            0 :                     ReflScDiffDiffBack = screen->DfRefVis;
    7662              : 
    7663            0 :                     auto const &b11 = screen->btars[ip1][it1];
    7664            0 :                     auto const &b12 = screen->btars[ip1][it2];
    7665            0 :                     auto const &b21 = screen->btars[ip2][it1];
    7666            0 :                     auto const &b22 = screen->btars[ip2][it2];
    7667              : 
    7668            0 :                     TransScBmDiffFront = BilinearInterp(b11.DfTransVis, b12.DfTransVis, b21.DfTransVis, b22.DfTransVis, coeffs);
    7669              : 
    7670            0 :                     transMult = TransScBmDiffFront * surfWin.glazedFrac * state.dataConstruction->Construct(IConst).TransDiffVis /
    7671            0 :                                 (1 - ReflGlDiffDiffFront * ReflScDiffDiffBack) * surfWin.lightWellEff;
    7672              : 
    7673            0 :                     transBmBmMult = BilinearInterp(b11.BmTransVis, b12.BmTransVis, b21.BmTransVis, b22.BmTransVis, coeffs);
    7674              : 
    7675            0 :                 } else if (BlindOn) { // Blind: get beam-diffuse and beam-beam vis trans of blind+glazing system
    7676              :                     // PETER:  As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
    7677              :                     //         for TDDs because it is based on TVISBR which is correctly calculated for TDDs above.
    7678            0 :                     auto const &surfShade = s_surf->surfShades(IWin);
    7679            0 :                     auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    7680            0 :                     assert(matBlind != nullptr);
    7681            0 :                     Real64 ProfAng = ProfileAngle(state, IWin, U, matBlind->SlatOrientation);
    7682              : 
    7683            0 :                     auto &btar = surfShade.blind.TAR;
    7684            0 :                     int idxLo = surfShade.blind.profAngIdxLo;
    7685            0 :                     int idxHi = std::min(Material::MaxProfAngs, idxLo + 1);
    7686            0 :                     Real64 interpFac = surfShade.blind.profAngInterpFac;
    7687            0 :                     TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7688              : 
    7689            0 :                     if (ShType == WinShadingType::IntBlind) { // Interior blind
    7690            0 :                         ReflGlDiffDiffBack = construct.ReflectVisDiffBack;
    7691            0 :                         ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7692            0 :                         ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
    7693            0 :                         TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
    7694            0 :                         transMult = TVISBR * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7695            0 :                                                                        (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7696              : 
    7697            0 :                     } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7698            0 :                         ReflGlDiffDiffFront = construct.ReflectVisDiffFront;
    7699            0 :                         ReflBlDiffDiffBack = btar.Vis.Bk.Df.Ref;
    7700            0 :                         transMult = TransBlBmDiffFront * surfWin.glazedFrac * construct.TransDiffVis /
    7701            0 :                                     (1.0 - ReflGlDiffDiffFront * ReflBlDiffDiffBack) * surfWin.lightWellEff;
    7702              : 
    7703              :                     } else { // Between-glass blind
    7704            0 :                         Real64 t1 = Window::POLYF(COSB, construct.tBareVisCoef(1));
    7705            0 :                         td2 = construct.tBareVisDiff(2);
    7706            0 :                         rbd1 = construct.rbBareVisDiff(1);
    7707            0 :                         rfd2 = construct.rfBareVisDiff(2);
    7708            0 :                         Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7709            0 :                         tfshd = btar.Vis.Ft.Df.Tra;
    7710            0 :                         Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7711            0 :                         rbshd = btar.Vis.Ft.Df.Ref;
    7712            0 :                         if (construct.TotGlassLayers == 2) { // 2 glass layers
    7713            0 :                             transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7714              :                         } else { // 3 glass layers; blind between layers 2 and 3
    7715            0 :                             Real64 t2 = Window::POLYF(COSB, construct.tBareVisCoef(2));
    7716            0 :                             td3 = construct.tBareVisDiff(3);
    7717            0 :                             rfd3 = construct.rfBareVisDiff(3);
    7718            0 :                             rbd2 = construct.rbBareVisDiff(2);
    7719            0 :                             transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7720            0 :                                         surfWin.lightWellEff;
    7721              :                         }
    7722              :                     }
    7723              : 
    7724            0 :                     transBmBmMult = TVISBR * matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
    7725              :                 } else { // Diffusing glass
    7726            0 :                     transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7727            0 :                                 surfWin.lightWellEff;
    7728              :                 } // End of check if shade, blind or diffusing glass
    7729              : 
    7730            0 :                 if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7731              :                     // No beam is transmitted.  This takes care of all types of screens and blinds.
    7732            0 :                     transBmBmMult = 0.0;
    7733              :                 }
    7734              : 
    7735              :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7736            0 :                 if (InShelfSurf > 0) { // Inside daylighting shelf
    7737            0 :                     transBmBmMult = 0.0;
    7738              :                 }
    7739              : 
    7740              :                 // DayltgInterReflectedIllumTransBmBmMult is used in the following for windows with blinds or screens to get contribution from light
    7741              :                 // passing directly between slats or between screen material without reflection.
    7742              : 
    7743            0 :                 auto &wlumsk = dl->winLum(IHR)[iWinCover_Shaded];
    7744            0 :                 auto &flfwsk = FLFW[iWinCover_Shaded];
    7745            0 :                 auto &flcwsk = FLCW[iWinCover_Shaded];
    7746              : 
    7747            0 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7748              :                     // Should these be bare or shaded?
    7749            0 :                     wlumsk.sky[iSky] += ZSK.sky[iSky] * transMult / Constant::Pi;
    7750            0 :                     flfwsk.sky[iSky] += ZSK.sky[iSky] * transMult * (1.0 - surfWin.fractionUpgoing);
    7751            0 :                     flcwsk.sky[iSky] += ZSK.sky[iSky] * transMult * surfWin.fractionUpgoing;
    7752              : 
    7753            0 :                     if (BlindOn || ScreenOn) {
    7754            0 :                         if (PH > 0.0) {
    7755            0 :                             flfwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
    7756              :                         } else {
    7757            0 :                             flcwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
    7758              :                         }
    7759              :                     }
    7760              :                 }
    7761              : 
    7762            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU * transMult / Constant::Pi;
    7763            0 :                 flfwsk.sun += ZSU * transMult * (1.0 - surfWin.fractionUpgoing);
    7764            0 :                 flcwsk.sun += ZSU * transMult * surfWin.fractionUpgoing;
    7765            0 :                 if (BlindOn || ScreenOn) {
    7766            0 :                     if (PH > 0.0) {
    7767            0 :                         flfwsk.sun += ZSU * transBmBmMult;
    7768              :                     } else {
    7769            0 :                         flcwsk.sun += ZSU * transBmBmMult;
    7770              :                     }
    7771              :                 }
    7772              :             } // End of window with shade, screen, blind or diffusing glass
    7773              : 
    7774              :         } // End of azimuth integration loop, ITH
    7775              :     } // End of altitude integration loop, IPH
    7776              : 
    7777         2066 :     if (OutShelfSurf > 0) { // Outside daylighting shelf
    7778              :         // Add exterior diffuse illuminance due to outside shelf
    7779              :         // Since all of the illuminance is added to the zone as upgoing diffuse, it can be added as a lump sum here
    7780              : 
    7781            0 :         TVISBR = construct.TransDiffVis; // Assume diffuse transmittance for shelf illuminance
    7782              : 
    7783            0 :         auto const &gilsk = dl->horIllum[IHR];
    7784            0 :         auto &flcwsk = FLCW[iWinCover_Bare];
    7785            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7786              :             // This is only an estimate because the anisotropic sky view of the shelf is not yet taken into account.
    7787              :             // SurfAnisoSkyMult would be great to use but it is not available until the heat balance starts up.
    7788            0 :             ZSK.sky[iSky] = gilsk.sky[iSky] * 1.0 * state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis *
    7789            0 :                             state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
    7790              : 
    7791              :             // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7792            0 :             flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
    7793              :         } // ISKY
    7794              : 
    7795            0 :         ZSU = dl->horIllum[IHR].sun * state.dataHeatBal->SurfSunlitFracHR(IHR, OutShelfSurf) *
    7796            0 :               state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis * state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
    7797            0 :         flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
    7798              :     }
    7799              : 
    7800              :     // Sky-related portion of internally reflected illuminance.
    7801              :     // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea, and ZoneDaylight(ZoneNum)%aveVisDiffReflect,
    7802              :     // were calculated in subr DayltgAveInteriorReflectance.
    7803              : 
    7804         6198 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    7805         4132 :         auto &eintsk = dl->reflIllum(IHR)[iWinCover];
    7806         4132 :         auto const &flfwsk = FLFW[iWinCover];
    7807         4132 :         auto const &flcwsk = FLCW[iWinCover];
    7808              : 
    7809        20660 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    7810              :             // Full area of window is used in following since effect of dividers on reducing
    7811              :             // effective window transmittance has already been accounted for in calc of FLFWSK and FLCWSK.
    7812        33056 :             eintsk.sky[iSky] = (flfwsk.sky[iSky] * surfWin.rhoFloorWall + flcwsk.sky[iSky] * surfWin.rhoCeilingWall) *
    7813        16528 :                                (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - dl->enclDaylight(enclNum).aveVisDiffReflect));
    7814              :         } // for (iSky)
    7815              :     } // for (iWinCover)
    7816              : 
    7817              :     // BEAM SOLAR RADIATION ON WINDOW
    7818              : 
    7819              :     // Beam reaching window directly (without specular reflection from exterior obstructions)
    7820              : 
    7821         2066 :     if (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0) {
    7822              :         // Cos of angle of incidence
    7823         1454 :         COSBSun = dl->sunAngles.sinPhi * std::sin(surfWin.phi) +
    7824         1454 :                   dl->sunAngles.cosPhi * std::cos(surfWin.phi) * std::cos(dl->sunAngles.theta - surfWin.theta);
    7825              : 
    7826         1454 :         if (COSBSun > 0.0) {
    7827              :             // Multiply direct normal illuminance (normalized to 1.0 lux)
    7828              :             // by incident angle factor and by fraction of window that is sunlit.
    7829              :             // Note that in the following SurfSunlitFracHR accounts for possibly non-zero transmittance of
    7830              :             // shading surfaces.
    7831              : 
    7832         1454 :             ZSU1 = COSBSun * state.dataHeatBal->SurfSunlitFracHR(IHR, IWin);
    7833              : 
    7834              :             // Contribution to window luminance and downgoing flux
    7835              : 
    7836              :             // -- Bare window
    7837              : 
    7838         1454 :             if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7839              :                 // Unshaded visible transmittance of TDD for collimated beam from the sun
    7840            0 :                 TVISBSun = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
    7841            0 :                 dl->TDDTransVisBeam(IHR, PipeNum) = TVISBSun;
    7842              : 
    7843            0 :                 FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
    7844              : 
    7845            0 :                 dl->winLum(IHR)[iWinCover_Bare].sun += ZSU1 * TVISBSun / Constant::Pi;
    7846            0 :                 FLFW[iWinCover_Bare].sun += ZSU1 * TVISBSun * (1.0 - surfWin.fractionUpgoing);
    7847            0 :                 FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
    7848              : 
    7849              :             } else { // Bare window
    7850         1454 :                 TVISBSun = Window::POLYF(COSBSun, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
    7851              : 
    7852              :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7853         1454 :                 if (InShelfSurf > 0) {                  // Inside daylighting shelf
    7854            0 :                     FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
    7855              : 
    7856              :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7857              :                     // WLUMSU(1,IHR) = WLUMSU(1,IHR) + ZSU1 * TVISBSun / PI
    7858              :                     // FLFWSU(1) = FLFWSU(1) + ZSU1 * TVISBSun * (1.0 - SurfaceWindow(IWin)%FractionUpgoing)
    7859            0 :                     FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
    7860              :                 } else { // Normal window
    7861         1454 :                     FLFW[iWinCover_Bare].sunDisk = ZSU1 * TVISBSun;
    7862              :                 }
    7863              :             }
    7864              : 
    7865              :             // -- Window with shade, screen, blind or diffusing glass
    7866         1454 :             if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
    7867            0 :                 Real64 transBmBmMult = 0.0;
    7868            0 :                 Real64 transMult = 0.0;
    7869              : 
    7870            0 :                 if (ShadeOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade or screen on or diffusing glass
    7871            0 :                     if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7872              :                         // Shaded visible transmittance of TDD for collimated beam from the sun
    7873            0 :                         transMult = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
    7874              : 
    7875            0 :                     } else if (ScreenOn) {
    7876            0 :                         auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
    7877            0 :                         assert(screen != nullptr);
    7878            0 :                         Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
    7879            0 :                         Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
    7880              :                         int ip1, ip2, it1, it2;
    7881              :                         BilinearInterpCoeffs coeffs;
    7882            0 :                         Material::NormalizePhiTheta(phi, theta);
    7883            0 :                         Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
    7884            0 :                         GetBilinearInterpCoeffs(
    7885            0 :                             phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
    7886            0 :                         Real64 BmBmTransVis = BilinearInterp(screen->btars[ip1][it1].BmTransVis,
    7887            0 :                                                              screen->btars[ip1][it2].BmTransVis,
    7888            0 :                                                              screen->btars[ip2][it1].BmTransVis,
    7889            0 :                                                              screen->btars[ip2][it2].BmTransVis,
    7890              :                                                              coeffs);
    7891              : 
    7892            0 :                         transMult = BmBmTransVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7893              :                     } else {
    7894            0 :                         int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
    7895            0 :                         if (s_surf->SurfWinSolarDiffusing(IWin)) {
    7896            0 :                             IConstShaded = surf.Construction;
    7897              :                         }
    7898            0 :                         transMult = Window::POLYF(COSBSun, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
    7899            0 :                                     surfWin.lightWellEff;
    7900              :                     }
    7901              : 
    7902              :                 } else { // Blind on
    7903            0 :                     auto const &surfShade = s_surf->surfShades(IWin);
    7904              :                     // As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
    7905              :                     // for TDDs because it is based on TVISBSun which is correctly calculated for TDDs above.
    7906            0 :                     auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    7907            0 :                     assert(matBlind != nullptr);
    7908              : 
    7909              :                     // These are "cached" in the surfShade struct
    7910            0 :                     auto &btar = surfShade.blind.TAR;
    7911            0 :                     int idxLo = surfShade.blind.profAngIdxLo;
    7912            0 :                     int idxHi = surfShade.blind.profAngIdxHi;
    7913            0 :                     int interpFac = surfShade.blind.profAngInterpFac;
    7914            0 :                     TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7915              : 
    7916            0 :                     if (ShType == WinShadingType::IntBlind) { // Interior blind
    7917              :                         // TH CR 8121, 7/7/2010
    7918              :                         // ReflBlBmDiffFront = WindowManager::InterpProfAng(ProfAng,Blind(BlNum)%VisFrontBeamDiffRefl)
    7919            0 :                         ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7920              : 
    7921              :                         // TH added 7/12/2010 for CR 8121
    7922            0 :                         ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
    7923            0 :                         TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
    7924              : 
    7925            0 :                         transMult = TVISBSun * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    7926            0 :                                                                          (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    7927              : 
    7928            0 :                     } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    7929            0 :                         transMult = TransBlBmDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
    7930            0 :                                     surfWin.glazedFrac * surfWin.lightWellEff;
    7931              : 
    7932              :                     } else { // Between-glass blind
    7933            0 :                         Real64 t1 = Window::POLYF(COSBSun, construct.tBareVisCoef(1));
    7934            0 :                         Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
    7935            0 :                         Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
    7936            0 :                         if (construct.TotGlassLayers == 2) { // 2 glass layers
    7937            0 :                             transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    7938              :                         } else { // 3 glass layers; blind between layers 2 and 3
    7939            0 :                             Real64 t2 = Window::POLYF(COSBSun, construct.tBareVisCoef(2));
    7940            0 :                             transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    7941            0 :                                         surfWin.lightWellEff;
    7942              :                         }
    7943              :                     }
    7944              : 
    7945            0 :                     transBmBmMult = TVISBSun * matBlind->BeamBeamTrans(surfShade.blind.profAng, surfShade.blind.slatAng);
    7946              :                 } // ShadeOn/ScreenOn/BlindOn/Diffusing glass
    7947              : 
    7948            0 :                 if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
    7949            0 :                     transBmBmMult = 0.0; // No beam, diffuse only
    7950              :                 }
    7951              : 
    7952              :                 // Daylighting shelf simplification:  No beam makes it past end of shelf, all light is diffuse
    7953            0 :                 if (InShelfSurf > 0) {   // Inside daylighting shelf
    7954            0 :                     transBmBmMult = 0.0; // No beam, diffuse only
    7955              :                     // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
    7956              :                 }
    7957              : 
    7958            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1 * transMult / Constant::Pi;
    7959            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult / Constant::Pi;
    7960            0 :                 FLFW[iWinCover_Shaded].sun += ZSU1 * transMult * (1.0 - surfWin.fractionUpgoing);
    7961            0 :                 FLFW[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult;
    7962            0 :                 FLCW[iWinCover_Shaded].sun += ZSU1 * transMult * surfWin.fractionUpgoing;
    7963              :             } // if (BlindOn || ShadeOn)
    7964              :         } // if (COSBSun > 0)
    7965              :     } // if (SurfSunlitFracHR > 0)
    7966              : 
    7967              :     // Beam reaching window after specular reflection from exterior obstruction
    7968              : 
    7969              :     // In the following, Beam normal illuminance times ZSU1refl = illuminance on window due to
    7970              :     // specular reflection from exterior surfaces
    7971              : 
    7972         2066 :     if (s_surf->CalcSolRefl && s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Dome) {
    7973              : 
    7974            0 :         ZSU1refl = s_surf->SurfReflFacBmToBmSolObs(IHR, IWin);
    7975              : 
    7976            0 :         if (ZSU1refl > 0.0) {
    7977              :             // Contribution to window luminance and downgoing flux
    7978              : 
    7979              :             // -- Bare window. We use diffuse-diffuse transmittance here rather than beam-beam to avoid
    7980              :             //    complications due to specular reflection from multiple exterior surfaces
    7981              : 
    7982            0 :             TVisSunRefl = construct.TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
    7983              :             // In the following it is assumed that all reflected beam is going downward, as it would be in the
    7984              :             // important case of reflection from a highly glazed facade of a neighboring building. However, in
    7985              :             // rare cases (such as upward specular reflection from a flat horizontal skylight) it may
    7986              :             // actually be going upward.
    7987            0 :             FLFW[iWinCover_Bare].sunDisk += ZSU1refl * TVisSunRefl;
    7988              : 
    7989              :             // -- Window with shade, blind or diffusing glass
    7990              : 
    7991            0 :             if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
    7992            0 :                 Real64 transMult = 0.0;
    7993              : 
    7994            0 :                 if (ShadeOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade on or diffusing glass
    7995            0 :                     int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
    7996            0 :                     if (s_surf->SurfWinSolarDiffusing(IWin)) {
    7997            0 :                         IConstShaded = s_surf->Surface(IWin).Construction;
    7998              :                     }
    7999            0 :                     transMult = state.dataConstruction->Construct(IConstShaded).TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
    8000              : 
    8001            0 :                 } else if (ScreenOn) { // Exterior screen on
    8002            0 :                     auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
    8003            0 :                     assert(screen != nullptr);
    8004            0 :                     Real64 TransScDiffDiffFront = screen->DfTransVis;
    8005              : 
    8006            0 :                     transMult = TransScDiffDiffFront *
    8007            0 :                                 (state.dataConstruction->Construct(IConst).TransDiffVis / (1.0 - ReflGlDiffDiffFront * ReflScDiffDiffBack)) *
    8008            0 :                                 surfWin.glazedFrac * surfWin.lightWellEff;
    8009              : 
    8010              :                 } else { // Blind on
    8011              : 
    8012            0 :                     auto const &surfShade = s_surf->surfShades(IWin);
    8013            0 :                     auto const &btar = surfShade.blind.TAR;
    8014            0 :                     auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
    8015              : 
    8016            0 :                     assert(matBlind != nullptr);
    8017              : 
    8018            0 :                     TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
    8019            0 :                     if (ShType == WinShadingType::IntBlind) { // Interior blind
    8020            0 :                         ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
    8021            0 :                         transMult = TVisSunRefl * (TransBlDiffDiffFront + ReflBlDiffDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
    8022            0 :                                                                               (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
    8023              : 
    8024            0 :                     } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
    8025            0 :                         transMult = TransBlDiffDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
    8026            0 :                                     surfWin.glazedFrac * surfWin.lightWellEff;
    8027              : 
    8028              :                     } else { // Between-glass blind
    8029            0 :                         Real64 t1 = construct.tBareVisDiff(1);
    8030            0 :                         Real64 tfshBd = btar.Vis.Ft.Df.Tra;
    8031            0 :                         Real64 rfshB = btar.Vis.Ft.Df.Ref;
    8032            0 :                         if (construct.TotGlassLayers == 2) { // 2 glass layers
    8033            0 :                             transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
    8034              :                         } else { // 3 glass layers; blind between layers 2 and 3
    8035            0 :                             Real64 t2 = construct.tBareVisDiff(2);
    8036            0 :                             transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
    8037            0 :                                         surfWin.lightWellEff;
    8038              :                         }
    8039              :                     } // End of check of interior/exterior/between-glass blind
    8040              :                 } // if (Blind)
    8041              : 
    8042            0 :                 dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1refl * transMult / Constant::Pi;
    8043            0 :                 FLFW[iWinCover_Shaded].sun += ZSU1refl * transMult * (1.0 - surfWin.fractionUpgoing);
    8044            0 :                 FLCW[iWinCover_Shaded].sun += ZSU1refl * transMult * surfWin.fractionUpgoing;
    8045              :             } // End of check if window has shade, blind or diffusing glass
    8046              :         } // End of check if ZSU1refl > 0.0
    8047              :     } // End of check if solar reflections are in effect
    8048              : 
    8049              :     // Sun-related portion of internally reflected illuminance
    8050              : 
    8051              :     // Full area of window is used in following since effect of dividers on reducing
    8052              :     // effective window transmittance already accounted for in calc of FLFWSU and FLCWSU
    8053              :     // CR 7869 added effect of intervening interior windows on transmittance and
    8054              :     // added inside surface area of adjacent zone
    8055         6198 :     for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    8056         8264 :         dl->reflIllum(IHR)[iWinCover].sun = (FLFW[iWinCover].sun * surfWin.rhoFloorWall + FLCW[iWinCover].sun * surfWin.rhoCeilingWall) *
    8057         4132 :                                             (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8058              : 
    8059         8264 :         dl->reflIllum(IHR)[iWinCover].sunDisk = FLFW[iWinCover].sunDisk * surfWin.rhoFloorWall * (surf.Area / surfWin.glazedFrac) /
    8060         4132 :                                                 (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8061              :     }
    8062         2066 : } // DayltgInterReflectedIllum()
    8063              : 
    8064            0 : void ComplexFenestrationLuminances(EnergyPlusData &state,
    8065              :                                    int const IWin,
    8066              :                                    int const WinEl,
    8067              :                                    int const NBasis,
    8068              :                                    int const IHR,
    8069              :                                    int const iRefPoint,
    8070              :                                    Array1D<Illums> &ElementLuminance, // luminance at window element (exterior side)
    8071              :                                    CalledFor const CalledFrom,
    8072              :                                    int const MapNum)
    8073              : {
    8074              : 
    8075              :     // SUBROUTINE INFORMATION:
    8076              :     //       AUTHOR         Simon Vidanovic
    8077              :     //       DATE WRITTEN   June 2013
    8078              : 
    8079            0 :     Vector3<Real64> obsHitPt;    // Coordinates of hit point on an obstruction (m)
    8080            0 :     Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
    8081              : 
    8082            0 :     auto &dl = state.dataDayltg;
    8083            0 :     auto &s_surf = state.dataSurface;
    8084              : 
    8085            0 :     int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
    8086            0 :     auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
    8087              :     // Calculate luminance from sky and sun excluding exterior obstruction transmittances and obstruction multipliers
    8088            0 :     int SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
    8089            0 :     for (int iIncElem = 1; iIncElem <= NBasis; ++iIncElem) {
    8090            0 :         Real64 LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
    8091              :         // COSB = ComplexWind(IWin)%Geom(CurCplxFenState)%CosInc(iIncElem)
    8092              :         // DA = ComplexWind(IWin)%Geom(CurCplxFenState)%DAInc(iIncElem)
    8093            0 :         Real64 Altitude = complexWinGeom.pInc(iIncElem).Altitude;
    8094            0 :         Real64 Azimuth = complexWinGeom.pInc(iIncElem).Azimuth;
    8095            0 :         auto &elemLum = ElementLuminance(iIncElem);
    8096            0 :         auto const &gilsk = dl->horIllum[IHR];
    8097              : 
    8098            0 :         if (Altitude > 0.0) {
    8099              :             // Ray from sky element
    8100            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8101            0 :                 elemLum.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc;
    8102              :             }
    8103            0 :         } else if (Altitude < 0.0) {
    8104              :             // Ray from ground element
    8105              :             // BeamObstrMultiplier = ComplexWind(IWin)%DaylghtGeom(CurCplxFenState)%GndObstrMultiplier(WinEl, iIncElem)
    8106            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8107            0 :                 elemLum.sky[iSky] = gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    8108              :             }
    8109            0 :             elemLum.sun = dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    8110              :         } else {
    8111              :             // Ray from the element which is half sky and half ground
    8112            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8113              :                 // in this case half of the pach is coming from the sky and half from the ground
    8114            0 :                 elemLum.sky[iSky] = 0.5 * DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc +
    8115            0 :                                     0.5 * gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    8116              :             }
    8117            0 :             elemLum.sun = 0.5 * dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
    8118              :         }
    8119              :         // Sun beam calculations
    8120            0 :         if ((SolBmIndex == iIncElem) && (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0)) {
    8121            0 :             elemLum.sunDisk = 1.0;
    8122              :         }
    8123              :     }
    8124              : 
    8125            0 :     auto const &complexWinDaylightGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CurCplxFenState);
    8126              : 
    8127            0 :     if (CalledFrom == CalledFor::RefPoint) {
    8128            0 :         auto const &complexWinRefPoint = complexWinDaylightGeom.RefPoint(iRefPoint);
    8129              :         // add exterior obstructions transmittances to calculated luminances
    8130            0 :         for (int iReflElem = 1; iReflElem <= complexWinRefPoint.NReflSurf(WinEl); ++iReflElem) {
    8131            0 :             Real64 ObstrTrans = complexWinRefPoint.TransOutSurf(iReflElem, WinEl);
    8132            0 :             int iReflElemIndex = complexWinRefPoint.RefSurfIndex(iReflElem, WinEl);
    8133              : 
    8134            0 :             auto &elemLum = ElementLuminance(iReflElemIndex);
    8135            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8136            0 :                 elemLum.sky[iSky] *= ObstrTrans;
    8137              :             }
    8138            0 :             elemLum.sun *= ObstrTrans;
    8139            0 :             elemLum.sunDisk *= ObstrTrans;
    8140              :         }
    8141              : 
    8142              :         // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
    8143              :         // sun reaches the ground for that point
    8144            0 :         Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
    8145            0 :         for (int iGndElem = 1; iGndElem <= complexWinRefPoint.NGnd(WinEl); ++iGndElem) {
    8146              :             // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
    8147              :             // were hit in the process
    8148              : 
    8149            0 :             Real64 BeamObstrMultiplier = complexWinRefPoint.GndObstrMultiplier(iGndElem, WinEl);
    8150            0 :             int iGndElemIndex = complexWinRefPoint.GndIndex(iGndElem, WinEl);
    8151              : 
    8152            0 :             auto &elemLum = ElementLuminance(iGndElemIndex);
    8153            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8154            0 :                 elemLum.sky[iSky] *= BeamObstrMultiplier;
    8155              :             }
    8156              : 
    8157              :             // direct sun disk reflect off the ground
    8158            0 :             Real64 SunObstrMultiplier = 1.0;
    8159            0 :             if (s_surf->CalcSolRefl) {
    8160              :                 // Sun reaches ground point if vector from this point to the sun is unobstructed
    8161            0 :                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    8162            0 :                     groundHitPt = complexWinRefPoint.GndPt(iGndElem, WinEl);
    8163            0 :                     bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    8164            0 :                     if (hitObs) {
    8165            0 :                         SunObstrMultiplier = 0.0;
    8166            0 :                         break;
    8167              :                     }
    8168            0 :                 }
    8169              :             }
    8170            0 :             elemLum.sun *= SunObstrMultiplier;
    8171              :         }
    8172              : 
    8173            0 :     } else { // if (CalledFrom != RefPoint)
    8174              : 
    8175            0 :         auto const &complexWinIllumMap = complexWinDaylightGeom.IlluminanceMap(iRefPoint, MapNum);
    8176              :         // add exterior obstructions transmittances to calculated luminances
    8177            0 :         for (int iReflElem = 1; iReflElem <= complexWinIllumMap.NReflSurf(WinEl); ++iReflElem) {
    8178            0 :             Real64 ObstrTrans = complexWinIllumMap.TransOutSurf(iReflElem, WinEl);
    8179            0 :             int iReflElemIndex = complexWinIllumMap.RefSurfIndex(iReflElem, WinEl);
    8180            0 :             auto &elemLum = ElementLuminance(iReflElemIndex);
    8181              : 
    8182            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8183            0 :                 elemLum.sky[iSky] *= ObstrTrans;
    8184              :             }
    8185            0 :             elemLum.sun *= ObstrTrans;
    8186            0 :             elemLum.sunDisk *= ObstrTrans;
    8187              :         }
    8188              : 
    8189              :         // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
    8190              :         // sun reaches the ground for that point
    8191            0 :         Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
    8192            0 :         for (int iGndElem = 1; iGndElem <= complexWinIllumMap.NGnd(WinEl); ++iGndElem) {
    8193              :             // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
    8194              :             // were hit in the process
    8195            0 :             Real64 BeamObstrMultiplier = complexWinIllumMap.GndObstrMultiplier(iGndElem, WinEl);
    8196            0 :             int iGndElemIndex = complexWinIllumMap.GndIndex(iGndElem, WinEl);
    8197              : 
    8198            0 :             auto &elemLum = ElementLuminance(iGndElemIndex);
    8199            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8200            0 :                 elemLum.sky[iSky] *= BeamObstrMultiplier;
    8201              :             }
    8202              : 
    8203              :             // direct sun disk reflect off the ground
    8204            0 :             Real64 SunObstrMultiplier = 1.0;
    8205            0 :             if (s_surf->CalcSolRefl) {
    8206              :                 // Sun reaches ground point if vector from this point to the sun is unobstructed
    8207            0 :                 for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    8208            0 :                     groundHitPt = complexWinIllumMap.GndPt(iGndElem, WinEl);
    8209              : 
    8210            0 :                     bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
    8211            0 :                     if (hitObs) {
    8212            0 :                         SunObstrMultiplier = 0.0;
    8213            0 :                         break;
    8214              :                     }
    8215            0 :                 }
    8216              :             }
    8217            0 :             elemLum.sun *= SunObstrMultiplier;
    8218              :         }
    8219            0 :     } // if (CalledFrom == RefPoint)
    8220            0 : } // ComplexFenestrationLuminances()
    8221              : 
    8222            0 : void DayltgInterReflectedIllumComplexFenestration(EnergyPlusData &state,
    8223              :                                                   int const IWin,            // Window index
    8224              :                                                   int const WinEl,           // Current window element counter
    8225              :                                                   int const IHR,             // Hour of day
    8226              :                                                   int const daylightCtrlNum, // Daylighting control number
    8227              :                                                   int const iRefPoint,       // reference point counter
    8228              :                                                   CalledFor const CalledFrom,
    8229              :                                                   int const MapNum)
    8230              : {
    8231              : 
    8232              :     // SUBROUTINE INFORMATION:
    8233              :     //       AUTHOR         Simon Vidanovic
    8234              :     //       DATE WRITTEN   April 2013
    8235              : 
    8236              :     // PURPOSE OF THIS SUBROUTINE:
    8237              :     // Called from CalcDayltgCoefficients for each complex (bsdf) fenestration and reference point in a daylit
    8238              :     // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
    8239              :     // to internally reflected light by integrating to determine the amount of flux from
    8240              :     // sky and ground (and beam reflected from obstructions) transmitted through
    8241              :     // the center of the window and then reflecting this
    8242              :     // light from the inside surfaces of the space.
    8243              : 
    8244            0 :     auto &dl = state.dataDayltg;
    8245            0 :     auto &s_surf = state.dataSurface;
    8246              : 
    8247            0 :     Array1D<Illums> FL; // Sky related luminous flux
    8248              :     // Array1D<Real64> FLSU;     // Sun related luminous flux, excluding entering beam
    8249              :     // Array1D<Real64> FLSUdisk; // Sun related luminous flux, due to entering beam
    8250              : 
    8251            0 :     Array1D<Illums> FirstFlux; // Sky related first reflected flux
    8252              :     // Array1D<Real64> FirstFluxSU;     // Sun related first reflected flux, excluding entering beam
    8253              :     // Array1D<Real64> FirstFluxSUdisk; // Sun related first reflected flux, due to entering beam
    8254              : 
    8255            0 :     Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
    8256              :     // Array1D<Real64> ElementLuminanceSun;     // sun related luminance at window element (exterior side), exluding beam
    8257              :     // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side), due to sun beam
    8258            0 :     Illums FLTot;
    8259              :     // Real64 FLSUTot;
    8260              :     // Real64 FLSUdiskTot;
    8261              : 
    8262              :     // Total for first relflected fluxes
    8263            0 :     Illums FFTot = Illums();
    8264              :     // Real64 FFSUTot;
    8265              :     // Real64 FFSUdiskTot;
    8266              : 
    8267              :     int NIncBasis;
    8268              :     int SolBmIndex; // index of current sun position
    8269              : 
    8270              :     Real64 LambdaInc; // current lambda value for incoming direction
    8271              :     // REAL(r64) :: LambdaTrn  ! current lambda value for incoming direction
    8272              :     Real64 dirTrans; // directional bsdf transmittance
    8273              : 
    8274            0 :     auto const &surf = s_surf->Surface(IWin);
    8275            0 :     auto const &surfWin = s_surf->SurfaceWindow(IWin);
    8276              : 
    8277            0 :     int CurCplxFenState = surfWin.ComplexFen.CurrentState;
    8278            0 :     auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
    8279            0 :     int iConst = surfWin.ComplexFen.State(CurCplxFenState).Konst;
    8280            0 :     int NTrnBasis = complexWinGeom.Trn.NBasis;
    8281              : 
    8282            0 :     if (!allocated(FL)) {
    8283            0 :         FL.allocate(NTrnBasis);
    8284              :     }
    8285            0 :     FL = Illums();
    8286              :     // if (!allocated(FLSU)) FLSU.dimension(NTrnBasis, 0.0);
    8287              :     // if (!allocated(FLSUdisk)) FLSUdisk.dimension(NTrnBasis, 0.0);
    8288              : 
    8289            0 :     if (!allocated(FirstFlux)) {
    8290            0 :         FirstFlux.allocate(NTrnBasis);
    8291              :     }
    8292            0 :     FirstFlux = Illums();
    8293              :     // if (!allocated(FirstFluxSU)) FirstFluxSU.dimension(NTrnBasis, 0.0);
    8294              :     // if (!allocated(FirstFluxSUdisk)) FirstFluxSUdisk.dimension(NTrnBasis, 0.0);
    8295              : 
    8296            0 :     NIncBasis = complexWinGeom.Inc.NBasis;
    8297            0 :     if (!allocated(ElementLuminance)) {
    8298            0 :         ElementLuminance.allocate(NIncBasis);
    8299              :     }
    8300            0 :     ElementLuminance = Illums();
    8301              :     // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
    8302              :     // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
    8303              : 
    8304              :     // Integration over sky/ground/sun elements is done over window incoming basis element and flux is calculated for each
    8305              :     // outgoing direction. This is used to calculate first reflected flux
    8306              : 
    8307            0 :     ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
    8308              : 
    8309              :     // luminance from sun disk needs to include fraction of sunlit area
    8310            0 :     SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
    8311            0 :     Real64 COSIncSun = (SolBmIndex > 0) ? complexWinGeom.CosInc(SolBmIndex) : 0.0;
    8312              : 
    8313            0 :     for (int i = 1; i <= (int)ElementLuminance.size(); ++i) {
    8314            0 :         ElementLuminance(i).sunDisk *= state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) * COSIncSun;
    8315              :     }
    8316              : 
    8317              :     //        FLSKTot = 0.0;
    8318            0 :     FLTot.sun = 0.0;
    8319            0 :     FLTot.sunDisk = 0.0;
    8320            0 :     FFTot.sun = 0.0;
    8321            0 :     FFTot.sunDisk = 0.0;
    8322              :     // now calculate flux into each outgoing direction by integrating over all incoming directions
    8323            0 :     for (int iBackElem = 1; iBackElem <= NTrnBasis; ++iBackElem) {
    8324            0 :         for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
    8325            0 :             LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
    8326            0 :             dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iBackElem, iIncElem);
    8327              : 
    8328            0 :             auto &fl = FL(iBackElem);
    8329            0 :             auto const &elemLum = ElementLuminance(iIncElem);
    8330            0 :             for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8331            0 :                 fl.sky[iSky] += dirTrans * LambdaInc * elemLum.sky[iSky];
    8332              :             }
    8333              : 
    8334            0 :             fl.sun += dirTrans * LambdaInc * elemLum.sun;
    8335            0 :             fl.sunDisk += dirTrans * LambdaInc * elemLum.sunDisk;
    8336              :         }
    8337              : 
    8338            0 :         auto &firstFlux = FirstFlux(iBackElem);
    8339            0 :         auto const &fl = FL(iBackElem);
    8340            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8341            0 :             firstFlux.sky[iSky] = fl.sky[iSky] * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8342            0 :             FFTot.sky[iSky] += firstFlux.sky[iSky];
    8343              :             //                FLSKTot( iSky ) += FLSK( iSky, iBackElem );
    8344              :         }
    8345            0 :         firstFlux.sun = fl.sun * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8346            0 :         FFTot.sun += firstFlux.sun;
    8347            0 :         FLTot.sun += fl.sun;
    8348              : 
    8349            0 :         firstFlux.sunDisk = fl.sunDisk * complexWinGeom.AveRhoVisOverlap(iBackElem);
    8350            0 :         FFTot.sunDisk += firstFlux.sunDisk;
    8351            0 :         FLTot.sunDisk += fl.sunDisk;
    8352              :     }
    8353              : 
    8354            0 :     auto const &thisEnclDaylight = dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex);
    8355            0 :     Real64 EnclInsideSurfArea = thisEnclDaylight.totInsSurfArea;
    8356              : 
    8357            0 :     auto &eintsk = dl->reflIllum(IHR)[iWinCover_Bare];
    8358            0 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8359            0 :         eintsk.sky[iSky] = FFTot.sky[iSky] * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8360              :     } // for (iSky)
    8361              : 
    8362            0 :     dl->reflIllum(IHR)[iWinCover_Bare].sun =
    8363            0 :         FFTot.sun * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8364            0 :     dl->reflIllum(IHR)[iWinCover_Bare].sunDisk =
    8365            0 :         FFTot.sunDisk * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
    8366              : 
    8367            0 :     if (allocated(FL)) {
    8368            0 :         FL.deallocate();
    8369              :     }
    8370              :     // if (allocated(FLSU)) FLSU.deallocate();
    8371              :     // if (allocated(FLSUdisk)) FLSUdisk.deallocate();
    8372              : 
    8373            0 :     if (allocated(FirstFlux)) {
    8374            0 :         FirstFlux.deallocate();
    8375              :     }
    8376              :     // if (allocated(FirstFluxSU)) FirstFluxSU.deallocate();
    8377              :     // if (allocated(FirstFluxSUdisk)) FirstFluxSUdisk.deallocate();
    8378              : 
    8379            0 :     if (allocated(ElementLuminance)) {
    8380            0 :         ElementLuminance.deallocate();
    8381              :     }
    8382              :     // if (allocated(ElementLuminanceSun)) ElementLuminanceSun.deallocate();
    8383              :     // if (allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.deallocate();
    8384            0 : }
    8385              : 
    8386            0 : void DayltgDirectIllumComplexFenestration(EnergyPlusData &state,
    8387              :                                           int const IWin,      // Window index
    8388              :                                           int const WinEl,     // Current window element counter
    8389              :                                           int const IHR,       // Hour of day
    8390              :                                           int const iRefPoint, // reference point index
    8391              :                                           CalledFor const CalledFrom,
    8392              :                                           int const MapNum)
    8393              : {
    8394              : 
    8395              :     // SUBROUTINE INFORMATION:
    8396              :     //       AUTHOR         Simon Vidanovic
    8397              :     //       DATE WRITTEN   June 2013
    8398              : 
    8399            0 :     auto &dl = state.dataDayltg;
    8400            0 :     auto &s_surf = state.dataSurface;
    8401              : 
    8402              :     // Luminances from different sources to the window
    8403            0 :     Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
    8404              :     // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side),
    8405              :     // exluding beam
    8406              :     // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side),
    8407              :     // due to sun beam
    8408              : 
    8409              :     int RefPointIndex; // reference point patch number
    8410              : 
    8411              :     Real64 dirTrans;    // directional BSDF transmittance
    8412              :     Real64 dOmega;      // solid view angle of current element
    8413              :     Real64 zProjection; // z-axe projection of solid view angle (used to calculate amount of light at horizontal surface
    8414              :     // laying at reference point)
    8415              : 
    8416            0 :     int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
    8417            0 :     auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
    8418            0 :     int iConst = s_surf->SurfaceWindow(IWin).ComplexFen.State(CurCplxFenState).Konst;
    8419            0 :     int NIncBasis = complexWin.Geom(CurCplxFenState).Inc.NBasis;
    8420              : 
    8421            0 :     if (!allocated(ElementLuminance)) {
    8422            0 :         ElementLuminance.allocate(NIncBasis);
    8423              :     }
    8424            0 :     ElementLuminance = Illums();
    8425              :     // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
    8426              :     // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
    8427              : 
    8428            0 :     ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
    8429              : 
    8430              :     // find number of outgoing basis towards current reference point
    8431            0 :     if (CalledFrom == CalledFor::RefPoint) {
    8432            0 :         RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).RefPoint(iRefPoint).RefPointIndex(WinEl);
    8433            0 :         dOmega = complexWin.RefPoint(iRefPoint).SolidAngle(WinEl);
    8434            0 :         zProjection = complexWin.RefPoint(iRefPoint).SolidAngleVec(WinEl).z;
    8435            0 :     } else if (CalledFrom == CalledFor::MapPoint) {
    8436            0 :         assert(MapNum > 0);
    8437            0 :         RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).IlluminanceMap(iRefPoint, MapNum).RefPointIndex(WinEl);
    8438            0 :         dOmega = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngle(WinEl);
    8439            0 :         zProjection = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngleVec(WinEl).z;
    8440              :     }
    8441              : 
    8442            0 :     Illums WinLum = Illums();
    8443            0 :     Illums EDir = Illums();
    8444              : 
    8445            0 :     for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
    8446              :         // LambdaInc = ComplexWind(IWin)%Geom(CurCplxFenState)%Inc%Lamda(iIncElem)
    8447            0 :         dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(RefPointIndex, iIncElem);
    8448              : 
    8449            0 :         auto const &elemLum = ElementLuminance(iIncElem);
    8450            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8451            0 :             WinLum.sky[iSky] += dirTrans * elemLum.sky[iSky];
    8452              :         }
    8453              : 
    8454            0 :         WinLum.sun += dirTrans * elemLum.sun;
    8455              : 
    8456              :         // For sun disk need to go throug outgoing directions and see which directions actually contain reference point
    8457              :     }
    8458              : 
    8459            0 :     if (zProjection > 0.0) {
    8460            0 :         for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8461            0 :             EDir.sky[iSky] = WinLum.sky[iSky] * dOmega * zProjection;
    8462              :         }
    8463            0 :         EDir.sun = WinLum.sun * dOmega * zProjection;
    8464              :     }
    8465              : 
    8466              :     // Store solution in global variables
    8467            0 :     auto &avwlsk = dl->avgWinLum(IHR)[iWinCover_Bare];
    8468            0 :     auto &edirsk = dl->dirIllum(IHR)[iWinCover_Bare];
    8469              : 
    8470            0 :     for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    8471            0 :         avwlsk.sky[iSky] += WinLum.sky[iSky];
    8472            0 :         edirsk.sky[iSky] += EDir.sky[iSky];
    8473              :     }
    8474              : 
    8475            0 :     dl->avgWinLum(IHR)[iWinCover_Bare].sun += WinLum.sun;
    8476            0 :     dl->dirIllum(IHR)[iWinCover_Bare].sun += EDir.sun;
    8477              :     // AVWLSUdisk(1,IHR) = AVWLSUdisk(1,IHR) + WinLumSUdisk
    8478            0 : } // DayltgDirectIllumComplexFenestration()
    8479              : 
    8480            0 : void DayltgDirectSunDiskComplexFenestration(EnergyPlusData &state,
    8481              :                                             int const iWin,  // Window index
    8482              :                                             int const iHour, // Hour of day
    8483              :                                             int const iRefPoint,
    8484              :                                             int const NumEl,            // Total number of window elements
    8485              :                                             Real64 const AZVIEW,        // Azimuth of view vector in absolute coord system for
    8486              :                                             CalledFor const CalledFrom, // indicate  which type of routine called this routine
    8487              :                                             int const MapNum)
    8488              : {
    8489              : 
    8490              :     // SUBROUTINE INFORMATION:
    8491              :     //       AUTHOR         Simon Vidanovic
    8492              :     //       DATE WRITTEN   June 2013
    8493              : 
    8494              :     // PURPOSE OF THIS SUBROUTINE:
    8495              :     // Calculate illuminance from sun disk for complex fenestration systems
    8496              : 
    8497              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8498            0 :     auto &dl = state.dataDayltg;
    8499            0 :     auto &s_surf = state.dataSurface;
    8500              : 
    8501            0 :     assert(CalledFrom != CalledFor::MapPoint || MapNum > 0);
    8502              : 
    8503            0 :     auto const &window = s_surf->SurfaceWindow(iWin);
    8504            0 :     int CurCplxFenState = window.ComplexFen.CurrentState;
    8505            0 :     int iConst = window.ComplexFen.State(CurCplxFenState).Konst;
    8506              : 
    8507            0 :     auto const &complexWindow = state.dataBSDFWindow->ComplexWind(iWin);
    8508            0 :     auto const &complexWindowGeom = complexWindow.Geom(CurCplxFenState);
    8509            0 :     auto const &complexWindowDayltgGeom = complexWindow.DaylghtGeom(CurCplxFenState);
    8510            0 :     int SolBmIndex = complexWindowGeom.SolBmIndex(iHour, state.dataGlobal->TimeStep);
    8511              : 
    8512            0 :     Real64 WindowSolidAngleDaylightPoint = (CalledFrom == CalledFor::RefPoint) ? window.refPts(iRefPoint).solidAngWtd : 0.0;
    8513            0 :     if (WindowSolidAngleDaylightPoint < 1e-6) {
    8514            0 :         return;
    8515              :     }
    8516              : 
    8517            0 :     Illums WinLum;
    8518            0 :     Illums ElemLum;
    8519              : 
    8520            0 :     int NTrnBasis = complexWindowGeom.Trn.NBasis;
    8521            0 :     for (int iTrnElem = 1; iTrnElem <= NTrnBasis; ++iTrnElem) {
    8522              :         // if ray from any part of the window can reach reference point
    8523              :         int refPointIntersect = (CalledFrom == CalledFor::RefPoint)
    8524            0 :                                     ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPointIntersection(iTrnElem)
    8525            0 :                                     : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPointIntersection(iTrnElem);
    8526              : 
    8527            0 :         if (refPointIntersect == 0) {
    8528            0 :             continue;
    8529              :         }
    8530              : 
    8531            0 :         Real64 PosFac = (CalledFrom == CalledFor::RefPoint) ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPtIntPosFac(iTrnElem)
    8532            0 :                                                             : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPtIntPosFac(iTrnElem);
    8533              : 
    8534            0 :         Real64 RayZ = -complexWindowGeom.sTrn(iTrnElem).z;
    8535              : 
    8536              :         // Need to recalculate position factor for dominant direction in case of specular bsdf.  Otherwise this will produce
    8537              :         // very inaccurate results because of position factor of the sun and bsdf pach can vary by lot
    8538            0 :         if (iTrnElem == SolBmIndex) {
    8539            0 :             Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
    8540            0 :             Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
    8541            0 :             PosFac = DayltgGlarePositionFactor(XR, YR);
    8542            0 :             RayZ = dl->sunAngles.sinPhi;
    8543              :         }
    8544              : 
    8545            0 :         if (PosFac == 0.0) {
    8546            0 :             continue;
    8547              :         }
    8548              : 
    8549            0 :         Real64 dirTrans = (SolBmIndex > 0) ? state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iTrnElem, SolBmIndex) : 0.0;
    8550            0 :         Real64 LambdaTrn = complexWindowGeom.Trn.Lamda(iTrnElem);
    8551            0 :         Vector3<Real64> V = -complexWindowGeom.sTrn(iTrnElem);
    8552            0 :         Vector3<Real64> RWin = s_surf->Surface(iWin).Centroid;
    8553            0 :         Real64 TransBeam = DayltgHitObstruction(state, iHour, iWin, RWin, V);
    8554              : 
    8555            0 :         WinLum.sunDisk += (14700.0 * std::sqrt(0.000068 * PosFac) * double(NumEl) / std::pow(WindowSolidAngleDaylightPoint, 0.8)) * dirTrans *
    8556            0 :                           LambdaTrn * TransBeam;
    8557              : 
    8558            0 :         ElemLum.sunDisk += RayZ * dirTrans * LambdaTrn * TransBeam;
    8559            0 :     } // for (iTrnElem)
    8560              : 
    8561            0 :     dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = WinLum.sunDisk;
    8562            0 :     dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = ElemLum.sunDisk;
    8563              : }
    8564              : 
    8565      2509936 : Real64 DayltgSkyLuminance(EnergyPlusData const &state,
    8566              :                           SkyType sky,        // Sky type: 1=clear, 2=clear turbid, 3=intermediate, 4=overcast
    8567              :                           Real64 const THSKY, // Azimuth and altitude of sky element (radians)
    8568              :                           Real64 const PHSKY)
    8569              : {
    8570              : 
    8571              :     // SUBROUTINE INFORMATION:
    8572              :     //       AUTHOR         Fred Winkelmann
    8573              :     //       DATE WRITTEN   July 1997
    8574              : 
    8575              :     // PURPOSE OF THIS SUBROUTINE:
    8576              :     // Called by CalcDayltgCoefficients, DayltgExtHorizIllum AND DayltgInterReflectedIllum.  gives
    8577              :     // luminance in cd/m2 for four different sky types, as described in R.Perez, P.Ineichen,
    8578              :     // R.Seals, J.Michalsky and R.Stewart, "Modeling daylight availability and irradiance
    8579              :     // components from direct and global irradiance," Solar Energy 44, 1990, 271-289.
    8580              :     // The luminance distributions in this routine are normalized such that
    8581              :     // the zenith luminance is 1.0, i.e., DayltgSkyLuminance =
    8582              :     // (sky luminance at THSKY, PHSKY)/(zenith luminance), which is dimensionless.
    8583              :     // The sky types are:
    8584              :     // 1. Standard CIE clear sky
    8585              :     // 2. Standard CIE high-turbidity clear sky
    8586              :     // 3. CIE intermediate sky
    8587              :     // 4. CIE overcast sky
    8588              : 
    8589              :     // METHODOLOGY EMPLOYED:
    8590              : 
    8591              :     // REFERENCES:
    8592              :     // Based on DOE-2.1E subroutine DSKYLU, which did only clear and overcast skies.
    8593              : 
    8594              :     // OTHER NOTES:
    8595              :     // THSKY ranges from 0 to 2Pi starting with 0 directly East and rotating clockwise.
    8596              :     // PHSKY ranges from 0 to Pi starting with 0 at the horizon and Pi/2 at the zenith.
    8597              : 
    8598              :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    8599      2509936 :     auto const &dl = state.dataDayltg;
    8600              : 
    8601      2509936 :     Real64 G = 0.0;    // Angle between sun and element of sky (radians)
    8602      2509936 :     Real64 COSG = 0.0; // Cosine of G
    8603              : 
    8604      2509936 :     Real64 SPHSKY = max(std::sin(PHSKY), 0.01); // Prevent floating point underflows
    8605      2509936 :     Real64 Z = Constant::PiOvr2 - dl->sunAngles.phi;
    8606      2509936 :     if (sky != SkyType::Overcast) { // Following not needed for overcast sky
    8607      1882452 :         COSG = SPHSKY * dl->sunAngles.sinPhi + std::cos(PHSKY) * dl->sunAngles.cosPhi * std::cos(THSKY - dl->sunAngles.theta);
    8608      1882452 :         COSG = max(DataPrecisionGlobals::constant_minusone, min(COSG, 1.0)); // Prevent out of range due to roundoff
    8609      1882452 :         G = std::acos(COSG);
    8610              :     }
    8611              : 
    8612      2509936 :     switch (sky) {
    8613       627484 :     case SkyType::Clear: {
    8614       627484 :         Real64 Z1 = 0.910 + 10.0 * std::exp(-3.0 * G) + 0.45 * COSG * COSG;
    8615       627484 :         Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
    8616       627484 :         Real64 Z3 = 0.27385 * (0.91 + 10.0 * std::exp(-3.0 * Z) + 0.45 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
    8617       627484 :         return Z1 * Z2 / Z3;
    8618              : 
    8619              :     } break;
    8620       627484 :     case SkyType::ClearTurbid: {
    8621       627484 :         Real64 Z1 = 0.856 + 16.0 * std::exp(-3.0 * G) + 0.3 * COSG * COSG;
    8622       627484 :         Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
    8623       627484 :         Real64 Z3 = 0.27385 * (0.856 + 16.0 * std::exp(-3.0 * Z) + 0.3 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
    8624       627484 :         return Z1 * Z2 / Z3;
    8625              : 
    8626              :     } break;
    8627              : 
    8628       627484 :     case SkyType::Intermediate: {
    8629       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;
    8630       627484 :         Real64 Z2 = std::exp(-G * 0.563 * ((dl->sunAngles.phi - 0.008) * (PHSKY + 1.059) + 0.812));
    8631       627484 :         Real64 Z3 = 0.99224 * std::sin(2.6 * dl->sunAngles.phi + 0.316) + 2.73852;
    8632       627484 :         Real64 Z4 = std::exp(-Z * 0.563 * ((dl->sunAngles.phi - 0.008) * 2.6298 + 0.812));
    8633       627484 :         return Z1 * Z2 / (Z3 * Z4);
    8634              :     } break;
    8635       627484 :     case SkyType::Overcast: {
    8636       627484 :         return (1.0 + 2.0 * SPHSKY) / 3.0;
    8637              :     } break;
    8638            0 :     default:
    8639            0 :         assert(false);
    8640              :         return 0.0;
    8641              :     }
    8642              : }
    8643              : 
    8644          816 : Real64 ProfileAngle(EnergyPlusData &state,
    8645              :                     int const SurfNum,                                     // Surface number
    8646              :                     Vector3<Real64> const &CosDirSun,                      // Solar direction cosines
    8647              :                     DataWindowEquivalentLayer::Orientation const HorOrVert // If HORIZONTAL, calculates ProfileAngHor
    8648              : )
    8649              : {
    8650              : 
    8651              :     // SUBROUTINE INFORMATION:
    8652              :     //       AUTHOR         Fred Winkelmann
    8653              :     //       DATE WRITTEN   May 2001
    8654              : 
    8655              :     // PURPOSE OF THIS SUBROUTINE:
    8656              :     // Calculates profile angle for a surface.
    8657              : 
    8658              :     // Locals
    8659              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8660              :     // For HorOrVert = HORIZONTAL,
    8661              :     //  this is the incidence angle in a plane that is normal to the window
    8662              :     //  and parallel to the Y-axis of the window (the axis along
    8663              :     //  which the height of the window is measured).
    8664              :     //  For HorOrVert = VERTICAL,
    8665              :     //  this is the incidence angle in a plane that is normal to the window
    8666              :     //  and parallel to the X-axis of the window (the axis along
    8667              :     //  which the width of the window is measured).
    8668              :     // If VERTICAL, calculates ProfileAngVert
    8669          816 :     auto &s_surf = state.dataSurface;
    8670              : 
    8671          816 :     auto const &surf = s_surf->Surface(SurfNum);
    8672          816 :     if (HorOrVert == DataWindowEquivalentLayer::Orientation::Horizontal) {  // Profile angle for horizontal structures
    8673          646 :         Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad; // Window elevation: angle between outward normal and horizontal (radians)
    8674          646 :         Real64 AzimWin = (90.0 - surf.Azimuth) * Constant::DegToRad;        // Window azimuth (radians)
    8675          646 :         Real64 ElevSun = std::asin(CosDirSun.z);                            // Sun elevation; angle between sun and horizontal (radians)
    8676          646 :         Real64 AzimSun = std::atan2(CosDirSun.y, CosDirSun.x);              // Sun azimuth (radians)
    8677          646 :         return std::atan(std::sin(ElevSun) / std::abs(std::cos(ElevSun) * std::cos(AzimWin - AzimSun))) - ElevWin;
    8678              :     } else { // Profile angle for vertical structures
    8679          170 :         Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad;
    8680          170 :         Real64 AzimWin = surf.Azimuth * Constant::DegToRad;    // 7952
    8681          170 :         Real64 AzimSun = std::atan2(CosDirSun.x, CosDirSun.y); // 7952
    8682              : 
    8683              :         Real64 ProfileAng;
    8684          170 :         if (std::abs(ElevWin) < 0.1) {      // Near-vertical window
    8685          170 :             ProfileAng = AzimWin - AzimSun; // CR7952 allow sign changes.
    8686              :         } else {
    8687            0 :             Vector3<Real64> WinNorm = surf.OutNormVec; // Window outward normal unit vector
    8688            0 :             Real64 ThWin = AzimWin - Constant::PiOvr2;
    8689            0 :             Real64 const sin_ElevWin = std::sin(ElevWin);
    8690              :             // Cross product of WinNorm and vector along window baseline
    8691            0 :             Vector3<Real64> WinNormCrossBase = {-sin_ElevWin * std::cos(ThWin), sin_ElevWin * std::sin(ThWin), std::cos(ElevWin)};
    8692              :             // Projection of sun vector onto plane (perpendicular to window plane) determined
    8693              :             // by WinNorm and vector along baseline of window
    8694            0 :             Vector3<Real64> SunPrime = CosDirSun - WinNormCrossBase * dot(CosDirSun, WinNormCrossBase);
    8695            0 :             ProfileAng = std::abs(std::acos(dot(WinNorm, SunPrime) / SunPrime.magnitude()));
    8696              :             // CR7952 correct sign of result for vertical slats
    8697            0 :             if ((AzimWin - AzimSun) < 0.0) {
    8698            0 :                 ProfileAng = -1.0 * ProfileAng;
    8699              :             }
    8700            0 :         }
    8701              :         // Constrain to 0 to pi
    8702          170 :         if (ProfileAng > Constant::Pi) {
    8703           84 :             ProfileAng = 2.0 * Constant::Pi - ProfileAng;
    8704              :         }
    8705          170 :         return ProfileAng;
    8706              :     }
    8707              : }
    8708              : 
    8709            0 : void DayltgClosestObstruction(EnergyPlusData &state,
    8710              :                               Vector3<Real64> const &RecPt,  // Point on window from which ray emanates (m)
    8711              :                               Vector3<Real64> const &RayVec, // Unit vector along ray pointing away from window (m)
    8712              :                               int &NearestHitSurfNum,        // Surface number of nearest obstruction that is hit by ray;
    8713              :                               Vector3<Real64> &NearestHitPt  // Ray's hit point on nearest obstruction (m)
    8714              : )
    8715              : {
    8716              : 
    8717              :     // SUBROUTINE INFORMATION:
    8718              :     //       AUTHOR         Fred Winkelmann
    8719              :     //       DATE WRITTEN   November 2003
    8720              : 
    8721              :     // PURPOSE OF THIS SUBROUTINE:
    8722              :     // Determines surface number and hit point of closest exterior obstruction hit
    8723              :     // by a ray from a window. If no obstruction is hit, NearestHitSurfNum = 0.
    8724              : 
    8725              :     // Locals
    8726              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8727              :     //  = 0 if no obstruction is hit.
    8728            0 :     auto &s_surf = state.dataSurface;
    8729              : 
    8730              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8731            0 :     Vector3<Real64> HitPt; // Hit point on an obstruction (m)
    8732              :     bool hit;              // True iff obstruction is hit
    8733              : 
    8734            0 :     NearestHitSurfNum = 0;
    8735            0 :     Real64 NearestHitDistance_sq(std::numeric_limits<Real64>::max()); // Distance squared from receiving point to nearest hit point for a ray (m^2)
    8736            0 :     NearestHitPt = 0.0;
    8737            0 :     if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
    8738              : 
    8739            0 :         for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    8740              :             // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
    8741            0 :             hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
    8742            0 :             if (!hit) { // Ray pierces surface
    8743            0 :                 continue;
    8744              :             }
    8745              : 
    8746              :             // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
    8747              :             // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
    8748            0 :             if ((s_surf->Surface(ObsSurfNum).Class == SurfaceClass::Window) && (s_surf->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum)) {
    8749            0 :                 NearestHitSurfNum = ObsSurfNum;
    8750              :             } else {
    8751              :                 // Distance squared from receiving point to hit point
    8752            0 :                 Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
    8753              :                 // Reset NearestHitSurfNum and NearestHitDistance_sq if this hit point is closer than previous closest
    8754            0 :                 if (HitDistance_sq < NearestHitDistance_sq) {
    8755            0 :                     NearestHitDistance_sq = HitDistance_sq;
    8756            0 :                     NearestHitSurfNum = ObsSurfNum;
    8757            0 :                     NearestHitPt = HitPt;
    8758              :                 }
    8759              :             } // End of check if obstruction was hit
    8760            0 :         } // for (ObsSurfNum)
    8761              : 
    8762              :     } else { // Surface octree search
    8763              : 
    8764            0 :         SurfaceData const *nearestHitSurface(nullptr);
    8765              : 
    8766              :         // Lambda function for the octree to test for surface hit
    8767            0 :         auto surfaceHit = [&s_surf, &RecPt, &RayVec, &hit, &NearestHitDistance_sq, &nearestHitSurface, &NearestHitPt](SurfaceData const &surface) {
    8768            0 :             if (surface.IsShadowPossibleObstruction) {
    8769            0 :                 Vector3<Real64> HitPt;
    8770              :                 // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
    8771            0 :                 hit = PierceSurface(surface, RecPt, RayVec, HitPt); // Check if ray pierces surface
    8772            0 :                 if (!hit) {
    8773            0 :                     return;
    8774              :                 }
    8775              : 
    8776              :                 // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
    8777              :                 // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
    8778            0 :                 if ((surface.Class == SurfaceClass::Window) && (surface.BaseSurf > 0) && (&s_surf->Surface(surface.BaseSurf) == nearestHitSurface)) {
    8779            0 :                     nearestHitSurface = &surface;
    8780              :                 } else {
    8781              :                     // Distance squared from receiving point to hit point
    8782            0 :                     Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
    8783              :                     // Reset nearestHitSurface and NearestHitDistance_sq if this hit point is closer than previous closest
    8784            0 :                     if (HitDistance_sq < NearestHitDistance_sq) {
    8785            0 :                         NearestHitDistance_sq = HitDistance_sq;
    8786            0 :                         nearestHitSurface = &surface;
    8787            0 :                         NearestHitPt = HitPt;
    8788              :                     }
    8789              :                 } // End of check if obstruction was hit
    8790            0 :             }
    8791            0 :         };
    8792              : 
    8793              :         // Process octree surface candidates
    8794            0 :         Vector3<Real64> const RayVec_inv(SurfaceOctreeCube::safe_inverse(RayVec));
    8795            0 :         state.dataHeatBalMgr->surfaceOctree.processSurfaceRayIntersectsCube(RecPt, RayVec, RayVec_inv, surfaceHit);
    8796            0 :         if (nearestHitSurface != nullptr) { // Find surface number: This is inefficient: Improve when surfaces know their own number
    8797            0 :             for (int i = 1; i <= s_surf->TotSurfaces; ++i) {
    8798            0 :                 if (&s_surf->Surface(i) == nearestHitSurface) {
    8799            0 :                     NearestHitSurfNum = i;
    8800            0 :                     break;
    8801              :                 }
    8802              :             }
    8803            0 :             assert(NearestHitSurfNum != 0);
    8804              :         }
    8805            0 :     }
    8806            0 : } // DayltgClosestObstruction()
    8807              : 
    8808            0 : Real64 DayltgSurfaceLumFromSun(EnergyPlusData &state,
    8809              :                                int const IHR,                   // Hour number
    8810              :                                Vector3<Real64> const &Ray,      // Ray from window to reflecting surface (m)
    8811              :                                int const ReflSurfNum,           // Number of surface for which luminance is being calculated
    8812              :                                Vector3<Real64> const &ReflHitPt // Point on ReflSurfNum for luminance calculation (m)
    8813              : )
    8814              : {
    8815              : 
    8816              :     // SUBROUTINE INFORMATION:
    8817              :     //       AUTHOR         Fred Winkelmann
    8818              :     //       DATE WRITTEN   November 2003
    8819              : 
    8820              :     // PURPOSE OF THIS SUBROUTINE:
    8821              :     // Calculates exterior surface luminance due to beam solar diffuse reflection.
    8822              : 
    8823              :     // Locals
    8824              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    8825              :     //  beam normal illuminance (cd/m2)
    8826              : 
    8827              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8828            0 :     Vector3<Real64> SurfaceLumFromSunReflNorm; // Unit normal to reflecting surface (m)
    8829            0 :     Vector3<Real64> SurfaceLumFromSunObsHitPt; // Hit point on obstruction (m)
    8830              :     bool hitObs;                               // True iff obstruction is hit
    8831              :     Real64 DiffVisRefl;                        // Diffuse visible reflectance of ReflSurfNum
    8832              : 
    8833            0 :     auto &s_surf = state.dataSurface;
    8834              :     // Skip daylighting shelves since reflection from these is separately calculated
    8835            0 :     if (s_surf->SurfDaylightingShelfInd(ReflSurfNum) > 0) {
    8836            0 :         return 0.0;
    8837              :     }
    8838              : 
    8839            0 :     auto const &reflSurf = s_surf->Surface(ReflSurfNum);
    8840              : 
    8841              :     // Normal to reflecting surface in hemisphere containing window element
    8842            0 :     SurfaceLumFromSunReflNorm = reflSurf.OutNormVec;
    8843            0 :     if (reflSurf.IsShadowing) {
    8844            0 :         if (dot(SurfaceLumFromSunReflNorm, Ray) > 0.0) {
    8845            0 :             SurfaceLumFromSunReflNorm *= -1.0;
    8846              :         }
    8847              :     }
    8848              :     // Cosine of angle of incidence of sun at HitPt if sun were to reach HitPt
    8849            0 :     Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
    8850            0 :     Real64 CosIncAngAtHitPt = dot(SurfaceLumFromSunReflNorm, SUNCOS_IHR);
    8851              :     // Require that the sun be in front of this surface relative to window element
    8852            0 :     if (CosIncAngAtHitPt <= 0.0) {
    8853            0 :         return 0.0; // Sun is in back of reflecting surface
    8854              :     }
    8855              :     // Sun reaches ReflHitPt if vector from ReflHitPt to sun is unobstructed
    8856            0 :     hitObs = false;
    8857            0 :     for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
    8858              :         // Exclude as a possible obstructor ReflSurfNum and its base surface (if it has one)
    8859            0 :         if (ObsSurfNum == ReflSurfNum || ObsSurfNum == reflSurf.BaseSurf) {
    8860            0 :             continue;
    8861              :         }
    8862            0 :         hitObs = PierceSurface(state, ObsSurfNum, ReflHitPt, SUNCOS_IHR, SurfaceLumFromSunObsHitPt);
    8863            0 :         if (hitObs) {
    8864            0 :             break;
    8865              :         }
    8866            0 :     }
    8867              : 
    8868            0 :     if (hitObs) {
    8869            0 :         return 0.0; // Obstruction was hit, blocking s auto surfaceHit = [&state, &GroundHitPtun
    8870              :     }
    8871              :     // Obstruction was not hit; sun reaches ReflHitPt.
    8872              :     // Calculate luminance at ReflHitPt due to beam solar reflection (for unit beam normal illuminance)
    8873            0 :     if (reflSurf.IsShadowing) {
    8874            0 :         DiffVisRefl = s_surf->SurfShadowDiffuseVisRefl(ReflSurfNum);
    8875              :         // Note that if the shadowing surface has a non-zero glazing fraction (e.g., neighboring bldg) that the above is
    8876              :         // (1 - glazing fraction) * (vis refl of opaque part of shadowing surface); specular reflection is
    8877              :         // excluded in this value of DiffVisRefl.
    8878              :     } else { // Exterior building surface
    8879            0 :         if (!state.dataConstruction->Construct(reflSurf.Construction).TypeIsWindow) {
    8880            0 :             DiffVisRefl = 1.0 - state.dataConstruction->Construct(reflSurf.Construction).OutsideAbsorpSolar;
    8881              :         } else {
    8882              :             // Window; assume bare so no beam-to-diffuse reflection
    8883            0 :             DiffVisRefl = 0.0;
    8884              :         }
    8885              :     }
    8886            0 :     return CosIncAngAtHitPt * DiffVisRefl / Constant::Pi;
    8887            0 : }
    8888              : 
    8889        88617 : void DayltgInteriorMapIllum(EnergyPlusData &state)
    8890              : {
    8891              : 
    8892              :     // *****super modified version of DayltgInteriorIllum by Peter Graham Ellis
    8893              :     // *****removes all control code, just calculates illum with previously determined control settings
    8894              :     // *****this should be packaged into a subroutine called from 2 places
    8895              : 
    8896              :     // SUBROUTINE INFORMATION:
    8897              :     //       AUTHOR         Fred Winkelmann
    8898              :     //       DATE WRITTEN   July 1997
    8899              :     //       MODIFIED       March 2000, FW: interpolate clear-sky daylight factors using
    8900              :     //                      HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
    8901              :     //                      only HourOfDay was used
    8902              :     //                      Jan 2001, FW: interpolate in slat angle for windows with blinds
    8903              :     //                      that have movable slats
    8904              :     //                      Dec 2003, FW: fix bug--even though between-glass shade/blind is on
    8905              :     //                        daylight illum at ref pt was calculated as though it was off
    8906              :     //                      June 2009, TH: modified for thermochromic windows
    8907              :     //                      March 2010, TH: fix bug (CR 8057) for electrochromic windows
    8908              :     //       RE-ENGINEERED  na
    8909              : 
    8910              :     // PURPOSE OF THIS SUBROUTINE:
    8911              :     // Using daylighting factors and exterior illuminance, determine
    8912              :     // the current-hour interior daylight illuminance and glare index
    8913              :     // at each reference point in a space.
    8914              : 
    8915              :     // Called by InitSurfaceHeatBalance.
    8916              : 
    8917              :     // REFERENCES:
    8918              :     // Based on DOE-2.1E subroutine DINTIL.
    8919        88617 :     auto &dl = state.dataDayltg;
    8920              : 
    8921              :     // Locals
    8922        88617 :     Array1D<Real64> daylight_illum;
    8923              : 
    8924              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    8925              :     int iSky1; // Sky type index values for averaging two sky types
    8926              :     int iSky2;
    8927              :     Real64 SkyWeight;    // Weighting factor used to average two different sky types
    8928              :     Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
    8929              :     //   luminous efficacy and horizontal illuminance from averaged sky
    8930              : 
    8931        88617 :     if (state.dataGlobal->WarmupFlag) {
    8932        79229 :         return;
    8933              :     }
    8934              : 
    8935         9388 :     auto &s_surf = state.dataSurface;
    8936              : 
    8937         9388 :     daylight_illum.allocate(MaxMapRefPoints);
    8938              : 
    8939              :     //              Initialize reference point illuminance and window background luminance
    8940              : 
    8941         9447 :     for (auto &thisMap : dl->illumMaps) {
    8942           59 :         int enclNum = thisMap.enclIndex;
    8943           59 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    8944              : 
    8945           59 :         int NREFPT = thisMap.TotalMapRefPoints; // Number of daylighting map reference points
    8946              : 
    8947           59 :         daylight_illum = 0.0;
    8948              : 
    8949           59 :         if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
    8950           50 :             SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
    8951           50 :             iSky1 = (int)SkyType::Clear;
    8952           50 :             iSky2 = (int)SkyType::ClearTurbid;
    8953            9 :         } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
    8954            8 :             SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
    8955            8 :             iSky1 = (int)SkyType::ClearTurbid;
    8956            8 :             iSky2 = (int)SkyType::Intermediate;
    8957              :         } else { // Sky is average of intermediate and overcast
    8958            1 :             SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
    8959            1 :             iSky1 = (int)SkyType::Intermediate;
    8960            1 :             iSky2 = (int)SkyType::Overcast;
    8961              :         }
    8962              : 
    8963              :         //              First loop over windows in this space.
    8964              :         //              Find contribution of each window to the daylight illum
    8965              :         //              and to the glare numerator at each reference point.
    8966              :         //              Use shading flags set in WindowShadingManager.
    8967              : 
    8968           59 :         auto &daylFacHrCurr = thisMap.daylFac[state.dataGlobal->HourOfDay];
    8969           59 :         auto &daylFacHrPrev = thisMap.daylFac[state.dataGlobal->PreviousHour];
    8970              : 
    8971          118 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    8972           59 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    8973              : 
    8974              :             // Added TH 6/29/2009 for thermochromic windows
    8975           59 :             Real64 VTRatio = 1.0;
    8976           59 :             if (NREFPT > 0) {
    8977           59 :                 int IConst = s_surf->Surface(IWin).Construction;
    8978           59 :                 auto const &construction = state.dataConstruction->Construct(IConst);
    8979           59 :                 if (construction.isTCWindow) {
    8980              :                     // For thermochromic windows, daylight and glare factors are always calculated
    8981              :                     //  based on the master construction. They need to be adjusted by the VTRatio, including:
    8982              :                     //  ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
    8983              :                     //  DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
    8984            0 :                     Real64 VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
    8985            0 :                     Real64 VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
    8986            0 :                     VTRatio = VTNow / VTMaster;
    8987              :                 }
    8988              :             }
    8989              : 
    8990           59 :             Real64 wgtThisHr = state.dataGlobal->WeightNow;
    8991           59 :             Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
    8992              : 
    8993           59 :             std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
    8994              : 
    8995           59 :             auto &dfhr = DFHR[iWinCover_Bare];
    8996           59 :             auto &dfhrSh = DFHR[iWinCover_Shaded];
    8997              : 
    8998           59 :             auto &surfShade = s_surf->surfShades(IWin);
    8999              :             //              Loop over reference points
    9000         5959 :             for (int ILB = 1; ILB <= NREFPT; ++ILB) {
    9001              :                 // if (ILB != 5) continue;
    9002         5900 :                 auto const &illSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Bare];
    9003         5900 :                 auto const &illSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Bare];
    9004         5900 :                 auto const &illShSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Shaded];
    9005         5900 :                 auto const &illShSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Shaded];
    9006              : 
    9007              :                 //          Daylight factors for current sun position
    9008        29500 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    9009              :                     //                                ===Bare window===
    9010        23600 :                     dfhr.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr.sky[iSky] + wgtPrevHr * illSkyPrev.sky[iSky]);
    9011              : 
    9012        70800 :                     if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    9013        47200 :                         (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
    9014              : 
    9015              :                         //                                 ===Shaded window===
    9016              :                         // Shade, screen, blind with fixed slats, or diffusing glass
    9017            0 :                         dfhrSh.sky[iSky] = VTRatio * (wgtThisHr * illShSkyCurr.sky[iSky] + wgtPrevHr * illShSkyPrev.sky[iSky]);
    9018              :                     } // End of check if window is shaded or has diffusing glass
    9019              :                 } // for (iSky)
    9020              : 
    9021              :                 // Sun daylight factor for bare/shaded window
    9022         5900 :                 std::array<Illums, (int)DataSurfaces::WinCover::Num> tmpDFHR;
    9023        11800 :                 tmpDFHR[iWinCover_Bare].sun =
    9024         5900 :                     VTRatio * (wgtThisHr * (illSkyCurr.sun + illSkyCurr.sunDisk) + wgtPrevHr * (illSkyPrev.sun + illSkyPrev.sunDisk));
    9025              : 
    9026        17700 :                 if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
    9027        11800 :                     (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
    9028              : 
    9029              :                     //                                 ===Shaded window===
    9030              :                     // Shade, screen, blind with fixed slats, or diffusing glass
    9031            0 :                     tmpDFHR[iWinCover_Shaded].sun = VTRatio * (wgtThisHr * illShSkyCurr.sun + wgtPrevHr * illShSkyPrev.sun);
    9032              : 
    9033            0 :                     if (!surfShade.blind.slatBlockBeam) {
    9034            0 :                         tmpDFHR[iWinCover_Shaded].sun += VTRatio * (wgtThisHr * illShSkyCurr.sunDisk + wgtPrevHr * illShSkyPrev.sunDisk);
    9035              :                     }
    9036              :                 } // End of check if window is shaded or has diffusing glass
    9037              : 
    9038              :                 //              Get illuminance at ref point from bare and shaded window by
    9039              :                 //              multiplying daylight factors by exterior horizontal illuminance
    9040              : 
    9041              :                 // Adding 0.001 in the following prevents zero DayltgInteriorMapIllumHorIllSky in early morning or late evening when sun
    9042              :                 // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
    9043         5900 :                 Illums tmpHorIll; // Horizontal illuminance for different sky types
    9044         5900 :                 auto const &gilCurr = dl->horIllum[state.dataGlobal->HourOfDay];
    9045         5900 :                 auto const &gilPrev = dl->horIllum[state.dataGlobal->PreviousHour];
    9046        29500 :                 for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
    9047        23600 :                     tmpHorIll.sky[iSky] = wgtThisHr * gilCurr.sky[iSky] + wgtPrevHr * gilPrev.sky[iSky] + 0.001;
    9048              :                 }
    9049              : 
    9050              :                 // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
    9051              :                 // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
    9052              :                 // also calculated in DayltgLuminousEfficacy.
    9053         5900 :                 HorIllSkyFac = state.dataEnvrn->HISKF / ((1.0 - SkyWeight) * tmpHorIll.sky[iSky2] + SkyWeight * tmpHorIll.sky[iSky1]);
    9054              : 
    9055        11800 :                 for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
    9056        11800 :                     if (iWinCover == iWinCover_Shaded) {
    9057         5900 :                         if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
    9058            0 :                             break;
    9059              :                         }
    9060         5900 :                         if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) && !s_surf->SurfWinSolarDiffusing(IWin)) {
    9061         5900 :                             break;
    9062              :                         }
    9063              :                     }
    9064         5900 :                     auto const &dfhr3 = DFHR[iWinCover];
    9065              : 
    9066        11800 :                     thisMap.refPts(ILB).winLums(loop)[iWinCover] = tmpDFHR[iWinCover].sun * state.dataEnvrn->HISUNF +
    9067         5900 :                                                                    HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * tmpHorIll.sky[iSky1] +
    9068         5900 :                                                                                    dfhr3.sky[iSky2] * (1.0 - SkyWeight) * tmpHorIll.sky[iSky2]);
    9069              :                 }
    9070              : 
    9071              :             } // End of reference point loop
    9072              :         } // End of first loop over windows
    9073              : 
    9074              :         //              Second loop over windows. Find total daylight illuminance
    9075              :         //              and background luminance for each ref pt from all windows in
    9076              :         //              the space.  Use shading flags.
    9077              : 
    9078          118 :         for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    9079           59 :             int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    9080           59 :             auto const &surfWin = s_surf->SurfaceWindow(IWin);
    9081              : 
    9082           59 :             WinCover winCover = findWinShadingStatus(state, IWin);
    9083              : 
    9084              :             // CR 8057. 3/17/2010.
    9085              :             // Switchable windows may be in partially switched state rather than fully dark state
    9086           59 :             Real64 VTMULT = 1.0;
    9087              : 
    9088           59 :             int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
    9089           59 :             if (s_surf->Surface(IWin).HasShadeControl) {
    9090            0 :                 if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
    9091            0 :                     s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
    9092              :                     // switchable windows in partial or fully switched state,
    9093              :                     //  get its intermediate VT calculated in DayltgInteriorIllum
    9094            0 :                     int IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
    9095            0 :                     if (IConstShaded > 0) {
    9096              :                         // Visible transmittance (VT) of electrochromic (EC) windows in fully dark state
    9097            0 :                         Real64 VTDark = Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
    9098            0 :                         if (VTDark > 0) {
    9099            0 :                             VTMULT = s_surf->SurfWinVisTransSelected(IWin) / VTDark;
    9100              :                         }
    9101              :                     }
    9102              :                 }
    9103              :             }
    9104              : 
    9105         5959 :             for (int IL = 1; IL <= NREFPT; ++IL) {
    9106              :                 //              Determine if illuminance contribution is from bare or shaded window
    9107         5900 :                 daylight_illum(IL) += VTMULT * thisMap.refPts(IL).winLums(loop)[(int)winCover];
    9108              :             }
    9109              :         } // End of second window loop
    9110              : 
    9111              :         //              Variables for reporting
    9112         5959 :         for (int IL = 1; IL <= NREFPT; ++IL) {
    9113         5900 :             thisMap.refPts(IL).lums[iLum_Illum] = max(daylight_illum(IL), 0.0);
    9114              :         }
    9115              :     } // End loop over maps
    9116        88617 : } // DayltgInteriorMapIllum()
    9117              : 
    9118           61 : void ReportIllumMap(EnergyPlusData &state, int const MapNum)
    9119              : {
    9120              : 
    9121              :     // SUBROUTINE INFORMATION:
    9122              :     //       AUTHOR         Peter Ellis
    9123              :     //       DATE WRITTEN   May 2003
    9124              : 
    9125              :     // PURPOSE OF THIS SUBROUTINE:
    9126              :     // This subroutine produces the Daylighting Illuminance Map output.  Each separate map (by zone)
    9127              :     // is placed on a temporary file and later (see CloseReportIllumMaps) coallesced into a single
    9128              :     // output file.
    9129              : 
    9130              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    9131              : 
    9132           61 :     std::string MapNoString;
    9133           61 :     auto &dl = state.dataDayltg;
    9134              : 
    9135           61 :     if (dl->ReportIllumMap_firstTime) {
    9136            1 :         dl->ReportIllumMap_firstTime = false;
    9137            1 :         dl->FirstTimeMaps.dimension((int)dl->illumMaps.size(), true);
    9138            1 :         dl->EnvrnPrint.dimension((int)dl->illumMaps.size(), true);
    9139            1 :         dl->SavedMnDy.allocate((int)dl->illumMaps.size());
    9140              :     }
    9141              : 
    9142           61 :     auto &illumMap = dl->illumMaps(MapNum);
    9143              : 
    9144           61 :     if (dl->FirstTimeMaps(MapNum)) {
    9145              : 
    9146            2 :         dl->FirstTimeMaps(MapNum) = false;
    9147              : 
    9148            2 :         auto openMapFile = [&](const fs::path &filePath) -> InputOutputFile & {
    9149            2 :             auto &outputFile = *illumMap.mapFile;
    9150            2 :             outputFile.filePath = FileSystem::appendSuffixToPath(filePath, fmt::to_string(MapNum));
    9151            2 :             outputFile.ensure_open(state, "ReportIllumMap");
    9152            2 :             return outputFile;
    9153            2 :         };
    9154            2 :         if (dl->MapColSep == DataStringGlobals::CharTab) {
    9155            0 :             if (!openMapFile(state.files.outputMapTabFilePath).good()) {
    9156            0 :                 return;
    9157              :             }
    9158              :             //                CommaDelimited = false; //Unused Set but never used
    9159            2 :         } else if (dl->MapColSep == DataStringGlobals::CharComma) {
    9160            1 :             if (!openMapFile(state.files.outputMapCsvFilePath).good()) {
    9161            0 :                 return;
    9162              :             }
    9163              :             //                CommaDelimited = true; //Unused Set but never used
    9164              :         } else {
    9165            1 :             if (!openMapFile(state.files.outputMapTxtFilePath).good()) {
    9166            0 :                 return;
    9167              :             }
    9168              :             //                CommaDelimited = false; //Unused Set but never used
    9169              :         }
    9170              : 
    9171            2 :         dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
    9172              : 
    9173            2 :         illumMap.Name = format("{} at {:.2R}m", illumMap.Name, illumMap.Z);
    9174              :     }
    9175           61 :     if (dl->SavedMnDy(MapNum) != state.dataEnvrn->CurMnDyHr.substr(0, 5)) {
    9176            0 :         dl->EnvrnPrint(MapNum) = true;
    9177            0 :         dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
    9178              :     }
    9179              : 
    9180           61 :     illumMap.pointsHeader = "";
    9181           61 :     int rCount = 0;
    9182          122 :     for (auto &thisDayltgCtrl : dl->daylightControl) {
    9183           61 :         if (thisDayltgCtrl.zoneIndex != illumMap.zoneIndex) {
    9184            0 :             continue;
    9185              :         }
    9186              : 
    9187          184 :         for (int R = 1; R <= thisDayltgCtrl.TotalDaylRefPoints; ++R) {
    9188          123 :             ++rCount;
    9189          123 :             auto const &refPt = thisDayltgCtrl.refPts(R);
    9190          123 :             illumMap.pointsHeader += format(" RefPt{}=({:.2R}:{:.2R}:{:.2R}),", rCount, refPt.absCoords.x, refPt.absCoords.y, refPt.absCoords.z);
    9191              :         }
    9192              :     }
    9193              : 
    9194           61 :     if (rCount > 0) {
    9195              :         // Remove trailing comma
    9196           61 :         illumMap.pointsHeader.pop_back();
    9197              :     }
    9198           61 :     if (dl->EnvrnPrint(MapNum)) {
    9199            1 :         WriteDaylightMapTitle(
    9200            1 :             state, MapNum, *illumMap.mapFile, illumMap.Name, state.dataEnvrn->EnvironmentName, illumMap.zoneIndex, illumMap.pointsHeader, illumMap.Z);
    9201            1 :         dl->EnvrnPrint(MapNum) = false;
    9202              :     }
    9203              : 
    9204           61 :     if (!state.dataGlobal->WarmupFlag) {
    9205           60 :         if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) { // Report only hourly
    9206              : 
    9207           16 :             int linelen = 0;
    9208              :             // Write X scale column header
    9209           16 :             std::string mapLine = format(" {} {:02}:00", dl->SavedMnDy(MapNum), state.dataGlobal->HourOfDay);
    9210           16 :             if (illumMap.HeaderXLineLengthNeeded) {
    9211            1 :                 linelen = int(len(mapLine));
    9212              :             }
    9213           16 :             int RefPt = 1;
    9214          176 :             for (int X = 1; X <= illumMap.Xnum; ++X) {
    9215              :                 const std::string AddXorYString =
    9216          160 :                     format("{}({:.2R};{:.2R})=", dl->MapColSep, illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
    9217          160 :                 if (illumMap.HeaderXLineLengthNeeded) {
    9218           10 :                     linelen += int(len(AddXorYString));
    9219              :                 }
    9220          160 :                 mapLine += AddXorYString;
    9221          160 :                 ++RefPt;
    9222          160 :             } // X
    9223              : 
    9224           16 :             if (illumMap.HeaderXLineLengthNeeded) {
    9225            1 :                 illumMap.HeaderXLineLength = linelen;
    9226            1 :                 if (static_cast<std::string::size_type>(illumMap.HeaderXLineLength) > len(mapLine)) {
    9227            0 :                     ShowWarningError(state,
    9228            0 :                                      format("ReportIllumMap: Map=\"{}\" -- the X Header overflows buffer -- will be truncated at {} characters.",
    9229            0 :                                             illumMap.Name,
    9230            0 :                                             int(len(mapLine))));
    9231            0 :                     ShowContinueError(state, format("...needed {} characters. Please contact EnergyPlus support.", illumMap.HeaderXLineLength));
    9232              :                 }
    9233            1 :                 illumMap.HeaderXLineLengthNeeded = false;
    9234              :             }
    9235              : 
    9236           16 :             print(*illumMap.mapFile, "{}\n", mapLine);
    9237              : 
    9238              :             // Write Y scale prefix and illuminance values
    9239           16 :             RefPt = 1;
    9240          176 :             for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    9241          160 :                 mapLine = format("({:.2R};{:.2R})=", illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
    9242         1760 :                 for (int R = RefPt; R <= RefPt + illumMap.Xnum - 1; ++R) {
    9243         1600 :                     int IllumOut = nint(illumMap.refPts(R).lumsHr[iLum_Illum]);
    9244         1600 :                     std::string String = fmt::to_string(IllumOut);
    9245              :                     ;
    9246         1600 :                     if (!illumMap.refPts(R).inBounds) {
    9247            0 :                         String = "*" + String;
    9248              :                     }
    9249         1600 :                     mapLine += dl->MapColSep + String;
    9250         1600 :                 }
    9251              : 
    9252          160 :                 print(*illumMap.mapFile, "{}\n", mapLine);
    9253              : 
    9254          160 :                 RefPt += illumMap.Xnum;
    9255              :             } // X
    9256              : 
    9257           16 :             if (state.dataSQLiteProcedures->sqlite) {
    9258            0 :                 if (dl->SQFirstTime) {
    9259            0 :                     int const nX(maxval(dl->illumMaps, &IllumMap::Xnum));
    9260            0 :                     int const nY(maxval(dl->illumMaps, &IllumMap::Ynum));
    9261            0 :                     dl->XValue.allocate(nX);
    9262            0 :                     dl->YValue.allocate(nY);
    9263            0 :                     dl->IllumValue.allocate(nX, nY);
    9264            0 :                     dl->SQFirstTime = false;
    9265              :                 }
    9266              : 
    9267            0 :                 for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
    9268            0 :                     dl->YValue(Y) = illumMap.Ymin + (Y - 1) * illumMap.Yinc;
    9269            0 :                     for (int X = 1; X <= illumMap.Xnum; ++X) {
    9270            0 :                         dl->XValue(X) = illumMap.Xmin + (X - 1) * illumMap.Xinc;
    9271            0 :                         int IllumIndex = X + (Y - 1) * illumMap.Xnum;
    9272            0 :                         dl->IllumValue(X, Y) = nint(illumMap.refPts(IllumIndex).lumsHr[iLum_Illum]);
    9273            0 :                         if (!illumMap.refPts(IllumIndex).inBounds) {
    9274            0 :                             dl->IllumValue(X, Y) = -dl->IllumValue(X, Y);
    9275              :                         }
    9276              :                     } // X Loop
    9277              :                 } // Y Loop
    9278              : 
    9279              :                 // We need DataGlobals::CalendarYear, and not DataEnvironment::Year because
    9280              :                 // otherwise if you run a TMY file, you'll get for eg 1977, 1981, etc
    9281            0 :                 state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMap(MapNum,
    9282            0 :                                                                             state.dataGlobal->CalendarYear,
    9283            0 :                                                                             state.dataEnvrn->Month,
    9284            0 :                                                                             state.dataEnvrn->DayOfMonth,
    9285            0 :                                                                             state.dataGlobal->HourOfDay,
    9286              :                                                                             illumMap.Xnum,
    9287            0 :                                                                             dl->XValue,
    9288              :                                                                             illumMap.Ynum,
    9289            0 :                                                                             dl->YValue,
    9290            0 :                                                                             dl->IllumValue);
    9291              : 
    9292              :             } // WriteOutputToSQLite
    9293           16 :         } // end time step
    9294              :     } // not Warmup
    9295           61 : }
    9296              : 
    9297           26 : void CloseReportIllumMaps(EnergyPlusData &state)
    9298              : {
    9299              : 
    9300              :     // SUBROUTINE INFORMATION:
    9301              :     //       AUTHOR         Linda K. Lawrie
    9302              :     //       DATE WRITTEN   June 2003
    9303              : 
    9304              :     // PURPOSE OF THIS SUBROUTINE:
    9305              :     // This subroutine "closes" out the created daylight illuminance maps by merging them
    9306              :     // into the "eplusout.map" file.
    9307              : 
    9308              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    9309           26 :     auto &dl = state.dataDayltg;
    9310              : 
    9311           26 :     if ((int)dl->illumMaps.size() > 0) {
    9312              :         // Write map header
    9313            0 :         if (dl->MapColSep == DataStringGlobals::CharTab) {
    9314            0 :             state.files.map.filePath = state.files.outputMapTabFilePath;
    9315            0 :         } else if (dl->MapColSep == DataStringGlobals::CharComma) {
    9316            0 :             state.files.map.filePath = state.files.outputMapCsvFilePath;
    9317              :         } else {
    9318            0 :             state.files.map.filePath = state.files.outputMapTxtFilePath;
    9319              :         }
    9320              : 
    9321            0 :         state.files.map.ensure_open(state, "CloseReportIllumMaps");
    9322              : 
    9323            0 :         for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
    9324            0 :             auto &illumMap = dl->illumMaps(MapNum);
    9325            0 :             if (!illumMap.mapFile->good()) {
    9326            0 :                 continue; // fatal error processing
    9327              :             }
    9328              : 
    9329            0 :             const std::vector<std::string> mapLines = illumMap.mapFile->getLines();
    9330            0 :             if (mapLines.empty()) {
    9331            0 :                 ShowSevereError(state, format("CloseReportIllumMaps: IllumMap=\"{}\" is empty.", illumMap.Name));
    9332            0 :                 break;
    9333              :             }
    9334            0 :             for (const std::string &mapLine : mapLines) {
    9335            0 :                 print(state.files.map, "{}\n", mapLine);
    9336            0 :             }
    9337            0 :             illumMap.mapFile->del();
    9338            0 :         }
    9339              : 
    9340            0 :         if (!dl->mapResultsReported && !state.dataErrTracking->AbortProcessing) {
    9341            0 :             const std::string message = "CloseReportIllumMaps: Illuminance maps requested but no data ever reported. Likely cause is no solar.";
    9342            0 :             ShowSevereError(state, message);
    9343            0 :             print(state.files.map, "{}\n", message);
    9344            0 :         }
    9345              :     }
    9346           26 : }
    9347              : 
    9348           26 : void CloseDFSFile(EnergyPlusData &state)
    9349              : {
    9350              : 
    9351              :     // SUBROUTINE INFORMATION:
    9352              :     //       AUTHOR         Linda Lawrie
    9353              :     //       DATE WRITTEN   August 2010
    9354              : 
    9355              :     // PURPOSE OF THIS SUBROUTINE:
    9356              :     // Make sure DFSFile is closed at exit time.  Do not rely on operating system to
    9357              :     // take care of it.
    9358              : 
    9359           26 :     state.files.dfs.close();
    9360           26 : }
    9361              : 
    9362            7 : void DayltgSetupAdjZoneListsAndPointers(EnergyPlusData &state)
    9363              : {
    9364              : 
    9365              :     // SUBROUTINE INFORMATION:
    9366              :     //       AUTHOR         Fred Winkelmann
    9367              :     //       DATE WRITTEN   Feb. 2004
    9368              :     //       MODIFIED:      June 2010;LKL - Merged two routines.
    9369              : 
    9370              :     // PURPOSE OF THIS SUBROUTINE:
    9371              :     // For each Daylighting:Detailed enclosure, creates a list of adjacent enclosures,
    9372              :     // that have one or more exterior windows and that share one or more interior
    9373              :     // windows with Z. Used in calculation of daylighting through interior windows.
    9374              : 
    9375              :     // Sets the daylighting factor pointers for each Daylighting:Detailed control. The pointer
    9376              :     // may be associated with an exterior window in a daylit target zone's enclosure or an exterior window in
    9377              :     // an adjacent enclosure, daylit or not, that shares interior windows with the target zone's enclosure.
    9378              : 
    9379              :     // Count number of exterior Windows (use to allocate arrays)
    9380            7 :     auto &dl = state.dataDayltg;
    9381            7 :     auto &s_surf = state.dataSurface;
    9382              : 
    9383           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9384            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9385            9 :         thisEnclDaylight.TotalExtWindows = 0;
    9386              : 
    9387              :         // Count exterior windows in this solar enclosure
    9388           64 :         for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9389           55 :             auto const &surf = s_surf->Surface(surfNum);
    9390           55 :             if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9391           46 :                 surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9392            9 :                 ++thisEnclDaylight.TotalExtWindows;
    9393              :             }
    9394              :         }
    9395              :     } // End of primary enclosure loop
    9396              : 
    9397           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9398            9 :         int NumList = 0;
    9399            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
    9400            0 :             continue;
    9401              :         }
    9402            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9403            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9404            0 :             continue;
    9405              :         }
    9406              :         // This is a Daylighting:Detailed enclosure
    9407              :         // Find adjacent zones/enclosures
    9408           22 :         for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
    9409           13 :             if (adjEnclNum == enclNum) {
    9410            9 :                 continue;
    9411              :             }
    9412              :             // Require that adjEnclNum have a least one exterior window
    9413            4 :             bool AdjEnclHasExtWins = false;
    9414           28 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9415           32 :                 if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
    9416            4 :                     (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
    9417            4 :                     AdjEnclHasExtWins = true;
    9418            4 :                     break;
    9419              :                 }
    9420              :             }
    9421            4 :             if (!AdjEnclHasExtWins) {
    9422            0 :                 continue;
    9423              :             }
    9424              :             // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to ZoneNum
    9425           32 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9426           28 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9427           28 :                 if ((surfAdj.Class == SurfaceClass::Window) && (surfAdj.ExtBoundCond >= 1)) {
    9428              :                     // This is an interior window in ZoneNumAdj
    9429            0 :                     if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex == enclNum) {
    9430              :                         // This interior window is adjacent to ZoneNum
    9431            0 :                         ++NumList;
    9432            0 :                         break;
    9433              :                     }
    9434              :                 }
    9435              :             }
    9436              :         }
    9437            9 :         thisEnclDaylight.AdjIntWinEnclNums.allocate(NumList);
    9438            9 :         thisEnclDaylight.AdjIntWinEnclNums = 0;
    9439              :     } // End of primary enclosure loop
    9440              : 
    9441           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9442            9 :         int NumList = 0;
    9443            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
    9444            0 :             continue;
    9445              :         }
    9446            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9447            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9448            0 :             continue;
    9449              :         }
    9450              :         // This is a Daylighting:Detailed enclosure
    9451              :         // Find adjacent zones/enclosures
    9452           22 :         for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
    9453           13 :             if (adjEnclNum == enclNum) {
    9454            9 :                 continue;
    9455              :             }
    9456              :             // Require that adjEnclNum have a least one exterior window
    9457            4 :             bool AdjEnclHasExtWins = false;
    9458           28 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9459           28 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9460           28 :                 if (surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) {
    9461            4 :                     AdjEnclHasExtWins = true;
    9462            4 :                     break;
    9463              :                 }
    9464              :             }
    9465            4 :             if (!AdjEnclHasExtWins) {
    9466            0 :                 continue;
    9467              :             }
    9468              :             // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to enclNum
    9469           32 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9470           28 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9471           28 :                 if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond < 1) {
    9472           28 :                     continue;
    9473              :                 }
    9474              : 
    9475              :                 // This is an interior window in adjEnclNum
    9476            0 :                 if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex != enclNum) {
    9477            0 :                     continue;
    9478              :                 }
    9479              : 
    9480              :                 // This interior window is adjacent to ZoneNum
    9481            0 :                 ++NumList;
    9482            0 :                 int enclNumAdj = surfAdj.SolarEnclIndex;
    9483            0 :                 thisEnclDaylight.AdjIntWinEnclNums(NumList) = enclNumAdj;
    9484            0 :                 dl->enclDaylight(enclNumAdj).adjEnclHasDayltgCtrl = true;
    9485            0 :                 break;
    9486              :             }
    9487              :         }
    9488            9 :         thisEnclDaylight.NumOfIntWinAdjEncls = NumList;
    9489              :     } // End of primary enclosure loop
    9490              : 
    9491              :     // now fill out information on relationship between adjacent exterior windows and associated interior windows
    9492           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9493            9 :         auto &enclDayl = dl->enclDaylight(enclNum);
    9494              :         // first find count of exterior windows
    9495            9 :         if (enclDayl.NumOfIntWinAdjEncls <= 0) {
    9496            9 :             enclDayl.NumOfIntWinAdjEnclExtWins = 0;
    9497            9 :             continue;
    9498              :         }
    9499            0 :         for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
    9500            0 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9501            0 :                 if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
    9502            0 :                     (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
    9503            0 :                     ++enclDayl.NumOfIntWinAdjEnclExtWins;
    9504              :                 }
    9505              :             }
    9506              :         }
    9507              :         // now allocate nested struct based on exterior window count
    9508            0 :         enclDayl.IntWinAdjEnclExtWin.allocate(enclDayl.NumOfIntWinAdjEnclExtWins);
    9509              : 
    9510              :         // now fill nested structure
    9511            0 :         int ExtWinIndex = 0;
    9512            0 :         for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
    9513            0 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9514            0 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9515            0 :                 if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond != ExternalEnvironment) {
    9516            0 :                     continue;
    9517              :                 }
    9518              : 
    9519            0 :                 ++ExtWinIndex;
    9520            0 :                 auto &intWinAdjEnclExtWin = enclDayl.IntWinAdjEnclExtWin(ExtWinIndex);
    9521            0 :                 intWinAdjEnclExtWin.SurfNum = SurfNumAdj;
    9522              : 
    9523              :                 // now count interior windows shared by both zones
    9524            0 :                 int NumOfIntWindowsCount = 0;
    9525            0 :                 for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9526            0 :                     auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
    9527            0 :                     if ((surfAdj2.Class == SurfaceClass::Window) && (surfAdj2.ExtBoundCond >= 1)) {
    9528              :                         // This is an interior window in ZoneNumAdj
    9529            0 :                         if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
    9530              :                             // This interior window is adjacent to ZoneNum and associated with this
    9531            0 :                             ++NumOfIntWindowsCount;
    9532              :                         }
    9533              :                     }
    9534              :                 } // for (SurfNumAdj2)
    9535              : 
    9536              :                 // allocate nested array
    9537            0 :                 intWinAdjEnclExtWin.IntWinNum.allocate(NumOfIntWindowsCount);
    9538            0 :                 intWinAdjEnclExtWin.IntWinNum = 0;
    9539            0 :                 int IntWinIndex = 0;
    9540            0 :                 for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9541            0 :                     auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
    9542            0 :                     if (surfAdj2.Class != SurfaceClass::Window || surfAdj2.ExtBoundCond < 1) {
    9543            0 :                         continue;
    9544              :                     }
    9545              : 
    9546              :                     // This is an interior window in ZoneNumAdj
    9547            0 :                     if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
    9548              :                         // This interior window is adjacent to ZoneNum and associated with this
    9549            0 :                         intWinAdjEnclExtWin.IntWinNum(++IntWinIndex) = SurfNumAdj2;
    9550              :                     }
    9551              :                 } // for (SurfNumAdj2)
    9552              :             } // for (SurfNumAdj)
    9553              :         } // for (adjEnclNum)
    9554              :     } // End of primary enclosure loop
    9555              : 
    9556            7 :     Array1D_int enclExtWin;
    9557            7 :     enclExtWin.dimension(state.dataViewFactor->NumOfSolarEnclosures, 0);
    9558              : 
    9559           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9560            9 :         enclExtWin(enclNum) = 0;
    9561            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
    9562            0 :             continue;
    9563              :         }
    9564            9 :         auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9565            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9566            0 :             continue;
    9567              :         }
    9568              :         // This is a Daylighting:Detailed zone
    9569              : 
    9570              :         // Get exterior windows in this solar enclosure
    9571           64 :         for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9572           55 :             auto const &surf = s_surf->Surface(surfNum);
    9573           55 :             if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9574           46 :                 surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9575            9 :                 ++enclExtWin(enclNum);
    9576              :             }
    9577              :         }
    9578              : 
    9579              :         // Get exterior windows in adjacent enclosures that share interior windows with enclNum
    9580            9 :         if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) {
    9581            9 :             continue;
    9582              :         }
    9583              : 
    9584            0 :         for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9585              :             // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
    9586              :             // it would not be an "AdjIntWinEncl"
    9587            0 :             for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9588            0 :                 auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9589            0 :                 if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
    9590            0 :                     surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9591            0 :                     ++enclExtWin(enclNum);
    9592              :                 }
    9593              :             }
    9594              :         } // for (adjEnclNum)
    9595              :     } // for (enclNum)
    9596              : 
    9597            7 :     dl->maxShadeDeployOrderExtWins = 0;
    9598           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9599            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9600            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9601            0 :             continue;
    9602              :         }
    9603            9 :         thisEnclDaylight.NumOfDayltgExtWins = 0;
    9604            9 :         int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
    9605            9 :         if (thisEnclNumRefPoints > 0) {
    9606              :             // This is a Daylighting:Detailed enclosure
    9607              : 
    9608              :             // Get exterior windows in this enclosure
    9609            9 :             if (enclExtWin(enclNum) == 0) {
    9610            0 :                 continue;
    9611              :             }
    9612            9 :             thisEnclDaylight.DayltgExtWinSurfNums.allocate(enclExtWin(enclNum));
    9613            9 :             thisEnclDaylight.DayltgExtWinSurfNums = 0;
    9614           18 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9615            9 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9616            9 :                 thisDayltgCtrl.MapShdOrdToLoopNum.allocate(enclExtWin(enclNum));
    9617            9 :                 thisDayltgCtrl.MapShdOrdToLoopNum = 0;
    9618              : 
    9619            9 :                 assert((int)thisDayltgCtrl.refPts.size() == thisDayltgCtrl.TotalDaylRefPoints);
    9620           23 :                 for (auto &refPt : thisDayltgCtrl.refPts) {
    9621           14 :                     refPt.extWins.allocate(enclExtWin(enclNum));
    9622           28 :                     for (auto &extWin : refPt.extWins) {
    9623           14 :                         new (&extWin) DaylRefPtExtWin();
    9624              :                     }
    9625              :                 }
    9626            9 :             }
    9627              : 
    9628            9 :             int enclExtWinCtr = 0;
    9629              : 
    9630           64 :             for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9631           55 :                 auto const &surf = s_surf->Surface(surfNum);
    9632           55 :                 if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
    9633           46 :                     surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9634            9 :                     ++enclExtWinCtr;
    9635            9 :                     thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = surfNum;
    9636              :                 }
    9637              :             }
    9638              : 
    9639              :             // Get exterior windows in adjacent enclosures that share interior windows with enclNum
    9640            9 :             if (thisEnclDaylight.NumOfIntWinAdjEncls > 0) {
    9641            0 :                 for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9642              :                     // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
    9643              :                     // it would not be an "AdjIntWinEncl"
    9644            0 :                     for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
    9645            0 :                         auto const &surfAdj = s_surf->Surface(SurfNumAdj);
    9646            0 :                         if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
    9647            0 :                             surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
    9648            0 :                             ++enclExtWinCtr;
    9649            0 :                             thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = SurfNumAdj;
    9650              : 
    9651            0 :                             auto &surfWinAdj = s_surf->SurfaceWindow(SurfNumAdj);
    9652              :                             // If no daylighting in the adjacent enclosure, set up variables anyway:
    9653            0 :                             if (state.dataViewFactor->EnclSolInfo(adjEnclNum).TotalEnclosureDaylRefPoints == 0 &&
    9654            0 :                                 !s_surf->SurfWinSurfDayLightInit(SurfNumAdj)) {
    9655            0 :                                 surfWinAdj.refPts.allocate(thisEnclNumRefPoints);
    9656            0 :                                 for (auto &refPt : surfWinAdj.refPts) {
    9657            0 :                                     new (&refPt) SurfaceWindowRefPt();
    9658              :                                 }
    9659            0 :                                 s_surf->SurfWinSurfDayLightInit(SurfNumAdj) = true;
    9660              :                             }
    9661              :                         }
    9662              :                     } // for (SurfNumAdj)
    9663              :                 } // for (adjEnclNum)
    9664              :             } // if (thisEnclDaylight.NumOfIntWinAdjEncls > 0)
    9665              : 
    9666            9 :             thisEnclDaylight.NumOfDayltgExtWins = enclExtWin(enclNum);
    9667            9 :             int winSize = enclExtWin(enclNum);
    9668           18 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9669            9 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9670            9 :                 int refSize = thisDayltgCtrl.TotalDaylRefPoints;
    9671          225 :                 for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
    9672          216 :                     thisDayltgCtrl.daylFac[iHr].allocate(winSize, refSize);
    9673              :                 }
    9674            9 :             }
    9675              :         } // if (thisEncl.NumOfRefPoints > 0)
    9676              : 
    9677            9 :         if (s_surf->TotWinShadingControl > 0) {
    9678            1 :             CreateShadeDeploymentOrder(state, enclNum);
    9679              :         }
    9680              :     } // for (enclNum)
    9681              : 
    9682           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9683            9 :         auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9684            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9685            0 :             continue;
    9686              :         }
    9687            9 :         int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
    9688            9 :         if (thisEnclNumRefPoints > 0) {
    9689            9 :             if (s_surf->TotWinShadingControl > 0) {
    9690            1 :                 MapShadeDeploymentOrderToLoopNumber(state, enclNum);
    9691              :             }
    9692              :         }
    9693              :     }
    9694              : 
    9695            8 :     for (auto &illumMap : dl->illumMaps) {
    9696            1 :         assert((int)illumMap.refPts.size() == illumMap.TotalMapRefPoints);
    9697            1 :         if (illumMap.TotalMapRefPoints == 0) {
    9698            0 :             continue;
    9699              :         }
    9700              : 
    9701            1 :         int numExtWin = enclExtWin(illumMap.enclIndex);
    9702            1 :         if (numExtWin == 0) {
    9703            0 :             continue;
    9704              :         }
    9705              : 
    9706          101 :         for (auto &refPt : illumMap.refPts) {
    9707          100 :             refPt.winLums.allocate(numExtWin);
    9708          200 :             for (auto &winLums : refPt.winLums) {
    9709          100 :                 winLums = {0.0, 0.0};
    9710              :             }
    9711              :         }
    9712              : 
    9713           25 :         for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
    9714           24 :             illumMap.daylFac[iHr].allocate(numExtWin, illumMap.TotalMapRefPoints);
    9715              :         }
    9716              : 
    9717              :     } // End of map loop
    9718              : 
    9719            7 :     dl->dirIllum.allocate(Constant::iHoursInDay);
    9720            7 :     dl->reflIllum.allocate(Constant::iHoursInDay);
    9721            7 :     dl->winLum.allocate(Constant::iHoursInDay);
    9722            7 :     dl->avgWinLum.allocate(Constant::iHoursInDay);
    9723              : 
    9724              :     static constexpr std::string_view Format_700("! <Enclosure/Window Adjacency Daylighting Counts>, Enclosure Name, Number of Exterior Windows, "
    9725              :                                                  "Number of Exterior Windows in Adjacent Enclosures\n");
    9726            7 :     print(state.files.eio, Format_700);
    9727           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9728            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9729            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9730            0 :             continue;
    9731              :         }
    9732            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
    9733            0 :             continue;
    9734              :         }
    9735              :         static constexpr std::string_view Format_701("Enclosure/Window Adjacency Daylighting Counts, {},{},{}\n");
    9736            9 :         print(state.files.eio,
    9737              :               Format_701,
    9738            9 :               state.dataViewFactor->EnclSolInfo(enclNum).Name,
    9739            9 :               thisEnclDaylight.TotalExtWindows,
    9740           18 :               (thisEnclDaylight.NumOfDayltgExtWins - thisEnclDaylight.TotalExtWindows));
    9741              :     }
    9742              :     static constexpr std::string_view Format_702(
    9743              :         "! <Enclosure/Window Adjacency Daylighting Matrix>, Enclosure Name, Number of Adjacent Enclosures with Windows,Adjacent "
    9744              :         "Enclosure Names - 1st 100 (max)\n");
    9745            7 :     print(state.files.eio, Format_702);
    9746           16 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9747            9 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9748            9 :         if (!thisEnclDaylight.hasSplitFluxDaylighting) {
    9749            0 :             continue;
    9750              :         }
    9751            9 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
    9752            0 :             continue;
    9753              :         }
    9754              :         static constexpr std::string_view Format_703("Enclosure/Window Adjacency Daylighting Matrix, {},{}");
    9755            9 :         print(state.files.eio, Format_703, state.dataViewFactor->EnclSolInfo(enclNum).Name, thisEnclDaylight.NumOfIntWinAdjEncls);
    9756            9 :         for (int loop = 1, loop_end = min(thisEnclDaylight.NumOfIntWinAdjEncls, 100); loop <= loop_end; ++loop) {
    9757            0 :             print(state.files.eio, ",{}", state.dataViewFactor->EnclSolInfo(thisEnclDaylight.AdjIntWinEnclNums(loop)).Name);
    9758              :         }
    9759            9 :         print(state.files.eio, "\n");
    9760              :     }
    9761              : 
    9762            7 :     enclExtWin.deallocate();
    9763            7 : }
    9764              : 
    9765            3 : void CreateShadeDeploymentOrder(EnergyPlusData &state, int const enclNum)
    9766              : {
    9767              :     // J. Glazer - 2018
    9768              :     // create sorted list for shade deployment order
    9769              :     // first step is to create a sortable list of WindowShadingControl objects by sequence
    9770            3 :     auto &dl = state.dataDayltg;
    9771            3 :     auto &s_surf = state.dataSurface;
    9772              : 
    9773            3 :     std::vector<std::pair<int, int>> shadeControlSequence; // sequence, WindowShadingControl
    9774           10 :     for (int iShadeCtrl = 1; iShadeCtrl <= s_surf->TotWinShadingControl; ++iShadeCtrl) {
    9775            7 :         auto &winShadeControl = s_surf->WindowShadingControl(iShadeCtrl);
    9776            7 :         for (int spaceNum : state.dataHeatBal->Zone(winShadeControl.ZoneIndex).spaceIndexes) {
    9777            7 :             int shadeCtrlEnclNum = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
    9778            7 :             if (shadeCtrlEnclNum == enclNum) {
    9779            7 :                 shadeControlSequence.push_back(std::make_pair(winShadeControl.SequenceNumber, iShadeCtrl));
    9780            7 :                 break;
    9781              :             }
    9782            7 :         }
    9783              :     }
    9784              :     // sort the WindowShadingControl objects based on sequence number
    9785            3 :     sort(shadeControlSequence.begin(), shadeControlSequence.end());
    9786              :     // now make the deployment list of lists.
    9787              :     // each sublist is a group of surfaces that should be deployed together
    9788              :     // often the sublist is just a single item.
    9789            3 :     dl->maxShadeDeployOrderExtWins = 0;
    9790            6 :     for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
    9791            3 :         auto &thisDaylightCtrl = dl->daylightControl(controlNum);
    9792           10 :         for (auto sequence : shadeControlSequence) { // This is an iterator (THIS_AUTO_OK)
    9793            7 :             int curShadeControlNum = sequence.second;
    9794            7 :             auto const &winShadeControl = s_surf->WindowShadingControl(curShadeControlNum);
    9795            7 :             if (winShadeControl.multiSurfaceControl == MultiSurfaceControl::Group) {
    9796              :                 // add a group of surfaces since they should be deployed as a group
    9797            5 :                 std::vector<int> group;
    9798           16 :                 for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
    9799           11 :                     group.push_back(winShadeControl.FenestrationIndex(i));
    9800              :                 }
    9801            5 :                 thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(group);
    9802            5 :             } else {
    9803              :                 // add each individual surface as a separate list so they are deployed individually
    9804           10 :                 for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
    9805            8 :                     std::vector<int> singleMemberVector;
    9806            8 :                     singleMemberVector.push_back(winShadeControl.FenestrationIndex(i));
    9807            8 :                     thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(singleMemberVector);
    9808            8 :                 }
    9809              :             }
    9810            3 :         }
    9811            3 :         dl->maxShadeDeployOrderExtWins = max(dl->maxShadeDeployOrderExtWins, (int)thisDaylightCtrl.ShadeDeployOrderExtWins.size());
    9812            3 :     }
    9813              : 
    9814            3 :     dl->maxDayltgExtWins = 0;
    9815            6 :     for (auto const &enclDayl : dl->enclDaylight) {
    9816            3 :         dl->maxDayltgExtWins = max(dl->maxDayltgExtWins, enclDayl.NumOfDayltgExtWins);
    9817              :     }
    9818              : 
    9819            3 : } // CreateShadeDeploymentOrder()
    9820              : 
    9821            2 : void MapShadeDeploymentOrderToLoopNumber(EnergyPlusData &state, int const enclNum)
    9822              : {
    9823              :     // J. Glazer - 2018
    9824              :     // Allow a way to map back to the original "loop" index that is used in many other places in the
    9825              :     // ZoneDayLight data structure when traversing the list in the order of the window shaded deployment
    9826            2 :     auto &dl = state.dataDayltg;
    9827            2 :     auto &s_surf = state.dataSurface;
    9828              : 
    9829            2 :     auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
    9830            2 :     auto const &thisEnclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    9831              : 
    9832            2 :     if (thisEnclSol.TotalEnclosureDaylRefPoints == 0 || thisEnclDaylight.NumOfDayltgExtWins == 0) {
    9833            0 :         return;
    9834              :     }
    9835              : 
    9836            4 :     for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9837            2 :         auto &thisDaylightCtrl = dl->daylightControl(controlNum);
    9838            2 :         if (thisDaylightCtrl.ShadeDeployOrderExtWins.size() == 0) {
    9839            0 :             continue;
    9840              :         }
    9841              : 
    9842            2 :         int count = 0;
    9843            2 :         bool showOnce = true;
    9844            9 :         for (auto const &listOfExtWin : thisDaylightCtrl.ShadeDeployOrderExtWins) {
    9845           17 :             for (int IWinShdOrd : listOfExtWin) {
    9846           10 :                 ++count;
    9847           10 :                 if (count > thisEnclDaylight.NumOfDayltgExtWins) {
    9848            0 :                     if (showOnce) {
    9849            0 :                         ShowWarningError(
    9850              :                             state,
    9851            0 :                             format("MapShadeDeploymentOrderToLoopNumber: too many controlled shaded windows in enclosure {}", thisEnclSol.Name));
    9852            0 :                         ShowContinueError(state,
    9853              :                                           "Check the Zone Name in the WindowShadingControl that references the following fenestration surfaces:");
    9854            0 :                         showOnce = false;
    9855              :                     }
    9856            0 :                     ShowContinueError(state, format("  -  {}", s_surf->Surface(IWinShdOrd).Name));
    9857              :                 }
    9858           46 :                 for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
    9859           46 :                     int IWinLoop = thisEnclDaylight.DayltgExtWinSurfNums(loop);
    9860           46 :                     if (IWinShdOrd == IWinLoop) {
    9861           10 :                         thisDaylightCtrl.MapShdOrdToLoopNum(count) = loop;
    9862           10 :                         break;
    9863              :                     }
    9864              :                 }
    9865            7 :             }
    9866            2 :         } // for (listOfExtWin)
    9867            2 :     } // for (controlNum)
    9868              : } // MapShadeDeploymentOrderToLoopNumber()
    9869              : 
    9870            0 : void DayltgInterReflIllFrIntWins(EnergyPlusData &state, int const enclNum)
    9871              : {
    9872              : 
    9873              :     // SUBROUTINE INFORMATION:
    9874              :     //       AUTHOR         Fred Winkelmann
    9875              :     //       DATE WRITTEN   Mar. 2004
    9876              : 
    9877              :     // PURPOSE OF THIS SUBROUTINE:
    9878              :     // Calculates the inter-reflected illuminance in a daylit zone from beam
    9879              :     // and diffuse daylight entering the zone through interior windows. This illuminance
    9880              :     // is determined by the split-flux method and is assumed to be uniform, i.e., the same
    9881              :     // at all reference points.
    9882              : 
    9883            0 :     auto &dl = state.dataDayltg;
    9884            0 :     auto &s_surf = state.dataSurface;
    9885              : 
    9886            0 :     auto &enclDayl = dl->enclDaylight(enclNum);
    9887            0 :     auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
    9888              : 
    9889            0 :     enclDayl.InterReflIllFrIntWins = 0.0;
    9890              : 
    9891            0 :     for (int const IWin : enclSol.SurfacePtr) {
    9892            0 :         auto &surf = s_surf->Surface(IWin);
    9893            0 :         if (surf.Class != SurfaceClass::Window || surf.ExtBoundCond < 1) {
    9894            0 :             continue;
    9895              :         }
    9896            0 :         auto const &surfWin = s_surf->SurfaceWindow(IWin);
    9897              :         // This is an interior window in ZoneNum
    9898            0 :         int const ConstrNum = surf.Construction;
    9899            0 :         int const adjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
    9900              :         // Luminous flux transmitted through an int win from adjacent zone's enclosure (lumens)
    9901            0 :         Real64 QDifTrans = state.dataHeatBal->EnclSolQSDifSol(adjEnclNum) * state.dataConstruction->Construct(ConstrNum).TransDiffVis * surf.Area *
    9902            0 :                            state.dataEnvrn->PDIFLW;
    9903            0 :         Real64 QDifTransUp = QDifTrans * surfWin.fractionUpgoing;         // Upgoing part of QDifTrans (lumens)
    9904            0 :         Real64 QDifTransDn = QDifTrans * (1.0 - surfWin.fractionUpgoing); // Downgoing part of QDifTrans (lumens)
    9905            0 :         if (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect) != 0.0) {
    9906            0 :             enclDayl.InterReflIllFrIntWins += (QDifTransDn * surfWin.rhoFloorWall + QDifTransUp * surfWin.rhoCeilingWall) /
    9907            0 :                                               (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
    9908              :         }
    9909              :     } // for (iWin)
    9910              : 
    9911              :     // Add inter-reflected illuminance from beam solar entering enclosure through interior windows
    9912              :     // TH, CR 7873, 9/17/2009
    9913            0 :     if (dl->enclDaylight(enclNum).totInsSurfArea > 0) {
    9914            0 :         enclDayl.InterReflIllFrIntWins +=
    9915            0 :             (state.dataHeatBal->EnclSolDBIntWin(enclNum) * state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW * enclDayl.floorVisRefl) /
    9916            0 :             (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
    9917              :     }
    9918            0 : } // DayltgInterReflIllFrIntWins()
    9919              : 
    9920            5 : void CalcMinIntWinSolidAngs(EnergyPlusData &state)
    9921              : {
    9922              : 
    9923              :     // SUBROUTINE INFORMATION:
    9924              :     //       AUTHOR         Fred Winkelmann
    9925              :     //       DATE WRITTEN   Feb. 2004
    9926              : 
    9927              :     // PURPOSE OF THIS SUBROUTINE:
    9928              :     // For each Daylighting:Detailed zone finds the minimum solid angle subtended
    9929              :     // by interior windows through which daylight can pass from adjacent zones with
    9930              :     // exterior windows.
    9931              : 
    9932            5 :     auto &dl = state.dataDayltg;
    9933            5 :     auto &s_surf = state.dataSurface;
    9934              : 
    9935           12 :     for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
    9936            7 :         auto &thisEnclDaylight = dl->enclDaylight(enclNum);
    9937            7 :         thisEnclDaylight.MinIntWinSolidAng = 2.0 * Constant::Pi;
    9938            7 :         if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
    9939            0 :             continue;
    9940              :         }
    9941            7 :         if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) {
    9942            7 :             continue;
    9943              :         }
    9944              : 
    9945            0 :         for (int IWin : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
    9946            0 :             auto const &surf = s_surf->Surface(IWin);
    9947              : 
    9948            0 :             if ((surf.Class != SurfaceClass::Window) || (surf.ExtBoundCond < 1)) {
    9949            0 :                 continue;
    9950              :             }
    9951              : 
    9952              :             // This is an interior window in enclNum
    9953            0 :             int const winAdjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
    9954            0 :             bool IntWinNextToIntWinAdjZone = false; // True if an interior window is next to a zone with one or more exterior windows
    9955            0 :             for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
    9956            0 :                 if (winAdjEnclNum == adjEnclNum) {
    9957            0 :                     IntWinNextToIntWinAdjZone = true;
    9958            0 :                     break;
    9959              :                 }
    9960              :             }
    9961              : 
    9962            0 :             if (!IntWinNextToIntWinAdjZone) {
    9963            0 :                 continue;
    9964              :             }
    9965              : 
    9966            0 :             for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
    9967            0 :                 auto &thisDayltgCtrl = dl->daylightControl(controlNum);
    9968            0 :                 for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
    9969              :                     // Reference point in absolute coordinate system
    9970            0 :                     Vector3<Real64> RREF = thisDayltgCtrl.refPts(IL).absCoords;
    9971            0 :                     bool is_Triangle = (surf.Sides == 3);
    9972            0 :                     bool is_Rectangle = (surf.Sides == 4);
    9973              : 
    9974            0 :                     Vector3<Real64> W1, W2, W3;
    9975            0 :                     if (is_Rectangle) {
    9976              :                         // Vertices of window numbered counter-clockwise starting at upper left as viewed
    9977              :                         // from inside of room. Assumes original vertices are numbered counter-clockwise from
    9978              :                         // upper left as viewed from outside.
    9979            0 :                         W3 = surf.Vertex(2);
    9980            0 :                         W2 = surf.Vertex(3);
    9981            0 :                         W1 = surf.Vertex(4);
    9982            0 :                     } else if (is_Triangle) {
    9983            0 :                         W3 = surf.Vertex(2);
    9984            0 :                         W2 = surf.Vertex(3);
    9985            0 :                         W1 = surf.Vertex(1);
    9986              :                     }
    9987              :                     // Unit vectors from window vertex 2 to 1 and 2 to 3, center point of window,
    9988              :                     // and vector from ref pt to center of window
    9989            0 :                     Vector3<Real64> W21 = W1 - W2;
    9990            0 :                     Vector3<Real64> W23 = W3 - W2;
    9991            0 :                     Real64 HW = W21.magnitude();
    9992            0 :                     Real64 WW = W23.magnitude();
    9993            0 :                     Vector3<Real64> WC = (is_Rectangle) ? (W2 + (W23 + W21) / 2.0) : (W2 + (W23 + W21) / 3.0);
    9994              : 
    9995              :                     // Vector from ref point to center of window
    9996            0 :                     Vector3<Real64> REFWC = WC - RREF;
    9997            0 :                     W21 /= HW;
    9998            0 :                     W23 /= WW;
    9999              :                     // Unit vector normal to window (pointing away from room)
   10000            0 :                     Vector3<Real64> WNORM = surf.OutNormVec;
   10001              :                     // Distance from ref point to center of window
   10002            0 :                     Real64 DIS = REFWC.magnitude();
   10003              :                     // Unit vector from ref point to center of window
   10004            0 :                     Vector3<Real64> Ray = REFWC / DIS;
   10005              :                     // Cosine of angle between ray from ref pt to center of window and window outward normal
   10006            0 :                     Real64 COSB = dot(WNORM, Ray);
   10007            0 :                     if (COSB > 0.01765) { // 0 <= B < 89 deg
   10008              :                         // Above test avoids case where ref point cannot receive daylight directly from the
   10009              :                         // interior window
   10010            0 :                         Real64 IntWinSolidAng = COSB * surf.Area / (pow_2(DIS) + 0.001);
   10011            0 :                         thisEnclDaylight.MinIntWinSolidAng = min(thisEnclDaylight.MinIntWinSolidAng, IntWinSolidAng);
   10012              :                     }
   10013            0 :                 } // for (IL)
   10014            0 :             } // for (controlNum)
   10015              :         } // for (IWin)
   10016              :     } // for (enclNum)
   10017            5 : }
   10018              : 
   10019           19 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
   10020              : {
   10021              : 
   10022              :     // SUBROUTINE INFORMATION:
   10023              :     //       AUTHOR         Linda Lawrie
   10024              :     //       DATE WRITTEN   February 2009
   10025              : 
   10026              :     // PURPOSE OF THIS SUBROUTINE:
   10027              :     // check for geometrytransform in the daylighting access for reference and map points
   10028              : 
   10029              :     // METHODOLOGY EMPLOYED:
   10030              :     // once reference points  have been converted to WCS,
   10031              :     //  change them to reflect a different aspect
   10032              :     // ratio for the entire building based on user input.
   10033              : 
   10034              :     // SUBROUTINE PARAMETER DEFINITIONS:
   10035              :     static constexpr std::string_view CurrentModuleObject = "GeometryTransform";
   10036              : 
   10037              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
   10038           19 :     Array1D_string cAlphas(1);
   10039           19 :     Array1D<Real64> rNumerics;
   10040              : 
   10041              :     // begin execution
   10042              :     // get user input...
   10043           19 :     doTransform = false;
   10044           19 :     OldAspectRatio = 1.0;
   10045           19 :     NewAspectRatio = 1.0;
   10046              : 
   10047           19 :     auto &ip = state.dataInputProcessing->inputProcessor;
   10048           19 :     auto const &s_ipsc = state.dataIPShortCut;
   10049           19 :     auto &s_surf = state.dataSurface;
   10050              : 
   10051           19 :     if (ip->getNumObjectsFound(state, CurrentModuleObject) == 1) {
   10052              :         int NAlphas;
   10053              :         int NNum;
   10054              :         int IOStat;
   10055            0 :         ip->getObjectItem(state,
   10056              :                           CurrentModuleObject,
   10057              :                           1,
   10058              :                           cAlphas,
   10059              :                           NAlphas,
   10060              :                           rNumerics,
   10061              :                           NNum,
   10062              :                           IOStat,
   10063            0 :                           s_ipsc->lNumericFieldBlanks,
   10064            0 :                           s_ipsc->lAlphaFieldBlanks,
   10065            0 :                           s_ipsc->cAlphaFieldNames,
   10066            0 :                           s_ipsc->cNumericFieldNames);
   10067            0 :         OldAspectRatio = rNumerics(1);
   10068            0 :         NewAspectRatio = rNumerics(2);
   10069            0 :         std::string transformPlane = cAlphas(1);
   10070            0 :         if (transformPlane != "XY") {
   10071            0 :             ShowWarningError(state, format("{}: invalid {}=\"{}...ignored.", CurrentModuleObject, s_ipsc->cAlphaFieldNames(1), cAlphas(1)));
   10072              :         }
   10073            0 :         doTransform = true;
   10074            0 :         s_surf->AspectTransform = true;
   10075            0 :     }
   10076           19 :     if (s_surf->WorldCoordSystem) {
   10077            0 :         doTransform = false;
   10078            0 :         s_surf->AspectTransform = false;
   10079              :     }
   10080           19 : }
   10081              : 
   10082            1 : void WriteDaylightMapTitle(EnergyPlusData &state,
   10083              :                            int const mapNum,
   10084              :                            InputOutputFile &mapFile,
   10085              :                            std::string const &mapName,
   10086              :                            std::string const &environmentName,
   10087              :                            int const ZoneNum,
   10088              :                            std::string const &refPts,
   10089              :                            Real64 const zcoord)
   10090              : {
   10091              :     // SUBROUTINE INFORMATION:
   10092              :     //       AUTHOR         Greg Stark
   10093              :     //       DATE WRITTEN   Sept 2008
   10094              : 
   10095              :     // PURPOSE OF THIS SUBROUTINE:
   10096              :     // The purpose of the routine is to allow the daylighting map data to be written in various formats
   10097              : 
   10098              :     // must add correct number of commas at end
   10099            1 :     auto &dl = state.dataDayltg;
   10100              : 
   10101            1 :     std::string fullmapName = fmt::format("{}:{}:{} Illuminance [lux] (Hourly)", state.dataHeatBal->Zone(ZoneNum).Name, environmentName, mapName);
   10102            1 :     print(mapFile, "Date/Time{}{}{}{}{}{}\n", dl->MapColSep, fullmapName, dl->MapColSep, refPts, dl->MapColSep, dl->MapColSep);
   10103              : 
   10104            1 :     if (state.dataSQLiteProcedures->sqlite) {
   10105            0 :         state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMapTitle(mapNum, fullmapName, environmentName, ZoneNum, refPts, zcoord);
   10106              :     }
   10107            1 : } // WritDaylightMapTitle()
   10108              : 
   10109              : } // namespace EnergyPlus::Dayltg
        

Generated by: LCOV version 2.0-1