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 0 : 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 0 : Vector3<Real64> RecVec; // First vertex of a receiving surface (m)
118 0 : Vector3<Real64> ObsVec; // A vertex of a candidate obstructing surface (m)
119 0 : Vector3<Real64> VecAB; // Vector from receiving surface vertex to obstruction surface vertex (m)
120 0 : 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 0 : 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 0 : 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 0 : Vector3<Real64> RecPt; // Receiving point (m)
153 0 : Vector3<Real64> RayVec; // Unit vector along ray
154 0 : Vector3<Real64> Vec1; // Vectors between hit surface vertices (m)
155 0 : Vector3<Real64> Vec2; // Vectors between hit surface vertices (m)
156 0 : 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 0 : 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 0 : static Vector3<Real64> const unit_z(0.0, 0.0, 1.0);
171 0 : 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 0 : state.dataSolarReflectionManager->TotSolReflRecSurf = 0;
180 0 : for (SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
181 0 : if (state.dataSurface->Surface(SurfNum).ExtSolar) {
182 0 : ++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 0 : 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 0 : if (state.dataEnvrn->IgnoreSolarRadiation) {
218 0 : state.dataSolarReflectionManager->TotSolReflRecSurf = 0;
219 0 : state.dataSurface->CalcSolRefl = false;
220 0 : return;
221 : }
222 :
223 0 : state.dataSolarReflectionManager->SolReflRecSurf.allocate(state.dataSolarReflectionManager->TotSolReflRecSurf);
224 :
225 0 : state.dataSurface->SurfReflFacBmToDiffSolObs.dimension(24, state.dataSurface->TotSurfaces, 0.0);
226 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd.dimension(24, state.dataSurface->TotSurfaces, 0.0);
227 0 : state.dataSurface->SurfReflFacBmToBmSolObs.dimension(24, state.dataSurface->TotSurfaces, 0.0);
228 0 : state.dataSurface->SurfReflFacSkySolObs.dimension(state.dataSurface->TotSurfaces, 0.0);
229 0 : state.dataSurface->SurfReflFacSkySolGnd.dimension(state.dataSurface->TotSurfaces, 0.0);
230 0 : 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 0 : RecSurfNum = 0;
235 0 : for (SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
236 0 : state.dataSurface->SurfShadowRecSurfNum(SurfNum) = 0;
237 0 : if (state.dataSurface->Surface(SurfNum).ExtSolar) {
238 0 : ++RecSurfNum;
239 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum = SurfNum;
240 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfName = state.dataSurface->Surface(SurfNum).Name;
241 0 : 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 0 : for (loop = 1; loop <= state.dataSurface->Surface(SurfNum).Sides; ++loop) {
245 0 : 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 0 : state.dataSurface->MaxRecPts = 1;
258 0 : for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
259 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts =
260 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum).Sides;
261 0 : if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts > state.dataSurface->MaxRecPts)
262 0 : state.dataSurface->MaxRecPts = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts;
263 : }
264 :
265 0 : state.dataSurface->MaxReflRays = AltAngStepsForSolReflCalc * AzimAngStepsForSolReflCalc;
266 0 : for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
267 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec = 0.0;
268 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt.dimension(state.dataSurface->MaxRecPts, zero3);
269 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RayVec.dimension(state.dataSurface->MaxReflRays, zero3);
270 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).CosIncAngRay.dimension(state.dataSurface->MaxReflRays, 0.0);
271 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).dOmegaRay.dimension(state.dataSurface->MaxReflRays, 0.0);
272 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
273 0 : .HitPt.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, zero3);
274 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
275 0 : .HitPtSurfNum.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, 0.0);
276 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
277 0 : .HitPtSolRefl.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, 0.0);
278 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
279 0 : .RecPtHitPtDis.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, 0.0);
280 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
281 0 : .HitPtNormVec.dimension(state.dataSurface->MaxReflRays, state.dataSurface->MaxRecPts, zero3);
282 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums.dimension(state.dataSurface->TotSurfaces, 0);
283 : }
284 :
285 0 : for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
286 0 : SurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum;
287 : // Outward norm to receiving surface
288 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec = state.dataSurface->Surface(SurfNum).OutNormVec;
289 0 : RecVec = state.dataSurface->Surface(SurfNum).Vertex(1);
290 : // Loop over all surfaces and find those that can be obstructing surfaces for this receiving surf
291 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs = 0;
292 0 : for (ObsSurfNum = 1; ObsSurfNum <= state.dataSurface->TotSurfaces; ++ObsSurfNum) {
293 : // Exclude the receiving surface itself and its base surface (if it has one)
294 0 : if (ObsSurfNum == SurfNum || ObsSurfNum == state.dataSurface->Surface(SurfNum).BaseSurf) continue;
295 : // Exclude non-exterior heat transfer surfaces
296 0 : if (state.dataSurface->Surface(ObsSurfNum).HeatTransSurf && state.dataSurface->Surface(ObsSurfNum).ExtBoundCond != 0) continue;
297 : // Exclude duplicate shading surfaces
298 : // IF(Surface(ObsSurfNum)%Name(1:3) == 'Mir') CYCLE
299 : // TH2 CR8959
300 : // IF(Surface(ObsSurfNum)%MirroredSurf) CYCLE
301 :
302 : // Exclude surfaces that are entirely behind the receiving surface.This is true if dot products of the
303 : // rec. surface outward normal and vector from first vertex of rec. surface and each vertex of
304 : // obstructing surface are all negative.
305 0 : ObsBehindRec = true;
306 0 : for (loop = 1; loop <= state.dataSurface->Surface(ObsSurfNum).Sides; ++loop) {
307 0 : ObsVec = state.dataSurface->Surface(ObsSurfNum).Vertex(loop);
308 0 : DotProd = dot(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec, ObsVec - RecVec);
309 : // CR8251 IF(DotProd > 0.01d0) THEN ! This obstructing-surface vertex is not behind receiving surface
310 0 : if (DotProd > Constant::OneMillionth) { // This obstructing-surface vertex is not behind receiving surface
311 0 : ObsBehindRec = false;
312 0 : break;
313 : }
314 : }
315 0 : if (ObsBehindRec) continue;
316 :
317 : // Exclude heat transfer surfaces that have no view with the receiving surface.
318 : // There is view if: for at least one vector VecAB from a receiving surface vertex to
319 : // a vertex of a potential obstructing surface that satisfies VecAB.nA > 0.0 and VecAB.nB < 0.0,
320 : // where nA and nB are the outward normal to the receiving and obstructing surface, resp.
321 0 : if (state.dataSurface->Surface(ObsSurfNum).HeatTransSurf) {
322 0 : ObsHasView = false;
323 0 : for (loopA = 1; loopA <= state.dataSurface->Surface(SurfNum).Sides; ++loopA) {
324 0 : for (loopB = 1; loopB <= state.dataSurface->Surface(ObsSurfNum).Sides; ++loopB) {
325 0 : VecAB = (state.dataSurface->Surface(ObsSurfNum).Vertex(loopB) - state.dataSurface->Surface(SurfNum).Vertex(loopA));
326 0 : if (dot(VecAB, state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec) > 0.0 &&
327 0 : dot(VecAB, state.dataSurface->Surface(ObsSurfNum).OutNormVec) < 0.0) {
328 0 : ObsHasView = true;
329 0 : break;
330 : }
331 : }
332 0 : if (ObsHasView) break;
333 : }
334 0 : if (!ObsHasView) continue;
335 : }
336 :
337 : // This is a possible obstructing surface for this receiving surface
338 0 : ++state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
339 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum)
340 0 : .PossibleObsSurfNums(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs) = ObsSurfNum;
341 : }
342 :
343 : // Get coordinates of receiving points on this receiving surface. The number of receiving points
344 : // is equal to the number of surface vertices (3 or higher).
345 :
346 0 : NumRecPts = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts;
347 0 : for (J = 1; J <= NumRecPts; ++J) {
348 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J) = 0.0;
349 0 : for (K = 1; K <= NumRecPts; ++K) {
350 0 : if (NumRecPts == 3) { // Receiving surface is a triangle
351 0 : VertexWt = 0.2;
352 0 : if (K == J) VertexWt = 0.6;
353 : } else { // Receiving surface has 4 or more vertices
354 0 : VertexWt = 1.0 / (2.0 * NumRecPts);
355 0 : if (K == J) VertexWt = (NumRecPts + 1.0) / (2.0 * NumRecPts);
356 : }
357 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J).x +=
358 0 : VertexWt * state.dataSurface->Surface(SurfNum).Vertex(K).x;
359 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J).y +=
360 0 : VertexWt * state.dataSurface->Surface(SurfNum).Vertex(K).y;
361 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(J).z +=
362 0 : VertexWt * state.dataSurface->Surface(SurfNum).Vertex(K).z;
363 : }
364 : }
365 :
366 : // Create rays going outward from receiving surface. The same rays will be used at each receiving point.
367 : // The rays are used in calculating diffusely reflected solar incident on receiving surface.
368 :
369 : // Divide hemisphere around receiving surface into elements of altitude Phi and
370 : // azimuth Theta and create ray unit vector at each Phi,Theta pair in front of the surface.
371 : // Phi = 0 at the horizon; Phi = Pi/2 at the zenith
372 :
373 0 : PhiSurf = std::asin(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.z);
374 0 : Real64 const tan_PhiSurf = std::tan(PhiSurf);
375 0 : Real64 const sin_PhiSurf = std::sin(PhiSurf);
376 0 : Real64 const cos_PhiSurf = std::cos(PhiSurf);
377 :
378 0 : if (std::abs(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.x) > 1.0e-5 ||
379 0 : std::abs(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.y) > 1.0e-5) {
380 0 : ThetaSurf = std::atan2(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.y,
381 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec.x);
382 : } else {
383 0 : ThetaSurf = 0.0;
384 : }
385 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PhiNormVec = PhiSurf;
386 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).ThetaNormVec = ThetaSurf;
387 0 : PhiMin = max(-Constant::PiOvr2, PhiSurf - Constant::PiOvr2);
388 0 : PhiMax = min(Constant::PiOvr2, PhiSurf + Constant::PiOvr2);
389 0 : DPhi = (PhiMax - PhiMin) / AltAngStepsForSolReflCalc;
390 0 : RayNum = 0;
391 :
392 : // Altitude loop
393 0 : for (IPhi = 1; IPhi <= AltAngStepsForSolReflCalc; ++IPhi) {
394 0 : Phi = PhiMin + (IPhi - 0.5) * DPhi;
395 0 : SPhi = std::sin(Phi);
396 0 : CPhi = std::cos(Phi);
397 : // Third component of ray unit vector in (Theta,Phi) direction
398 0 : URay(3) = SPhi;
399 :
400 0 : if (PhiSurf >= 0.0) {
401 0 : if (Phi >= Constant::PiOvr2 - PhiSurf) {
402 0 : ThetaMin = -Constant::Pi;
403 0 : ThetaMax = Constant::Pi;
404 : } else {
405 0 : ACosTanTan = std::acos(-std::tan(Phi) * tan_PhiSurf);
406 0 : ThetaMin = ThetaSurf - std::abs(ACosTanTan);
407 0 : ThetaMax = ThetaSurf + std::abs(ACosTanTan);
408 : }
409 :
410 : } else { // PhiSurf < 0.0
411 0 : if (Phi <= -PhiSurf - Constant::PiOvr2) {
412 0 : ThetaMin = -Constant::Pi;
413 0 : ThetaMax = Constant::Pi;
414 : } else {
415 0 : ACosTanTan = std::acos(-std::tan(Phi) * tan_PhiSurf);
416 0 : ThetaMin = ThetaSurf - std::abs(ACosTanTan);
417 0 : ThetaMax = ThetaSurf + std::abs(ACosTanTan);
418 : }
419 : }
420 :
421 0 : DTheta = (ThetaMax - ThetaMin) / AzimAngStepsForSolReflCalc;
422 0 : dOmega = CPhi * DTheta * DPhi;
423 :
424 : // Azimuth loop
425 0 : for (ITheta = 1; ITheta <= AzimAngStepsForSolReflCalc; ++ITheta) {
426 0 : Theta = ThetaMin + (ITheta - 0.5) * DTheta;
427 0 : URay.x = CPhi * std::cos(Theta);
428 0 : URay.y = CPhi * std::sin(Theta);
429 : // Cosine of angle of incidence of ray on receiving surface
430 0 : CosIncAngRay = SPhi * sin_PhiSurf + CPhi * cos_PhiSurf * std::cos(Theta - ThetaSurf);
431 0 : if (CosIncAngRay < 0.0) continue; // Ray is behind receiving surface (although there shouldn't be any)
432 0 : ++RayNum;
433 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RayVec(RayNum) = URay;
434 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).CosIncAngRay(RayNum) = CosIncAngRay;
435 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).dOmegaRay(RayNum) = dOmega;
436 : } // End of azimuth loop
437 :
438 : } // End of altitude loop
439 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumReflRays = RayNum;
440 :
441 : } // End of loop over receiving surfaces
442 :
443 : // Loop again over receiving surfaces and, for each ray, get hit point and info associated with that point
444 : // (hit point = point that ray intersects nearest obstruction, or, if ray is downgoing and hits no
445 : // obstructions, point that ray intersects ground plane).
446 :
447 0 : for (RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
448 0 : SurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum;
449 0 : for (RecPtNum = 1; RecPtNum <= state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts; ++RecPtNum) {
450 0 : RecPt = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(RecPtNum);
451 0 : for (RayNum = 1; RayNum <= state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumReflRays; ++RayNum) {
452 : // Loop over possible obstructions. If ray hits one or more obstructions get hit point on closest obstruction.
453 : // If ray hits no obstructions and is going upward set HitPointSurfNum = 0.
454 : // If ray hits no obstructions and is going downward set HitPointSurfNum = -1 and get hit point on ground.
455 0 : TotObstructionsHit = 0;
456 0 : NearestHitSurfNum = 0;
457 0 : NearestHitDistance = 1.0e+8;
458 0 : ObsSurfNumToSkip = 0;
459 0 : RayVec = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RayVec(RayNum);
460 0 : for (loop1 = 1; loop1 <= state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; ++loop1) {
461 : // Surface number of this obstruction
462 0 : ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop1);
463 : // If a window was hit previously (see below), ObsSurfNumToSkip was set to the window's base surface in order
464 : // to remove that surface from consideration as a hit surface for this ray
465 0 : if (ObsSurfNum == ObsSurfNumToSkip) continue;
466 : // Determine if this ray hits ObsSurfNum (in which case hit is true) and, if so, what the
467 : // distance from the receiving point to the hit point is
468 0 : hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
469 0 : if (hit) {
470 : // added TH 3/29/2010 to set ObsSurfNumToSkip
471 0 : if (state.dataSurface->Surface(ObsSurfNum).Class == SurfaceClass::Window) {
472 0 : ObsSurfNumToSkip = state.dataSurface->Surface(ObsSurfNum).BaseSurf;
473 : }
474 :
475 : // If obstruction is a window and its base surface is the nearest obstruction hit so far,
476 : // set NearestHitSurfNum to this window. Note that in this case NearestHitDistance has already
477 : // been calculated, so does not have to be recalculated.
478 0 : if (state.dataSurface->Surface(ObsSurfNum).Class == SurfaceClass::Window &&
479 0 : state.dataSurface->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum) {
480 0 : NearestHitSurfNum = ObsSurfNum;
481 : } else {
482 0 : ++TotObstructionsHit;
483 : // Distance from receiving point to hit point
484 0 : HitDistance = distance(HitPt, RecPt);
485 : // Reset NearestHitSurfNum and NearestHitDistance if this hit point is closer than previous closest
486 0 : if (HitDistance < NearestHitDistance) {
487 0 : NearestHitDistance = HitDistance;
488 0 : NearestHitSurfNum = ObsSurfNum;
489 0 : NearestHitPt = HitPt;
490 0 : } else if (HitDistance == NearestHitDistance) { // TH2 CR8959
491 : // Ray hits mirrored surfaces. Choose the surface facing the ray.
492 0 : if (dot(state.dataSurface->Surface(ObsSurfNum).OutNormVec, RayVec) <= 0.0) {
493 0 : NearestHitSurfNum = ObsSurfNum;
494 : }
495 : }
496 : }
497 : } // End of check if obstruction was hit
498 : } // End of loop over possible obstructions for this ray
499 :
500 0 : if (TotObstructionsHit > 0) {
501 : // One or more obstructions were hit by this ray
502 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSurfNum(RayNum, RecPtNum) = NearestHitSurfNum;
503 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPtHitPtDis(RayNum, RecPtNum) = NearestHitDistance;
504 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPt(RayNum, RecPtNum) = NearestHitPt;
505 : // For hit surface, calculate unit normal vector pointing into the hemisphere
506 : // containing the receiving point
507 0 : Vec1 = (state.dataSurface->Surface(NearestHitSurfNum).Vertex(1) - state.dataSurface->Surface(NearestHitSurfNum).Vertex(3));
508 0 : Vec2 = (state.dataSurface->Surface(NearestHitSurfNum).Vertex(2) - state.dataSurface->Surface(NearestHitSurfNum).Vertex(3));
509 0 : VNorm = cross(Vec1, Vec2);
510 0 : VNorm.normalize(); // Do Handle magnitude==0
511 0 : if (dot(VNorm, -RayVec) < 0.0) VNorm = -VNorm;
512 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtNormVec(RayNum, RecPtNum) = VNorm;
513 : // Get solar and visible beam-to-diffuse reflectance at nearest hit point
514 0 : ObsConstrNum = state.dataSurface->Surface(NearestHitSurfNum).Construction;
515 0 : if (ObsConstrNum > 0) {
516 : // Exterior building surface is nearest hit
517 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
518 : // Obstruction is not a window, i.e., is an opaque surface
519 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) =
520 0 : 1.0 - state.dataConstruction->Construct(ObsConstrNum).OutsideAbsorpSolar;
521 : } else {
522 : // Obstruction is a window. Assume it is bare so that there is no beam-to-diffuse reflection
523 : // (beam-to-beam reflection is calculated in subroutine CalcBeamSolSpecularReflFactors).
524 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) = 0.0;
525 : }
526 : } else {
527 : // Shading surface is nearest hit
528 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) =
529 0 : state.dataSurface->SurfShadowDiffuseSolRefl(NearestHitSurfNum);
530 : }
531 : } else {
532 : // No obstructions were hit by this ray
533 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSurfNum(RayNum, RecPtNum) = 0;
534 : // If ray is going downward find the hit point on the ground plane if the receiving point
535 : // is above ground level; note that GroundLevelZ is <= 0.0
536 0 : if (RayVec(3) < 0.0 &&
537 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(RecPtNum).z > state.dataSurface->GroundLevelZ) {
538 : // Ray hits ground
539 0 : Alfa = std::acos(-RayVec.z);
540 0 : Beta = std::atan2(RayVec.y, RayVec.x);
541 0 : HorDis = (RecPt.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
542 0 : GroundHitPt.z = state.dataSurface->GroundLevelZ;
543 0 : GroundHitPt.x = RecPt.x + HorDis * std::cos(Beta);
544 0 : GroundHitPt.y = RecPt.y + HorDis * std::sin(Beta);
545 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPt(RayNum, RecPtNum) = GroundHitPt;
546 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSurfNum(RayNum, RecPtNum) = -1;
547 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPtHitPtDis(RayNum, RecPtNum) =
548 0 : (RecPt(3) - state.dataSurface->GroundLevelZ) / (-RayVec(3));
549 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtSolRefl(RayNum, RecPtNum) =
550 0 : state.dataEnvrn->GndReflectance;
551 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).HitPtNormVec(RayNum, RecPtNum) = unit_z;
552 : } // End of check if ray hits ground
553 : } // End of check if obstruction hit
554 : } // End of RayNum loop
555 : } // End of receiving point loop
556 : } // End of receiving surface loop
557 0 : }
558 :
559 : //=====================================================================================================
560 :
561 0 : void CalcBeamSolDiffuseReflFactors(EnergyPlusData &state)
562 : {
563 :
564 : // SUBROUTINE INFORMATION:
565 : // AUTHOR Fred Winkelmann
566 : // DATE WRITTEN September 2003
567 : // MODIFIED TH 4/6/2010, fixed CR 7872
568 : // RE-ENGINEERED B. Griffith, October 2012, for timestep integrated solar.
569 :
570 : // PURPOSE OF THIS SUBROUTINE:
571 : // manage calculations for factors for irradiance on exterior heat transfer surfaces due to
572 : // beam-to-diffuse solar reflection from obstructions and ground.
573 :
574 : // METHODOLOGY EMPLOYED: call worker routine depending on solar calculation method
575 :
576 0 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
577 0 : if (state.dataGlobal->BeginSimFlag) {
578 0 : DisplayString(state, "Calculating Beam-to-Diffuse Exterior Solar Reflection Factors");
579 : } else {
580 0 : DisplayString(state, "Updating Beam-to-Diffuse Exterior Solar Reflection Factors");
581 : }
582 0 : state.dataSurface->SurfReflFacBmToDiffSolObs = 0.0;
583 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd = 0.0;
584 0 : for (state.dataSolarReflectionManager->IHr = 1; state.dataSolarReflectionManager->IHr <= 24; ++state.dataSolarReflectionManager->IHr) {
585 0 : FigureBeamSolDiffuseReflFactors(state, state.dataSolarReflectionManager->IHr);
586 : } // End of IHr loop
587 : } else { // timestep integrated solar, use current hour of day
588 0 : state.dataSurface->SurfReflFacBmToDiffSolObs(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
589 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
590 0 : FigureBeamSolDiffuseReflFactors(state, state.dataGlobal->HourOfDay);
591 : }
592 0 : }
593 :
594 0 : void FigureBeamSolDiffuseReflFactors(EnergyPlusData &state, int const iHour)
595 : {
596 :
597 : // SUBROUTINE INFORMATION:
598 : // AUTHOR Fred Winkelmann, derived from original CalcBeamSolDiffuseReflFactors
599 : // DATE WRITTEN September 2003
600 : // MODIFIED na
601 : // RE-ENGINEERED B. Griffith, October 2012, revised for timestep integrated solar
602 :
603 : // PURPOSE OF THIS SUBROUTINE:
604 : // Calculates factors for irradiance on exterior heat transfer surfaces due to
605 : // beam-to-diffuse solar reflection from obstructions and ground.
606 :
607 0 : Array1D<Real64> ReflBmToDiffSolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
608 : // beam solar diffusely reflected from obstructions, divided by
609 : // beam normal irradiance
610 0 : Array1D<Real64> ReflBmToDiffSolGnd(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
611 : // beam solar diffusely reflected from the ground, divided by
612 : // beam normal irradiance
613 : bool hit; // True iff obstruction is hit
614 0 : ReflBmToDiffSolObs = 0.0;
615 0 : ReflBmToDiffSolGnd = 0.0;
616 :
617 : // Unit vector to sun
618 0 : state.dataSolarReflectionManager->SunVec = state.dataSurface->SurfSunCosHourly(iHour);
619 :
620 : // loop through each surface that can receive beam solar reflected as diffuse solar from other surfaces
621 0 : for (state.dataSolarReflectionManager->RecSurfNum = 1;
622 0 : state.dataSolarReflectionManager->RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf;
623 0 : ++state.dataSolarReflectionManager->RecSurfNum) {
624 0 : state.dataSolarReflectionManager->SurfNum =
625 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).SurfNum;
626 :
627 0 : for (state.dataSolarReflectionManager->RecPtNum = 1;
628 0 : state.dataSolarReflectionManager->RecPtNum <=
629 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).NumRecPts;
630 0 : ++state.dataSolarReflectionManager->RecPtNum) {
631 0 : ReflBmToDiffSolObs(state.dataSolarReflectionManager->RecPtNum) = 0.0;
632 0 : ReflBmToDiffSolGnd(state.dataSolarReflectionManager->RecPtNum) = 0.0;
633 :
634 0 : for (state.dataSolarReflectionManager->RayNum = 1;
635 0 : state.dataSolarReflectionManager->RayNum <=
636 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).NumReflRays;
637 0 : ++state.dataSolarReflectionManager->RayNum) {
638 0 : state.dataSolarReflectionManager->HitPtSurfNum =
639 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
640 0 : .HitPtSurfNum(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum);
641 :
642 : // Skip rays that do not hit an obstruction or ground.
643 : // (Note that if a downgoing ray does not hit an obstruction it will have HitPtSurfNum = 0
644 : // if the receiving point is below ground level (see subr. InitSolReflRecSurf); this means
645 : // that a below-ground-level receiving point receives no ground-reflected radiation although
646 : // it is allowed to receive obstruction-reflected solar radiation and direct (unreflected)
647 : // beam and sky solar radiation. As far as reflected solar is concerned, the program does
648 : // not handle a sloped ground plane or a horizontal ground plane whose level is different
649 : // from one side of the building to another.)
650 0 : if (state.dataSolarReflectionManager->HitPtSurfNum == 0)
651 0 : continue; // Ray hits sky or obstruction with receiving pt. below ground level
652 :
653 0 : if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
654 : // Skip rays that hit a daylighting shelf, from which solar reflection is calculated separately.
655 0 : if (state.dataSurface->SurfDaylightingShelfInd(state.dataSolarReflectionManager->HitPtSurfNum) > 0) continue;
656 :
657 : // Skip rays that hit a window
658 : // If hit point's surface is a window or glass door go to next ray since it is assumed for now
659 : // that windows have only beam-to-beam, not beam-to-diffuse, reflection
660 : // TH 3/29/2010. Code modified and moved
661 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).Class == SurfaceClass::Window ||
662 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).Class == SurfaceClass::GlassDoor)
663 0 : continue;
664 :
665 : // Skip rays that hit non-sunlit surface. Assume first time step of the hour.
666 0 : state.dataSolarReflectionManager->SunLitFract =
667 0 : state.dataHeatBal->SurfSunlitFrac(iHour, 1, state.dataSolarReflectionManager->HitPtSurfNum);
668 :
669 : // If hit point's surface is not sunlit go to next ray
670 : // TH 3/25/2010. why limit to HeatTransSurf? shading surfaces should also apply
671 : // IF(Surface(HitPtSurfNum)%HeatTransSurf .AND. SunLitFract < 0.01d0) CYCLE
672 0 : if (state.dataSolarReflectionManager->SunLitFract < 0.01) continue;
673 :
674 : // TH 3/26/2010. If the hit point falls into the shadow even though SunLitFract > 0, can Cycle.
675 : // This cannot be done now, therefore there are follow-up checks of blocking sun ray
676 : // from the hit point.
677 :
678 : // TH 3/29/2010. Code modified and moved up
679 : // If hit point's surface is a window go to next ray since it is assumed for now
680 : // that windows have only beam-to-beam, not beam-to-diffuse, reflection
681 : // IF(Surface(HitPtSurfNum)%Construction > 0) THEN
682 : // IF(Construct(Surface(HitPtSurfNum)%Construction)%TypeIsWindow) CYCLE
683 : // END IF
684 : }
685 :
686 : // Does an obstruction block the vector from this ray's hit point to the sun?
687 0 : state.dataSolarReflectionManager->OriginThisRay =
688 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
689 0 : .HitPt(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum);
690 :
691 : // Note: if sun is in back of hit surface relative to receiving point, CosIncBmAtHitPt will be < 0
692 0 : state.dataSolarReflectionManager->CosIncBmAtHitPt =
693 0 : dot(state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
694 0 : .HitPtNormVec(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum),
695 0 : state.dataSolarReflectionManager->SunVec);
696 0 : if (state.dataSolarReflectionManager->CosIncBmAtHitPt <= 0.0) continue;
697 :
698 : // CR 7872 - TH 4/6/2010. The shading surfaces should point to the receiveing heat transfer surface
699 : // according to the the right hand rule. If user inputs do not follow the rule, use the following
700 : // code to check the mirrored shading surface
701 0 : if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
702 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).IsShadowing) {
703 0 : if (state.dataSolarReflectionManager->HitPtSurfNum + 1 < state.dataSurface->TotSurfaces) {
704 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum + 1).IsShadowing &&
705 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum + 1).MirroredSurf) {
706 : // Check whether the sun is behind the mirrored shading surface
707 0 : state.dataSolarReflectionManager->CosIncBmAtHitPt2 =
708 0 : dot(state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum + 1).OutNormVec,
709 0 : state.dataSolarReflectionManager->SunVec);
710 0 : if (state.dataSolarReflectionManager->CosIncBmAtHitPt2 >= 0.0) continue;
711 : }
712 : }
713 : }
714 : }
715 :
716 : // TH 3/25/2010. CR 7872. Seems should loop over all possible obstructions for the HitPtSurfNum
717 : // rather than RecSurfNum, because if the HitPtSurfNum is a shading surface,
718 : // it does not belong to SolReflRecSurf which only contain heat transfer surfaces
719 : // that can receive reflected solar (ExtSolar = True)!
720 :
721 : // To speed up, ideally should store all possible shading surfaces for the HitPtSurfNum
722 : // obstruction surface in the SolReflSurf(HitPtSurfNum)%PossibleObsSurfNums(loop) array as well
723 0 : hit = false;
724 0 : for (state.dataSolarReflectionManager->ObsSurfNum = 1;
725 0 : state.dataSolarReflectionManager->ObsSurfNum <= state.dataSurface->TotSurfaces;
726 0 : ++state.dataSolarReflectionManager->ObsSurfNum) {
727 : // DO loop = 1,SolReflRecSurf(RecSurfNum)%NumPossibleObs
728 : // ObsSurfNum = SolReflRecSurf(RecSurfNum)%PossibleObsSurfNums(loop)
729 :
730 : // CR 8959 -- The other side of a mirrored surface cannot obstruct the mirrored surface
731 0 : if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
732 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNum).MirroredSurf) {
733 0 : if (state.dataSolarReflectionManager->ObsSurfNum == state.dataSolarReflectionManager->HitPtSurfNum - 1) continue;
734 : }
735 : }
736 :
737 : // skip the hit surface
738 0 : if (state.dataSolarReflectionManager->ObsSurfNum == state.dataSolarReflectionManager->HitPtSurfNum) continue;
739 :
740 : // skip mirrored surfaces
741 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->ObsSurfNum).MirroredSurf) continue;
742 : // IF(Surface(ObsSurfNum)%ShadowingSurf .AND. Surface(ObsSurfNum)%Name(1:3) == 'Mir') THEN
743 : // CYCLE
744 : // ENDIF
745 :
746 : // skip interior surfaces
747 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->ObsSurfNum).ExtBoundCond >= 1) continue;
748 :
749 : // For now it is assumed that obstructions that are shading surfaces are opaque.
750 : // An improvement here would be to allow these to have transmittance.
751 0 : hit = PierceSurface(state,
752 0 : state.dataSolarReflectionManager->ObsSurfNum,
753 0 : state.dataSolarReflectionManager->OriginThisRay,
754 0 : state.dataSolarReflectionManager->SunVec,
755 0 : state.dataSolarReflectionManager->ObsHitPt);
756 0 : if (hit) break; // An obstruction was hit
757 : }
758 0 : if (hit) continue; // Sun does not reach this ray's hit point
759 :
760 : // Sun reaches this ray's hit point; get beam-reflected diffuse radiance at hit point for
761 : // unit beam normal solar
762 :
763 : // CosIncBmAtHitPt = DOT_PRODUCT(SolReflRecSurf(RecSurfNum)%HitPtNormVec(RecPtNum,RayNum),SunVec)
764 : // Note: if sun is in back of hit surface relative to receiving point, CosIncBmAtHitPt will be < 0
765 : // and use of MAX in following gives zero beam solar reflecting at hit point.
766 : // BmReflSolRadiance = MAX(0.0d0,CosIncBmAtHitPt)*SolReflRecSurf(RecSurfNum)%HitPtSolRefl(RecPtNum,RayNum)
767 :
768 0 : state.dataSolarReflectionManager->BmReflSolRadiance =
769 0 : state.dataSolarReflectionManager->CosIncBmAtHitPt *
770 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
771 0 : .HitPtSolRefl(state.dataSolarReflectionManager->RayNum, state.dataSolarReflectionManager->RecPtNum);
772 :
773 0 : if (state.dataSolarReflectionManager->BmReflSolRadiance > 0.0) {
774 : // Contribution to reflection factor from this hit point
775 0 : if (state.dataSolarReflectionManager->HitPtSurfNum > 0) {
776 : // Ray hits an obstruction
777 0 : state.dataSolarReflectionManager->dReflBeamToDiffSol =
778 0 : state.dataSolarReflectionManager->BmReflSolRadiance *
779 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
780 0 : .dOmegaRay(state.dataSolarReflectionManager->RayNum) *
781 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
782 0 : .CosIncAngRay(state.dataSolarReflectionManager->RayNum) /
783 : Constant::Pi;
784 0 : ReflBmToDiffSolObs(state.dataSolarReflectionManager->RecPtNum) += state.dataSolarReflectionManager->dReflBeamToDiffSol;
785 : } else {
786 : // Ray hits ground (in this case we do not multiply by BmReflSolRadiance since
787 : // ground reflectance and cos of incidence angle of sun on
788 : // ground is taken into account later when SurfReflFacBmToDiffSolGnd is used)
789 0 : state.dataSolarReflectionManager->dReflBeamToDiffSol =
790 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
791 0 : .dOmegaRay(state.dataSolarReflectionManager->RayNum) *
792 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum)
793 0 : .CosIncAngRay(state.dataSolarReflectionManager->RayNum) /
794 : Constant::Pi;
795 0 : ReflBmToDiffSolGnd(state.dataSolarReflectionManager->RecPtNum) += state.dataSolarReflectionManager->dReflBeamToDiffSol;
796 : }
797 : }
798 : } // End of loop over rays from receiving point
799 : } // End of loop over receiving points
800 :
801 : // Average over receiving points
802 0 : state.dataSurface->SurfReflFacBmToDiffSolObs(iHour, state.dataSolarReflectionManager->SurfNum) = 0.0;
803 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) = 0.0;
804 0 : state.dataSolarReflectionManager->NumRecPts =
805 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->RecSurfNum).NumRecPts;
806 0 : for (state.dataSolarReflectionManager->RecPtNum = 1;
807 0 : state.dataSolarReflectionManager->RecPtNum <= state.dataSolarReflectionManager->NumRecPts;
808 0 : ++state.dataSolarReflectionManager->RecPtNum) {
809 0 : state.dataSurface->SurfReflFacBmToDiffSolObs(iHour, state.dataSolarReflectionManager->SurfNum) +=
810 0 : ReflBmToDiffSolObs(state.dataSolarReflectionManager->RecPtNum);
811 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) +=
812 0 : ReflBmToDiffSolGnd(state.dataSolarReflectionManager->RecPtNum);
813 : }
814 0 : state.dataSurface->SurfReflFacBmToDiffSolObs(iHour, state.dataSolarReflectionManager->SurfNum) /=
815 0 : state.dataSolarReflectionManager->NumRecPts;
816 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) /=
817 0 : state.dataSolarReflectionManager->NumRecPts;
818 :
819 : // Do not allow SurfReflFacBmToDiffSolGnd to exceed the surface's unobstructed ground view factor
820 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum) =
821 0 : min(0.5 * (1.0 - state.dataSurface->Surface(state.dataSolarReflectionManager->SurfNum).CosTilt),
822 0 : state.dataSurface->SurfReflFacBmToDiffSolGnd(iHour, state.dataSolarReflectionManager->SurfNum));
823 : // Note: the above factors are dimensionless; they are equal to
824 : // (W/m2 reflected solar incident on SurfNum)/(W/m2 beam normal solar)
825 : } // End of loop over receiving surfaces
826 0 : }
827 :
828 : //=================================================================================================
829 :
830 0 : void CalcBeamSolSpecularReflFactors(EnergyPlusData &state)
831 : {
832 :
833 : // SUBROUTINE INFORMATION:
834 : // AUTHOR Fred Winkelmann
835 : // DATE WRITTEN September 2003
836 : // MODIFIED na
837 : // RE-ENGINEERED B. Griffith, October 2012, for timestep integrated solar
838 :
839 : // PURPOSE OF THIS SUBROUTINE:
840 : // Manage calculation of factors for beam solar irradiance on exterior heat transfer surfaces due to
841 : // specular (beam-to-beam) reflection from obstructions such as a highly-glazed neighboring
842 : // building.
843 :
844 : // METHODOLOGY EMPLOYED:
845 : // call worker routine as appropriate
846 :
847 0 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
848 0 : if (state.dataGlobal->BeginSimFlag) {
849 0 : DisplayString(state, "Calculating Beam-to-Beam Exterior Solar Reflection Factors");
850 : } else {
851 0 : DisplayString(state, "Updating Beam-to-Beam Exterior Solar Reflection Factors");
852 : }
853 0 : state.dataSurface->SurfReflFacBmToBmSolObs = 0.0;
854 0 : state.dataSurface->SurfCosIncAveBmToBmSolObs = 0.0;
855 0 : for (state.dataSolarReflectionManager->NumHr = 1; state.dataSolarReflectionManager->NumHr <= 24;
856 0 : ++state.dataSolarReflectionManager->NumHr) {
857 0 : FigureBeamSolSpecularReflFactors(state, state.dataSolarReflectionManager->NumHr);
858 : } // End of NumHr loop
859 : } else { // timestep integrated solar, use current hour of day
860 0 : state.dataSurface->SurfReflFacBmToBmSolObs(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
861 0 : state.dataSurface->SurfCosIncAveBmToBmSolObs(state.dataGlobal->HourOfDay, {1, state.dataSurface->TotSurfaces}) = 0.0;
862 0 : FigureBeamSolSpecularReflFactors(state, state.dataGlobal->HourOfDay);
863 : }
864 0 : }
865 :
866 0 : void FigureBeamSolSpecularReflFactors(EnergyPlusData &state, int const iHour)
867 : {
868 :
869 : // SUBROUTINE INFORMATION:
870 : // AUTHOR Fred Winkelmann
871 : // DATE WRITTEN September 2003
872 : // MODIFIED na
873 : // RE-ENGINEERED B. Griffith, October 2012, for timestep integrated solar
874 :
875 : // PURPOSE OF THIS SUBROUTINE:
876 : // Calculates factors for beam solar irradiance on exterior heat transfer surfaces due to
877 : // specular (beam-to-beam) reflection from obstructions such as a highly-glazed neighboring
878 : // building. Specular reflection can occur from shading surfaces with non-zero specular
879 : // reflectance and from exterior windows of the building (in calculating reflection from
880 : // these windows, they are assumed to have no shades or blinds).
881 : // Reflection from the ground and opaque building surfaces is assumed to be totally diffuse,
882 : // i.e. these surfaces has no specular reflection component.
883 :
884 : // METHODOLOGY EMPLOYED:
885 : // <description>
886 :
887 : // REFERENCES:
888 : // na
889 :
890 : // Locals
891 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
892 0 : Array1D<Real64> ReflBmToDiffSolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
893 : // beam solar diffusely reflected from obstructions, divided by
894 : // beam normal irradiance
895 : // unused INTEGER :: RayNum =0 ! Ray number
896 : bool hitRefl; // True iff reflecting surface is hit
897 : bool hitObs; // True iff obstruction is hit
898 : bool hitObsRefl; // True iff obstruction hit between rec. pt. and reflection point
899 0 : Array1D<Real64> ReflBmToBmSolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for
900 : // beam solar specularly reflected from obstructions, divided by
901 : // beam normal irradiance
902 : Real64 ReflDistanceSq; // Distance squared from receiving point to hit point on a reflecting surface (m)
903 : Real64 ReflDistance; // Distance from receiving point to hit point on a reflecting surface (m)
904 0 : Array1D<Real64> ReflFacTimesCosIncSum(state.dataSurface->MaxRecPts); // Sum of ReflFac times CosIncAngRefl
905 :
906 0 : ReflBmToDiffSolObs = 0.0;
907 0 : ReflFacTimesCosIncSum = 0.0;
908 :
909 0 : if (state.dataSurface->SurfSunCosHourly(iHour)(3) < DataEnvironment::SunIsUpValue) return; // Skip if sun is below horizon
910 :
911 : // Unit vector to sun
912 0 : state.dataSolarReflectionManager->SunVect = state.dataSurface->SurfSunCosHourly(iHour);
913 :
914 0 : for (int RecSurfNum = 1; RecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf; ++RecSurfNum) {
915 : int const SurfNum =
916 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).SurfNum; // Heat transfer surface number corresponding to RecSurfNum
917 0 : if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
918 0 : ReflBmToBmSolObs = 0.0;
919 0 : ReflFacTimesCosIncSum = 0.0;
920 0 : int const NumRecPts = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumRecPts;
921 : // Find possible reflecting surfaces for this receiving surface
922 0 : for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end; ++loop) {
923 : int const ReflSurfNum =
924 0 : state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop); // Reflecting surface number
925 : // Keep windows; keep shading surfaces with specular reflectance
926 0 : if ((state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window && state.dataSurface->Surface(ReflSurfNum).ExtSolar) ||
927 0 : (state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) > 0.0 && state.dataSurface->Surface(ReflSurfNum).IsShadowing)) {
928 : // Skip if window and not sunlit
929 0 : if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window &&
930 0 : state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNum) < 0.01)
931 0 : continue;
932 : // Check if sun is in front of this reflecting surface.
933 0 : state.dataSolarReflectionManager->ReflNorm = state.dataSurface->Surface(ReflSurfNum).OutNormVec;
934 0 : state.dataSolarReflectionManager->CosIncAngRefl =
935 0 : dot(state.dataSolarReflectionManager->SunVect, state.dataSolarReflectionManager->ReflNorm);
936 0 : if (state.dataSolarReflectionManager->CosIncAngRefl < 0.0) continue;
937 :
938 : // Get sun position unit vector for mirror image of sun in reflecting surface
939 0 : state.dataSolarReflectionManager->SunVecMir =
940 0 : state.dataSolarReflectionManager->SunVect -
941 0 : 2.0 * dot(state.dataSolarReflectionManager->SunVect, state.dataSolarReflectionManager->ReflNorm) *
942 0 : state.dataSolarReflectionManager->ReflNorm;
943 : // Angle of incidence of reflected beam on receiving surface
944 0 : state.dataSolarReflectionManager->CosIncAngRec =
945 0 : dot(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec, state.dataSolarReflectionManager->SunVecMir);
946 0 : if (state.dataSolarReflectionManager->CosIncAngRec <= 0.0) continue;
947 0 : for (int RecPtNum = 1; RecPtNum <= NumRecPts; ++RecPtNum) {
948 : // See if ray from receiving point to mirrored sun hits the reflecting surface
949 0 : state.dataSolarReflectionManager->RecPt = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).RecPt(RecPtNum);
950 0 : hitRefl = PierceSurface(state,
951 : ReflSurfNum,
952 0 : state.dataSolarReflectionManager->RecPt,
953 0 : state.dataSolarReflectionManager->SunVecMir,
954 0 : state.dataSolarReflectionManager->HitPtRefl);
955 0 : if (hitRefl) { // Reflecting surface was hit
956 : ReflDistanceSq =
957 0 : distance_squared(state.dataSolarReflectionManager->HitPtRefl, state.dataSolarReflectionManager->RecPt);
958 0 : ReflDistance = std::sqrt(ReflDistanceSq);
959 : // Determine if ray from receiving point to hit point is obstructed
960 0 : hitObsRefl = false;
961 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
962 0 : loop2 <= loop2_end;
963 : ++loop2) {
964 0 : int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
965 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == state.dataSurface->Surface(ReflSurfNum).BaseSurf) continue;
966 0 : hitObs = PierceSurface(state,
967 : ObsSurfNum,
968 0 : state.dataSolarReflectionManager->RecPt,
969 0 : state.dataSolarReflectionManager->SunVecMir,
970 : ReflDistance,
971 0 : state.dataSolarReflectionManager->HitPtObs); // ReflDistance cutoff added
972 0 : if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
973 0 : if (distance_squared(state.dataSolarReflectionManager->HitPtObs, state.dataSolarReflectionManager->RecPt) <
974 : ReflDistanceSq) {
975 0 : hitObsRefl = true;
976 0 : break;
977 : }
978 : }
979 : }
980 0 : if (hitObsRefl) continue; // Obstruction closer than reflection pt. was hit; go to next rec. pt.
981 : // There is no obstruction for this ray between rec. pt. and hit point on reflecting surface.
982 : // See if ray from hit pt. on reflecting surface to original (unmirrored) sun position is obstructed
983 0 : hitObs = false;
984 0 : if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) { // Reflecting surface is a window
985 : // Receiving surface number for this window
986 0 : int const ReflSurfRecNum = state.dataSurface->SurfShadowRecSurfNum(
987 0 : ReflSurfNum); // Receiving surface number corresponding to a reflecting surface number
988 0 : if (ReflSurfRecNum > 0) {
989 : // Loop over possible obstructions for this window
990 0 : for (int loop2 = 1,
991 0 : loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
992 0 : loop2 <= loop2_end;
993 : ++loop2) {
994 : int const ObsSurfNum =
995 0 : state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
996 0 : hitObs = PierceSurface(state,
997 : ObsSurfNum,
998 0 : state.dataSolarReflectionManager->HitPtRefl,
999 0 : state.dataSolarReflectionManager->SunVect,
1000 0 : state.dataSolarReflectionManager->HitPtObs);
1001 0 : if (hitObs) break;
1002 : }
1003 : }
1004 : } else { // Reflecting surface is a building shade
1005 0 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
1006 0 : if (ObsSurfNum == ReflSurfNum) continue;
1007 :
1008 : // TH2 CR8959 -- Skip mirrored surfaces
1009 0 : if (state.dataSurface->Surface(ObsSurfNum).MirroredSurf) continue;
1010 : // TH2 CR8959 -- The other side of a mirrored surface cannot obstruct the mirrored surface
1011 0 : if (state.dataSurface->Surface(ReflSurfNum).MirroredSurf) {
1012 0 : if (ObsSurfNum == ReflSurfNum - 1) continue;
1013 : }
1014 :
1015 0 : hitObs = PierceSurface(state,
1016 : ObsSurfNum,
1017 0 : state.dataSolarReflectionManager->HitPtRefl,
1018 0 : state.dataSolarReflectionManager->SunVect,
1019 0 : state.dataSolarReflectionManager->HitPtObs);
1020 0 : if (hitObs) break;
1021 : }
1022 : }
1023 :
1024 0 : if (hitObs) continue; // Obstruction hit between reflection hit point and sun; go to next receiving pt.
1025 :
1026 : // No obstructions. Calculate reflected beam irradiance at receiving pt. from this reflecting surface.
1027 0 : state.dataSolarReflectionManager->SpecReflectance = 0.0;
1028 0 : if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
1029 0 : state.dataSolarReflectionManager->ConstrNumRefl = state.dataSurface->Surface(ReflSurfNum).Construction;
1030 0 : state.dataSolarReflectionManager->SpecReflectance = Window::POLYF(
1031 0 : std::abs(state.dataSolarReflectionManager->CosIncAngRefl),
1032 0 : state.dataConstruction->Construct(state.dataSolarReflectionManager->ConstrNumRefl).ReflSolBeamFrontCoef);
1033 : }
1034 0 : if (state.dataSurface->Surface(ReflSurfNum).IsShadowing &&
1035 0 : state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum) > 0) {
1036 0 : state.dataSolarReflectionManager->ConstrNumRefl = state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum);
1037 0 : state.dataSolarReflectionManager->SpecReflectance =
1038 0 : state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) *
1039 0 : Window::POLYF(
1040 0 : std::abs(state.dataSolarReflectionManager->CosIncAngRefl),
1041 0 : state.dataConstruction->Construct(state.dataSolarReflectionManager->ConstrNumRefl).ReflSolBeamFrontCoef);
1042 : }
1043 : // Angle of incidence of reflected beam on receiving surface
1044 0 : state.dataSolarReflectionManager->CosIncAngRec =
1045 0 : dot(state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NormVec,
1046 0 : state.dataSolarReflectionManager->SunVecMir);
1047 0 : state.dataSolarReflectionManager->ReflFac =
1048 0 : state.dataSolarReflectionManager->SpecReflectance * state.dataSolarReflectionManager->CosIncAngRec;
1049 : // Contribution to specular reflection factor
1050 0 : ReflBmToBmSolObs(RecPtNum) += state.dataSolarReflectionManager->ReflFac;
1051 0 : ReflFacTimesCosIncSum(RecPtNum) +=
1052 0 : state.dataSolarReflectionManager->ReflFac * state.dataSolarReflectionManager->CosIncAngRec;
1053 : } // End of check if reflecting surface was hit
1054 : } // End of loop over receiving points
1055 : } // End of check if valid reflecting surface
1056 : } // End of loop over obstructing surfaces
1057 : // Average over receiving points
1058 :
1059 0 : for (int RecPtNum = 1; RecPtNum <= NumRecPts; ++RecPtNum) {
1060 0 : if (ReflBmToBmSolObs(RecPtNum) != 0.0) {
1061 0 : state.dataSolarReflectionManager->CosIncWeighted = ReflFacTimesCosIncSum(RecPtNum) / ReflBmToBmSolObs(RecPtNum);
1062 : } else {
1063 0 : state.dataSolarReflectionManager->CosIncWeighted = 0.0;
1064 : }
1065 0 : state.dataSurface->SurfCosIncAveBmToBmSolObs(iHour, SurfNum) += state.dataSolarReflectionManager->CosIncWeighted;
1066 0 : state.dataSurface->SurfReflFacBmToBmSolObs(iHour, SurfNum) += ReflBmToBmSolObs(RecPtNum);
1067 : }
1068 0 : state.dataSurface->SurfReflFacBmToBmSolObs(iHour, SurfNum) /= double(NumRecPts);
1069 0 : state.dataSurface->SurfCosIncAveBmToBmSolObs(iHour, SurfNum) /= double(NumRecPts);
1070 : } // End of check if number of possible obstructions > 0
1071 : } // End of loop over receiving surfaces
1072 0 : }
1073 :
1074 : //=================================================================================================
1075 :
1076 0 : void CalcSkySolDiffuseReflFactors(EnergyPlusData &state)
1077 : {
1078 :
1079 : // SUBROUTINE INFORMATION:
1080 : // AUTHOR Fred Winkelmann
1081 : // DATE WRITTEN October 2003
1082 : // MODIFIED na
1083 : // RE-ENGINEERED na
1084 :
1085 : // PURPOSE OF THIS SUBROUTINE:
1086 : // Calculates factors for irradiance on exterior heat transfer surfaces due to
1087 : // reflection of sky diffuse solar radiation from obstructions and ground.
1088 :
1089 : // Using/Aliasing
1090 :
1091 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1092 0 : Array1D<Real64> ReflSkySolObs(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for sky diffuse solar
1093 : // reflected from obstructions, divided by unobstructed
1094 : // sky diffuse horizontal irradiance
1095 0 : Array1D<Real64> ReflSkySolGnd(state.dataSurface->MaxRecPts); // Irradiance at a receiving point for sky diffuse solar
1096 : // reflected from ground, divided by unobstructed
1097 : // sky diffuse horizontal irradiance
1098 : bool hitObs; // True iff obstruction is hit
1099 :
1100 0 : Real64 const DPhi(Constant::PiOvr2 / (AltAngStepsForSolReflCalc / 2.0)); // Altitude angle and increment (radians)
1101 0 : Real64 const DTheta(2.0 * Constant::Pi / (2.0 * AzimAngStepsForSolReflCalc)); // Azimuth increment (radians)
1102 :
1103 : // Pre-compute these constants
1104 : // Initialize the 0 index with dummy value so the iterators line up below
1105 0 : std::vector<Real64> sin_Phi({-1});
1106 0 : std::vector<Real64> cos_Phi({-1});
1107 0 : for (int IPhi = 1; IPhi <= (AltAngStepsForSolReflCalc / 2); ++IPhi) {
1108 0 : Real64 Phi((IPhi - 0.5) * DPhi); // Altitude angle and increment (radians)
1109 0 : sin_Phi.push_back(std::sin(Phi));
1110 0 : cos_Phi.push_back(std::cos(Phi));
1111 : }
1112 :
1113 0 : std::vector<Real64> sin_Theta({-1});
1114 0 : std::vector<Real64> cos_Theta({-1});
1115 0 : for (int ITheta = 1; ITheta <= 2 * AzimAngStepsForSolReflCalc; ++ITheta) {
1116 0 : Real64 Theta = (ITheta - 0.5) * DTheta; // Azimuth angle (radians)
1117 0 : sin_Theta.push_back(std::sin(Theta));
1118 0 : cos_Theta.push_back(std::cos(Theta));
1119 : }
1120 :
1121 0 : DisplayString(state, "Calculating Sky Diffuse Exterior Solar Reflection Factors");
1122 0 : ReflSkySolObs = 0.0;
1123 0 : ReflSkySolGnd = 0.0;
1124 :
1125 0 : for (state.dataSolarReflectionManager->iRecSurfNum = 1;
1126 0 : state.dataSolarReflectionManager->iRecSurfNum <= state.dataSolarReflectionManager->TotSolReflRecSurf;
1127 0 : ++state.dataSolarReflectionManager->iRecSurfNum) {
1128 0 : state.dataSolarReflectionManager->iSurfNum =
1129 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).SurfNum;
1130 0 : for (state.dataSolarReflectionManager->iRecPtNum = 1;
1131 0 : state.dataSolarReflectionManager->iRecPtNum <=
1132 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).NumRecPts;
1133 0 : ++state.dataSolarReflectionManager->iRecPtNum) {
1134 0 : ReflSkySolObs(state.dataSolarReflectionManager->iRecPtNum) = 0.0;
1135 0 : ReflSkySolGnd(state.dataSolarReflectionManager->iRecPtNum) = 0.0;
1136 0 : for (state.dataSolarReflectionManager->iRayNum = 1;
1137 0 : state.dataSolarReflectionManager->iRayNum <=
1138 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).NumReflRays;
1139 0 : ++state.dataSolarReflectionManager->iRayNum) {
1140 0 : state.dataSolarReflectionManager->HitPntSurfNum =
1141 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1142 0 : .HitPtSurfNum(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
1143 : // Skip rays that do not hit an obstruction or ground.
1144 : // (Note that if a downgoing ray does not hit an obstruction it will have HitPtSurfNum = 0
1145 : // if the receiving point is below ground level (see subr. InitSolReflRecSurf); this means
1146 : // that a below-ground-level receiving point receives no ground-reflected radiation although
1147 : // it is allowed to receive obstruction-reflected solar radiation and direct (unreflected)
1148 : // beam and sky solar radiation. As far as reflected solar is concerned, the program does
1149 : // not handle a sloped ground plane or a horizontal ground plane whose level is different
1150 : // from one side of the building to another.)
1151 0 : if (state.dataSolarReflectionManager->HitPntSurfNum == 0)
1152 0 : continue; // Ray hits sky or obstruction with receiving pt. below ground level
1153 0 : state.dataSolarReflectionManager->HitPntRefl =
1154 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1155 0 : .HitPt(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
1156 0 : if (state.dataSolarReflectionManager->HitPntSurfNum > 0) {
1157 : // Ray hits an obstruction
1158 : // Skip hit points on daylighting shelves, from which solar reflection is separately calculated
1159 0 : if (state.dataSurface->SurfDaylightingShelfInd(state.dataSolarReflectionManager->HitPntSurfNum) > 0) continue;
1160 : // Reflected radiance at hit point divided by unobstructed sky diffuse horizontal irradiance
1161 0 : state.dataSolarReflectionManager->HitPtSurfNumX = state.dataSolarReflectionManager->HitPntSurfNum;
1162 : // Each shading surface has a "mirror" duplicate surface facing in the opposite direction.
1163 : // The following gets the correct side of a shading surface in order to get the right value
1164 : // of DifShdgRatioIsoSky (the two sides can have different sky shadowing).
1165 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->HitPntSurfNum).IsShadowing) {
1166 0 : if (dot(state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1167 0 : .RayVec(state.dataSolarReflectionManager->iRayNum),
1168 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->HitPntSurfNum).OutNormVec) > 0.0) {
1169 0 : if (state.dataSolarReflectionManager->HitPntSurfNum + 1 < state.dataSurface->TotSurfaces)
1170 0 : state.dataSolarReflectionManager->HitPtSurfNumX = state.dataSolarReflectionManager->HitPntSurfNum + 1;
1171 0 : if (state.dataSurface->SurfDaylightingShelfInd(state.dataSolarReflectionManager->HitPtSurfNumX) > 0) continue;
1172 : }
1173 : }
1174 :
1175 0 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
1176 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
1177 0 : state.dataSolarReflectionManager->SkyReflSolRadiance =
1178 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNumX).ViewFactorSky *
1179 0 : state.dataSolarShading->SurfDifShdgRatioIsoSky(state.dataSolarReflectionManager->HitPtSurfNumX) *
1180 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1181 0 : .HitPtSolRefl(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
1182 : } else {
1183 0 : state.dataSolarReflectionManager->SkyReflSolRadiance =
1184 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->HitPtSurfNumX).ViewFactorSky *
1185 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, 1, state.dataSolarReflectionManager->HitPtSurfNumX) *
1186 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1187 0 : .HitPtSolRefl(state.dataSolarReflectionManager->iRayNum, state.dataSolarReflectionManager->iRecPtNum);
1188 : }
1189 0 : state.dataSolarReflectionManager->dReflSkySol =
1190 0 : state.dataSolarReflectionManager->SkyReflSolRadiance *
1191 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1192 0 : .dOmegaRay(state.dataSolarReflectionManager->iRayNum) *
1193 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1194 0 : .CosIncAngRay(state.dataSolarReflectionManager->iRayNum) /
1195 : Constant::Pi;
1196 0 : ReflSkySolObs(state.dataSolarReflectionManager->iRecPtNum) += state.dataSolarReflectionManager->dReflSkySol;
1197 : } else {
1198 : // Ray hits ground;
1199 : // Find radiance at hit point due to reflection of sky diffuse reaching
1200 : // ground directly, i.e., without reflecting from obstructions.
1201 : // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
1202 : // Divide hemisphere centered at ground hit point into elements of altitude Phi and
1203 : // azimuth Theta and create upward-going ray unit vector at each Phi,Theta pair.
1204 : // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
1205 0 : Real64 dReflSkyGnd = 0.0; // Factor for ground radiance due to direct sky diffuse reflection
1206 : // Altitude loop
1207 0 : for (int IPhi = 1; IPhi <= (AltAngStepsForSolReflCalc / 2); ++IPhi) {
1208 : // Third component of ray unit vector in (Theta,Phi) direction
1209 0 : state.dataSolarReflectionManager->URay(3) = sin_Phi[IPhi];
1210 0 : Real64 dOmega = cos_Phi[IPhi] * DTheta * DPhi; // Solid angle increment (steradians)
1211 : // Cosine of angle of incidence of ray on ground
1212 0 : Real64 CosIncAngRayToSky = sin_Phi[IPhi]; // Cosine of incidence angle on ground of ray to sky
1213 : // Azimuth loop
1214 0 : for (int ITheta = 1; ITheta <= 2 * AzimAngStepsForSolReflCalc; ++ITheta) {
1215 0 : state.dataSolarReflectionManager->URay.x = cos_Phi[IPhi] * cos_Theta[ITheta];
1216 0 : state.dataSolarReflectionManager->URay.y = cos_Phi[IPhi] * sin_Theta[ITheta];
1217 : // Does this ray hit an obstruction?
1218 0 : hitObs = false;
1219 0 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
1220 0 : state.dataSolarReflectionManager->iObsSurfNum = ObsSurfNum;
1221 : // Horizontal roof surfaces cannot be obstructions for rays from ground
1222 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Tilt < 5.0) continue;
1223 0 : if (!state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).IsShadowing) {
1224 0 : if (dot(state.dataSolarReflectionManager->URay,
1225 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).OutNormVec) >= 0.0)
1226 0 : continue;
1227 : // Special test for vertical surfaces with URay dot OutNormVec < 0; excludes
1228 : // case where ground hit point is in back of ObsSurfNum
1229 0 : if (state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Tilt > 89.0 &&
1230 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Tilt < 91.0) {
1231 0 : state.dataSolarReflectionManager->SurfVert =
1232 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).Vertex(2);
1233 0 : state.dataSolarReflectionManager->SurfVertToGndPt =
1234 0 : state.dataSolarReflectionManager->HitPntRefl - state.dataSolarReflectionManager->SurfVert;
1235 0 : if (dot(state.dataSolarReflectionManager->SurfVertToGndPt,
1236 0 : state.dataSurface->Surface(state.dataSolarReflectionManager->iObsSurfNum).OutNormVec) < 0.0)
1237 0 : continue;
1238 : }
1239 : }
1240 0 : hitObs = PierceSurface(state,
1241 0 : state.dataSolarReflectionManager->iObsSurfNum,
1242 0 : state.dataSolarReflectionManager->HitPntRefl,
1243 0 : state.dataSolarReflectionManager->URay,
1244 0 : state.dataSolarReflectionManager->HitPntObs);
1245 0 : if (hitObs) break;
1246 : }
1247 0 : if (hitObs) continue; // Obstruction hit
1248 : // Sky is hit
1249 0 : dReflSkyGnd += CosIncAngRayToSky * dOmega / Constant::Pi;
1250 : } // End of azimuth loop
1251 : } // End of altitude loop
1252 0 : ReflSkySolGnd(state.dataSolarReflectionManager->iRecPtNum) +=
1253 0 : dReflSkyGnd *
1254 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1255 0 : .dOmegaRay(state.dataSolarReflectionManager->iRayNum) *
1256 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum)
1257 0 : .CosIncAngRay(state.dataSolarReflectionManager->iRayNum) /
1258 : Constant::Pi;
1259 : } // End of check if ray from receiving point hits obstruction or ground
1260 : } // End of loop over rays from receiving point
1261 : } // End of loop over receiving points
1262 :
1263 : // Average over receiving points
1264 0 : state.dataSurface->SurfReflFacSkySolObs(state.dataSolarReflectionManager->iSurfNum) = 0.0;
1265 0 : state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) = 0.0;
1266 0 : state.dataSolarReflectionManager->iNumRecPts =
1267 0 : state.dataSolarReflectionManager->SolReflRecSurf(state.dataSolarReflectionManager->iRecSurfNum).NumRecPts;
1268 0 : for (state.dataSolarReflectionManager->iRecPtNum = 1;
1269 0 : state.dataSolarReflectionManager->iRecPtNum <= state.dataSolarReflectionManager->iNumRecPts;
1270 0 : ++state.dataSolarReflectionManager->iRecPtNum) {
1271 0 : state.dataSurface->SurfReflFacSkySolObs(state.dataSolarReflectionManager->iSurfNum) +=
1272 0 : ReflSkySolObs(state.dataSolarReflectionManager->iRecPtNum);
1273 0 : state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) +=
1274 0 : ReflSkySolGnd(state.dataSolarReflectionManager->iRecPtNum);
1275 : }
1276 0 : state.dataSurface->SurfReflFacSkySolObs(state.dataSolarReflectionManager->iSurfNum) /= state.dataSolarReflectionManager->iNumRecPts;
1277 0 : state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) /= state.dataSolarReflectionManager->iNumRecPts;
1278 : // Do not allow SurfReflFacBmToDiffSolGnd to exceed the surface's unobstructed ground view factor
1279 0 : state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum) =
1280 0 : min(0.5 * (1.0 - state.dataSurface->Surface(state.dataSolarReflectionManager->iSurfNum).CosTilt),
1281 0 : state.dataSurface->SurfReflFacSkySolGnd(state.dataSolarReflectionManager->iSurfNum));
1282 : // Note: the above factors are dimensionless; they are equal to
1283 : // (W/m2 reflected solar incident on SurfNum)/(W/m2 unobstructed horizontal sky diffuse irradiance)
1284 : } // End of loop over receiving surfaces
1285 0 : }
1286 :
1287 : } // namespace SolarReflectionManager
1288 :
1289 : } // namespace EnergyPlus
|