LCOV - code coverage report
Current view: top level - EnergyPlus - SolarReflectionManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 89.8 % 638 573
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 6 6

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cassert>
      50              : #include <cmath>
      51              : 
      52              : // EnergyPlus Headers
      53              : #include <EnergyPlus/Construction.hh>
      54              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      55              : #include <EnergyPlus/DataEnvironment.hh>
      56              : #include <EnergyPlus/DataHeatBalance.hh>
      57              : #include <EnergyPlus/DataSurfaces.hh>
      58              : #include <EnergyPlus/DataSystemVariables.hh>
      59              : #include <EnergyPlus/DataVectorTypes.hh>
      60              : #include <EnergyPlus/DisplayRoutines.hh>
      61              : #include <EnergyPlus/General.hh>
      62              : #include <EnergyPlus/PierceSurface.hh>
      63              : #include <EnergyPlus/ScheduleManager.hh>
      64              : #include <EnergyPlus/SolarReflectionManager.hh>
      65              : #include <EnergyPlus/SolarShading.hh>
      66              : 
      67              : namespace EnergyPlus {
      68              : 
      69              : namespace SolarReflectionManager {
      70              : 
      71              :     // MODULE INFORMATION
      72              :     //       AUTHOR         Fred Winkelmann
      73              :     //       DATE WRITTEN   September 2003
      74              :     //       MODIFIED       May 2004, FCW: modify calculation of receiving point location on a
      75              :     //                        receiving surface so can handle surface of any number of vertices
      76              :     //                        (previously restricted to 3- or 4-sided surfaces).
      77              :     //       RE-ENGINEERED  na
      78              : 
      79              :     // PURPOSE OF THIS MODULE:
      80              :     // Manages the calculation of factors for solar reflected from obstructions and ground.
      81              : 
      82              :     // METHODOLOGY EMPLOYED:
      83              :     // REFERENCES: na
      84              : 
      85              :     // OTHER NOTES: na
      86              : 
      87              :     // Using/Aliasing
      88              :     using namespace DataHeatBalance;
      89              :     using namespace DataSurfaces;
      90              :     using namespace DataEnvironment;
      91              : 
      92              :     using namespace DataVectorTypes;
      93              : 
      94            9 :     void InitSolReflRecSurf(EnergyPlusData &state)
      95              :     {
      96              : 
      97              :         // SUBROUTINE INFORMATION:
      98              :         //       AUTHOR         Fred Winkelmann
      99              :         //       DATE WRITTEN   September 2003
     100              :         //       MODIFIED       na
     101              :         //       RE-ENGINEERED  na
     102              : 
     103              :         // PURPOSE OF THIS SUBROUTINE:
     104              :         // Initializes the derived type SolReflRecSurf, which contains information
     105              :         // needed to calculate factors for solar reflection from obstructions and ground.
     106              : 
     107              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     108              :         int SurfNum;            // Surface number
     109              :         int RecSurfNum;         // Receiving surface number
     110              :         int loop;               // DO loop indices
     111              :         int loop1;              // DO loop indices
     112              :         int loopA;              // DO loop indices
     113              :         int loopB;              // DO loop indices
     114              :         int ObsSurfNum;         // Surface number of an obstruction
     115              :         bool ObsBehindRec;      // True if an obstruction is entirely behind a receiving surface
     116              :         bool ObsHasView;        // True if view between receiving surface and heat trans surf obstruction
     117            9 :         Vector3<Real64> RecVec; // First vertex of a receiving surface (m)
     118            9 :         Vector3<Real64> ObsVec; // A vertex of a candidate obstructing surface (m)
     119            9 :         Vector3<Real64> VecAB;  // Vector from receiving surface vertex to obstruction surface vertex (m)
     120            9 :         Vector3<Real64> HitPt;  // Hit point (m)
     121              :         Real64 DotProd;         // Dot product of vectors (m2)
     122              :         int RecPtNum;           // Receiving point number
     123              :         // unused  REAL(r64)         :: SumX                 ! Sum of X (or Y or Z) coordinate values of a surface
     124              :         // unused  REAL(r64)         :: SumY                 ! Sum of X (or Y or Z) coordinate values of a surface
     125              :         // unused  REAL(r64)         :: SumZ                 ! Sum of X (or Y or Z) coordinate values of a surface
     126              :         Real64 PhiSurf;   // Altitude of normal to receiving surface (radians)
     127              :         Real64 ThetaSurf; // Azimuth of normal to receiving surface (radians)
     128              :         Real64 PhiMin;    // Minimum and maximum values of ray altitude angle (radians)
     129              :         Real64 PhiMax;    // Minimum and maximum values of ray altitude angle (radians)
     130              :         Real64 ThetaMin;  // Minimum and maximum values of ray azimuth angle (radians)
     131              :         Real64 ThetaMax;  // Minimum and maximum values of ray azimuth angle (radians)
     132              :         Real64 Phi;       // Ray altitude angle, increment, sine, and cosine
     133              :         Real64 DPhi;      // Ray altitude angle, increment, sine, and cosine
     134              :         Real64 SPhi;      // Ray altitude angle, increment, sine, and cosine
     135              :         Real64 CPhi;      // Ray altitude angle, increment, sine, and cosine
     136              :         Real64 Theta;     // Ray azimuth angle and increment
     137              :         Real64 DTheta;    // Ray azimuth angle and increment
     138              :         int IPhi;         // Ray altitude angle and azimuth angle indices
     139              :         int ITheta;       // Ray altitude angle and azimuth angle indices
     140              :         // unused  REAL(r64)         :: APhi                 ! Intermediate variable
     141              :         int RayNum;                   // Ray number
     142            9 :         Vector3<Real64> URay;         // Unit vector along ray pointing away from receiving surface
     143              :         Real64 CosIncAngRay;          // Cosine of angle of incidence of ray on receiving surface
     144              :         Real64 dOmega;                // Solid angle associated with a ray
     145              :         bool hit;                     // True iff obstruction is hit
     146              :         int TotObstructionsHit;       // Number of obstructions hit by a ray
     147              :         Real64 HitDistance;           // Distance from receiving point to hit point for a ray (m)
     148              :         int NearestHitSurfNum;        // Surface number of nearest obstruction hit by a ray
     149            9 :         Vector3<Real64> NearestHitPt; // Nearest hit pit for a ray (m)
     150              :         Real64 NearestHitDistance;    // Distance from receiving point to nearest hit point for a ray (m)
     151              :         int ObsSurfNumToSkip;         // Surface number of obstruction to be ignored
     152            9 :         Vector3<Real64> RecPt;        // Receiving point (m)
     153            9 :         Vector3<Real64> RayVec;       // Unit vector along ray
     154            9 :         Vector3<Real64> Vec1;         // Vectors between hit surface vertices (m)
     155            9 :         Vector3<Real64> Vec2;         // Vectors between hit surface vertices (m)
     156            9 :         Vector3<Real64> VNorm;        // For a hit surface, unit normal vector pointing into the hemisphere
     157              :         // containing the receiving point
     158              :         int ObsConstrNum; // Construction number of obstruction; = 0 if a shading surface
     159              :         Real64 Alfa;      // Direction angles for ray heading towards the ground (radians)
     160              :         Real64 Beta;
     161              :         Real64 HorDis;               // Distance between ground hit point and proj'n of receiving pt onto ground (m)
     162            9 :         Vector3<Real64> GroundHitPt; // Coordinates of ground hit point
     163              :         // unused  REAL(r64)         :: ArgASin
     164              :         Real64 ACosTanTan;
     165              :         int J;           // DO loop indices
     166              :         int K;           // DO loop indices
     167              :         int NumRecPts;   // Number of surface receiving points for reflected solar radiation
     168              :         Real64 VertexWt; // Vertex weighting factor for calculating receiving points
     169              : 
     170            9 :         static Vector3<Real64> const unit_z(0.0, 0.0, 1.0);
     171            9 :         static Vector3<Real64> const zero3(0.0);
     172              : 
     173              :         // Find number of surfaces that are sun-exposed exterior building heat transfer surfaces.
     174              :         // These are candidates for receiving solar reflected from obstructions and ground.
     175              :         // CR 7640.  12/3/2008 BG simplified logic to allow for Other Side Conditions Modeled boundary condition.
     176              :         //           and solar collectors on shading surfaces that need this.
     177              : 
     178              :         // shading surfaces have ExtSolar = False, so they are not included in TotSolReflRecSurf
     179            9 :         state.dataSolarReflectionManager->TotSolReflRecSurf = 0;
     180          101 :         for (SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
     181           92 :             if (state.dataSurface->Surface(SurfNum).ExtSolar) {
     182           49 :                 ++state.dataSolarReflectionManager->TotSolReflRecSurf;
     183              :             }
     184              :         }
     185              : 
     186              :         // TH 3/29/2010. ShadowSurfPossibleReflector is not used!
     187              :         // Set flag that determines whether a surface can be an exterior reflector
     188              :         // DO SurfNum = 1,TotSurfaces
     189              :         //  Surface(SurfNum)%ShadowSurfPossibleReflector = .FALSE.
     190              :         // Exclude non-exterior heat transfer surfaces (but not OtherSideCondModeledExt = -4 CR7640)
     191              :         //  IF(Surface(SurfNum)%HeatTransSurf .AND. Surface(SurfNum)%ExtBoundCond > 0 ) CYCLE
     192              :         //  IF(Surface(SurfNum)%HeatTransSurf .AND. Surface(SurfNum)%ExtBoundCond == Ground) CYCLE
     193              :         //  IF(Surface(SurfNum)%HeatTransSurf .AND. Surface(SurfNum)%ExtBoundCond == OtherSideCoefNoCalcExt) CYCLE
     194              :         //  IF(Surface(SurfNum)%HeatTransSurf .AND. Surface(SurfNum)%ExtBoundCond == OtherSideCoefCalcExt) CYCLE
     195              : 
     196              :         // Exclude daylighting shelves. A separate solar reflection calculation is done for these.
     197              :         //  IF(Surface(SurfNum)%Shelf > 0) CYCLE
     198              : 
     199              :         // Exclude duplicate shading surfaces
     200              :         // TH 3/24/2010. Why? a mirror shading surface can reflect solar (either beam or diffuse)
     201              :         //  can use a flag like Surface(SurfNum)%Mirrored (True or False) to avoid string comparison
     202              :         //   and to allow surface names starting with 'Mir'
     203              :         // IF(Surface(SurfNum)%Name(1:3) == 'Mir') CYCLE
     204              :         //  IF(Surface(SurfNum)%MirroredSurf) CYCLE
     205              : 
     206              :         //  Surface(SurfNum)%ShadowSurfPossibleReflector = .TRUE.
     207              :         // END DO
     208              : 
     209            9 :         if (state.dataSolarReflectionManager->TotSolReflRecSurf == 0) {
     210            0 :             ShowWarningError(state, "Calculation of solar reflected from obstructions has been requested but there");
     211            0 :             ShowContinueError(state, "are no building surfaces that can receive reflected solar. Calculation will not be done.");
     212            0 :             state.dataSurface->CalcSolRefl = false;
     213            0 :             return;
     214              :         }
     215              : 
     216              :         // Should this be moved up front?
     217            9 :         if (state.dataEnvrn->IgnoreSolarRadiation) {
     218            0 :             state.dataSolarReflectionManager->TotSolReflRecSurf = 0;
     219            0 :             state.dataSurface->CalcSolRefl = false;
     220            0 :             return;
     221              :         }
     222              : 
     223            9 :         state.dataSolarReflectionManager->SolReflRecSurf.allocate(state.dataSolarReflectionManager->TotSolReflRecSurf);
     224              : 
     225            9 :         state.dataSurface->SurfReflFacBmToDiffSolObs.dimension(24, state.dataSurface->TotSurfaces, 0.0);
     226            9 :         state.dataSurface->SurfReflFacBmToDiffSolGnd.dimension(24, state.dataSurface->TotSurfaces, 0.0);
     227            9 :         state.dataSurface->SurfReflFacBmToBmSolObs.dimension(24, state.dataSurface->TotSurfaces, 0.0);
     228            9 :         state.dataSurface->SurfReflFacSkySolObs.dimension(state.dataSurface->TotSurfaces, 0.0);
     229            9 :         state.dataSurface->SurfReflFacSkySolGnd.dimension(state.dataSurface->TotSurfaces, 0.0);
     230            9 :         state.dataSurface->SurfCosIncAveBmToBmSolObs.dimension(24, state.dataSurface->TotSurfaces, 0.0);
     231              : 
     232              :         // Only surfaces with sun exposure can receive solar reflection from ground or onstructions
     233              :         //  Shading surfaces are always not exposed to solar (ExtSolar = False)
     234            9 :         RecSurfNum = 0;
     235          101 :         for (SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
     236           92 :             state.dataSurface->SurfShadowRecSurfNum(SurfNum) = 0;
     237           92 :             if (state.dataSurface->Surface(SurfNum).ExtSolar) {
     238           49 :                 ++RecSurfNum;
     239           49 :                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum = SurfNum;
     240           49 :                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfName = state.dataSurface->Surface(SurfNum).Name;
     241           49 :                 state.dataSurface->SurfShadowRecSurfNum(SurfNum) = RecSurfNum;
     242              : 
     243              :                 // Warning if any receiving surface vertex is below ground level, taken to be at Z = 0 in absolute coords
     244          245 :                 for (loop = 1; loop <= state.dataSurface->Surface(SurfNum).Sides; ++loop) {
     245          196 :                     if (state.dataSurface->Surface(SurfNum).Vertex(loop).z < state.dataSurface->GroundLevelZ) {
     246            0 :                         ShowWarningError(
     247              :                             state,
     248            0 :                             format("Calculation of reflected solar onto surface={} may be inaccurate", state.dataSurface->Surface(SurfNum).Name));
     249            0 :                         ShowContinueError(state, "because it has one or more vertices below ground level.");
     250            0 :                         break;
     251              :                     }
     252              :                 }
     253              :             }
     254              :         }
     255              : 
     256              :         // Get MaxRecPts for allocating SolReflRecSurf arrays that depend on number of receiving points
     257            9 :         state.dataSurface->MaxRecPts = 1;
     258           58 :         for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
     259           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts =
     260           49 :                 state.dataSurface->Surface(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum).Sides;
     261           49 :             if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts > state.dataSurface->MaxRecPts) {
     262            9 :                 state.dataSurface->MaxRecPts = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts;
     263              :             }
     264              :         }
     265              : 
     266            9 :         state.dataSurface->MaxReflRays = AltAngStepsForSolReflCalc * AzimAngStepsForSolReflCalc;
     267           58 :         for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
     268           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec = 0.0;
     269           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt.dimension(state.dataSurface->MaxRecPts, zero3);
     270           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RayVec.dimension(state.dataSurface->MaxReflRays, zero3);
     271           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).CosIncAngRay.dimension(state.dataSurface->MaxReflRays, 0.0);
     272           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).dOmegaRay.dimension(state.dataSurface->MaxReflRays, 0.0);
     273           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
     274           49 :                 .HitPt.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, zero3);
     275           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
     276           49 :                 .HitPtSurfNum.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, 0.0);
     277           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
     278           49 :                 .HitPtSolRefl.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, 0.0);
     279           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
     280           49 :                 .RecPtHitPtDis.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, 0.0);
     281           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
     282           49 :                 .HitPtNormVec.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, zero3);
     283           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums.dimension(state.dataSurface->TotSurfaces, 0);
     284              :         }
     285              : 
     286           58 :         for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
     287           49 :             SurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum;
     288              :             // Outward norm to receiving surface
     289           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec = state.dataSurface->Surface(SurfNum).OutNormVec;
     290           49 :             RecVec = state.dataSurface->Surface(SurfNum).Vertex(1);
     291              :             // Loop over all surfaces and find those that can be obstructing surfaces for this receiving surf
     292           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs = 0;
     293          733 :             for (ObsSurfNum = 1; ObsSurfNum <= state.dataSurface->TotSurfaces; ++ObsSurfNum) {
     294              :                 // Exclude the receiving surface itself and its base surface (if it has one)
     295          684 :                 if (ObsSurfNum == SurfNum || ObsSurfNum == state.dataSurface->Surface(SurfNum).BaseSurf) {
     296           64 :                     continue;
     297              :                 }
     298              :                 // Exclude non-exterior heat transfer surfaces
     299          620 :                 if (state.dataSurface->Surface(ObsSurfNum).HeatTransSurf && state.dataSurface->Surface(ObsSurfNum).ExtBoundCond != 0) {
     300          239 :                     continue;
     301              :                 }
     302              :                 // Exclude duplicate shading surfaces
     303              :                 // IF(Surface(ObsSurfNum)%Name(1:3) == 'Mir') CYCLE
     304              :                 // TH2 CR8959
     305              :                 // IF(Surface(ObsSurfNum)%MirroredSurf) CYCLE
     306              : 
     307              :                 // Exclude surfaces that are entirely behind the receiving surface.This is true if dot products of the
     308              :                 // rec. surface outward normal and vector from first vertex of rec. surface and each vertex of
     309              :                 // obstructing surface are all negative.
     310          381 :                 ObsBehindRec = true;
     311         1699 :                 for (loop = 1; loop <= state.dataSurface->Surface(ObsSurfNum).Sides; ++loop) {
     312         1376 :                     ObsVec = state.dataSurface->Surface(ObsSurfNum).Vertex(loop);
     313         1376 :                     DotProd = dot(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec, ObsVec - RecVec);
     314              :                     // CR8251      IF(DotProd > 0.01d0) THEN  ! This obstructing-surface vertex is not behind receiving surface
     315         1376 :                     if (DotProd > Constant::OneMillionth) { // This obstructing-surface vertex is not behind receiving surface
     316           58 :                         ObsBehindRec = false;
     317           58 :                         break;
     318              :                     }
     319              :                 }
     320          381 :                 if (ObsBehindRec) {
     321          323 :                     continue;
     322              :                 }
     323              : 
     324              :                 // Exclude heat transfer surfaces that have no view with the receiving surface.
     325              :                 // There is view if: for at least one vector VecAB from a receiving surface vertex to
     326              :                 // a vertex of a potential obstructing surface that satisfies VecAB.nA > 0.0 and VecAB.nB < 0.0,
     327              :                 // where nA and nB are the outward normal to the receiving and obstructing surface, resp.
     328           58 :                 if (state.dataSurface->Surface(ObsSurfNum).HeatTransSurf) {
     329           30 :                     ObsHasView = false;
     330           96 :                     for (loopA = 1; loopA <= state.dataSurface->Surface(SurfNum).Sides; ++loopA) {
     331          350 :                         for (loopB = 1; loopB <= state.dataSurface->Surface(ObsSurfNum).Sides; ++loopB) {
     332          284 :                             VecAB = (state.dataSurface->Surface(ObsSurfNum).Vertex(loopB) - state.dataSurface->Surface(SurfNum).Vertex(loopA));
     333          508 :                             if (dot(VecAB, state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec) > 0.0 &&
     334          224 :                                 dot(VecAB, state.dataSurface->Surface(ObsSurfNum).OutNormVec) < 0.0) {
     335           16 :                                 ObsHasView = true;
     336           16 :                                 break;
     337              :                             }
     338              :                         }
     339           82 :                         if (ObsHasView) {
     340           16 :                             break;
     341              :                         }
     342              :                     }
     343           30 :                     if (!ObsHasView) {
     344           14 :                         continue;
     345              :                     }
     346              :                 }
     347              : 
     348              :                 // This is a possible obstructing surface for this receiving surface
     349           44 :                 ++state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
     350           44 :                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
     351           88 :                     .PossibleObsSurfNums(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs) = ObsSurfNum;
     352              :             }
     353              : 
     354              :             // Get coordinates of receiving points on this receiving surface. The number of receiving points
     355              :             // is equal to the number of surface vertices (3 or higher).
     356              : 
     357           49 :             NumRecPts = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts;
     358          245 :             for (J = 1; J <= NumRecPts; ++J) {
     359          196 :                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J) = 0.0;
     360          980 :                 for (K = 1; K <= NumRecPts; ++K) {
     361          784 :                     if (NumRecPts == 3) { // Receiving surface is a triangle
     362            0 :                         VertexWt = 0.2;
     363            0 :                         if (K == J) {
     364            0 :                             VertexWt = 0.6;
     365              :                         }
     366              :                     } else { // Receiving surface has 4 or more vertices
     367          784 :                         VertexWt = 1.0 / (2.0 * NumRecPts);
     368          784 :                         if (K == J) {
     369          196 :                             VertexWt = (NumRecPts + 1.0) / (2.0 * NumRecPts);
     370              :                         }
     371              :                     }
     372          784 :                     state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J).x +=
     373          784 :                         VertexWt * state.dataSurface->Surface(SurfNum).Vertex(K).x;
     374          784 :                     state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J).y +=
     375          784 :                         VertexWt * state.dataSurface->Surface(SurfNum).Vertex(K).y;
     376          784 :                     state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J).z +=
     377          784 :                         VertexWt * state.dataSurface->Surface(SurfNum).Vertex(K).z;
     378              :                 }
     379              :             }
     380              : 
     381              :             // Create rays going outward from receiving surface. The same rays will be used at each receiving point.
     382              :             // The rays are used in calculating diffusely reflected solar incident on receiving surface.
     383              : 
     384              :             // Divide hemisphere around receiving surface into elements of altitude Phi and
     385              :             // azimuth Theta and create ray unit vector at each Phi,Theta pair in front of the surface.
     386              :             // Phi = 0 at the horizon; Phi = Pi/2 at the zenith
     387              : 
     388           49 :             PhiSurf = std::asin(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.z);
     389           49 :             Real64 const tan_PhiSurf = std::tan(PhiSurf);
     390           49 :             Real64 const sin_PhiSurf = std::sin(PhiSurf);
     391           49 :             Real64 const cos_PhiSurf = std::cos(PhiSurf);
     392              : 
     393           76 :             if (std::abs(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.x) > 1.0e-5 ||
     394           27 :                 std::abs(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.y) > 1.0e-5) {
     395           40 :                 ThetaSurf = std::atan2(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.y,
     396           40 :                                        state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.x);
     397              :             } else {
     398            9 :                 ThetaSurf = 0.0;
     399              :             }
     400           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PhiNormVec = PhiSurf;
     401           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).ThetaNormVec = ThetaSurf;
     402           49 :             PhiMin = max(-Constant::PiOvr2, PhiSurf - Constant::PiOvr2);
     403           49 :             PhiMax = min(Constant::PiOvr2, PhiSurf + Constant::PiOvr2);
     404           49 :             DPhi = (PhiMax - PhiMin) / AltAngStepsForSolReflCalc;
     405           49 :             RayNum = 0;
     406              : 
     407              :             // Altitude loop
     408          539 :             for (IPhi = 1; IPhi <= AltAngStepsForSolReflCalc; ++IPhi) {
     409          490 :                 Phi = PhiMin + (IPhi - 0.5) * DPhi;
     410          490 :                 SPhi = std::sin(Phi);
     411          490 :                 CPhi = std::cos(Phi);
     412              :                 // Third component of ray unit vector in (Theta,Phi) direction
     413          490 :                 URay(3) = SPhi;
     414              : 
     415          490 :                 if (PhiSurf >= 0.0) {
     416          470 :                     if (Phi >= Constant::PiOvr2 - PhiSurf) {
     417           70 :                         ThetaMin = -Constant::Pi;
     418           70 :                         ThetaMax = Constant::Pi;
     419              :                     } else {
     420          400 :                         ACosTanTan = std::acos(-std::tan(Phi) * tan_PhiSurf);
     421          400 :                         ThetaMin = ThetaSurf - std::abs(ACosTanTan);
     422          400 :                         ThetaMax = ThetaSurf + std::abs(ACosTanTan);
     423              :                     }
     424              : 
     425              :                 } else { // PhiSurf < 0.0
     426           20 :                     if (Phi <= -PhiSurf - Constant::PiOvr2) {
     427           20 :                         ThetaMin = -Constant::Pi;
     428           20 :                         ThetaMax = Constant::Pi;
     429              :                     } else {
     430            0 :                         ACosTanTan = std::acos(-std::tan(Phi) * tan_PhiSurf);
     431            0 :                         ThetaMin = ThetaSurf - std::abs(ACosTanTan);
     432            0 :                         ThetaMax = ThetaSurf + std::abs(ACosTanTan);
     433              :                     }
     434              :                 }
     435              : 
     436          490 :                 DTheta = (ThetaMax - ThetaMin) / AzimAngStepsForSolReflCalc;
     437          490 :                 dOmega = CPhi * DTheta * DPhi;
     438              : 
     439              :                 // Azimuth loop
     440         4900 :                 for (ITheta = 1; ITheta <= AzimAngStepsForSolReflCalc; ++ITheta) {
     441         4410 :                     Theta = ThetaMin + (ITheta - 0.5) * DTheta;
     442         4410 :                     URay.x = CPhi * std::cos(Theta);
     443         4410 :                     URay.y = CPhi * std::sin(Theta);
     444              :                     // Cosine of angle of incidence of ray on receiving surface
     445         4410 :                     CosIncAngRay = SPhi * sin_PhiSurf + CPhi * cos_PhiSurf * std::cos(Theta - ThetaSurf);
     446         4410 :                     if (CosIncAngRay < 0.0) {
     447            0 :                         continue; // Ray is behind receiving surface (although there shouldn't be any)
     448              :                     }
     449         4410 :                     ++RayNum;
     450         4410 :                     state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RayVec(RayNum) = URay;
     451         4410 :                     state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).CosIncAngRay(RayNum) = CosIncAngRay;
     452         4410 :                     state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).dOmegaRay(RayNum) = dOmega;
     453              :                 } // End of azimuth loop
     454              : 
     455              :             } // End of altitude loop
     456           49 :             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumReflRays = RayNum;
     457              : 
     458              :         } // End of loop over receiving surfaces
     459              : 
     460              :         // Loop again over receiving surfaces and, for each ray, get hit point and info associated with that point
     461              :         // (hit point = point that ray intersects nearest obstruction, or, if ray is downgoing and hits no
     462              :         // obstructions, point that ray intersects ground plane).
     463              : 
     464           58 :         for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
     465           49 :             SurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum;
     466          245 :             for (RecPtNum = 1; RecPtNum <= state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts; ++RecPtNum) {
     467          196 :                 RecPt = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(RecPtNum);
     468        17836 :                 for (RayNum = 1; RayNum <= state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumReflRays; ++RayNum) {
     469              :                     // Loop over possible obstructions. If ray hits one or more obstructions get hit point on closest obstruction.
     470              :                     // If ray hits no obstructions and is going upward set HitPointSurfNum = 0.
     471              :                     // If ray hits no obstructions and is going downward set HitPointSurfNum = -1 and get hit point on ground.
     472        17640 :                     TotObstructionsHit = 0;
     473        17640 :                     NearestHitSurfNum = 0;
     474        17640 :                     NearestHitDistance = 1.0e+8;
     475        17640 :                     ObsSurfNumToSkip = 0;
     476        17640 :                     RayVec = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RayVec(RayNum);
     477        33480 :                     for (loop1 = 1; loop1 <= state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; ++loop1) {
     478              :                         // Surface number of this obstruction
     479        15840 :                         ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop1);
     480              :                         // If a window was hit previously (see below), ObsSurfNumToSkip was set to the window's base surface in order
     481              :                         // to remove that surface from consideration as a hit surface for this ray
     482        15840 :                         if (ObsSurfNum == ObsSurfNumToSkip) {
     483            0 :                             continue;
     484              :                         }
     485              :                         // Determine if this ray hits ObsSurfNum (in which case hit is true) and, if so, what the
     486              :                         // distance from the receiving point to the hit point is
     487        15840 :                         hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
     488        15840 :                         if (hit) {
     489              :                             // added TH 3/29/2010 to set ObsSurfNumToSkip
     490         1440 :                             if (state.dataSurface->Surface(ObsSurfNum).Class == SurfaceClass::Window) {
     491          106 :                                 ObsSurfNumToSkip = state.dataSurface->Surface(ObsSurfNum).BaseSurf;
     492              :                             }
     493              : 
     494              :                             // If obstruction is a window and its base surface is the nearest obstruction hit so far,
     495              :                             // set NearestHitSurfNum to this window. Note that in this case NearestHitDistance has already
     496              :                             // been calculated, so does not have to be recalculated.
     497         1546 :                             if (state.dataSurface->Surface(ObsSurfNum).Class == SurfaceClass::Window &&
     498          106 :                                 state.dataSurface->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum) {
     499          106 :                                 NearestHitSurfNum = ObsSurfNum;
     500              :                             } else {
     501         1334 :                                 ++TotObstructionsHit;
     502              :                                 // Distance from receiving point to hit point
     503         1334 :                                 HitDistance = distance(HitPt, RecPt);
     504              :                                 // Reset NearestHitSurfNum and NearestHitDistance if this hit point is closer than previous closest
     505         1334 :                                 if (HitDistance < NearestHitDistance) {
     506          840 :                                     NearestHitDistance = HitDistance;
     507          840 :                                     NearestHitSurfNum = ObsSurfNum;
     508          840 :                                     NearestHitPt = HitPt;
     509          494 :                                 } else if (HitDistance == NearestHitDistance) { // TH2 CR8959
     510              :                                     // Ray hits mirrored surfaces. Choose the surface facing the ray.
     511          494 :                                     if (dot(state.dataSurface->Surface(ObsSurfNum).OutNormVec, RayVec) <= 0.0) {
     512          494 :                                         NearestHitSurfNum = ObsSurfNum;
     513              :                                     }
     514              :                                 }
     515              :                             }
     516              :                         } // End of check if obstruction was hit
     517              :                     } // End of loop over possible obstructions for this ray
     518              : 
     519        17640 :                     if (TotObstructionsHit > 0) {
     520              :                         // One or more obstructions were hit by this ray
     521          830 :                         state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSurfNum(RayNum, RecPtNum) = NearestHitSurfNum;
     522          830 :                         state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPtHitPtDis(RayNum, RecPtNum) = NearestHitDistance;
     523          830 :                         state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPt(RayNum, RecPtNum) = NearestHitPt;
     524              :                         // For hit surface, calculate unit normal vector pointing into the hemisphere
     525              :                         // containing the receiving point
     526          830 :                         Vec1 = (state.dataSurface->Surface(NearestHitSurfNum).Vertex(1) - state.dataSurface->Surface(NearestHitSurfNum).Vertex(3));
     527          830 :                         Vec2 = (state.dataSurface->Surface(NearestHitSurfNum).Vertex(2) - state.dataSurface->Surface(NearestHitSurfNum).Vertex(3));
     528          830 :                         VNorm = cross(Vec1, Vec2);
     529          830 :                         VNorm.normalize(); // Do Handle magnitude==0
     530          830 :                         if (dot(VNorm, -RayVec) < 0.0) {
     531            0 :                             VNorm = -VNorm;
     532              :                         }
     533          830 :                         state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtNormVec(RayNum, RecPtNum) = VNorm;
     534              :                         // Get solar and visible beam-to-diffuse reflectance at nearest hit point
     535          830 :                         ObsConstrNum = state.dataSurface->Surface(NearestHitSurfNum).Construction;
     536          830 :                         if (ObsConstrNum > 0) {
     537              :                             // Exterior building surface is nearest hit
     538          346 :                             if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
     539              :                                 // Obstruction is not a window, i.e., is an opaque surface
     540          240 :                                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) =
     541          240 :                                     1.0 - state.dataConstruction->Construct(ObsConstrNum).OutsideAbsorpSolar;
     542              :                             } else {
     543              :                                 // Obstruction is a window. Assume it is bare so that there is no beam-to-diffuse reflection
     544              :                                 // (beam-to-beam reflection is calculated in subroutine CalcBeamSolSpecularReflFactors).
     545          106 :                                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) = 0.0;
     546              :                             }
     547              :                         } else {
     548              :                             // Shading surface is nearest hit
     549          484 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) =
     550          484 :                                 state.dataSurface->SurfShadowDiffuseSolRefl(NearestHitSurfNum);
     551              :                         }
     552              :                     } else {
     553              :                         // No obstructions were hit by this ray
     554        16810 :                         state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSurfNum(RayNum, RecPtNum) = 0;
     555              :                         // If ray is going downward find the hit point on the ground plane if the receiving point
     556              :                         // is above ground level; note that GroundLevelZ is <= 0.0
     557        24414 :                         if (RayVec(3) < 0.0 &&
     558         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(RecPtNum).z > state.dataSurface->GroundLevelZ) {
     559              :                             // Ray hits ground
     560         7604 :                             Alfa = std::acos(-RayVec.z);
     561         7604 :                             Beta = std::atan2(RayVec.y, RayVec.x);
     562         7604 :                             HorDis = (RecPt.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
     563         7604 :                             GroundHitPt.z = state.dataSurface->GroundLevelZ;
     564         7604 :                             GroundHitPt.x = RecPt.x + HorDis * std::cos(Beta);
     565         7604 :                             GroundHitPt.y = RecPt.y + HorDis * std::sin(Beta);
     566         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPt(RayNum, RecPtNum) = GroundHitPt;
     567         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSurfNum(RayNum, RecPtNum) = -1;
     568         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPtHitPtDis(RayNum, RecPtNum) =
     569         7604 :                                 (RecPt(3) - state.dataSurface->GroundLevelZ) / (-RayVec(3));
     570         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) =
     571         7604 :                                 state.dataEnvrn->GndReflectance;
     572         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtNormVec(RayNum, RecPtNum) = unit_z;
     573              :                         } // End of check if ray hits ground
     574              :                     } // End of check if obstruction hit
     575              :                 } // End of RayNum loop
     576              :             } // End of receiving point loop
     577              :         } // End of receiving surface loop
     578            9 :     }
     579              : 
     580              :     //=====================================================================================================
     581              : 
     582           71 :     void CalcBeamSolDiffuseReflFactors(EnergyPlusData &state)
     583              :     {
     584              : 
     585              :         // SUBROUTINE INFORMATION:
     586              :         //       AUTHOR         Fred Winkelmann
     587              :         //       DATE WRITTEN   September 2003
     588              :         //       MODIFIED       TH 4/6/2010, fixed CR 7872
     589              :         //       RE-ENGINEERED  B. Griffith, October 2012, for timestep integrated solar.
     590              : 
     591              :         // PURPOSE OF THIS SUBROUTINE:
     592              :         // manage calculations for factors for irradiance on exterior heat transfer surfaces due to
     593              :         // beam-to-diffuse solar reflection from obstructions and ground.
     594              : 
     595              :         // METHODOLOGY EMPLOYED: call worker routine depending on solar calculation method
     596              : 
     597           71 :         if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     598           71 :             if (state.dataGlobal->BeginSimFlag) {
     599            9 :                 DisplayString(state, "Calculating Beam-to-Diffuse Exterior Solar Reflection Factors");
     600              :             } else {
     601           62 :                 DisplayString(state, "Updating Beam-to-Diffuse Exterior Solar Reflection Factors");
     602              :             }
     603           71 :             state.dataSurface->SurfReflFacBmToDiffSolObs = 0.0;
     604           71 :             state.dataSurface->SurfReflFacBmToDiffSolGnd = 0.0;
     605         1775 :             for (state.dataSolarReflectionManager->IHr = 1; state.dataSolarReflectionManager->IHr <= 24; ++state.dataSolarReflectionManager->IHr) {
     606         1704 :                 FigureBeamSolDiffuseReflFactors(state, state.dataSolarReflectionManager->IHr);
     607              :             } // End of IHr loop
     608              :         } else { // timestep integrated solar, use current hour of day
     609            0 :             state.dataSurface->SurfReflFacBmToDiffSolObs(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
     610            0 :             state.dataSurface->SurfReflFacBmToDiffSolGnd(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
     611            0 :             FigureBeamSolDiffuseReflFactors(state, state.dataGlobal->HourOfDay);
     612              :         }
     613           71 :     }
     614              : 
     615         1704 :     void FigureBeamSolDiffuseReflFactors(EnergyPlusData &state, int const iHour)
     616              :     {
     617              : 
     618              :         // SUBROUTINE INFORMATION:
     619              :         //       AUTHOR         Fred Winkelmann, derived from original CalcBeamSolDiffuseReflFactors
     620              :         //       DATE WRITTEN   September 2003
     621              :         //       MODIFIED       na
     622              :         //       RE-ENGINEERED  B. Griffith, October 2012, revised for timestep integrated solar
     623              : 
     624              :         // PURPOSE OF THIS SUBROUTINE:
     625              :         // Calculates factors for irradiance on exterior heat transfer surfaces due to
     626              :         // beam-to-diffuse solar reflection from obstructions and ground.
     627              : 
     628         1704 :         Array1D<Real64> ReflBmToDiffSolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
     629              :         // beam solar diffusely reflected from obstructions, divided by
     630              :         // beam normal irradiance
     631         1704 :         Array1D<Real64> ReflBmToDiffSolGnd(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
     632              :         // beam solar diffusely reflected from the ground, divided by
     633              :         // beam normal irradiance
     634              :         bool hit; // True iff obstruction is hit
     635         1704 :         ReflBmToDiffSolObs = 0.0;
     636         1704 :         ReflBmToDiffSolGnd = 0.0;
     637              : 
     638              :         // Unit vector to sun
     639         1704 :         state.dataSolarReflectionManager->SunVec = state.dataSurface->SurfSunCosHourly(iHour);
     640              : 
     641              :         // loop through each surface that can receive beam solar reflected as diffuse solar from other surfaces
     642         1704 :         for (state.dataSolarReflectionManager->RecSurfNum = 1;
     643        11256 :              state.dataSolarReflectionManager->RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf;
     644         9552 :              ++state.dataSolarReflectionManager->RecSurfNum) {
     645        19104 :             state.dataSolarReflectionManager->SurfNum =
     646         9552 :                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).SurfNum;
     647              : 
     648         9552 :             for (state.dataSolarReflectionManager->RecPtNum = 1;
     649        47760 :                  state.dataSolarReflectionManager->RecPtNum <=
     650        47760 :                  state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).NumRecPts;
     651        38208 :                  ++state.dataSolarReflectionManager->RecPtNum) {
     652        38208 :                 ReflBmToDiffSolObs(state.dataSolarReflectionManager->RecPtNum) = 0.0;
     653        38208 :                 ReflBmToDiffSolGnd(state.dataSolarReflectionManager->RecPtNum) = 0.0;
     654              : 
     655        38208 :                 for (state.dataSolarReflectionManager->RayNum = 1;
     656      3476928 :                      state.dataSolarReflectionManager->RayNum <=
     657      3476928 :                      state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).NumReflRays;
     658      3438720 :                      ++state.dataSolarReflectionManager->RayNum) {
     659      6877440 :                     state.dataSolarReflectionManager->HitPtSurfNum =
     660      3438720 :                         state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     661      3438720 :                             .HitPtSurfNum(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum);
     662              : 
     663              :                     // Skip rays that do not hit an obstruction or ground.
     664              :                     // (Note that if a downgoing ray does not hit an obstruction it will have HitPtSurfNum = 0
     665              :                     // if the receiving point is below ground level (see subr. InitSolReflRecSurf); this means
     666              :                     // that a below-ground-level receiving point receives no ground-reflected radiation although
     667              :                     // it is allowed to receive obstruction-reflected solar radiation and direct (unreflected)
     668              :                     // beam and sky solar radiation. As far as reflected solar is concerned, the program does
     669              :                     // not handle a sloped ground plane or a horizontal ground plane whose level is different
     670              :                     // from one side of the building to another.)
     671      3438720 :                     if (state.dataSolarReflectionManager->HitPtSurfNum == 0) {
     672      1654464 :                         continue; // Ray hits sky or obstruction with receiving pt. below ground level
     673              :                     }
     674              : 
     675      1784256 :                     if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
     676              :                         // Skip rays that hit a daylighting shelf, from which solar reflection is calculated separately.
     677       307776 :                         if (state.dataSurface->SurfDaylightingShelfInd(state.dataSolarReflectionManager->HitPtSurfNum) > 0) {
     678            0 :                             continue;
     679              :                         }
     680              : 
     681              :                         // Skip rays that hit a window
     682              :                         // If hit point's surface is a window or glass door go to next ray since it is assumed for now
     683              :                         // that windows have only beam-to-beam, not beam-to-diffuse, reflection
     684              :                         // TH 3/29/2010. Code modified and moved
     685       560448 :                         if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).Class == SurfaceClass::Window ||
     686       252672 :                             state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).Class == SurfaceClass::GlassDoor) {
     687        55104 :                             continue;
     688              :                         }
     689              : 
     690              :                         // Skip rays that hit non-sunlit surface. Assume first time step of the hour.
     691       505344 :                         state.dataSolarReflectionManager->SunLitFract =
     692       252672 :                             state.dataHeatBal->SurfSunlitFrac(iHour, 1, state.dataSolarReflectionManager->HitPtSurfNum);
     693              : 
     694              :                         // If hit point's surface is not sunlit go to next ray
     695              :                         // TH 3/25/2010. why limit to HeatTransSurf? shading surfaces should also apply
     696              :                         // IF(Surface(HitPtSurfNum)%HeatTransSurf .AND. SunLitFract < 0.01d0) CYCLE
     697       252672 :                         if (state.dataSolarReflectionManager->SunLitFract < 0.01) {
     698       231352 :                             continue;
     699              :                         }
     700              : 
     701              :                         // TH 3/26/2010. If the hit point falls into the shadow even though SunLitFract > 0, can Cycle.
     702              :                         //  This cannot be done now, therefore there are follow-up checks of blocking sun ray
     703              :                         //   from the hit point.
     704              : 
     705              :                         // TH 3/29/2010. Code modified and moved up
     706              :                         // If hit point's surface is a window go to next ray since it is assumed for now
     707              :                         // that windows have only beam-to-beam, not beam-to-diffuse, reflection
     708              :                         // IF(Surface(HitPtSurfNum)%Construction > 0) THEN
     709              :                         //  IF(Construct(Surface(HitPtSurfNum)%Construction)%TypeIsWindow) CYCLE
     710              :                         // END IF
     711              :                     }
     712              : 
     713              :                     // Does an obstruction block the vector from this ray's hit point to the sun?
     714      1497800 :                     state.dataSolarReflectionManager->OriginThisRay =
     715      1497800 :                         state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     716      1497800 :                             .HitPt(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum);
     717              : 
     718              :                     // Note: if sun is in back of hit surface relative to receiving point, CosIncBmAtHitPt will be < 0
     719      1497800 :                     state.dataSolarReflectionManager->CosIncBmAtHitPt =
     720      1497800 :                         dot(state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     721      1497800 :                                 .HitPtNormVec(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum),
     722      1497800 :                             state.dataSolarReflectionManager->SunVec);
     723      1497800 :                     if (state.dataSolarReflectionManager->CosIncBmAtHitPt <= 0.0) {
     724      1008392 :                         continue;
     725              :                     }
     726              : 
     727              :                     // CR 7872 - TH 4/6/2010. The shading surfaces should point to the receiveing heat transfer surface
     728              :                     //  according to the the right hand rule. If user inputs do not follow the rule, use the following
     729              :                     //  code to check the mirrored shading surface
     730       489408 :                     if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
     731        18588 :                         if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).IsShadowing) {
     732         3328 :                             if (state.dataSolarReflectionManager->HitPtSurfNum + 1 < state.dataSurface->TotSurfaces) {
     733         3328 :                                 if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum + 1).IsShadowing &&
     734            0 :                                     state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum + 1).MirroredSurf) {
     735              :                                     // Check whether the sun is behind the mirrored shading surface
     736            0 :                                     state.dataSolarReflectionManager->CosIncBmAtHitPt2 =
     737            0 :                                         dot(state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum + 1).OutNormVec,
     738            0 :                                             state.dataSolarReflectionManager->SunVec);
     739            0 :                                     if (state.dataSolarReflectionManager->CosIncBmAtHitPt2 >= 0.0) {
     740            0 :                                         continue;
     741              :                                     }
     742              :                                 }
     743              :                             }
     744              :                         }
     745              :                     }
     746              : 
     747              :                     // TH 3/25/2010. CR 7872. Seems should loop over all possible obstructions for the HitPtSurfNum
     748              :                     //  rather than RecSurfNum, because if the HitPtSurfNum is a shading surface,
     749              :                     //  it does not belong to SolReflRecSurf which only contain heat transfer surfaces
     750              :                     //  that can receive reflected solar (ExtSolar = True)!
     751              : 
     752              :                     // To speed up, ideally should store all possible shading surfaces for the HitPtSurfNum
     753              :                     //  obstruction surface in the SolReflSurf(HitPtSurfNum)%PossibleObsSurfNums(loop) array as well
     754       489408 :                     hit = false;
     755       489408 :                     for (state.dataSolarReflectionManager->ObsSurfNum = 1;
     756      5522378 :                          state.dataSolarReflectionManager->ObsSurfNum <= state.dataSurface->TotSurfaces;
     757      5032970 :                          ++state.dataSolarReflectionManager->ObsSurfNum) {
     758              :                         //        DO loop = 1,SolReflRecSurf(RecSurfNum)%NumPossibleObs
     759              :                         //          ObsSurfNum = SolReflRecSurf(RecSurfNum)%PossibleObsSurfNums(loop)
     760              : 
     761              :                         // CR 8959 -- The other side of a mirrored surface cannot obstruct the mirrored surface
     762      5169825 :                         if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
     763       162789 :                             if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).MirroredSurf) {
     764        79872 :                                 if (state.dataSolarReflectionManager->ObsSurfNum == state.dataSolarReflectionManager->HitPtSurfNum - 1) {
     765         3328 :                                     continue;
     766              :                                 }
     767              :                             }
     768              :                         }
     769              : 
     770              :                         // skip the hit surface
     771      5166497 :                         if (state.dataSolarReflectionManager->ObsSurfNum == state.dataSolarReflectionManager->HitPtSurfNum) {
     772         7510 :                             continue;
     773              :                         }
     774              : 
     775              :                         // skip mirrored surfaces
     776      5158987 :                         if (state.dataSurface->Surface(state.dataSolarReflectionManager->ObsSurfNum).MirroredSurf) {
     777       590757 :                             continue;
     778              :                         }
     779              :                         // IF(Surface(ObsSurfNum)%ShadowingSurf .AND. Surface(ObsSurfNum)%Name(1:3) == 'Mir') THEN
     780              :                         //  CYCLE
     781              :                         // ENDIF
     782              : 
     783              :                         // skip interior surfaces
     784      4568230 :                         if (state.dataSurface->Surface(state.dataSolarReflectionManager->ObsSurfNum).ExtBoundCond >= 1) {
     785      1896662 :                             continue;
     786              :                         }
     787              : 
     788              :                         // For now it is assumed that obstructions that are shading surfaces are opaque.
     789              :                         // An improvement here would be to allow these to have transmittance.
     790      2671568 :                         hit = PierceSurface(state,
     791      2671568 :                                             state.dataSolarReflectionManager->ObsSurfNum,
     792      2671568 :                                             state.dataSolarReflectionManager->OriginThisRay,
     793      2671568 :                                             state.dataSolarReflectionManager->SunVec,
     794      2671568 :                                             state.dataSolarReflectionManager->ObsHitPt);
     795      2671568 :                         if (hit) {
     796       136855 :                             break; // An obstruction was hit
     797              :                         }
     798              :                     }
     799       489408 :                     if (hit) {
     800       136855 :                         continue; // Sun does not reach this ray's hit point
     801              :                     }
     802              : 
     803              :                     // Sun reaches this ray's hit point; get beam-reflected diffuse radiance at hit point for
     804              :                     // unit beam normal solar
     805              : 
     806              :                     // CosIncBmAtHitPt = DOT_PRODUCT(SolReflRecSurf(RecSurfNum)%HitPtNormVec(RecPtNum,RayNum),SunVec)
     807              :                     // Note: if sun is in back of hit surface relative to receiving point, CosIncBmAtHitPt will be < 0
     808              :                     // and use of MAX in following gives zero beam solar reflecting at hit point.
     809              :                     // BmReflSolRadiance = MAX(0.0d0,CosIncBmAtHitPt)*SolReflRecSurf(RecSurfNum)%HitPtSolRefl(RecPtNum,RayNum)
     810              : 
     811       705106 :                     state.dataSolarReflectionManager->BmReflSolRadiance =
     812       352553 :                         state.dataSolarReflectionManager->CosIncBmAtHitPt *
     813       352553 :                         state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     814       352553 :                             .HitPtSolRefl(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum);
     815              : 
     816       352553 :                     if (state.dataSolarReflectionManager->BmReflSolRadiance > 0.0) {
     817              :                         // Contribution to reflection factor from this hit point
     818       352553 :                         if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
     819              :                             // Ray hits an obstruction
     820        14906 :                             state.dataSolarReflectionManager->dReflBeamToDiffSol =
     821         7453 :                                 state.dataSolarReflectionManager->BmReflSolRadiance *
     822         7453 :                                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     823         7453 :                                     .dOmegaRay(state.dataSolarReflectionManager->RayNum) *
     824         7453 :                                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     825         7453 :                                     .CosIncAngRay(state.dataSolarReflectionManager->RayNum) /
     826              :                                 Constant::Pi;
     827         7453 :                             ReflBmToDiffSolObs(state.dataSolarReflectionManager->RecPtNum) += state.dataSolarReflectionManager->dReflBeamToDiffSol;
     828              :                         } else {
     829              :                             // Ray hits ground (in this case we do not multiply by BmReflSolRadiance since
     830              :                             // ground reflectance and cos of incidence angle of sun on
     831              :                             // ground is taken into account later when SurfReflFacBmToDiffSolGnd is used)
     832       690200 :                             state.dataSolarReflectionManager->dReflBeamToDiffSol =
     833       345100 :                                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     834       345100 :                                     .dOmegaRay(state.dataSolarReflectionManager->RayNum) *
     835       345100 :                                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
     836       345100 :                                     .CosIncAngRay(state.dataSolarReflectionManager->RayNum) /
     837              :                                 Constant::Pi;
     838       345100 :                             ReflBmToDiffSolGnd(state.dataSolarReflectionManager->RecPtNum) += state.dataSolarReflectionManager->dReflBeamToDiffSol;
     839              :                         }
     840              :                     }
     841              :                 } // End of loop over rays from receiving point
     842              :             } // End of loop over receiving points
     843              : 
     844              :             // Average over receiving points
     845         9552 :             state.dataSurface->SurfReflFacBmToDiffSolObs(iHour, state.dataSolarReflectionManager->SurfNum) = 0.0;
     846         9552 :             state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) = 0.0;
     847        19104 :             state.dataSolarReflectionManager->NumRecPts =
     848         9552 :                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).NumRecPts;
     849         9552 :             for (state.dataSolarReflectionManager->RecPtNum = 1;
     850        47760 :                  state.dataSolarReflectionManager->RecPtNum <= state.dataSolarReflectionManager->NumRecPts;
     851        38208 :                  ++state.dataSolarReflectionManager->RecPtNum) {
     852        38208 :                 state.dataSurface->SurfReflFacBmToDiffSolObs(iHour, state.dataSolarReflectionManager->SurfNum) +=
     853        38208 :                     ReflBmToDiffSolObs(state.dataSolarReflectionManager->RecPtNum);
     854        38208 :                 state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) +=
     855        38208 :                     ReflBmToDiffSolGnd(state.dataSolarReflectionManager->RecPtNum);
     856              :             }
     857         9552 :             state.dataSurface->SurfReflFacBmToDiffSolObs(iHour, state.dataSolarReflectionManager->SurfNum) /=
     858         9552 :                 state.dataSolarReflectionManager->NumRecPts;
     859         9552 :             state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) /=
     860         9552 :                 state.dataSolarReflectionManager->NumRecPts;
     861              : 
     862              :             // Do not allow SurfReflFacBmToDiffSolGnd to exceed the surface's unobstructed ground view factor
     863         9552 :             state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) =
     864         9552 :                 min(0.5 * (1.0 - state.dataSurface->Surface(state.dataSolarReflectionManager->SurfNum).CosTilt),
     865         9552 :                     state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum));
     866              :             // Note: the above factors are dimensionless; they are equal to
     867              :             // (W/m2 reflected solar incident on SurfNum)/(W/m2 beam normal solar)
     868              :         } // End of loop over receiving surfaces
     869         1704 :     }
     870              : 
     871              :     //=================================================================================================
     872              : 
     873           71 :     void CalcBeamSolSpecularReflFactors(EnergyPlusData &state)
     874              :     {
     875              : 
     876              :         // SUBROUTINE INFORMATION:
     877              :         //       AUTHOR         Fred Winkelmann
     878              :         //       DATE WRITTEN   September 2003
     879              :         //       MODIFIED       na
     880              :         //       RE-ENGINEERED  B. Griffith, October 2012, for timestep integrated solar
     881              : 
     882              :         // PURPOSE OF THIS SUBROUTINE:
     883              :         // Manage calculation of factors for beam solar irradiance on exterior heat transfer surfaces due to
     884              :         // specular (beam-to-beam) reflection from obstructions such as a highly-glazed neighboring
     885              :         // building.
     886              : 
     887              :         // METHODOLOGY EMPLOYED:
     888              :         // call worker routine as appropriate
     889              : 
     890           71 :         if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
     891           71 :             if (state.dataGlobal->BeginSimFlag) {
     892            9 :                 DisplayString(state, "Calculating Beam-to-Beam Exterior Solar Reflection Factors");
     893              :             } else {
     894           62 :                 DisplayString(state, "Updating Beam-to-Beam Exterior Solar Reflection Factors");
     895              :             }
     896           71 :             state.dataSurface->SurfReflFacBmToBmSolObs = 0.0;
     897           71 :             state.dataSurface->SurfCosIncAveBmToBmSolObs = 0.0;
     898         1775 :             for (state.dataSolarReflectionManager->NumHr = 1; state.dataSolarReflectionManager->NumHr <= 24;
     899         1704 :                  ++state.dataSolarReflectionManager->NumHr) {
     900         1704 :                 FigureBeamSolSpecularReflFactors(state, state.dataSolarReflectionManager->NumHr);
     901              :             } // End of NumHr loop
     902              :         } else { // timestep integrated solar, use current hour of day
     903            0 :             state.dataSurface->SurfReflFacBmToBmSolObs(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
     904            0 :             state.dataSurface->SurfCosIncAveBmToBmSolObs(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
     905            0 :             FigureBeamSolSpecularReflFactors(state, state.dataGlobal->HourOfDay);
     906              :         }
     907           71 :     }
     908              : 
     909         1704 :     void FigureBeamSolSpecularReflFactors(EnergyPlusData &state, int const iHour)
     910              :     {
     911              : 
     912              :         // SUBROUTINE INFORMATION:
     913              :         //       AUTHOR         Fred Winkelmann
     914              :         //       DATE WRITTEN   September 2003
     915              :         //       MODIFIED       na
     916              :         //       RE-ENGINEERED  B. Griffith, October 2012, for timestep integrated solar
     917              : 
     918              :         // PURPOSE OF THIS SUBROUTINE:
     919              :         // Calculates factors for beam solar irradiance on exterior heat transfer surfaces due to
     920              :         // specular (beam-to-beam) reflection from obstructions such as a highly-glazed neighboring
     921              :         // building. Specular reflection can occur from shading surfaces with non-zero specular
     922              :         // reflectance and from exterior windows of the building (in calculating reflection from
     923              :         // these windows, they are assumed to have no shades or blinds).
     924              :         // Reflection from the ground and opaque building surfaces is assumed to be totally diffuse,
     925              :         // i.e. these surfaces has no specular reflection component.
     926              : 
     927              :         // METHODOLOGY EMPLOYED:
     928              :         // <description>
     929              : 
     930              :         // REFERENCES:
     931              :         // na
     932              : 
     933              :         // Locals
     934              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     935         1704 :         Array1D<Real64> ReflBmToDiffSolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
     936              :         // beam solar diffusely reflected from obstructions, divided by
     937              :         // beam normal irradiance
     938              :         // unused  INTEGER           :: RayNum               =0   ! Ray number
     939              :         bool hitRefl;                                                   // True iff reflecting surface is hit
     940              :         bool hitObs;                                                    // True iff obstruction is hit
     941              :         bool hitObsRefl;                                                // True iff obstruction hit between rec. pt. and reflection point
     942         1704 :         Array1D<Real64> ReflBmToBmSolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
     943              :         // beam solar specularly reflected from obstructions, divided by
     944              :         // beam normal irradiance
     945              :         Real64 ReflDistanceSq; // Distance squared from receiving point to hit point on a reflecting surface (m)
     946              :         Real64 ReflDistance;   // Distance from receiving point to hit point on a reflecting surface (m)
     947         1704 :         Array1D<Real64> ReflFacTimesCosIncSum(state.dataSurface->MaxRecPts); // Sum of ReflFac times CosIncAngRefl
     948              : 
     949         1704 :         ReflBmToDiffSolObs = 0.0;
     950         1704 :         ReflFacTimesCosIncSum = 0.0;
     951              : 
     952         1704 :         if (state.dataSurface->SurfSunCosHourly(iHour)(3) < DataEnvironment::SunIsUpValue) {
     953         1149 :             return; // Skip if sun is below horizon
     954              :         }
     955              : 
     956              :         // Unit vector to sun
     957          555 :         state.dataSolarReflectionManager->SunVect = state.dataSurface->SurfSunCosHourly(iHour);
     958              : 
     959         3615 :         for (int RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
     960              :             int const SurfNum =
     961         3060 :                 state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum; // Heat transfer surface number corresponding to RecSurfNum
     962         3060 :             if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
     963         1626 :                 ReflBmToBmSolObs = 0.0;
     964         1626 :                 ReflFacTimesCosIncSum = 0.0;
     965         1626 :                 int const NumRecPts = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts;
     966              :                 // Find possible reflecting surfaces for this receiving surface
     967         5118 :                 for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end; ++loop) {
     968              :                     int const ReflSurfNum =
     969         3492 :                         state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop); // Reflecting surface number
     970              :                     // Keep windows; keep shading surfaces with specular reflectance
     971         6506 :                     if ((state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window && state.dataSurface->Surface(ReflSurfNum).ExtSolar) ||
     972         3014 :                         (state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) > 0.0 && state.dataSurface->Surface(ReflSurfNum).IsShadowing)) {
     973              :                         // Skip if window and not sunlit
     974         1436 :                         if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window &&
     975          478 :                             state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNum) < 0.01) {
     976          261 :                             continue;
     977              :                         }
     978              :                         // Check if sun is in front of this reflecting surface.
     979          697 :                         state.dataSolarReflectionManager->ReflNorm = state.dataSurface->Surface(ReflSurfNum).OutNormVec;
     980          697 :                         state.dataSolarReflectionManager->CosIncAngRefl =
     981          697 :                             dot(state.dataSolarReflectionManager->SunVect, state.dataSolarReflectionManager->ReflNorm);
     982          697 :                         if (state.dataSolarReflectionManager->CosIncAngRefl < 0.0) {
     983          257 :                             continue;
     984              :                         }
     985              : 
     986              :                         // Get sun position unit vector for mirror image of sun in reflecting surface
     987          440 :                         state.dataSolarReflectionManager->SunVecMir =
     988          880 :                             state.dataSolarReflectionManager->SunVect -
     989          880 :                             2.0 * dot(state.dataSolarReflectionManager->SunVect, state.dataSolarReflectionManager->ReflNorm) *
     990          880 :                                 state.dataSolarReflectionManager->ReflNorm;
     991              :                         // Angle of incidence of reflected beam on receiving surface
     992          440 :                         state.dataSolarReflectionManager->CosIncAngRec =
     993          440 :                             dot(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec, state.dataSolarReflectionManager->SunVecMir);
     994          440 :                         if (state.dataSolarReflectionManager->CosIncAngRec <= 0.0) {
     995          262 :                             continue;
     996              :                         }
     997          890 :                         for (int RecPtNum = 1; RecPtNum <= NumRecPts; ++RecPtNum) {
     998              :                             // See if ray from receiving point to mirrored sun hits the reflecting surface
     999          712 :                             state.dataSolarReflectionManager->RecPt = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(RecPtNum);
    1000          712 :                             hitRefl = PierceSurface(state,
    1001              :                                                     ReflSurfNum,
    1002          712 :                                                     state.dataSolarReflectionManager->RecPt,
    1003          712 :                                                     state.dataSolarReflectionManager->SunVecMir,
    1004          712 :                                                     state.dataSolarReflectionManager->HitPtRefl);
    1005          712 :                             if (hitRefl) { // Reflecting surface was hit
    1006              :                                 ReflDistanceSq =
    1007          122 :                                     distance_squared(state.dataSolarReflectionManager->HitPtRefl, state.dataSolarReflectionManager->RecPt);
    1008          122 :                                 ReflDistance = std::sqrt(ReflDistanceSq);
    1009              :                                 // Determine if ray from receiving point to hit point is obstructed
    1010          122 :                                 hitObsRefl = false;
    1011          383 :                                 for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
    1012          383 :                                      loop2 <= loop2_end;
    1013              :                                      ++loop2) {
    1014          266 :                                     int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
    1015          266 :                                     if (ObsSurfNum == ReflSurfNum || ObsSurfNum == state.dataSurface->Surface(ReflSurfNum).BaseSurf) {
    1016          122 :                                         continue;
    1017              :                                     }
    1018          144 :                                     hitObs = PierceSurface(state,
    1019              :                                                            ObsSurfNum,
    1020          144 :                                                            state.dataSolarReflectionManager->RecPt,
    1021          144 :                                                            state.dataSolarReflectionManager->SunVecMir,
    1022              :                                                            ReflDistance,
    1023          144 :                                                            state.dataSolarReflectionManager->HitPtObs); // ReflDistance cutoff added
    1024          144 :                                     if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
    1025           84 :                                         if (distance_squared(state.dataSolarReflectionManager->HitPtObs, state.dataSolarReflectionManager->RecPt) <
    1026              :                                             ReflDistanceSq) {
    1027            5 :                                             hitObsRefl = true;
    1028            5 :                                             break;
    1029              :                                         }
    1030              :                                     }
    1031              :                                 }
    1032          122 :                                 if (hitObsRefl) {
    1033            5 :                                     continue; // Obstruction closer than reflection pt. was hit; go to next rec. pt.
    1034              :                                 }
    1035              :                                 // There is no obstruction for this ray between rec. pt. and hit point on reflecting surface.
    1036              :                                 // See if ray from hit pt. on reflecting surface to original (unmirrored) sun position is obstructed
    1037          117 :                                 hitObs = false;
    1038          117 :                                 if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) { // Reflecting surface is a window
    1039              :                                     // Receiving surface number for this window
    1040            0 :                                     int const ReflSurfRecNum = state.dataSurface->SurfShadowRecSurfNum(
    1041            0 :                                         ReflSurfNum); // Receiving surface number corresponding to a reflecting surface number
    1042            0 :                                     if (ReflSurfRecNum > 0) {
    1043              :                                         // Loop over possible obstructions for this window
    1044            0 :                                         for (int loop2 = 1,
    1045            0 :                                                  loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
    1046            0 :                                              loop2 <= loop2_end;
    1047              :                                              ++loop2) {
    1048              :                                             int const ObsSurfNum =
    1049            0 :                                                 state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
    1050            0 :                                             hitObs = PierceSurface(state,
    1051              :                                                                    ObsSurfNum,
    1052            0 :                                                                    state.dataSolarReflectionManager->HitPtRefl,
    1053            0 :                                                                    state.dataSolarReflectionManager->SunVect,
    1054            0 :                                                                    state.dataSolarReflectionManager->HitPtObs);
    1055            0 :                                             if (hitObs) {
    1056            0 :                                                 break;
    1057              :                                             }
    1058              :                                         }
    1059              :                                     }
    1060              :                                 } else { // Reflecting surface is a building shade
    1061         1521 :                                     for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    1062         1404 :                                         if (ObsSurfNum == ReflSurfNum) {
    1063            0 :                                             continue;
    1064              :                                         }
    1065              : 
    1066              :                                         // TH2 CR8959 -- Skip mirrored surfaces
    1067         1404 :                                         if (state.dataSurface->Surface(ObsSurfNum).MirroredSurf) {
    1068            0 :                                             continue;
    1069              :                                         }
    1070              :                                         // TH2 CR8959 -- The other side of a mirrored surface cannot obstruct the mirrored surface
    1071         1404 :                                         if (state.dataSurface->Surface(ReflSurfNum).MirroredSurf) {
    1072         1404 :                                             if (ObsSurfNum == ReflSurfNum - 1) {
    1073          117 :                                                 continue;
    1074              :                                             }
    1075              :                                         }
    1076              : 
    1077         1287 :                                         hitObs = PierceSurface(state,
    1078              :                                                                ObsSurfNum,
    1079         1287 :                                                                state.dataSolarReflectionManager->HitPtRefl,
    1080         1287 :                                                                state.dataSolarReflectionManager->SunVect,
    1081         1287 :                                                                state.dataSolarReflectionManager->HitPtObs);
    1082         1287 :                                         if (hitObs) {
    1083            0 :                                             break;
    1084              :                                         }
    1085          117 :                                     }
    1086              :                                 }
    1087              : 
    1088          117 :                                 if (hitObs) {
    1089            0 :                                     continue; // Obstruction hit between reflection hit point and sun; go to next receiving pt.
    1090              :                                 }
    1091              : 
    1092              :                                 // No obstructions. Calculate reflected beam irradiance at receiving pt. from this reflecting surface.
    1093          117 :                                 state.dataSolarReflectionManager->SpecReflectance = 0.0;
    1094          117 :                                 if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
    1095            0 :                                     state.dataSolarReflectionManager->ConstrNumRefl = state.dataSurface->Surface(ReflSurfNum).Construction;
    1096            0 :                                     state.dataSolarReflectionManager->SpecReflectance = Window::POLYF(
    1097            0 :                                         std::abs(state.dataSolarReflectionManager->CosIncAngRefl),
    1098            0 :                                         state.dataConstruction->Construct(state.dataSolarReflectionManager->ConstrNumRefl).ReflSolBeamFrontCoef);
    1099              :                                 }
    1100          234 :                                 if (state.dataSurface->Surface(ReflSurfNum).IsShadowing &&
    1101          117 :                                     state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum) > 0) {
    1102          117 :                                     state.dataSolarReflectionManager->ConstrNumRefl = state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum);
    1103          117 :                                     state.dataSolarReflectionManager->SpecReflectance =
    1104          234 :                                         state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) *
    1105          117 :                                         Window::POLYF(
    1106          117 :                                             std::abs(state.dataSolarReflectionManager->CosIncAngRefl),
    1107          117 :                                             state.dataConstruction->Construct(state.dataSolarReflectionManager->ConstrNumRefl).ReflSolBeamFrontCoef);
    1108              :                                 }
    1109              :                                 // Angle of incidence of reflected beam on receiving surface
    1110          117 :                                 state.dataSolarReflectionManager->CosIncAngRec =
    1111          117 :                                     dot(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec,
    1112          117 :                                         state.dataSolarReflectionManager->SunVecMir);
    1113          234 :                                 state.dataSolarReflectionManager->ReflFac =
    1114          117 :                                     state.dataSolarReflectionManager->SpecReflectance * state.dataSolarReflectionManager->CosIncAngRec;
    1115              :                                 // Contribution to specular reflection factor
    1116          117 :                                 ReflBmToBmSolObs(RecPtNum) += state.dataSolarReflectionManager->ReflFac;
    1117          117 :                                 ReflFacTimesCosIncSum(RecPtNum) +=
    1118          117 :                                     state.dataSolarReflectionManager->ReflFac * state.dataSolarReflectionManager->CosIncAngRec;
    1119              :                             } // End of check if reflecting surface was hit
    1120              :                         } // End of loop over receiving points
    1121              :                     } // End of check if valid reflecting surface
    1122              :                 } // End of loop over obstructing surfaces
    1123              :                 // Average over receiving points
    1124              : 
    1125         8130 :                 for (int RecPtNum = 1; RecPtNum <= NumRecPts; ++RecPtNum) {
    1126         6504 :                     if (ReflBmToBmSolObs(RecPtNum) != 0.0) {
    1127          117 :                         state.dataSolarReflectionManager->CosIncWeighted = ReflFacTimesCosIncSum(RecPtNum) / ReflBmToBmSolObs(RecPtNum);
    1128              :                     } else {
    1129         6387 :                         state.dataSolarReflectionManager->CosIncWeighted = 0.0;
    1130              :                     }
    1131         6504 :                     state.dataSurface->SurfCosIncAveBmToBmSolObs(iHour, SurfNum) += state.dataSolarReflectionManager->CosIncWeighted;
    1132         6504 :                     state.dataSurface->SurfReflFacBmToBmSolObs(iHour, SurfNum) += ReflBmToBmSolObs(RecPtNum);
    1133              :                 }
    1134         1626 :                 state.dataSurface->SurfReflFacBmToBmSolObs(iHour, SurfNum) /= double(NumRecPts);
    1135         1626 :                 state.dataSurface->SurfCosIncAveBmToBmSolObs(iHour, SurfNum) /= double(NumRecPts);
    1136              :             } // End of check if number of possible obstructions > 0
    1137              :         } // End of loop over receiving surfaces
    1138         4002 :     }
    1139              : 
    1140              :     //=================================================================================================
    1141              : 
    1142            9 :     void CalcSkySolDiffuseReflFactors(EnergyPlusData &state)
    1143              :     {
    1144              : 
    1145              :         // SUBROUTINE INFORMATION:
    1146              :         //       AUTHOR         Fred Winkelmann
    1147              :         //       DATE WRITTEN   October 2003
    1148              :         //       MODIFIED       na
    1149              :         //       RE-ENGINEERED  na
    1150              : 
    1151              :         // PURPOSE OF THIS SUBROUTINE:
    1152              :         // Calculates factors for irradiance on exterior heat transfer surfaces due to
    1153              :         // reflection of sky diffuse solar radiation from obstructions and ground.
    1154              : 
    1155              :         // Using/Aliasing
    1156              : 
    1157              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1158            9 :         Array1D<Real64> ReflSkySolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for sky diffuse solar
    1159              :         // reflected from obstructions, divided by unobstructed
    1160              :         // sky diffuse horizontal irradiance
    1161            9 :         Array1D<Real64> ReflSkySolGnd(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for sky diffuse solar
    1162              :         // reflected from ground, divided by unobstructed
    1163              :         // sky diffuse horizontal irradiance
    1164              :         bool hitObs; // True iff obstruction is hit
    1165              : 
    1166            9 :         Real64 const DPhi(Constant::PiOvr2 / (AltAngStepsForSolReflCalc / 2.0));      // Altitude angle and increment (radians)
    1167            9 :         Real64 const DTheta(2.0 * Constant::Pi / (2.0 * AzimAngStepsForSolReflCalc)); // Azimuth increment (radians)
    1168              : 
    1169              :         // Pre-compute these constants
    1170              :         // Initialize the 0 index with dummy value so the iterators line up below
    1171            9 :         std::vector<Real64> sin_Phi({-1});
    1172            9 :         std::vector<Real64> cos_Phi({-1});
    1173           54 :         for (int IPhi = 1; IPhi <= (AltAngStepsForSolReflCalc / 2); ++IPhi) {
    1174           45 :             Real64 Phi((IPhi - 0.5) * DPhi); // Altitude angle and increment (radians)
    1175           45 :             sin_Phi.push_back(std::sin(Phi));
    1176           45 :             cos_Phi.push_back(std::cos(Phi));
    1177              :         }
    1178              : 
    1179            9 :         std::vector<Real64> sin_Theta({-1});
    1180            9 :         std::vector<Real64> cos_Theta({-1});
    1181          171 :         for (int ITheta = 1; ITheta <= 2 * AzimAngStepsForSolReflCalc; ++ITheta) {
    1182          162 :             Real64 Theta = (ITheta - 0.5) * DTheta; // Azimuth angle (radians)
    1183          162 :             sin_Theta.push_back(std::sin(Theta));
    1184          162 :             cos_Theta.push_back(std::cos(Theta));
    1185              :         }
    1186              : 
    1187            9 :         DisplayString(state, "Calculating Sky Diffuse Exterior Solar Reflection Factors");
    1188            9 :         ReflSkySolObs = 0.0;
    1189            9 :         ReflSkySolGnd = 0.0;
    1190              : 
    1191            9 :         for (state.dataSolarReflectionManager->iRecSurfNum = 1;
    1192           58 :              state.dataSolarReflectionManager->iRecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf;
    1193           49 :              ++state.dataSolarReflectionManager->iRecSurfNum) {
    1194           98 :             state.dataSolarReflectionManager->iSurfNum =
    1195           49 :                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).SurfNum;
    1196           49 :             for (state.dataSolarReflectionManager->iRecPtNum = 1;
    1197          245 :                  state.dataSolarReflectionManager->iRecPtNum <=
    1198          245 :                  state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).NumRecPts;
    1199          196 :                  ++state.dataSolarReflectionManager->iRecPtNum) {
    1200          196 :                 ReflSkySolObs(state.dataSolarReflectionManager->iRecPtNum) = 0.0;
    1201          196 :                 ReflSkySolGnd(state.dataSolarReflectionManager->iRecPtNum) = 0.0;
    1202          196 :                 for (state.dataSolarReflectionManager->iRayNum = 1;
    1203        17836 :                      state.dataSolarReflectionManager->iRayNum <=
    1204        17836 :                      state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).NumReflRays;
    1205        17640 :                      ++state.dataSolarReflectionManager->iRayNum) {
    1206        35280 :                     state.dataSolarReflectionManager->HitPntSurfNum =
    1207        17640 :                         state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1208        17640 :                             .HitPtSurfNum(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
    1209              :                     // Skip rays that do not hit an obstruction or ground.
    1210              :                     // (Note that if a downgoing ray does not hit an obstruction it will have HitPtSurfNum = 0
    1211              :                     // if the receiving point is below ground level (see subr. InitSolReflRecSurf); this means
    1212              :                     // that a below-ground-level receiving point receives no ground-reflected radiation although
    1213              :                     // it is allowed to receive obstruction-reflected solar radiation and direct (unreflected)
    1214              :                     // beam and sky solar radiation. As far as reflected solar is concerned, the program does
    1215              :                     // not handle a sloped ground plane or a horizontal ground plane whose level is different
    1216              :                     // from one side of the building to another.)
    1217        17640 :                     if (state.dataSolarReflectionManager->HitPntSurfNum == 0) {
    1218         9206 :                         continue; // Ray hits sky or obstruction with receiving pt. below ground level
    1219              :                     }
    1220         8434 :                     state.dataSolarReflectionManager->HitPntRefl =
    1221         8434 :                         state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1222         8434 :                             .HitPt(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
    1223         8434 :                     if (state.dataSolarReflectionManager->HitPntSurfNum > 0) {
    1224              :                         // Ray hits an obstruction
    1225              :                         // Skip hit points on daylighting shelves, from which solar reflection is separately calculated
    1226          830 :                         if (state.dataSurface->SurfDaylightingShelfInd(state.dataSolarReflectionManager->HitPntSurfNum) > 0) {
    1227            0 :                             continue;
    1228              :                         }
    1229              :                         // Reflected radiance at hit point divided by unobstructed sky diffuse horizontal irradiance
    1230          830 :                         state.dataSolarReflectionManager->HitPtSurfNumX = state.dataSolarReflectionManager->HitPntSurfNum;
    1231              :                         // Each shading surface has a "mirror" duplicate surface facing in the opposite direction.
    1232              :                         // The following gets the correct side of a shading surface in order to get the right value
    1233              :                         // of DifShdgRatioIsoSky (the two sides can have different sky shadowing).
    1234          830 :                         if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPntSurfNum).IsShadowing) {
    1235          484 :                             if (dot(state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1236          484 :                                         .RayVec(state.dataSolarReflectionManager->iRayNum),
    1237          968 :                                     state.dataSurface->Surface(state.dataSolarReflectionManager->HitPntSurfNum).OutNormVec) > 0.0) {
    1238            0 :                                 if (state.dataSolarReflectionManager->HitPntSurfNum + 1 < state.dataSurface->TotSurfaces) {
    1239            0 :                                     state.dataSolarReflectionManager->HitPtSurfNumX = state.dataSolarReflectionManager->HitPntSurfNum + 1;
    1240              :                                 }
    1241            0 :                                 if (state.dataSurface->SurfDaylightingShelfInd(state.dataSolarReflectionManager->HitPtSurfNumX) > 0) {
    1242            0 :                                     continue;
    1243              :                                 }
    1244              :                             }
    1245              :                         }
    1246              : 
    1247          830 :                         if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
    1248            0 :                             state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
    1249          830 :                             state.dataSolarReflectionManager->SkyReflSolRadiance =
    1250          830 :                                 state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNumX).ViewFactorSky *
    1251          830 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSky(state.dataSolarReflectionManager->HitPtSurfNumX) *
    1252          830 :                                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1253          830 :                                     .HitPtSolRefl(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
    1254              :                         } else {
    1255            0 :                             state.dataSolarReflectionManager->SkyReflSolRadiance =
    1256            0 :                                 state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNumX).ViewFactorSky *
    1257            0 :                                 state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, 1, state.dataSolarReflectionManager->HitPtSurfNumX) *
    1258            0 :                                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1259            0 :                                     .HitPtSolRefl(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
    1260              :                         }
    1261         1660 :                         state.dataSolarReflectionManager->dReflSkySol =
    1262          830 :                             state.dataSolarReflectionManager->SkyReflSolRadiance *
    1263          830 :                             state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1264          830 :                                 .dOmegaRay(state.dataSolarReflectionManager->iRayNum) *
    1265          830 :                             state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1266          830 :                                 .CosIncAngRay(state.dataSolarReflectionManager->iRayNum) /
    1267              :                             Constant::Pi;
    1268          830 :                         ReflSkySolObs(state.dataSolarReflectionManager->iRecPtNum) += state.dataSolarReflectionManager->dReflSkySol;
    1269              :                     } else {
    1270              :                         // Ray hits ground;
    1271              :                         // Find radiance at hit point due to reflection of sky diffuse reaching
    1272              :                         // ground directly, i.e., without reflecting from obstructions.
    1273              :                         // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
    1274              :                         // Divide hemisphere centered at ground hit point into elements of altitude Phi and
    1275              :                         // azimuth Theta and create upward-going ray unit vector at each Phi,Theta pair.
    1276              :                         // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
    1277         7604 :                         Real64 dReflSkyGnd = 0.0; // Factor for ground radiance due to direct sky diffuse reflection
    1278              :                         // Altitude loop
    1279        45624 :                         for (int IPhi = 1; IPhi <= (AltAngStepsForSolReflCalc / 2); ++IPhi) {
    1280              :                             // Third component of ray unit vector in (Theta,Phi) direction
    1281        38020 :                             state.dataSolarReflectionManager->URay(3) = sin_Phi[IPhi];
    1282        38020 :                             Real64 dOmega = cos_Phi[IPhi] * DTheta * DPhi; // Solid angle increment (steradians)
    1283              :                             // Cosine of angle of incidence of ray on ground
    1284        38020 :                             Real64 CosIncAngRayToSky = sin_Phi[IPhi]; // Cosine of incidence angle on ground of ray to sky
    1285              :                             // Azimuth loop
    1286       722380 :                             for (int ITheta = 1; ITheta <= 2 * AzimAngStepsForSolReflCalc; ++ITheta) {
    1287       684360 :                                 state.dataSolarReflectionManager->URay.x = cos_Phi[IPhi] * cos_Theta[ITheta];
    1288       684360 :                                 state.dataSolarReflectionManager->URay.y = cos_Phi[IPhi] * sin_Theta[ITheta];
    1289              :                                 // Does this ray hit an obstruction?
    1290       684360 :                                 hitObs = false;
    1291      3959120 :                                 for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
    1292      3453910 :                                     state.dataSolarReflectionManager->iObsSurfNum = ObsSurfNum;
    1293              :                                     // Horizontal roof surfaces cannot be obstructions for rays from ground
    1294      3453910 :                                     if (state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Tilt < 5.0) {
    1295       810975 :                                         continue;
    1296              :                                     }
    1297      2642935 :                                     if (!state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).IsShadowing) {
    1298      2454295 :                                         if (dot(state.dataSolarReflectionManager->URay,
    1299      4908590 :                                                 state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).OutNormVec) >= 0.0) {
    1300      1159882 :                                             continue;
    1301              :                                         }
    1302              :                                         // Special test for vertical surfaces with URay dot OutNormVec < 0; excludes
    1303              :                                         // case where ground hit point is in back of ObsSurfNum
    1304      2588826 :                                         if (state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Tilt > 89.0 &&
    1305      1294413 :                                             state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Tilt < 91.0) {
    1306      1217487 :                                             state.dataSolarReflectionManager->SurfVert =
    1307      1217487 :                                                 state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Vertex(2);
    1308      1217487 :                                             state.dataSolarReflectionManager->SurfVertToGndPt =
    1309      2434974 :                                                 state.dataSolarReflectionManager->HitPntRefl - state.dataSolarReflectionManager->SurfVert;
    1310      1217487 :                                             if (dot(state.dataSolarReflectionManager->SurfVertToGndPt,
    1311      2434974 :                                                     state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).OutNormVec) < 0.0) {
    1312       745174 :                                                 continue;
    1313              :                                             }
    1314              :                                         }
    1315              :                                     }
    1316       737879 :                                     hitObs = PierceSurface(state,
    1317       737879 :                                                            state.dataSolarReflectionManager->iObsSurfNum,
    1318       737879 :                                                            state.dataSolarReflectionManager->HitPntRefl,
    1319       737879 :                                                            state.dataSolarReflectionManager->URay,
    1320       737879 :                                                            state.dataSolarReflectionManager->HitPntObs);
    1321       737879 :                                     if (hitObs) {
    1322       179150 :                                         break;
    1323              :                                     }
    1324       684360 :                                 }
    1325       684360 :                                 if (hitObs) {
    1326       179150 :                                     continue; // Obstruction hit
    1327              :                                 }
    1328              :                                 // Sky is hit
    1329       505210 :                                 dReflSkyGnd += CosIncAngRayToSky * dOmega / Constant::Pi;
    1330              :                             } // End of azimuth loop
    1331              :                         } // End of altitude loop
    1332         7604 :                         ReflSkySolGnd(state.dataSolarReflectionManager->iRecPtNum) +=
    1333         7604 :                             dReflSkyGnd *
    1334         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1335         7604 :                                 .dOmegaRay(state.dataSolarReflectionManager->iRayNum) *
    1336         7604 :                             state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
    1337         7604 :                                 .CosIncAngRay(state.dataSolarReflectionManager->iRayNum) /
    1338              :                             Constant::Pi;
    1339              :                     } // End of check if ray from receiving point hits obstruction or ground
    1340              :                 } // End of loop over rays from receiving point
    1341              :             } // End of loop over receiving points
    1342              : 
    1343              :             // Average over receiving points
    1344           49 :             state.dataSurface->SurfReflFacSkySolObs(state.dataSolarReflectionManager->iSurfNum) = 0.0;
    1345           49 :             state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) = 0.0;
    1346           98 :             state.dataSolarReflectionManager->iNumRecPts =
    1347           49 :                 state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).NumRecPts;
    1348           49 :             for (state.dataSolarReflectionManager->iRecPtNum = 1;
    1349          245 :                  state.dataSolarReflectionManager->iRecPtNum <= state.dataSolarReflectionManager->iNumRecPts;
    1350          196 :                  ++state.dataSolarReflectionManager->iRecPtNum) {
    1351          196 :                 state.dataSurface->SurfReflFacSkySolObs(state.dataSolarReflectionManager->iSurfNum) +=
    1352          196 :                     ReflSkySolObs(state.dataSolarReflectionManager->iRecPtNum);
    1353          196 :                 state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) +=
    1354          196 :                     ReflSkySolGnd(state.dataSolarReflectionManager->iRecPtNum);
    1355              :             }
    1356           49 :             state.dataSurface->SurfReflFacSkySolObs(state.dataSolarReflectionManager->iSurfNum) /= state.dataSolarReflectionManager->iNumRecPts;
    1357           49 :             state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) /= state.dataSolarReflectionManager->iNumRecPts;
    1358              :             // Do not allow SurfReflFacBmToDiffSolGnd to exceed the surface's unobstructed ground view factor
    1359           49 :             state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) =
    1360           49 :                 min(0.5 * (1.0 - state.dataSurface->Surface(state.dataSolarReflectionManager->iSurfNum).CosTilt),
    1361           49 :                     state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum));
    1362              :             // Note: the above factors are dimensionless; they are equal to
    1363              :             // (W/m2 reflected solar incident on SurfNum)/(W/m2 unobstructed horizontal sky diffuse irradiance)
    1364              :         } // End of loop over receiving surfaces
    1365            9 :     }
    1366              : 
    1367              : } // namespace SolarReflectionManager
    1368              : 
    1369              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1