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
|