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