Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <algorithm>
50 : #include <cassert>
51 : #include <cmath>
52 : #include <string>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array.functions.hh>
56 : #include <ObjexxFCL/Fmath.hh>
57 : #include <ObjexxFCL/Vector3.hh>
58 : // #include <ObjexxFCL/Vector4.hh>
59 : #include <ObjexxFCL/member.functions.hh>
60 : #include <ObjexxFCL/random.hh>
61 : #include <ObjexxFCL/string.functions.hh>
62 :
63 : // EnergyPlus Headers
64 : #include <EnergyPlus/DElightManagerF.hh>
65 : #include <EnergyPlus/Data/EnergyPlusData.hh>
66 : #include <EnergyPlus/DataBSDFWindow.hh>
67 : #include <EnergyPlus/DataDElight.hh>
68 : #include <EnergyPlus/DataDaylighting.hh>
69 : #include <EnergyPlus/DataDaylightingDevices.hh>
70 : #include <EnergyPlus/DataEnvironment.hh>
71 : #include <EnergyPlus/DataErrorTracking.hh>
72 : #include <EnergyPlus/DataHeatBalance.hh>
73 : #include <EnergyPlus/DataIPShortCuts.hh>
74 : #include <EnergyPlus/DataPrecisionGlobals.hh>
75 : #include <EnergyPlus/DataStringGlobals.hh>
76 : #include <EnergyPlus/DataSurfaces.hh>
77 : #include <EnergyPlus/DataSystemVariables.hh>
78 : #include <EnergyPlus/DataViewFactorInformation.hh>
79 : #include <EnergyPlus/DaylightingDevices.hh>
80 : #include <EnergyPlus/DaylightingManager.hh>
81 : #include <EnergyPlus/DisplayRoutines.hh>
82 : #include <EnergyPlus/FileSystem.hh>
83 : #include <EnergyPlus/General.hh>
84 : #include <EnergyPlus/HeatBalanceManager.hh>
85 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
86 : #include <EnergyPlus/InternalHeatGains.hh>
87 : #include <EnergyPlus/OutputProcessor.hh>
88 : #include <EnergyPlus/OutputReportPredefined.hh>
89 : #include <EnergyPlus/PierceSurface.hh>
90 : #include <EnergyPlus/SQLiteProcedures.hh>
91 : #include <EnergyPlus/ScheduleManager.hh>
92 : #include <EnergyPlus/SolarReflectionManager.hh>
93 : #include <EnergyPlus/SolarShading.hh>
94 : #include <EnergyPlus/SurfaceOctree.hh>
95 : #include <EnergyPlus/UtilityRoutines.hh>
96 : #include <EnergyPlus/WindowComplexManager.hh>
97 : #include <EnergyPlus/WindowManager.hh>
98 :
99 : namespace EnergyPlus::Dayltg {
100 :
101 : // MODULE INFORMATION
102 : // AUTHOR Fred Winkelmann
103 : // DATE WRITTEN July 1997, December 1998
104 : // MODIFIED Oct 2004; LKL -- Efficiencies and code restructure
105 : // Aug 2012: BG -- Added availability schedule
106 :
107 : // PURPOSE OF THIS MODULE:
108 : // Manages the daylighting calculations for each thermal zone that has an associated
109 : // Daylighting:Controls object.
110 :
111 : // Includes calculation of interior daylight illuminance and glare
112 : // from each of the windows in a zone, control of window shading devices
113 : // to reduce glare, and control of overhead electric lighting in response
114 : // to interior daylight illuminance level at one or two user-specified
115 : // reference points at which sensors are located.
116 :
117 : // METHODOLOGY EMPLOYED:
118 : // REFERENCES:
119 : // "Daylighting Calculation in DOE-2," F.C.Winkelmann, LBL-11353, May 1983
120 : // "Daylighting Simulation in the DOE-2 Building Energy Analysis Program,"
121 : // F.C. Winkelmann and S. Selkowitz, Energy and Buildings 8(1985)271-286
122 :
123 : // OTHER NOTES:
124 : // This module was created from DOE-2.1E subroutines.
125 :
126 : // Correspondence between DOE-2.1E and EnergyPlus subroutine names:
127 :
128 : // DOE-2.1E EnergyPlus In Module Called from Module
129 : // DAVREF DayltgAveInteriorReflectance DaylightingManager DaylightingManager
130 : // DCOF CalcDayltgCoefficients DaylightingManager DaylightingManager
131 : // DCROSS DayltgCrossProduct DaylightingManager DaylightingManager
132 : // DEXTIL DayltgCurrentExtHorizIllum WeatherManager WeatherManager
133 : // DGLARE DayltgGlare DaylightingManager DaylightingManager
134 : // DHILL DayltgExtHorizIllum DaylightingManager DaylightingManager
135 : // DHITSH DayltgHitObstruction DaylightingManager DaylightingManager
136 : // DINTIL DayltgInteriorIllum DaylightingManager HeatBalanceSurfaceManager
137 : // DLTSYS DayltgElecLightingControl DaylightingManager HeatBalanceSurfaceManager
138 : // DNSOL not used
139 : // DPFAC DayltgPositionFactor DaylightingManager DaylightingManager
140 : // DPIERC PierceSurface PierceSurface DaylightingManager
141 : // DREFLT DayltgInterReflectedIllum DaylightingManager DaylightingManager
142 : // DSKYLU DayltgSkyLuminance DaylightingManager DaylightingManager
143 : // DTHLIM DayltgAzimuthLimits DaylightingManager DaylightingManager
144 : // DLUMEF DayltgLuminousEfficacy WeatherManager WeatherManager
145 :
146 : // Using/Aliasing
147 : using namespace DataSurfaces;
148 :
149 335 : void DayltgAveInteriorReflectance(EnergyPlusData &state, int const enclNum) // Enclosure number
150 : {
151 :
152 : // SUBROUTINE INFORMATION:
153 : // AUTHOR Fred Winkelmann
154 : // DATE WRITTEN July 1997
155 : // MODIFIED Mar 2004, FCW: add calculation of following SurfaceWindow variables:
156 : // EnclAreaMinusThisSurf, EnclAreaReflProdMinusThisSurf, RhoCeilingWall,
157 : // RhoFloorWall, FractionUpgoing. Add calculation of ZoneDaylight%floorVisRefl.
158 :
159 : // PURPOSE OF THIS SUBROUTINE:
160 : // Called by CalcDayltgCoefficients for each daylit zone. Determines total
161 : // area and area-weighted average visible reflectance of
162 : // all inside faces of the surfaces of a zone. In addition, finds
163 : // area and average reflectance of interzone, underground and exterior
164 : // heat-transfer surfaces in the following categories: floor (tilt > 170 deg),
165 : // ceiling (tilt < 10 deg), and wall (10 < tilt < 170 deg).
166 : // The window reflectance values used here assume the windows have no shading
167 : // devices. This information is used in the calculation of the
168 : // internally-reflected daylighting component.
169 :
170 : // Finds total number of exterior windows in the space.
171 :
172 : // REFERENCES:
173 : // Based on DOE-2.1E subroutine DAVREF
174 335 : auto &dl = state.dataDayltg;
175 335 : auto &s_surf = state.dataSurface;
176 :
177 : // Total inside surface area, including windows
178 335 : Real64 AInsTot = 0.0;
179 : // Sum of products of inside surface area * vis reflectance
180 335 : Real64 ARHTOT = 0.0;
181 :
182 : // Area sum and area * reflectance sum for different orientations
183 335 : std::array<Real64, (int)FWC::Num> AR = {0.0, 0.0, 0.0};
184 335 : std::array<Real64, (int)FWC::Num> ARH = {0.0, 0.0, 0.0};
185 : // Loop over surfaces in the zone's enclosure
186 :
187 335 : auto &thisEnclosure = state.dataViewFactor->EnclSolInfo(enclNum);
188 3905 : for (int ISurf : thisEnclosure.SurfacePtr) {
189 3570 : auto const &surf = s_surf->Surface(ISurf);
190 :
191 3570 : SurfaceClass IType = surf.Class;
192 : // Error if window has multiplier > 1 since this causes incorrect illuminance calc
193 3570 : if (IType == SurfaceClass::Window && surf.Multiplier > 1.0) {
194 0 : if (thisEnclosure.TotalEnclosureDaylRefPoints > 0) {
195 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
196 0 : ShowContinueError(state, "...not allowed since it is in a zone or enclosure with daylighting.");
197 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
198 : } else {
199 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
200 0 : ShowContinueError(state, "...an adjacent Zone has daylighting. Simulation cannot proceed.");
201 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
202 : }
203 : }
204 3570 : if (IType == SurfaceClass::Wall || IType == SurfaceClass::Floor || IType == SurfaceClass::Roof || IType == SurfaceClass::Window ||
205 : IType == SurfaceClass::Door) {
206 3277 : Real64 AREA = surf.Area;
207 : // In following, FrameArea and DividerArea can be non-zero only for exterior windows
208 3277 : AInsTot += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
209 3277 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
210 3277 : ARHTOT +=
211 3277 : AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
212 3277 : s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
213 3277 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
214 :
215 3277 : FWC fwc = FWC::Ceiling; // Ceiling
216 3277 : if (surf.Tilt > 10.0 && surf.Tilt < 170.0) {
217 2266 : fwc = FWC::Wall; // Wall
218 : }
219 3277 : if (surf.Tilt >= 170.0) {
220 368 : fwc = FWC::Floor; // Floor
221 : }
222 3277 : AR[(int)fwc] += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
223 3277 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
224 3277 : ARH[(int)fwc] +=
225 3277 : AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
226 3277 : s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
227 3277 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
228 : }
229 : }
230 :
231 : // Average inside surface reflectance of enclosure
232 335 : if (AInsTot <= 0.0) {
233 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Total opaque surface area is <=0.0 in solar enclosure={}", thisEnclosure.Name));
234 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
235 : }
236 335 : dl->enclDaylight(enclNum).aveVisDiffReflect = ARHTOT / AInsTot;
237 : // Total inside surface area of enclosure
238 335 : dl->enclDaylight(enclNum).totInsSurfArea = AInsTot;
239 : // Average floor visible reflectance
240 335 : dl->enclDaylight(enclNum).floorVisRefl = ARH[iFWC_Ceiling] / (AR[iFWC_Ceiling] + 1.e-6);
241 :
242 3905 : for (int ISurf : thisEnclosure.SurfacePtr) {
243 3570 : auto const &surf = s_surf->Surface(ISurf);
244 3570 : if (surf.Class != SurfaceClass::Wall && surf.Class != SurfaceClass::Floor && surf.Class != SurfaceClass::Roof) {
245 1411 : continue;
246 : }
247 :
248 : // Remove this surface from the space inside surface area and area*reflectivity
249 : // The resulting areas are AP(ITILT). The resulting area*reflectivity is ARHP(ITILT).
250 : // Initialize gross area of surface (including subsurfaces)
251 2159 : Real64 ATWL = surf.Area; // This is the surface area less subsurfaces
252 : // Area * reflectance for this surface, excluding attached windows and doors
253 2159 : Real64 ARHTWL = surf.Area * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack;
254 :
255 2159 : FWC fwc = (surf.Tilt > 45.0 && surf.Tilt < 135.0) ? FWC::Wall : ((surf.Tilt >= 135.0) ? FWC::Floor : FWC::Ceiling);
256 :
257 : // Loop over windows and doors on this wall
258 26625 : for (int IWinDr : thisEnclosure.SurfacePtr) {
259 24466 : auto const &surfWinDr = s_surf->Surface(IWinDr);
260 24466 : if ((surfWinDr.Class != SurfaceClass::Window && surfWinDr.Class != SurfaceClass::Door) || surfWinDr.BaseSurf != ISurf) {
261 23348 : continue;
262 : }
263 :
264 1118 : ATWL += surfWinDr.Area + s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) +
265 1118 : s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr));
266 1118 : ARHTWL +=
267 1118 : surfWinDr.Area * state.dataConstruction->Construct(surfWinDr.Construction).ReflectVisDiffBack +
268 1118 : s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(IWinDr)) +
269 1118 : s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(IWinDr));
270 : }
271 :
272 : std::array<Real64, (int)FWC::Num> AP;
273 : std::array<Real64, (int)FWC::Num> ARHP;
274 : // Inside surface area of floor, walls and ceilings, minus surface ISurf and its subsurfaces
275 8636 : for (int iFWC = iFWC_Floor; iFWC < (int)FWC::Num; ++iFWC) {
276 6477 : if (iFWC == (int)fwc) {
277 2159 : AP[iFWC] = AR[iFWC] - ATWL;
278 2159 : ARHP[iFWC] = ARH[iFWC] - ARHTWL;
279 : } else {
280 4318 : AP[iFWC] = AR[iFWC];
281 4318 : ARHP[iFWC] = ARH[iFWC];
282 : }
283 : }
284 2159 : s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf = AP;
285 2159 : s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf = ARHP;
286 : } // for (ISurf)
287 :
288 3905 : for (int IWin : thisEnclosure.SurfacePtr) {
289 3570 : auto const &surf = s_surf->Surface(IWin);
290 3570 : if (surf.Class != SurfaceClass::Window) {
291 2658 : continue;
292 : }
293 :
294 912 : auto &surfWin = s_surf->SurfaceWindow(IWin);
295 912 : auto const &zone = state.dataHeatBal->Zone(surf.Zone);
296 912 : int ISurf = surf.BaseSurf;
297 :
298 : // Ratio of floor-to-window-center height and average floor-to-ceiling height
299 912 : Real64 ETA = max(0.0, min(1.0, (surfWin.WinCenter.z - zone.OriginZ) * zone.FloorArea / zone.Volume));
300 :
301 912 : std::array<Real64, (int)FWC::Num> AP = s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf;
302 912 : std::array<Real64, (int)FWC::Num> ARHP = s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf;
303 : // Average reflectance seen by light moving up (RhoCeilingWall) and down (RhoFloorWall)
304 : // across horizontal plane through center of window
305 912 : surfWin.rhoCeilingWall = (ARHP[iFWC_Wall] * (1.0 - ETA) + ARHP[iFWC_Ceiling]) / (AP[iFWC_Wall] * (1.0 - ETA) + AP[iFWC_Ceiling] + 1.0e-5);
306 912 : surfWin.rhoFloorWall = (ARHP[iFWC_Wall] * ETA + ARHP[iFWC_Floor]) / (AP[iFWC_Wall] * ETA + AP[iFWC_Floor] + 1.e-9);
307 :
308 : // Angle factor for windows with diffusing shades. SurfaceWindow(IWin)%FractionUpgoing is
309 : // fraction of light from the shade that goes up toward ceiling and upper part of walls.
310 : // 1 - SurfaceWindow(IWin)%FractionUpgoing is fraction that goes down toward floor and lower part of walls.
311 912 : surfWin.fractionUpgoing = surf.Tilt / 180.0;
312 :
313 : // Daylighting shelf simplification: All light goes up to the ceiling regardless of orientation of shelf
314 912 : if (s_surf->SurfDaylightingShelfInd(IWin) > 0) {
315 1 : if (state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf > 0) {
316 1 : surfWin.fractionUpgoing = 1.0;
317 : }
318 : }
319 : } // for (IWin)
320 335 : } // DayltgAveInteriorReflectance()
321 :
322 14698 : void CalcDayltgCoefficients(EnergyPlusData &state)
323 : {
324 :
325 : // SUBROUTINE INFORMATION:
326 : // AUTHOR Fred Winkelmann
327 : // DATE WRITTEN July 1997
328 : // MODIFIED FW, Jan 2002: add variable slat angle blinds
329 : // FW, Mar 2002: add triangular windows
330 : // FW, Oct 2002: remove warning on window discretization relative to
331 : // reference point distance to window plane
332 : // FW, Jan 2003: add between-glass shades and blinds
333 : // FW, Apr 2003: initialize shading type to 'NOSHADE' in window loop
334 : // PE, May 2003: add light pipes (tubular daylighting devices)
335 : // FW, Jul 2003: account for possible non-zero transmittance of
336 : // shading surfaces (previously all shading surfaces were
337 : // assumed to be opaque)
338 : // PE, Aug 2003: add daylighting shelves
339 : // FW, Sep 2003: write the bare-window overcast sky daylight factors to the eio file
340 : // FW, Nov 2003: add exterior beam and sky solar diffuse reflection from obstructions;
341 : // add beam solar and sky solar reflection from ground with obstructions.
342 : // FW, Nov 2003: change expression for NDIVX, NDIVY (no. of window elements in X,Y) to
343 : // round up to nearest integer rather than down
344 : // FW, Nov 2003: add specular reflection of beam solar from obstructions
345 : // RJH, Jan 2004: add alternative daylighting analysis using DElight
346 : // All modifications demarked with RJH (Rob Hitchcock)
347 : // FW, Feb 2004: add daylighting through interior windows
348 : // FW, Apr 2004: add light well efficiency that multiplies glazing transmittance
349 : // FW, Apr 2004: add diffusing glazing
350 : // RJH, Jul 2004: add error handling for warnings/errors returned from DElight
351 : // LKL, Oct 2004: Separate "map" and "ref" point calculations -- move some input routines to
352 : // separate routines.
353 :
354 : // PURPOSE OF THIS SUBROUTINE:
355 : // Calculates daylighting factors for later use in the time-step loop.
356 :
357 : // METHODOLOGY EMPLOYED:
358 :
359 : // For each combination of exterior window and reference point in a zone,
360 : // calculates daylighting factors (interior illuminance / exterior illuminance)
361 : // and glare factors for clear and overcast skies and for windows with and
362 : // without shading devices. These factors are calculated for each hourly
363 : // sun position for design days and for selected days throughout the year.
364 :
365 : // If a target zone has one or more interior windows, also calculates daylighting
366 : // factors for the target zone that are associated with exterior windows in adjacent
367 : // zones that share interior windows with the target zone.
368 :
369 : // The daylight illuminance at a reference point from a window is determined
370 : // by dividing the window into rectangular elements and calculating the illuminance
371 : // reaching the reference point directly from each element. The illumination
372 : // from an element can come from the sky or ground if the window is unshaded, or from
373 : // a shading device illuminated by solar radiation. Also considered are the
374 : // illuminance contribution from interreflection among the zone's interior surfaces
375 : // and sunlight striking the reference point.
376 :
377 : // In calculating sky-related interior illuminance and luminance quantities,
378 : // the sky luminance for the different sky types are determined from distributions
379 : // in which the zenith luminance is normalized to 1.0 cd/m2. Similarly, sun-related
380 : // illuminance and luminance quantities are based on beam normal solar illuminance
381 : // normalized to 1.0 lux.
382 : // The daylight and glare factors calculated in this subroutine are used in DayltgInteriorIllum
383 : // to get the daylight illuminance and glare at each time step.
384 : // Based on this information and user-input lighting setpoint and type of lighting
385 : // control system, DayltgElecLightingControl then determines how much the overhead electric lighting
386 : // can be reduced.
387 :
388 : // REFERENCES:
389 : // Based on DOE-2.1E subroutine DCOF.
390 :
391 14698 : auto &dl = state.dataDayltg;
392 14698 : auto &s_surf = state.dataSurface;
393 :
394 14698 : if (dl->CalcDayltghCoefficients_firstTime) {
395 801 : GetDaylightingParametersInput(state);
396 801 : CheckTDDsAndLightShelvesInDaylitZones(state);
397 801 : AssociateWindowShadingControlWithDaylighting(state);
398 801 : dl->CalcDayltghCoefficients_firstTime = false;
399 : } // End of check if firstTime
400 :
401 : // Find the total number of exterior windows associated with all Daylighting:Detailed enclosures.
402 : // An exterior window is associated with such a enclosure if (1) it is an exterior window in the enclosure, or
403 : // (2) it is an exterior window in an adjacent enclosure that shares an interior window with the enclosure.
404 : // Note that exterior windows in category (2) may be counted more than once if an adjacent enclosure
405 : // is adjacent to more than one daylit enclosure with which the adjacent enclosure shares interior windows.
406 : // If there are no interior windows in a building, than TotWindowsWithDayl is just the total number of
407 : // exterior windows in Daylighting:Detailed enclosures. Note that it is possible for a
408 : // Daylighting:Detailed enclosure to have zero exterior windows of its own, but it may have an interior
409 : // through which daylight passes from adjacent enclosures with exterior windows.
410 14698 : if ((int)dl->DaylRefPt.size() == 0) {
411 14144 : return;
412 : }
413 554 : if (state.dataGlobal->BeginSimFlag) {
414 65 : dl->TotWindowsWithDayl = 0;
415 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
416 851 : dl->TotWindowsWithDayl += dl->enclDaylight(enclNum).NumOfDayltgExtWins;
417 : }
418 : }
419 :
420 554 : if (dl->TotWindowsWithDayl == 0) {
421 0 : return;
422 : }
423 :
424 : //-----------------------------------------!
425 : // Detailed daylighting factor calculation !
426 : //-----------------------------------------!
427 554 : if (!state.dataSysVars->DetailedSolarTimestepIntegration && !state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) {
428 231 : if (state.dataGlobal->WarmupFlag) {
429 213 : DisplayString(state, "Calculating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
430 : } else {
431 18 : DisplayString(state, "Updating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
432 : }
433 : }
434 :
435 554 : if (state.dataGlobal->BeginSimFlag) {
436 :
437 : // Find minimum solid angle subtended by an interior window in Daylighting:Detailed zones.
438 : // Used in calculating daylighting through interior windows.
439 65 : CalcMinIntWinSolidAngs(state);
440 :
441 : // Warning if detailed daylighting has been requested for a zone with no associated exterior windows.
442 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
443 851 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
444 851 : if (thisEnclDaylight.NumOfDayltgExtWins == 0 && thisEnclDaylight.TotalExtWindows == 0) {
445 293 : for (int daylightCtrlNum : thisEnclDaylight.daylightControlIndexes) {
446 0 : if (dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints > 0) {
447 0 : ShowWarningError(
448 : state,
449 0 : format("Detailed daylighting will not be done for Daylighting:Controls={}", dl->daylightControl(daylightCtrlNum).Name));
450 0 : ShowContinueError(state, "because it has no associated exterior windows.");
451 : }
452 293 : }
453 : }
454 :
455 : // Find area and reflectance quantities used in calculating inter-reflected illuminance.
456 : // TH 9/10/2009. Need to calculate for zones without daylighting controls (TotalDaylRefPoints = 0)
457 : // but with adjacent zones having daylighting controls.
458 851 : if ((thisEnclDaylight.NumOfDayltgExtWins > 0) || thisEnclDaylight.adjEnclHasDayltgCtrl) {
459 335 : DayltgAveInteriorReflectance(state, enclNum);
460 : }
461 : }
462 : }
463 :
464 554 : int numTDD = (int)state.dataDaylightingDevicesData->TDDPipe.size();
465 : // Zero daylighting factor arrays
466 554 : if (numTDD > 0) {
467 5 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
468 5 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
469 125 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
470 360 : for (int iTDD = 1; iTDD <= numTDD; ++iTDD) {
471 240 : dl->TDDTransVisBeam(iHr, iTDD) = 0.0;
472 960 : dl->TDDFluxInc(iHr, iTDD) = Illums();
473 960 : dl->TDDFluxTrans(iHr, iTDD) = Illums();
474 : }
475 : }
476 : }
477 :
478 554 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
479 554 : if (state.dataGlobal->BeginDayFlag) {
480 : // Calculate hourly sun angles, clear sky zenith luminance, and exterior horizontal illuminance
481 554 : dl->sunAngles = SunAngles();
482 554 : dl->sunAnglesHr = {SunAngles()};
483 554 : dl->horIllum = {Illums()};
484 13850 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
485 13296 : auto const &surfSunCosHr = s_surf->SurfSunCosHourly(IHR);
486 13296 : if (surfSunCosHr.z < DataEnvironment::SunIsUpValue) {
487 8461 : continue; // Skip if sun is below horizon //Autodesk SurfSunCosHourly was uninitialized here
488 : }
489 :
490 4835 : Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
491 4835 : Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
492 4835 : dl->sunAngles = dl->sunAnglesHr[IHR] = {phi, std::sin(phi), std::cos(phi), theta};
493 :
494 4835 : DayltgExtHorizIllum(state, dl->horIllum[IHR]);
495 : }
496 : }
497 : } else { // timestep integrated calculations
498 0 : dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {SunAngles()};
499 0 : dl->horIllum[state.dataGlobal->HourOfDay] = Illums();
500 0 : auto const &surfSunCosHr = s_surf->SurfSunCosHourly(state.dataGlobal->HourOfDay);
501 0 : if (!(surfSunCosHr.z < DataEnvironment::SunIsUpValue)) { // Skip if sun is below horizon
502 0 : Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
503 0 : Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
504 0 : dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {phi, std::sin(phi), std::cos(phi), theta};
505 0 : DayltgExtHorizIllum(state, dl->horIllum[state.dataGlobal->HourOfDay]);
506 : }
507 : }
508 :
509 554 : CalcDayltgCoeffsRefMapPoints(state);
510 :
511 619 : if (dl->doSkyReporting && (!state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) &&
512 65 : (dl->FirstTimeDaylFacCalc && dl->TotWindowsWithDayl > 0 &&
513 65 : (!state.dataSysVars->DetailedSolarTimestepIntegration || state.dataGlobal->HourOfDay == 12))) {
514 : // Write the bare-window four sky daylight factors at noon time to the eio file; this is done only
515 : // for first time that daylight factors are calculated and so is insensitive to possible variation
516 : // due to change in ground reflectance from month to month, or change in storm window status.
517 : static constexpr std::string_view Format_700("! <Sky Daylight Factors>, Sky Type, MonthAndDay, Daylighting Control Name, Enclosure Name, "
518 : "Window Name, Reference Point, Daylight Factor\n");
519 65 : print(state.files.eio, Format_700);
520 403 : for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
521 338 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
522 338 : int enclNum = thisDayltgCtrl.enclIndex;
523 338 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
524 :
525 338 : if (thisEnclDaylight.NumOfDayltgExtWins == 0 || !thisEnclDaylight.hasSplitFluxDaylighting) {
526 3 : continue;
527 : }
528 1248 : for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
529 913 : int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
530 : // For this report, do not include ext wins in zone adjacent to ZoneNum since the inter-reflected
531 : // component will not be calculated for these windows until the time-step loop.
532 913 : if (s_surf->Surface(windowSurfNum).SolarEnclIndex != enclNum) {
533 1 : continue;
534 : }
535 : // Output for each reference point, for each sky. Group by sky type first
536 :
537 : static constexpr std::array<std::string_view, (int)SkyType::Num> skyTypeStrings = {
538 : "Clear Sky", "Clear Turbid Sky", "Intermediate Sky", "Overcast Sky"};
539 :
540 4560 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
541 9860 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
542 6212 : Real64 DaylFac = thisDayltgCtrl.daylFac[12](windowCounter, refPtNum)[iWinCover_Bare][iLum_Illum].sky[iSky];
543 6212 : print(state.files.eio,
544 : " Sky Daylight Factors,{},{},{},{},{},{},{:.4R}\n",
545 6212 : skyTypeStrings[iSky],
546 6212 : state.dataEnvrn->CurMnDy,
547 6212 : thisDayltgCtrl.Name,
548 6212 : state.dataViewFactor->EnclSolInfo(thisDayltgCtrl.enclIndex).Name,
549 6212 : s_surf->Surface(windowSurfNum).Name,
550 6212 : dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
551 : DaylFac);
552 : } // for (refPtNum)
553 : } // for (iSky)
554 : } // for (windowCounter)
555 : } // for (controlNum)
556 65 : dl->FirstTimeDaylFacCalc = false;
557 65 : dl->doSkyReporting = false;
558 : } // if (detailedIntegration etc.)
559 :
560 : // Skip if no daylight windows
561 554 : if (dl->TotWindowsWithDayl == 0) {
562 0 : return;
563 : }
564 :
565 : // Skip if no request of reporting
566 554 : if ((!dl->DFSReportSizingDays) && (!dl->DFSReportAllShadowCalculationDays)) {
567 545 : return;
568 : }
569 :
570 : // Skip duplicate calls
571 9 : if (state.dataGlobal->KickOffSizing) {
572 2 : return;
573 : }
574 7 : if (state.dataGlobal->DoingSizing) {
575 2 : return;
576 : }
577 5 : if (state.dataGlobal->KickOffSimulation) {
578 3 : return;
579 : }
580 :
581 2 : if (dl->DFSReportSizingDays) {
582 2 : if (state.dataGlobal->DoWeathSim && state.dataGlobal->DoDesDaySim) {
583 0 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) {
584 0 : return;
585 : }
586 : }
587 : }
588 :
589 2 : if (dl->DFSReportAllShadowCalculationDays) {
590 0 : if (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather) {
591 0 : return;
592 : }
593 : }
594 :
595 : // open a new file eplusout.dfs for saving the daylight factors
596 2 : if (dl->CreateDFSReportFile) {
597 2 : InputOutputFile &dfs = state.files.dfs.ensure_open(state, "CalcDayltgCoefficients", state.files.outputControl.dfs);
598 1 : print(dfs, "{}\n", "This file contains daylight factors for all exterior windows of daylight enclosures.");
599 1 : print(dfs, "{}\n", "MonthAndDay,Enclosure Name,Zone Name,Window Name,Window State");
600 1 : print(dfs,
601 : "{}\n",
602 : "Hour,Reference Point,Daylight Factor for Clear Sky,Daylight Factor for Clear Turbid Sky,"
603 : "Daylight Factor for Intermediate Sky,Daylight Factor for Overcast Sky");
604 1 : dl->CreateDFSReportFile = false;
605 : }
606 :
607 26 : for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
608 24 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
609 24 : int enclNum = thisDayltgCtrl.enclIndex;
610 24 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
611 24 : if (thisEnclDaylight.NumOfDayltgExtWins == 0) {
612 0 : continue;
613 : }
614 :
615 48 : for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
616 24 : int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
617 24 : auto &surf = s_surf->Surface(windowSurfNum);
618 : // For this report, do not include ext wins in zone/enclosure adjacent to ZoneNum since the inter-reflected
619 : // component will not be calculated for these windows until the time-step loop.
620 24 : if (surf.SolarEnclIndex == enclNum) {
621 :
622 24 : int numWinCover = surf.HasShadeControl ? (int)WinCover::Num : 1;
623 :
624 : // loop over each slat angle
625 48 : for (int iWinCover = 0; iWinCover < numWinCover; ++iWinCover) {
626 24 : if (iWinCover == iWinCover_Bare) {
627 : // base window without shades, screens, or blinds
628 24 : print(state.files.dfs,
629 : "{},{},{},{},Base Window\n",
630 24 : state.dataEnvrn->CurMnDy,
631 24 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
632 24 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
633 24 : s_surf->Surface(windowSurfNum).Name);
634 0 : } else if (iWinCover == iWinCover_Shaded) {
635 : // window shade or blind with fixed slat angle
636 0 : print(state.files.dfs,
637 : "{},{},{},{},Blind or Slat Applied\n",
638 0 : state.dataEnvrn->CurMnDy,
639 0 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
640 0 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
641 0 : s_surf->Surface(windowSurfNum).Name);
642 : }
643 :
644 600 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
645 : // For each Daylight Reference Point
646 576 : auto &daylFacHr = thisDayltgCtrl.daylFac[IHR];
647 1152 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
648 576 : auto &illums = daylFacHr(windowCounter, refPtNum)[iWinCover][iLum_Illum];
649 :
650 : // write daylight factors - 4 sky types for each daylight ref point
651 576 : print(state.files.dfs,
652 : "{},{},{:.5R},{:.5R},{:.5R},{:.5R}\n",
653 : IHR,
654 576 : dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
655 576 : illums.sky[(int)SkyType::Clear],
656 576 : illums.sky[(int)SkyType::ClearTurbid],
657 576 : illums.sky[(int)SkyType::Intermediate],
658 576 : illums.sky[(int)SkyType::Overcast]);
659 :
660 : } // for (refPtNum) Reference Point
661 : } // for (IHR) hour
662 : } // for (ISlatAngle) slat angle
663 : } // if (SolarEnclIndex == enclNum)
664 : } // for (windowCounter) exterior windows in enclosure
665 : } // for (controlNum) daylighting control
666 : } // CalcDayltgCoefficients()
667 :
668 554 : void CalcDayltgCoeffsRefMapPoints(EnergyPlusData &state)
669 : {
670 :
671 : // SUBROUTINE INFORMATION:
672 : // AUTHOR Linda Lawrie
673 : // DATE WRITTEN October 2004
674 : // MODIFIED May 2006 (RR): added exterior window screens
675 : // April 2012 (LKL); change to allow multiple maps per zone
676 :
677 : // PURPOSE OF THIS SUBROUTINE:
678 : // This subroutine does the daylighting coefficient calculation for the
679 : // daylighting and illuminance map reference points.
680 554 : auto &dl = state.dataDayltg;
681 554 : auto const &s_surf = state.dataSurface;
682 :
683 554 : if (dl->VeryFirstTime) {
684 : // make sure all necessary surfaces match to pipes
685 65 : bool ErrorsFound = false;
686 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
687 1761 : for (int loopwin = 1; loopwin <= dl->enclDaylight(enclNum).NumOfDayltgExtWins; ++loopwin) {
688 910 : int IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
689 910 : if (s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Diffuser) {
690 908 : continue;
691 : }
692 : // Look up the TDD:DOME object
693 2 : int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
694 2 : if (PipeNum == 0) {
695 0 : ShowSevereError(
696 : state,
697 0 : format("GetTDDInput: Surface={}, TDD:Dome object does not reference a valid Diffuser object.", s_surf->Surface(IWin).Name));
698 0 : ShowContinueError(state, "...needs DaylightingDevice:Tubular of same name as Surface.");
699 0 : ErrorsFound = true;
700 : }
701 : }
702 : }
703 :
704 65 : if (ErrorsFound) {
705 0 : ShowFatalError(state, "Not all TubularDaylightDome objects have corresponding DaylightingDevice:Tubular objects. Program terminates.");
706 : }
707 65 : dl->VeryFirstTime = false;
708 : }
709 :
710 : // Calc for daylighting reference points for daylighting controls that use SplitFlux method
711 3968 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
712 3414 : if (dl->daylightControl(daylightCtrlNum).DaylightMethod != DaylightingMethod::SplitFlux) {
713 15 : continue;
714 : }
715 : // Skip enclosures with no exterior windows or in adjacent enclosure(s) with which an interior window is shared
716 3399 : if (dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex).NumOfDayltgExtWins == 0) {
717 0 : continue;
718 : }
719 3399 : CalcDayltgCoeffsRefPoints(state, daylightCtrlNum);
720 : }
721 554 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
722 : // Calc for illuminance maps
723 158 : if ((int)dl->illumMaps.size() > 0) {
724 50 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
725 28 : int mapZoneNum = dl->illumMaps(MapNum).zoneIndex;
726 28 : std::string name = format("Zone={}", state.dataHeatBal->Zone(mapZoneNum).Name);
727 28 : int mapSpaceNum = dl->illumMaps(MapNum).spaceIndex;
728 28 : if (mapSpaceNum > 0) {
729 2 : name = format("Space={}", state.dataHeatBal->space(mapSpaceNum).Name);
730 : }
731 28 : if (state.dataGlobal->WarmupFlag) {
732 28 : DisplayString(state, format("Calculating Daylighting Coefficients (Map Points), {}", name));
733 : } else {
734 0 : DisplayString(state, format("Updating Daylighting Coefficients (Map Points), {}", name));
735 : }
736 28 : CalcDayltgCoeffsMapPoints(state, MapNum);
737 28 : }
738 : }
739 : }
740 554 : } // CalcDayltgCoeffsRefMapPoints()
741 :
742 3399 : void CalcDayltgCoeffsRefPoints(EnergyPlusData &state, int const daylightCtrlNum)
743 : {
744 :
745 : // SUBROUTINE INFORMATION:
746 : // AUTHOR Linda Lawrie
747 : // DATE WRITTEN April 2012
748 : // MODIFIED November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
749 :
750 : // PURPOSE OF THIS SUBROUTINE:
751 : // Provides calculations for Daylighting Coefficients for daylighting reference points
752 3399 : auto &dl = state.dataDayltg;
753 3399 : auto const &s_surf = state.dataSurface;
754 :
755 : // glare calculation (radians)
756 : int IConst; // Construction counter
757 : int ICtrl; // Window control counter
758 : int IWin; // Window counter
759 : int IWin2; // Secondary window counter (for TDD:DOME object, if exists)
760 : int InShelfSurf; // Inside daylighting shelf surface number
761 : WinShadingType ShType; // Window shading type
762 : int BlNum; // Window Blind Number
763 : int LSHCAL; // Interior shade calculation flag: 0=not yet
764 : // calculated, 1=already calculated
765 : int NWX; // Number of window elements in x direction for dayltg calc
766 : int NWY; // Number of window elements in y direction for dayltg calc
767 : int NWYlim; // For triangle, largest NWY for a given IX
768 : Real64 COSB; // Cosine of angle between window outward normal and ray from
769 : // reference point to window element
770 : Real64 PHRAY; // Altitude of ray from reference point to window element (radians)
771 : Real64 THRAY; // Azimuth of ray from reference point to window element (radians)
772 : Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
773 : Real64 TVISB; // Visible transmittance of window for COSB angle of incidence (times light well
774 : // efficiency, if appropriate)
775 : int ISunPos; // Sun position counter; used to avoid calculating various
776 : // quantities that do not depend on sun position.
777 : Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
778 : // from reference point through a window element
779 : bool is_Rectangle; // True if window is rectangular
780 : bool is_Triangle; // True if window is triangular
781 : Real64 DWX; // Horizontal dimension of window element (m)
782 : Real64 DWY; // Vertical dimension of window element (m)
783 : Real64 DAXY; // Area of window element
784 : Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
785 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
786 : int BRef;
787 : int ILB;
788 : bool hitIntObs; // True iff interior obstruction hit
789 : bool hitExtObs; // True iff ray from ref pt to ext win hits an exterior obstruction
790 : Real64 TVISIntWin; // Visible transmittance of int win at COSBIntWin for light from ext win
791 : Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
792 :
793 3399 : Vector3<Real64> W2;
794 3399 : Vector3<Real64> W3;
795 3399 : Vector3<Real64> W21;
796 3399 : Vector3<Real64> W23;
797 3399 : Vector3<Real64> RREF2;
798 3399 : Vector3<Real64> RWIN;
799 3399 : Vector3<Real64> RWIN2;
800 3399 : Vector3<Real64> Ray;
801 3399 : Vector3<Real64> WNORM2;
802 3399 : Vector3<Real64> VIEWVC;
803 3399 : Vector3<Real64> U2;
804 3399 : Vector3<Real64> U21;
805 3399 : Vector3<Real64> U23;
806 3399 : Vector3<Real64> VIEWVC2;
807 :
808 : int WinEl; // Current window element
809 :
810 3399 : if (dl->refFirstTime && (dl->maxControlRefPoints > 0)) {
811 65 : dl->RefErrIndex.allocate(dl->maxControlRefPoints, s_surf->TotSurfaces);
812 65 : dl->RefErrIndex = 0;
813 65 : dl->refFirstTime = false;
814 : }
815 :
816 3399 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
817 3399 : auto const &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
818 3399 : int zoneNum = thisDayltgCtrl.zoneIndex;
819 : // Azimuth of view vector in absolute coord sys
820 3399 : Real64 AZVIEW = (thisDayltgCtrl.ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth + state.dataHeatBal->BuildingAzimuth +
821 3399 : state.dataHeatBal->BuildingRotationAppendixG) *
822 3399 : Constant::DegToRad;
823 : // View vector components in absolute coord sys
824 3399 : VIEWVC = {std::sin(AZVIEW), std::cos(AZVIEW), 0.0};
825 :
826 8643 : for (auto &refPt : thisDayltgCtrl.refPts) {
827 5244 : refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
828 5244 : refPt.glareIndex = 0.0; // Glare index at reference points
829 21245 : for (auto &extWin : refPt.extWins) {
830 16001 : extWin.solidAng = extWin.solidAngWtd = 0.0;
831 16001 : extWin.lums[iLum_Illum] = extWin.lums[iLum_Back] = extWin.lums[iLum_Source] = {0.0, 0.0};
832 : }
833 : }
834 :
835 3399 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
836 3399 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
837 3399 : int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
838 3399 : int numRefPts = thisDayltgCtrl.TotalDaylRefPoints;
839 :
840 84975 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
841 81576 : auto &daylFacHr = thisDayltgCtrl.daylFac[iHr];
842 304872 : for (int iWin = 1; iWin <= numExtWins; ++iWin) {
843 607320 : for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
844 1152072 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
845 768048 : auto &daylFac = daylFacHr(iWin, iRefPt)[iWinCover];
846 3072192 : daylFac[iLum_Illum] = Illums();
847 3072192 : daylFac[iLum_Source] = Illums();
848 3072192 : daylFac[iLum_Back] = Illums();
849 : } // for (iSlatAng)
850 : } // for (iRefPt)
851 : } // for (iWin)
852 : } // for (iHr)
853 :
854 3399 : BRef = 0;
855 :
856 8643 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
857 5244 : auto const &refPt = thisDayltgCtrl.refPts(IL);
858 : // Reference point in absolute coordinate system
859 5244 : Vector3<Real64> RREF = refPt.absCoords;
860 :
861 : // -------------
862 : // ---------- WINDOW LOOP ----------
863 : // -------------
864 21245 : for (int loopwin = 1; loopwin <= thisEnclDaylight.NumOfDayltgExtWins; ++loopwin) {
865 :
866 16001 : FigureDayltgCoeffsAtPointsSetupForWindow(state,
867 : daylightCtrlNum,
868 : IL,
869 : loopwin,
870 : CalledFor::RefPoint,
871 : RREF,
872 : VIEWVC,
873 : IWin,
874 : IWin2,
875 : NWX,
876 : NWY,
877 : W2,
878 : W3,
879 : W21,
880 : W23,
881 : LSHCAL,
882 : InShelfSurf,
883 : ICtrl,
884 : ShType,
885 : BlNum,
886 : WNORM2,
887 : extWinType,
888 : IConst,
889 : RREF2,
890 : DWX,
891 : DWY,
892 : DAXY,
893 : U2,
894 : U23,
895 : U21,
896 : VIEWVC2,
897 : is_Rectangle,
898 : is_Triangle);
899 : // ---------------------
900 : // ---------- WINDOW ELEMENT LOOP ----------
901 : // ---------------------
902 :
903 16001 : WinEl = 0;
904 :
905 219843 : for (int IX = 1; IX <= NWX; ++IX) {
906 203842 : if (is_Rectangle) {
907 203842 : NWYlim = NWY;
908 0 : } else if (is_Triangle) {
909 0 : NWYlim = NWY - IX + 1;
910 : }
911 :
912 816445 : for (int IY = 1; IY <= NWYlim; ++IY) {
913 :
914 612603 : ++WinEl;
915 :
916 612603 : FigureDayltgCoeffsAtPointsForWindowElements(state,
917 : daylightCtrlNum,
918 : IL,
919 : loopwin,
920 : CalledFor::RefPoint,
921 : WinEl,
922 : IWin,
923 : IWin2,
924 : IX,
925 : IY,
926 : SkyObstructionMult,
927 : W2,
928 : W21,
929 : W23,
930 : RREF,
931 : NWYlim,
932 : VIEWVC2,
933 : DWX,
934 : DWY,
935 : DAXY,
936 : U2,
937 : U23,
938 : U21,
939 : RWIN,
940 : RWIN2,
941 : Ray,
942 : PHRAY,
943 : LSHCAL,
944 : COSB,
945 : ObTrans,
946 : TVISB,
947 : DOMEGA,
948 : THRAY,
949 : hitIntObs,
950 : hitExtObs,
951 : WNORM2,
952 : extWinType,
953 : IConst,
954 : RREF2,
955 : is_Triangle,
956 : TVISIntWin,
957 : TVISIntWinDisk);
958 :
959 : // -------------------
960 : // ---------- SUN POSITION LOOP ----------
961 : // -------------------
962 :
963 : // Sun position counter. Used to avoid calculating various quantities
964 : // that do not depend on sun position.
965 :
966 612603 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
967 612603 : ISunPos = 0;
968 15315075 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
969 :
970 14702472 : FigureDayltgCoeffsAtPointsForSunPosition(state,
971 : daylightCtrlNum,
972 : IL,
973 : IX,
974 : NWX,
975 : IY,
976 : NWYlim,
977 : WinEl,
978 : IWin,
979 : IWin2,
980 : IHR,
981 : ISunPos,
982 : SkyObstructionMult,
983 : RWIN2,
984 : Ray,
985 : PHRAY,
986 : LSHCAL,
987 : InShelfSurf,
988 : COSB,
989 : ObTrans,
990 : TVISB,
991 : DOMEGA,
992 : ICtrl,
993 : ShType,
994 : BlNum,
995 : THRAY,
996 : WNORM2,
997 : extWinType,
998 : IConst,
999 : AZVIEW,
1000 : RREF2,
1001 : hitIntObs,
1002 : hitExtObs,
1003 : CalledFor::RefPoint,
1004 : TVISIntWin,
1005 : TVISIntWinDisk);
1006 :
1007 : } // End of hourly sun position loop, IHR
1008 : } else { // timestep integrated
1009 0 : if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1010 0 : ISunPos = 0;
1011 0 : dl->MySunIsUpFlag = true;
1012 0 : } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1013 0 : ISunPos = 1;
1014 0 : } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1015 0 : dl->MySunIsUpFlag = false;
1016 0 : ISunPos = -1;
1017 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1018 0 : ISunPos = -1;
1019 : }
1020 :
1021 0 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1022 : daylightCtrlNum,
1023 : IL,
1024 : IX,
1025 : NWX,
1026 : IY,
1027 : NWYlim,
1028 : WinEl,
1029 : IWin,
1030 : IWin2,
1031 0 : state.dataGlobal->HourOfDay,
1032 : ISunPos,
1033 : SkyObstructionMult,
1034 : RWIN2,
1035 : Ray,
1036 : PHRAY,
1037 : LSHCAL,
1038 : InShelfSurf,
1039 : COSB,
1040 : ObTrans,
1041 : TVISB,
1042 : DOMEGA,
1043 : ICtrl,
1044 : ShType,
1045 : BlNum,
1046 : THRAY,
1047 : WNORM2,
1048 : extWinType,
1049 : IConst,
1050 : AZVIEW,
1051 : RREF2,
1052 : hitIntObs,
1053 : hitExtObs,
1054 : CalledFor::RefPoint,
1055 : TVISIntWin,
1056 : TVISIntWinDisk);
1057 : }
1058 :
1059 : } // End of window Y-element loop, IY
1060 : } // End of window X-element loop, IX
1061 :
1062 : // Loop again over hourly sun positions and calculate daylight factors by adding
1063 : // direct and inter-reflected illum components, then dividing by exterior horiz illum.
1064 : // Also calculate corresponding glare factors.
1065 :
1066 16001 : ILB = BRef + IL;
1067 :
1068 16001 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1069 16001 : ISunPos = 0;
1070 400025 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
1071 384024 : FigureRefPointDayltgFactorsToAddIllums(state, daylightCtrlNum, ILB, IHR, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
1072 :
1073 : } // End of sun position loop, IHR
1074 : } else {
1075 0 : if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1076 0 : ISunPos = 0;
1077 0 : dl->MySunIsUpFlag = true;
1078 0 : } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1079 0 : ISunPos = 1;
1080 0 : } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1081 0 : dl->MySunIsUpFlag = false;
1082 0 : ISunPos = -1;
1083 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1084 0 : ISunPos = -1;
1085 : }
1086 0 : FigureRefPointDayltgFactorsToAddIllums(
1087 0 : state, daylightCtrlNum, ILB, state.dataGlobal->HourOfDay, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
1088 : }
1089 : } // End of window loop, loopwin - IWin
1090 :
1091 5244 : } // End of reference point loop, IL
1092 3399 : }
1093 :
1094 28 : void CalcDayltgCoeffsMapPoints(EnergyPlusData &state, int const mapNum)
1095 : {
1096 :
1097 : // SUBROUTINE INFORMATION:
1098 : // AUTHOR Linda Lawrie
1099 : // DATE WRITTEN April 2012
1100 : // MODIFIED November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
1101 :
1102 : // PURPOSE OF THIS SUBROUTINE:
1103 : // Provides calculations for Daylighting Coefficients for map illuminance points
1104 :
1105 : // METHODOLOGY EMPLOYED:
1106 : // Was previously part of CalcDayltgCoeffsRefMapPoints -- broken out to all multiple
1107 : // maps per zone
1108 28 : auto &dl = state.dataDayltg;
1109 28 : auto const &s_surf = state.dataSurface;
1110 :
1111 : // In the following four variables, I=1 for clear sky, 2 for overcast.
1112 : int numRefPts; // Number of daylighting reference points in a zone
1113 : // glare calculation (radians)
1114 : int IConst; // Construction counter
1115 : int ICtrl; // Window control counter
1116 : int IWin; // Window counter
1117 : int IWin2; // Secondary window counter (for TDD:DOME object, if exists)
1118 : int InShelfSurf; // Inside daylighting shelf surface number
1119 : WinShadingType ShType; // Window shading type
1120 : int BlNum; // Window Blind Number
1121 : int LSHCAL; // Interior shade calculation flag: 0=not yet
1122 : // calculated, 1=already calculated
1123 : int NWX; // Number of window elements in x direction for dayltg calc
1124 : int NWY; // Number of window elements in y direction for dayltg calc
1125 : int NWYlim; // For triangle, largest NWY for a given IX
1126 : Real64 DWX; // Horizontal dimension of window element (m)
1127 : Real64 DWY; // Vertical dimension of window element (m)
1128 : Real64 COSB; // Cosine of angle between window outward normal and ray from
1129 : // reference point to window element
1130 : Real64 PHRAY; // Altitude of ray from reference point to window element (radians)
1131 : Real64 THRAY; // Azimuth of ray from reference point to window element (radians)
1132 : Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
1133 : Real64 TVISB; // Visible transmittance of window for COSB angle of incidence (times light well
1134 : // efficiency, if appropriate)
1135 : int ISunPos; // Sun position counter; used to avoid calculating various
1136 : // quantities that do not depend on sun position.
1137 : Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
1138 : // from reference point through a window element
1139 : bool is_Rectangle; // True if window is rectangular
1140 : bool is_Triangle; // True if window is triangular
1141 : Real64 DAXY; // Area of window element
1142 : Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
1143 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1144 : int ILB;
1145 : bool hitIntObs; // True iff interior obstruction hit
1146 : bool hitExtObs; // True iff ray from ref pt to ext win hits an exterior obstruction
1147 : Real64 TVISIntWin; // Visible transmittance of int win at COSBIntWin for light from ext win
1148 : Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
1149 : int WinEl; // window elements counter
1150 :
1151 28 : Vector3<Real64> W2;
1152 28 : Vector3<Real64> W3;
1153 28 : Vector3<Real64> W21;
1154 28 : Vector3<Real64> W23;
1155 28 : Vector3<Real64> RREF2;
1156 28 : Vector3<Real64> RWIN;
1157 28 : Vector3<Real64> RWIN2;
1158 28 : Vector3<Real64> Ray;
1159 28 : Vector3<Real64> WNORM2;
1160 28 : Vector3<Real64> VIEWVC;
1161 28 : Vector3<Real64> U2;
1162 28 : Vector3<Real64> U21;
1163 28 : Vector3<Real64> U23;
1164 28 : Vector3<Real64> VIEWVC2;
1165 :
1166 28 : if (dl->mapFirstTime && (int)dl->illumMaps.size() > 0) {
1167 6 : int IL = -999;
1168 15 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
1169 9 : IL = max(IL, dl->illumMaps(MapNum).TotalMapRefPoints);
1170 : }
1171 6 : dl->MapErrIndex.dimension(IL, s_surf->TotSurfaces, 0);
1172 6 : dl->mapFirstTime = false;
1173 : }
1174 :
1175 28 : auto &illumMap = dl->illumMaps(mapNum);
1176 28 : int enclNum = illumMap.enclIndex;
1177 28 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
1178 :
1179 : // Azimuth of view vector in absolute coord sys - set to zero here, because glare isn't calculated for map points
1180 : // but these are arguments to some of the functions that are shared with regular reference points, so initalize here.
1181 28 : Real64 AZVIEW = 0.0;
1182 : // View vector components in absolute coord sys
1183 28 : VIEWVC = {0.0, 0.0, 0.0};
1184 :
1185 28 : numRefPts = illumMap.TotalMapRefPoints;
1186 28 : int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
1187 :
1188 2278 : for (auto &refPt : illumMap.refPts) {
1189 2250 : refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
1190 15950 : for (int iExtWin = 1; iExtWin <= numExtWins; ++iExtWin) {
1191 13700 : refPt.winLums(iExtWin) = {0.0, 0.0};
1192 : }
1193 : }
1194 :
1195 28 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
1196 28 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
1197 :
1198 700 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
1199 672 : auto &daylFacHr = illumMap.daylFac[iHr];
1200 4224 : for (int iWin = 1; iWin <= numExtWins; ++iWin) {
1201 332352 : for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
1202 986400 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
1203 2630400 : daylFacHr(iWin, iRefPt)[iWinCover] = Illums();
1204 : }
1205 : }
1206 : }
1207 : }
1208 :
1209 2278 : for (int IL = 1; IL <= numRefPts; ++IL) {
1210 2250 : auto const &refPt = illumMap.refPts(IL);
1211 2250 : Vector3<Real64> RREF = refPt.absCoords;
1212 :
1213 : // -------------
1214 : // ---------- WINDOW LOOP ----------
1215 : // -------------
1216 :
1217 15950 : for (int loopwin = 1; loopwin <= numExtWins; ++loopwin) {
1218 :
1219 : // daylightingCtrlNum parameter is unused for map points
1220 13700 : FigureDayltgCoeffsAtPointsSetupForWindow(state,
1221 : 0,
1222 : IL,
1223 : loopwin,
1224 : CalledFor::MapPoint,
1225 : RREF,
1226 : VIEWVC,
1227 : IWin,
1228 : IWin2,
1229 : NWX,
1230 : NWY,
1231 : W2,
1232 : W3,
1233 : W21,
1234 : W23,
1235 : LSHCAL,
1236 : InShelfSurf,
1237 : ICtrl,
1238 : ShType,
1239 : BlNum,
1240 : WNORM2,
1241 : extWinType,
1242 : IConst,
1243 : RREF2,
1244 : DWX,
1245 : DWY,
1246 : DAXY,
1247 : U2,
1248 : U23,
1249 : U21,
1250 : VIEWVC2,
1251 : is_Rectangle,
1252 : is_Triangle,
1253 : mapNum);
1254 : // ---------------------
1255 : // ---------- WINDOW ELEMENT LOOP ----------
1256 : // ---------------------
1257 13700 : WinEl = 0;
1258 :
1259 90726 : for (int IX = 1; IX <= NWX; ++IX) {
1260 77026 : if (is_Rectangle) {
1261 77026 : NWYlim = NWY;
1262 0 : } else if (is_Triangle) {
1263 0 : NWYlim = NWY - IX + 1;
1264 : }
1265 :
1266 2309108 : for (int IY = 1; IY <= NWYlim; ++IY) {
1267 :
1268 2232082 : ++WinEl;
1269 :
1270 : // daylightingCtrlNum parameter is unused for map points
1271 2232082 : FigureDayltgCoeffsAtPointsForWindowElements(state,
1272 : 0,
1273 : IL,
1274 : loopwin,
1275 : CalledFor::MapPoint,
1276 : WinEl,
1277 : IWin,
1278 : IWin2,
1279 : IX,
1280 : IY,
1281 : SkyObstructionMult,
1282 : W2,
1283 : W21,
1284 : W23,
1285 : RREF,
1286 : NWYlim,
1287 : VIEWVC2,
1288 : DWX,
1289 : DWY,
1290 : DAXY,
1291 : U2,
1292 : U23,
1293 : U21,
1294 : RWIN,
1295 : RWIN2,
1296 : Ray,
1297 : PHRAY,
1298 : LSHCAL,
1299 : COSB,
1300 : ObTrans,
1301 : TVISB,
1302 : DOMEGA,
1303 : THRAY,
1304 : hitIntObs,
1305 : hitExtObs,
1306 : WNORM2,
1307 : extWinType,
1308 : IConst,
1309 : RREF2,
1310 : is_Triangle,
1311 : TVISIntWin,
1312 : TVISIntWinDisk,
1313 : mapNum);
1314 : // -------------------
1315 : // ---------- SUN POSITION LOOP ----------
1316 : // -------------------
1317 :
1318 : // Sun position counter. Used to avoid calculating various quantities
1319 : // that do not depend on sun position.
1320 2232082 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1321 2232082 : ISunPos = 0;
1322 55802050 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
1323 : // daylightingCtrlNum parameter is unused for map points
1324 53569968 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1325 : 0,
1326 : IL,
1327 : IX,
1328 : NWX,
1329 : IY,
1330 : NWYlim,
1331 : WinEl,
1332 : IWin,
1333 : IWin2,
1334 : IHR,
1335 : ISunPos,
1336 : SkyObstructionMult,
1337 : RWIN2,
1338 : Ray,
1339 : PHRAY,
1340 : LSHCAL,
1341 : InShelfSurf,
1342 : COSB,
1343 : ObTrans,
1344 : TVISB,
1345 : DOMEGA,
1346 : ICtrl,
1347 : ShType,
1348 : BlNum,
1349 : THRAY,
1350 : WNORM2,
1351 : extWinType,
1352 : IConst,
1353 : AZVIEW,
1354 : RREF2,
1355 : hitIntObs,
1356 : hitExtObs,
1357 : CalledFor::MapPoint,
1358 : TVISIntWin,
1359 : TVISIntWinDisk,
1360 : mapNum);
1361 : } // End of hourly sun position loop, IHR
1362 : } else {
1363 0 : if (state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1364 0 : ISunPos = 0;
1365 0 : dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = true;
1366 0 : } else if (state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1367 0 : ISunPos = 1;
1368 0 : } else if (!state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1369 0 : dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = false;
1370 0 : ISunPos = -1;
1371 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1372 0 : ISunPos = -1;
1373 : }
1374 : // daylightingCtrlNum parameter is unused for map points
1375 0 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1376 : 0,
1377 : IL,
1378 : IX,
1379 : NWX,
1380 : IY,
1381 : NWYlim,
1382 : WinEl,
1383 : IWin,
1384 : IWin2,
1385 0 : state.dataGlobal->HourOfDay,
1386 : ISunPos,
1387 : SkyObstructionMult,
1388 : RWIN2,
1389 : Ray,
1390 : PHRAY,
1391 : LSHCAL,
1392 : InShelfSurf,
1393 : COSB,
1394 : ObTrans,
1395 : TVISB,
1396 : DOMEGA,
1397 : ICtrl,
1398 : ShType,
1399 : BlNum,
1400 : THRAY,
1401 : WNORM2,
1402 : extWinType,
1403 : IConst,
1404 : AZVIEW,
1405 : RREF2,
1406 : hitIntObs,
1407 : hitExtObs,
1408 : CalledFor::MapPoint,
1409 : TVISIntWin,
1410 : TVISIntWinDisk,
1411 : mapNum);
1412 : }
1413 : } // End of window Y-element loop, IY
1414 : } // End of window X-element loop, IX
1415 :
1416 13700 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1417 : // Loop again over hourly sun positions and calculate daylight factors by adding
1418 : // direct and inter-reflected illum components, then dividing by exterior horiz illum.
1419 : // Also calculate corresponding glare factors.
1420 13700 : ILB = IL;
1421 342500 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
1422 328800 : FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, IHR, IWin, loopwin, ICtrl);
1423 : } // End of sun position loop, IHR
1424 : } else {
1425 0 : ILB = IL;
1426 0 : FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, state.dataGlobal->HourOfDay, IWin, loopwin, ICtrl);
1427 : }
1428 :
1429 : } // End of window loop, loopwin - IWin
1430 :
1431 2250 : } // End of reference point loop, IL
1432 28 : }
1433 :
1434 29701 : void FigureDayltgCoeffsAtPointsSetupForWindow(EnergyPlusData &state,
1435 : int const daylightCtrlNum, // zero if called for map points
1436 : int const iRefPoint,
1437 : int const loopwin,
1438 : CalledFor const CalledFrom, // indicate which type of routine called this routine
1439 : Vector3<Real64> const &RREF, // Location of a reference point in absolute coordinate system
1440 : Vector3<Real64> const &VIEWVC, // View vector in absolute coordinate system
1441 : int &IWin,
1442 : int &IWin2,
1443 : int &NWX,
1444 : int &NWY,
1445 : Vector3<Real64> &W2, // Second vertex of window
1446 : Vector3<Real64> &W3, // Third vertex of window
1447 : Vector3<Real64> &W21, // Vector from window vertex 2 to window vertex 1
1448 : Vector3<Real64> &W23, // Vector from window vertex 2 to window vertex 3
1449 : int &LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
1450 : int &InShelfSurf, // Inside daylighting shelf surface number
1451 : int &ICtrl, // Window control counter
1452 : WinShadingType &ShType, // Window shading type
1453 : int &BlNum, // Window blind number
1454 : Vector3<Real64> &WNORM2, // Unit vector normal to window
1455 : ExtWinType &extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1456 : int &IConst, // Construction counter
1457 : Vector3<Real64> &RREF2, // Location of virtual reference point in absolute coordinate system
1458 : Real64 &DWX, // Horizontal dimension of window element (m)
1459 : Real64 &DWY, // Vertical dimension of window element (m)
1460 : Real64 &DAXY, // Area of window element
1461 : Vector3<Real64> &U2, // Second vertex of window for TDD:DOME (if exists)
1462 : Vector3<Real64> &U23, // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
1463 : Vector3<Real64> &U21, // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
1464 : Vector3<Real64> &VIEWVC2, // Virtual view vector in absolute coordinate system
1465 : bool &is_Rectangle, // True if window is rectangular
1466 : bool &is_Triangle, // True if window is triangular
1467 : int const MapNum)
1468 : {
1469 : // SUBROUTINE INFORMATION:
1470 : // AUTHOR B. Griffith
1471 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
1472 :
1473 : // PURPOSE OF THIS SUBROUTINE:
1474 : // collect code to setup calculations for each window for daylighting coefficients
1475 :
1476 : // METHODOLOGY EMPLOYED:
1477 : // switch as need to serve both reference points and map points based on calledFrom
1478 29701 : auto &dl = state.dataDayltg;
1479 29701 : auto &s_surf = state.dataSurface;
1480 :
1481 : int ShelfNum; // Daylighting shelf object number
1482 : int NDIVX; // Number of window x divisions for daylighting calc
1483 : int NDIVY; // Number of window y divisions for daylighting calc
1484 : Real64 ALF; // Distance from reference point to window plane (m)
1485 : Real64 D1a; // Projection of vector from window origin to reference
1486 : // on window X axis (m)
1487 : Real64 D1b; // Projection of vector from window origin to reference
1488 : // on window Y axis (m)
1489 : Real64 SolidAngExtWin; // Approx. solid angle subtended by an ext. window wrt ref pt
1490 : Real64 SolidAngMinIntWin; // Approx. smallest solid angle subtended by an int. window wrt ref pt
1491 : Real64 SolidAngRatio; // Ratio of SolidAngExtWin and SolidAngMinIntWin
1492 : Real64 SinCornerAng; // For triangle, sine of corner angle of window element
1493 :
1494 29701 : int zoneNum = 0; // zone number
1495 29701 : int enclNum = 0; // enclosure number
1496 :
1497 29701 : Vector3<Real64> W1 = {0.0, 0.0, 0.0};
1498 29701 : Vector3<Real64> WC = {0.0, 0.0, 0.0};
1499 :
1500 29701 : if (CalledFrom == CalledFor::RefPoint) {
1501 16001 : auto &daylCtrl = dl->daylightControl(daylightCtrlNum);
1502 16001 : daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAng = 0.0;
1503 16001 : daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAngWtd = 0.0;
1504 16001 : zoneNum = daylCtrl.zoneIndex;
1505 16001 : enclNum = daylCtrl.enclIndex;
1506 13700 : } else if (CalledFrom == CalledFor::MapPoint) {
1507 13700 : assert(MapNum > 0);
1508 13700 : auto const &illumMap = dl->illumMaps(MapNum);
1509 13700 : zoneNum = illumMap.zoneIndex;
1510 13700 : enclNum = illumMap.enclIndex;
1511 : }
1512 29701 : IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
1513 :
1514 29701 : auto &surf = s_surf->Surface(IWin);
1515 29701 : auto &surfWin = s_surf->SurfaceWindow(IWin);
1516 :
1517 29701 : if (s_surf->Surface(surf.BaseSurf).SolarEnclIndex == enclNum) {
1518 29497 : extWinType = ExtWinType::InZone;
1519 : } else {
1520 204 : extWinType = ExtWinType::AdjZone;
1521 : }
1522 :
1523 29701 : IConst = s_surf->SurfActiveConstruction(IWin);
1524 :
1525 : // For thermochromic windows, the daylight and glare factors are calculated for a base window cosntruction
1526 : // at base TC layer temperature. During each time step calculations at DayltgInteriorIllum,
1527 : // DayltgInteriorMapIllum, and DayltgGlare, the daylight and glare factors are adjusted by the visible
1528 : // transmittance ratio = VT of actual TC window based on last hour TC layer temperature / VT of the base TC window
1529 29701 : if (state.dataConstruction->Construct(IConst).isTCWindow) {
1530 : // For thermochromic windows, use the base window construction at base temperature of the TC layer
1531 0 : IConst = state.dataConstruction->Construct(IConst).TCMasterConstrNum;
1532 : }
1533 :
1534 29701 : ICtrl = surf.activeWindowShadingControl;
1535 29701 : ShType = WinShadingType::NoShade; // 'NOSHADE'
1536 29701 : BlNum = 0;
1537 : // ScNum = 0; //Unused Set but never used
1538 29701 : if (surf.HasShadeControl) {
1539 12662 : ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
1540 : }
1541 29701 : if (ANY_BLIND(ShType)) {
1542 0 : BlNum = s_surf->surfShades(IWin).blind.matNum;
1543 : }
1544 : // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
1545 :
1546 29701 : ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
1547 29701 : if (ShelfNum > 0) {
1548 210 : InShelfSurf =
1549 210 : state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf; // Inside daylighting shelf present if > 0
1550 : } else {
1551 29491 : InShelfSurf = 0;
1552 : }
1553 :
1554 29701 : is_Rectangle = false;
1555 29701 : is_Triangle = false;
1556 29701 : if (surf.Sides == 3) {
1557 0 : is_Triangle = true;
1558 : }
1559 29701 : if (surf.Sides == 4) {
1560 29701 : is_Rectangle = true;
1561 : }
1562 :
1563 29701 : if (is_Rectangle) {
1564 : // Vertices of window (numbered counter-clockwise starting at upper left as viewed
1565 : // from inside of room). Assumes original vertices are numbered counter-clockwise from
1566 : // upper left as viewed from outside.
1567 29701 : W3 = surf.Vertex(2);
1568 29701 : W2 = surf.Vertex(3);
1569 29701 : W1 = surf.Vertex(4);
1570 0 : } else if (is_Triangle) {
1571 0 : W3 = surf.Vertex(2);
1572 0 : W2 = surf.Vertex(3);
1573 0 : W1 = surf.Vertex(1);
1574 : }
1575 :
1576 : // Shade/blind calculation flag
1577 29701 : LSHCAL = 0;
1578 :
1579 : // Visible transmittance at normal incidence
1580 29701 : s_surf->SurfWinVisTransSelected(IWin) = Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
1581 : // For windows with switchable glazing, ratio of visible transmittance at normal
1582 : // incidence for fully switched (dark) state to that of unswitched state
1583 29701 : s_surf->SurfWinVisTransRatio(IWin) = 1.0;
1584 29701 : if (ICtrl > 0) {
1585 12662 : if (ShType == WinShadingType::SwitchableGlazing) {
1586 12562 : int IConstShaded = surf.activeShadedConstruction; // Shaded construction counter
1587 12562 : s_surf->SurfWinVisTransRatio(IWin) =
1588 12562 : General::SafeDivide(Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef),
1589 12562 : Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef));
1590 : }
1591 : }
1592 :
1593 : // Unit vectors from window vertex 2 to 1 and 2 to 3,
1594 : // center point of window, and vector from ref pt to center of window
1595 29701 : W21 = W1 - W2;
1596 29701 : W23 = W3 - W2;
1597 29701 : Real64 HW = W21.magnitude();
1598 29701 : Real64 WW = W23.magnitude();
1599 29701 : if (is_Rectangle) {
1600 29701 : WC = W2 + (W23 + W21) / 2.0;
1601 0 : } else if (is_Triangle) {
1602 0 : WC = W2 + (W23 + W21) / 3.0;
1603 : }
1604 29701 : s_surf->SurfaceWindow(IWin).WinCenter = WC;
1605 29701 : Vector3<Real64> REFWC = WC - RREF;
1606 : // Unit vectors
1607 29701 : W21 /= HW;
1608 29701 : W23 /= WW;
1609 :
1610 : // Unit vector normal to window (pointing away from room)
1611 29701 : Vector3<Real64> WNORM = surf.lcsz;
1612 :
1613 : // Initialize number of window elements
1614 29701 : NDIVX = 40; // Does this mean that windows are split into 1,600 points for daylighting? WHYYYYYY?
1615 29701 : NDIVY = 40;
1616 :
1617 : // Distance from ref point to window plane
1618 29701 : ALF = std::abs(dot(WNORM, REFWC));
1619 29701 : if (CalledFrom == CalledFor::RefPoint) {
1620 : // Check if ref point to close to window due to input error (0.1524 m below is 0.5 ft)
1621 16001 : if (ALF < 0.1524 && extWinType == ExtWinType::InZone) {
1622 : // Ref pt is close to window plane. Get vector from window
1623 : // origin to projection of ref pt on window plane.
1624 0 : Vector3<Real64> W2REF = RREF + ALF * WNORM - W2;
1625 :
1626 0 : D1a = dot(W2REF, W23);
1627 0 : D1b = dot(W2REF, W21);
1628 :
1629 : // ! Error message if ref pt is too close to window.
1630 0 : if (D1a > 0.0 && D1b > 0.0 && D1b <= HW && D1a <= WW) {
1631 0 : ShowSevereError(
1632 : state,
1633 0 : format("CalcDaylightCoeffRefPoints: Daylighting calculation cannot be done for Daylighting:Controls={} because reference point "
1634 : "#{} is less than 0.15m (6\") from window plane {}",
1635 0 : dl->daylightControl(daylightCtrlNum).Name,
1636 : iRefPoint,
1637 0 : surf.Name));
1638 0 : ShowContinueError(state, format("Distance=[{:.5R}]. This is too close; check position of reference point.", ALF));
1639 0 : ShowFatalError(state, "Program terminates due to preceding condition.");
1640 : }
1641 16001 : } else if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
1642 0 : if (dl->RefErrIndex(iRefPoint, IWin) == 0) { // only show error message once
1643 0 : ShowWarningError(state,
1644 0 : format("CalcDaylightCoeffRefPoints: For Daylghting:Controls=\"{}\" External Window=\"{}\"in Zone=\"{}\" reference "
1645 : "point is less than 0.15m (6\") from window plane ",
1646 0 : dl->daylightControl(daylightCtrlNum).Name,
1647 0 : surf.Name,
1648 0 : state.dataHeatBal->Zone(surf.Zone).Name));
1649 0 : ShowContinueError(state,
1650 0 : format("Distance=[{:.1R} m] to ref point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Daylighting Calcs may result.",
1651 : ALF,
1652 0 : RREF.x,
1653 0 : RREF.y,
1654 0 : RREF.z));
1655 0 : dl->RefErrIndex(iRefPoint, IWin) = 1;
1656 : }
1657 : }
1658 13700 : } else if (CalledFrom == CalledFor::MapPoint) {
1659 13700 : if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
1660 0 : if (dl->MapErrIndex(iRefPoint, IWin) == 0) { // only show error message once
1661 0 : ShowWarningError(state,
1662 0 : format("CalcDaylightCoeffMapPoints: For Zone=\"{}\" External Window=\"{}\"in Zone=\"{}\" map point is less than "
1663 : "0.15m (6\") from window plane ",
1664 0 : state.dataHeatBal->Zone(zoneNum).Name,
1665 0 : surf.Name,
1666 0 : state.dataHeatBal->Zone(surf.Zone).Name));
1667 0 : ShowContinueError(
1668 : state,
1669 0 : format("Distance=[{:.1R} m] map point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Map Calcs may result.", ALF, RREF.x, RREF.y, RREF.z));
1670 0 : dl->MapErrIndex(iRefPoint, IWin) = 1;
1671 : }
1672 : }
1673 : }
1674 : // Number of window elements in X and Y for daylighting calculation
1675 29701 : if (ALF > 0.1524) {
1676 28421 : NDIVX = 1 + int(4.0 * WW / ALF);
1677 28421 : NDIVY = 1 + int(4.0 * HW / ALF);
1678 : }
1679 :
1680 29701 : if (extWinType == ExtWinType::AdjZone) {
1681 : // Adjust number of exterior window elements to give acceptable number of rays through
1682 : // interior windows in the zone (for accuracy of interior window daylighting calculation)
1683 204 : SolidAngExtWin = General::SafeDivide(((surf.Area + s_surf->SurfWinDividerArea(IWin)) / surf.Multiplier), pow_2(ALF));
1684 204 : SolidAngMinIntWin = dl->enclDaylight(enclNum).MinIntWinSolidAng;
1685 204 : SolidAngRatio = max(1.0, SolidAngExtWin / SolidAngMinIntWin);
1686 204 : NDIVX *= std::sqrt(SolidAngRatio);
1687 204 : NDIVY *= std::sqrt(SolidAngRatio);
1688 : }
1689 :
1690 29701 : NWX = min(40, NDIVX);
1691 29701 : NWY = min(40, NDIVY);
1692 :
1693 : // Discretization of triangle is simpler if NWX = NWY
1694 29701 : if (is_Triangle) {
1695 0 : NWX = max(NWX, NWY);
1696 0 : NWY = NWX;
1697 : }
1698 :
1699 : // Edge lengths of window elements
1700 29701 : DWX = WW / NWX;
1701 29701 : DWY = HW / NWY;
1702 :
1703 : // Azimuth and altitude of window normal
1704 29701 : surfWin.phi = std::asin(WNORM.z);
1705 29701 : surfWin.theta = (std::abs(WNORM.x) > 1.0e-5 || std::abs(WNORM.y) > 1.0e-5) ? std::atan2(WNORM.y, WNORM.x) : 0.0;
1706 :
1707 : // Recalculation of values for TDD:DOME
1708 29701 : if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
1709 :
1710 : // Look up the TDD:DOME object
1711 420 : int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
1712 420 : IWin2 = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
1713 :
1714 420 : auto &surf2 = s_surf->Surface(IWin2);
1715 420 : auto &surfWin2 = s_surf->SurfaceWindow(IWin2);
1716 :
1717 : // Calculate reference point coords relative to the diffuser coordinate system
1718 : // W21, W23, and WNORM are the unit vectors
1719 420 : Vector3<Real64> REFD = {dot(REFWC, W21), dot(REFWC, W23), dot(REFWC, WNORM)};
1720 :
1721 : // Calculate view vector coords relative to the diffuser coordinate system
1722 420 : Vector3<Real64> VIEWVD = {dot(VIEWVC, W21), dot(VIEWVC, W23), dot(VIEWVC, WNORM)};
1723 :
1724 420 : Vector3<Real64> U3 = surf2.Vertex(2);
1725 420 : U2 = surf2.Vertex(3);
1726 420 : Vector3<Real64> U1;
1727 :
1728 420 : if (surf2.Sides == 4) {
1729 : // Vertices of window (numbered counter-clockwise starting
1730 : // at upper left as viewed from inside of room)
1731 : // Assumes original vertices are numbered counter-clockwise from
1732 : // upper left as viewed from outside.
1733 420 : U3 = surf2.Vertex(2);
1734 420 : U2 = surf2.Vertex(3);
1735 420 : U1 = surf2.Vertex(4);
1736 0 : } else if (surf2.Sides == 3) {
1737 0 : U3 = surf2.Vertex(2);
1738 0 : U2 = surf2.Vertex(3);
1739 0 : U1 = surf2.Vertex(1);
1740 : }
1741 :
1742 : // Unit vectors from window vertex 2 to 1 and 2 to 3,
1743 : // center point of window, and vector from ref pt to center of window
1744 420 : U21 = U1 - U2;
1745 420 : U23 = U3 - U2;
1746 420 : HW = U21.magnitude();
1747 420 : WW = U23.magnitude();
1748 420 : if (surf2.Sides == 4) {
1749 420 : WC = U2 + (U23 + U21) / 2.0;
1750 0 : } else if (surf2.Sides == 3) {
1751 0 : WC = U2 + (U23 + U21) / 3.0;
1752 : }
1753 420 : s_surf->SurfaceWindow(IWin2).WinCenter = WC;
1754 : // Unit vectors
1755 420 : U21 /= HW;
1756 420 : U23 /= WW;
1757 :
1758 : // Unit vector normal to dome (pointing away from TDD)
1759 : // These are specific to the exterior.
1760 : // NOTE: Preserve WNORM for later in the code.
1761 420 : WNORM2 = cross(U21, U23).normalize();
1762 :
1763 : // Azimuth and altitude of dome normal
1764 : // These are specific to the exterior.
1765 420 : surfWin2.phi = std::asin(WNORM2.z);
1766 420 : surfWin2.theta = (std::abs(WNORM2.x) > 1.0e-5 || std::abs(WNORM2.y) > 1.0e-5) ? std::atan2(WNORM2.y, WNORM2.x) : 0.0;
1767 :
1768 : // Calculate new virtual reference point coords relative to dome coord system
1769 : // W21, W23, and WNORM2 are now the unit vectors for the dome coord system
1770 420 : REFWC = REFD.x * U21 + REFD.y * U23 + REFD.z * WNORM2;
1771 420 : RREF2 = WC - REFWC;
1772 :
1773 : // Calculate new virtual view vector coords relative to dome coord system
1774 420 : VIEWVC2 = VIEWVD.x * U21 + VIEWVD.y * U23 + VIEWVD.z * WNORM2;
1775 :
1776 : // Copy several values from the diffuser so that DayltgInterReflectedIllum works correctly
1777 : // These are specific to the interior.
1778 420 : surfWin2.rhoCeilingWall = surfWin.rhoCeilingWall;
1779 420 : surfWin2.rhoFloorWall = surfWin.rhoFloorWall;
1780 420 : surfWin2.fractionUpgoing = surfWin.fractionUpgoing;
1781 420 : surfWin2.glazedFrac = surfWin.glazedFrac;
1782 :
1783 420 : } else {
1784 : // This is not a TDD:DIFFUSER. Make sure nothing is messed up for a regular window.
1785 29281 : IWin2 = IWin;
1786 29281 : WNORM2 = WNORM;
1787 29281 : RREF2 = RREF;
1788 29281 : VIEWVC2 = VIEWVC;
1789 :
1790 29281 : U2 = W2;
1791 29281 : U21 = W21;
1792 29281 : U23 = W23;
1793 : }
1794 :
1795 : // Initialize bsdf daylighting coefficients here. Only one time initialization
1796 29701 : if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
1797 18 : if (!state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized) {
1798 18 : int NRefPts = 0;
1799 18 : if (CalledFrom == CalledFor::MapPoint) {
1800 0 : NRefPts = dl->illumMaps(MapNum).TotalMapRefPoints;
1801 18 : } else if (CalledFrom == CalledFor::RefPoint) {
1802 18 : NRefPts = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
1803 : }
1804 18 : InitializeCFSDaylighting(state, daylightCtrlNum, IWin, NWX, NWY, RREF, NRefPts, iRefPoint, CalledFrom, MapNum);
1805 : // if ((WinEl == (NWX * NWY)).and.(CalledFrom == CalledForMapPoint).and.(NRefPts == iRefPoint)) then
1806 18 : if ((CalledFrom == CalledFor::MapPoint) && (NRefPts == iRefPoint)) {
1807 0 : state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized = true;
1808 : }
1809 : }
1810 : }
1811 :
1812 29701 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
1813 29701 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
1814 :
1815 742525 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
1816 : // Initialize sky and sun components of direct illuminance (arrays EDIRSK, EDIRSU, EDIRSUdisk)
1817 : // and average window luminance (arrays AVWLSK, AVWLSU, AVWLSUdisk), at ref pt.
1818 2851296 : dl->dirIllum(iHr)[iWinCover_Bare] = dl->dirIllum(iHr)[iWinCover_Shaded] = Illums();
1819 2851296 : dl->avgWinLum(iHr)[iWinCover_Bare] = dl->avgWinLum(iHr)[iWinCover_Shaded] = Illums();
1820 : }
1821 :
1822 29701 : if (CalledFrom == CalledFor::RefPoint) {
1823 : // Initialize solid angle subtended by window wrt ref pt
1824 : // and solid angle weighted by glare position factor
1825 16001 : s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAng = 0.0;
1826 16001 : s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd = 0.0;
1827 : }
1828 : // Area of window element
1829 29701 : if (is_Rectangle) {
1830 29701 : DAXY = DWX * DWY;
1831 0 : } else if (is_Triangle) {
1832 0 : SinCornerAng = std::sqrt(1.0 - pow_2(dot(W21, W23)));
1833 0 : DAXY = DWX * DWY * SinCornerAng;
1834 : }
1835 29701 : }
1836 :
1837 2844685 : void FigureDayltgCoeffsAtPointsForWindowElements(
1838 : EnergyPlusData &state,
1839 : int const daylightCtrlNum, // Current daylighting control number (only used when called from RefPoint)
1840 : int const iRefPoint,
1841 : int const loopwin,
1842 : CalledFor const CalledFrom, // indicate which type of routine called this routine
1843 : int const WinEl, // Current window element number
1844 : int const IWin,
1845 : int const IWin2,
1846 : int const iXelement,
1847 : int const iYelement,
1848 : Real64 &SkyObstructionMult,
1849 : Vector3<Real64> const &W2, // Second vertex of window
1850 : Vector3<Real64> const &W21, // Vector from window vertex 2 to window vertex 1
1851 : Vector3<Real64> const &W23, // Vector from window vertex 2 to window vertex 3
1852 : Vector3<Real64> const &RREF, // Location of a reference point in absolute coordinate system
1853 : int const NWYlim, // For triangle, largest NWY for a given IX
1854 : Vector3<Real64> const &VIEWVC2, // Virtual view vector in absolute coordinate system
1855 : Real64 const DWX, // Horizontal dimension of window element (m)
1856 : Real64 const DWY, // Vertical dimension of window element (m)
1857 : Real64 const DAXY, // Area of window element
1858 : Vector3<Real64> const &U2, // Second vertex of window for TDD:DOME (if exists)
1859 : Vector3<Real64> const &U23, // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
1860 : Vector3<Real64> const &U21, // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
1861 : Vector3<Real64> &RWIN, // Center of a window element for TDD:DOME (if exists) in abs coord sys
1862 : Vector3<Real64> &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
1863 : Vector3<Real64> &Ray, // Unit vector along ray from reference point to window element
1864 : Real64 &PHRAY, // Altitude of ray from reference point to window element (radians)
1865 : int &LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
1866 : Real64 &COSB, // Cosine of angle between window outward normal and ray from reference point to window element
1867 : Real64 &ObTrans, // Product of solar transmittances of exterior obstructions hit by ray
1868 : Real64 &TVISB, // Visible transmittance of window for COSB angle of incidence (times light well
1869 : Real64 &DOMEGA, // Solid angle subtended by window element wrt reference point (steradians)
1870 : Real64 &THRAY, // Azimuth of ray from reference point to window element (radians)
1871 : bool &hitIntObs, // True iff interior obstruction hit
1872 : bool &hitExtObs, // True iff ray from ref pt to ext win hits an exterior obstruction
1873 : Vector3<Real64> const &WNORM2, // Unit vector normal to window
1874 : ExtWinType const extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1875 : int const IConst, // Construction counter
1876 : Vector3<Real64> const &RREF2, // Location of virtual reference point in absolute coordinate system
1877 : bool const is_Triangle,
1878 : Real64 &TVISIntWin, // Visible transmittance of int win at COSBIntWin for light from ext win
1879 : Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
1880 : int const MapNum)
1881 : {
1882 :
1883 : // SUBROUTINE INFORMATION:
1884 : // AUTHOR B. Griffith
1885 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
1886 :
1887 : // PURPOSE OF THIS SUBROUTINE:
1888 : // collect code to do calculations for each window element for daylighting coefficients
1889 :
1890 : // REFERENCES:
1891 : // switch as need to serve both reference points and map points based on calledFrom
1892 2844685 : auto &dl = state.dataDayltg;
1893 2844685 : auto &s_surf = state.dataSurface;
1894 :
1895 : Real64 RR; // Distance from ref point to intersection of view vector
1896 : // and plane normal to view vector and window element (m)
1897 : Real64 ASQ; // Square of distance from above intersection to window element (m2)
1898 : Real64 YD; // Vertical displacement of window element wrt ref point
1899 :
1900 : Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
1901 : // exterior window element or between ref pt and sun
1902 :
1903 : // Local complex fenestration variables
1904 : Real64 TransBeam; // Obstructions transmittance for incoming BSDF rays (temporary variable)
1905 :
1906 2844685 : auto &surfWin = s_surf->SurfaceWindow(IWin);
1907 :
1908 2844685 : ++LSHCAL;
1909 2844685 : SkyObstructionMult = 1.0;
1910 :
1911 : // Center of win element in absolute coord sys
1912 2844685 : RWIN = W2 + (double(iXelement) - 0.5) * W23 * DWX + (double(iYelement) - 0.5) * W21 * DWY;
1913 :
1914 : // Center of win element on TDD:DOME in absolute coord sys
1915 : // If no TDD, RWIN2 = RWIN
1916 2844685 : RWIN2 = U2 + (double(iXelement) - 0.5) * U23 * DWX + (double(iYelement) - 0.5) * U21 * DWY;
1917 :
1918 : // Distance between ref pt and window element
1919 2844685 : Real64 DIS = distance(RWIN, RREF);
1920 :
1921 : // Unit vector along ray from ref pt to element
1922 2844685 : Ray = (RWIN - RREF) / DIS;
1923 :
1924 : // Cosine of angle between ray and window outward normal
1925 2844685 : COSB = dot(WNORM2, Ray);
1926 :
1927 : // If COSB > 0, direct light from window can reach ref pt. Otherwise go to loop
1928 : // over sun position and calculate inter-reflected component of illuminance
1929 2844685 : if (COSB <= 0.0) {
1930 0 : return;
1931 : }
1932 :
1933 : // Azimuth (-pi to pi) and altitude (-pi/2 to pi/2) of ray. Azimuth = 0 is along east.
1934 2844685 : PHRAY = std::asin(Ray.z);
1935 2844685 : if (std::abs(Ray.x) > 1.0e-5 || std::abs(Ray.y) > 1.0e-5) {
1936 2844680 : THRAY = std::atan2(Ray.y, Ray.x);
1937 : } else {
1938 5 : THRAY = 0.0;
1939 : }
1940 :
1941 : // Solid angle subtended by element wrt ref pt.
1942 2844685 : Real64 DAXY1 = DAXY; // For triangle, area of window element at end of column
1943 : // For triangle, at end of Y column only one half of parallelopiped's area contributes
1944 2844685 : if (is_Triangle && iYelement == NWYlim) {
1945 0 : DAXY1 = 0.5 * DAXY;
1946 : }
1947 2844685 : DOMEGA = DAXY1 * COSB / (DIS * DIS);
1948 :
1949 : // Calculate position factor (used in glare calculation) for this
1950 : // win element / ref pt / view-vector combination
1951 2844685 : Real64 POSFAC = 0.0;
1952 :
1953 : // Distance from ref pt to intersection of view vector and plane
1954 : // normal to view vector containing the window element
1955 :
1956 2844685 : if (CalledFrom == CalledFor::RefPoint) {
1957 612603 : RR = DIS * dot(Ray, VIEWVC2);
1958 612603 : if (RR > 0.0) {
1959 : // Square of distance from above intersection point to win element
1960 545687 : ASQ = DIS * DIS - RR * RR;
1961 : // Vertical displacement of win element wrt ref pt
1962 545687 : YD = RWIN2.z - RREF2.z;
1963 : // Horizontal and vertical displacement ratio and position factor
1964 545687 : Real64 XR = std::sqrt(std::abs(ASQ - YD * YD)) / RR;
1965 545687 : Real64 YR = std::abs(YD / RR);
1966 545687 : POSFAC = DayltgGlarePositionFactor(XR, YR);
1967 : }
1968 : }
1969 :
1970 2844685 : hitIntObs = false;
1971 2844685 : int IntWinHitNum = 0; // Surface number of interior window that is intersected
1972 2844685 : bool hitIntWin = false; // Ray from ref pt passes through interior window
1973 2844685 : TVISIntWinDisk = 0.0; // Init Value
1974 2844685 : TVISIntWin = 0.0;
1975 :
1976 2844685 : Vector3<Real64> HitPtIntWin = {0.0, 0.0, 0.0};
1977 2844685 : auto const &surf = s_surf->Surface(IWin);
1978 2844685 : if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
1979 : // Look up the TDD:DOME object
1980 420 : int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
1981 : // Unshaded visible transmittance of TDD for a single ray from sky/ground element
1982 420 : TVISB = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
1983 :
1984 : } else { // Regular window
1985 2844265 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
1986 : // Vis trans of glass for COSB incidence angle
1987 2844121 : TVISB = Window::POLYF(COSB, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
1988 : } else {
1989 : // Complex fenestration needs to use different equation for visible transmittance. That will be calculated later
1990 : // in the code since it depends on different incoming directions. For now, just put zero to differentiate from
1991 : // regular windows
1992 144 : TVISB = 0.0;
1993 : }
1994 2844265 : if (extWinType == ExtWinType::AdjZone) {
1995 23680 : int zoneNum = 0;
1996 23680 : if (CalledFrom == CalledFor::RefPoint) {
1997 40 : zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
1998 23640 : } else if (CalledFrom == CalledFor::MapPoint) {
1999 23640 : assert(MapNum > 0);
2000 23640 : zoneNum = dl->illumMaps(MapNum).zoneIndex;
2001 : }
2002 : // Does ray pass through an interior window in zone (ZoneNum) containing the ref point?
2003 47360 : for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
2004 23680 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
2005 26948 : for (int IntWin = thisSpace.WindowSurfaceFirst; IntWin <= thisSpace.WindowSurfaceLast; ++IntWin) {
2006 23680 : auto const &surfIntWin = s_surf->Surface(IntWin);
2007 : // in develop this was Surface(IntWin).Class == SurfaceClass::Window && Surface(IntWin).ExtBoundCond >= 1
2008 23680 : if (surfIntWin.ExtBoundCond < 1) {
2009 0 : continue;
2010 : }
2011 :
2012 23680 : if (s_surf->Surface(surfIntWin.ExtBoundCond).Zone != surf.Zone) {
2013 0 : continue;
2014 : }
2015 :
2016 23680 : hitIntWin = PierceSurface(state, IntWin, RREF, Ray, HitPtIntWin);
2017 23680 : if (hitIntWin) {
2018 20412 : IntWinHitNum = IntWin;
2019 20412 : COSBIntWin = dot(surfIntWin.OutNormVec, Ray);
2020 20412 : if (COSBIntWin <= 0.0) {
2021 0 : hitIntWin = false;
2022 0 : IntWinHitNum = 0;
2023 0 : continue;
2024 : }
2025 20412 : TVISIntWin = Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWin.Construction).TransVisBeamCoef);
2026 20412 : TVISB *= TVISIntWin;
2027 20412 : break; // Ray passes thru interior window; exit from DO loop
2028 : }
2029 : }
2030 23680 : } // End of loop over surfaces in zone ZoneNum
2031 :
2032 23680 : if (!hitIntWin) {
2033 : // Ray does not pass through an int win in ZoneNum. Therefore, it hits the opaque part
2034 : // of a surface between ref point in ZoneNum and ext win element in adjacent zone.
2035 3268 : hitIntObs = true;
2036 : }
2037 : } // End of check if this is an ext win in an adjacent zone
2038 : } // End of check if TDD:Diffuser or regular exterior window or complex fenestration
2039 :
2040 : // Check for interior obstructions
2041 2844685 : if (extWinType == ExtWinType::InZone && !hitIntObs) {
2042 : // Check for obstruction between reference point and window element
2043 : // Returns hitIntObs = true iff obstruction is hit
2044 : // (Example of interior obstruction is a wall in an L-shaped room that lies
2045 : // between reference point and window.)
2046 2821005 : hitIntObs = DayltgHitInteriorObstruction(state, IWin, RREF, RWIN);
2047 : }
2048 :
2049 2844685 : if (extWinType == ExtWinType::AdjZone && IntWinHitNum > 0 && !hitIntObs) {
2050 : // Check for obstruction between ref point and interior window through which ray passes
2051 20412 : hitIntObs = DayltgHitInteriorObstruction(state, IntWinHitNum, RREF, HitPtIntWin);
2052 20412 : if (!hitIntObs) {
2053 : // Check for obstruction between intersection point on int window and ext win element
2054 20412 : hitIntObs = DayltgHitBetWinObstruction(state, IntWinHitNum, IWin, HitPtIntWin, RWIN);
2055 : }
2056 : }
2057 2844685 : if (CalledFrom == CalledFor::RefPoint) {
2058 : // Glare calculations only done for regular reference points, not for maps
2059 612603 : if (!hitIntObs) {
2060 612533 : if (extWinType == ExtWinType::InZone || (extWinType == ExtWinType::AdjZone && hitIntWin)) {
2061 : // Increment solid angle subtended by portion of window above ref pt
2062 612533 : surfWin.refPts(iRefPoint).solidAng += DOMEGA;
2063 612533 : dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAng += DOMEGA;
2064 : // Increment position-factor-modified solid angle
2065 612533 : surfWin.refPts(iRefPoint).solidAngWtd += DOMEGA * POSFAC;
2066 612533 : dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAngWtd += DOMEGA * POSFAC;
2067 : }
2068 : }
2069 : }
2070 2844685 : if (hitIntObs) {
2071 34586 : ObTrans = 0.0;
2072 : }
2073 :
2074 2844685 : hitExtObs = false;
2075 2844685 : if (!hitIntObs) {
2076 : // No interior obstruction was hit.
2077 : // Check for exterior obstructions between window element and sky/ground.
2078 : // Get product of transmittances of obstructions hit by ray.
2079 : // ObTrans = 1.0 will be returned if no exterior obstructions are hit.
2080 :
2081 2810099 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
2082 : // the IHR (now HourOfDay) here is/was not correct, this is outside of hour loop
2083 : // the hour is used to query schedule for transmission , not sure what to do
2084 : // it will work for detailed and never did work correctly before.
2085 2809955 : ObTrans = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin2, RWIN2, Ray);
2086 2809955 : if (ObTrans < 1.0) {
2087 86915 : hitExtObs = true;
2088 : }
2089 : } else {
2090 : // Transmittance from exterior obstruction surfaces is calculated here. This needs to be done for each timestep
2091 : // in order to account for changes in exterior surface transmittances
2092 144 : int CplxFenState = surfWin.ComplexFen.CurrentState;
2093 144 : auto &complexWinDayltgGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CplxFenState);
2094 144 : int NReflSurf = 0; // Number of blocked beams for complex fenestration
2095 144 : if (CalledFrom == CalledFor::RefPoint) {
2096 144 : NReflSurf = complexWinDayltgGeom.RefPoint(iRefPoint).NReflSurf(WinEl);
2097 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2098 0 : NReflSurf = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).NReflSurf(WinEl);
2099 : }
2100 : int RayIndex;
2101 144 : for (int ICplxFen = 1; ICplxFen <= NReflSurf; ++ICplxFen) {
2102 0 : if (CalledFrom == CalledFor::RefPoint) {
2103 0 : RayIndex = complexWinDayltgGeom.RefPoint(iRefPoint).RefSurfIndex(ICplxFen, WinEl);
2104 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2105 0 : RayIndex = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefSurfIndex(ICplxFen, WinEl);
2106 : }
2107 0 : Vector3<Real64> RayVector = state.dataBSDFWindow->ComplexWind(IWin).Geom(CplxFenState).sInc(RayIndex);
2108 : // It will get product of all transmittances
2109 0 : TransBeam = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin, RWIN, RayVector);
2110 : // IF (TransBeam > 0.0d0) ObTrans = TransBeam
2111 0 : if (CalledFrom == CalledFor::RefPoint) {
2112 0 : complexWinDayltgGeom.RefPoint(iRefPoint).TransOutSurf(ICplxFen, WinEl) = TransBeam;
2113 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2114 0 : complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).TransOutSurf(ICplxFen, WinEl) = TransBeam;
2115 : }
2116 0 : }
2117 : // This will avoid obstruction multiplier calculations for non-CFS window
2118 144 : ObTrans = 0.0;
2119 : }
2120 : }
2121 :
2122 2844685 : if (s_surf->CalcSolRefl && PHRAY < 0.0 && ObTrans > 1.0e-6) {
2123 : // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
2124 : // by the ray. This effect is given by the ratio SkyObstructionMult =
2125 : // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
2126 : // This ratio is calculated for an isotropic sky.
2127 : // Ground point hit by the ray:
2128 220 : Real64 Alfa = std::acos(-Ray.z);
2129 220 : Real64 Beta = std::atan2(Ray.y, Ray.x);
2130 : // Distance between ground hit point and proj'n of center of window element onto ground (m)
2131 220 : Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
2132 220 : Vector3<Real64> GroundHitPt = {RWIN2.x + HorDis * std::cos(Beta), RWIN2.y + HorDis * std::sin(Beta), s_surf->GroundLevelZ};
2133 :
2134 220 : SkyObstructionMult =
2135 220 : CalcObstrMultiplier(state, GroundHitPt, DataSurfaces::AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
2136 220 : } // End of check if solar reflection calculation is in effect
2137 2844685 : } // FigureDayltgCoeffsAtPointsForWindowElements()
2138 :
2139 18 : void InitializeCFSDaylighting(EnergyPlusData &state,
2140 : int const daylightCtrlNum, // Current daylighting control number
2141 : int const IWin, // Complex fenestration number
2142 : int const NWX, // Number of horizontal divisions
2143 : int const NWY, // Number of vertical divisions
2144 : Vector3<Real64> const &RefPoint, // reference point coordinates
2145 : int const NRefPts, // Number of reference points
2146 : int const iRefPoint, // Reference points counter
2147 : CalledFor const CalledFrom,
2148 : int const MapNum)
2149 : {
2150 : // SUBROUTINE INFORMATION:
2151 : // AUTHOR Simon Vidanovic
2152 : // DATE WRITTEN April 2013
2153 :
2154 : // PURPOSE OF THIS SUBROUTINE:
2155 : // For incoming BSDF window direction calculates whether bin is coming from sky, ground or reflected surface.
2156 : // Routine also calculates intersection points with ground and exterior reflection surfaces.
2157 18 : auto &dl = state.dataDayltg;
2158 18 : auto &s_surf = state.dataSurface;
2159 :
2160 : // Object Data
2161 18 : DataBSDFWindow::BSDFDaylghtPosition elPos; // altitude and azimuth of intersection element
2162 18 : Vector Vec; // temporary vector variable
2163 :
2164 18 : int NumOfWinEl = NWX * NWY; // Number of window elements
2165 :
2166 18 : auto &surf = s_surf->Surface(IWin);
2167 18 : Real64 DWX = surf.Width / NWX; // Window element width
2168 18 : Real64 DWY = surf.Height / NWY; // Window element height
2169 :
2170 18 : int zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
2171 18 : Real64 AZVIEW = (dl->daylightControl(daylightCtrlNum).ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth +
2172 18 : state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) *
2173 18 : Constant::DegToRad;
2174 :
2175 : // Perform necessary calculations for window coordinates and vectors. This will be used to calculate centroids for
2176 : // each window element
2177 18 : Vector3<Real64> W1 = {0.0, 0.0, 0.0};
2178 18 : Vector3<Real64> W2 = {0.0, 0.0, 0.0};
2179 18 : Vector3<Real64> W3 = {0.0, 0.0, 0.0};
2180 :
2181 18 : if (surf.Sides == 4) {
2182 18 : W3 = surf.Vertex(2);
2183 18 : W2 = surf.Vertex(3);
2184 18 : W1 = surf.Vertex(4);
2185 0 : } else if (surf.Sides == 3) {
2186 0 : W3 = surf.Vertex(2);
2187 0 : W2 = surf.Vertex(3);
2188 0 : W1 = surf.Vertex(1);
2189 : }
2190 :
2191 18 : Vector3<Real64> W21 = W1 - W2;
2192 18 : W21 /= surf.Height;
2193 18 : Vector3<Real64> W23 = W3 - W2;
2194 18 : W23 /= surf.Width;
2195 18 : Vector3<Real64> WNorm = surf.lcsz;
2196 :
2197 18 : Real64 WinElArea = DWX * DWY;
2198 18 : if (surf.Sides == 3) {
2199 0 : WinElArea *= std::sqrt(1.0 - pow_2(dot(W21, W23)));
2200 : }
2201 :
2202 18 : auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
2203 :
2204 18 : if (CalledFrom == CalledFor::MapPoint) {
2205 :
2206 0 : if (!allocated(complexWin.IlluminanceMap)) {
2207 0 : complexWin.IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
2208 : }
2209 :
2210 0 : AllocateForCFSRefPointsGeometry(complexWin.IlluminanceMap(iRefPoint, MapNum), NumOfWinEl);
2211 :
2212 18 : } else if (CalledFrom == CalledFor::RefPoint) {
2213 18 : if (!allocated(complexWin.RefPoint)) {
2214 2 : complexWin.RefPoint.allocate(NRefPts);
2215 : }
2216 :
2217 18 : AllocateForCFSRefPointsGeometry(complexWin.RefPoint(iRefPoint), NumOfWinEl);
2218 : }
2219 :
2220 : //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2221 : //! Allocation for each complex fenestration state reference points
2222 : //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2223 18 : if (!allocated(complexWin.DaylghtGeom)) {
2224 2 : complexWin.DaylghtGeom.allocate(state.dataBSDFWindow->ComplexWind(IWin).NumStates);
2225 : }
2226 :
2227 : // Calculation needs to be performed for each state
2228 36 : for (int CurFenState = 1; CurFenState <= complexWin.NumStates; ++CurFenState) {
2229 : // number of incident basis directions for current state
2230 18 : int NBasis = complexWin.Geom(CurFenState).Inc.NBasis;
2231 : // number of outgoing basis directions for current state
2232 18 : int NTrnBasis = complexWin.Geom(CurFenState).Trn.NBasis;
2233 :
2234 18 : if (CalledFrom == CalledFor::MapPoint) {
2235 0 : if ((int)dl->illumMaps.size() > 0) {
2236 : // illuminance map for each state
2237 0 : if (!allocated(complexWin.DaylghtGeom(CurFenState).IlluminanceMap)) {
2238 0 : complexWin.DaylghtGeom(CurFenState).IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
2239 : }
2240 :
2241 0 : AllocateForCFSRefPointsState(
2242 0 : state, complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum), NumOfWinEl, NBasis, NTrnBasis);
2243 :
2244 0 : InitializeCFSStateData(state,
2245 0 : complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum),
2246 : complexWin.IlluminanceMap(iRefPoint, MapNum),
2247 : daylightCtrlNum,
2248 : IWin,
2249 : RefPoint,
2250 : CurFenState,
2251 : NBasis,
2252 : NTrnBasis,
2253 : AZVIEW,
2254 : NWX,
2255 : NWY,
2256 : W2,
2257 : W21,
2258 : W23,
2259 : DWX,
2260 : DWY,
2261 : WNorm,
2262 : WinElArea);
2263 : }
2264 :
2265 18 : } else if (CalledFrom == CalledFor::RefPoint) {
2266 18 : if (!allocated(complexWin.DaylghtGeom(CurFenState).RefPoint)) {
2267 2 : complexWin.DaylghtGeom(CurFenState).RefPoint.allocate(NRefPts);
2268 : }
2269 :
2270 18 : AllocateForCFSRefPointsState(state, complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint), NumOfWinEl, NBasis, NTrnBasis);
2271 :
2272 36 : InitializeCFSStateData(state,
2273 18 : complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint),
2274 : complexWin.RefPoint(iRefPoint),
2275 : daylightCtrlNum,
2276 : IWin,
2277 : RefPoint,
2278 : CurFenState,
2279 : NBasis,
2280 : NTrnBasis,
2281 : AZVIEW,
2282 : NWX,
2283 : NWY,
2284 : W2,
2285 : W21,
2286 : W23,
2287 : DWX,
2288 : DWY,
2289 : WNorm,
2290 : WinElArea);
2291 : }
2292 : }
2293 18 : } // InitializeCFSDaylighting()
2294 :
2295 18 : void InitializeCFSStateData(EnergyPlusData &state,
2296 : DataBSDFWindow::BSDFRefPoints &StateRefPoint,
2297 : DataBSDFWindow::BSDFRefPointsGeomDescr &DaylghtGeomDescr,
2298 : [[maybe_unused]] int const daylightCtrlNum, // Current daylighting control number
2299 : int const iWin,
2300 : Vector3<Real64> const &RefPoint, // reference point
2301 : int const CurFenState,
2302 : int const NBasis,
2303 : int const NTrnBasis,
2304 : Real64 const AZVIEW,
2305 : int const NWX,
2306 : int const NWY,
2307 : Vector3<Real64> const &W2,
2308 : Vector3<Real64> const &W21,
2309 : Vector3<Real64> const &W23,
2310 : Real64 const DWX,
2311 : Real64 const DWY,
2312 : Vector3<Real64> const &WNorm, // unit vector from window (point towards outside)
2313 : Real64 const WinElArea)
2314 : {
2315 : // SUBROUTINE INFORMATION:
2316 : // AUTHOR Simon Vidanovic
2317 : // DATE WRITTEN June 2013
2318 :
2319 : // PURPOSE OF THIS SUBROUTINE:
2320 : // Initialize daylight state data for current
2321 18 : auto &s_surf = state.dataSurface;
2322 :
2323 : // SUBROUTINE LOCAL VARIABLES
2324 : int curWinEl;
2325 : bool hit;
2326 : int TotHits;
2327 : Real64 DotProd; // Temporary variable for manipulating dot product .dot.
2328 : int NSky;
2329 : int NGnd;
2330 : int NReflSurf;
2331 : int MaxTotHits;
2332 : Real64 LeastHitDsq; // dist^2 from window element center to hit point
2333 : Real64 HitDsq;
2334 : Real64 TransRSurf;
2335 : int J;
2336 :
2337 18 : Vector3<Real64> RWin;
2338 18 : Vector3<Real64> V;
2339 18 : Vector3<Real64> GroundHitPt;
2340 :
2341 : // temporary arrays for surfaces
2342 : // Each complex fenestration state can have different number of basis elements
2343 : // This is the reason for making these temporary arrays local
2344 18 : Array1D_int TmpSkyInd(NBasis, 0); // Temporary sky index list
2345 18 : Array1D_int TmpGndInd(NBasis, 0); // Temporary gnd index list
2346 18 : Array1D<Real64> TmpGndMultiplier(NBasis, 0.0); // Temporary ground obstruction multiplier
2347 18 : Array1D_int TmpRfSfInd(NBasis, 0); // Temporary RefSurfIndex
2348 18 : Array1D_int TmpRfRyNH(NBasis, 0); // Temporary RefRayNHits
2349 18 : Array2D_int TmpHSurfNo(s_surf->TotSurfaces, NBasis, 0); // Temporary HitSurfNo
2350 18 : Array2D<Real64> TmpHSurfDSq(s_surf->TotSurfaces, NBasis, 0.0); // Temporary HitSurfDSq
2351 :
2352 : // Object Data
2353 18 : Vector3<Real64> Centroid; // current window element centroid
2354 18 : Vector3<Real64> HitPt; // surface hit point
2355 18 : Array1D<Vector3<Real64>> TmpGndPt(NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary ground intersection list
2356 18 : Array2D<Vector3<Real64>> TmpHitPt(s_surf->TotSurfaces, NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary HitPt
2357 :
2358 18 : CFSRefPointPosFactor(state, RefPoint, StateRefPoint, iWin, CurFenState, NTrnBasis, AZVIEW);
2359 :
2360 18 : auto const &surf = s_surf->Surface(iWin);
2361 :
2362 18 : curWinEl = 0;
2363 : // loop through window elements. This will calculate sky, ground and reflection bins for each window element
2364 54 : for (int IX = 1; IX <= NWX; ++IX) {
2365 180 : for (int IY = 1; IY <= NWY; ++IY) {
2366 :
2367 144 : ++curWinEl;
2368 :
2369 : // centroid coordinates for current window element
2370 144 : Centroid = W2 + (double(IX) - 0.5) * W23 * DWX + (double(IY) - 0.5) * W21 * DWY;
2371 144 : RWin = Centroid;
2372 :
2373 144 : CFSRefPointSolidAngle(state, RefPoint, RWin, WNorm, StateRefPoint, DaylghtGeomDescr, iWin, CurFenState, NTrnBasis, curWinEl, WinElArea);
2374 :
2375 144 : NSky = 0;
2376 144 : NGnd = 0;
2377 144 : NReflSurf = 0;
2378 144 : MaxTotHits = 0;
2379 : // Calculation of potential surface obstruction for each incoming direction
2380 21024 : for (int IRay = 1; IRay <= NBasis; ++IRay) {
2381 :
2382 20880 : hit = false;
2383 20880 : TotHits = 0;
2384 187920 : for (int JSurf = 1; JSurf <= s_surf->TotSurfaces; ++JSurf) {
2385 167040 : auto &surf2 = s_surf->Surface(JSurf);
2386 :
2387 : // the following test will cycle on anything except exterior surfaces and shading surfaces
2388 167040 : if (surf2.HeatTransSurf && surf2.ExtBoundCond != ExternalEnvironment) {
2389 125280 : continue;
2390 : }
2391 : // skip the base surface containing the window and any other subsurfaces of that surface
2392 41760 : if (JSurf == surf.BaseSurf || surf2.BaseSurf == surf.BaseSurf) {
2393 41760 : continue;
2394 : }
2395 : // skip surfaces that face away from the window
2396 0 : DotProd = dot(state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), surf2.NewellSurfaceNormalVector);
2397 0 : if (DotProd >= 0) {
2398 0 : continue;
2399 : }
2400 0 : hit = PierceSurface(state, JSurf, Centroid, state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), HitPt);
2401 0 : if (!hit) {
2402 0 : continue; // Miss: Try next surface
2403 : }
2404 0 : if (TotHits == 0) {
2405 : // First hit for this ray
2406 0 : TotHits = 1;
2407 0 : ++NReflSurf;
2408 0 : TmpRfSfInd(NReflSurf) = IRay;
2409 0 : TmpRfRyNH(NReflSurf) = 1;
2410 0 : TmpHSurfNo(1, NReflSurf) = JSurf;
2411 0 : TmpHitPt(1, NReflSurf) = HitPt;
2412 0 : V = HitPt - Centroid; // vector array from window ctr to hit pt
2413 0 : LeastHitDsq = V.magnitude_squared(); // dist^2 window ctr to hit pt
2414 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
2415 0 : if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
2416 0 : TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance, treat it here as completely transparent
2417 : } else {
2418 0 : TransRSurf = 0.0;
2419 : }
2420 : } else {
2421 0 : V = HitPt - Centroid;
2422 0 : HitDsq = V.magnitude_squared();
2423 0 : if (HitDsq >= LeastHitDsq) {
2424 0 : if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
2425 0 : J = TotHits + 1;
2426 0 : if (TotHits > 1) {
2427 0 : for (int I = 2; I <= TotHits; ++I) {
2428 0 : if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
2429 0 : J = I;
2430 0 : break;
2431 : }
2432 : }
2433 0 : if (!surf2.HeatTransSurf && surf2.shadowSurfSched == nullptr) {
2434 : // The new hit is opaque, so we can drop all the hits further away
2435 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
2436 0 : TmpHitPt(J, NReflSurf) = HitPt;
2437 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
2438 0 : TotHits = J;
2439 : } else {
2440 : // The new hit is scheduled (presumed transparent), so keep the more distant hits
2441 : // Note that all the hists in the list will be transparent except the last,
2442 : // which may be either transparent or opaque
2443 0 : if (TotHits >= J) {
2444 0 : for (int I = TotHits; I >= J; --I) {
2445 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
2446 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
2447 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
2448 : }
2449 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
2450 0 : TmpHitPt(J, NReflSurf) = HitPt;
2451 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
2452 0 : ++TotHits;
2453 : }
2454 : } // if (.NOT.Surface(JSurf)%HeatTransSurf .AND. Surface(JSurf)%SchedShadowSurfIndex == 0) then
2455 : } // if (TotHits > 1) then
2456 : } // if (TransRSurf > 0.0d0) then
2457 : } else { // if (HitDsq >= LeastHitDsq) then
2458 : // A new closest hit. If it is opaque, drop the current hit list,
2459 : // otherwise add it at the front
2460 0 : LeastHitDsq = HitDsq;
2461 0 : if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
2462 0 : TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
2463 0 : for (int I = TotHits; I >= 1; --I) {
2464 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
2465 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
2466 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
2467 0 : ++TotHits;
2468 : }
2469 0 : } else {
2470 0 : TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
2471 0 : TotHits = 1;
2472 : }
2473 0 : TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
2474 0 : TmpHitPt(1, NReflSurf) = HitPt;
2475 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
2476 : }
2477 : }
2478 : } // do JSurf = 1, TotSurfaces
2479 20880 : if (TotHits <= 0) {
2480 20880 : auto const &sIncRay = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay);
2481 : // This ray reached the sky or ground unobstructed
2482 20880 : if (sIncRay.z < 0.0) {
2483 : // A ground ray
2484 9216 : ++NGnd;
2485 9216 : TmpGndInd(NGnd) = IRay;
2486 9216 : TmpGndPt(NGnd).x = Centroid.x - (sIncRay.x / sIncRay.z) * Centroid.z;
2487 9216 : TmpGndPt(NGnd).y = Centroid.y - (sIncRay.y / sIncRay.z) * Centroid.z;
2488 9216 : TmpGndPt(NGnd).z = 0.0;
2489 :
2490 : // for solar reflectance calculations, need to precalculate obstruction multipliers
2491 9216 : if (s_surf->CalcSolRefl) {
2492 9216 : GroundHitPt = TmpGndPt(NGnd);
2493 9216 : TmpGndMultiplier(NGnd) =
2494 9216 : CalcObstrMultiplier(state, GroundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
2495 : }
2496 : } else {
2497 : // A sky ray
2498 11664 : ++NSky;
2499 11664 : TmpSkyInd(NSky) = IRay;
2500 : }
2501 : } else {
2502 : // Save the number of hits for this ray
2503 0 : TmpRfRyNH(NReflSurf) = TotHits;
2504 : }
2505 20880 : MaxTotHits = max(MaxTotHits, TotHits);
2506 : } // do IRay = 1, ComplexWind(IWin)%Geom(CurFenState)%Inc%NBasis
2507 :
2508 : // Fill up state data for current window element data
2509 144 : StateRefPoint.NSky(curWinEl) = NSky;
2510 144 : StateRefPoint.SkyIndex({1, NSky}, curWinEl) = TmpSkyInd({1, NSky});
2511 :
2512 144 : StateRefPoint.NGnd(curWinEl) = NGnd;
2513 144 : StateRefPoint.GndIndex({1, NGnd}, curWinEl) = TmpGndInd({1, NGnd});
2514 144 : StateRefPoint.GndPt({1, NGnd}, curWinEl) = TmpGndPt({1, NGnd});
2515 144 : StateRefPoint.GndObstrMultiplier({1, NGnd}, curWinEl) = TmpGndMultiplier({1, NGnd});
2516 :
2517 144 : StateRefPoint.NReflSurf(curWinEl) = NReflSurf;
2518 144 : StateRefPoint.RefSurfIndex({1, NReflSurf}, curWinEl) = TmpRfSfInd({1, NReflSurf});
2519 144 : StateRefPoint.RefRayNHits({1, NReflSurf}, curWinEl) = TmpRfRyNH({1, NReflSurf});
2520 144 : StateRefPoint.HitSurfNo({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfNo({1, MaxTotHits}, {1, NReflSurf});
2521 144 : StateRefPoint.HitSurfDSq({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfDSq({1, MaxTotHits}, {1, NReflSurf});
2522 144 : StateRefPoint.HitPt({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHitPt({1, MaxTotHits}, {1, NReflSurf});
2523 : } // do IY = 1, NWY
2524 : } // do IX = 1, NWX
2525 18 : }
2526 :
2527 18 : void AllocateForCFSRefPointsState(
2528 : [[maybe_unused]] EnergyPlusData &state, DataBSDFWindow::BSDFRefPoints &StateRefPoint, int const NumOfWinEl, int const NBasis, int const NTrnBasis)
2529 : {
2530 : // SUBROUTINE INFORMATION:
2531 : // AUTHOR Simon Vidanovic
2532 : // DATE WRITTEN June 2013
2533 :
2534 : // PURPOSE OF THIS SUBROUTINE:
2535 : // Memory allocation for complex fenestration systems reference points geometry
2536 18 : auto &s_surf = state.dataSurface;
2537 :
2538 18 : if (!allocated(StateRefPoint.NSky)) {
2539 2 : StateRefPoint.NSky.allocate(NumOfWinEl);
2540 2 : StateRefPoint.NSky = 0;
2541 : }
2542 :
2543 18 : if (!allocated(StateRefPoint.SkyIndex)) {
2544 2 : StateRefPoint.SkyIndex.allocate(NBasis, NumOfWinEl);
2545 2 : StateRefPoint.SkyIndex = 0;
2546 : }
2547 :
2548 18 : if (!allocated(StateRefPoint.NGnd)) {
2549 2 : StateRefPoint.NGnd.allocate(NumOfWinEl);
2550 2 : StateRefPoint.NGnd = 0;
2551 : }
2552 :
2553 18 : if (!allocated(StateRefPoint.GndIndex)) {
2554 2 : StateRefPoint.GndIndex.allocate(NBasis, NumOfWinEl);
2555 2 : StateRefPoint.GndIndex = 0;
2556 : }
2557 :
2558 18 : if (!allocated(StateRefPoint.GndPt)) {
2559 2 : StateRefPoint.GndPt.allocate(NBasis, NumOfWinEl);
2560 2 : StateRefPoint.GndPt = Vector(0.0, 0.0, 0.0);
2561 : }
2562 :
2563 18 : if (!allocated(StateRefPoint.GndObstrMultiplier)) {
2564 2 : StateRefPoint.GndObstrMultiplier.allocate(NBasis, NumOfWinEl);
2565 2 : StateRefPoint.GndObstrMultiplier = 0.0;
2566 : }
2567 :
2568 18 : if (!allocated(StateRefPoint.NReflSurf)) {
2569 2 : StateRefPoint.NReflSurf.allocate(NumOfWinEl);
2570 2 : StateRefPoint.NReflSurf = 0;
2571 : }
2572 :
2573 18 : if (!allocated(StateRefPoint.RefSurfIndex)) {
2574 2 : StateRefPoint.RefSurfIndex.allocate(NBasis, NumOfWinEl);
2575 2 : StateRefPoint.RefSurfIndex = 0;
2576 : }
2577 :
2578 18 : if (!allocated(StateRefPoint.TransOutSurf)) {
2579 2 : StateRefPoint.TransOutSurf.allocate(NBasis, NumOfWinEl);
2580 2 : StateRefPoint.TransOutSurf = 1.0;
2581 : }
2582 :
2583 18 : if (!allocated(StateRefPoint.RefRayNHits)) {
2584 2 : StateRefPoint.RefRayNHits.allocate(NBasis, NumOfWinEl);
2585 2 : StateRefPoint.RefRayNHits = 0;
2586 : }
2587 :
2588 18 : if (!allocated(StateRefPoint.HitSurfNo)) {
2589 2 : StateRefPoint.HitSurfNo.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
2590 2 : StateRefPoint.HitSurfNo = 0;
2591 : }
2592 :
2593 18 : if (!allocated(StateRefPoint.HitSurfDSq)) {
2594 2 : StateRefPoint.HitSurfDSq.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
2595 2 : StateRefPoint.HitSurfDSq = 0.0;
2596 : }
2597 :
2598 18 : if (!allocated(StateRefPoint.HitPt)) {
2599 2 : StateRefPoint.HitPt.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
2600 2 : StateRefPoint.HitPt = Vector(0.0, 0.0, 0.0);
2601 : }
2602 :
2603 18 : if (!allocated(StateRefPoint.RefPointIndex)) {
2604 2 : StateRefPoint.RefPointIndex.allocate(NumOfWinEl);
2605 2 : StateRefPoint.RefPointIndex = 0;
2606 : }
2607 :
2608 18 : if (!allocated(StateRefPoint.RefPointIntersection)) {
2609 2 : StateRefPoint.RefPointIntersection.allocate(NTrnBasis);
2610 2 : StateRefPoint.RefPointIntersection = false;
2611 : }
2612 :
2613 18 : if (!allocated(StateRefPoint.RefPtIntPosFac)) {
2614 2 : StateRefPoint.RefPtIntPosFac.allocate(NTrnBasis);
2615 2 : StateRefPoint.RefPtIntPosFac = 0.0;
2616 : }
2617 18 : }
2618 :
2619 18 : void AllocateForCFSRefPointsGeometry(DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointsGeomDescr, int const NumOfWinEl)
2620 : {
2621 : // SUBROUTINE INFORMATION:
2622 : // AUTHOR Simon Vidanovic
2623 : // DATE WRITTEN June 2013
2624 :
2625 : // PURPOSE OF THIS SUBROUTINE:
2626 : // Memory allocation for complex fenestration systems reference points geometry
2627 :
2628 : // SUBROUTINE LOCAL VARIABLES
2629 :
2630 18 : if (!allocated(RefPointsGeomDescr.SolidAngle)) {
2631 2 : RefPointsGeomDescr.SolidAngle.allocate(NumOfWinEl);
2632 2 : RefPointsGeomDescr.SolidAngle = 0.0;
2633 : }
2634 :
2635 18 : if (!allocated(RefPointsGeomDescr.SolidAngleVec)) {
2636 2 : RefPointsGeomDescr.SolidAngleVec.allocate(NumOfWinEl);
2637 2 : RefPointsGeomDescr.SolidAngleVec = Vector(0.0, 0.0, 0.0);
2638 : }
2639 18 : }
2640 :
2641 144 : void CFSRefPointSolidAngle(EnergyPlusData &state,
2642 : Vector3<Real64> const &RefPoint,
2643 : Vector3<Real64> const &RWin,
2644 : Vector3<Real64> const &WNorm,
2645 : DataBSDFWindow::BSDFRefPoints &RefPointMap,
2646 : DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointGeomMap,
2647 : int const iWin,
2648 : int const CurFenState,
2649 : int const NTrnBasis,
2650 : int const curWinEl,
2651 : Real64 const WinElArea)
2652 : {
2653 : // SUBROUTINE INFORMATION:
2654 : // AUTHOR Simon Vidanovic
2655 : // DATE WRITTEN June 2013
2656 :
2657 : // PURPOSE OF THIS SUBROUTINE:
2658 : // Calculate position factor for given reference point.
2659 :
2660 : // calculate vector from center of window element to the current reference point
2661 144 : Vector3<Real64> Ray = RefPoint - RWin;
2662 :
2663 : // figure out outgoing beam direction from current reference point
2664 144 : Real64 BestMatch = 0.0;
2665 21024 : for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
2666 20880 : Vector3<Real64> const &V = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn(iTrnRay);
2667 20880 : Real64 temp = dot(Ray, V);
2668 20880 : if (temp > BestMatch) {
2669 702 : BestMatch = temp;
2670 702 : RefPointMap.RefPointIndex(curWinEl) = iTrnRay;
2671 : }
2672 : }
2673 :
2674 : // calculate solid view angle
2675 144 : Real64 Dist = Ray.magnitude();
2676 144 : Vector3<Real64> RayNorm = Ray / (-Dist);
2677 144 : RefPointGeomMap.SolidAngleVec(curWinEl) = RayNorm;
2678 144 : Real64 CosB = dot(WNorm, RayNorm);
2679 144 : RefPointGeomMap.SolidAngle(curWinEl) = WinElArea * CosB / (Dist * Dist);
2680 144 : }
2681 :
2682 18 : void CFSRefPointPosFactor(EnergyPlusData &state,
2683 : Vector3<Real64> const &RefPoint,
2684 : DataBSDFWindow::BSDFRefPoints &RefPointMap,
2685 : int const iWin,
2686 : int const CurFenState,
2687 : int const NTrnBasis,
2688 : Real64 const AZVIEW)
2689 : {
2690 : // SUBROUTINE INFORMATION:
2691 : // AUTHOR Simon Vidanovic
2692 : // DATE WRITTEN June 2013
2693 :
2694 : // PURPOSE OF THIS SUBROUTINE:
2695 : // Calculate position factor for given reference point.
2696 :
2697 18 : auto const &sTrn = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn;
2698 2628 : for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
2699 2610 : Vector3<Real64> V = sTrn(iTrnRay);
2700 2610 : V.negate();
2701 :
2702 2610 : Vector3<Real64> InterPoint;
2703 :
2704 2610 : bool hit = PierceSurface(state, iWin, RefPoint, V, InterPoint);
2705 2610 : if (hit) {
2706 234 : RefPointMap.RefPointIntersection(iTrnRay) = true;
2707 :
2708 234 : DataBSDFWindow::BSDFDaylghtPosition elPos = WindowComplexManager::DaylghtAltAndAzimuth(V);
2709 :
2710 234 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - elPos.Azimuth) + 0.001);
2711 234 : Real64 YR = std::tan(elPos.Altitude + 0.001);
2712 234 : RefPointMap.RefPtIntPosFac(iTrnRay) = DayltgGlarePositionFactor(XR, YR);
2713 : }
2714 2610 : }
2715 18 : } // CFSRefPointPosFactor()
2716 :
2717 15516 : Real64 CalcObstrMultiplier(EnergyPlusData &state,
2718 : Vector3<Real64> const &GroundHitPt, // Coordinates of point that ray hits ground (m)
2719 : int const AltSteps, // Number of steps in altitude angle for solar reflection calc
2720 : int const AzimSteps // Number of steps in azimuth angle of solar reflection calc
2721 : )
2722 : {
2723 :
2724 : // SUBROUTINE INFORMATION:
2725 : // AUTHOR Simon Vidanovic
2726 : // DATE WRITTEN April 2013, refactor from legacy code by Fred Winklemann
2727 :
2728 : // PURPOSE OF THIS SUBROUTINE:
2729 : // collect code to do obstruction multiplier from ground point
2730 :
2731 : // METHODOLOGY EMPLOYED:
2732 : // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
2733 : // Divide hemisphere centered at ground hit point into elements of altitude Phi and
2734 : // azimuth Theta and create upward-going ground ray unit vector at each Phi,Theta pair.
2735 : // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
2736 :
2737 : // Locals
2738 15516 : auto const &dl = state.dataDayltg;
2739 15516 : auto const &s_surf = state.dataSurface;
2740 :
2741 : bool hitObs; // True iff obstruction is hit
2742 :
2743 15516 : Vector3<Real64> URay; // Unit vector in (Phi,Theta) direction
2744 15516 : Vector3<Real64> ObsHitPt; // Unit vector in (Phi,Theta) direction
2745 :
2746 15516 : assert(AzimSteps <= DataSurfaces::AzimAngStepsForSolReflCalc);
2747 :
2748 15516 : Real64 DPhi = Constant::PiOvr2 / (AltSteps / 2.0); // Phi increment (radians)
2749 15516 : Real64 DTheta = Constant::Pi / AzimSteps; // Theta increment (radians)
2750 :
2751 : // Tuned Precompute Phi trig table
2752 15516 : if (AltSteps != dl->AltSteps_last) {
2753 18 : for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
2754 15 : Real64 Phi = (IPhi - 0.5) * DPhi;
2755 15 : dl->cos_Phi[IPhi] = std::cos(Phi);
2756 15 : dl->sin_Phi[IPhi] = std::sin(Phi);
2757 : }
2758 3 : dl->AltSteps_last = AltSteps;
2759 : }
2760 : // Tuned Precompute Theta trig table
2761 15516 : if (AzimSteps != dl->AzimSteps_last) {
2762 57 : for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
2763 54 : Real64 Theta = (ITheta - 0.5) * DTheta;
2764 54 : dl->cos_Theta[ITheta] = std::cos(Theta);
2765 54 : dl->sin_Theta[ITheta] = std::sin(Theta);
2766 : }
2767 3 : dl->AzimSteps_last = AzimSteps;
2768 : }
2769 :
2770 15516 : Real64 SkyGndObs = 0.0; // Obstructed sky irradiance at a ground point
2771 15516 : Real64 SkyGndUnObs = 0.0; // Unobstructed sky irradiance at a ground point
2772 :
2773 : // Altitude loop
2774 93096 : for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
2775 77580 : Real64 sinPhi = dl->sin_Phi[IPhi]; // sinPhi
2776 77580 : Real64 cosPhi = dl->cos_Phi[IPhi]; // cosPhi
2777 :
2778 : // Third component of ground ray unit vector in (Theta,Phi) direction
2779 77580 : URay.z = sinPhi;
2780 77580 : Real64 dOmegaGnd = cosPhi * DTheta * DPhi; // Solid angle element of ray from ground point (steradians)
2781 : // Cosine of angle of incidence of ground ray on ground plane
2782 77580 : Real64 CosIncAngURay = sinPhi;
2783 77580 : Real64 IncAngSolidAngFac = CosIncAngURay * dOmegaGnd / Constant::Pi; // CosIncAngURay*dOmegaGnd/Pi
2784 : // Azimuth loop
2785 1474020 : for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
2786 1396440 : URay.x = cosPhi * dl->cos_Theta[ITheta];
2787 1396440 : URay.y = cosPhi * dl->sin_Theta[ITheta];
2788 1396440 : SkyGndUnObs += IncAngSolidAngFac;
2789 : // Does this ground ray hit an obstruction?
2790 1396440 : hitObs = false;
2791 1396440 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
2792 :
2793 3919492 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
2794 2876654 : hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
2795 2876654 : if (hitObs) {
2796 353602 : break;
2797 : }
2798 1396440 : }
2799 :
2800 : } else { // Surface octree search
2801 :
2802 : // Lambda function for the octree to test for surface hit
2803 0 : auto surfaceHit = [&GroundHitPt, &hitObs, &URay, &ObsHitPt](SurfaceData const &surface) -> bool {
2804 0 : if (surface.IsShadowPossibleObstruction) {
2805 0 : hitObs = PierceSurface(surface, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
2806 0 : return hitObs;
2807 : } else {
2808 0 : return false;
2809 : }
2810 0 : };
2811 :
2812 : // Check octree surface candidates until a hit is found, if any
2813 0 : Vector3<Real64> const URay_inv(SurfaceOctreeCube::safe_inverse(URay));
2814 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceRayIntersectsCube(GroundHitPt, URay, URay_inv, surfaceHit);
2815 0 : }
2816 :
2817 1396440 : if (hitObs) {
2818 353602 : continue; // Obstruction hit
2819 : }
2820 : // Sky is hit
2821 1042838 : SkyGndObs += IncAngSolidAngFac;
2822 : } // End of azimuth loop
2823 : } // End of altitude loop
2824 :
2825 : // in case ground point is surrounded by obstructions (SkyGndUnObs == 0), then multiplier will be equal to zero
2826 : // This should not happen anyway because in that case ray would not be able to reach ground point
2827 31032 : return (SkyGndUnObs != 0.0) ? (SkyGndObs / SkyGndUnObs) : 0.0;
2828 15516 : } // CalcObstrMultiplier()
2829 :
2830 68272440 : void FigureDayltgCoeffsAtPointsForSunPosition(
2831 : EnergyPlusData &state,
2832 : int const daylightCtrlNum, // Daylighting control index
2833 : int const iRefPoint,
2834 : int const iXelement,
2835 : int const NWX, // Number of window elements in x direction for dayltg calc
2836 : int const iYelement,
2837 : int const NWY, // Number of window elements in y direction for dayltg calc
2838 : int const WinEl, // Current window element counter
2839 : int const IWin,
2840 : int const IWin2,
2841 : int const iHour,
2842 : int &ISunPos,
2843 : Real64 const SkyObstructionMult,
2844 : Vector3<Real64> const &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
2845 : Vector3<Real64> const &Ray, // Unit vector along ray from reference point to window element
2846 : Real64 const PHRAY, // Altitude of ray from reference point to window element (radians)
2847 : int const LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
2848 : int const InShelfSurf, // Inside daylighting shelf surface number
2849 : Real64 const COSB, // Cosine of angle between window outward normal and ray from reference point to window element
2850 : Real64 const ObTrans, // Product of solar transmittances of exterior obstructions hit by ray from reference point through a window element
2851 : Real64 const TVISB, // Visible transmittance of window for COSB angle of incidence (times light well efficiency, if appropriate)
2852 : Real64 const DOMEGA, // Solid angle subtended by window element wrt reference point (steradians)
2853 : int const ICtrl, // Window control counter
2854 : WinShadingType const ShType, // Window shading type
2855 : [[maybe_unused]] int const BlNum, // Window blind number
2856 : Real64 const THRAY, // Azimuth of ray from reference point to window element (radians)
2857 : Vector3<Real64> const &WNORM2, // Unit vector normal to window
2858 : ExtWinType const extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
2859 : int const IConst, // Construction counter
2860 : Real64 const AZVIEW, // Azimuth of view vector in absolute coord system for glare calculation (radians)
2861 : Vector3<Real64> const &RREF2, // Location of virtual reference point in absolute coordinate system
2862 : bool const hitIntObs, // True iff interior obstruction hit
2863 : bool const hitExtObs, // True iff ray from ref pt to ext win hits an exterior obstruction
2864 : CalledFor const CalledFrom, // indicate which type of routine called this routine
2865 : Real64 TVISIntWin, // Visible transmittance of int win at COSBIntWin for light from ext win
2866 : Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
2867 : int const MapNum)
2868 : {
2869 :
2870 : // SUBROUTINE INFORMATION:
2871 : // AUTHOR B. Griffith
2872 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
2873 :
2874 : // PURPOSE OF THIS SUBROUTINE:
2875 : // collect code for calculations sun position aspects for daylighting coefficients
2876 :
2877 : // METHODOLOGY EMPLOYED:
2878 : // switch as need to serve both reference points and map points based on calledFrom
2879 68272440 : auto &s_surf = state.dataSurface;
2880 68272440 : if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) {
2881 35272882 : return;
2882 : }
2883 :
2884 33391406 : auto &dl = state.dataDayltg;
2885 33391406 : auto &s_mat = state.dataMaterial;
2886 :
2887 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2888 33391406 : Vector3<Real64> RREF{0.0, 0.0, 0.0}; // Location of a reference point in absolute coordinate system //Autodesk Was used uninitialized:
2889 :
2890 : Real64 ObTransDisk; // Product of solar transmittances of exterior obstructions hit by ray from reference point to sun
2891 : Real64 LumAtHitPtFrSun; // Luminance at hit point of obstruction by reflection of direct light from sun (cd/m2)
2892 :
2893 : Real64 TVISS; // Direct solar visible transmittance of window at given angle of incidence
2894 : // (times light well efficiency, if appropriate)
2895 : Real64 XAVWL; // XAVWL*TVISS is contribution of window luminance from solar disk (cd/m2)
2896 :
2897 : bool hitObs; // True iff obstruction is hit
2898 : Real64 ObsVisRefl; // Visible reflectance of obstruction
2899 : Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by
2900 :
2901 : Real64 SpecReflectance; // Specular reflectance of a reflecting surface
2902 : Real64 TVisRefl; // Bare window vis trans for reflected beam
2903 : // (times light well efficiency, if appropriate)
2904 : Real64 PHSUNrefl; // Altitude angle of reflected sun (radians)
2905 : Real64 THSUNrefl; // Azimuth anggle of reflected sun (radians)
2906 :
2907 : Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
2908 : // exterior window element or between ref pt and sun
2909 : Real64 TVisIntWinMult; // Interior window vis trans multiplier for ext win in adjacent zone
2910 : Real64 TVisIntWinDiskMult; // Interior window vis trans solar disk multiplier for ext win in adj zone
2911 : Real64 WindowSolidAngleDaylightPoint;
2912 :
2913 33391406 : ++ISunPos;
2914 :
2915 : // Altitude of sun (degrees)
2916 33391406 : dl->sunAngles = dl->sunAnglesHr[iHour];
2917 :
2918 : // First time through, call routine to calculate inter-reflected illuminance
2919 : // at reference point and luminance of window with shade, screen or blind.
2920 :
2921 : // Rob/TH - Not sure whether this call is necessary for interior zones with interior windows only.
2922 : // new code would be -
2923 : // IF (LSHCAL == 1 .AND. ExtWinType /= AdjZoneExtWin) CALL DayltgInterReflectedIllum(ISunPos,IHR,ZoneNum,IWin2)
2924 33391406 : int enclNum = 0; // enclosure index
2925 33391406 : int zoneNum = 0; // zone index
2926 33391406 : if (CalledFrom == CalledFor::RefPoint) {
2927 6606422 : zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
2928 6606422 : enclNum = dl->daylightControl(daylightCtrlNum).enclIndex;
2929 26784984 : } else if (CalledFrom == CalledFor::MapPoint) {
2930 26784984 : assert(MapNum > 0);
2931 26784984 : zoneNum = dl->illumMaps(MapNum).zoneIndex;
2932 26784984 : enclNum = dl->illumMaps(MapNum).enclIndex;
2933 : }
2934 33391406 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
2935 33389918 : if (LSHCAL == 1) {
2936 333774 : DayltgInterReflectedIllum(state, ISunPos, iHour, enclNum, IWin2);
2937 : }
2938 : } else {
2939 1488 : if (LSHCAL == 1) {
2940 186 : DayltgInterReflectedIllumComplexFenestration(state, IWin2, WinEl, iHour, daylightCtrlNum, iRefPoint, CalledFrom, MapNum);
2941 : }
2942 1488 : if (COSB <= 0.0) {
2943 0 : return;
2944 : }
2945 1488 : DayltgDirectIllumComplexFenestration(state, IWin, WinEl, iHour, iRefPoint, CalledFrom, MapNum);
2946 : // Call direct sun component only once since calculation is done for entire window
2947 1488 : if (WinEl == (NWX * NWY)) {
2948 186 : DayltgDirectSunDiskComplexFenestration(state, IWin2, iHour, iRefPoint, WinEl, AZVIEW, CalledFrom, MapNum);
2949 : }
2950 1488 : return;
2951 : }
2952 :
2953 : // Daylighting shelf simplification: The shelf completely blocks all view of the window,
2954 : // only interrelflected illumination is allowed (see DayltgInterReflectedIllum above).
2955 : // Everything else in this loop has to do with direct luminance from the window.
2956 33389918 : if (InShelfSurf > 0) {
2957 390360 : return;
2958 : }
2959 :
2960 32999558 : if (COSB <= 0.0) {
2961 0 : return;
2962 : }
2963 :
2964 32999558 : auto &surfWin = s_surf->SurfaceWindow(IWin);
2965 :
2966 32999558 : Illums XDirIllum;
2967 32999558 : Illums XAvgWinLum;
2968 32999558 : Real64 const Ray_3 = Ray.z;
2969 32999558 : Real64 const DOMEGA_Ray_3 = DOMEGA * Ray_3;
2970 :
2971 : // Add contribution of this window element to glare and to
2972 : // direct illuminance at reference point
2973 :
2974 : // The I,J,K indices for sky and sun components of direct illuminance
2975 : // (EDIRSK, EDIRSU) and average window luminance (AVWLSK, AVWLSU) are:
2976 : // I=1 for clear sky, =2 Clear turbid, =3 Intermediate, =4 Overcast;
2977 : // J=1 for bare window, =2 for window with shade or fixed slat-angle blind;
2978 : // K = sun position index.
2979 :
2980 : // ----- CASE I -- BARE WINDOW (no shading device)
2981 :
2982 : // Beam solar and sky solar reflected from nearest obstruction.
2983 : // In the following hitIntObs == false ==> no interior obstructions hit, and
2984 : // hitExtObs == true ==> one or more exterior obstructions hit.
2985 32999558 : if (s_surf->CalcSolRefl && !hitIntObs && hitExtObs) {
2986 : int NearestHitSurfNum; // Surface number of nearest obstruction
2987 0 : Vector3<Real64> NearestHitPt; // Hit point of ray on nearest obstruction
2988 : // One or more exterior obstructions was hit; get contribution of reflection
2989 : // from nearest obstruction.
2990 : // Find obstruction whose hit point is closest to this ray's window element
2991 0 : DayltgClosestObstruction(state, RWIN2, Ray, NearestHitSurfNum, NearestHitPt);
2992 0 : if (NearestHitSurfNum > 0) {
2993 :
2994 : // Beam solar reflected from nearest obstruction
2995 :
2996 0 : LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, iHour, Ray, NearestHitSurfNum, NearestHitPt);
2997 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * TVISB;
2998 0 : if (PHRAY >= 0.0) {
2999 0 : dl->dirIllum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * DOMEGA_Ray_3 * TVISB;
3000 : }
3001 :
3002 : // Sky solar reflected from nearest obstruction
3003 :
3004 0 : int const ObsConstrNum = s_surf->SurfActiveConstruction(NearestHitSurfNum);
3005 0 : if (ObsConstrNum > 0) {
3006 : // Exterior building surface is nearest hit
3007 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
3008 : // Obstruction is not a window, i.e., is an opaque surface
3009 0 : ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
3010 : } else {
3011 : // Obstruction is a window; assume it is bare
3012 0 : ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
3013 : }
3014 : } else {
3015 : // Shadowing surface is nearest hit
3016 0 : if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
3017 : // This is a daylighting shelf, for which reflection is separately calculated
3018 0 : ObsVisRefl = 0.0;
3019 : } else {
3020 0 : ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
3021 0 : if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0) {
3022 0 : ObsVisRefl += s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
3023 0 : state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
3024 : }
3025 : }
3026 : }
3027 : // Surface number to use when obstruction is a shadowing surface
3028 0 : int NearestHitSurfNumX = NearestHitSurfNum;
3029 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
3030 : // The following gets the correct side of a shadowing surface for reflection.
3031 0 : if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
3032 0 : if (dot(Ray, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) {
3033 0 : NearestHitSurfNumX = NearestHitSurfNum + 1;
3034 : }
3035 : }
3036 0 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
3037 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
3038 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
3039 0 : state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
3040 : } else {
3041 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
3042 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, iHour, NearestHitSurfNumX) / Constant::Pi;
3043 : }
3044 0 : assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
3045 0 : auto &gilsk = dl->horIllum[iHour];
3046 0 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
3047 0 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
3048 :
3049 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3050 0 : XAvgWinLum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum;
3051 0 : avwlsk.sky[iSky] += XAvgWinLum.sky[iSky] * TVISB;
3052 0 : if (PHRAY >= 0.0) {
3053 0 : XDirIllum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum * DOMEGA_Ray_3;
3054 0 : edirsk.sky[iSky] += XDirIllum.sky[iSky] * TVISB;
3055 : }
3056 : }
3057 : }
3058 0 : } // End of check if solar reflection calculation is in effect
3059 :
3060 32999558 : if (ObTrans > 1.e-6) {
3061 : // Ray did not hit an obstruction or the transmittance product of hit obstructions is non-zero.
3062 : // Contribution of sky or ground luminance in cd/m2
3063 32012798 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
3064 : // Make all transmitted light diffuse for a TDD with a bare diffuser
3065 2448 : assert(equal_dimensions(dl->avgWinLum, dl->winLum));
3066 2448 : assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
3067 2448 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
3068 2448 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
3069 2448 : auto &wlumsk = dl->winLum(iHour)[iWinCover_Bare];
3070 12240 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3071 9792 : avwlsk.sky[iSky] += wlumsk.sky[iSky];
3072 9792 : if (PHRAY > 0.0) {
3073 9792 : edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3;
3074 : }
3075 : }
3076 :
3077 2448 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun;
3078 2448 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += dl->winLum(iHour)[iWinCover_Bare].sunDisk;
3079 :
3080 2448 : if (PHRAY > 0.0) {
3081 2448 : dl->dirIllum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun * DOMEGA_Ray_3;
3082 : }
3083 : } else { // Bare window
3084 32010350 : Vector3<Real64> GroundHitPt; // Coordinates of point that ray hits ground (m)
3085 : // Tuned Hoisted operations out of loop and linear indexing
3086 32010350 : if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
3087 7264 : Real64 Alfa = std::acos(-Ray_3);
3088 7264 : Real64 const Ray_1(Ray.x);
3089 7264 : Real64 const Ray_2(Ray.y);
3090 : // Beta = std::atan2( Ray_2, Ray_1 ); //Unused Tuning below eliminated use
3091 : // Distance between ground hit point and proj'n of center of window element onto ground (m)
3092 7264 : Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
3093 7264 : GroundHitPt.z = s_surf->GroundLevelZ;
3094 : // Tuned Replaced by below: sqrt is faster than sincos
3095 : // GroundHitPt( 1 ) = RWIN2( 1 ) + HorDis * std::cos( Beta );
3096 : // GroundHitPt( 2 ) = RWIN2( 2 ) + HorDis * std::sin( Beta );
3097 7264 : Real64 const Ray_r(std::sqrt(square(Ray_1) + square(Ray_2)));
3098 7264 : if (Ray_r > 0.0) {
3099 7264 : HorDis /= Ray_r;
3100 7264 : GroundHitPt.x = RWIN2.x + HorDis * Ray_1;
3101 7264 : GroundHitPt.y = RWIN2.y + HorDis * Ray_2;
3102 : } else { // Treat as angle==0
3103 0 : GroundHitPt.x = RWIN2.x + HorDis;
3104 0 : GroundHitPt.y = RWIN2.y;
3105 : }
3106 : }
3107 32010350 : Real64 const GILSK_mult((state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * ObTrans * SkyObstructionMult);
3108 32010350 : Real64 const TVISB_ObTrans(TVISB * ObTrans);
3109 32010350 : Real64 const AVWLSU_add(TVISB_ObTrans * dl->horIllum[iHour].sun * (state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi));
3110 32010350 : Vector3<Real64> const SUNCOS_iHour(s_surf->SurfSunCosHourly(iHour));
3111 32010350 : assert(equal_dimensions(dl->dirIllum, dl->avgWinLum));
3112 32010350 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
3113 32010350 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
3114 :
3115 160051750 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3116 128041400 : if (PHRAY > 0.0) { // Ray heads upward to sky
3117 126984652 : Real64 ELUM = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), THRAY, PHRAY); // Sky or ground luminance (cd/m2)
3118 126984652 : XDirIllum.sky[iSky] = ELUM * DOMEGA_Ray_3;
3119 126984652 : Real64 DEDIR = XDirIllum.sky[iSky] * TVISB; // Illuminance contribution at reference point from window element (lux)
3120 126984652 : edirsk.sky[iSky] += DEDIR * ObTrans;
3121 126984652 : avwlsk.sky[iSky] += ELUM * TVISB_ObTrans;
3122 126984652 : XAvgWinLum.sky[iSky] = ELUM * ObTrans;
3123 : } else { // PHRAY <= 0.
3124 : // Ray heads downward to ground.
3125 : // Contribution from sky diffuse reflected from ground
3126 1056748 : XAvgWinLum.sky[iSky] = dl->horIllum[iHour].sky[iSky] * GILSK_mult;
3127 1056748 : avwlsk.sky[iSky] += TVISB * XAvgWinLum.sky[iSky];
3128 : // Contribution from beam solar reflected from ground (beam reaching ground point
3129 : // can be obstructed [SunObstructionMult < 1.0] if CalcSolRefl = .TRUE.)
3130 : } // End of check if ray is going up or down
3131 : } // for (iSky)
3132 :
3133 32010350 : if (PHRAY <= 0.0) {
3134 : // SunObstructionMult = 1.0; //Tuned
3135 264187 : if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
3136 : // Sun reaches ground point if vector from this point to the sun is unobstructed
3137 2270 : hitObs = false;
3138 2270 : Vector3<Real64> ObsHitPt; // Coordinates of hit point on an obstruction (m)
3139 10957 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
3140 8912 : hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, SUNCOS_iHour, ObsHitPt);
3141 8912 : if (hitObs) {
3142 225 : break;
3143 : }
3144 2270 : }
3145 : // if ( hitObs ) SunObstructionMult = 0.0;
3146 2270 : if (!hitObs) {
3147 2045 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
3148 : }
3149 2270 : } else {
3150 261917 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
3151 : }
3152 : } // (PHRAY <= 0.0)
3153 32010350 : }
3154 : } // End of check if bare window or TDD:DIFFUSER
3155 :
3156 : // Illuminance from beam solar (without interior reflection)
3157 : // Just run this once on the last pass
3158 32999558 : if (iXelement == NWX && iYelement == NWY) { // Last pass
3159 :
3160 : // Beam solar reaching reference point directly without exterior reflection
3161 :
3162 : // Unit vector from ref. pt. to sun
3163 331326 : Vector3<Real64> RAYCOS;
3164 331326 : RAYCOS.x = dl->sunAngles.cosPhi * std::cos(dl->sunAngles.theta);
3165 331326 : RAYCOS.y = dl->sunAngles.cosPhi * std::sin(dl->sunAngles.theta);
3166 331326 : RAYCOS.z = dl->sunAngles.sinPhi;
3167 :
3168 : // Is sun on front side of exterior window?
3169 331326 : Real64 COSI = dot(WNORM2, RAYCOS); // Cosine of angle between direct sun and window outward normal
3170 : bool hit; // True if ray from ref point thru window element hits an obstruction
3171 : bool hitWin; // True if ray passes thru window
3172 331326 : Vector3<Real64> HP;
3173 331326 : if (COSI > 0.0) {
3174 :
3175 : // Does RAYCOS pass thru exterior window? HP is point that RAYCOS intersects window plane.
3176 247627 : hitWin = PierceSurface(state, IWin2, RREF2, RAYCOS, HP);
3177 : // True if ray from ref pt to sun hits an interior obstruction
3178 247627 : if (hitWin) {
3179 8969 : bool hitIntObsDisk = false;
3180 8969 : if (extWinType == ExtWinType::InZone) {
3181 : // Check for interior obstructions between reference point and HP.
3182 8743 : hitIntObsDisk = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
3183 : }
3184 8969 : ObTransDisk = 0.0; // Init value
3185 : // Init flag for vector from RP to sun passing through interior window
3186 8969 : bool hitIntWinDisk = false;
3187 8969 : if (extWinType == ExtWinType::AdjZone) { // This block is for RPs in zones with interior windows
3188 : // adjacent to zones with exterior windows
3189 : // Does RAYCOS pass through interior window in zone containing RP?
3190 : // Loop over zone surfaces looking for interior windows between reference point and sun
3191 : // Surface number of int window intersected by ray betw ref pt and sun
3192 : int IntWinDiskHitNum;
3193 : // Intersection point on an interior window for ray from ref pt to sun (m)
3194 226 : Vector3<Real64> HitPtIntWinDisk;
3195 226 : auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
3196 452 : for (int const spaceNum : thisZone.spaceIndexes) {
3197 226 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
3198 452 : for (int IntWinDisk = thisSpace.WindowSurfaceFirst, IntWinDisk_end = thisSpace.WindowSurfaceLast;
3199 452 : IntWinDisk <= IntWinDisk_end;
3200 : ++IntWinDisk) {
3201 226 : auto const &surfIntWinDisk = s_surf->Surface(IntWinDisk);
3202 226 : if (surfIntWinDisk.ExtBoundCond < 1) {
3203 0 : continue;
3204 : }
3205 :
3206 226 : if (s_surf->Surface(surfIntWinDisk.ExtBoundCond).Zone != s_surf->Surface(IWin2).Zone) {
3207 0 : continue;
3208 : }
3209 :
3210 226 : hitIntWinDisk = PierceSurface(state, IntWinDisk, RREF, RAYCOS, HitPtIntWinDisk);
3211 226 : if (!hitIntWinDisk) {
3212 226 : continue;
3213 : }
3214 :
3215 0 : IntWinDiskHitNum = IntWinDisk;
3216 0 : COSBIntWin = dot(surfIntWinDisk.OutNormVec, RAYCOS);
3217 0 : if (COSBIntWin <= 0.0) {
3218 0 : hitIntWinDisk = false;
3219 0 : IntWinDiskHitNum = 0;
3220 0 : continue;
3221 : }
3222 0 : TVISIntWinDisk =
3223 0 : Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWinDisk.Construction).TransVisBeamCoef);
3224 0 : break;
3225 : } // for (IntWinDisk)
3226 226 : } // for (spaceNum)
3227 :
3228 226 : if (!hitIntWinDisk) { // Vector from RP to sun does not pass through interior window
3229 226 : ObTransDisk = 0.0;
3230 226 : hit = true; //! fcw Is this needed?
3231 : }
3232 :
3233 : // Check for interior obstructions between ref point and interior window
3234 226 : hitIntObsDisk = false;
3235 226 : if (hitIntWinDisk) {
3236 0 : hitIntObsDisk = DayltgHitInteriorObstruction(state, IntWinDiskHitNum, RREF, HitPtIntWinDisk);
3237 : // If no obstruction between RP and hit int win, check for obstruction
3238 : // between int win and ext win
3239 0 : if (!hitIntObsDisk) {
3240 0 : hitIntObsDisk = DayltgHitBetWinObstruction(state, IntWinDiskHitNum, IWin2, HitPtIntWinDisk, HP);
3241 : }
3242 : }
3243 226 : if (hitIntObsDisk) {
3244 0 : ObTransDisk = 0.0;
3245 : }
3246 226 : } // case where RP is in zone with interior window adjacent to zone with exterior window
3247 :
3248 : // hitExtObsDisk = false; //Unused Set but never used
3249 : // RJH 08-25-07 hitIntWinDisk should not be reset to false here, and should be tested below.
3250 : // This is to correct logic flaw causing direct solar to reach adjacent zone refpt
3251 : // when vector to sun does not pass through interior window
3252 : // hitIntWinDisk = false
3253 8969 : if (!hitIntObsDisk) { // No interior obstruction was hit
3254 : // Net transmittance of exterior obstructions encountered by RAYCOS
3255 : // ObTransDisk = 1.0 will be returned if no exterior obstructions are hit.
3256 8965 : ObTransDisk = DayltgHitObstruction(state, iHour, IWin2, RREF2, RAYCOS);
3257 : // if ( ObTransDisk < 1.0 ) hitExtObsDisk = true; //Unused Set but never used
3258 : // RJH 08-26-07 However, if this is a case of interior window
3259 : // and vector to sun does not pass through interior window
3260 : // then reset ObTransDisk to 0.0 since it is the key test for adding
3261 : // contribution of sun to RP below.
3262 8965 : if ((extWinType == ExtWinType::AdjZone) && (!hitIntWinDisk)) {
3263 226 : ObTransDisk = 0.0;
3264 : }
3265 : }
3266 :
3267 : // PETER: need side wall mounted TDD to test this
3268 : // PETER: probably need to replace RREF2 with RWIN2
3269 : // PETER: need to check for interior obstructions too.
3270 :
3271 8969 : if (ObTransDisk > 1.e-6) {
3272 :
3273 : // Sun reaches reference point; increment illuminance.
3274 : // Direct normal illuminance is normalized to 1.0
3275 :
3276 8307 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
3277 : // No beam is transmitted. Takes care of TDD with a bare diffuser and all types of blinds.
3278 0 : TVISS = 0.0;
3279 : } else {
3280 : // Beam transmittance for bare window and all types of blinds
3281 8307 : TVISS = Window::POLYF(COSI, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
3282 8307 : surfWin.lightWellEff;
3283 8307 : if (extWinType == ExtWinType::AdjZone && hitIntWinDisk) {
3284 0 : TVISS *= TVISIntWinDisk;
3285 : }
3286 : }
3287 :
3288 8307 : dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = RAYCOS.z * TVISS * ObTransDisk; // Bare window
3289 :
3290 8307 : Real64 transBmBmMult = 0.0;
3291 :
3292 8307 : if (ANY_BLIND(ShType)) {
3293 0 : auto const &surfShade = s_surf->surfShades(IWin);
3294 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
3295 0 : assert(matBlind != nullptr);
3296 :
3297 0 : Real64 ProfAng = ProfileAngle(state, IWin, RAYCOS, matBlind->SlatOrientation);
3298 0 : transBmBmMult = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
3299 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
3300 :
3301 8307 : } else if (ShType == WinShadingType::ExtScreen) {
3302 : // pass angle from sun to window normal here using PHSUN and THSUN from above and surface angles
3303 : // SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
3304 : // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
3305 0 : auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
3306 0 : assert(screen != nullptr);
3307 :
3308 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
3309 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
3310 : int ip1, ip2, it1, it2;
3311 : BilinearInterpCoeffs coeffs;
3312 0 : Material::NormalizePhiTheta(phi, theta);
3313 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
3314 0 : GetBilinearInterpCoeffs(
3315 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
3316 0 : transBmBmMult = BilinearInterp(screen->btars[ip1][it1].BmTrans,
3317 0 : screen->btars[ip1][it2].BmTrans,
3318 0 : screen->btars[ip2][it1].BmTrans,
3319 0 : screen->btars[ip2][it2].BmTrans,
3320 : coeffs);
3321 :
3322 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
3323 : }
3324 :
3325 8307 : if (CalledFrom == CalledFor::RefPoint) {
3326 : // Glare from solar disk
3327 :
3328 : // Position factor for sun (note that AZVIEW is wrt y-axis and THSUN is wrt
3329 : // x-axis of absolute coordinate system.
3330 6319 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
3331 6319 : Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
3332 : Real64 POSFAC =
3333 6319 : DayltgGlarePositionFactor(XR, YR); // Position factor for a window element / ref point / view vector combination
3334 :
3335 6319 : WindowSolidAngleDaylightPoint = s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd;
3336 :
3337 6319 : if (POSFAC != 0.0 && WindowSolidAngleDaylightPoint > 0.000001) {
3338 : // Increment window luminance. Luminance of solar disk (cd/m2)
3339 : // is 1.47*10^4*(direct normal solar illuminance) for direct normal solar
3340 : // illuminance in lux (lumens/m2). For purposes of calculating daylight factors
3341 : // direct normal solar illuminance = 1.0.
3342 : // Solid angle subtended by sun is 0.000068 steradians
3343 :
3344 3929 : XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) / std::pow(WindowSolidAngleDaylightPoint, 0.8);
3345 3929 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = XAVWL * TVISS * ObTransDisk; // Bare window
3346 :
3347 3929 : if (ANY_BLIND(ShType)) {
3348 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
3349 3929 : } else if (ShType == WinShadingType::ExtScreen) {
3350 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
3351 : }
3352 : } // Position Factor
3353 : } // if (calledFrom == RefPt)
3354 : } // if (ObTransDisk > 1e-6) // Beam avoids all obstructions
3355 : } // if (hitWin)
3356 : } // if (COSI > 0.0) // Sun on front side
3357 :
3358 : // Beam solar reaching reference point after beam-beam (specular) reflection from
3359 : // an exterior surface
3360 :
3361 331326 : if (s_surf->CalcSolRefl) {
3362 : // Receiving surface number corresponding this window
3363 908 : int RecSurfNum = s_surf->SurfShadowRecSurfNum(IWin2);
3364 908 : if (RecSurfNum > 0) { // interior windows do not apply
3365 908 : if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
3366 : bool hitRefl; // True iff ray hits reflecting surface
3367 908 : Vector3<Real64> HitPtRefl; // Point that ray hits reflecting surface
3368 908 : Vector3<Real64> SunVecMir; // Sun ray mirrored in reflecting surface
3369 908 : Vector3<Real64> ReflNorm; // Normal vector to reflecting surface
3370 : // This window has associated obstructions that could reflect beam onto the window
3371 2724 : for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end;
3372 : ++loop) {
3373 1816 : int ReflSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop);
3374 1816 : int ReflSurfNumX = ReflSurfNum;
3375 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
3376 : // The following gets the correct side of a shadowing surface for reflection.
3377 1816 : if (s_surf->Surface(ReflSurfNum).IsShadowing) {
3378 1816 : if (dot(RAYCOS, s_surf->Surface(ReflSurfNum).OutNormVec) < 0.0) {
3379 908 : ReflSurfNumX = ReflSurfNum + 1;
3380 : }
3381 : }
3382 : // Require that the surface can have specular reflection
3383 1816 : if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window || s_surf->SurfShadowGlazingFrac(ReflSurfNum) > 0.0) {
3384 0 : ReflNorm = s_surf->Surface(ReflSurfNumX).OutNormVec;
3385 : // Vector to sun that is mirrored in obstruction
3386 0 : SunVecMir = RAYCOS - 2.0 * dot(RAYCOS, ReflNorm) * ReflNorm;
3387 : // Skip if reflecting surface is not sunlit
3388 0 : if (state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNumX) < 0.01) {
3389 0 : continue;
3390 : }
3391 : // Skip if altitude angle of mirrored sun is negative since reflected sun cannot
3392 : // reach reference point in this case
3393 0 : if (SunVecMir.z <= 0.0) {
3394 0 : continue;
3395 : }
3396 : // Cosine of incidence angle of reflected beam on window
3397 0 : Real64 CosIncAngRec = dot(s_surf->Surface(IWin2).OutNormVec, SunVecMir);
3398 0 : if (CosIncAngRec <= 0.0) {
3399 0 : continue;
3400 : }
3401 : // Does ray from ref. pt. along SunVecMir pass through window?
3402 0 : hitWin = PierceSurface(state, IWin2, RREF2, SunVecMir, HP);
3403 0 : if (!hitWin) {
3404 0 : continue; // Ray did not pass through window
3405 : }
3406 : // Check if this ray hits interior obstructions
3407 0 : hit = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
3408 0 : if (hit) {
3409 0 : continue; // Interior obstruction was hit
3410 : }
3411 : // Does ray hit this reflecting surface?
3412 0 : hitRefl = PierceSurface(state, ReflSurfNum, RREF2, SunVecMir, HitPtRefl);
3413 0 : if (!hitRefl) {
3414 0 : continue; // Ray did not hit this reflecting surface
3415 : }
3416 0 : Real64 ReflDistanceSq = distance_squared(HitPtRefl, RREF2);
3417 0 : Real64 ReflDistance = std::sqrt(ReflDistanceSq);
3418 : // Is ray from ref. pt. to reflection point (HitPtRefl) obstructed?
3419 0 : bool hitObsRefl = false;
3420 0 : Vector3<Real64> HitPtObs; // Hit point on obstruction
3421 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
3422 0 : loop2 <= loop2_end;
3423 : ++loop2) {
3424 0 : int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
3425 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == s_surf->Surface(ReflSurfNum).BaseSurf) {
3426 0 : continue;
3427 : }
3428 0 : hitObs = PierceSurface(state, ObsSurfNum, RREF2, SunVecMir, ReflDistance, HitPtObs); // ReflDistance cutoff added
3429 0 : if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
3430 0 : if (distance_squared(HitPtObs, RREF2) < ReflDistanceSq) { // Distance squared from ref pt to reflection point
3431 0 : hitObsRefl = true;
3432 0 : break;
3433 : }
3434 : }
3435 : }
3436 0 : if (hitObsRefl) {
3437 0 : continue; // Obstruction closer than reflection pt. was hit; go to next obstruction
3438 : }
3439 : // There is no obstruction for this ray between ref pt and hit pt on reflecting surface.
3440 : // See if ray from hit pt on reflecting surface to original (unmirrored) sun position is obstructed
3441 0 : hitObs = false;
3442 0 : if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
3443 : // Reflecting surface is a window.
3444 : // Receiving surface number for this reflecting window.
3445 0 : int ReflSurfRecNum = s_surf->SurfShadowRecSurfNum(ReflSurfNum);
3446 0 : if (ReflSurfRecNum > 0) {
3447 : // Loop over possible obstructions for this reflecting window
3448 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
3449 0 : loop2 <= loop2_end;
3450 : ++loop2) {
3451 : int const ObsSurfNum =
3452 0 : state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
3453 0 : hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
3454 0 : if (hitObs) {
3455 0 : break;
3456 : }
3457 : }
3458 : }
3459 : } else {
3460 : // Reflecting surface is a building shade
3461 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
3462 0 : if (ObsSurfNum == ReflSurfNum) {
3463 0 : continue;
3464 : }
3465 0 : hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
3466 0 : if (hitObs) {
3467 0 : break;
3468 : }
3469 0 : }
3470 : } // End of check if reflector is a window or shadowing surface
3471 :
3472 0 : if (hitObs) {
3473 0 : continue; // Obstruction hit between reflection hit point and sun; go to next obstruction
3474 : }
3475 :
3476 : // No obstructions. Calculate reflected beam illuminance at ref. pt. from this reflecting surface.
3477 0 : SpecReflectance = 0.0;
3478 0 : Real64 CosIncAngRefl = std::abs(dot(RAYCOS, ReflNorm)); // Cos of angle of incidence of beam on reflecting surface
3479 0 : if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
3480 0 : int const ConstrNumRefl = s_surf->SurfActiveConstruction(ReflSurfNum);
3481 : SpecReflectance =
3482 0 : Window::POLYF(std::abs(CosIncAngRefl), state.dataConstruction->Construct(ConstrNumRefl).ReflSolBeamFrontCoef);
3483 : }
3484 0 : if (s_surf->Surface(ReflSurfNum).IsShadowing && s_surf->SurfShadowGlazingConstruct(ReflSurfNum) > 0) {
3485 0 : SpecReflectance =
3486 0 : s_surf->SurfShadowGlazingFrac(ReflSurfNum) *
3487 0 : Window::POLYF(
3488 : std::abs(CosIncAngRefl),
3489 0 : state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(ReflSurfNum)).ReflSolBeamFrontCoef);
3490 : }
3491 0 : TVisRefl = Window::POLYF(CosIncAngRec, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
3492 0 : surfWin.lightWellEff;
3493 0 : dl->dirIllum(iHour)[iWinCover_Bare].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl; // Bare window
3494 :
3495 0 : Real64 TransBmBmMultRefl = 0.0;
3496 0 : if (ANY_BLIND(ShType)) {
3497 0 : auto const &surfShade = s_surf->surfShades(IWin);
3498 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
3499 :
3500 0 : Real64 ProfAng = ProfileAngle(state, IWin, SunVecMir, matBlind->SlatOrientation);
3501 0 : TransBmBmMultRefl = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
3502 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
3503 0 : } else if (ShType == WinShadingType::ExtScreen) {
3504 : // pass angle from sun to window normal here using PHSUN and THSUN from above and
3505 : // surface angles SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
3506 : // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
3507 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
3508 0 : assert(screen != nullptr);
3509 :
3510 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
3511 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
3512 : int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
3513 : BilinearInterpCoeffs coeffs;
3514 0 : Material::NormalizePhiTheta(phi, theta);
3515 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
3516 0 : GetBilinearInterpCoeffs(
3517 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
3518 :
3519 0 : TransBmBmMultRefl = BilinearInterp(screen->btars[ip1][it1].BmTrans,
3520 0 : screen->btars[ip1][it2].BmTrans,
3521 0 : screen->btars[ip2][it1].BmTrans,
3522 0 : screen->btars[ip2][it2].BmTrans,
3523 : coeffs);
3524 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
3525 : } // End of check if window has a blind or screen
3526 :
3527 : // Glare from reflected solar disk
3528 :
3529 0 : PHSUNrefl = SunVecMir.z;
3530 0 : THSUNrefl = std::atan2(SunVecMir.y, SunVecMir.x);
3531 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - THSUNrefl) + 0.001);
3532 0 : Real64 YR = std::tan(PHSUNrefl + 0.001);
3533 0 : Real64 POSFAC = DayltgGlarePositionFactor(XR, YR);
3534 0 : if (POSFAC != 0.0 && s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd > 0.000001) {
3535 0 : XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) /
3536 0 : std::pow(s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd, 0.8);
3537 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += XAVWL * TVisRefl * SpecReflectance; // Bare window
3538 0 : if (ANY_BLIND(ShType)) {
3539 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
3540 0 : } else if (ShType == WinShadingType::ExtScreen) {
3541 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
3542 : }
3543 : }
3544 0 : } // End of check that obstruction can specularly reflect
3545 : } // End of loop over obstructions associated with this window
3546 :
3547 908 : } // End of check if this window has associated obstructions
3548 : } // End of check to see if this is exterior type window
3549 : } // End of check if exterior reflection calculation is in effect
3550 :
3551 331326 : } // Last pass
3552 :
3553 32999558 : if ((ICtrl > 0 && (ANY_BLIND(ShType) || ANY_SHADE_SCREEN(ShType))) || s_surf->SurfWinSolarDiffusing(IWin)) {
3554 :
3555 : // ----- CASE II -- WINDOW WITH SCREEN, SHADE, BLIND, OR DIFFUSING WINDOW
3556 : // Interior window visible transmittance multiplier for exterior window in adjacent zone
3557 12496 : TVisIntWinMult = 1.0;
3558 12496 : TVisIntWinDiskMult = 1.0;
3559 12496 : if (s_surf->Surface(IWin).SolarEnclIndex != dl->daylightControl(daylightCtrlNum).enclIndex) {
3560 0 : TVisIntWinMult = TVISIntWin;
3561 0 : TVisIntWinDiskMult = TVISIntWinDisk;
3562 : }
3563 :
3564 12496 : Real64 const DOMEGA_Ray_3_TVisIntWinMult(DOMEGA_Ray_3 * TVisIntWinMult);
3565 :
3566 12496 : auto &wlumsk = dl->winLum(iHour)[iWinCover_Shaded];
3567 12496 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Shaded];
3568 12496 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Shaded];
3569 62480 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3570 : // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3571 49984 : avwlsk.sky[iSky] += wlumsk.sky[iSky] * TVisIntWinMult;
3572 49984 : if (PHRAY > 0.0) {
3573 39656 : edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3_TVisIntWinMult;
3574 : }
3575 : } // for (iSky)
3576 :
3577 12496 : dl->avgWinLum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * TVisIntWinMult;
3578 12496 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += dl->winLum(iHour)[iWinCover_Shaded].sunDisk * TVisIntWinDiskMult;
3579 :
3580 12496 : if (PHRAY > 0.0) {
3581 9914 : dl->dirIllum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * DOMEGA_Ray_3_TVisIntWinMult;
3582 : }
3583 : }
3584 33391406 : } // FigureDayltgCoeffsAtPointsForSunPosition()
3585 :
3586 384024 : void FigureRefPointDayltgFactorsToAddIllums(EnergyPlusData &state,
3587 : int const daylightCtrlNum, // Current daylighting control number
3588 : int const iRefPoint,
3589 : int const iHour,
3590 : int &ISunPos,
3591 : int const IWin,
3592 : int const loopwin,
3593 : int const NWX, // Number of window elements in x direction for dayltg calc
3594 : int const NWY, // Number of window elements in y direction for dayltg calc
3595 : int const ICtrl // Window control counter
3596 : )
3597 : {
3598 :
3599 : // SUBROUTINE INFORMATION:
3600 : // AUTHOR B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann
3601 : // DATE WRITTEN Oct. 2012
3602 :
3603 : // PURPOSE OF THIS SUBROUTINE:
3604 : // calculation worker routine to fill daylighting coefficients
3605 :
3606 : // METHODOLOGY EMPLOYED:
3607 : // this version is just for reference points.
3608 :
3609 : // SUBROUTINE PARAMETER DEFINITIONS:
3610 384024 : Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating the daylighting and glare factors
3611 :
3612 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3613 384024 : auto &dl = state.dataDayltg;
3614 384024 : auto &s_surf = state.dataSurface;
3615 :
3616 384024 : if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) {
3617 214464 : return;
3618 : }
3619 :
3620 169560 : ++ISunPos;
3621 :
3622 : // Altitude of sun (degrees)
3623 169560 : dl->sunAngles = dl->sunAnglesHr[iHour];
3624 :
3625 169560 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
3626 :
3627 169560 : auto &surf = s_surf->Surface(IWin);
3628 :
3629 169560 : int const enclNum = surf.SolarEnclIndex;
3630 :
3631 : // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
3632 :
3633 : // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
3634 : // related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
3635 : // and interior surfaces with high visible reflectance.
3636 : // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
3637 : // the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
3638 :
3639 169560 : auto &daylFacHr = thisDayltgCtrl.daylFac[iHour];
3640 :
3641 508680 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
3642 :
3643 339120 : auto const &gilsk = dl->horIllum[iHour];
3644 339120 : auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
3645 339120 : auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
3646 339120 : auto const &avwlsk = dl->avgWinLum(iHour)[iWinCover];
3647 :
3648 339120 : auto &daylFac = daylFacHr(loopwin, iRefPoint)[iWinCover];
3649 339120 : auto &illFac = daylFac[iLum_Illum];
3650 339120 : auto &sourceFac = daylFac[iLum_Source];
3651 339120 : auto &backFac = daylFac[iLum_Back];
3652 :
3653 1695600 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
3654 :
3655 1356480 : if (gilsk.sky[iSky] > tmpDFCalc) {
3656 1356480 : illFac.sky[iSky] = (edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky];
3657 1356480 : sourceFac.sky[iSky] = avwlsk.sky[iSky] / (NWX * NWY * gilsk.sky[iSky]);
3658 1356480 : backFac.sky[iSky] = eintsk.sky[iSky] * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * gilsk.sky[iSky]);
3659 : } else {
3660 0 : illFac.sky[iSky] = 0.0;
3661 0 : sourceFac.sky[iSky] = 0.0;
3662 0 : backFac.sky[iSky] = 0.0;
3663 : }
3664 :
3665 : } // for (iSky)
3666 :
3667 339120 : if (dl->horIllum[iHour].sun > tmpDFCalc) {
3668 320874 : daylFac[iLum_Illum].sun = (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
3669 641748 : daylFac[iLum_Illum].sunDisk =
3670 320874 : (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
3671 320874 : daylFac[iLum_Source].sun = dl->avgWinLum(iHour)[iWinCover].sun / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
3672 320874 : daylFac[iLum_Source].sunDisk = dl->avgWinLum(iHour)[iWinCover].sunDisk / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
3673 320874 : daylFac[iLum_Back].sun = dl->reflIllum(iHour)[iWinCover].sun * dl->enclDaylight(enclNum).aveVisDiffReflect /
3674 320874 : (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
3675 641748 : daylFac[iLum_Back].sunDisk = dl->reflIllum(iHour)[iWinCover].sunDisk * dl->enclDaylight(enclNum).aveVisDiffReflect /
3676 320874 : (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
3677 : } else {
3678 18246 : daylFac[iLum_Illum].sun = 0.0;
3679 18246 : daylFac[iLum_Illum].sunDisk = 0.0;
3680 :
3681 18246 : daylFac[iLum_Source].sun = 0.0;
3682 18246 : daylFac[iLum_Source].sunDisk = 0.0;
3683 :
3684 18246 : daylFac[iLum_Back].sun = 0.0;
3685 18246 : daylFac[iLum_Back].sunDisk = 0.0;
3686 : }
3687 : } // for (jSH)
3688 :
3689 : // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
3690 169560 : if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
3691 :
3692 2090 : Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // Ratio of Tvis of fully-switched state to that of the unswitched state
3693 2090 : auto &daylFac2 = daylFacHr(loopwin, iRefPoint)[iWinCover_Shaded];
3694 2090 : auto const &daylFac1 = daylFacHr(loopwin, iRefPoint)[iWinCover_Bare];
3695 :
3696 10450 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3697 8360 : daylFac2[iLum_Illum].sky[iSky] = daylFac1[iLum_Illum].sky[iSky] * VTR;
3698 8360 : daylFac2[iLum_Source].sky[iSky] = daylFac1[iLum_Source].sky[iSky] * VTR;
3699 8360 : daylFac2[iLum_Back].sky[iSky] = daylFac1[iLum_Back].sky[iSky] * VTR;
3700 : } // for (iSky)
3701 :
3702 2090 : daylFac2[iLum_Illum].sun = daylFac1[iLum_Illum].sun * VTR;
3703 2090 : daylFac2[iLum_Source].sun = daylFac1[iLum_Source].sun * VTR;
3704 2090 : daylFac2[iLum_Back].sun = daylFac1[iLum_Back].sun * VTR;
3705 2090 : daylFac2[iLum_Illum].sunDisk = daylFac1[iLum_Illum].sunDisk * VTR;
3706 2090 : daylFac2[iLum_Source].sunDisk = daylFac1[iLum_Source].sunDisk * VTR;
3707 2090 : daylFac2[iLum_Back].sunDisk = daylFac1[iLum_Back].sunDisk * VTR;
3708 : } // ICtrl > 0
3709 : } // FigureRefPointDayltgFactorsToAddIllums()
3710 :
3711 328800 : void FigureMapPointDayltgFactorsToAddIllums(EnergyPlusData &state,
3712 : int const MapNum,
3713 : int const iMapPoint,
3714 : int const iHour,
3715 : int const IWin,
3716 : int const loopwin,
3717 : int const ICtrl // Window control counter
3718 : )
3719 : {
3720 :
3721 : // SUBROUTINE INFORMATION:
3722 : // AUTHOR B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann, Peter Ellis, Linda Lawrie
3723 : // DATE WRITTEN Nov. 2012
3724 :
3725 : // PURPOSE OF THIS SUBROUTINE:
3726 : // calculation worker routine to fill daylighting coefficients
3727 :
3728 : // METHODOLOGY EMPLOYED:
3729 : // this version is just for map points.
3730 :
3731 : // SUBROUTINE PARAMETER DEFINITIONS:
3732 328800 : Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating
3733 : // the daylighting and glare factors
3734 :
3735 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3736 328800 : auto &dl = state.dataDayltg;
3737 328800 : auto &s_surf = state.dataSurface;
3738 :
3739 328800 : if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) {
3740 164400 : return;
3741 : }
3742 :
3743 : // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or blind;
3744 :
3745 : // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
3746 : // related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
3747 : // and interior surfaces with high visible reflectance.
3748 : // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
3749 : // the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
3750 :
3751 164400 : auto &illumMap = dl->illumMaps(MapNum);
3752 164400 : auto &daylFacHr = illumMap.daylFac[iHour];
3753 493200 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
3754 :
3755 328800 : auto const &gilsk = dl->horIllum[iHour];
3756 328800 : auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
3757 328800 : auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
3758 328800 : auto &illSky = daylFacHr(loopwin, iMapPoint)[iWinCover];
3759 :
3760 1644000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
3761 1315200 : illSky.sky[iSky] = (gilsk.sky[iSky] > tmpDFCalc) ? ((edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky]) : 0.0;
3762 : } // for (iSky)
3763 :
3764 328800 : if (dl->horIllum[iHour].sun > tmpDFCalc) {
3765 315100 : daylFacHr(loopwin, iMapPoint)[iWinCover].sun =
3766 315100 : (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
3767 315100 : daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk =
3768 315100 : (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
3769 : } else {
3770 13700 : daylFacHr(loopwin, iMapPoint)[iWinCover].sun = 0.0;
3771 13700 : daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk = 0.0;
3772 : }
3773 : } // for (iWinCover)
3774 :
3775 : // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
3776 164400 : if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
3777 146400 : Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // ratio of Tvis of switched to unswitched state
3778 146400 : auto &illSky2 = daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded];
3779 146400 : auto const &illSky1 = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare];
3780 732000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3781 585600 : illSky2.sky[iSky] = illSky1.sky[iSky] * VTR;
3782 : }
3783 :
3784 146400 : daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sun = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sun * VTR;
3785 146400 : daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sunDisk = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sunDisk * VTR;
3786 : } // ICtrl > 0
3787 : }
3788 :
3789 801 : void GetDaylightingParametersInput(EnergyPlusData &state)
3790 : {
3791 :
3792 : // SUBROUTINE INFORMATION:
3793 : // AUTHOR Linda Lawrie
3794 : // DATE WRITTEN Oct 2004
3795 :
3796 : // PURPOSE OF THIS SUBROUTINE:
3797 : // This subroutine provides a simple structure to get all daylighting
3798 : // parameters.
3799 801 : auto &dl = state.dataDayltg;
3800 801 : auto &s_surf = state.dataSurface;
3801 :
3802 801 : if (!dl->getDaylightingParametersInputFlag) {
3803 0 : return;
3804 : }
3805 801 : dl->getDaylightingParametersInputFlag = false;
3806 :
3807 801 : auto const &s_ipsc = state.dataIPShortCut;
3808 801 : s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
3809 801 : bool ErrorsFound = false;
3810 801 : int TotDaylightingControls = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
3811 801 : if (TotDaylightingControls > 0) {
3812 65 : dl->enclDaylight.allocate(state.dataViewFactor->NumOfSolarEnclosures);
3813 65 : GetInputDayliteRefPt(state, ErrorsFound);
3814 65 : GetDaylightingControls(state, ErrorsFound);
3815 65 : GeometryTransformForDaylighting(state);
3816 65 : GetInputIlluminanceMap(state, ErrorsFound);
3817 65 : GetLightWellData(state, ErrorsFound);
3818 65 : if (ErrorsFound) {
3819 0 : ShowFatalError(state, "Program terminated for above reasons, related to DAYLIGHTING");
3820 : }
3821 65 : DayltgSetupAdjZoneListsAndPointers(state);
3822 : }
3823 :
3824 801 : dl->maxNumRefPtInAnyDaylCtrl = 0;
3825 801 : dl->maxNumRefPtInAnyEncl = 0;
3826 : // Loop through all daylighting controls to find total reference points in each enclosure
3827 1139 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
3828 338 : int numRefPoints = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
3829 338 : dl->maxNumRefPtInAnyDaylCtrl = max(numRefPoints, dl->maxNumRefPtInAnyDaylCtrl);
3830 : }
3831 6001 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
3832 5200 : dl->maxNumRefPtInAnyEncl = max(state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints, dl->maxNumRefPtInAnyEncl);
3833 : }
3834 :
3835 801 : dl->maxEnclSubSurfaces = max(maxval(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::NumSubSurfaces),
3836 801 : maxval(dl->enclDaylight, &EnclDaylightCalc::NumOfDayltgExtWins));
3837 :
3838 7160 : for (int SurfNum : s_surf->AllHTWindowSurfaceList) {
3839 6359 : auto const &surf = s_surf->Surface(SurfNum);
3840 6359 : int const surfEnclNum = surf.SolarEnclIndex;
3841 6359 : int const numEnclRefPoints = state.dataViewFactor->EnclSolInfo(surfEnclNum).TotalEnclosureDaylRefPoints;
3842 6359 : auto &surfWin = s_surf->SurfaceWindow(SurfNum);
3843 6359 : if (numEnclRefPoints > 0) {
3844 913 : if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
3845 913 : surfWin.refPts.allocate(numEnclRefPoints);
3846 2473 : for (auto &refPt : surfWin.refPts) {
3847 1560 : new (&refPt) SurfaceWindowRefPt();
3848 : }
3849 :
3850 913 : s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
3851 : }
3852 : } else {
3853 5446 : int SurfNumAdj = surf.ExtBoundCond;
3854 5446 : if (SurfNumAdj > 0) {
3855 13 : int const adjSurfEnclNum = s_surf->Surface(SurfNumAdj).SolarEnclIndex;
3856 13 : int const numAdjEnclRefPoints = state.dataViewFactor->EnclSolInfo(adjSurfEnclNum).TotalEnclosureDaylRefPoints;
3857 13 : if (numAdjEnclRefPoints > 0) {
3858 1 : if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
3859 1 : surfWin.refPts.allocate(numAdjEnclRefPoints);
3860 2 : for (auto &refPt : surfWin.refPts) {
3861 1 : new (&refPt) SurfaceWindowRefPt();
3862 : }
3863 1 : s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
3864 : }
3865 : }
3866 : }
3867 : }
3868 :
3869 6359 : if (surf.ExtBoundCond != ExternalEnvironment) {
3870 14 : continue;
3871 : }
3872 :
3873 6345 : if (!surf.HasShadeControl) {
3874 6194 : continue;
3875 : }
3876 :
3877 151 : auto &thisSurfEnclosure(state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex));
3878 151 : if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).GlareControlIsActive) {
3879 : // Error if GlareControlIsActive but window is not in a Daylighting:Detailed zone
3880 30 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
3881 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3882 0 : ShowContinueError(state, "GlareControlIsActive = Yes but it is not in a Daylighting zone or enclosure.");
3883 0 : ShowContinueError(state, format("Zone or enclosure indicated={}", state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex).Name));
3884 0 : ErrorsFound = true;
3885 : }
3886 : // Error if GlareControlIsActive and window is in a Daylighting:Detailed zone/enclosure with
3887 : // an interior window adjacent to another Daylighting:Detailed zone/enclosure
3888 30 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3889 357 : for (int const intWin : thisSurfEnclosure.SurfacePtr) {
3890 327 : int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
3891 327 : if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
3892 0 : auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
3893 0 : if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3894 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3895 0 : ShowContinueError(state, "GlareControlIsActive = Yes and is in a Daylighting zone or enclosure");
3896 0 : ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
3897 0 : ShowContinueError(state, format("Adjacent Zone or Enclosure indicated={}", adjSurfEnclosure.Name));
3898 0 : ErrorsFound = true;
3899 : }
3900 : }
3901 : }
3902 : }
3903 : }
3904 :
3905 151 : if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
3906 149 : continue;
3907 : }
3908 :
3909 : // Error if window has shadingControlType = MeetDaylightingIlluminanceSetpoint &
3910 : // but is not in a Daylighting:Detailed zone
3911 2 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
3912 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3913 0 : ShowContinueError(state, "MeetDaylightingIlluminanceSetpoint but it is not in a Daylighting zone or enclosure.");
3914 0 : ShowContinueError(state, format("Zone or enclosure indicated={}", thisSurfEnclosure.Name));
3915 0 : ErrorsFound = true;
3916 0 : continue;
3917 : }
3918 :
3919 : // Error if window has shadingControlType = MeetDaylightIlluminanceSetpoint and is in a &
3920 : // Daylighting:Detailed zone with an interior window adjacent to another Daylighting:Detailed zone
3921 22 : for (int const intWin : thisSurfEnclosure.SurfacePtr) {
3922 20 : int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
3923 20 : if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
3924 0 : auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
3925 0 : if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3926 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3927 0 : ShowContinueError(state, "MeetDaylightIlluminanceSetpoint and is in a Daylighting zone or enclosure");
3928 0 : ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
3929 0 : ShowContinueError(state, format("Adjacent Zone or enclosure indicated={}", adjSurfEnclosure.Name));
3930 0 : ErrorsFound = true;
3931 : }
3932 : }
3933 : }
3934 801 : } // for (SurfNum)
3935 :
3936 801 : if (!state.dataHeatBal->AnyAirBoundary) {
3937 47775 : for (int SurfLoop = 1; SurfLoop <= s_surf->TotSurfaces; ++SurfLoop) {
3938 46979 : auto const &surf = s_surf->Surface(SurfLoop);
3939 46979 : if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) {
3940 40666 : continue;
3941 : }
3942 :
3943 6313 : int const enclOfSurf = surf.SolarEnclIndex;
3944 6313 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclOfSurf);
3945 6313 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow || !dl->enclDaylight(enclOfSurf).hasSplitFluxDaylighting) {
3946 5413 : continue;
3947 : }
3948 :
3949 900 : auto &surfWin = s_surf->SurfaceWindow(SurfLoop);
3950 2439 : for (int refPtNum = 1; refPtNum <= enclSol.TotalEnclosureDaylRefPoints; ++refPtNum) {
3951 1539 : auto &refPt = surfWin.refPts(refPtNum);
3952 4617 : SetupOutputVariable(state,
3953 3078 : format("Daylighting Window Reference Point {} Illuminance", refPtNum),
3954 : Constant::Units::lux,
3955 1539 : refPt.illumFromWinRep,
3956 : OutputProcessor::TimeStepType::Zone,
3957 : OutputProcessor::StoreType::Average,
3958 1539 : surf.Name);
3959 4617 : SetupOutputVariable(state,
3960 3078 : format("Daylighting Window Reference Point {} View Luminance", refPtNum),
3961 : Constant::Units::cd_m2,
3962 1539 : refPt.lumWinRep,
3963 : OutputProcessor::TimeStepType::Zone,
3964 : OutputProcessor::StoreType::Average,
3965 1539 : surf.Name);
3966 : }
3967 : }
3968 : } else {
3969 34 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
3970 29 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
3971 302 : for (int const enclSurfNum : enclSol.SurfacePtr) {
3972 273 : auto const &surf = s_surf->Surface(enclSurfNum);
3973 273 : auto &surfWindow = s_surf->SurfaceWindow(enclSurfNum);
3974 :
3975 273 : if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) {
3976 243 : continue;
3977 : }
3978 :
3979 30 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow) {
3980 23 : continue;
3981 : }
3982 :
3983 7 : auto const &enclDayltg = dl->enclDaylight(enclNum);
3984 7 : if (!enclDayltg.hasSplitFluxDaylighting) {
3985 0 : continue;
3986 : }
3987 :
3988 7 : int refPtCount = 0;
3989 17 : for (int controlNum : enclDayltg.daylightControlIndexes) {
3990 10 : auto const &control = dl->daylightControl(controlNum);
3991 20 : for (int refPtNum = 1; refPtNum <= control.TotalDaylRefPoints; ++refPtNum) {
3992 10 : ++refPtCount; // Count reference points across each daylighting control in the same enclosure
3993 10 : auto &refPt = surfWindow.refPts(refPtCount);
3994 10 : std::string varKey = format("{} to {}", surf.Name, state.dataDayltg->DaylRefPt(control.refPts(refPtNum).num).Name);
3995 20 : SetupOutputVariable(state,
3996 : "Daylighting Window Reference Point Illuminance",
3997 : Constant::Units::lux,
3998 10 : refPt.illumFromWinRep,
3999 : OutputProcessor::TimeStepType::Zone,
4000 : OutputProcessor::StoreType::Average,
4001 : varKey);
4002 20 : SetupOutputVariable(state,
4003 : "Daylighting Window Reference Point View Luminance",
4004 : Constant::Units::cd_m2,
4005 10 : refPt.lumWinRep,
4006 : OutputProcessor::TimeStepType::Zone,
4007 : OutputProcessor::StoreType::Average,
4008 : varKey);
4009 10 : }
4010 7 : } // for (controlNum)
4011 : } // for (enclSurfNum)
4012 : } // for (enclNum)
4013 : }
4014 :
4015 : // RJH DElight Modification Begin - Calls to DElight preprocessing subroutines
4016 801 : if (doesDayLightingUseDElight(state)) {
4017 3 : Real64 dLatitude = state.dataEnvrn->Latitude;
4018 3 : DisplayString(state, "Calculating DElight Daylighting Factors");
4019 3 : DElightManagerF::DElightInputGenerator(state);
4020 : // Init Error Flag to 0 (no Warnings or Errors)
4021 3 : DisplayString(state, "ReturnFrom DElightInputGenerator");
4022 3 : int iErrorFlag = 0;
4023 3 : DisplayString(state, "Calculating DElight DaylightCoefficients");
4024 3 : DElightManagerF::GenerateDElightDaylightCoefficients(dLatitude, iErrorFlag);
4025 : // Check Error Flag for Warnings or Errors returning from DElight
4026 : // RJH 2008-03-07: open file for READWRITE and DELETE file after processing
4027 3 : DisplayString(state, "ReturnFrom DElight DaylightCoefficients Calc");
4028 3 : if (iErrorFlag != 0) {
4029 : // Open DElight Daylight Factors Error File for reading
4030 0 : auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
4031 :
4032 : // Sequentially read lines in DElight Daylight Factors Error File
4033 : // and process them using standard EPlus warning/error handling calls
4034 : // Process all error/warning messages first
4035 : // Then, if any error has occurred, ShowFatalError to terminate processing
4036 0 : bool bEndofErrFile = !iDElightErrorFile.good();
4037 0 : std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
4038 0 : while (!bEndofErrFile) {
4039 0 : auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
4040 0 : if (cErrorLine.eof) {
4041 0 : bEndofErrFile = true;
4042 0 : continue;
4043 : }
4044 : // Is the current line a Warning message?
4045 0 : if (has_prefix(cErrorLine.data, "WARNING: ")) {
4046 0 : cErrorMsg = cErrorLine.data.substr(9);
4047 0 : ShowWarningError(state, cErrorMsg);
4048 : }
4049 : // Is the current line an Error message?
4050 0 : if (has_prefix(cErrorLine.data, "ERROR: ")) {
4051 0 : cErrorMsg = cErrorLine.data.substr(7);
4052 0 : ShowSevereError(state, cErrorMsg);
4053 0 : iErrorFlag = 1;
4054 : }
4055 0 : }
4056 :
4057 : // Close and Delete DElight Error File
4058 0 : if (iDElightErrorFile.is_open()) {
4059 0 : iDElightErrorFile.close();
4060 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
4061 : }
4062 :
4063 : // If any DElight Error occurred then ShowFatalError to terminate
4064 0 : if (iErrorFlag > 0) {
4065 0 : ErrorsFound = true;
4066 : }
4067 0 : } else {
4068 3 : if (FileSystem::fileExists(state.files.outputDelightDfdmpFilePath.filePath)) {
4069 3 : FileSystem::removeFile(state.files.outputDelightDfdmpFilePath.filePath);
4070 : }
4071 : }
4072 : }
4073 : // RJH DElight Modification End - Calls to DElight preprocessing subroutines
4074 :
4075 : // TH 6/3/2010, added to report daylight factors
4076 801 : s_ipsc->cCurrentModuleObject = "Output:DaylightFactors";
4077 801 : int NumReports = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4078 801 : if (NumReports > 0) {
4079 : int NumNames;
4080 : int NumNumbers;
4081 : int IOStat;
4082 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
4083 1 : s_ipsc->cCurrentModuleObject,
4084 : 1,
4085 1 : s_ipsc->cAlphaArgs,
4086 : NumNames,
4087 1 : s_ipsc->rNumericArgs,
4088 : NumNumbers,
4089 : IOStat,
4090 1 : s_ipsc->lNumericFieldBlanks,
4091 1 : s_ipsc->lAlphaFieldBlanks,
4092 1 : s_ipsc->cAlphaFieldNames,
4093 1 : s_ipsc->cNumericFieldNames);
4094 1 : if (has_prefix(s_ipsc->cAlphaArgs(1), "SIZINGDAYS")) {
4095 1 : dl->DFSReportSizingDays = true;
4096 0 : } else if (has_prefix(s_ipsc->cAlphaArgs(1), "ALLSHADOWCALCULATIONDAYS")) {
4097 0 : dl->DFSReportAllShadowCalculationDays = true;
4098 : }
4099 : }
4100 :
4101 801 : if (ErrorsFound) {
4102 0 : ShowFatalError(state, "Program terminated for above reasons");
4103 : }
4104 : } // FigureMapPointDayltgFactorsToAddIllums()
4105 :
4106 65 : void GetInputIlluminanceMap(EnergyPlusData &state, bool &ErrorsFound)
4107 : {
4108 : // Perform the GetInput function for the Output:IlluminanceMap
4109 : // Glazer - June 2016 (moved from GetDaylightingControls)
4110 65 : auto &dl = state.dataDayltg;
4111 65 : auto const &s_surf = state.dataSurface;
4112 :
4113 65 : Array1D_bool ZoneMsgDone;
4114 :
4115 65 : Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
4116 65 : Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
4117 : // these are only for Building Rotation for Appendix G when using world coordinate system
4118 65 : Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
4119 65 : Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
4120 :
4121 65 : bool doTransform = false;
4122 65 : Real64 OldAspectRatio = 1.0;
4123 65 : Real64 NewAspectRatio = 1.0;
4124 :
4125 65 : CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
4126 :
4127 65 : auto &ip = state.dataInputProcessing->inputProcessor;
4128 65 : auto const &s_ipsc = state.dataIPShortCut;
4129 65 : s_ipsc->cCurrentModuleObject = "Output:IlluminanceMap";
4130 65 : int TotIllumMaps = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4131 :
4132 65 : dl->illumMaps.allocate(TotIllumMaps);
4133 :
4134 65 : if (TotIllumMaps > 0) {
4135 : int IOStat;
4136 : int NumAlpha;
4137 : int NumNumber;
4138 6 : auto &ip = state.dataInputProcessing->inputProcessor;
4139 15 : for (int MapNum = 1; MapNum <= TotIllumMaps; ++MapNum) {
4140 18 : ip->getObjectItem(state,
4141 9 : s_ipsc->cCurrentModuleObject,
4142 : MapNum,
4143 9 : s_ipsc->cAlphaArgs,
4144 : NumAlpha,
4145 9 : s_ipsc->rNumericArgs,
4146 : NumNumber,
4147 : IOStat,
4148 9 : s_ipsc->lNumericFieldBlanks,
4149 9 : s_ipsc->lAlphaFieldBlanks,
4150 9 : s_ipsc->cAlphaFieldNames,
4151 9 : s_ipsc->cNumericFieldNames);
4152 :
4153 9 : auto &illumMap = dl->illumMaps(MapNum);
4154 9 : illumMap.Name = s_ipsc->cAlphaArgs(1);
4155 9 : int const zoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
4156 9 : if (zoneNum > 0) {
4157 8 : illumMap.zoneIndex = zoneNum;
4158 : // set enclosure index for first space in zone
4159 8 : int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
4160 8 : illumMap.enclIndex = enclNum;
4161 : // check that all spaces in the zone are in the same enclosure
4162 8 : for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
4163 0 : int spaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
4164 0 : if (enclNum != state.dataHeatBal->space(spaceNum).solarEnclosureNum) {
4165 0 : ShowSevereError(state,
4166 0 : format("{}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting illuminance maps.",
4167 0 : s_ipsc->cCurrentModuleObject,
4168 0 : s_ipsc->cAlphaArgs(1)));
4169 0 : ShowContinueError(
4170 0 : state, format("Zone=\"{}\" spans multiple enclosures. Use a Space Name instead.", state.dataHeatBal->Zone(zoneNum).Name));
4171 0 : ErrorsFound = true;
4172 0 : break;
4173 : }
4174 : }
4175 : } else {
4176 1 : int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
4177 1 : if (spaceNum == 0) {
4178 0 : ShowSevereError(state,
4179 0 : format("{}=\"{}\", invalid {}=\"{}\".",
4180 0 : s_ipsc->cCurrentModuleObject,
4181 0 : s_ipsc->cAlphaArgs(1),
4182 0 : s_ipsc->cAlphaFieldNames(2),
4183 0 : s_ipsc->cAlphaArgs(2)));
4184 0 : ErrorsFound = true;
4185 : } else {
4186 1 : illumMap.spaceIndex = spaceNum;
4187 1 : illumMap.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
4188 1 : illumMap.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
4189 1 : assert(illumMap.enclIndex > 0);
4190 : }
4191 : }
4192 :
4193 9 : illumMap.Z = s_ipsc->rNumericArgs(1);
4194 9 : illumMap.Xmin = s_ipsc->rNumericArgs(2);
4195 9 : illumMap.Xmax = s_ipsc->rNumericArgs(3);
4196 9 : if (s_ipsc->rNumericArgs(2) > s_ipsc->rNumericArgs(3)) {
4197 0 : ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
4198 0 : ShowContinueError(state,
4199 0 : format("...{} {:.2R} must be <= {} {:.2R}.",
4200 0 : s_ipsc->cNumericFieldNames(2),
4201 0 : s_ipsc->rNumericArgs(2),
4202 0 : s_ipsc->cNumericFieldNames(3),
4203 0 : s_ipsc->rNumericArgs(3)));
4204 0 : ErrorsFound = true;
4205 : }
4206 9 : illumMap.Xnum = s_ipsc->rNumericArgs(4);
4207 9 : illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
4208 :
4209 9 : illumMap.Ymin = s_ipsc->rNumericArgs(5);
4210 9 : illumMap.Ymax = s_ipsc->rNumericArgs(6);
4211 9 : if (s_ipsc->rNumericArgs(5) > s_ipsc->rNumericArgs(6)) {
4212 0 : ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
4213 0 : ShowContinueError(state,
4214 0 : format("...{} {:.2R} must be <= {} {:.2R}.",
4215 0 : s_ipsc->cNumericFieldNames(5),
4216 0 : s_ipsc->rNumericArgs(5),
4217 0 : s_ipsc->cNumericFieldNames(6),
4218 0 : s_ipsc->rNumericArgs(6)));
4219 0 : ErrorsFound = true;
4220 : }
4221 9 : illumMap.Ynum = s_ipsc->rNumericArgs(7);
4222 9 : illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
4223 :
4224 9 : if (illumMap.Xnum * illumMap.Ynum > MaxMapRefPoints) {
4225 0 : ShowSevereError(state, format("{}=\"{}\", too many map points specified.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
4226 0 : ShowContinueError(state,
4227 0 : format("...{}[{}] * {}[{}].= [{}] must be <= [{}].",
4228 0 : s_ipsc->cNumericFieldNames(4),
4229 0 : illumMap.Xnum,
4230 0 : s_ipsc->cNumericFieldNames(7),
4231 0 : illumMap.Ynum,
4232 0 : illumMap.Xnum * illumMap.Ynum,
4233 : MaxMapRefPoints));
4234 0 : ErrorsFound = true;
4235 : }
4236 : } // MapNum
4237 6 : s_ipsc->cCurrentModuleObject = "OutputControl:IlluminanceMap:Style";
4238 6 : int MapStyleIn = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4239 :
4240 6 : if (MapStyleIn == 0) {
4241 5 : s_ipsc->cAlphaArgs(1) = "COMMA";
4242 5 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4243 1 : } else if (MapStyleIn == 1) {
4244 2 : ip->getObjectItem(state,
4245 1 : s_ipsc->cCurrentModuleObject,
4246 : 1,
4247 1 : s_ipsc->cAlphaArgs,
4248 : NumAlpha,
4249 1 : s_ipsc->rNumericArgs,
4250 : NumNumber,
4251 : IOStat,
4252 1 : s_ipsc->lNumericFieldBlanks,
4253 1 : s_ipsc->lAlphaFieldBlanks,
4254 1 : s_ipsc->cAlphaFieldNames,
4255 1 : s_ipsc->cNumericFieldNames);
4256 1 : if (s_ipsc->cAlphaArgs(1) == "COMMA") {
4257 1 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4258 0 : } else if (s_ipsc->cAlphaArgs(1) == "TAB") {
4259 0 : dl->MapColSep = DataStringGlobals::CharTab; // tab
4260 0 : } else if (s_ipsc->cAlphaArgs(1) == "FIXED" || s_ipsc->cAlphaArgs(1) == "SPACE") {
4261 0 : dl->MapColSep = DataStringGlobals::CharSpace; // space
4262 : } else {
4263 0 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4264 0 : ShowWarningError(state,
4265 0 : format("{}: invalid {}=\"{}\", Commas will be used to separate fields.",
4266 0 : s_ipsc->cCurrentModuleObject,
4267 0 : s_ipsc->cAlphaFieldNames(1),
4268 0 : s_ipsc->cAlphaArgs(1)));
4269 0 : s_ipsc->cAlphaArgs(1) = "COMMA";
4270 : }
4271 : }
4272 6 : print(state.files.eio, "! <Daylighting:Illuminance Maps>,#Maps,Style\n");
4273 6 : ConvertCaseToLower(s_ipsc->cAlphaArgs(1), s_ipsc->cAlphaArgs(2));
4274 6 : s_ipsc->cAlphaArgs(1).erase(1);
4275 6 : s_ipsc->cAlphaArgs(1) += s_ipsc->cAlphaArgs(2).substr(1);
4276 6 : print(state.files.eio, "Daylighting:Illuminance Maps,{},{}\n", TotIllumMaps, s_ipsc->cAlphaArgs(1));
4277 : }
4278 :
4279 : // Check for illuminance maps associated with this zone
4280 74 : for (auto &illumMap : dl->illumMaps) {
4281 :
4282 9 : if (illumMap.zoneIndex == 0) {
4283 0 : continue;
4284 : }
4285 :
4286 9 : auto &zone = state.dataHeatBal->Zone(illumMap.zoneIndex);
4287 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
4288 9 : Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
4289 9 : Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
4290 :
4291 9 : if (illumMap.Xnum * illumMap.Ynum == 0) {
4292 0 : continue;
4293 : }
4294 :
4295 : // Add additional daylighting reference points for map
4296 9 : illumMap.TotalMapRefPoints = illumMap.Xnum * illumMap.Ynum;
4297 9 : illumMap.refPts.allocate(illumMap.TotalMapRefPoints);
4298 634 : for (auto &refPt : illumMap.refPts) {
4299 625 : new (&refPt) DaylMapPt();
4300 : }
4301 :
4302 9 : if (illumMap.TotalMapRefPoints > MaxMapRefPoints) {
4303 0 : ShowSevereError(state, "GetDaylighting Parameters: Total Map Reference points entered is greater than maximum allowed.");
4304 0 : ShowContinueError(state, format("Occurs in Zone={}", zone.Name));
4305 0 : ShowContinueError(state,
4306 0 : format("Maximum reference points allowed={}, entered amount ( when error first occurred )={}",
4307 : MaxMapRefPoints,
4308 0 : illumMap.TotalMapRefPoints));
4309 0 : ErrorsFound = true;
4310 0 : break;
4311 : }
4312 :
4313 : // Calc cos and sin of Zone Relative North values for later use in transforming Map Point coordinates
4314 : // CosZoneRelNorth = std::cos( -zone.RelNorth * DegToRadians ); //Tuned These should not be changing
4315 : // SinZoneRelNorth = std::sin( -zone.RelNorth * DegToRadians );
4316 9 : illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
4317 9 : illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
4318 :
4319 : // Map points and increments are stored in AbsCoord and then that is operated on if relative coords entered.
4320 94 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
4321 710 : for (int X = 1; X <= illumMap.Xnum; ++X) {
4322 625 : int iRefPt = (Y - 1) * illumMap.Xnum + X;
4323 625 : auto &refPt = illumMap.refPts(iRefPt);
4324 625 : refPt.absCoords = {illumMap.Xmin + (X - 1) * illumMap.Xinc, illumMap.Ymin + (Y - 1) * illumMap.Yinc, illumMap.Z};
4325 : }
4326 : }
4327 :
4328 94 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
4329 710 : for (int X = 1; X <= illumMap.Xnum; ++X) {
4330 625 : int iRefPt = (Y - 1) * illumMap.Xnum + X;
4331 625 : auto &refPt = illumMap.refPts(iRefPt);
4332 :
4333 625 : if (!s_surf->DaylRefWorldCoordSystem) {
4334 425 : Real64 Xb = refPt.absCoords.x * CosZoneRelNorth - refPt.absCoords.y * SinZoneRelNorth + zone.OriginX;
4335 425 : Real64 Yb = refPt.absCoords.x * SinZoneRelNorth + refPt.absCoords.y * CosZoneRelNorth + zone.OriginY;
4336 425 : refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
4337 425 : refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
4338 425 : refPt.absCoords.z += zone.OriginZ;
4339 425 : if (doTransform) {
4340 0 : Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
4341 0 : Real64 Yo = refPt.absCoords.y;
4342 : // next derotate the building
4343 0 : Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
4344 0 : Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
4345 : // translate
4346 0 : Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
4347 0 : Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
4348 : // rerotate
4349 0 : refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
4350 :
4351 0 : refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
4352 : }
4353 : } else {
4354 200 : Real64 Xb = refPt.absCoords.x;
4355 200 : Real64 Yb = refPt.absCoords.y;
4356 200 : refPt.absCoords.x = Xb * CosBldgRotAppGonly - Yb * SinBldgRotAppGonly;
4357 200 : refPt.absCoords.y = Xb * SinBldgRotAppGonly + Yb * CosBldgRotAppGonly;
4358 : }
4359 625 : if (iRefPt == 1) {
4360 9 : illumMap.Xmin = refPt.absCoords.x;
4361 9 : illumMap.Ymin = refPt.absCoords.y;
4362 9 : illumMap.Xmax = refPt.absCoords.x;
4363 9 : illumMap.Ymax = refPt.absCoords.y;
4364 9 : illumMap.Z = refPt.absCoords.z;
4365 : }
4366 625 : illumMap.Xmin = min(illumMap.Xmin, refPt.absCoords.x);
4367 625 : illumMap.Ymin = min(illumMap.Ymin, refPt.absCoords.y);
4368 625 : illumMap.Xmax = max(illumMap.Xmax, refPt.absCoords.x);
4369 625 : illumMap.Ymax = max(illumMap.Ymax, refPt.absCoords.y);
4370 625 : if ((refPt.absCoords.x < zone.MinimumX && (zone.MinimumX - refPt.absCoords.x) > 0.001) ||
4371 625 : (refPt.absCoords.x > zone.MaximumX && (refPt.absCoords.x - zone.MaximumX) > 0.001) ||
4372 625 : (refPt.absCoords.y < zone.MinimumY && (zone.MinimumY - refPt.absCoords.y) > 0.001) ||
4373 625 : (refPt.absCoords.y > zone.MaximumY && (refPt.absCoords.y - zone.MaximumY) > 0.001) ||
4374 625 : (refPt.absCoords.z < zone.MinimumZ && (zone.MinimumZ - refPt.absCoords.z) > 0.001) ||
4375 625 : (refPt.absCoords.z > zone.MaximumZ && (refPt.absCoords.z - zone.MaximumZ) > 0.001)) {
4376 0 : refPt.inBounds = false;
4377 : }
4378 :
4379 : // Test extremes of Map Points against Zone Min/Max
4380 625 : if (iRefPt != 1 && iRefPt != illumMap.TotalMapRefPoints) {
4381 625 : continue;
4382 : }
4383 :
4384 18 : if (refPt.inBounds) {
4385 18 : continue;
4386 : }
4387 :
4388 0 : if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
4389 0 : ShowWarningError(
4390 : state,
4391 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], X Value outside Zone Min/Max X, Zone={}", iRefPt, zone.Name));
4392 0 : ShowContinueError(state,
4393 0 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
4394 0 : refPt.absCoords.x,
4395 0 : zone.MinimumX,
4396 0 : zone.MaximumX));
4397 0 : ShowContinueError(
4398 : state,
4399 0 : format("...X Reference Distance Outside MinimumX= {:.4R} m.",
4400 0 : (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
4401 : }
4402 0 : if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
4403 0 : ShowWarningError(
4404 : state,
4405 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], Y Value outside Zone Min/Max Y, Zone={}", iRefPt, zone.Name));
4406 0 : ShowContinueError(state,
4407 0 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
4408 0 : refPt.absCoords.y,
4409 0 : zone.MinimumY,
4410 0 : zone.MaximumY));
4411 0 : ShowContinueError(
4412 : state,
4413 0 : format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
4414 0 : (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
4415 : }
4416 0 : if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
4417 0 : ShowWarningError(
4418 : state,
4419 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], Z Value outside Zone Min/Max Z, Zone={}", iRefPt, zone.Name));
4420 0 : ShowContinueError(state,
4421 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
4422 0 : refPt.absCoords.z,
4423 0 : zone.MinimumZ,
4424 0 : zone.MaximumZ));
4425 0 : ShowContinueError(
4426 : state,
4427 0 : format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
4428 0 : (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
4429 : }
4430 : } // for (X)
4431 : } // for (Y)
4432 : } // for (MapNum)
4433 :
4434 65 : ZoneMsgDone.dimension(state.dataGlobal->NumOfZones, false);
4435 74 : for (auto const &illumMap : dl->illumMaps) {
4436 9 : if (illumMap.zoneIndex == 0) {
4437 0 : continue;
4438 : }
4439 9 : int enclNum = illumMap.enclIndex;
4440 9 : if (!dl->enclDaylight(enclNum).hasSplitFluxDaylighting && !ZoneMsgDone(illumMap.zoneIndex)) {
4441 0 : ShowSevereError(state,
4442 0 : format("Zone Name in Output:IlluminanceMap is not used for Daylighting:Controls={}",
4443 0 : state.dataHeatBal->Zone(illumMap.zoneIndex).Name));
4444 0 : ErrorsFound = true;
4445 : }
4446 : }
4447 65 : ZoneMsgDone.deallocate();
4448 65 : if (ErrorsFound) {
4449 0 : return;
4450 : }
4451 :
4452 65 : if (TotIllumMaps > 0) {
4453 6 : print(state.files.eio,
4454 : "! <Daylighting:Illuminance Maps:Detail>,Name,Zone,XMin {{m}},XMax {{m}},Xinc {{m}},#X Points,YMin "
4455 : "{{m}},YMax {{m}},Yinc {{m}},#Y Points,Z {{m}}\n");
4456 : }
4457 74 : for (auto const &illumMap : dl->illumMaps) {
4458 9 : print(state.files.eio,
4459 : "Daylighting:Illuminance Maps:Detail,{},{},{:.2R},{:.2R},{:.2R},{},{:.2R},{:.2R},{:.2R},{},{:.2R}\n",
4460 9 : illumMap.Name,
4461 9 : state.dataHeatBal->Zone(illumMap.zoneIndex).Name,
4462 9 : illumMap.Xmin,
4463 9 : illumMap.Xmax,
4464 9 : illumMap.Xinc,
4465 9 : illumMap.Xnum,
4466 9 : illumMap.Ymin,
4467 9 : illumMap.Ymax,
4468 9 : illumMap.Yinc,
4469 9 : illumMap.Ynum,
4470 9 : illumMap.Z);
4471 : }
4472 :
4473 65 : } // GetInputIlluminanceMap()
4474 :
4475 65 : void GetDaylightingControls(EnergyPlusData &state, bool &ErrorsFound)
4476 : {
4477 : // AUTHOR Fred Winkelmann
4478 : // DATE WRITTEN March 2002
4479 : // MODIFIED Glazer - July 2016 - Move geometry transformation portion, rearrange input, allow more than three reference points
4480 : // Obtain the user input data for Daylighting:Controls object in the input file.
4481 :
4482 : static constexpr std::string_view routineName = "GetDaylightingControls";
4483 :
4484 65 : auto &dl = state.dataDayltg;
4485 :
4486 : int IOStat;
4487 : int NumAlpha;
4488 : int NumNumber;
4489 :
4490 : // Smallest deviation from unity for the sum of all fractions
4491 : // Accept approx 4 to 8 ULP error (technically abs(1.0 + sumFracs) should be close to 2)
4492 : // constexpr Real64 FractionTolerance(4 * std::numeric_limits<Real64>::epsilon());
4493 : // Instead, we use a 0.001 = 0.1% tolerance
4494 65 : constexpr Real64 FractionTolerance(0.001);
4495 :
4496 65 : auto &ip = state.dataInputProcessing->inputProcessor;
4497 65 : auto const &s_ipsc = state.dataIPShortCut;
4498 65 : s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
4499 65 : int totDaylightingControls = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4500 65 : dl->daylightControl.allocate(totDaylightingControls);
4501 65 : Array1D<bool> spaceHasDaylightingControl;
4502 65 : spaceHasDaylightingControl.dimension(state.dataGlobal->numSpaces, false);
4503 : // Reset to zero in case this is called more than once in unit tests
4504 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
4505 851 : state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints = 0;
4506 : }
4507 403 : for (int controlNum = 1; controlNum <= totDaylightingControls; ++controlNum) {
4508 338 : s_ipsc->cAlphaArgs = "";
4509 338 : s_ipsc->rNumericArgs = 0.0;
4510 676 : ip->getObjectItem(state,
4511 338 : s_ipsc->cCurrentModuleObject,
4512 : controlNum,
4513 338 : s_ipsc->cAlphaArgs,
4514 : NumAlpha,
4515 338 : s_ipsc->rNumericArgs,
4516 : NumNumber,
4517 : IOStat,
4518 338 : s_ipsc->lNumericFieldBlanks,
4519 338 : s_ipsc->lAlphaFieldBlanks,
4520 338 : s_ipsc->cAlphaFieldNames,
4521 338 : s_ipsc->cNumericFieldNames);
4522 :
4523 338 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
4524 :
4525 338 : auto &daylightControl = dl->daylightControl(controlNum);
4526 338 : daylightControl.Name = s_ipsc->cAlphaArgs(1);
4527 :
4528 : // Is it a zone or space name?
4529 338 : int const zoneNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->Zone);
4530 338 : if (zoneNum > 0) {
4531 337 : daylightControl.zoneIndex = zoneNum;
4532 : // set enclosure index for first space in zone
4533 337 : int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
4534 337 : daylightControl.enclIndex = enclNum;
4535 : // check that all spaces in the zone are in the same enclosure
4536 337 : for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
4537 0 : int zoneSpaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
4538 0 : if (daylightControl.enclIndex != state.dataHeatBal->space(zoneSpaceNum).solarEnclosureNum) {
4539 0 : ShowSevereError(state,
4540 0 : format("{}: invalid {}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting.",
4541 0 : s_ipsc->cCurrentModuleObject,
4542 0 : s_ipsc->cAlphaFieldNames(2),
4543 0 : s_ipsc->cAlphaArgs(2)));
4544 0 : ErrorsFound = true;
4545 0 : break;
4546 : }
4547 : }
4548 674 : for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
4549 : // Check if this is a duplicate
4550 337 : if (spaceHasDaylightingControl(zoneSpaceNum)) {
4551 0 : ShowWarningError(state,
4552 0 : format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
4553 0 : s_ipsc->cCurrentModuleObject,
4554 0 : daylightControl.Name,
4555 0 : state.dataHeatBal->space(zoneSpaceNum).Name,
4556 0 : s_ipsc->cCurrentModuleObject));
4557 0 : ShowContinueError(state, "This control will override the lighting power factor for this space.");
4558 : }
4559 337 : spaceHasDaylightingControl(zoneSpaceNum) = true;
4560 337 : }
4561 : } else {
4562 1 : int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
4563 1 : if (spaceNum == 0) {
4564 0 : ShowSevereError(state,
4565 0 : format("{}: invalid {}=\"{}\".", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
4566 0 : ErrorsFound = true;
4567 0 : continue;
4568 : } else {
4569 1 : daylightControl.spaceIndex = spaceNum;
4570 1 : daylightControl.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
4571 1 : daylightControl.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
4572 : // Check if this is a duplicate
4573 1 : if (spaceHasDaylightingControl(spaceNum)) {
4574 0 : ShowWarningError(state,
4575 0 : format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
4576 0 : s_ipsc->cCurrentModuleObject,
4577 0 : daylightControl.Name,
4578 0 : state.dataHeatBal->space(spaceNum).Name,
4579 0 : s_ipsc->cCurrentModuleObject));
4580 0 : ShowContinueError(state, "This control will override the lighting power factor for this space.");
4581 : }
4582 1 : spaceHasDaylightingControl(spaceNum) = true;
4583 : }
4584 : }
4585 :
4586 338 : dl->enclDaylight(daylightControl.enclIndex).daylightControlIndexes.emplace_back(controlNum);
4587 338 : daylightControl.ZoneName = state.dataHeatBal->Zone(daylightControl.zoneIndex).Name;
4588 :
4589 338 : if (s_ipsc->lAlphaFieldBlanks(3)) {
4590 0 : daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
4591 : } else {
4592 338 : daylightControl.DaylightMethod =
4593 338 : static_cast<DaylightingMethod>(getEnumValue(DaylightingMethodNamesUC, Util::makeUPPER(s_ipsc->cAlphaArgs(3))));
4594 :
4595 338 : if (daylightControl.DaylightMethod == DaylightingMethod::Invalid) {
4596 0 : daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
4597 0 : ShowWarningError(state,
4598 0 : format("Invalid {} = {}, occurs in {}object for {}=\"{}",
4599 0 : s_ipsc->cAlphaFieldNames(3),
4600 0 : s_ipsc->cAlphaArgs(3),
4601 0 : s_ipsc->cCurrentModuleObject,
4602 0 : s_ipsc->cCurrentModuleObject,
4603 0 : s_ipsc->cAlphaArgs(1)));
4604 0 : ShowContinueError(state, "SplitFlux assumed, and the simulation continues.");
4605 : }
4606 : }
4607 338 : dl->enclDaylight(daylightControl.enclIndex).hasSplitFluxDaylighting |= (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux);
4608 :
4609 338 : if (s_ipsc->lAlphaFieldBlanks(4)) { // Field: Availability Schedule Name
4610 332 : daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
4611 6 : } else if ((daylightControl.availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
4612 0 : ShowWarningItemNotFound(state,
4613 : eoh,
4614 0 : s_ipsc->cAlphaFieldNames(4),
4615 0 : s_ipsc->cAlphaArgs(4),
4616 : "Schedule was not found so controls will always be available, and the simulation continues.");
4617 0 : daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
4618 : }
4619 :
4620 338 : daylightControl.LightControlType = static_cast<LtgCtrlType>(getEnumValue(LtgCtrlTypeNamesUC, s_ipsc->cAlphaArgs(5)));
4621 338 : if (daylightControl.LightControlType == LtgCtrlType::Invalid) {
4622 0 : ShowWarningInvalidKey(
4623 0 : state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), "Continuous assumed, and the simulation continues.");
4624 : }
4625 :
4626 338 : daylightControl.MinPowerFraction = s_ipsc->rNumericArgs(1); // Field: Minimum Input Power Fraction for Continuous Dimming Control
4627 338 : daylightControl.MinLightFraction = s_ipsc->rNumericArgs(2); // Field: Minimum Light Output Fraction for Continuous Dimming Control
4628 338 : daylightControl.LightControlSteps = s_ipsc->rNumericArgs(3); // Field: Number of Stepped Control Steps
4629 338 : daylightControl.LightControlProbability =
4630 338 : s_ipsc->rNumericArgs(4); // Field: Probability Lighting will be Reset When Needed in Manual Stepped Control
4631 :
4632 338 : if (!s_ipsc->lAlphaFieldBlanks(6)) { // Field: Glare Calculation Daylighting Reference Point Name
4633 334 : daylightControl.glareRefPtNumber = Util::FindItemInList(s_ipsc->cAlphaArgs(6),
4634 334 : dl->DaylRefPt,
4635 : &RefPointData::Name); // Field: Glare Calculation Daylighting Reference Point Name
4636 334 : if (daylightControl.glareRefPtNumber == 0) {
4637 0 : ShowSevereError(state,
4638 0 : format("{}: invalid {}=\"{}\" for object named: {}",
4639 0 : s_ipsc->cCurrentModuleObject,
4640 0 : s_ipsc->cAlphaFieldNames(6),
4641 0 : s_ipsc->cAlphaArgs(6),
4642 0 : s_ipsc->cAlphaArgs(1)));
4643 0 : ErrorsFound = true;
4644 0 : continue;
4645 : }
4646 4 : } else if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
4647 1 : ShowWarningError(state, format("No {} provided for object named: {}", s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(1)));
4648 3 : ShowContinueError(state, "No glare calculation performed, and the simulation continues.");
4649 : }
4650 :
4651 : // Field: Glare Calculation Azimuth Angle of View Direction Clockwise from Zone y-Axis
4652 338 : daylightControl.ViewAzimuthForGlare = !s_ipsc->lNumericFieldBlanks(5) ? s_ipsc->rNumericArgs(5) : 0.0;
4653 :
4654 338 : daylightControl.MaxGlareallowed = s_ipsc->rNumericArgs(6); // Field: Maximum Allowable Discomfort Glare Index
4655 338 : daylightControl.DElightGriddingResolution = s_ipsc->rNumericArgs(7); // Field: DElight Gridding Resolution
4656 :
4657 338 : int curTotalDaylRefPts = NumAlpha - 6; // first six alpha fields are not part of extensible group
4658 338 : daylightControl.TotalDaylRefPoints = curTotalDaylRefPts;
4659 338 : state.dataViewFactor->EnclSolInfo(daylightControl.enclIndex).TotalEnclosureDaylRefPoints += curTotalDaylRefPts;
4660 338 : dl->ZoneDaylight(daylightControl.zoneIndex).totRefPts += curTotalDaylRefPts;
4661 338 : dl->maxControlRefPoints = max(dl->maxControlRefPoints, curTotalDaylRefPts);
4662 338 : if ((NumNumber - 7) / 2 != daylightControl.TotalDaylRefPoints) {
4663 0 : ShowSevereError(state,
4664 0 : format("{}The number of extensible numeric fields and alpha fields is inconsistent for: {}",
4665 0 : s_ipsc->cCurrentModuleObject,
4666 0 : s_ipsc->cAlphaArgs(1)));
4667 0 : ShowContinueError(state,
4668 0 : format("For each field: {} there needs to be the following fields: Fraction Controlled by Reference Point and "
4669 : "Illuminance Setpoint at Reference Point",
4670 0 : s_ipsc->cAlphaFieldNames(NumAlpha)));
4671 0 : ErrorsFound = true;
4672 : }
4673 :
4674 338 : daylightControl.refPts.allocate(curTotalDaylRefPts);
4675 :
4676 855 : for (auto &refPt : daylightControl.refPts) {
4677 517 : refPt = DaylRefPt();
4678 : }
4679 :
4680 338 : int countRefPts = 0;
4681 855 : for (int refPtNum = 1; refPtNum <= curTotalDaylRefPts; ++refPtNum) {
4682 517 : auto &refPt = daylightControl.refPts(refPtNum);
4683 517 : refPt.num =
4684 517 : Util::FindItemInList(s_ipsc->cAlphaArgs(6 + refPtNum), dl->DaylRefPt, &RefPointData::Name); // Field: Daylighting Reference Point Name
4685 517 : if (refPt.num == 0) {
4686 0 : ShowSevereError(state,
4687 0 : format("{}: invalid {}=\"{}\" for object named: {}",
4688 0 : s_ipsc->cCurrentModuleObject,
4689 0 : s_ipsc->cAlphaFieldNames(6 + refPtNum),
4690 0 : s_ipsc->cAlphaArgs(6 + refPtNum),
4691 0 : s_ipsc->cAlphaArgs(1)));
4692 0 : ErrorsFound = true;
4693 0 : continue;
4694 : } else {
4695 517 : ++countRefPts;
4696 : }
4697 517 : refPt.fracZoneDaylit = s_ipsc->rNumericArgs(6 + refPtNum * 2); // Field: Fraction Controlled by Reference Point
4698 517 : refPt.illumSetPoint = s_ipsc->rNumericArgs(7 + refPtNum * 2); // Field: Illuminance Setpoint at Reference Point
4699 :
4700 517 : if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
4701 1533 : SetupOutputVariable(state,
4702 1022 : format("Daylighting Reference Point {} Illuminance", refPtNum),
4703 : Constant::Units::lux,
4704 511 : refPt.lums[iLum_Illum],
4705 : OutputProcessor::TimeStepType::Zone,
4706 : OutputProcessor::StoreType::Average,
4707 511 : daylightControl.Name);
4708 1533 : SetupOutputVariable(state,
4709 1022 : format("Daylighting Reference Point {} Daylight Illuminance Setpoint Exceeded Time", refPtNum),
4710 : Constant::Units::hr,
4711 511 : refPt.timeExceedingDaylightIlluminanceSetPoint,
4712 : OutputProcessor::TimeStepType::Zone,
4713 : OutputProcessor::StoreType::Sum,
4714 511 : daylightControl.Name);
4715 1533 : SetupOutputVariable(state,
4716 1022 : format("Daylighting Reference Point {} Glare Index", refPtNum),
4717 : Constant::Units::None,
4718 511 : refPt.glareIndex,
4719 : OutputProcessor::TimeStepType::Zone,
4720 : OutputProcessor::StoreType::Average,
4721 511 : daylightControl.Name);
4722 1533 : SetupOutputVariable(state,
4723 1022 : format("Daylighting Reference Point {} Glare Index Setpoint Exceeded Time", refPtNum),
4724 : Constant::Units::hr,
4725 511 : refPt.timeExceedingGlareIndexSetPoint,
4726 : OutputProcessor::TimeStepType::Zone,
4727 : OutputProcessor::StoreType::Sum,
4728 511 : daylightControl.Name);
4729 : } // if (DaylightMethod == SplitFlux)
4730 : } // for (RefPtNum)
4731 :
4732 : // Register Error if 0 DElight RefPts have been input for valid DElight object
4733 338 : if (countRefPts < 1) {
4734 0 : ShowSevereError(state, format("No Reference Points input for {} zone ={}", s_ipsc->cCurrentModuleObject, daylightControl.ZoneName));
4735 0 : ErrorsFound = true;
4736 : }
4737 :
4738 338 : Real64 sumFracs = 0.0;
4739 855 : for (auto const &refPt : daylightControl.refPts) {
4740 517 : sumFracs += refPt.fracZoneDaylit;
4741 : }
4742 :
4743 338 : daylightControl.sumFracLights = sumFracs;
4744 338 : if ((1.0 - sumFracs) > FractionTolerance) {
4745 332 : ShowWarningError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is < 1.0.");
4746 332 : ShowContinueError(state,
4747 332 : format("..discovered in {}=\"{}\", only {:.3R} of the zone or space is controlled.",
4748 166 : s_ipsc->cCurrentModuleObject,
4749 166 : daylightControl.Name,
4750 : sumFracs));
4751 172 : } else if ((sumFracs - 1.0) > FractionTolerance) {
4752 0 : ShowSevereError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is > 1.0.");
4753 0 : ShowContinueError(state,
4754 0 : format("..discovered in {}=\"{}\", trying to control {:.3R} of the zone or space.",
4755 0 : s_ipsc->cCurrentModuleObject,
4756 0 : daylightControl.Name,
4757 : sumFracs));
4758 0 : ErrorsFound = true;
4759 : }
4760 :
4761 338 : if (daylightControl.LightControlType == LtgCtrlType::Stepped && daylightControl.LightControlSteps <= 0) {
4762 0 : ShowWarningError(state, "GetDaylightingControls: For Stepped Control, the number of steps must be > 0");
4763 0 : ShowContinueError(state,
4764 0 : format("..discovered in \"{}\" for Zone=\"{}\", will use 1", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(2)));
4765 0 : daylightControl.LightControlSteps = 1;
4766 : }
4767 676 : SetupOutputVariable(state,
4768 : "Daylighting Lighting Power Multiplier",
4769 : Constant::Units::None,
4770 338 : daylightControl.PowerReductionFactor,
4771 : OutputProcessor::TimeStepType::Zone,
4772 : OutputProcessor::StoreType::Average,
4773 338 : daylightControl.Name);
4774 : } // for (controlNum)
4775 65 : } // GetDaylightingControls()
4776 :
4777 65 : void GeometryTransformForDaylighting(EnergyPlusData &state)
4778 : {
4779 : // AUTHOR Fred Winkelmann
4780 : // DATE WRITTEN March 2002
4781 : // MODIFIED Glazer - July 2016 - separated this from GetInput function
4782 : // For splitflux daylighting, transform the geometry
4783 65 : auto &dl = state.dataDayltg;
4784 65 : auto const &s_surf = state.dataSurface;
4785 :
4786 : // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
4787 65 : Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
4788 65 : Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
4789 : // these are only for Building Rotation for Appendix G when using world coordinate system
4790 65 : Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
4791 65 : Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
4792 :
4793 65 : bool doTransform = false;
4794 65 : Real64 OldAspectRatio = 1.0;
4795 65 : Real64 NewAspectRatio = 1.0;
4796 :
4797 65 : CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
4798 403 : for (auto &daylCntrl : dl->daylightControl) {
4799 338 : auto &zone = state.dataHeatBal->Zone(daylCntrl.zoneIndex);
4800 :
4801 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
4802 338 : Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
4803 338 : Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
4804 :
4805 338 : Real64 rLightLevel = InternalHeatGains::GetDesignLightingLevelForZone(state, daylCntrl.zoneIndex);
4806 338 : InternalHeatGains::CheckLightsReplaceableMinMaxForZone(state, daylCntrl.zoneIndex);
4807 :
4808 855 : for (int refPtNum = 1; refPtNum <= daylCntrl.TotalDaylRefPoints; ++refPtNum) {
4809 517 : auto &refPt = daylCntrl.refPts(refPtNum);
4810 517 : auto &curRefPt = dl->DaylRefPt(refPt.num); // get the active daylighting:referencepoint
4811 517 : curRefPt.indexToFracAndIllum = refPtNum; // back reference to the index to the ZoneDaylight structure arrays related to reference points
4812 517 : if (s_surf->DaylRefWorldCoordSystem) {
4813 : // transform only by appendix G rotation
4814 7 : refPt.absCoords.x = curRefPt.coords.x * CosBldgRotAppGonly - curRefPt.coords.y * SinBldgRotAppGonly;
4815 7 : refPt.absCoords.y = curRefPt.coords.x * SinBldgRotAppGonly + curRefPt.coords.y * CosBldgRotAppGonly;
4816 7 : refPt.absCoords.z = curRefPt.coords.z;
4817 : } else {
4818 : // Transform reference point coordinates into building coordinate system
4819 510 : Real64 Xb = curRefPt.coords.x * CosZoneRelNorth - curRefPt.coords.y * SinZoneRelNorth + zone.OriginX;
4820 510 : Real64 Yb = curRefPt.coords.x * SinZoneRelNorth + curRefPt.coords.y * CosZoneRelNorth + zone.OriginY;
4821 : // Transform into World Coordinate System
4822 510 : refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
4823 510 : refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
4824 510 : refPt.absCoords.z = curRefPt.coords.z + zone.OriginZ;
4825 510 : if (doTransform) {
4826 0 : Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
4827 0 : Real64 Yo = refPt.absCoords.y;
4828 : // next derotate the building
4829 0 : Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
4830 0 : Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
4831 : // translate
4832 0 : Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
4833 0 : Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
4834 : // rerotate
4835 0 : refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
4836 0 : refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
4837 : }
4838 : }
4839 :
4840 517 : auto &orp = state.dataOutRptPredefined;
4841 517 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtZone, curRefPt.Name, daylCntrl.ZoneName);
4842 517 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlName, curRefPt.Name, daylCntrl.Name);
4843 517 : if (daylCntrl.DaylightMethod == DaylightingMethod::SplitFlux) {
4844 511 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "SplitFlux");
4845 : } else {
4846 6 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "DElight");
4847 : }
4848 : // ( 1=continuous, 2=stepped, 3=continuous/off )
4849 517 : if (daylCntrl.LightControlType == LtgCtrlType::Continuous) {
4850 60 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous");
4851 457 : } else if (daylCntrl.LightControlType == LtgCtrlType::Stepped) {
4852 131 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Stepped");
4853 326 : } else if (daylCntrl.LightControlType == LtgCtrlType::ContinuousOff) {
4854 326 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous/Off");
4855 : }
4856 517 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtFrac, curRefPt.Name, refPt.fracZoneDaylit);
4857 517 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWInst, curRefPt.Name, rLightLevel);
4858 517 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWCtrl, curRefPt.Name, rLightLevel * refPt.fracZoneDaylit);
4859 :
4860 517 : if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
4861 0 : refPt.inBounds = false;
4862 0 : ShowWarningError(state,
4863 0 : format("GeometryTransformForDaylighting: Reference point X Value outside Zone Min/Max X, Zone={}", zone.Name));
4864 0 : ShowContinueError(state,
4865 0 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
4866 0 : refPt.absCoords.x,
4867 0 : zone.MinimumX,
4868 0 : zone.MaximumX));
4869 0 : ShowContinueError(
4870 : state,
4871 0 : format("...X Reference Distance Outside MinimumX= {:.4R} m.",
4872 0 : (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
4873 : }
4874 517 : if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
4875 0 : refPt.inBounds = false;
4876 0 : ShowWarningError(state,
4877 0 : format("GeometryTransformForDaylighting: Reference point Y Value outside Zone Min/Max Y, Zone={}", zone.Name));
4878 0 : ShowContinueError(state,
4879 0 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
4880 0 : refPt.absCoords.x,
4881 0 : zone.MinimumY,
4882 0 : zone.MaximumY));
4883 0 : ShowContinueError(
4884 : state,
4885 0 : format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
4886 0 : (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
4887 : }
4888 517 : if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
4889 0 : refPt.inBounds = false;
4890 0 : ShowWarningError(state,
4891 0 : format("GeometryTransformForDaylighting: Reference point Z Value outside Zone Min/Max Z, Zone={}", zone.Name));
4892 0 : ShowContinueError(state,
4893 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
4894 0 : refPt.absCoords.z,
4895 0 : zone.MinimumZ,
4896 0 : zone.MaximumZ));
4897 0 : ShowContinueError(
4898 : state,
4899 0 : format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
4900 0 : (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
4901 : }
4902 : } // for (refPt)
4903 : } // for (daylightCtrl)
4904 65 : } // GeometryTransformForDaylighting()
4905 :
4906 65 : void GetInputDayliteRefPt(EnergyPlusData &state, bool &ErrorsFound)
4907 : {
4908 : // Perform GetInput function for the Daylighting:ReferencePoint object
4909 : // Glazer - July 2016
4910 65 : auto const &dl = state.dataDayltg;
4911 65 : auto &ip = state.dataInputProcessing->inputProcessor;
4912 65 : auto const &s_ipsc = state.dataIPShortCut;
4913 65 : s_ipsc->cCurrentModuleObject = "Daylighting:ReferencePoint";
4914 :
4915 65 : int RefPtNum = 0;
4916 : int IOStat;
4917 : int NumAlpha;
4918 : int NumNumber;
4919 :
4920 65 : int TotRefPoints = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4921 :
4922 65 : dl->DaylRefPt.allocate(TotRefPoints);
4923 582 : for (auto &pt : dl->DaylRefPt) {
4924 1034 : ip->getObjectItem(state,
4925 517 : s_ipsc->cCurrentModuleObject,
4926 : ++RefPtNum,
4927 517 : s_ipsc->cAlphaArgs,
4928 : NumAlpha,
4929 517 : s_ipsc->rNumericArgs,
4930 : NumNumber,
4931 : IOStat,
4932 517 : s_ipsc->lNumericFieldBlanks,
4933 517 : s_ipsc->lAlphaFieldBlanks,
4934 517 : s_ipsc->cAlphaFieldNames,
4935 517 : s_ipsc->cNumericFieldNames);
4936 517 : pt.Name = s_ipsc->cAlphaArgs(1);
4937 517 : pt.ZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
4938 517 : if (pt.ZoneNum == 0) {
4939 1 : int spaceNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->space);
4940 1 : if (spaceNum == 0) {
4941 0 : ShowSevereError(state,
4942 0 : format("{}=\"{}\", invalid {}=\"{}\".",
4943 0 : s_ipsc->cCurrentModuleObject,
4944 0 : s_ipsc->cAlphaArgs(1),
4945 0 : s_ipsc->cAlphaFieldNames(2),
4946 0 : s_ipsc->cAlphaArgs(2)));
4947 0 : ErrorsFound = true;
4948 : } else {
4949 1 : pt.ZoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
4950 : }
4951 : }
4952 517 : pt.coords = {s_ipsc->rNumericArgs(1), s_ipsc->rNumericArgs(2), s_ipsc->rNumericArgs(3)};
4953 : }
4954 65 : }
4955 :
4956 1070 : bool doesDayLightingUseDElight(EnergyPlusData const &state)
4957 : {
4958 1070 : auto const &dl = state.dataDayltg;
4959 1402 : for (auto const &znDayl : dl->daylightControl) {
4960 335 : if (znDayl.DaylightMethod == DaylightingMethod::DElight) {
4961 3 : return true;
4962 : }
4963 : }
4964 1067 : return false;
4965 : }
4966 :
4967 801 : void CheckTDDsAndLightShelvesInDaylitZones(EnergyPlusData &state)
4968 : {
4969 : // SUBROUTINE INFORMATION:
4970 : // AUTHOR Brent Griffith
4971 : // DATE WRITTEN Dec 2007
4972 :
4973 : // PURPOSE OF THIS SUBROUTINE:
4974 : // This subroutine checks daylighting input for TDDs and light shelfs
4975 : // which need to be checked after daylighting input has been read in (CR 7145)
4976 : // (eventually this should be changed once/if implementations change to decouple from daylighting calcs so that
4977 : // these devices can be used in models without daylighting controls
4978 : // CR 7145 was for TDDs, but also implenting check for light shelves, the other "daylighting device"
4979 :
4980 : // METHODOLOGY EMPLOYED:
4981 : // loop thru daylighting devices and check that their zones have daylight controls
4982 :
4983 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4984 801 : auto &s_surf = state.dataSurface;
4985 :
4986 801 : bool ErrorsFound = false;
4987 :
4988 803 : for (auto const &pipe : state.dataDaylightingDevicesData->TDDPipe) {
4989 2 : int SurfNum = pipe.Diffuser;
4990 2 : if (SurfNum > 0) {
4991 2 : int const pipeEnclNum = s_surf->Surface(SurfNum).SolarEnclIndex;
4992 2 : if (state.dataViewFactor->EnclSolInfo(pipeEnclNum).TotalEnclosureDaylRefPoints == 0) {
4993 0 : ShowWarningError(state,
4994 0 : format("DaylightingDevice:Tubular = {}: is not connected to a Zone that has Daylighting, no visible transmittance "
4995 : "will be modeled through the daylighting device.",
4996 0 : pipe.Name));
4997 : }
4998 : } else { // SurfNum == 0
4999 : // should not come here (would have already been caught in TDD get input), but is an error
5000 0 : ShowSevereError(state, format("DaylightingDevice:Tubular = {}: Diffuser surface not found ", pipe.Name));
5001 0 : ErrorsFound = true;
5002 : }
5003 : } // for (pipe)
5004 :
5005 802 : for (auto const &shelf : state.dataDaylightingDevicesData->Shelf) {
5006 1 : if (shelf.Window == 0) {
5007 : // should not come here (would have already been caught in shelf get input), but is an error
5008 0 : ShowSevereError(state, format("DaylightingDevice:Shelf = {}: window not found ", shelf.Name));
5009 0 : ErrorsFound = true;
5010 : }
5011 : } // for (shelf)
5012 :
5013 801 : if (ErrorsFound) {
5014 0 : ShowFatalError(state, "CheckTDDsAndLightShelvesInDaylitZones: Errors in DAYLIGHTING input.");
5015 : }
5016 801 : }
5017 :
5018 801 : void AssociateWindowShadingControlWithDaylighting(EnergyPlusData &state)
5019 : {
5020 801 : auto &dl = state.dataDayltg;
5021 801 : auto &s_surf = state.dataSurface;
5022 :
5023 874 : for (auto &winShadeControl : s_surf->WindowShadingControl) {
5024 73 : if (winShadeControl.DaylightingControlName.empty()) {
5025 48 : continue;
5026 : }
5027 25 : int found = -1;
5028 27 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5029 27 : if (Util::SameString(winShadeControl.DaylightingControlName, dl->daylightControl(daylightCtrlNum).Name)) {
5030 25 : found = daylightCtrlNum;
5031 25 : break;
5032 : }
5033 : }
5034 25 : if (found > 0) {
5035 25 : winShadeControl.DaylightControlIndex = found;
5036 : } else {
5037 0 : ShowWarningError(state, "AssociateWindowShadingControlWithDaylighting: Daylighting object name used in WindowShadingControl not found.");
5038 0 : ShowContinueError(state,
5039 0 : format("..The WindowShadingControl object=\"{}\" and referenes an object named: \"{}\"",
5040 0 : winShadeControl.Name,
5041 0 : winShadeControl.DaylightingControlName));
5042 : }
5043 801 : }
5044 801 : } // AssociateWindowShadingControlWithDaylighting()
5045 :
5046 65 : void GetLightWellData(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
5047 : {
5048 :
5049 : // SUBROUTINE INFORMATION:
5050 : // AUTHOR Fred Winkelmann
5051 : // DATE WRITTEN Apr 2004
5052 :
5053 : // PURPOSE OF THIS SUBROUTINE:
5054 : // Gets data for a light well associated with a rectangular exterior window.
5055 : // Calculates light well efficiency, defined as the ratio of the amount of visible
5056 : // solar radiation leaving a well to the amount entering the well.
5057 :
5058 : // METHODOLOGY EMPLOYED:
5059 : // Based on fit to Fig. 8-21, "Efficiency factors for various depths of light wells
5060 : // based on well-interreflectance values," Lighting Handbook, 8th Edition, Illuminating
5061 : // Engineering Society of North America, 1993.
5062 :
5063 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5064 :
5065 : int IOStat; // IO Status when calling get input subroutine
5066 : int NumAlpha; // Number of alpha names being passed
5067 : int NumProp; // Number of properties being passed
5068 : int TotLightWells; // Total Light Well objects
5069 :
5070 65 : auto &ip = state.dataInputProcessing->inputProcessor;
5071 65 : auto &s_surf = state.dataSurface;
5072 65 : auto const &s_ipsc = state.dataIPShortCut;
5073 :
5074 : // Get the total number of Light Well objects
5075 65 : s_ipsc->cCurrentModuleObject = "DaylightingDevice:LightWell";
5076 65 : TotLightWells = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
5077 65 : if (TotLightWells == 0) {
5078 64 : return;
5079 : }
5080 :
5081 3 : for (int loop = 1; loop <= TotLightWells; ++loop) {
5082 :
5083 4 : ip->getObjectItem(state,
5084 2 : s_ipsc->cCurrentModuleObject,
5085 : loop,
5086 2 : s_ipsc->cAlphaArgs,
5087 : NumAlpha,
5088 2 : s_ipsc->rNumericArgs,
5089 : NumProp,
5090 : IOStat,
5091 2 : s_ipsc->lNumericFieldBlanks,
5092 2 : s_ipsc->lAlphaFieldBlanks,
5093 2 : s_ipsc->cAlphaFieldNames,
5094 2 : s_ipsc->cNumericFieldNames);
5095 :
5096 2 : int SurfNum = Util::FindItemInList(s_ipsc->cAlphaArgs(1), s_surf->Surface);
5097 2 : if (SurfNum == 0) {
5098 0 : ShowSevereError(
5099 0 : state, format("{}: invalid {}=\"{}\" not found.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
5100 0 : ErrorsFound = true;
5101 0 : continue;
5102 : }
5103 :
5104 2 : auto const &surf = s_surf->Surface(SurfNum);
5105 2 : auto &surfWin = s_surf->SurfaceWindow(SurfNum);
5106 : // Check that associated surface is an exterior window
5107 : // True if associated surface is not an exterior window
5108 2 : if (surf.Class != SurfaceClass::Window && surf.ExtBoundCond != ExternalEnvironment) {
5109 0 : ShowSevereError(state,
5110 0 : format("{}: invalid {}=\"{}\" - not an exterior window.",
5111 0 : s_ipsc->cCurrentModuleObject,
5112 0 : s_ipsc->cAlphaFieldNames(1),
5113 0 : s_ipsc->cAlphaArgs(1)));
5114 0 : ErrorsFound = true;
5115 0 : continue;
5116 : }
5117 :
5118 : // Associated surface is an exterior window; calculate light well efficiency.
5119 2 : surfWin.lightWellEff = 1.0;
5120 2 : Real64 HeightWell = s_ipsc->rNumericArgs(1); // Well height (from window to bottom of well) (m)
5121 2 : Real64 PerimWell = s_ipsc->rNumericArgs(2); // Well perimeter (at bottom of well) (m)
5122 2 : Real64 AreaWell = s_ipsc->rNumericArgs(3); // Well area (at bottom of well) (m2)
5123 2 : Real64 VisReflWell = s_ipsc->rNumericArgs(4); // Area-weighted visible reflectance of well walls
5124 :
5125 : // Warning if light well area is less than window area
5126 2 : if (AreaWell < (surf.Area + s_surf->SurfWinDividerArea(SurfNum) - 0.1)) {
5127 0 : ShowSevereError(
5128 0 : state, format("{}: invalid {}=\"{}\" - Areas.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
5129 0 : ShowContinueError(state, format("has Area of Bottom of Well={:.1R} that is less than window area={:.1R}", surf.Area, AreaWell));
5130 : }
5131 :
5132 2 : if (HeightWell >= 0.0 && PerimWell > 0.0 && AreaWell > 0.0) {
5133 2 : Real64 WellCavRatio = 2.5 * HeightWell * PerimWell / AreaWell;
5134 2 : surfWin.lightWellEff = std::exp(-WellCavRatio * (0.16368 - 0.14467 * VisReflWell));
5135 : }
5136 : } // End of loop over light well objects
5137 : } // GetLightWellData()
5138 :
5139 4371653 : inline WinCover findWinShadingStatus(EnergyPlusData &state, int const IWin)
5140 : {
5141 : // Return the window shading status, 1=unshaded, 2=shaded
5142 :
5143 4371653 : auto &s_surf = state.dataSurface;
5144 4371653 : bool WinShadedNoGlareControl = IS_SHADED_NO_GLARE_CTRL(s_surf->SurfWinShadingFlag(IWin));
5145 :
5146 8737874 : return ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) && (WinShadedNoGlareControl || s_surf->SurfWinSolarDiffusing(IWin)))
5147 8737874 : ? WinCover::Shaded
5148 4371653 : : WinCover::Bare;
5149 : }
5150 :
5151 1119521 : Real64 DayltgGlare(EnergyPlusData &state,
5152 : int IL, // Reference point index: 1=first ref pt, 2=second ref pt
5153 : Real64 BLUM, // Window background (surround) luminance (cd/m2)
5154 : int const daylightCtrlNum // Current daylighting control number
5155 : )
5156 : {
5157 :
5158 : // SUBROUTINE INFORMATION:
5159 : // AUTHOR Fred Winkelmann
5160 : // DATE WRITTEN July 1997
5161 :
5162 : // PURPOSE OF THIS SUBROUTINE:
5163 : // CALCULATE GLARE INDEX.
5164 :
5165 : // METHODOLOGY EMPLOYED:
5166 : // Called from DayltgInteriorIllum. Finds glare index at reference
5167 : // point no. IL in a space using the Cornell/BRS large source
5168 : // glare formula. BLUM is the background luminance (cd/m**2).
5169 : // TH comment 1/21/2010: The SurfaceWindow(IWin)%ShadingFlag has to be set
5170 : // before calling this subroutine. For switchable glazings this is tricky
5171 : // because the ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop)
5172 : // may change every time step to represent intermediate switched state.
5173 :
5174 : // REFERENCES:
5175 : // Based on DOE-2.1E subroutine DGLARE.
5176 :
5177 1119521 : Real64 GTOT = 0.0; // Glare constant
5178 :
5179 1119521 : auto &dl = state.dataDayltg;
5180 :
5181 : // Loop over exterior windows associated with zone
5182 1119521 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5183 1119521 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
5184 3906503 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5185 2786982 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5186 2786982 : WinCover winCover = findWinShadingStatus(state, IWin);
5187 : // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
5188 : // below, which is (0.2936)**0.6
5189 2786982 : auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
5190 2786982 : Real64 GTOT1 = 0.4794 * std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6) //
5191 2786982 : * std::pow(extWin.solidAngWtd, 0.8);
5192 2786982 : Real64 GTOT2 = BLUM + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
5193 2786982 : GTOT += GTOT1 / (GTOT2 + 0.000001);
5194 : }
5195 :
5196 : // Glare index (adding 0.000001 prevents LOG10 (0))
5197 1119521 : return max(0.0, 10.0 * std::log10(GTOT + 0.000001));
5198 : }
5199 :
5200 1015 : void DayltgGlareWithIntWins(EnergyPlusData &state,
5201 : int const daylightCtrlNum // Current daylighting control number
5202 : )
5203 : {
5204 :
5205 : // SUBROUTINE INFORMATION:
5206 : // AUTHOR Fred Winkelmann
5207 : // DATE WRITTEN March 2004
5208 :
5209 : // PURPOSE OF THIS SUBROUTINE:
5210 : // Calculate daylighting glare index for zones with interior windows.
5211 :
5212 : // METHODOLOGY EMPLOYED:
5213 : // Finds glare index at reference point IL in a daylit zone using the Cornell/BRS large source
5214 : // glare formula. Takes into account inter-reflected illuminance from light entering
5215 : // the zone through interior windows
5216 :
5217 : // REFERENCES:
5218 : // Based on subroutine DayltgGlare.
5219 :
5220 1015 : Real64 GTOT = 0.0; // Glare constant(?) // TODO: does this need to be reset for every refPt?
5221 :
5222 : // Calculate background luminance including effect of inter-reflected illuminance from light
5223 : // entering zone through its interior windows
5224 1015 : auto &dl = state.dataDayltg;
5225 1015 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5226 1015 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
5227 1015 : int RefPoints = thisDayltgCtrl.TotalDaylRefPoints; // Number of daylighting reference points in zone
5228 2030 : for (int IL = 1; IL <= RefPoints; ++IL) {
5229 1015 : auto &refPt = thisDayltgCtrl.refPts(IL);
5230 :
5231 1015 : Real64 BackgroundLum = refPt.lums[iLum_Back] + thisEnclDaylight.InterReflIllFrIntWins * thisEnclDaylight.aveVisDiffReflect / Constant::Pi;
5232 1015 : BackgroundLum = max(refPt.illumSetPoint * thisEnclDaylight.aveVisDiffReflect / Constant::Pi, BackgroundLum);
5233 :
5234 : // Loop over exterior windows associated with zone
5235 2030 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5236 1015 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5237 1015 : WinCover winCover = findWinShadingStatus(state, IWin);
5238 : // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
5239 : // below, which is (0.2936)**0.6
5240 1015 : auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
5241 1015 : Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6)) //
5242 1015 : * std::pow(extWin.solidAngWtd, 0.8);
5243 1015 : Real64 GTOT2 = BackgroundLum + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
5244 1015 : GTOT += GTOT1 / (GTOT2 + 0.000001);
5245 : }
5246 :
5247 : // Glare index
5248 1015 : refPt.glareIndex = max(0.0, 10.0 * std::log10(GTOT + 0.000001));
5249 : } // for (IL)
5250 1015 : } // DaylGlareWithIntWins()
5251 :
5252 4835 : void DayltgExtHorizIllum(EnergyPlusData &state,
5253 : Illums &HI // Horizontal illuminance from sky for different sky types
5254 : )
5255 : {
5256 :
5257 : // SUBROUTINE INFORMATION:
5258 : // AUTHOR Fred Winkelmann
5259 : // DATE WRITTEN July 1997
5260 :
5261 : // PURPOSE OF THIS SUBROUTINE:
5262 : // Calculates exterior daylight illuminance.
5263 :
5264 : // METHODOLOGY EMPLOYED:
5265 : // Called by CalcDayltgCoefficients. Calculates illuminance
5266 : // on unobstructed horizontal surface by integrating
5267 : // over the luminance distribution of standard CIE skies.
5268 : // Calculates horizontal beam illuminance.
5269 : // REFERENCES:
5270 : // Based on DOE-2.1E subroutine DHILL.
5271 :
5272 : // Argument array dimensioning
5273 :
5274 : // SUBROUTINE PARAMETER DEFINITIONS:
5275 4835 : Real64 constexpr DTH = (2.0 * Constant::Pi) / double(NTH); // Sky integration azimuth stepsize (radians)
5276 4835 : Real64 constexpr DPH = Constant::PiOvr2 / double(NPH); // Sky integration altitude stepsize (radians)
5277 :
5278 : // Integrate to obtain illuminance from sky.
5279 : // The contribution in lumens/m2 from a patch of sky at altitude PH and azimuth TH
5280 : // is L(TH,PH)*SIN(PH)*COS(PH)*DTH*DPH, where L(TH,PH) is the luminance
5281 : // of the patch in cd/m2.
5282 4835 : auto &dl = state.dataDayltg;
5283 :
5284 : // Init
5285 4835 : if (dl->DayltgExtHorizIllum_firstTime) {
5286 585 : for (int IPH = 1; IPH <= NPH; ++IPH) {
5287 520 : dl->PH[IPH] = (IPH - 0.5) * DPH;
5288 520 : dl->SPHCPH[IPH] = std::sin(dl->PH[IPH]) * std::cos(dl->PH[IPH]); // DA = COS(PH)*DTH*DPH
5289 : }
5290 1235 : for (int ITH = 1; ITH <= NTH; ++ITH) {
5291 1170 : dl->TH[ITH] = (ITH - 0.5) * DTH;
5292 : }
5293 65 : dl->DayltgExtHorizIllum_firstTime = false;
5294 : }
5295 :
5296 19340 : HI = Illums();
5297 :
5298 : // Sky integration
5299 43515 : for (int IPH = 1; IPH <= NPH; ++IPH) {
5300 38680 : Real64 const PH_IPH = dl->PH[IPH];
5301 38680 : Real64 const SPHCPH_IPH = dl->SPHCPH[IPH];
5302 734920 : for (int ITH = 1; ITH <= NTH; ++ITH) {
5303 696240 : Real64 const TH_ITH = dl->TH[ITH];
5304 3481200 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5305 2784960 : HI.sky[iSky] += DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH_ITH, PH_IPH) * SPHCPH_IPH;
5306 : }
5307 : }
5308 : }
5309 :
5310 24175 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5311 19340 : HI.sky[iSky] *= DTH * DPH;
5312 : }
5313 :
5314 : // Direct solar horizontal illum (for unit direct normal illuminance)
5315 4835 : HI.sun = dl->sunAngles.sinPhi * 1.0;
5316 4835 : } // DayltgExtHorizIllum()
5317 :
5318 : // Product of solar transmittances of exterior obstructions
5319 7026280 : Real64 DayltgHitObstruction(EnergyPlusData &state,
5320 : int const IHOUR, // Hour number
5321 : int const IWin, // Window index
5322 : Vector3<Real64> const &R1, // Origin of ray (m)
5323 : Vector3<Real64> const &RN // Unit vector along ray
5324 : )
5325 : {
5326 :
5327 : // SUBROUTINE INFORMATION:
5328 : // AUTHOR Fred Winkelmann
5329 : // DATE WRITTEN July 1997
5330 : // MODIFIED FCW, May 2003: update list of surface classes that qualify as obstructions;
5331 : // add interior surfaces as possible obstructors;
5332 : // return from DO loop over surfaces as soon as any obstruction is hit;
5333 : // FCW, July 2003: change from returning whether an obstruction is hit or not
5334 : // to product of solar transmittances of hit obstructions.
5335 : // FCW, Nov 2003: remove interior surfaces as possible obstructors since there
5336 : // is now a separate check for interior obstructions; exclude windows and
5337 : // doors as obstructors since if they are obstructors their base surfaces will
5338 : // also be obstructors
5339 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5340 :
5341 : // PURPOSE OF THIS SUBROUTINE:
5342 : // Determines the product of the solar transmittances of the obstructions hit by a ray
5343 : // from R1 in the direction of vector RN.
5344 :
5345 : // REFERENCES:
5346 : // Based on DOE-2.1E subroutine DHITSH.
5347 :
5348 7026280 : auto &s_surf = state.dataSurface;
5349 : // Local declarations
5350 : bool hit; // True iff a particular obstruction is hit
5351 :
5352 7026280 : Real64 ObTrans = 1.0;
5353 :
5354 7026280 : auto const &window = s_surf->Surface(IWin);
5355 7026280 : int const window_iBaseSurf = window.BaseSurf;
5356 :
5357 7026280 : Vector3<Real64> DayltgHitObstructionHP;
5358 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5359 : // Building elements are assumed to be opaque
5360 : // A shadowing surface is opaque unless its transmittance schedule value is non-zero
5361 7026280 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5362 :
5363 58062399 : for (int ISurf : s_surf->AllShadowPossObstrSurfaceList) {
5364 53412214 : auto const &surface = s_surf->Surface(ISurf);
5365 53412214 : SurfaceClass IType = surface.Class;
5366 53412214 : if ((IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf)) {
5367 48010663 : hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
5368 48010663 : if (hit) { // Building element is hit (assumed opaque)
5369 45108 : ObTrans = 0.0;
5370 45108 : break;
5371 : }
5372 5401551 : } else if (surface.IsShadowing) {
5373 706392 : hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
5374 706392 : if (hit) { // Shading surface is hit
5375 : // Get solar transmittance of the shading surface
5376 75782 : Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
5377 75782 : if (Trans < 1.e-6) {
5378 75782 : ObTrans = 0.0;
5379 75782 : break;
5380 : } else {
5381 0 : ObTrans *= Trans;
5382 : }
5383 : }
5384 : }
5385 4771075 : }
5386 :
5387 : } else { // Surface octree search
5388 :
5389 2255205 : auto const &window_base(window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window);
5390 2255205 : auto const *window_base_p(&window_base);
5391 :
5392 : // Lambda function for the octree to test for surface hit and update transmittance if hit
5393 336544556 : auto solarTransmittance = [=, &state, &R1, &RN, &hit, &ObTrans](SurfaceData const &surface) -> bool {
5394 336544556 : if (!surface.IsShadowPossibleObstruction) {
5395 262288709 : return false; // Do Consider separate octree without filtered surfaces
5396 : }
5397 74255847 : DataSurfaces::SurfaceClass const sClass(surface.Class);
5398 74255847 : Vector3<Real64> HP;
5399 74255847 : if ((sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && (&surface != window_base_p)) {
5400 70926934 : hit = PierceSurface(surface, R1, RN, HP);
5401 70926934 : if (hit) { // Building element is hit (assumed opaque)
5402 94717 : ObTrans = 0.0;
5403 94717 : return true;
5404 : }
5405 3328913 : } else if (surface.IsShadowing) {
5406 1138122 : hit = PierceSurface(surface, R1, RN, HP);
5407 1138122 : if (hit) { // Shading surface is hit
5408 : // Get solar transmittance of the shading surface
5409 11230 : Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
5410 11230 : if (Trans < 1.e-6) {
5411 11230 : ObTrans = 0.0;
5412 11230 : return true;
5413 : } else {
5414 0 : ObTrans *= Trans;
5415 0 : return ObTrans == 0.0;
5416 : }
5417 : }
5418 : }
5419 74149900 : return false;
5420 74255847 : };
5421 :
5422 : // Check octree surface candidates for hits: short circuits if zero transmittance reached
5423 2255205 : Vector3<Real64> const RN_inv(SurfaceOctreeCube::safe_inverse(RN));
5424 2255205 : state.dataHeatBalMgr->surfaceOctree.processSomeSurfaceRayIntersectsCube(state, R1, RN, RN_inv, solarTransmittance);
5425 2255205 : }
5426 :
5427 7026280 : return ObTrans;
5428 7026280 : } // DayltgHitObstruction()
5429 :
5430 2850160 : bool DayltgHitInteriorObstruction(EnergyPlusData &state,
5431 : int const IWin, // Window index
5432 : Vector3<Real64> const &R1, // Origin of ray (m)
5433 : Vector3<Real64> const &R2 // Destination of ray (m)
5434 : )
5435 : {
5436 :
5437 : // SUBROUTINE INFORMATION:
5438 : // AUTHOR Fred Winkelmann
5439 : // DATE WRITTEN July 1997
5440 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5441 :
5442 : // PURPOSE OF THIS SUBROUTINE:
5443 : // This subroutine checks for interior obstructions between reference point and window element.
5444 :
5445 2850160 : auto &s_surf = state.dataSurface;
5446 :
5447 : // Preconditions
5448 2850160 : assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
5449 :
5450 2850160 : bool hit = false;
5451 2850160 : Vector3<Real64> RN = (R2 - R1).normalize(); // Make unit vector
5452 2850160 : Real64 const d12 = distance(R1, R2); // Distance between R1 and R2
5453 :
5454 2850160 : auto const &window = s_surf->Surface(IWin);
5455 2850160 : int const window_Enclosure = window.SolarEnclIndex;
5456 2850160 : int const window_iBaseSurf = window.BaseSurf;
5457 2850160 : auto const &window_base = window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window;
5458 2850160 : int const window_base_iExtBoundCond = window_base.ExtBoundCond;
5459 :
5460 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5461 2850160 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5462 : // Hit coordinates, if ray hits an obstruction
5463 2308235 : Vector3<Real64> DayltgHitInteriorObstructionHP;
5464 :
5465 76265487 : for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
5466 73988574 : auto const &surface = s_surf->Surface(ISurf);
5467 73988574 : SurfaceClass IType = surface.Class;
5468 73988574 : if ((surface.IsShadowing) || // Shadowing surface
5469 73360072 : ((surface.SolarEnclIndex == window_Enclosure) && // Wall/ceiling/floor is in same zone as window
5470 34720785 : (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf) &&
5471 : (ISurf != window_base_iExtBoundCond))) // Exclude window's base or base-adjacent surfaces
5472 : {
5473 12351769 : hit = PierceSurface(state, ISurf, R1, RN, d12, DayltgHitInteriorObstructionHP); // Check if R2-R1 segment pierces surface
5474 12351769 : if (hit) {
5475 31322 : break; // Segment pierces surface: Don't check the rest
5476 : }
5477 : }
5478 : }
5479 :
5480 2308235 : } else { // Surface octree search
5481 :
5482 541925 : auto const *window_base_p = &window_base;
5483 541925 : auto const &window_base_adjacent = window_base_iExtBoundCond > 0 ? s_surf->Surface(window_base_iExtBoundCond) : window_base;
5484 541925 : auto const *window_base_adjacent_p = &window_base_adjacent;
5485 :
5486 : // Lambda function for the octree to test for surface hit
5487 85400407 : auto surfaceHit = [=, &R1, &hit](SurfaceData const &surface) -> bool {
5488 85400407 : DataSurfaces::SurfaceClass const sClass = surface.Class;
5489 85400407 : Vector3<Real64> HP; // Hit point
5490 85400407 : if ((surface.IsShadowing) || // Shadowing surface
5491 84893297 : ((surface.SolarEnclIndex == window_Enclosure) && // Surface is in same zone as window
5492 2996178 : (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5493 3074641 : (&surface != window_base_p) && (&surface != window_base_adjacent_p))) // Exclude window's base or base-adjacent surfaces
5494 : {
5495 3039826 : hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5496 3039826 : return hit;
5497 : } else {
5498 82360581 : return false;
5499 : }
5500 85942332 : };
5501 :
5502 : // Check octree surface candidates until a hit is found, if any
5503 541925 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
5504 541925 : }
5505 :
5506 2850160 : return hit;
5507 2850160 : } // DayltgHitInteriorObstruction()
5508 :
5509 20412 : bool DayltgHitBetWinObstruction(EnergyPlusData &state,
5510 : int const IWin1, // Surface number of origin window
5511 : int const IWin2, // Surface number of destination window
5512 : Vector3<Real64> const &R1, // Origin of ray (on IWin1) (m)
5513 : Vector3<Real64> const &R2 // Destination of ray (on IWin2) (m)
5514 : )
5515 : {
5516 :
5517 : // SUBROUTINE INFORMATION:
5518 : // AUTHOR Fred Winkelmann
5519 : // DATE WRITTEN Feb 2004
5520 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5521 :
5522 : // PURPOSE OF THIS SUBROUTINE:
5523 : // Determines if a ray from point R1 on window IWin1 to point R2
5524 : // on window IWin2 hits an obstruction
5525 :
5526 20412 : auto &s_surf = state.dataSurface;
5527 :
5528 : // Preconditions
5529 20412 : assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
5530 :
5531 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5532 : SurfaceClass IType; // Surface type/class
5533 :
5534 20412 : bool hit = false;
5535 20412 : Vector3<Real64> RN = (R2 - R1).normalize(); // Unit vector
5536 :
5537 20412 : Real64 const d12 = distance(R1, R2); // Distance between R1 and R2 (m)
5538 :
5539 20412 : auto const &window1 = s_surf->Surface(IWin1);
5540 20412 : int const window1_iBaseSurf = window1.BaseSurf;
5541 20412 : auto const &window1_base = window1_iBaseSurf > 0 ? s_surf->Surface(window1_iBaseSurf) : window1;
5542 20412 : int const window1_base_iExtBoundCond = window1_base.ExtBoundCond;
5543 :
5544 20412 : auto const &window2 = s_surf->Surface(IWin2);
5545 20412 : int const window2_Enclosure = window2.SolarEnclIndex;
5546 20412 : int const window2_iBaseSurf = window2.BaseSurf;
5547 20412 : auto const &window2_base = window2_iBaseSurf > 0 ? s_surf->Surface(window2_iBaseSurf) : window2;
5548 20412 : int const window2_base_iExtBoundCond = window2_base.ExtBoundCond;
5549 :
5550 : // Preconditions
5551 : // assert( window1.Zone == window2_Zone ); //? This is violated in PurchAirWithDoubleFacadeDaylighting so then why the asymmetry
5552 : // of only checking for wall/roof/floor for window2 zone below?
5553 :
5554 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5555 20412 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5556 :
5557 612360 : for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
5558 591948 : auto const &surface = s_surf->Surface(ISurf);
5559 591948 : IType = surface.Class;
5560 591948 : if ((surface.IsShadowing) || // Shadowing surface
5561 591948 : ((surface.SolarEnclIndex == window2_Enclosure) && // Wall/ceiling/floor is in same zone as windows
5562 163296 : (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5563 122472 : (ISurf != window1_iBaseSurf) && (ISurf != window2_iBaseSurf) && // Exclude windows' base surfaces
5564 81648 : (ISurf != window1_base_iExtBoundCond) && (ISurf != window2_base_iExtBoundCond))) // Exclude windows' base-adjacent surfaces
5565 : {
5566 81648 : Vector3<Real64> HP;
5567 81648 : hit = PierceSurface(state, ISurf, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5568 81648 : if (hit) {
5569 0 : break; // Segment pierces surface: Don't check the rest
5570 : }
5571 81648 : }
5572 : }
5573 :
5574 : } else { // Surface octree search
5575 :
5576 0 : auto const *window1_base_p = &window1_base;
5577 0 : auto const &window1_base_adjacent = window1_base_iExtBoundCond > 0 ? s_surf->Surface(window1_base_iExtBoundCond) : window1_base;
5578 0 : auto const *window1_base_adjacent_p = &window1_base_adjacent;
5579 :
5580 0 : auto const *window2_base_p = &window2_base;
5581 0 : auto const &window2_base_adjacent = (window2_base_iExtBoundCond > 0) ? s_surf->Surface(window2_base_iExtBoundCond) : window2_base;
5582 0 : auto const *window2_base_adjacent_p = &window2_base_adjacent;
5583 :
5584 : // Lambda function for the octree to test for surface hit
5585 0 : auto surfaceHit = [=, &R1, &RN, &hit](SurfaceData const &surface) -> bool {
5586 0 : DataSurfaces::SurfaceClass const sClass = surface.Class;
5587 0 : Vector3<Real64> HP;
5588 0 : if ((surface.IsShadowing) || // Shadowing surface
5589 0 : ((surface.SolarEnclIndex == window2_Enclosure) && // Surface is in same zone as window
5590 0 : (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5591 0 : (&surface != window1_base_p) && (&surface != window2_base_p) && // Exclude windows' base surfaces
5592 0 : (&surface != window1_base_adjacent_p) && (&surface != window2_base_adjacent_p))) // Exclude windows' base-adjacent surfaces
5593 : {
5594 0 : hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5595 0 : return hit;
5596 : } else {
5597 0 : return false;
5598 : }
5599 0 : };
5600 :
5601 : // Check octree surface candidates until a hit is found, if any
5602 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
5603 : }
5604 :
5605 20412 : return hit;
5606 20412 : } // DayltingHitBetWinObstruction()
5607 :
5608 2828408 : void initDaylighting(EnergyPlusData &state, bool const initSurfaceHeatBalancefirstTime)
5609 : {
5610 : // For daylit zones, calculate interior daylight illuminance at reference points and
5611 : // simulate lighting control system to get overhead electric lighting reduction
5612 : // factor due to daylighting.
5613 2828408 : auto &dl = state.dataDayltg;
5614 2828408 : auto &s_surf = state.dataSurface;
5615 :
5616 26639690 : for (int SurfNum : s_surf->AllExtSolWindowSurfaceList) {
5617 28576935 : for (auto &refPt : s_surf->SurfaceWindow(SurfNum).refPts) {
5618 4765653 : refPt.illumFromWinRep = refPt.lumWinRep = 0.0;
5619 : }
5620 2828408 : }
5621 :
5622 : // Reset space power reduction factors
5623 23412419 : for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
5624 20584011 : dl->spacePowerReductionFactor(spaceNum) = 1.0;
5625 : }
5626 4480841 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5627 1652433 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5628 1652433 : thisDayltgCtrl.PowerReductionFactor = 1.0;
5629 1652433 : if (state.dataEnvrn->PreviousSolRadPositive) {
5630 : // Reset to zero only if there was solar in the previous timestep, otherwise these are already zero
5631 505493 : dl->enclDaylight(thisDayltgCtrl.enclIndex).InterReflIllFrIntWins = 0.0; // inter-reflected illuminance from interior windows
5632 1157774 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
5633 652281 : auto &refPt = thisDayltgCtrl.refPts(refPtNum);
5634 652281 : refPt.lums[iLum_Illum] = 0.0;
5635 652281 : refPt.glareIndex = 0.0;
5636 652281 : refPt.timeExceedingGlareIndexSetPoint = 0.0;
5637 652281 : refPt.timeExceedingDaylightIlluminanceSetPoint = 0.0;
5638 : }
5639 : }
5640 :
5641 1652433 : if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0) {
5642 825914 : if (initSurfaceHeatBalancefirstTime) {
5643 0 : DisplayString(state, "Computing Interior Daylighting Illumination");
5644 : }
5645 825914 : DayltgInteriorIllum(state, daylightCtrlNum);
5646 : }
5647 : }
5648 :
5649 : // The following report variables are valid only for daylit zones/enclosures without interior windows
5650 2828408 : if (state.dataEnvrn->SunIsUp) {
5651 11712088 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
5652 11122520 : if ((state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) ||
5653 823877 : (state.dataViewFactor->EnclSolInfo(enclNum).HasInterZoneWindow)) {
5654 9475781 : continue;
5655 : }
5656 :
5657 822862 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
5658 2387344 : for (int extWinNum = 1; extWinNum <= thisEnclDaylight.NumOfDayltgExtWins; ++extWinNum) {
5659 1564482 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(extWinNum);
5660 1564482 : WinCover winCover = WinCover::Bare;
5661 4606954 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
5662 3042472 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
5663 81060 : winCover = WinCover::Shaded;
5664 : }
5665 1564482 : int refPtCount = 0;
5666 3135075 : for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
5667 1570593 : auto &daylCtrl = dl->daylightControl(controlNum);
5668 1570593 : if (daylCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
5669 0 : continue;
5670 : }
5671 :
5672 3928157 : for (int refPtNum = 1; refPtNum <= daylCtrl.TotalDaylRefPoints; ++refPtNum) {
5673 2357564 : ++refPtCount; // Count reference points across each daylighting control in the same enclosure
5674 2357564 : auto &refPt = s_surf->SurfaceWindow(IWin).refPts(refPtCount);
5675 2357564 : auto const &daylCtrlRefPt = daylCtrl.refPts(refPtNum);
5676 2357564 : refPt.illumFromWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Illum][(int)winCover];
5677 2357564 : refPt.lumWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Source][(int)winCover];
5678 : }
5679 1564482 : } // for (controlNum)
5680 : } // for (extWinNum)
5681 : } // for (enclNum)
5682 : } // if (SunIsUp)
5683 :
5684 2828408 : if (state.dataEnvrn->SunIsUp && (int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
5685 1015 : if (initSurfaceHeatBalancefirstTime) {
5686 0 : DisplayString(state, "Computing Interior Daylighting Illumination for TDD pipes");
5687 : }
5688 1015 : DayltgInteriorTDDIllum(state);
5689 : }
5690 :
5691 4480841 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5692 1652433 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5693 :
5694 : // RJH DElight Modification Begin - Call to DElight electric lighting control subroutine
5695 : // Check if the sun is up and the current Thermal Zone hosts a Daylighting:DElight object
5696 1652433 : if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0 && (thisDayltgCtrl.DaylightMethod == DaylightingMethod::DElight)) {
5697 2037 : int zoneNum = thisDayltgCtrl.zoneIndex;
5698 : // Call DElight interior illuminance and electric lighting control subroutine
5699 2037 : Real64 dPowerReducFac = 1.0; // Return value Electric Lighting Power Reduction Factor for current Zone and Timestep
5700 2037 : Real64 dHISKFFC = state.dataEnvrn->HISKF * DataDElight::LUX2FC;
5701 2037 : Real64 dHISUNFFC = state.dataEnvrn->HISUNF * DataDElight::LUX2FC;
5702 2037 : Real64 dSOLCOS1 = state.dataEnvrn->SOLCOS.x;
5703 2037 : Real64 dSOLCOS2 = state.dataEnvrn->SOLCOS.y;
5704 2037 : Real64 dSOLCOS3 = state.dataEnvrn->SOLCOS.z;
5705 2037 : Real64 dLatitude = state.dataEnvrn->Latitude;
5706 2037 : Real64 dCloudFraction = state.dataEnvrn->CloudFraction;
5707 : // Init Error Flag to 0 (no Warnings or Errors) (returned from DElight)
5708 2037 : int iErrorFlag = 0;
5709 :
5710 2037 : DElightManagerF::DElightElecLtgCtrl(len(state.dataHeatBal->Zone(zoneNum).Name),
5711 2037 : state.dataHeatBal->Zone(zoneNum).Name,
5712 : dLatitude,
5713 : dHISKFFC,
5714 : dHISUNFFC,
5715 : dCloudFraction,
5716 : dSOLCOS1,
5717 : dSOLCOS2,
5718 : dSOLCOS3,
5719 : dPowerReducFac,
5720 : iErrorFlag);
5721 : // Check Error Flag for Warnings or Errors returning from DElight
5722 : // RJH 2008-03-07: If no warnings/errors then read refpt illuminances for standard output reporting
5723 2037 : if (iErrorFlag != 0) {
5724 0 : std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
5725 : // Open DElight Electric Lighting Error File for reading
5726 0 : auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
5727 0 : bool elOpened = iDElightErrorFile.good();
5728 :
5729 : // Sequentially read lines in DElight Electric Lighting Error File
5730 : // and process them using standard EPlus warning/error handling calls
5731 0 : bool bEndofErrFile = false;
5732 0 : while (!bEndofErrFile && elOpened) {
5733 0 : auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
5734 0 : if (cErrorLine.eof) {
5735 0 : bEndofErrFile = true;
5736 0 : continue;
5737 : }
5738 :
5739 : // Is the current line a Warning message?
5740 0 : if (has_prefix(cErrorLine.data, "WARNING: ")) {
5741 0 : cErrorMsg = cErrorLine.data.substr(9);
5742 0 : ShowWarningError(state, cErrorMsg);
5743 : }
5744 : // Is the current line an Error message?
5745 0 : if (has_prefix(cErrorLine.data, "ERROR: ")) {
5746 0 : cErrorMsg = cErrorLine.data.substr(7);
5747 0 : ShowSevereError(state, cErrorMsg);
5748 0 : iErrorFlag = 1;
5749 : }
5750 0 : }
5751 :
5752 : // Close DElight Error File and delete
5753 :
5754 0 : if (elOpened) {
5755 0 : iDElightErrorFile.close();
5756 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
5757 : }
5758 : // If any DElight Error occurred then ShowFatalError to terminate
5759 0 : if (iErrorFlag > 0) {
5760 0 : ShowFatalError(state, "End of DElight Error Messages");
5761 : }
5762 0 : } else { // RJH 2008-03-07: No errors
5763 : // extract reference point illuminance values from DElight Electric Lighting dump file for reporting
5764 : // Open DElight Electric Lighting Dump File for reading
5765 2037 : auto iDElightErrorFile = state.files.outputDelightEldmpFilePath.try_open(state.files.outputControl.delighteldmp); // (THIS_AUTO_OK)
5766 2037 : bool elOpened = iDElightErrorFile.is_open();
5767 :
5768 : // Sequentially read lines in DElight Electric Lighting Dump File
5769 : // and extract refpt illuminances for standard EPlus output handling
5770 2037 : bool bEndofErrFile = false;
5771 2037 : int iDElightRefPt = 0; // Reference Point number for reading DElight Dump File (eplusout.delighteldmp)
5772 8148 : while (!bEndofErrFile && elOpened) {
5773 6111 : auto line = iDElightErrorFile.read<Real64>(); // (THIS_AUTO_OK)
5774 6111 : Real64 dRefPtIllum = line.data; // tmp var for reading RefPt illuminance
5775 6111 : if (line.eof) {
5776 2037 : bEndofErrFile = true;
5777 2037 : continue;
5778 : }
5779 : // Increment refpt counter
5780 4074 : ++iDElightRefPt;
5781 : // Assure refpt index does not exceed number of refpts in this zone
5782 4074 : if (iDElightRefPt <= thisDayltgCtrl.TotalDaylRefPoints) {
5783 4074 : thisDayltgCtrl.refPts(iDElightRefPt).lums[iLum_Illum] = dRefPtIllum;
5784 : }
5785 : }
5786 :
5787 : // Close DElight Electric Lighting Dump File and delete
5788 2037 : if (elOpened) {
5789 2037 : iDElightErrorFile.close();
5790 2037 : FileSystem::removeFile(iDElightErrorFile.filePath);
5791 : };
5792 2037 : }
5793 : // Store the calculated total zone Power Reduction Factor due to DElight daylighting
5794 : // in the ZoneDaylight structure for later use
5795 2037 : thisDayltgCtrl.PowerReductionFactor = dPowerReducFac;
5796 : }
5797 : // RJH DElight Modification End - Call to DElight electric lighting control subroutine
5798 : }
5799 :
5800 2828408 : if (state.dataEnvrn->SunIsUp && !state.dataGlobal->DoingSizing) {
5801 996072 : DayltgInteriorMapIllum(state);
5802 : }
5803 23363819 : for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
5804 41119422 : for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
5805 20584011 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
5806 44426535 : for (int SurfNum = thisSpace.WindowSurfaceFirst; SurfNum <= thisSpace.WindowSurfaceLast; ++SurfNum) {
5807 23842524 : s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
5808 23842524 : if (IS_SHADED(s_surf->SurfWinShadingFlag(SurfNum))) {
5809 286801 : s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 1.0;
5810 : } else {
5811 23555723 : s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
5812 : }
5813 : }
5814 20535411 : }
5815 : }
5816 2828408 : }
5817 :
5818 2828408 : void manageDaylighting(EnergyPlusData &state)
5819 : {
5820 2828408 : auto &dl = state.dataDayltg;
5821 :
5822 2828408 : if (state.dataEnvrn->SunIsUp && (state.dataEnvrn->BeamSolarRad + state.dataEnvrn->GndSolarRad + state.dataEnvrn->DifSolarRad > 0.0)) {
5823 7489275 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
5824 6563746 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
5825 6563746 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || !enclSol.HasInterZoneWindow) {
5826 6562731 : continue;
5827 : }
5828 :
5829 1015 : DayltgInterReflIllFrIntWins(state, enclNum);
5830 2030 : for (int daylightCtrlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
5831 1015 : DayltgGlareWithIntWins(state, daylightCtrlNum);
5832 1015 : }
5833 : }
5834 925529 : DayltgElecLightingControl(state);
5835 1902879 : } else if (dl->mapResultsToReport && state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
5836 41 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
5837 22 : ReportIllumMap(state, MapNum);
5838 : }
5839 19 : dl->mapResultsToReport = false;
5840 : }
5841 2828408 : } // manageDaylighting()
5842 :
5843 825914 : void DayltgInteriorIllum(EnergyPlusData &state,
5844 : int const daylightCtrlNum) // Daylighting:Controls number
5845 : {
5846 :
5847 : // SUBROUTINE INFORMATION:
5848 : // AUTHOR Fred Winkelmann
5849 : // DATE WRITTEN July 1997
5850 : // MODIFIED March 2000, FCW: interpolate clear-sky daylight factors using
5851 : // HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
5852 : // only HourOfDay was used
5853 : // Jan 2001, FCW: interpolate in slat angle for windows with blinds
5854 : // that have movable slats
5855 : // Oct 2002, LKL: changed interpolation steps to HourOfDay/WeightNow
5856 : // LastHour/WeightPreviousHour
5857 : // Aug 2003, FCW: fix bug that prevented shadingControlType =
5858 : // MEETDAYLIGHTILLUMINANCESETPOINT from working
5859 : // Mar 2004, FCW: fix bug in calc of illuminance setpoint contribution
5860 : // to background luminance: now it is divided by pi to give cd/m2
5861 : // Mar 2004, FCW: modify to handle daylighting through interior windows
5862 : // June 2009, TH: modified for thermochromic windows
5863 : // Jan 2010, TH (CR 7984): added iterations for switchable windows with shading
5864 : // control of MeetDaylightIlluminanceSetpoint and glare control is active
5865 : // Also corrected bugs (CR 7988) for switchable glazings not related to CR 7984
5866 :
5867 : // PURPOSE OF THIS SUBROUTINE:
5868 : // Using daylighting factors and exterior illuminance, determine
5869 : // the current-hour interior daylight illuminance and glare index
5870 : // at each reference point in a space. Deploy window shading window by window
5871 : // if glare control is active for window and if the acceptable glare index
5872 : // is exceeded at both reference points.
5873 :
5874 : // Called by InitSurfaceHeatBalance.
5875 :
5876 : // REFERENCES:
5877 : // Based on DOE-2.1E subroutine DINTIL.
5878 825914 : auto &dl = state.dataDayltg;
5879 825914 : auto &s_surf = state.dataSurface;
5880 :
5881 825914 : Real64 constexpr tmpSWIterStep(0.05); // step of switching factor, assuming maximum of 20 switching states
5882 :
5883 : int NREFPT; // Number of daylighting reference points
5884 : int iSky1; // Sky type index values for averaging two sky types
5885 : int iSky2;
5886 825914 : Array1D<Real64> SetPnt; // Illuminance setpoint at reference points (lux)
5887 825914 : Array1D<Real64> GLRNEW; // New glare index at reference point
5888 :
5889 825914 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5890 825914 : int enclNum = thisDayltgCtrl.enclIndex;
5891 825914 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
5892 : int ISWFLG; // Switchable glazing flag: =1 if one or more windows in a zone
5893 : // has switchable glazing that adjusts visible transmittance to just meet
5894 : // daylighting setpoint; =0 otherwise.
5895 : Real64 VTRAT; // Ratio between switched and unswitched visible transmittance at normal incidence
5896 : Real64 BACL; // Window background (surround) luminance for glare calc (cd/m2)
5897 : Real64 SkyWeight; // Weighting factor used to average two different sky types
5898 : Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
5899 : // luminous efficacy and horizontal illuminance from averaged sky
5900 : bool GlareFlag; // True if maximum glare is exceeded
5901 :
5902 : Real64 VTRatio; // VT (visible transmittance) ratio = VTNow / VTMaster
5903 : Real64 VTNow; // VT of the time step actual TC window
5904 : Real64 VTMaster; // VT of the base/master TC window
5905 :
5906 825914 : Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> tmpDaylFromWinAtRefPt;
5907 :
5908 825914 : bool breakOuterLoop(false);
5909 825914 : bool continueOuterLoop(false);
5910 :
5911 : struct ShadeGroupLums
5912 : {
5913 : Array1D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> WDAYIL; // Illuminance from window at ref-point
5914 : Array1D<std::array<Real64, (int)Lum::Num>> RDAYIL; // Illuminance from window at ref-point after closing shade
5915 : Real64 switchedWinLum;
5916 : Real64 unswitchedWinLum;
5917 : Real64 switchedTvis;
5918 : Real64 unswitchedTvis;
5919 : Real64 lumRatio;
5920 : };
5921 :
5922 825914 : Array1D<ShadeGroupLums> shadeGroupsLums;
5923 :
5924 : // Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, iLum_Num>> WDAYIL; // Illuminance from window at reference point
5925 : // (second index)
5926 : // the number of shade deployment groups (third index)
5927 : // Array2D<std::array<Real64, (int)DataSurfaces::WinCover::Num>> WBACLU; // Background illuminance from window at reference point (second index)
5928 : // the number of shade deployment groups (third index)
5929 : // Array2D<std::array<Real64, iLum_Num>> RDAYIL; // Illuminance from window at reference point after closing shade
5930 : // Array2D<Real64> RBACLU; // Background illuminance from window at reference point after closing shade
5931 : // Array1D<Real64> DILLSW; // Illuminance a ref point from a group of windows that can be switched together,
5932 : // Array1D<Real64> DILLUN; // and from those that aren't (lux)
5933 : // Array1D<Real64> TVIS1; // Visible transmittance at normal incidence of unswitched glazing
5934 : // Array1D<Real64> TVIS2; // Visible transmittance at normal incidence of fully-switched glazing
5935 : // Array1D<Real64> ASETIL; // Illuminance ratio (lux)
5936 :
5937 825914 : if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
5938 2037 : return;
5939 : }
5940 :
5941 823877 : NREFPT = thisDayltgCtrl.TotalDaylRefPoints;
5942 :
5943 823877 : if (dl->DayltgInteriorIllum_firstTime) {
5944 65 : dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5945 65 : dl->DayltgInteriorIllum_firstTime = false;
5946 : }
5947 :
5948 : // size these for the maximum of the shade deployment order
5949 823877 : shadeGroupsLums.allocate(dl->maxShadeDeployOrderExtWins);
5950 929001 : for (auto &shadeGroupLums : shadeGroupsLums) {
5951 105124 : shadeGroupLums.WDAYIL.allocate(dl->maxControlRefPoints);
5952 105124 : shadeGroupLums.RDAYIL.allocate(dl->maxControlRefPoints);
5953 : }
5954 :
5955 : // Three arrays to save original clear and dark (fully switched) states'
5956 : // zone/window daylighting properties.
5957 823877 : tmpDaylFromWinAtRefPt.allocate(dl->maxNumRefPtInAnyDaylCtrl, dl->maxEnclSubSurfaces);
5958 :
5959 823877 : SetPnt.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5960 823877 : dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5961 823877 : GLRNEW.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5962 :
5963 1887698 : for (int iRefPt = 1; iRefPt <= (int)dl->maxNumRefPtInAnyDaylCtrl; ++iRefPt) {
5964 11674967 : for (int iExtWin = 1; iExtWin <= (int)dl->maxEnclSubSurfaces; ++iExtWin) {
5965 10611146 : auto &tmpDayl = tmpDaylFromWinAtRefPt(iRefPt, iExtWin);
5966 10611146 : tmpDayl[iLum_Illum] = tmpDayl[iLum_Back] = tmpDayl[iLum_Source] = {0.0, 0.0};
5967 : }
5968 : }
5969 :
5970 : // Initialize reference point illuminance and window background luminance
5971 1881490 : for (int IL = 1; IL <= NREFPT; ++IL) {
5972 1057613 : auto &refPt = thisDayltgCtrl.refPts(IL);
5973 1057613 : SetPnt(IL) = refPt.illumSetPoint;
5974 1057613 : dl->DaylIllum(IL) = 0.0;
5975 1057613 : refPt.lums[iLum_Back] = 0.0;
5976 : }
5977 :
5978 823877 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
5979 400476 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
5980 400476 : iSky1 = (int)SkyType::Clear;
5981 400476 : iSky2 = (int)SkyType::ClearTurbid;
5982 423401 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
5983 90478 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
5984 90478 : iSky1 = (int)SkyType::ClearTurbid;
5985 90478 : iSky2 = (int)SkyType::Intermediate;
5986 : } else { // Sky is average of intermediate and overcast
5987 332923 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
5988 332923 : iSky1 = (int)SkyType::Intermediate;
5989 332923 : iSky2 = (int)SkyType::Overcast;
5990 : }
5991 :
5992 : // First loop over exterior windows associated with this zone. The window may be an exterior window in
5993 : // the zone or an exterior window in an adjacent zone that shares an interior window with the zone.
5994 : // Find contribution of each window to the daylight illum and to the glare numerator at each reference point.
5995 : // Use shading flags set in WindowShadingManager.
5996 2395485 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5997 1571608 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5998 :
5999 : // Added TH 6/29/2009 for thermochromic windows
6000 1571608 : VTRatio = 1.0;
6001 1571608 : if (NREFPT > 0) {
6002 1571608 : int const IConst = s_surf->Surface(IWin).Construction;
6003 1571608 : auto const &construction = state.dataConstruction->Construct(IConst);
6004 1571608 : if (construction.isTCWindow) {
6005 : // For thermochromic windows, daylight and glare factors are always calculated
6006 : // based on the master construction. They need to be adjusted by the VTRatio, including:
6007 : // ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
6008 : // DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
6009 0 : VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
6010 0 : VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
6011 0 : VTRatio = VTNow / VTMaster;
6012 : }
6013 : }
6014 :
6015 4585890 : bool ShadedOrDiffusingGlassWin = s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
6016 3014282 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin));
6017 :
6018 1571608 : Real64 wgtCurrHr = state.dataGlobal->WeightNow;
6019 1571608 : Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
6020 :
6021 1571608 : std::array<Illums, (int)DataSurfaces::WinCover::Num> SFHR; // Sky source luminance factor for sky type, bare/shaded window
6022 1571608 : std::array<Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
6023 1571608 : std::array<Illums, (int)DataSurfaces::WinCover::Num> BFHR; // Sky background luminance factor for sky type, bare/shaded window
6024 :
6025 : // Loop over reference points
6026 3930187 : for (int IL = 1; IL <= NREFPT; ++IL) {
6027 :
6028 2358579 : auto const &daylFacCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Bare];
6029 2358579 : auto const &daylFacPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Bare];
6030 : // Daylight factors for current sun position
6031 2358579 : auto const &illFacCurr = daylFacCurr[iLum_Illum];
6032 2358579 : auto const &illFacPrev = daylFacPrev[iLum_Illum];
6033 2358579 : auto &dfhr = DFHR[iWinCover_Bare];
6034 2358579 : auto const &backFacCurr = daylFacCurr[iLum_Back];
6035 2358579 : auto const &backFacPrev = daylFacPrev[iLum_Back];
6036 2358579 : auto &bfhr = BFHR[iWinCover_Bare];
6037 2358579 : auto const &sourceFacCurr = daylFacCurr[iLum_Source];
6038 2358579 : auto const &sourceFacPrev = daylFacPrev[iLum_Source];
6039 2358579 : auto &sfhr = SFHR[iWinCover_Bare];
6040 :
6041 2358579 : auto const &daylFac2Curr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
6042 2358579 : auto const &daylFac2Prev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
6043 :
6044 2358579 : auto const &illFac2Curr = daylFac2Curr[iLum_Illum];
6045 2358579 : auto const &illFac2Prev = daylFac2Prev[iLum_Illum];
6046 2358579 : auto &dfhr2 = DFHR[iWinCover_Shaded];
6047 2358579 : auto const &backFac2Curr = daylFac2Curr[iLum_Back];
6048 2358579 : auto const &backFac2Prev = daylFac2Prev[iLum_Back];
6049 2358579 : auto &bfhr2 = BFHR[iWinCover_Shaded];
6050 2358579 : auto const &sourceFac2Curr = daylFac2Curr[iLum_Source];
6051 2358579 : auto const &sourceFac2Prev = daylFac2Prev[iLum_Source];
6052 2358579 : auto &sfhr2 = SFHR[iWinCover_Shaded];
6053 :
6054 : #ifdef GET_OUT
6055 : auto const &daylFacShCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
6056 : auto const &daylFacShPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
6057 :
6058 : auto const &illFacShCurr = daylFacShCurr[iLum_Illum];
6059 : auto const &illFacShPrev = daylFacShPrev[iLum_Illum];
6060 :
6061 : auto const &backFacShCurr = daylFacShCurr[iLum_Back];
6062 : auto const &backFacShPrev = daylFacShPrev[iLum_Back];
6063 :
6064 : auto const &sourceFacShCurr = daylFacShCurr[iLum_Source];
6065 : auto const &sourceFacShPrev = daylFacShPrev[iLum_Source];
6066 : #endif // GET_OUT
6067 11792895 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
6068 :
6069 : // ===Bare window===
6070 : // Sky daylight factor for sky type (second index), bare/shaded window (first index)
6071 9434316 : dfhr.sky[iSky] = VTRatio * (wgtCurrHr * illFacCurr.sky[iSky] + wgtPrevHr * illFacPrev.sky[iSky]);
6072 9434316 : bfhr.sky[iSky] = VTRatio * (wgtCurrHr * backFacCurr.sky[iSky] + wgtPrevHr * backFacPrev.sky[iSky]);
6073 9434316 : sfhr.sky[iSky] = VTRatio * (wgtCurrHr * sourceFacCurr.sky[iSky] + wgtPrevHr * sourceFacPrev.sky[iSky]);
6074 :
6075 9434316 : if (ShadedOrDiffusingGlassWin) {
6076 :
6077 : // ===Shaded window or window with diffusing glass===
6078 : // Shade, screen, blind with fixed slats, or diffusing glass
6079 639984 : dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illFac2Curr.sky[iSky] + wgtPrevHr * illFac2Prev.sky[iSky]);
6080 639984 : bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backFac2Curr.sky[iSky] + wgtPrevHr * backFac2Prev.sky[iSky]);
6081 639984 : sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceFac2Curr.sky[iSky] + wgtPrevHr * sourceFac2Prev.sky[iSky]);
6082 : } // End of check if window is shaded or has diffusing glass
6083 : } // for (iSky)
6084 :
6085 : // Sun daylight factor for bare/shaded window
6086 4717158 : DFHR[iWinCover_Bare].sun =
6087 2358579 : VTRatio * (wgtCurrHr * (illFacCurr.sun + illFacCurr.sunDisk) + wgtPrevHr * (illFacPrev.sun + illFacPrev.sunDisk));
6088 :
6089 : // Sun background luminance factor for bare/shaded window
6090 4717158 : BFHR[iWinCover_Bare].sun =
6091 2358579 : VTRatio * (wgtCurrHr * (backFacCurr.sun + backFacCurr.sunDisk) + wgtPrevHr * (backFacPrev.sun + backFacPrev.sunDisk));
6092 :
6093 : // Sun source luminance factor for bare/shaded window
6094 4717158 : SFHR[iWinCover_Bare].sun =
6095 2358579 : VTRatio * (wgtCurrHr * (sourceFacCurr.sun + sourceFacCurr.sunDisk) + wgtPrevHr * (sourceFacPrev.sun + sourceFacPrev.sunDisk));
6096 :
6097 2358579 : if (ShadedOrDiffusingGlassWin) {
6098 :
6099 : // ===Shaded window or window with diffusing glass===
6100 : // Shade, screen, blind with fixed slats, or diffusing glass
6101 159996 : DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * illFac2Curr.sun + wgtPrevHr * illFac2Prev.sun);
6102 159996 : BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * backFac2Curr.sun + wgtPrevHr * backFac2Prev.sun);
6103 159996 : SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * sourceFac2Curr.sun + wgtPrevHr * sourceFac2Prev.sun);
6104 :
6105 159996 : auto const &surfShade = s_surf->surfShades(IWin);
6106 159996 : if (!surfShade.blind.slatBlockBeam) {
6107 159996 : DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * illFac2Curr.sunDisk + wgtPrevHr * illFac2Prev.sunDisk);
6108 159996 : BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * backFac2Curr.sunDisk + wgtPrevHr * backFac2Prev.sunDisk);
6109 159996 : SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * sourceFac2Curr.sunDisk + wgtPrevHr * sourceFac2Prev.sunDisk);
6110 : }
6111 : } // End of check if window is shaded or has diffusing glass
6112 :
6113 : // Get illuminance at ref point from bare and shaded window by
6114 : // multiplying daylight factors by exterior horizontal illuminance
6115 :
6116 : // Adding 0.001 in the following prevents zero HorIllSky in early morning or late evening when sun
6117 : // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
6118 2358579 : auto const &gilskCurr = dl->horIllum[state.dataGlobal->HourOfDay];
6119 2358579 : auto const &gilskPrev = dl->horIllum[state.dataGlobal->PreviousHour];
6120 :
6121 : // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
6122 : // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
6123 : // also calculated in DayltgLuminousEfficacy.
6124 : Real64 horIllSky1 =
6125 2358579 : state.dataGlobal->WeightNow * gilskCurr.sky[iSky1] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky1] + 0.001;
6126 : Real64 horIllSky2 =
6127 2358579 : state.dataGlobal->WeightNow * gilskCurr.sky[iSky2] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky2] + 0.001;
6128 :
6129 2358579 : HorIllSkyFac = state.dataEnvrn->HISKF / ((1 - SkyWeight) * horIllSky2 + SkyWeight * horIllSky1);
6130 :
6131 2358579 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6132 2358579 : auto &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6133 4877154 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
6134 4717158 : auto const &dfhr3 = DFHR[iWinCover];
6135 4717158 : auto const &bfhr3 = BFHR[iWinCover];
6136 4717158 : auto const &sfhr3 = SFHR[iWinCover];
6137 :
6138 : // What is this?
6139 4717158 : if (iWinCover == iWinCover_Shaded && !ShadedOrDiffusingGlassWin) {
6140 2198583 : break;
6141 : }
6142 :
6143 2518575 : daylFromWinAtRefPt[iLum_Illum][iWinCover] =
6144 2518575 : dfhr3.sun * state.dataEnvrn->HISUNF +
6145 2518575 : HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * horIllSky1 + dfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
6146 2518575 : daylFromWinAtRefPt[iLum_Back][iWinCover] =
6147 2518575 : bfhr3.sun * state.dataEnvrn->HISUNF +
6148 2518575 : HorIllSkyFac * (bfhr3.sky[iSky1] * SkyWeight * horIllSky1 + bfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
6149 2518575 : daylFromWinAtRefPt[iLum_Source][iWinCover] =
6150 2518575 : sfhr3.sun * state.dataEnvrn->HISUNF +
6151 2518575 : HorIllSkyFac * (sfhr3.sky[iSky1] * SkyWeight * horIllSky1 + sfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
6152 :
6153 2518575 : daylFromWinAtRefPt[iLum_Source][iWinCover] = max(daylFromWinAtRefPt[iLum_Source][iWinCover], 0.0);
6154 :
6155 : // Added TH 1/21/2010 - save the original clear and dark (fully switched) states'
6156 : // zone daylighting values, needed for switachable glazings
6157 2518575 : tmpDayl[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
6158 2518575 : tmpDayl[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
6159 2518575 : tmpDayl[iLum_Source][iWinCover] = daylFromWinAtRefPt[iLum_Source][iWinCover];
6160 : } // for for (iWinCover)
6161 :
6162 : } // End of reference point loop, IL
6163 : } // End of first loop over exterior windows associated with this zone
6164 :
6165 : // Initialize flag that one or more windows has switchable glazing
6166 : // control that adjusts visible transmittance to just meet dayltg setpoint
6167 : // (and the window has not already been switched)
6168 823877 : ISWFLG = 0;
6169 :
6170 : // Second loop over windows. Find total daylight illuminance and background luminance
6171 : // for each ref pt from all exterior windows associated with the zone. Use shading flags.
6172 : // This illuminance excludes contribution of inter-reflected illuminance produced by solar
6173 : // entering the zone through interior windows (which is calculated in DayltgInterReflIllFrIntWins.
6174 :
6175 2395485 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
6176 1571608 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
6177 1571608 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6178 1571608 : if (s_surf->Surface(IWin).HasShadeControl && ISWFLG == 0) {
6179 122172 : if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
6180 665 : s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) {
6181 665 : ISWFLG = 1;
6182 : }
6183 : }
6184 :
6185 : // Determine if illuminance contribution is from bare or shaded window
6186 : // For switchable glazings with shading control type of WSCT_MeetDaylIlumSetp,
6187 : // the shading flag is initialized at GlassConditionallyLightened (20), and
6188 : // the window is initialized at clear state: IS = 1
6189 : // For other windows with glare control, the shading flag is initialized at >10, to be determined
6190 1571608 : WinCover winCover = findWinShadingStatus(state, IWin);
6191 :
6192 3930187 : for (int IL = 1; IL <= NREFPT; ++IL) {
6193 2358579 : auto &refPt = thisDayltgCtrl.refPts(IL);
6194 2358579 : dl->DaylIllum(IL) += refPt.extWins(loop).lums[iLum_Illum][(int)winCover];
6195 2358579 : refPt.lums[iLum_Back] += refPt.extWins(loop).lums[iLum_Back][(int)winCover];
6196 : }
6197 : } // End of second window loop over exterior windows associated with this zone
6198 :
6199 : // Optical switching control (e.g. electrochromic glass) to adjust
6200 : // window's vis trans downward so daylight level equals or is as
6201 : // close as possible to the illuminance setpoint at first reference point.
6202 : // Assumes vis trans in the fully switched state is less than that in the
6203 : // unswitched state. Assumes some windows in a space may have this control and
6204 : // others not.
6205 :
6206 823877 : int count = 0;
6207 :
6208 : // If daylight illuminance is above setpoint, allow switching
6209 823877 : if (ISWFLG != 0 && dl->DaylIllum(1) > SetPnt(1)) {
6210 :
6211 : // array of flags to indicate that previously groups would have already shaded this window
6212 385 : Array1D_bool previously_shaded;
6213 385 : previously_shaded.dimension(dl->maxDayltgExtWins, false);
6214 :
6215 : // Third loop over windows. Get illuminance at ref pt 1 from
6216 : // windows that can be switched (DILLSW) with a group and those that can't (DILLUN).
6217 : // Windows that can be switched are initially in the unswitched state. For subsequent
6218 : // groups the windows in previous groups are fully switched.
6219 1155 : for (auto &shadeGroupLum : shadeGroupsLums) {
6220 770 : shadeGroupLum.switchedWinLum = shadeGroupLum.unswitchedWinLum = 0.0;
6221 : }
6222 :
6223 1155 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6224 770 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6225 770 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6226 2310 : for (const int IWin : listOfExtWin) {
6227 1540 : ++count;
6228 : // need to map back to the original order of the "loop" to not change all the other data structures
6229 1540 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6230 1540 : if (loop == 0) {
6231 0 : continue;
6232 : }
6233 :
6234 1540 : if (!s_surf->Surface(IWin).HasShadeControl) {
6235 0 : continue;
6236 : }
6237 :
6238 1540 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6239 1540 : WinCover winCover = findWinShadingStatus(state, IWin);
6240 :
6241 1540 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Illum];
6242 1540 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened &&
6243 2310 : s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
6244 770 : !previously_shaded(loop)) {
6245 770 : shadeGroupLums.switchedWinLum += daylFromWinAtRefPt[(int)winCover];
6246 770 : previously_shaded(loop) = true;
6247 : } else {
6248 770 : shadeGroupLums.unswitchedWinLum +=
6249 770 : !previously_shaded(loop) ? daylFromWinAtRefPt[(int)winCover] : daylFromWinAtRefPt[iWinCover_Shaded];
6250 : }
6251 770 : } // for (IWin)
6252 : } // for (igroup)
6253 :
6254 : // Transmittance multiplier
6255 1155 : for (auto &shadeGroupLums : shadeGroupsLums) {
6256 770 : shadeGroupLums.lumRatio = (SetPnt(1) - shadeGroupLums.unswitchedWinLum) / (shadeGroupLums.switchedWinLum + 0.00001);
6257 : }
6258 :
6259 : // ASETIL < 1 means there's enough light, so check for switching
6260 :
6261 : // Fourth loop over windows to determine which to switch
6262 : // iterate in the order that the shades are specified in WindowShadeControl
6263 385 : count = 0;
6264 385 : breakOuterLoop = false;
6265 385 : continueOuterLoop = false;
6266 1155 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6267 770 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6268 770 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6269 :
6270 2310 : for (const int IWin : listOfExtWin) {
6271 1540 : ++count;
6272 1540 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
6273 : // need to map back to the original order of the "loop" to not change all the other data structures
6274 1540 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6275 1540 : if (loop > 0 && shadeGroupLums.lumRatio < 1.0) {
6276 :
6277 1022 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6278 1022 : if (!s_surf->Surface(IWin).HasShadeControl) {
6279 0 : continueOuterLoop = true;
6280 0 : continue;
6281 : }
6282 1764 : if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::GlassConditionallyLightened ||
6283 742 : s_surf->WindowShadingControl(ICtrl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
6284 280 : continueOuterLoop = true;
6285 280 : continue;
6286 : }
6287 :
6288 742 : int const IConst = s_surf->SurfActiveConstruction(IWin);
6289 : // Vis trans at normal incidence of unswitched glass
6290 742 : shadeGroupLums.unswitchedTvis =
6291 742 : Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
6292 :
6293 : // Vis trans at normal incidence of fully switched glass
6294 742 : int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
6295 742 : shadeGroupLums.switchedTvis =
6296 742 : Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
6297 :
6298 : // Reset shading flag to indicate that window is shaded by being partially or fully switched
6299 742 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
6300 :
6301 : // ASETIL < 0 means illuminance from non-daylight-switchable windows exceeds setpoint,
6302 : // so completely switch all daylight-switchable windows to minimize solar gain
6303 742 : if (shadeGroupLums.lumRatio <= 0.0) {
6304 0 : s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
6305 0 : s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
6306 : } else {
6307 : // Case where 0 < ASETIL < 1: darken glass in all
6308 : // daylight-switchable windows to just meet illuminance setpoint
6309 : // From this equation: SETPNT(1) = DILLUN + DILLSW/TVIS1 * VisTransSelected
6310 742 : s_surf->SurfWinVisTransSelected(IWin) =
6311 742 : max(shadeGroupLums.switchedTvis, shadeGroupLums.lumRatio * shadeGroupLums.unswitchedTvis) + 0.000001;
6312 1484 : s_surf->SurfWinSwitchingFactor(IWin) = (shadeGroupLums.unswitchedTvis - s_surf->SurfWinVisTransSelected(IWin)) /
6313 742 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis + 0.000001);
6314 : // bound switching factor between 0 and 1
6315 742 : s_surf->SurfWinSwitchingFactor(IWin) = min(1.0, s_surf->SurfWinSwitchingFactor(IWin));
6316 742 : s_surf->SurfWinSwitchingFactor(IWin) = max(0.0, s_surf->SurfWinSwitchingFactor(IWin));
6317 : }
6318 :
6319 : // Adjust daylight quantities based on ratio between switched and unswitched visible transmittance
6320 1484 : for (int IL = 1; IL <= NREFPT; ++IL) {
6321 : // DaylIllum(IL) and BacLum(IL) were calculated at the clear state:
6322 : // and need to adjusted for intermediate switched state at VisTransSelected:
6323 742 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6324 742 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6325 :
6326 742 : VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.unswitchedTvis + 0.000001);
6327 742 : dl->DaylIllum(IL) += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
6328 742 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
6329 :
6330 : // Adjust illum, background illum and source luminance for this window in intermediate switched state
6331 : // for later use in the DayltgGlare calc because SurfaceWindow(IWin)%ShadingFlag = WinShadingType::SwitchableGlazing = 2
6332 742 : VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.switchedTvis + 0.000001);
6333 742 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Illum][iWinCover_Shaded];
6334 742 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Back][iWinCover_Shaded];
6335 742 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Source][iWinCover_Shaded];
6336 : } // for (IL)
6337 : } // if (loop > 0 && ASETIL < 1)
6338 : // If new daylight does not exceed the illuminance setpoint, done, no more checking other groups of switchable glazings
6339 1260 : if (dl->DaylIllum(1) <= SetPnt(1)) {
6340 0 : breakOuterLoop = true;
6341 0 : break;
6342 : }
6343 770 : } // for (Win)
6344 770 : if (breakOuterLoop) {
6345 0 : break;
6346 : }
6347 770 : if (continueOuterLoop) {
6348 140 : continue;
6349 : }
6350 : } // for (igroup)
6351 :
6352 385 : } // ISWFLG /= 0 .AND. DaylIllum(1) > SETPNT(1)
6353 :
6354 : // loop over windows to do luminance based control
6355 823877 : count = 0;
6356 927671 : for (int igroup = 1; igroup <= (int)thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6357 227296 : for (int const IWin : thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1]) {
6358 123502 : ++count;
6359 123502 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6360 123502 : WindowShadingControlType shCtrlType = s_surf->WindowShadingControl(ICtrl).shadingControlType;
6361 123502 : if (!((shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffMidNight) ||
6362 : (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffSunset) ||
6363 : (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffNextMorning))) {
6364 122172 : continue;
6365 : }
6366 : // need to map back to the original order of the "loop" to not change all the other data structures
6367 1330 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6368 1330 : if (loop == 0) {
6369 0 : continue;
6370 : }
6371 :
6372 1330 : WinShadingType currentFlag = s_surf->SurfWinShadingFlag(IWin);
6373 1330 : WinShadingType ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
6374 1330 : if ((currentFlag != WinShadingType::IntShadeConditionallyOff) && (currentFlag != WinShadingType::GlassConditionallyLightened) &&
6375 714 : (currentFlag != WinShadingType::ExtShadeConditionallyOff) && (currentFlag != WinShadingType::IntBlindConditionallyOff) &&
6376 714 : (currentFlag != WinShadingType::ExtBlindConditionallyOff) && (currentFlag != WinShadingType::BGShadeConditionallyOff) &&
6377 : (currentFlag != WinShadingType::BGBlindConditionallyOff)) {
6378 714 : continue;
6379 : }
6380 :
6381 616 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums;
6382 616 : if (daylFromWinAtRefPt[iLum_Source][iWinCover_Bare] > s_surf->WindowShadingControl(ICtrl).SetPoint2) {
6383 : // shade on if luminance of this window is above setpoint
6384 0 : s_surf->SurfWinShadingFlag(IWin) = ShType;
6385 : // update total illuminance and background luminance
6386 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6387 0 : dl->DaylIllum(IL) += daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
6388 0 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] +=
6389 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
6390 : }
6391 : } else {
6392 : // shade off if luminance is below setpoint
6393 616 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6394 : }
6395 103794 : } // for (IWin)
6396 : } // for (igroup)
6397 :
6398 : // Calculate glare index at each reference point assuming the daylight illuminance setpoint is
6399 : // met at both reference points, either by daylight or electric lights
6400 1881490 : for (int IL = 1; IL <= NREFPT; ++IL) {
6401 1057613 : auto &refPt = thisDayltgCtrl.refPts(IL);
6402 1057613 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, refPt.lums[iLum_Back]);
6403 : // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,1,loop) for unshaded windows, and
6404 : // ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded windows
6405 1057613 : refPt.glareIndex = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6406 : }
6407 :
6408 : // Check if glare level is less than maximum allowed at each ref pt. If maximum
6409 : // is exceeded at either ref pt, attempt to reduce glare to acceptable level by closing
6410 : // shading device on windows that have shades that have not already been closed.
6411 823877 : GlareFlag = false;
6412 1720154 : for (auto const &refPt : thisDayltgCtrl.refPts) {
6413 1026190 : if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) {
6414 129913 : GlareFlag = true;
6415 129913 : break;
6416 : }
6417 : }
6418 :
6419 823877 : if (GlareFlag) {
6420 : bool blnCycle;
6421 : bool GlareOK;
6422 : Real64 tmpMult;
6423 : // Glare is too high at a ref pt. Loop through windows.
6424 129913 : count = 0;
6425 :
6426 129913 : continueOuterLoop = false;
6427 174061 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6428 57972 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6429 57972 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6430 :
6431 57972 : int countBeforeListOfExtWinLoop = count;
6432 57972 : bool atLeastOneGlareControlIsActive = false;
6433 :
6434 132952 : for (const int IWin : listOfExtWin) {
6435 74980 : ++count;
6436 : // need to map back to the original order of the "loop" to not change all the other data structures
6437 74980 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6438 74980 : if (loop == 0) {
6439 0 : continue;
6440 : }
6441 :
6442 74980 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
6443 : // Check if window is eligible for glare control
6444 : // TH 1/21/2010. Switchable glazings already in partially switched state
6445 : // should be allowed to further dim to control glare
6446 : // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
6447 149960 : if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
6448 74980 : ANY_BLIND(s_surf->SurfWinShadingFlag(IWin))) {
6449 0 : continueOuterLoop = false;
6450 0 : continue;
6451 : }
6452 74980 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6453 74980 : if (!s_surf->Surface(IWin).HasShadeControl) {
6454 0 : continueOuterLoop = false;
6455 0 : continue;
6456 : }
6457 74980 : if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
6458 74980 : atLeastOneGlareControlIsActive = true;
6459 :
6460 : // Illuminance (WDAYIL) and background luminance (WBACLU) contribution from this
6461 : // window without shading (IS=1) and with shading (IS=2) for each ref pt
6462 : // For switchable windows, this may be partially switched rather than fully dark
6463 153896 : for (int IL = 1; IL <= NREFPT; ++IL) {
6464 78916 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6465 236748 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
6466 157832 : shadeGroupLums.WDAYIL(IL)[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
6467 157832 : shadeGroupLums.WDAYIL(IL)[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
6468 : }
6469 : }
6470 :
6471 : // Recalculate illuminance and glare with shading on this window.
6472 : // For switchable glazings, this is the fully switched (dark) state
6473 153896 : for (int IL = 1; IL <= NREFPT; ++IL) {
6474 78916 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6475 78916 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6476 78916 : auto const &refPt = thisDayltgCtrl.refPts(IL);
6477 :
6478 78916 : if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::SwitchableGlazing) {
6479 : // for non switchable glazings or switchable glazings not switched yet (still in clear state)
6480 : // SurfaceWindow(IWin)%ShadingFlag = WinShadingFlag::GlassConditionallyLightened
6481 78916 : rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Bare] + wdayil[iLum_Illum][iWinCover_Shaded];
6482 78916 : rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Bare] + wdayil[iLum_Back][iWinCover_Shaded];
6483 : } else {
6484 : // switchable glazings already in partially switched state when calc the RDAYIL(IL) & RBACLU(IL)
6485 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(loop, IL);
6486 0 : rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Shaded] + tmpDayl[iLum_Illum][iWinCover_Shaded];
6487 0 : rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Shaded] + tmpDayl[iLum_Back][iWinCover_Shaded];
6488 : }
6489 : } // for (IL)
6490 :
6491 74980 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) {
6492 71044 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
6493 3936 : } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) {
6494 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntShade;
6495 3936 : } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) {
6496 3936 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtShade;
6497 0 : } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) {
6498 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntBlind;
6499 0 : } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff) {
6500 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtBlind;
6501 0 : } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGShadeConditionallyOff) {
6502 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGShade;
6503 0 : } else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGBlindConditionallyOff) {
6504 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGBlind;
6505 : }
6506 :
6507 : // For switchable glazings, it is switched to fully dark state,
6508 : // update ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for use in DayltgGlare
6509 74980 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6510 142088 : for (int IL = 1; IL <= NREFPT; ++IL) {
6511 71044 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6512 71044 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6513 :
6514 71044 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6515 71044 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6516 71044 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6517 : }
6518 :
6519 71044 : int const IConst = s_surf->SurfActiveConstruction(IWin);
6520 : // Vis trans at normal incidence of unswitched glass
6521 71044 : shadeGroupLums.unswitchedTvis =
6522 71044 : Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
6523 :
6524 : // Vis trans at normal incidence of fully switched glass
6525 71044 : int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
6526 71044 : shadeGroupLums.switchedTvis =
6527 71044 : Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
6528 : } // if (switchableGlazing)
6529 : } // if (GlareControlIsActive)
6530 57972 : } // for (IWin)
6531 57972 : if (continueOuterLoop) {
6532 0 : continue;
6533 : }
6534 :
6535 57972 : if (atLeastOneGlareControlIsActive) {
6536 :
6537 : // Re-calc daylight and glare at shaded state. For switchable glazings, it is the fully dark state.
6538 119880 : for (int IL = 1; IL <= NREFPT; ++IL) {
6539 61908 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, shadeGroupLums.RDAYIL(IL)[iLum_Back]);
6540 : // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded state
6541 61908 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6542 : }
6543 :
6544 : // Check if the shading did not improve the glare conditions
6545 : //
6546 : // blnCycle when true resets the specific window to its non-shaded condition. A later comment says
6547 : // Shading this window has not improved the glare situation.
6548 : // Reset shading flag to no shading condition, go to next window.
6549 : //
6550 : // If the original glare was too high at all reference points and the new glare is lower at all reference points it is good, don't
6551 : // reset it. For each reference point, if the original glare was too high but ok at other reference points and the glare gets
6552 : // lower at the reference and stays ok at the other reference points it is good, don't reset it.
6553 : //
6554 : // The old comments when there were only two reference points were:
6555 : // One ref pt; go to next window if glare has increased.
6556 : // Two ref pts. There are three cases depending on glare values.
6557 : // (1) Initial glare too high at both ref pts. Deploy shading on
6558 : // this window if this decreases glare at both ref pts.
6559 : // (2) Initial glare too high only at first ref pt. Deploy shading
6560 : // on this window if glare at first ref pt decreases and
6561 : // glare at second ref pt stays below max.
6562 : // (3) Initial glare too high at second ref pt. Deploy shading if glare
6563 : // at second ref pt decreases and glare at first ref pt stays below max.
6564 : //
6565 : // The approach taken is just to count the number of reference points that fulfill the individual requirements and see if it
6566 : // covers all the reference points.
6567 57972 : int numRefPtOldAboveMaxNewBelowOld = 0;
6568 57972 : int numRefPtOldBelowMaxNewBelowMax = 0;
6569 119880 : for (int IL = 1; IL <= NREFPT; ++IL) {
6570 61908 : auto const &refPt = thisDayltgCtrl.refPts(IL);
6571 :
6572 61908 : if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= refPt.glareIndex) {
6573 57872 : ++numRefPtOldAboveMaxNewBelowOld;
6574 4036 : } else if (refPt.glareIndex <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= thisDayltgCtrl.MaxGlareallowed) {
6575 3936 : ++numRefPtOldBelowMaxNewBelowMax;
6576 : }
6577 : }
6578 57972 : blnCycle = true;
6579 57972 : if ((numRefPtOldAboveMaxNewBelowOld + numRefPtOldBelowMaxNewBelowMax) == NREFPT) {
6580 57872 : blnCycle = false;
6581 : }
6582 : }
6583 :
6584 : // restore the count to the value prior to the last loop through the group of exterior windows
6585 57972 : count = countBeforeListOfExtWinLoop;
6586 57972 : breakOuterLoop = false;
6587 :
6588 117708 : for (const int IWin : listOfExtWin) {
6589 73560 : ++count;
6590 : // need to map back to the original order of the "loop" to not change all the other data structures
6591 73560 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6592 73560 : if (loop == 0) {
6593 0 : continue;
6594 : }
6595 :
6596 : // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
6597 143184 : if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
6598 69624 : ANY_BLIND(s_surf->SurfWinShadingFlag(IWin))) {
6599 3936 : continue;
6600 : }
6601 :
6602 69624 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6603 69624 : if (!s_surf->Surface(IWin).HasShadeControl) {
6604 0 : continue;
6605 : }
6606 69624 : if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
6607 :
6608 : // Shading this window has not improved the glare situation.
6609 : // Reset shading flag to no shading condition, go to next window.
6610 69624 : if (blnCycle) {
6611 : // for switchable glazings, reset properties to clear state or partial switched state?
6612 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6613 0 : s_surf->SurfWinSwitchingFactor(IWin) = 0.0;
6614 0 : s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.unswitchedTvis;
6615 :
6616 : // RESET properties for fully dark state
6617 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6618 0 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6619 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6620 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6621 0 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6622 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6623 : }
6624 : }
6625 :
6626 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6627 0 : continue;
6628 0 : }
6629 :
6630 : // Shading this window has improved the glare situation.
6631 : // Reset background luminance, glare index, and daylight illuminance at each ref pt.
6632 : // For switchable glazings, this is fully switched, dark state
6633 139248 : for (int IL = 1; IL <= NREFPT; ++IL) {
6634 69624 : auto &refPt = thisDayltgCtrl.refPts(IL);
6635 69624 : refPt.lums[iLum_Back] = shadeGroupLums.RDAYIL(IL)[iLum_Back];
6636 69624 : refPt.glareIndex = GLRNEW(IL);
6637 69624 : dl->DaylIllum(IL) = shadeGroupLums.RDAYIL(IL)[iLum_Illum];
6638 : }
6639 :
6640 : // TH comments (5/22/2009): seems for EC windows, if the calculated glare exceeds the max setpoint,
6641 : // the EC windows will be reset to fully dark state which significantly reduces the available daylight.
6642 : // A better way is to dim the EC windows as necessary just to meet the glare index, which will still
6643 : // provide more daylight while not exceeding the max glare! The question is then how to set the
6644 : // SwitchingFactor to just meet the glare index.
6645 : // This was addressed in CR 7984 for E+ 5.0. 1/19/2010
6646 :
6647 : // If switchable glazing, set switching factor to 1: fully switched.
6648 69624 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6649 : // tmpSWFactor0 = SurfaceWindow( IWin ).SwitchingFactor; // save original
6650 : // switching factor
6651 : ////Unused Set but never used
6652 69624 : s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
6653 69624 : s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
6654 :
6655 : // restore fully dark values
6656 139248 : for (int IL = 1; IL <= NREFPT; ++IL) {
6657 69624 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6658 69624 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6659 69624 : auto &wdayil = shadeGroupLums.WDAYIL(IL);
6660 69624 : wdayil[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6661 69624 : wdayil[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6662 69624 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6663 69624 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6664 69624 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6665 : }
6666 : }
6667 :
6668 : // Check if glare now acceptable at each ref pt.
6669 69624 : GlareOK = false;
6670 69624 : if (NREFPT == 1) {
6671 69624 : if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed) {
6672 13824 : GlareOK = true;
6673 : }
6674 0 : } else if (NREFPT > 1) {
6675 0 : if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed &&
6676 0 : thisDayltgCtrl.refPts(2).glareIndex <= thisDayltgCtrl.MaxGlareallowed) {
6677 0 : GlareOK = true;
6678 : }
6679 : }
6680 :
6681 69624 : if (GlareOK) {
6682 27648 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing &&
6683 13824 : s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp) {
6684 : // Added TH 1/14/2010
6685 : // Only for switchable glazings with MeetDaylightIlluminanceSetpoint control
6686 : // The glazing is in fully dark state, it might lighten a bit to provide more daylight
6687 : // while meeting maximum discomfort glare index
6688 : // Iteration to find the right switching factor meeting the glare index
6689 :
6690 : // get fully dark state values
6691 0 : Real64 tmpSWSL1 = tmpDaylFromWinAtRefPt(1, loop)[iLum_Source][iWinCover_Shaded];
6692 0 : Real64 tmpSWSL2 = (NREFPT > 1) ? tmpDaylFromWinAtRefPt(2, loop)[iLum_Source][iWinCover_Shaded] : 0.0;
6693 :
6694 : // use simple fixed step search in iteraction, can be improved in future
6695 0 : Real64 tmpSWFactor = 1.0 - tmpSWIterStep;
6696 0 : while (tmpSWFactor > 0) {
6697 : // calc new glare at new switching state
6698 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6699 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6700 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6701 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6702 0 : rdayil[iLum_Illum] =
6703 0 : dl->DaylIllum(IL) +
6704 0 : (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6705 0 : rdayil[iLum_Back] =
6706 0 : refPt.lums[iLum_Back] +
6707 0 : (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6708 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
6709 : // needs to update SourceLumFromWinAtRefPt(IL,2,loop) before re-calc DayltgGlare
6710 0 : tmpMult = (shadeGroupLums.unswitchedTvis -
6711 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6712 0 : shadeGroupLums.switchedTvis;
6713 0 : refPt.extWins(loop).lums[iLum_Source][iWinCover_Shaded] = ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
6714 : // Calc new glare
6715 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6716 : } // for (IL)
6717 :
6718 : // Check whether new glare is OK
6719 0 : GlareOK = false;
6720 0 : if (NREFPT == 1) {
6721 0 : if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed) {
6722 0 : GlareOK = true;
6723 : }
6724 0 : } else if (NREFPT > 1) {
6725 0 : if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(2) <= thisDayltgCtrl.MaxGlareallowed) {
6726 0 : GlareOK = true;
6727 : }
6728 : }
6729 :
6730 0 : if (GlareOK) {
6731 0 : if (tmpSWFactor >= tmpSWIterStep) {
6732 : // Continue to lighten the glazing
6733 0 : tmpSWFactor -= tmpSWIterStep;
6734 0 : continue;
6735 : } else {
6736 : // Glare still OK but glazing already in clear state, no more lighten
6737 0 : breakOuterLoop = true;
6738 0 : break;
6739 : }
6740 : } else {
6741 : // Glare too high, exit and use previous switching state
6742 0 : tmpSWFactor += tmpSWIterStep;
6743 0 : breakOuterLoop = true;
6744 0 : break;
6745 : }
6746 : } // if (tmpSWFactor > 0)
6747 :
6748 : // Final re-calculation if needed
6749 0 : if (!GlareOK) {
6750 : // Glare too high, use previous state and re-calc
6751 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6752 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6753 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6754 0 : rdayil[iLum_Illum] =
6755 0 : dl->DaylIllum(IL) +
6756 0 : (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6757 0 : rdayil[iLum_Back] =
6758 0 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] +
6759 0 : (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6760 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
6761 :
6762 : // needs to update SourceLumFromWinAtRefPt(IL,2,IWin) before re-calc DayltgGlare
6763 0 : tmpMult = (shadeGroupLums.unswitchedTvis -
6764 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6765 0 : shadeGroupLums.switchedTvis;
6766 0 : thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Source][iWinCover_Shaded] =
6767 0 : ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
6768 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6769 : }
6770 : }
6771 :
6772 : // Update final results
6773 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6774 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6775 0 : auto const &rdayil = shadeGroupLums.RDAYIL(IL);
6776 0 : refPt.lums[iLum_Back] = rdayil[iLum_Back];
6777 0 : refPt.glareIndex = GLRNEW(IL);
6778 0 : dl->DaylIllum(IL) = rdayil[iLum_Illum];
6779 :
6780 0 : tmpMult =
6781 0 : (shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6782 0 : shadeGroupLums.switchedTvis;
6783 : // update report variables
6784 0 : auto &daylFromWinAtRefPt = refPt.extWins(loop).lums;
6785 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6786 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded] * tmpMult;
6787 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded] * tmpMult;
6788 : }
6789 0 : s_surf->SurfWinSwitchingFactor(IWin) = tmpSWFactor;
6790 0 : s_surf->SurfWinVisTransSelected(IWin) =
6791 0 : shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor;
6792 :
6793 : } else {
6794 : // For un-switchable glazing or switchable glazing but not MeetDaylightIlluminaceSetpoint control,
6795 : // it is in shaded state and glare is ok - job is done, exit the window loop - IWin
6796 13824 : breakOuterLoop = true;
6797 13824 : break;
6798 : }
6799 : } // if (glareOK)
6800 : } // if (glareControlIsActive)
6801 57972 : } // for (IWin)
6802 57972 : if (breakOuterLoop) {
6803 13824 : break;
6804 : }
6805 : } // for (igroup)
6806 : } // if (GlareFlag)
6807 :
6808 : // Loop again over windows and reset remaining shading flags that
6809 : // are 10 or higher (i.e., conditionally off) to off
6810 1651828 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6811 827951 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
6812 2391411 : for (int IWin = thisSpace.WindowSurfaceFirst; IWin <= thisSpace.WindowSurfaceLast; ++IWin) {
6813 1563460 : if (s_surf->Surface(IWin).ExtBoundCond != ExternalEnvironment) {
6814 1015 : continue;
6815 : }
6816 1562445 : bool anyGlareControl = (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) ||
6817 1562445 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) ||
6818 1548553 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) ||
6819 4645509 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) ||
6820 1520619 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff);
6821 1562445 : if (anyGlareControl) {
6822 41826 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6823 : }
6824 : }
6825 823877 : }
6826 :
6827 : // Variables for reporting
6828 1881490 : for (int IL = 1; IL <= NREFPT; ++IL) {
6829 1057613 : auto &refPt = thisDayltgCtrl.refPts(IL);
6830 1057613 : refPt.lums[iLum_Illum] = dl->DaylIllum(IL);
6831 :
6832 : // added TH 12/2/2008
6833 1057613 : refPt.timeExceedingGlareIndexSetPoint = (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) ? state.dataGlobal->TimeStepZone : 0.0;
6834 : // added TH 7/6/2009
6835 1057613 : refPt.timeExceedingDaylightIlluminanceSetPoint = (dl->DaylIllum(IL) > refPt.illumSetPoint) ? state.dataGlobal->TimeStepZone : 0.0;
6836 : }
6837 832025 : } // DayltgInteriorIllum()
6838 :
6839 1015 : void DayltgInteriorTDDIllum(EnergyPlusData &state)
6840 : {
6841 :
6842 : // SUBROUTINE INFORMATION:
6843 : // AUTHOR Linda Lawrie
6844 : // DATE WRITTEN October 2006
6845 :
6846 : // PURPOSE OF THIS SUBROUTINE:
6847 : // Calculate the TDD Pipe illuminance values
6848 1015 : auto &dl = state.dataDayltg;
6849 :
6850 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
6851 : int iSky1; // Sky type index values for averaging two sky types
6852 : int iSky2;
6853 : Real64 SkyWeight; // Weighting factor used to average two different sky types
6854 :
6855 1015 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
6856 518 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
6857 518 : iSky1 = (int)SkyType::Clear;
6858 518 : iSky2 = (int)SkyType::ClearTurbid;
6859 497 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
6860 91 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
6861 91 : iSky1 = (int)SkyType::ClearTurbid;
6862 91 : iSky2 = (int)SkyType::Intermediate;
6863 : } else { // Sky is average of intermediate and overcast
6864 406 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
6865 406 : iSky1 = (int)SkyType::Intermediate;
6866 406 : iSky2 = (int)SkyType::Overcast;
6867 : }
6868 :
6869 : // Calculate and report TDD visible transmittances
6870 3045 : for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
6871 :
6872 2030 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam =
6873 2030 : state.dataGlobal->WeightNow * dl->TDDTransVisBeam(state.dataGlobal->HourOfDay, PipeNum) +
6874 2030 : state.dataGlobal->WeightPreviousHour * dl->TDDTransVisBeam(state.dataGlobal->PreviousHour, PipeNum);
6875 :
6876 2030 : auto const &tddFluxIncCurr = dl->TDDFluxInc(state.dataGlobal->HourOfDay, PipeNum);
6877 2030 : auto const &tddFluxIncPrev = dl->TDDFluxInc(state.dataGlobal->PreviousHour, PipeNum);
6878 :
6879 2030 : auto const &tddFluxTransCurr = dl->TDDFluxTrans(state.dataGlobal->HourOfDay, PipeNum);
6880 2030 : auto const &tddFluxTransPrev = dl->TDDFluxTrans(state.dataGlobal->PreviousHour, PipeNum);
6881 :
6882 2030 : Illums TDDTransVisDiff;
6883 6090 : for (int iSky = iSky1; iSky <= iSky2; ++iSky) {
6884 4060 : Real64 tddTransVisDiffCurr = (tddFluxIncCurr.sky[iSky] > 0.0) ? (tddFluxTransCurr.sky[iSky] / tddFluxIncCurr.sky[iSky]) : 0.0;
6885 4060 : Real64 tddTransVisDiffPrev = (tddFluxIncPrev.sky[iSky] > 0.0) ? (tddFluxTransPrev.sky[iSky] / tddFluxIncPrev.sky[iSky]) : 0.0;
6886 :
6887 4060 : TDDTransVisDiff.sky[iSky] =
6888 4060 : state.dataGlobal->WeightNow * tddTransVisDiffCurr + state.dataGlobal->WeightPreviousHour * tddTransVisDiffPrev;
6889 : } // for (iSky)
6890 :
6891 2030 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff =
6892 2030 : SkyWeight * TDDTransVisDiff.sky[iSky1] + (1.0 - SkyWeight) * TDDTransVisDiff.sky[iSky2];
6893 : } // for (PipeNum)
6894 1015 : } // DayltgInteriorTDDIllum()
6895 :
6896 925529 : void DayltgElecLightingControl(EnergyPlusData &state)
6897 : {
6898 :
6899 : // SUBROUTINE INFORMATION:
6900 : // AUTHOR Fred Winkelmann
6901 : // DATE WRITTEN July 1997
6902 : // MODIFIED Mar 2004, FCW: add inter-reflected illuminance from interior windows to DaylIllum
6903 : // Apr 2004, FCW: move CALL ReportIllumMap from DayltgInteriorIllum2 (DayltgInteriorMapIllum)
6904 : // Apr 2010, BG NREL: remove inter-reflected illuminance to stop double counting
6905 : // Aug 2012, BG NREL: added availability schedule logic
6906 :
6907 : // PURPOSE OF THIS SUBROUTINE:
6908 : // For a daylit space, determines lighting power reduction factor due to
6909 : // daylighting for different lighting control systems.
6910 :
6911 : // Called by InitSurfaceHeatBalance.
6912 :
6913 : // REFERENCES:
6914 : // Based on DOE-2.1E subroutine DLTSYS.
6915 925529 : auto &dl = state.dataDayltg;
6916 :
6917 925529 : if (dl->daylightControl.empty()) {
6918 825413 : return;
6919 : }
6920 : // Reset space power reduction factors
6921 1164862 : for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
6922 1064746 : dl->spacePowerReductionFactor(spaceNum) = 1.0;
6923 : }
6924 :
6925 605609 : for (auto &thisDayltgCtrl : dl->daylightControl) {
6926 :
6927 505493 : if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
6928 : // Set space power reduction factors
6929 2037 : if (thisDayltgCtrl.PowerReductionFactor < 1.0) {
6930 2016 : if (thisDayltgCtrl.spaceIndex > 0) {
6931 : // This is a space-level daylighting control
6932 0 : dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = thisDayltgCtrl.PowerReductionFactor;
6933 : } else {
6934 : // This is a zone-level daylighting control
6935 4032 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6936 2016 : dl->spacePowerReductionFactor(spaceNum) = thisDayltgCtrl.PowerReductionFactor;
6937 2016 : }
6938 : }
6939 : }
6940 2037 : continue;
6941 2037 : }
6942 :
6943 : // Electric lighting power reduction factor for a given daylighting control
6944 503456 : Real64 &TotReduction = thisDayltgCtrl.PowerReductionFactor;
6945 503456 : TotReduction = 0.0;
6946 503456 : Real64 ZFTOT = 0.0;
6947 :
6948 : // check if scheduled to be available
6949 503456 : if (thisDayltgCtrl.availSched->getCurrentVal() > 0.0) {
6950 :
6951 : // Loop over reference points
6952 1143403 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
6953 644077 : auto &refPt = thisDayltgCtrl.refPts(IL);
6954 : // Total fraction of zone that is daylit
6955 644077 : ZFTOT += refPt.fracZoneDaylit;
6956 :
6957 644077 : dl->DaylIllum(IL) = refPt.lums[iLum_Illum];
6958 644077 : Real64 FL = 0.0;
6959 644077 : if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
6960 177962 : FL = (refPt.illumSetPoint - dl->DaylIllum(IL)) / refPt.illumSetPoint;
6961 : }
6962 :
6963 : // BRANCH ON LIGHTING SYSTEM TYPE
6964 644077 : LtgCtrlType LSYSTP = thisDayltgCtrl.LightControlType;
6965 644077 : Real64 FP = 0.0;
6966 644077 : if (LSYSTP != LtgCtrlType::Stepped) {
6967 : // Continuously dimmable system with linear power curve
6968 : // Fractional output power required to meet setpoint
6969 401529 : FP = 1.0;
6970 : // LIGHT-CTRL-TYPE = CONTINUOUS (LSYSTP = 1)
6971 401529 : if (FL <= thisDayltgCtrl.MinLightFraction) {
6972 281194 : FP = thisDayltgCtrl.MinPowerFraction;
6973 : }
6974 : // LIGHT-CTRL-TYPE = CONTINUOUS/OFF (LSYSTP = 3)
6975 401529 : if (FL <= thisDayltgCtrl.MinLightFraction && LSYSTP == LtgCtrlType::ContinuousOff) {
6976 263749 : FP = 0.0;
6977 : }
6978 401529 : if (FL > thisDayltgCtrl.MinLightFraction && FL < 1.0) {
6979 119580 : FP = (FL + (1.0 - FL) * thisDayltgCtrl.MinPowerFraction - thisDayltgCtrl.MinLightFraction) /
6980 119580 : (1.0 - thisDayltgCtrl.MinLightFraction);
6981 : }
6982 :
6983 : } else { // LSYSTP = 2
6984 : // Stepped system
6985 242548 : FP = 0.0;
6986 : // #9060: Use a tolerance, otherwise at very low (< 1e-12) daylighting conditions, you can get a multiplier > 1.0
6987 242548 : if (dl->DaylIllum(IL) < 0.1) {
6988 3512 : FP = 1.0;
6989 239036 : } else if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
6990 32676 : FP = double(int(thisDayltgCtrl.LightControlSteps * FL) + 1) / double(thisDayltgCtrl.LightControlSteps);
6991 : }
6992 :
6993 242548 : if (thisDayltgCtrl.LightControlProbability < 1.0) {
6994 : // Manual operation. Occupant sets lights one level too high a fraction of the time equal to
6995 : // 1. - ZoneDaylight(ZoneNum)%LightControlProbability. RANDOM_NUMBER returns a random number
6996 : // between 0 and 1.
6997 : Real64 XRAN;
6998 0 : RANDOM_NUMBER(XRAN);
6999 0 : if (XRAN >= thisDayltgCtrl.LightControlProbability) {
7000 : // Set level one higher
7001 0 : if (FP < 1.0) {
7002 0 : FP += (1.0 / double(thisDayltgCtrl.LightControlSteps));
7003 : }
7004 : } // XRAN
7005 : } // Light Control Probability < 1
7006 : } // Lighting System Type
7007 :
7008 644077 : refPt.powerReductionFactor = FP;
7009 :
7010 : // Accumulate net ltg power reduction factor for entire zone
7011 644077 : TotReduction += refPt.powerReductionFactor * refPt.fracZoneDaylit;
7012 :
7013 : } // End of loop over reference points, IL
7014 :
7015 : // Correct for fraction of zone (1-ZFTOT) not controlled by
7016 : // the reference points. For this fraction (which is usually zero),
7017 : // the electric lighting is unaffected and the power reduction
7018 : // factor is therefore 1.0.
7019 499326 : TotReduction += (1.0 - ZFTOT);
7020 : } else { // controls not currently available
7021 4130 : TotReduction = 1.0;
7022 : }
7023 :
7024 : // Set space power reduction factors
7025 503456 : if (thisDayltgCtrl.spaceIndex > 0) {
7026 : // This is a space-level daylighting control
7027 1239 : dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = TotReduction;
7028 : } else {
7029 : // This is a zone-level daylighting control
7030 1004434 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
7031 502217 : dl->spacePowerReductionFactor(spaceNum) = TotReduction;
7032 502217 : }
7033 : }
7034 : } // end daylighting control loop
7035 :
7036 : // IF(TotIllumMaps > 0 .and. .not. DoingSizing .and. .not. WarmupFlag .and. .not. KickoffSimulation) THEN
7037 100116 : if ((int)dl->illumMaps.size() > 0 && !state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
7038 3019 : for (int mapNum = 1; mapNum <= (int)dl->illumMaps.size(); ++mapNum) {
7039 1627 : auto &illumMap = dl->illumMaps(mapNum);
7040 1627 : if (state.dataGlobal->TimeStep == 1) {
7041 283 : dl->mapResultsToReport = false;
7042 : }
7043 143752 : for (auto &refPt : illumMap.refPts) {
7044 142125 : refPt.lumsHr[iLum_Illum] += refPt.lums[iLum_Illum] / double(state.dataGlobal->TimeStepsInHour);
7045 142125 : if (refPt.lumsHr[iLum_Illum] > 0.0) {
7046 142125 : dl->mapResultsToReport = true;
7047 142125 : dl->mapResultsReported = true;
7048 : }
7049 : }
7050 1627 : ReportIllumMap(state, mapNum);
7051 1627 : if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
7052 24357 : for (auto &refPt : illumMap.refPts) {
7053 24075 : refPt.lumsHr[iLum_Illum] = refPt.lums[iLum_Illum] = 0.0;
7054 : }
7055 : }
7056 : } // for (mapNum)
7057 : } // if (MapSize > 0)
7058 : } // DayltgElecLightingControl()
7059 :
7060 552240 : Real64 DayltgGlarePositionFactor(Real64 X, // Lateral and vertical distance of luminous window element from
7061 : Real64 Y)
7062 : {
7063 :
7064 : // SUBROUTINE INFORMATION:
7065 : // AUTHOR Fred Winkelmann
7066 : // DATE WRITTEN July 1997
7067 :
7068 : // PURPOSE OF THIS SUBROUTINE:
7069 : // by table interpolation, evaluates the
7070 : // Hopkinson position factor used in glare calculation
7071 : // (Hopkinson, Petherbridge, AND Longmore -- Daylighting,
7072 : // London, 1966, PP 307, 323). X (Y) is the lateral
7073 : // (vertical) distance of luminous window element from
7074 : // horizontal line of vision, divided by horizontal distance
7075 : // from eye of observer. The array PF contains values of
7076 : // the position factor for X = 0, 0.5, 1.0, 1.5, 2.0, 2.5,
7077 : // and 3.0 and Y = 0, 0.5, 1.0, 1.5, 2.0. Called by CalcDayltgCoefficients.
7078 :
7079 : // REFERENCES:
7080 : // Based on DOE-2.1E subroutine DPFAC.
7081 :
7082 : // Position factor array
7083 : static constexpr std::array<std::array<Real64, 7>, 5> PF = {{
7084 : {1.00, 0.492, 0.226, 0.128, 0.081, 0.061, 0.057},
7085 : {0.123, 0.119, 0.065, 0.043, 0.029, 0.026, 0.023},
7086 : {0.019, 0.026, 0.019, 0.016, 0.014, 0.011, 0.011},
7087 : {0.008, 0.008, 0.008, 0.008, 0.008, 0.006, 0.006},
7088 : {0.0, 0.0, 0.003, 0.003, 0.003, 0.003, 0.003},
7089 : }};
7090 :
7091 552240 : if (X < 0.0 || X >= 3.0) {
7092 356528 : return 0.0;
7093 : }
7094 195712 : if (Y < 0.0 || Y >= 2.0) {
7095 553 : return 0.0;
7096 : }
7097 :
7098 195159 : int IX = 1 + int(2.0 * X);
7099 195159 : int IY = 1 + int(2.0 * Y);
7100 195159 : Real64 X1 = 0.5 * double(IX - 1);
7101 195159 : Real64 Y1 = 0.5 * double(IY - 1);
7102 195159 : Real64 FA = PF[IY - 1][IX - 1] + 2.0 * (X - X1) * (PF[IY - 1][IX] - PF[IY - 1][IX - 1]);
7103 195159 : Real64 FB = PF[IY][IX - 1] + 2.0 * (X - X1) * (PF[IY][IX] - PF[IY][IX - 1]);
7104 195159 : return FA + 2.0 * (Y - Y1) * (FB - FA);
7105 : } // DayltgGlarePositionFactor()
7106 :
7107 333774 : void DayltgInterReflectedIllum(EnergyPlusData &state,
7108 : int const ISunPos, // Sun position counter; used to avoid calculating various
7109 : int const IHR, // Hour of day
7110 : int const enclNum, // Daylighting enclosure index
7111 : int const IWin // Window index
7112 : )
7113 : {
7114 :
7115 : // SUBROUTINE INFORMATION:
7116 : // AUTHOR Fred Winkelmann
7117 : // DATE WRITTEN July 1997
7118 : // MODIFIED FCW December 1998
7119 : // FCW June 2001: Add blind calculations
7120 : // FCW Jan 2001: Add blinds with movable slats
7121 : // FCW Jan 2003: Add between-glass blinds
7122 : // FCW Jul 2003: account for transmittance of shading surfaces
7123 : // (previously these were assumed opaque even if transmittance schedule
7124 : // value was non-zero)
7125 : // FCW Aug 2003: modify initialization of WinLum from WinLum = 0. TO
7126 : // WinLum(:,:,IHR) = 0. Otherwise values calculated in previous
7127 : // call are incorrectly zeroed. Result was that window luminance with
7128 : // shade or blind included only contribution from first window element
7129 : // in window element loop in CalcDayltgCoefficients, thus seriously
7130 : // undercalculating window luminance for windows with more than one
7131 : // window element. Similarly, modified initialization of WLUMSU from
7132 : // WLUMSU = 0. to WLUMSU(:,IHR) = 0., and of WLUMSUdisk from
7133 : // WLUMSUdisk = 0. to WLUMSUdisk(:,IHR) = 0.
7134 : // PGE Aug 2003: Add daylighting shelves.
7135 : // FCW Nov 2003: Add beam solar and sky solar reflected from obstructions;
7136 : // add beam solar reflected from ground accounting for obstructions.
7137 : // FCW Nov 2003: increase NPHMAX from 9 to 10 to avoid rays with altitude angle = 0
7138 : // for vertical surfaces.
7139 : // FCW Nov 2003: fix the expression for min and max limits of azimuth; old expression
7140 : // broke down for window normals with negative altitude angle
7141 : // FCW Nov 2003: add specular reflection from exterior obstructions
7142 : // FCW Apr 2004: add light well efficiency multiplying window transmittance
7143 : // FCW Apr 2004: add diffusing glazing
7144 : // RAR (FSEC) May 2006: add exterior window screen
7145 : // B. Griffith NREL April 2010: CR7869 add adjacent zone area if window is not on this zone
7146 : // apply interior window transmission and blocking to beam transmission from ext win
7147 :
7148 : // PURPOSE OF THIS SUBROUTINE:
7149 : // Called from CalcDayltgCoefficients for each window and reference point in a daylit
7150 : // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
7151 : // to internally reflected light by integrating to determine the amount of flux from
7152 : // sky and ground (and beam reflected from obstructions) transmitted through
7153 : // the center of the window and then reflecting this
7154 : // light from the inside surfaces of the space. The "split-flux" method is used
7155 : // (Lynes, Principles of Natural Lighting, 1968). EINT is determined for
7156 : // different sky types and for window with and without shades, screens or blinds.
7157 : // Also finds luminance (WinLum and WLUMSU) of window with shade or blind, &
7158 : // or with diffusing glass, for different sky types.
7159 :
7160 : // REFERENCES:
7161 : // Based on DOE-2.1E subroutine DREFLT.
7162 :
7163 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7164 : // In the following I,J arrays:
7165 : // I = sky type;
7166 : // J = 1 for bare window, 2 and above for window with shade or blind.
7167 333774 : Illums ZSK; // Sky-related and sun-related illuminance on window from sky/ground
7168 333774 : Vector3<Real64> U; // Unit vector in (PH,TH) direction
7169 333774 : Vector3<Real64> nearestHitPt; // Hit point of ray on nearest obstruction (m)
7170 333774 : Vector3<Real64> obsHitPt; // Coordinates of hit point on an obstruction (m)
7171 333774 : Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
7172 333774 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLCW = {Illums()}; // Sky-related upgoing luminous flux
7173 333774 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLFW = {Illums()}; // Sky-related downgoing luminous flux
7174 :
7175 : // 3=intermediate, 4=overcast
7176 : Real64 DPH; // Sky/ground element altitude and azimuth increments (radians)
7177 : Real64 DTH;
7178 : Real64 PH; // Sky/ground element altitude and azimuth (radians)
7179 : Real64 TH;
7180 : Real64 SPH; // Sine and cosine of PH
7181 : Real64 CPH;
7182 : Real64 PHMIN; // Limits of altitude integration (radians)
7183 : Real64 PHMAX;
7184 : Real64 ThMin; // Limits of azimuth integration (radians)
7185 : Real64 ThMax;
7186 : Real64 PhWin; // Altitude, azimuth angle of window normal (radians)
7187 : Real64 ThWin;
7188 : Real64 ACosTanTan; // ACOS(-TAN(Ph)*TAN(PhWin))
7189 : Real64 DA; // CPH*DTH*DPH
7190 : Real64 COSB; // Cosine of angle of incidence of light from sky or ground
7191 : Real64 TVISBR; // Transmittance of window without shading at COSB
7192 : // (times light well efficiency, if appropriate)
7193 : Real64 ZSU;
7194 : // element for clear and overcast sky
7195 : Real64 ObTrans; // Product of solar transmittances of obstructions seen by a light ray
7196 :
7197 : // unused REAL(r64) :: HitPointLumFrClearSky ! Luminance of obstruction from clear sky (cd/m2)
7198 : // unused REAL(r64) :: HitPointLumFrOvercSky ! Luminance of obstruction from overcast sky (cd/m2)
7199 : // unused REAL(r64) :: HitPointLumFrSun ! Luminance of obstruction from sun (cd/m2)
7200 : int ICtrl; // Window control pointer
7201 : Real64 COSBSun; // Cosine of angle of incidence of direct sun on window
7202 : Real64 TVISBSun; // Window's visible transmittance at COSBSun
7203 : // (times light well efficiency, if appropriate)
7204 : Real64 ZSU1; // Transmitted direct normal illuminance (lux)
7205 : // CHARACTER(len=32) :: ShType ! Window shading device type
7206 : bool ShadeOn; // True if exterior or interior window shade present
7207 : bool BlindOn; // True if exterior or interior window blind present
7208 : bool ScreenOn; // True if exterior window screen present
7209 : // int ScNum; // Screen number //Unused Set but never used
7210 : int PipeNum; // TDD pipe object number
7211 : int ShelfNum; // Daylighting shelf object number
7212 : int InShelfSurf; // Inside daylighting shelf surface number
7213 : int OutShelfSurf; // Outside daylighting shelf surface number
7214 : Real64 TransBlBmDiffFront; // Isolated blind vis beam-diffuse front transmittance
7215 : Real64 TransScBmDiffFront; // Isolated screen vis beam-diffuse front transmittance
7216 : Real64 ReflGlDiffDiffBack; // Bare glazing system vis diffuse back reflectance
7217 : Real64 ReflGlDiffDiffFront; // Bare glazing system vis diffuse front reflectance
7218 : Real64 ReflBlBmDiffFront; // Isolated blind vis beam-diffuse front reflectance
7219 : Real64 TransBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front transmittance
7220 : Real64 ReflBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front reflectance
7221 : Real64 ReflBlDiffDiffBack; // Isolated blind vis diffuse-diffuse back reflectance
7222 : Real64 ReflScDiffDiffBack; // Isolated screen vis diffuse-diffuse back reflectance
7223 :
7224 : Real64 td2; // Diffuse-diffuse vis trans of bare glass layers 2 and 3
7225 : Real64 td3;
7226 : Real64 rbd1; // Beam-diffuse back vis reflectance of bare glass layers 1 and 2
7227 : Real64 rbd2;
7228 : Real64 rfd2; // Beam-diffuse front vis reflectance of bare glass layers 2 and 3
7229 : Real64 rfd3;
7230 : Real64 tfshd; // Diffuse-diffuse front vis trans of bare blind
7231 : Real64 rbshd; // Diffuse-diffuse back vis reflectance of bare blind
7232 : Real64 ZSUObsRefl; // Illuminance on window from beam solar reflected by an
7233 : // obstruction (for unit beam normal illuminance)
7234 : int NearestHitSurfNum; // Surface number of nearest obstruction
7235 : int NearestHitSurfNumX; // Surface number to use when obstruction is a shadowing surface
7236 : Real64 LumAtHitPtFrSun; // Luminance at hit point on obstruction from solar reflection
7237 : // for unit beam normal illuminance (cd/m2)
7238 : Real64 SunObstructionMult; // = 1 if sun hits a ground point; otherwise = 0
7239 : bool hitObs; // True iff obstruction is hit
7240 : Real64 ObsVisRefl; // Visible reflectance of obstruction
7241 : Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by unobstructed sky
7242 : // diffuse horizontal illuminance [(cd/m2)/lux]
7243 : Real64 dReflObsSky; // Contribution to sky-related illuminance on window due to sky diffuse
7244 : // reflection from an obstruction
7245 : Real64 TVisSunRefl; // Diffuse vis trans of bare window for beam reflection calc
7246 : // (times light well efficiency, if appropriate)
7247 : Real64 ZSU1refl; // Beam normal illuminance times ZSU1refl = illuminance on window
7248 : // due to specular reflection from exterior surfaces
7249 :
7250 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
7251 : Real64 EnclInsideSurfArea; // temporary for calculations, total surface area of enclosure surfaces m2
7252 : int IntWinAdjZoneExtWinNum; // the index of the exterior window in IntWinAdjZoneExtWin nested struct
7253 : int IntWinNum; // window index for interior windows associated with exterior windows
7254 : Real64 COSBintWin;
7255 :
7256 : WinShadingType ShType;
7257 :
7258 333774 : auto &s_mat = state.dataMaterial;
7259 333774 : auto &dl = state.dataDayltg;
7260 333774 : auto &s_surf = state.dataSurface;
7261 :
7262 333774 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
7263 333774 : auto const &surf = s_surf->Surface(IWin);
7264 333774 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
7265 333774 : int const enclNumThisWin = s_surf->Surface(surf.BaseSurf).SolarEnclIndex;
7266 : // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea was calculated in subr DayltgAveInteriorReflectance
7267 :
7268 333774 : if (enclNumThisWin == enclNum) {
7269 326454 : extWinType = ExtWinType::InZone;
7270 326454 : EnclInsideSurfArea = dl->enclDaylight(enclNumThisWin).totInsSurfArea;
7271 326454 : IntWinAdjZoneExtWinNum = 0;
7272 : } else {
7273 7320 : extWinType = ExtWinType::AdjZone;
7274 : // If window is exterior window in adjacent zone, then use areas of both enclosures
7275 7320 : EnclInsideSurfArea = dl->enclDaylight(enclNum).totInsSurfArea + dl->enclDaylight(enclNumThisWin).totInsSurfArea;
7276 : // find index in IntWinAdjZoneExtWin
7277 7320 : for (int AdjExtWinLoop = 1; AdjExtWinLoop <= thisEnclDaylight.NumOfIntWinAdjEnclExtWins; ++AdjExtWinLoop) {
7278 2424 : if (IWin == thisEnclDaylight.IntWinAdjEnclExtWin(AdjExtWinLoop).SurfNum) { // found it
7279 2424 : IntWinAdjZoneExtWinNum = AdjExtWinLoop;
7280 2424 : break; // added TH 4/13/2010
7281 : }
7282 : }
7283 : }
7284 :
7285 : // Initialize window luminance and fluxes for split-flux calculation
7286 1335096 : dl->winLum(IHR)[(int)iWinCover_Bare] = dl->winLum(IHR)[(int)iWinCover_Shaded] = Illums();
7287 : // dl->WLUMSU(IHR, _) = 0.0;
7288 : // dl->WLUMSUdisk(IHR, _) = 0.0;
7289 :
7290 333774 : int const IConst = s_surf->SurfActiveConstruction(IWin);
7291 333774 : auto const &construct = state.dataConstruction->Construct(IConst);
7292 :
7293 333774 : BlindOn = false;
7294 333774 : ShadeOn = false;
7295 333774 : ScreenOn = false;
7296 :
7297 333774 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7298 4896 : PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
7299 : }
7300 :
7301 333774 : ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
7302 333774 : if (ShelfNum > 0) {
7303 2448 : InShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf; // Inside daylighting shelf present if > 0
7304 2448 : OutShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf; // Outside daylighting shelf present if > 0
7305 : } else {
7306 331326 : InShelfSurf = 0;
7307 331326 : OutShelfSurf = 0;
7308 : }
7309 :
7310 : // Divide sky and ground into elements of altitude PH and
7311 : // azimuth TH, and add the contribution of light coming from each
7312 : // element to the transmitted flux at the center of the window
7313 : // Azimuth ranges over a maximum of 2 Pi radians.
7314 : // Altitude ranges over a maximum of Pi/2 radians between -Pi/2 < PH < +Pi/2, so that elements are not counted twice
7315 : // PH = 0 at the horizon; PH = Pi/2 at the zenith
7316 333774 : PHMIN = max(-Constant::PiOvr2, surfWin.phi - Constant::PiOvr2);
7317 333774 : PHMAX = min(Constant::PiOvr2, surfWin.phi + Constant::PiOvr2);
7318 333774 : DPH = (PHMAX - PHMIN) / double(NPHMAX);
7319 :
7320 : // Sky/ground element altitude integration
7321 333774 : Vector3<Real64> const SUNCOS_IHR(s_surf->SurfSunCosHourly(IHR));
7322 3671514 : for (int IPH = 1; IPH <= NPHMAX; ++IPH) {
7323 3337740 : PH = PHMIN + (double(IPH) - 0.5) * DPH;
7324 :
7325 3337740 : SPH = std::sin(PH);
7326 3337740 : CPH = std::cos(PH);
7327 : // Third component of unit vector in (TH,PH) direction
7328 3337740 : U.z = SPH;
7329 :
7330 : // Limits of azimuth integration
7331 3337740 : PhWin = surfWin.phi;
7332 3337740 : ThWin = surfWin.theta;
7333 3337740 : if (PhWin >= 0.0) {
7334 3337740 : if (PH >= Constant::PiOvr2 - PhWin) {
7335 633828 : ThMin = -Constant::Pi;
7336 633828 : ThMax = Constant::Pi;
7337 : } else {
7338 2703912 : ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
7339 2703912 : ThMin = ThWin - std::abs(ACosTanTan);
7340 2703912 : ThMax = ThWin + std::abs(ACosTanTan);
7341 : }
7342 :
7343 : } else { // PhiSurf < 0.0
7344 0 : if (PH <= -PhWin - Constant::PiOvr2) {
7345 0 : ThMin = -Constant::Pi;
7346 0 : ThMax = Constant::Pi;
7347 : } else {
7348 0 : ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
7349 0 : ThMin = ThWin - std::abs(ACosTanTan);
7350 0 : ThMax = ThWin + std::abs(ACosTanTan);
7351 : }
7352 : }
7353 :
7354 3337740 : DTH = (ThMax - ThMin) / double(NTHMAX);
7355 3337740 : DA = CPH * DTH * DPH;
7356 :
7357 : // Sky/ground element azimuth integration
7358 3337740 : Real64 const sin_window_phi(std::sin(surfWin.phi));
7359 3337740 : Real64 const cos_window_phi(std::cos(surfWin.phi));
7360 56741580 : for (int ITH = 1; ITH <= NTHMAX; ++ITH) {
7361 53403840 : TH = ThMin + (double(ITH) - 0.5) * DTH;
7362 53403840 : U.x = CPH * std::cos(TH);
7363 53403840 : U.y = CPH * std::sin(TH);
7364 : // Cosine of angle of incidence of light from sky or ground element
7365 53403840 : COSB = SPH * sin_window_phi + CPH * cos_window_phi * std::cos(TH - surfWin.theta);
7366 53403840 : if (COSB < 0.0) {
7367 0 : continue; // Sky/ground elements behind window (although there shouldn't be any)
7368 : }
7369 :
7370 : // Initialize illuminance on window for this sky/ground element
7371 213615360 : ZSK = Illums();
7372 53403840 : ZSU = 0.0;
7373 : // Initialize illuminance on window from beam solar reflection if ray hits an obstruction
7374 53403840 : ZSUObsRefl = 0.0;
7375 :
7376 53403840 : if (ISunPos == 1) { // Intersection calculation has to be done only for first sun position
7377 : // Determine net transmittance of obstructions that the ray hits. ObTrans will be 1.0
7378 : // if no obstructions are hit.
7379 4207360 : ObTrans = DayltgHitObstruction(state, IHR, IWin, s_surf->SurfaceWindow(IWin).WinCenter, U);
7380 4207360 : dl->ObTransM[IPH][ITH] = ObTrans;
7381 4207360 : dl->SkyObstructionMult[IPH][ITH] = 1.0;
7382 : }
7383 :
7384 : // SKY AND GROUND RADIATION ON WINDOW
7385 :
7386 : // Contribution is from sky if PH > 0 (ray goes upward), and from ground if PH < 0 (ray goes downward)
7387 : // (There may also be contributions from reflection from obstructions; see 'BEAM SOLAR AND SKY SOLAR
7388 : // REFLECTED FROM NEAREST OBSTRUCTION,' below.)
7389 :
7390 53403840 : if (PH > 0.0) { // Contribution is from sky
7391 158862720 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7392 127090176 : ZSK.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH, PH) * COSB * DA * dl->ObTransM[IPH][ITH];
7393 : }
7394 : } else { // PH <= 0.0; contribution is from ground
7395 21631296 : if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
7396 : // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
7397 : // by the ray. This effect is given by the ratio SkyObstructionMult =
7398 : // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
7399 : // This ratio is calculated for an isotropic sky.
7400 : // Ground point hit by the ray:
7401 6080 : Real64 Alfa = std::acos(-U.z);
7402 6080 : Real64 Beta = std::atan2(U.y, U.x);
7403 6080 : Real64 HorDis = (s_surf->SurfaceWindow(IWin).WinCenter.z - s_surf->GroundLevelZ) * std::tan(Alfa);
7404 6080 : groundHitPt.z = s_surf->GroundLevelZ;
7405 6080 : groundHitPt.x = s_surf->SurfaceWindow(IWin).WinCenter.x + HorDis * std::cos(Beta);
7406 6080 : groundHitPt.y = s_surf->SurfaceWindow(IWin).WinCenter.y + HorDis * std::sin(Beta);
7407 :
7408 6080 : dl->SkyObstructionMult[IPH][ITH] =
7409 6080 : CalcObstrMultiplier(state, groundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
7410 : } // End of check if solar reflection calc is in effect
7411 :
7412 21631296 : auto const &gilsk = dl->horIllum[IHR];
7413 108156480 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7414 : // Below, luminance of ground in cd/m2 is illuminance on ground in lumens/m2
7415 : // times ground reflectance, divided by pi, times obstruction multiplier.
7416 173050368 : ZSK.sky[iSky] = (gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
7417 86525184 : dl->SkyObstructionMult[IPH][ITH];
7418 : }
7419 : // Determine if sun illuminates the point that ray hits the ground. If the solar reflection
7420 : // calculation has been requested (CalcSolRefl = .TRUE.) shading by obstructions, including
7421 : // the building itself, is considered in determining whether sun hits the ground point.
7422 : // Otherwise this shading is ignored and the sun always hits the ground point.
7423 21631296 : SunObstructionMult = 1.0;
7424 21631296 : if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
7425 : // Sun reaches ground point if vector from this point to the sun is unobstructed
7426 6080 : hitObs = false;
7427 27444 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
7428 23806 : hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
7429 23806 : if (hitObs) {
7430 2442 : break;
7431 : }
7432 6080 : }
7433 6080 : if (hitObs) {
7434 2442 : SunObstructionMult = 0.0;
7435 : }
7436 : }
7437 21631296 : ZSU = (dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
7438 : SunObstructionMult;
7439 : }
7440 : // BEAM SOLAR AND SKY SOLAR REFLECTED FROM NEAREST OBSTRUCTION
7441 :
7442 53403840 : if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] < 1.0) {
7443 : // Find obstruction whose hit point is closest to the center of the window
7444 36320 : DayltgClosestObstruction(state, s_surf->SurfaceWindow(IWin).WinCenter, U, NearestHitSurfNum, nearestHitPt);
7445 36320 : if (NearestHitSurfNum > 0) {
7446 :
7447 : // Beam solar reflected from nearest obstruction.
7448 36320 : LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, IHR, U, NearestHitSurfNum, nearestHitPt);
7449 36320 : ZSUObsRefl = LumAtHitPtFrSun * COSB * DA;
7450 36320 : ZSU += ZSUObsRefl;
7451 :
7452 : // Sky solar reflected from nearest obstruction.
7453 36320 : int const ObsConstrNum = s_surf->Surface(NearestHitSurfNum).Construction;
7454 36320 : if (ObsConstrNum > 0) {
7455 : // Exterior building surface is nearest hit
7456 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
7457 : // Obstruction is not a window, i.e., is an opaque surface
7458 0 : ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
7459 : } else {
7460 : // Obstruction is a window; assume it is bare
7461 0 : ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
7462 : }
7463 : } else {
7464 : // Shadowing surface is nearest hit
7465 36320 : if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
7466 : // Skip daylighting shelves, whose reflection is separately calculated
7467 0 : ObsVisRefl = 0.0;
7468 : } else {
7469 36320 : ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
7470 36320 : if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0) {
7471 0 : ObsVisRefl +=
7472 0 : s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
7473 0 : state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
7474 : }
7475 : // Note in the above that ShadowSurfDiffuseVisRefl is the reflectance of opaque part of
7476 : // shadowing surface times (1 - ShadowSurfGlazingFrac)
7477 : }
7478 : }
7479 36320 : NearestHitSurfNumX = NearestHitSurfNum;
7480 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
7481 : // The following gets the correct side of a shadowing surface for reflection.
7482 36320 : if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
7483 36320 : if (dot(U, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) {
7484 36320 : NearestHitSurfNumX = NearestHitSurfNum + 1;
7485 : }
7486 : }
7487 36320 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
7488 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
7489 36320 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
7490 36320 : state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
7491 : } else {
7492 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
7493 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, IHR, NearestHitSurfNumX) / Constant::Pi;
7494 : }
7495 36320 : dReflObsSky = SkyReflVisLum * COSB * DA;
7496 :
7497 36320 : auto const &gilsk = dl->horIllum[IHR];
7498 181600 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7499 145280 : ZSK.sky[iSky] += gilsk.sky[iSky] * dReflObsSky;
7500 : }
7501 : }
7502 : } // End of check if exterior solar reflection calculation is active
7503 :
7504 : // ===Bare window (no shade or blind; non-diffusing glass)===
7505 :
7506 : // Increment flux entering space and window luminance (cd/m2).
7507 : // FLCW--(I,J) = part of incoming flux (in lumens) that goes up to ceiling and upper part of walls.
7508 : // FLFW--(I,J) = part that goes down to floor and lower part of walls
7509 :
7510 53403840 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7511 : // Unshaded visible transmittance of TDD for a single ray from sky/ground element
7512 783360 : TVISBR = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
7513 :
7514 : // Make all transmitted light diffuse for a TDD with a bare diffuser
7515 783360 : auto &wlumsk = dl->winLum(IHR)[iWinCover_Bare];
7516 783360 : auto &flfwsk = FLFW[iWinCover_Bare];
7517 783360 : auto &flcwsk = FLCW[iWinCover_Bare];
7518 :
7519 783360 : auto &tddFluxInc = dl->TDDFluxInc(IHR, PipeNum);
7520 783360 : auto &tddFluxTrans = dl->TDDFluxTrans(IHR, PipeNum);
7521 3916800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7522 3133440 : wlumsk.sky[iSky] += ZSK.sky[iSky] * TVISBR / Constant::Pi;
7523 3133440 : flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * (1.0 - surfWin.fractionUpgoing);
7524 3133440 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7525 :
7526 : // For later calculation of diffuse visible transmittance
7527 3133440 : tddFluxInc.sky[iSky] += ZSK.sky[iSky];
7528 3133440 : tddFluxTrans.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7529 :
7530 : } // for (iSky)
7531 :
7532 783360 : tddFluxInc.sky[(int)SkyType::Clear] += ZSU;
7533 783360 : tddFluxTrans.sky[(int)SkyType::Clear] += ZSU * TVISBR;
7534 :
7535 783360 : dl->winLum(IHR)[iWinCover_Bare].sun += ZSU * TVISBR / Constant::Pi;
7536 783360 : flfwsk.sun += ZSU * TVISBR * (1.0 - surfWin.fractionUpgoing);
7537 783360 : flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7538 :
7539 : } else { // Bare window
7540 : // Transmittance of bare window for this sky/ground element
7541 52620480 : TVISBR = Window::POLYF(COSB, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
7542 :
7543 52620480 : if (InShelfSurf > 0) { // Inside daylighting shelf
7544 : // Daylighting shelf simplification: All light is diffuse
7545 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7546 391680 : auto &flcwsk = FLCW[iWinCover_Bare];
7547 1958400 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7548 1566720 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7549 : }
7550 391680 : flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7551 :
7552 : } else { // Normal window
7553 :
7554 : // CR 7869 correct TVISBR if disk beam passes thru interior window
7555 52228800 : if (extWinType == ExtWinType::AdjZone) {
7556 : // modify TVISBR by second window transmission
7557 : // first determine if ray from point passes thru any interior window
7558 387840 : hitObs = false;
7559 387840 : for (int IntWinLoop = 1; IntWinLoop <= thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).NumOfIntWindows;
7560 : ++IntWinLoop) {
7561 0 : IntWinNum = thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).IntWinNum(IntWinLoop);
7562 0 : auto const &surfIntWin = s_surf->SurfaceWindow(IntWinNum);
7563 0 : hitObs = PierceSurface(state, IntWinNum, surfIntWin.WinCenter, SUNCOS_IHR, obsHitPt);
7564 0 : if (hitObs) { // disk passes thru
7565 : // cosine of incidence angle of light from sky or ground element for
7566 0 : COSBintWin = SPH * std::sin(surfIntWin.phi) + CPH * std::cos(surfIntWin.phi) * std::cos(TH - surfIntWin.theta);
7567 0 : TVISBR *= Window::POLYF(COSBintWin,
7568 0 : state.dataConstruction->Construct(s_surf->Surface(IntWinNum).Construction).TransVisBeamCoef);
7569 0 : break;
7570 : }
7571 : }
7572 387840 : if (!hitObs) { // blocked by opaque parts, beam does not actually pass thru interior window to reach zone
7573 387840 : TVISBR = 0.0;
7574 : }
7575 : }
7576 :
7577 52228800 : auto &flfwsk = FLFW[iWinCover_Bare];
7578 52228800 : auto &flcwsk = FLCW[iWinCover_Bare];
7579 261144000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7580 : // IF (PH < 0.0d0) THEN
7581 : // Fixed by FCW, Nov. 2003:
7582 208915200 : if (PH > 0.0) {
7583 123486720 : flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7584 : } else {
7585 85428480 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7586 : }
7587 : } // for (iSky)
7588 :
7589 52228800 : if (PH > 0.0) {
7590 30871680 : flfwsk.sun += ZSU * TVISBR;
7591 : } else {
7592 21357120 : flcwsk.sun += ZSU * TVISBR;
7593 : }
7594 :
7595 : } // End of check if window with daylighting shelf or normal window
7596 : } // End of check if TDD:DOME or bare window
7597 :
7598 : // Check if window has shade or blind
7599 53403840 : ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
7600 53403840 : if (s_surf->Surface(IWin).HasShadeControl) {
7601 23911360 : ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
7602 23911360 : ShadeOn = ANY_SHADE(ShType);
7603 23911360 : BlindOn = ANY_BLIND(ShType);
7604 23911360 : ScreenOn = (ShType == WinShadingType::ExtScreen);
7605 : }
7606 :
7607 53403840 : if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
7608 :
7609 : // ===Window with interior or exterior shade or blind, exterior screen, or with diffusing glass===
7610 :
7611 : // Increment flux entering space and window luminance. Shades and diffusing glass are
7612 : // assumed to be perfect diffusers, i.e., the transmittance is independent of angle of
7613 : // incidence and the transmitted light is isotropic. The transmittance of a blind is
7614 : // assumed to depend on profile angle and slat angle; the diffuse light entering the room from
7615 : // the slats of the blind is assumed to be isotropic. With blinds, light can also enter
7616 : // the room by passing between the slats without reflection. The beam transmittance of a screen
7617 : // is assumed to depend on sun azimuth and azimuth angle.
7618 :
7619 : // For light from a shade, or from diffusing glass, or from the slats of a blind, a flux fraction,
7620 : // SurfaceWindow(IWin)%FractionUpgoing (determined by window tilt), goes up toward
7621 : // ceiling and upper part of walls, and 1-Surfacewindow(iwin)%FractionUpgoing
7622 : // goes down toward floor and lower part of walls. For a blind, the light passing
7623 : // between the slats goes either up or down depending on the altitude angle of the
7624 : // element from which the light came. For a screen, the light passing
7625 : // between the screen's cylinders goes either up or down depending on the altitude angle of the
7626 : // element from which the light came.
7627 :
7628 152960 : int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
7629 152960 : if (s_surf->SurfWinSolarDiffusing(IWin)) {
7630 0 : IConstShaded = s_surf->Surface(IWin).Construction;
7631 : }
7632 :
7633 : // Transmittance of window including shade, screen or blind
7634 152960 : Real64 transBmBmMult = 0.0;
7635 152960 : Real64 transMult = 0.0;
7636 :
7637 152960 : if (ShadeOn) { // Shade
7638 152960 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7639 : // Shaded visible transmittance of TDD for a single ray from sky/ground element
7640 0 : transMult = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
7641 : } else { // Shade only, no TDD
7642 : // Calculate transmittance of the combined window and shading device for this sky/ground element
7643 152960 : transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7644 152960 : surfWin.lightWellEff;
7645 : }
7646 :
7647 0 : } else if (ScreenOn) { // Screen: get beam-beam, beam-diffuse and diffuse-diffuse vis trans/ref of screen and glazing system
7648 0 : auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
7649 0 : assert(screen != nullptr);
7650 :
7651 0 : Real64 phi = std::abs(PH - surfWin.phi);
7652 0 : Real64 theta = std::abs(TH - surfWin.theta);
7653 : int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
7654 : BilinearInterpCoeffs coeffs;
7655 :
7656 0 : Material::NormalizePhiTheta(phi, theta);
7657 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
7658 0 : GetBilinearInterpCoeffs(phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
7659 :
7660 0 : ReflGlDiffDiffFront = state.dataConstruction->Construct(IConst).ReflectVisDiffFront;
7661 0 : ReflScDiffDiffBack = screen->DfRefVis;
7662 :
7663 0 : auto const &b11 = screen->btars[ip1][it1];
7664 0 : auto const &b12 = screen->btars[ip1][it2];
7665 0 : auto const &b21 = screen->btars[ip2][it1];
7666 0 : auto const &b22 = screen->btars[ip2][it2];
7667 :
7668 0 : TransScBmDiffFront = BilinearInterp(b11.DfTransVis, b12.DfTransVis, b21.DfTransVis, b22.DfTransVis, coeffs);
7669 :
7670 0 : transMult = TransScBmDiffFront * surfWin.glazedFrac * state.dataConstruction->Construct(IConst).TransDiffVis /
7671 0 : (1 - ReflGlDiffDiffFront * ReflScDiffDiffBack) * surfWin.lightWellEff;
7672 :
7673 0 : transBmBmMult = BilinearInterp(b11.BmTransVis, b12.BmTransVis, b21.BmTransVis, b22.BmTransVis, coeffs);
7674 :
7675 0 : } else if (BlindOn) { // Blind: get beam-diffuse and beam-beam vis trans of blind+glazing system
7676 : // PETER: As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
7677 : // for TDDs because it is based on TVISBR which is correctly calculated for TDDs above.
7678 0 : auto const &surfShade = s_surf->surfShades(IWin);
7679 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
7680 0 : assert(matBlind != nullptr);
7681 0 : Real64 ProfAng = ProfileAngle(state, IWin, U, matBlind->SlatOrientation);
7682 :
7683 0 : auto &btar = surfShade.blind.TAR;
7684 0 : int idxLo = surfShade.blind.profAngIdxLo;
7685 0 : int idxHi = std::min(Material::MaxProfAngs, idxLo + 1);
7686 0 : Real64 interpFac = surfShade.blind.profAngInterpFac;
7687 0 : TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7688 :
7689 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7690 0 : ReflGlDiffDiffBack = construct.ReflectVisDiffBack;
7691 0 : ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7692 0 : ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
7693 0 : TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
7694 0 : transMult = TVISBR * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7695 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7696 :
7697 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7698 0 : ReflGlDiffDiffFront = construct.ReflectVisDiffFront;
7699 0 : ReflBlDiffDiffBack = btar.Vis.Bk.Df.Ref;
7700 0 : transMult = TransBlBmDiffFront * surfWin.glazedFrac * construct.TransDiffVis /
7701 0 : (1.0 - ReflGlDiffDiffFront * ReflBlDiffDiffBack) * surfWin.lightWellEff;
7702 :
7703 : } else { // Between-glass blind
7704 0 : Real64 t1 = Window::POLYF(COSB, construct.tBareVisCoef(1));
7705 0 : td2 = construct.tBareVisDiff(2);
7706 0 : rbd1 = construct.rbBareVisDiff(1);
7707 0 : rfd2 = construct.rfBareVisDiff(2);
7708 0 : Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7709 0 : tfshd = btar.Vis.Ft.Df.Tra;
7710 0 : Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7711 0 : rbshd = btar.Vis.Ft.Df.Ref;
7712 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7713 0 : transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7714 : } else { // 3 glass layers; blind between layers 2 and 3
7715 0 : Real64 t2 = Window::POLYF(COSB, construct.tBareVisCoef(2));
7716 0 : td3 = construct.tBareVisDiff(3);
7717 0 : rfd3 = construct.rfBareVisDiff(3);
7718 0 : rbd2 = construct.rbBareVisDiff(2);
7719 0 : transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7720 0 : surfWin.lightWellEff;
7721 : }
7722 : }
7723 :
7724 0 : transBmBmMult = TVISBR * matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
7725 : } else { // Diffusing glass
7726 0 : transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7727 0 : surfWin.lightWellEff;
7728 : } // End of check if shade, blind or diffusing glass
7729 :
7730 152960 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7731 : // No beam is transmitted. This takes care of all types of screens and blinds.
7732 0 : transBmBmMult = 0.0;
7733 : }
7734 :
7735 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7736 152960 : if (InShelfSurf > 0) { // Inside daylighting shelf
7737 0 : transBmBmMult = 0.0;
7738 : }
7739 :
7740 : // DayltgInterReflectedIllumTransBmBmMult is used in the following for windows with blinds or screens to get contribution from light
7741 : // passing directly between slats or between screen material without reflection.
7742 :
7743 152960 : auto &wlumsk = dl->winLum(IHR)[iWinCover_Shaded];
7744 152960 : auto &flfwsk = FLFW[iWinCover_Shaded];
7745 152960 : auto &flcwsk = FLCW[iWinCover_Shaded];
7746 :
7747 764800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7748 : // Should these be bare or shaded?
7749 611840 : wlumsk.sky[iSky] += ZSK.sky[iSky] * transMult / Constant::Pi;
7750 611840 : flfwsk.sky[iSky] += ZSK.sky[iSky] * transMult * (1.0 - surfWin.fractionUpgoing);
7751 611840 : flcwsk.sky[iSky] += ZSK.sky[iSky] * transMult * surfWin.fractionUpgoing;
7752 :
7753 611840 : if (BlindOn || ScreenOn) {
7754 0 : if (PH > 0.0) {
7755 0 : flfwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
7756 : } else {
7757 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
7758 : }
7759 : }
7760 : }
7761 :
7762 152960 : dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU * transMult / Constant::Pi;
7763 152960 : flfwsk.sun += ZSU * transMult * (1.0 - surfWin.fractionUpgoing);
7764 152960 : flcwsk.sun += ZSU * transMult * surfWin.fractionUpgoing;
7765 152960 : if (BlindOn || ScreenOn) {
7766 0 : if (PH > 0.0) {
7767 0 : flfwsk.sun += ZSU * transBmBmMult;
7768 : } else {
7769 0 : flcwsk.sun += ZSU * transBmBmMult;
7770 : }
7771 : }
7772 : } // End of window with shade, screen, blind or diffusing glass
7773 :
7774 : } // End of azimuth integration loop, ITH
7775 : } // End of altitude integration loop, IPH
7776 :
7777 333774 : if (OutShelfSurf > 0) { // Outside daylighting shelf
7778 : // Add exterior diffuse illuminance due to outside shelf
7779 : // Since all of the illuminance is added to the zone as upgoing diffuse, it can be added as a lump sum here
7780 :
7781 2448 : TVISBR = construct.TransDiffVis; // Assume diffuse transmittance for shelf illuminance
7782 :
7783 2448 : auto const &gilsk = dl->horIllum[IHR];
7784 2448 : auto &flcwsk = FLCW[iWinCover_Bare];
7785 12240 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7786 : // This is only an estimate because the anisotropic sky view of the shelf is not yet taken into account.
7787 : // SurfAnisoSkyMult would be great to use but it is not available until the heat balance starts up.
7788 9792 : ZSK.sky[iSky] = gilsk.sky[iSky] * 1.0 * state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis *
7789 9792 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
7790 :
7791 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7792 9792 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7793 : } // ISKY
7794 :
7795 2448 : ZSU = dl->horIllum[IHR].sun * state.dataHeatBal->SurfSunlitFracHR(IHR, OutShelfSurf) *
7796 2448 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis * state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
7797 2448 : flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7798 : }
7799 :
7800 : // Sky-related portion of internally reflected illuminance.
7801 : // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea, and ZoneDaylight(ZoneNum)%aveVisDiffReflect,
7802 : // were calculated in subr DayltgAveInteriorReflectance.
7803 :
7804 1001322 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
7805 667548 : auto &eintsk = dl->reflIllum(IHR)[iWinCover];
7806 667548 : auto const &flfwsk = FLFW[iWinCover];
7807 667548 : auto const &flcwsk = FLCW[iWinCover];
7808 :
7809 3337740 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7810 : // Full area of window is used in following since effect of dividers on reducing
7811 : // effective window transmittance has already been accounted for in calc of FLFWSK and FLCWSK.
7812 5340384 : eintsk.sky[iSky] = (flfwsk.sky[iSky] * surfWin.rhoFloorWall + flcwsk.sky[iSky] * surfWin.rhoCeilingWall) *
7813 2670192 : (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - dl->enclDaylight(enclNum).aveVisDiffReflect));
7814 : } // for (iSky)
7815 : } // for (iWinCover)
7816 :
7817 : // BEAM SOLAR RADIATION ON WINDOW
7818 :
7819 : // Beam reaching window directly (without specular reflection from exterior obstructions)
7820 :
7821 333774 : if (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0) {
7822 : // Cos of angle of incidence
7823 247741 : COSBSun = dl->sunAngles.sinPhi * std::sin(surfWin.phi) +
7824 247741 : dl->sunAngles.cosPhi * std::cos(surfWin.phi) * std::cos(dl->sunAngles.theta - surfWin.theta);
7825 :
7826 247741 : if (COSBSun > 0.0) {
7827 : // Multiply direct normal illuminance (normalized to 1.0 lux)
7828 : // by incident angle factor and by fraction of window that is sunlit.
7829 : // Note that in the following SurfSunlitFracHR accounts for possibly non-zero transmittance of
7830 : // shading surfaces.
7831 :
7832 247741 : ZSU1 = COSBSun * state.dataHeatBal->SurfSunlitFracHR(IHR, IWin);
7833 :
7834 : // Contribution to window luminance and downgoing flux
7835 :
7836 : // -- Bare window
7837 :
7838 247741 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7839 : // Unshaded visible transmittance of TDD for collimated beam from the sun
7840 4488 : TVISBSun = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
7841 4488 : dl->TDDTransVisBeam(IHR, PipeNum) = TVISBSun;
7842 :
7843 4488 : FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
7844 :
7845 4488 : dl->winLum(IHR)[iWinCover_Bare].sun += ZSU1 * TVISBSun / Constant::Pi;
7846 4488 : FLFW[iWinCover_Bare].sun += ZSU1 * TVISBSun * (1.0 - surfWin.fractionUpgoing);
7847 4488 : FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
7848 :
7849 : } else { // Bare window
7850 243253 : TVISBSun = Window::POLYF(COSBSun, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
7851 :
7852 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7853 243253 : if (InShelfSurf > 0) { // Inside daylighting shelf
7854 1836 : FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
7855 :
7856 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7857 : // WLUMSU(1,IHR) = WLUMSU(1,IHR) + ZSU1 * TVISBSun / PI
7858 : // FLFWSU(1) = FLFWSU(1) + ZSU1 * TVISBSun * (1.0 - SurfaceWindow(IWin)%FractionUpgoing)
7859 1836 : FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
7860 : } else { // Normal window
7861 241417 : FLFW[iWinCover_Bare].sunDisk = ZSU1 * TVISBSun;
7862 : }
7863 : }
7864 :
7865 : // -- Window with shade, screen, blind or diffusing glass
7866 247741 : if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
7867 472 : Real64 transBmBmMult = 0.0;
7868 472 : Real64 transMult = 0.0;
7869 :
7870 472 : if (ShadeOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade or screen on or diffusing glass
7871 472 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7872 : // Shaded visible transmittance of TDD for collimated beam from the sun
7873 0 : transMult = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
7874 :
7875 472 : } else if (ScreenOn) {
7876 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
7877 0 : assert(screen != nullptr);
7878 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
7879 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
7880 : int ip1, ip2, it1, it2;
7881 : BilinearInterpCoeffs coeffs;
7882 0 : Material::NormalizePhiTheta(phi, theta);
7883 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
7884 0 : GetBilinearInterpCoeffs(
7885 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
7886 0 : Real64 BmBmTransVis = BilinearInterp(screen->btars[ip1][it1].BmTransVis,
7887 0 : screen->btars[ip1][it2].BmTransVis,
7888 0 : screen->btars[ip2][it1].BmTransVis,
7889 0 : screen->btars[ip2][it2].BmTransVis,
7890 : coeffs);
7891 :
7892 0 : transMult = BmBmTransVis * surfWin.glazedFrac * surfWin.lightWellEff;
7893 : } else {
7894 472 : int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
7895 472 : if (s_surf->SurfWinSolarDiffusing(IWin)) {
7896 0 : IConstShaded = surf.Construction;
7897 : }
7898 472 : transMult = Window::POLYF(COSBSun, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7899 472 : surfWin.lightWellEff;
7900 : }
7901 :
7902 : } else { // Blind on
7903 0 : auto const &surfShade = s_surf->surfShades(IWin);
7904 : // As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
7905 : // for TDDs because it is based on TVISBSun which is correctly calculated for TDDs above.
7906 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
7907 0 : assert(matBlind != nullptr);
7908 :
7909 : // These are "cached" in the surfShade struct
7910 0 : auto &btar = surfShade.blind.TAR;
7911 0 : int idxLo = surfShade.blind.profAngIdxLo;
7912 0 : int idxHi = surfShade.blind.profAngIdxHi;
7913 0 : int interpFac = surfShade.blind.profAngInterpFac;
7914 0 : TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7915 :
7916 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7917 : // TH CR 8121, 7/7/2010
7918 : // ReflBlBmDiffFront = WindowManager::InterpProfAng(ProfAng,Blind(BlNum)%VisFrontBeamDiffRefl)
7919 0 : ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7920 :
7921 : // TH added 7/12/2010 for CR 8121
7922 0 : ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
7923 0 : TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
7924 :
7925 0 : transMult = TVISBSun * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7926 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7927 :
7928 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7929 0 : transMult = TransBlBmDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
7930 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7931 :
7932 : } else { // Between-glass blind
7933 0 : Real64 t1 = Window::POLYF(COSBSun, construct.tBareVisCoef(1));
7934 0 : Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7935 0 : Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7936 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7937 0 : transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7938 : } else { // 3 glass layers; blind between layers 2 and 3
7939 0 : Real64 t2 = Window::POLYF(COSBSun, construct.tBareVisCoef(2));
7940 0 : transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7941 0 : surfWin.lightWellEff;
7942 : }
7943 : }
7944 :
7945 0 : transBmBmMult = TVISBSun * matBlind->BeamBeamTrans(surfShade.blind.profAng, surfShade.blind.slatAng);
7946 : } // ShadeOn/ScreenOn/BlindOn/Diffusing glass
7947 :
7948 472 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7949 0 : transBmBmMult = 0.0; // No beam, diffuse only
7950 : }
7951 :
7952 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7953 472 : if (InShelfSurf > 0) { // Inside daylighting shelf
7954 0 : transBmBmMult = 0.0; // No beam, diffuse only
7955 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7956 : }
7957 :
7958 472 : dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1 * transMult / Constant::Pi;
7959 472 : dl->winLum(IHR)[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult / Constant::Pi;
7960 472 : FLFW[iWinCover_Shaded].sun += ZSU1 * transMult * (1.0 - surfWin.fractionUpgoing);
7961 472 : FLFW[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult;
7962 472 : FLCW[iWinCover_Shaded].sun += ZSU1 * transMult * surfWin.fractionUpgoing;
7963 : } // if (BlindOn || ShadeOn)
7964 : } // if (COSBSun > 0)
7965 : } // if (SurfSunlitFracHR > 0)
7966 :
7967 : // Beam reaching window after specular reflection from exterior obstruction
7968 :
7969 : // In the following, Beam normal illuminance times ZSU1refl = illuminance on window due to
7970 : // specular reflection from exterior surfaces
7971 :
7972 333774 : if (s_surf->CalcSolRefl && s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Dome) {
7973 :
7974 908 : ZSU1refl = s_surf->SurfReflFacBmToBmSolObs(IHR, IWin);
7975 :
7976 908 : if (ZSU1refl > 0.0) {
7977 : // Contribution to window luminance and downgoing flux
7978 :
7979 : // -- Bare window. We use diffuse-diffuse transmittance here rather than beam-beam to avoid
7980 : // complications due to specular reflection from multiple exterior surfaces
7981 :
7982 0 : TVisSunRefl = construct.TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
7983 : // In the following it is assumed that all reflected beam is going downward, as it would be in the
7984 : // important case of reflection from a highly glazed facade of a neighboring building. However, in
7985 : // rare cases (such as upward specular reflection from a flat horizontal skylight) it may
7986 : // actually be going upward.
7987 0 : FLFW[iWinCover_Bare].sunDisk += ZSU1refl * TVisSunRefl;
7988 :
7989 : // -- Window with shade, blind or diffusing glass
7990 :
7991 0 : if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
7992 0 : Real64 transMult = 0.0;
7993 :
7994 0 : if (ShadeOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade on or diffusing glass
7995 0 : int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
7996 0 : if (s_surf->SurfWinSolarDiffusing(IWin)) {
7997 0 : IConstShaded = s_surf->Surface(IWin).Construction;
7998 : }
7999 0 : transMult = state.dataConstruction->Construct(IConstShaded).TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
8000 :
8001 0 : } else if (ScreenOn) { // Exterior screen on
8002 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
8003 0 : assert(screen != nullptr);
8004 0 : Real64 TransScDiffDiffFront = screen->DfTransVis;
8005 :
8006 0 : transMult = TransScDiffDiffFront *
8007 0 : (state.dataConstruction->Construct(IConst).TransDiffVis / (1.0 - ReflGlDiffDiffFront * ReflScDiffDiffBack)) *
8008 0 : surfWin.glazedFrac * surfWin.lightWellEff;
8009 :
8010 : } else { // Blind on
8011 :
8012 0 : auto const &surfShade = s_surf->surfShades(IWin);
8013 0 : auto const &btar = surfShade.blind.TAR;
8014 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
8015 :
8016 0 : assert(matBlind != nullptr);
8017 :
8018 0 : TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
8019 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
8020 0 : ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
8021 0 : transMult = TVisSunRefl * (TransBlDiffDiffFront + ReflBlDiffDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
8022 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
8023 :
8024 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
8025 0 : transMult = TransBlDiffDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
8026 0 : surfWin.glazedFrac * surfWin.lightWellEff;
8027 :
8028 : } else { // Between-glass blind
8029 0 : Real64 t1 = construct.tBareVisDiff(1);
8030 0 : Real64 tfshBd = btar.Vis.Ft.Df.Tra;
8031 0 : Real64 rfshB = btar.Vis.Ft.Df.Ref;
8032 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
8033 0 : transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
8034 : } else { // 3 glass layers; blind between layers 2 and 3
8035 0 : Real64 t2 = construct.tBareVisDiff(2);
8036 0 : transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
8037 0 : surfWin.lightWellEff;
8038 : }
8039 : } // End of check of interior/exterior/between-glass blind
8040 : } // if (Blind)
8041 :
8042 0 : dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1refl * transMult / Constant::Pi;
8043 0 : FLFW[iWinCover_Shaded].sun += ZSU1refl * transMult * (1.0 - surfWin.fractionUpgoing);
8044 0 : FLCW[iWinCover_Shaded].sun += ZSU1refl * transMult * surfWin.fractionUpgoing;
8045 : } // End of check if window has shade, blind or diffusing glass
8046 : } // End of check if ZSU1refl > 0.0
8047 : } // End of check if solar reflections are in effect
8048 :
8049 : // Sun-related portion of internally reflected illuminance
8050 :
8051 : // Full area of window is used in following since effect of dividers on reducing
8052 : // effective window transmittance already accounted for in calc of FLFWSU and FLCWSU
8053 : // CR 7869 added effect of intervening interior windows on transmittance and
8054 : // added inside surface area of adjacent zone
8055 1001322 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
8056 1335096 : dl->reflIllum(IHR)[iWinCover].sun = (FLFW[iWinCover].sun * surfWin.rhoFloorWall + FLCW[iWinCover].sun * surfWin.rhoCeilingWall) *
8057 667548 : (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8058 :
8059 1335096 : dl->reflIllum(IHR)[iWinCover].sunDisk = FLFW[iWinCover].sunDisk * surfWin.rhoFloorWall * (surf.Area / surfWin.glazedFrac) /
8060 667548 : (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8061 : }
8062 333774 : } // DayltgInterReflectedIllum()
8063 :
8064 1674 : void ComplexFenestrationLuminances(EnergyPlusData &state,
8065 : int const IWin,
8066 : int const WinEl,
8067 : int const NBasis,
8068 : int const IHR,
8069 : int const iRefPoint,
8070 : Array1D<Illums> &ElementLuminance, // luminance at window element (exterior side)
8071 : CalledFor const CalledFrom,
8072 : int const MapNum)
8073 : {
8074 :
8075 : // SUBROUTINE INFORMATION:
8076 : // AUTHOR Simon Vidanovic
8077 : // DATE WRITTEN June 2013
8078 :
8079 1674 : Vector3<Real64> obsHitPt; // Coordinates of hit point on an obstruction (m)
8080 1674 : Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
8081 :
8082 1674 : auto &dl = state.dataDayltg;
8083 1674 : auto &s_surf = state.dataSurface;
8084 :
8085 1674 : int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
8086 1674 : auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
8087 : // Calculate luminance from sky and sun excluding exterior obstruction transmittances and obstruction multipliers
8088 1674 : int SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
8089 244404 : for (int iIncElem = 1; iIncElem <= NBasis; ++iIncElem) {
8090 242730 : Real64 LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
8091 : // COSB = ComplexWind(IWin)%Geom(CurCplxFenState)%CosInc(iIncElem)
8092 : // DA = ComplexWind(IWin)%Geom(CurCplxFenState)%DAInc(iIncElem)
8093 242730 : Real64 Altitude = complexWinGeom.pInc(iIncElem).Altitude;
8094 242730 : Real64 Azimuth = complexWinGeom.pInc(iIncElem).Azimuth;
8095 242730 : auto &elemLum = ElementLuminance(iIncElem);
8096 242730 : auto const &gilsk = dl->horIllum[IHR];
8097 :
8098 242730 : if (Altitude > 0.0) {
8099 : // Ray from sky element
8100 535680 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8101 428544 : elemLum.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc;
8102 : }
8103 135594 : } else if (Altitude < 0.0) {
8104 : // Ray from ground element
8105 : // BeamObstrMultiplier = ComplexWind(IWin)%DaylghtGeom(CurCplxFenState)%GndObstrMultiplier(WinEl, iIncElem)
8106 535680 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8107 428544 : elemLum.sky[iSky] = gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
8108 : }
8109 107136 : elemLum.sun = dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
8110 : } else {
8111 : // Ray from the element which is half sky and half ground
8112 142290 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8113 : // in this case half of the pach is coming from the sky and half from the ground
8114 227664 : elemLum.sky[iSky] = 0.5 * DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc +
8115 113832 : 0.5 * gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
8116 : }
8117 28458 : elemLum.sun = 0.5 * dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
8118 : }
8119 : // Sun beam calculations
8120 242730 : if ((SolBmIndex == iIncElem) && (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0)) {
8121 1008 : elemLum.sunDisk = 1.0;
8122 : }
8123 : }
8124 :
8125 1674 : auto const &complexWinDaylightGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CurCplxFenState);
8126 :
8127 1674 : if (CalledFrom == CalledFor::RefPoint) {
8128 1674 : auto const &complexWinRefPoint = complexWinDaylightGeom.RefPoint(iRefPoint);
8129 : // add exterior obstructions transmittances to calculated luminances
8130 1674 : for (int iReflElem = 1; iReflElem <= complexWinRefPoint.NReflSurf(WinEl); ++iReflElem) {
8131 0 : Real64 ObstrTrans = complexWinRefPoint.TransOutSurf(iReflElem, WinEl);
8132 0 : int iReflElemIndex = complexWinRefPoint.RefSurfIndex(iReflElem, WinEl);
8133 :
8134 0 : auto &elemLum = ElementLuminance(iReflElemIndex);
8135 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8136 0 : elemLum.sky[iSky] *= ObstrTrans;
8137 : }
8138 0 : elemLum.sun *= ObstrTrans;
8139 0 : elemLum.sunDisk *= ObstrTrans;
8140 : }
8141 :
8142 : // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
8143 : // sun reaches the ground for that point
8144 1674 : Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
8145 108810 : for (int iGndElem = 1; iGndElem <= complexWinRefPoint.NGnd(WinEl); ++iGndElem) {
8146 : // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
8147 : // were hit in the process
8148 :
8149 107136 : Real64 BeamObstrMultiplier = complexWinRefPoint.GndObstrMultiplier(iGndElem, WinEl);
8150 107136 : int iGndElemIndex = complexWinRefPoint.GndIndex(iGndElem, WinEl);
8151 :
8152 107136 : auto &elemLum = ElementLuminance(iGndElemIndex);
8153 535680 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8154 428544 : elemLum.sky[iSky] *= BeamObstrMultiplier;
8155 : }
8156 :
8157 : // direct sun disk reflect off the ground
8158 107136 : Real64 SunObstrMultiplier = 1.0;
8159 107136 : if (s_surf->CalcSolRefl) {
8160 : // Sun reaches ground point if vector from this point to the sun is unobstructed
8161 204042 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
8162 107136 : groundHitPt = complexWinRefPoint.GndPt(iGndElem, WinEl);
8163 107136 : bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
8164 107136 : if (hitObs) {
8165 10230 : SunObstrMultiplier = 0.0;
8166 10230 : break;
8167 : }
8168 107136 : }
8169 : }
8170 107136 : elemLum.sun *= SunObstrMultiplier;
8171 : }
8172 :
8173 1674 : } else { // if (CalledFrom != RefPoint)
8174 :
8175 0 : auto const &complexWinIllumMap = complexWinDaylightGeom.IlluminanceMap(iRefPoint, MapNum);
8176 : // add exterior obstructions transmittances to calculated luminances
8177 0 : for (int iReflElem = 1; iReflElem <= complexWinIllumMap.NReflSurf(WinEl); ++iReflElem) {
8178 0 : Real64 ObstrTrans = complexWinIllumMap.TransOutSurf(iReflElem, WinEl);
8179 0 : int iReflElemIndex = complexWinIllumMap.RefSurfIndex(iReflElem, WinEl);
8180 0 : auto &elemLum = ElementLuminance(iReflElemIndex);
8181 :
8182 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8183 0 : elemLum.sky[iSky] *= ObstrTrans;
8184 : }
8185 0 : elemLum.sun *= ObstrTrans;
8186 0 : elemLum.sunDisk *= ObstrTrans;
8187 : }
8188 :
8189 : // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
8190 : // sun reaches the ground for that point
8191 0 : Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
8192 0 : for (int iGndElem = 1; iGndElem <= complexWinIllumMap.NGnd(WinEl); ++iGndElem) {
8193 : // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
8194 : // were hit in the process
8195 0 : Real64 BeamObstrMultiplier = complexWinIllumMap.GndObstrMultiplier(iGndElem, WinEl);
8196 0 : int iGndElemIndex = complexWinIllumMap.GndIndex(iGndElem, WinEl);
8197 :
8198 0 : auto &elemLum = ElementLuminance(iGndElemIndex);
8199 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8200 0 : elemLum.sky[iSky] *= BeamObstrMultiplier;
8201 : }
8202 :
8203 : // direct sun disk reflect off the ground
8204 0 : Real64 SunObstrMultiplier = 1.0;
8205 0 : if (s_surf->CalcSolRefl) {
8206 : // Sun reaches ground point if vector from this point to the sun is unobstructed
8207 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
8208 0 : groundHitPt = complexWinIllumMap.GndPt(iGndElem, WinEl);
8209 :
8210 0 : bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
8211 0 : if (hitObs) {
8212 0 : SunObstrMultiplier = 0.0;
8213 0 : break;
8214 : }
8215 0 : }
8216 : }
8217 0 : elemLum.sun *= SunObstrMultiplier;
8218 : }
8219 0 : } // if (CalledFrom == RefPoint)
8220 1674 : } // ComplexFenestrationLuminances()
8221 :
8222 186 : void DayltgInterReflectedIllumComplexFenestration(EnergyPlusData &state,
8223 : int const IWin, // Window index
8224 : int const WinEl, // Current window element counter
8225 : int const IHR, // Hour of day
8226 : int const daylightCtrlNum, // Daylighting control number
8227 : int const iRefPoint, // reference point counter
8228 : CalledFor const CalledFrom,
8229 : int const MapNum)
8230 : {
8231 :
8232 : // SUBROUTINE INFORMATION:
8233 : // AUTHOR Simon Vidanovic
8234 : // DATE WRITTEN April 2013
8235 :
8236 : // PURPOSE OF THIS SUBROUTINE:
8237 : // Called from CalcDayltgCoefficients for each complex (bsdf) fenestration and reference point in a daylit
8238 : // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
8239 : // to internally reflected light by integrating to determine the amount of flux from
8240 : // sky and ground (and beam reflected from obstructions) transmitted through
8241 : // the center of the window and then reflecting this
8242 : // light from the inside surfaces of the space.
8243 :
8244 186 : auto &dl = state.dataDayltg;
8245 186 : auto &s_surf = state.dataSurface;
8246 :
8247 186 : Array1D<Illums> FL; // Sky related luminous flux
8248 : // Array1D<Real64> FLSU; // Sun related luminous flux, excluding entering beam
8249 : // Array1D<Real64> FLSUdisk; // Sun related luminous flux, due to entering beam
8250 :
8251 186 : Array1D<Illums> FirstFlux; // Sky related first reflected flux
8252 : // Array1D<Real64> FirstFluxSU; // Sun related first reflected flux, excluding entering beam
8253 : // Array1D<Real64> FirstFluxSUdisk; // Sun related first reflected flux, due to entering beam
8254 :
8255 186 : Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
8256 : // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side), exluding beam
8257 : // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side), due to sun beam
8258 186 : Illums FLTot;
8259 : // Real64 FLSUTot;
8260 : // Real64 FLSUdiskTot;
8261 :
8262 : // Total for first relflected fluxes
8263 186 : Illums FFTot = Illums();
8264 : // Real64 FFSUTot;
8265 : // Real64 FFSUdiskTot;
8266 :
8267 : int NIncBasis;
8268 : int SolBmIndex; // index of current sun position
8269 :
8270 : Real64 LambdaInc; // current lambda value for incoming direction
8271 : // REAL(r64) :: LambdaTrn ! current lambda value for incoming direction
8272 : Real64 dirTrans; // directional bsdf transmittance
8273 :
8274 186 : auto const &surf = s_surf->Surface(IWin);
8275 186 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
8276 :
8277 186 : int CurCplxFenState = surfWin.ComplexFen.CurrentState;
8278 186 : auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
8279 186 : int iConst = surfWin.ComplexFen.State(CurCplxFenState).Konst;
8280 186 : int NTrnBasis = complexWinGeom.Trn.NBasis;
8281 :
8282 186 : if (!allocated(FL)) {
8283 186 : FL.allocate(NTrnBasis);
8284 : }
8285 744 : FL = Illums();
8286 : // if (!allocated(FLSU)) FLSU.dimension(NTrnBasis, 0.0);
8287 : // if (!allocated(FLSUdisk)) FLSUdisk.dimension(NTrnBasis, 0.0);
8288 :
8289 186 : if (!allocated(FirstFlux)) {
8290 186 : FirstFlux.allocate(NTrnBasis);
8291 : }
8292 744 : FirstFlux = Illums();
8293 : // if (!allocated(FirstFluxSU)) FirstFluxSU.dimension(NTrnBasis, 0.0);
8294 : // if (!allocated(FirstFluxSUdisk)) FirstFluxSUdisk.dimension(NTrnBasis, 0.0);
8295 :
8296 186 : NIncBasis = complexWinGeom.Inc.NBasis;
8297 186 : if (!allocated(ElementLuminance)) {
8298 186 : ElementLuminance.allocate(NIncBasis);
8299 : }
8300 744 : ElementLuminance = Illums();
8301 : // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
8302 : // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
8303 :
8304 : // Integration over sky/ground/sun elements is done over window incoming basis element and flux is calculated for each
8305 : // outgoing direction. This is used to calculate first reflected flux
8306 :
8307 186 : ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
8308 :
8309 : // luminance from sun disk needs to include fraction of sunlit area
8310 186 : SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
8311 186 : Real64 COSIncSun = (SolBmIndex > 0) ? complexWinGeom.CosInc(SolBmIndex) : 0.0;
8312 :
8313 27156 : for (int i = 1; i <= (int)ElementLuminance.size(); ++i) {
8314 26970 : ElementLuminance(i).sunDisk *= state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) * COSIncSun;
8315 : }
8316 :
8317 : // FLSKTot = 0.0;
8318 186 : FLTot.sun = 0.0;
8319 186 : FLTot.sunDisk = 0.0;
8320 186 : FFTot.sun = 0.0;
8321 186 : FFTot.sunDisk = 0.0;
8322 : // now calculate flux into each outgoing direction by integrating over all incoming directions
8323 27156 : for (int iBackElem = 1; iBackElem <= NTrnBasis; ++iBackElem) {
8324 3937620 : for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
8325 3910650 : LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
8326 3910650 : dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iBackElem, iIncElem);
8327 :
8328 3910650 : auto &fl = FL(iBackElem);
8329 3910650 : auto const &elemLum = ElementLuminance(iIncElem);
8330 19553250 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8331 15642600 : fl.sky[iSky] += dirTrans * LambdaInc * elemLum.sky[iSky];
8332 : }
8333 :
8334 3910650 : fl.sun += dirTrans * LambdaInc * elemLum.sun;
8335 3910650 : fl.sunDisk += dirTrans * LambdaInc * elemLum.sunDisk;
8336 : }
8337 :
8338 26970 : auto &firstFlux = FirstFlux(iBackElem);
8339 26970 : auto const &fl = FL(iBackElem);
8340 134850 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8341 107880 : firstFlux.sky[iSky] = fl.sky[iSky] * complexWinGeom.AveRhoVisOverlap(iBackElem);
8342 107880 : FFTot.sky[iSky] += firstFlux.sky[iSky];
8343 : // FLSKTot( iSky ) += FLSK( iSky, iBackElem );
8344 : }
8345 26970 : firstFlux.sun = fl.sun * complexWinGeom.AveRhoVisOverlap(iBackElem);
8346 26970 : FFTot.sun += firstFlux.sun;
8347 26970 : FLTot.sun += fl.sun;
8348 :
8349 26970 : firstFlux.sunDisk = fl.sunDisk * complexWinGeom.AveRhoVisOverlap(iBackElem);
8350 26970 : FFTot.sunDisk += firstFlux.sunDisk;
8351 26970 : FLTot.sunDisk += fl.sunDisk;
8352 : }
8353 :
8354 186 : auto const &thisEnclDaylight = dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex);
8355 186 : Real64 EnclInsideSurfArea = thisEnclDaylight.totInsSurfArea;
8356 :
8357 186 : auto &eintsk = dl->reflIllum(IHR)[iWinCover_Bare];
8358 930 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8359 744 : eintsk.sky[iSky] = FFTot.sky[iSky] * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8360 : } // for (iSky)
8361 :
8362 186 : dl->reflIllum(IHR)[iWinCover_Bare].sun =
8363 186 : FFTot.sun * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8364 186 : dl->reflIllum(IHR)[iWinCover_Bare].sunDisk =
8365 186 : FFTot.sunDisk * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8366 :
8367 186 : if (allocated(FL)) {
8368 186 : FL.deallocate();
8369 : }
8370 : // if (allocated(FLSU)) FLSU.deallocate();
8371 : // if (allocated(FLSUdisk)) FLSUdisk.deallocate();
8372 :
8373 186 : if (allocated(FirstFlux)) {
8374 186 : FirstFlux.deallocate();
8375 : }
8376 : // if (allocated(FirstFluxSU)) FirstFluxSU.deallocate();
8377 : // if (allocated(FirstFluxSUdisk)) FirstFluxSUdisk.deallocate();
8378 :
8379 186 : if (allocated(ElementLuminance)) {
8380 186 : ElementLuminance.deallocate();
8381 : }
8382 : // if (allocated(ElementLuminanceSun)) ElementLuminanceSun.deallocate();
8383 : // if (allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.deallocate();
8384 186 : }
8385 :
8386 1488 : void DayltgDirectIllumComplexFenestration(EnergyPlusData &state,
8387 : int const IWin, // Window index
8388 : int const WinEl, // Current window element counter
8389 : int const IHR, // Hour of day
8390 : int const iRefPoint, // reference point index
8391 : CalledFor const CalledFrom,
8392 : int const MapNum)
8393 : {
8394 :
8395 : // SUBROUTINE INFORMATION:
8396 : // AUTHOR Simon Vidanovic
8397 : // DATE WRITTEN June 2013
8398 :
8399 1488 : auto &dl = state.dataDayltg;
8400 1488 : auto &s_surf = state.dataSurface;
8401 :
8402 : // Luminances from different sources to the window
8403 1488 : Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
8404 : // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side),
8405 : // exluding beam
8406 : // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side),
8407 : // due to sun beam
8408 :
8409 : int RefPointIndex; // reference point patch number
8410 :
8411 : Real64 dirTrans; // directional BSDF transmittance
8412 : Real64 dOmega; // solid view angle of current element
8413 : Real64 zProjection; // z-axe projection of solid view angle (used to calculate amount of light at horizontal surface
8414 : // laying at reference point)
8415 :
8416 1488 : int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
8417 1488 : auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
8418 1488 : int iConst = s_surf->SurfaceWindow(IWin).ComplexFen.State(CurCplxFenState).Konst;
8419 1488 : int NIncBasis = complexWin.Geom(CurCplxFenState).Inc.NBasis;
8420 :
8421 1488 : if (!allocated(ElementLuminance)) {
8422 1488 : ElementLuminance.allocate(NIncBasis);
8423 : }
8424 5952 : ElementLuminance = Illums();
8425 : // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
8426 : // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
8427 :
8428 1488 : ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
8429 :
8430 : // find number of outgoing basis towards current reference point
8431 1488 : if (CalledFrom == CalledFor::RefPoint) {
8432 1488 : RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).RefPoint(iRefPoint).RefPointIndex(WinEl);
8433 1488 : dOmega = complexWin.RefPoint(iRefPoint).SolidAngle(WinEl);
8434 1488 : zProjection = complexWin.RefPoint(iRefPoint).SolidAngleVec(WinEl).z;
8435 0 : } else if (CalledFrom == CalledFor::MapPoint) {
8436 0 : assert(MapNum > 0);
8437 0 : RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).IlluminanceMap(iRefPoint, MapNum).RefPointIndex(WinEl);
8438 0 : dOmega = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngle(WinEl);
8439 0 : zProjection = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngleVec(WinEl).z;
8440 : }
8441 :
8442 1488 : Illums WinLum = Illums();
8443 1488 : Illums EDir = Illums();
8444 :
8445 217248 : for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
8446 : // LambdaInc = ComplexWind(IWin)%Geom(CurCplxFenState)%Inc%Lamda(iIncElem)
8447 215760 : dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(RefPointIndex, iIncElem);
8448 :
8449 215760 : auto const &elemLum = ElementLuminance(iIncElem);
8450 1078800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8451 863040 : WinLum.sky[iSky] += dirTrans * elemLum.sky[iSky];
8452 : }
8453 :
8454 215760 : WinLum.sun += dirTrans * elemLum.sun;
8455 :
8456 : // For sun disk need to go throug outgoing directions and see which directions actually contain reference point
8457 : }
8458 :
8459 1488 : if (zProjection > 0.0) {
8460 5580 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8461 4464 : EDir.sky[iSky] = WinLum.sky[iSky] * dOmega * zProjection;
8462 : }
8463 1116 : EDir.sun = WinLum.sun * dOmega * zProjection;
8464 : }
8465 :
8466 : // Store solution in global variables
8467 1488 : auto &avwlsk = dl->avgWinLum(IHR)[iWinCover_Bare];
8468 1488 : auto &edirsk = dl->dirIllum(IHR)[iWinCover_Bare];
8469 :
8470 7440 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8471 5952 : avwlsk.sky[iSky] += WinLum.sky[iSky];
8472 5952 : edirsk.sky[iSky] += EDir.sky[iSky];
8473 : }
8474 :
8475 1488 : dl->avgWinLum(IHR)[iWinCover_Bare].sun += WinLum.sun;
8476 1488 : dl->dirIllum(IHR)[iWinCover_Bare].sun += EDir.sun;
8477 : // AVWLSUdisk(1,IHR) = AVWLSUdisk(1,IHR) + WinLumSUdisk
8478 1488 : } // DayltgDirectIllumComplexFenestration()
8479 :
8480 186 : void DayltgDirectSunDiskComplexFenestration(EnergyPlusData &state,
8481 : int const iWin, // Window index
8482 : int const iHour, // Hour of day
8483 : int const iRefPoint,
8484 : int const NumEl, // Total number of window elements
8485 : Real64 const AZVIEW, // Azimuth of view vector in absolute coord system for
8486 : CalledFor const CalledFrom, // indicate which type of routine called this routine
8487 : int const MapNum)
8488 : {
8489 :
8490 : // SUBROUTINE INFORMATION:
8491 : // AUTHOR Simon Vidanovic
8492 : // DATE WRITTEN June 2013
8493 :
8494 : // PURPOSE OF THIS SUBROUTINE:
8495 : // Calculate illuminance from sun disk for complex fenestration systems
8496 :
8497 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8498 186 : auto &dl = state.dataDayltg;
8499 186 : auto &s_surf = state.dataSurface;
8500 :
8501 186 : assert(CalledFrom != CalledFor::MapPoint || MapNum > 0);
8502 :
8503 186 : auto const &window = s_surf->SurfaceWindow(iWin);
8504 186 : int CurCplxFenState = window.ComplexFen.CurrentState;
8505 186 : int iConst = window.ComplexFen.State(CurCplxFenState).Konst;
8506 :
8507 186 : auto const &complexWindow = state.dataBSDFWindow->ComplexWind(iWin);
8508 186 : auto const &complexWindowGeom = complexWindow.Geom(CurCplxFenState);
8509 186 : auto const &complexWindowDayltgGeom = complexWindow.DaylghtGeom(CurCplxFenState);
8510 186 : int SolBmIndex = complexWindowGeom.SolBmIndex(iHour, state.dataGlobal->TimeStep);
8511 :
8512 186 : Real64 WindowSolidAngleDaylightPoint = (CalledFrom == CalledFor::RefPoint) ? window.refPts(iRefPoint).solidAngWtd : 0.0;
8513 186 : if (WindowSolidAngleDaylightPoint < 1e-6) {
8514 186 : return;
8515 : }
8516 :
8517 0 : Illums WinLum;
8518 0 : Illums ElemLum;
8519 :
8520 0 : int NTrnBasis = complexWindowGeom.Trn.NBasis;
8521 0 : for (int iTrnElem = 1; iTrnElem <= NTrnBasis; ++iTrnElem) {
8522 : // if ray from any part of the window can reach reference point
8523 : int refPointIntersect = (CalledFrom == CalledFor::RefPoint)
8524 0 : ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPointIntersection(iTrnElem)
8525 0 : : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPointIntersection(iTrnElem);
8526 :
8527 0 : if (refPointIntersect == 0) {
8528 0 : continue;
8529 : }
8530 :
8531 0 : Real64 PosFac = (CalledFrom == CalledFor::RefPoint) ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPtIntPosFac(iTrnElem)
8532 0 : : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPtIntPosFac(iTrnElem);
8533 :
8534 0 : Real64 RayZ = -complexWindowGeom.sTrn(iTrnElem).z;
8535 :
8536 : // Need to recalculate position factor for dominant direction in case of specular bsdf. Otherwise this will produce
8537 : // very inaccurate results because of position factor of the sun and bsdf pach can vary by lot
8538 0 : if (iTrnElem == SolBmIndex) {
8539 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
8540 0 : Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
8541 0 : PosFac = DayltgGlarePositionFactor(XR, YR);
8542 0 : RayZ = dl->sunAngles.sinPhi;
8543 : }
8544 :
8545 0 : if (PosFac == 0.0) {
8546 0 : continue;
8547 : }
8548 :
8549 0 : Real64 dirTrans = (SolBmIndex > 0) ? state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iTrnElem, SolBmIndex) : 0.0;
8550 0 : Real64 LambdaTrn = complexWindowGeom.Trn.Lamda(iTrnElem);
8551 0 : Vector3<Real64> V = -complexWindowGeom.sTrn(iTrnElem);
8552 0 : Vector3<Real64> RWin = s_surf->Surface(iWin).Centroid;
8553 0 : Real64 TransBeam = DayltgHitObstruction(state, iHour, iWin, RWin, V);
8554 :
8555 0 : WinLum.sunDisk += (14700.0 * std::sqrt(0.000068 * PosFac) * double(NumEl) / std::pow(WindowSolidAngleDaylightPoint, 0.8)) * dirTrans *
8556 0 : LambdaTrn * TransBeam;
8557 :
8558 0 : ElemLum.sunDisk += RayZ * dirTrans * LambdaTrn * TransBeam;
8559 0 : } // for (iTrnElem)
8560 :
8561 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = WinLum.sunDisk;
8562 0 : dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = ElemLum.sunDisk;
8563 : }
8564 :
8565 257402164 : Real64 DayltgSkyLuminance(EnergyPlusData const &state,
8566 : SkyType sky, // Sky type: 1=clear, 2=clear turbid, 3=intermediate, 4=overcast
8567 : Real64 const THSKY, // Azimuth and altitude of sky element (radians)
8568 : Real64 const PHSKY)
8569 : {
8570 :
8571 : // SUBROUTINE INFORMATION:
8572 : // AUTHOR Fred Winkelmann
8573 : // DATE WRITTEN July 1997
8574 :
8575 : // PURPOSE OF THIS SUBROUTINE:
8576 : // Called by CalcDayltgCoefficients, DayltgExtHorizIllum AND DayltgInterReflectedIllum. gives
8577 : // luminance in cd/m2 for four different sky types, as described in R.Perez, P.Ineichen,
8578 : // R.Seals, J.Michalsky and R.Stewart, "Modeling daylight availability and irradiance
8579 : // components from direct and global irradiance," Solar Energy 44, 1990, 271-289.
8580 : // The luminance distributions in this routine are normalized such that
8581 : // the zenith luminance is 1.0, i.e., DayltgSkyLuminance =
8582 : // (sky luminance at THSKY, PHSKY)/(zenith luminance), which is dimensionless.
8583 : // The sky types are:
8584 : // 1. Standard CIE clear sky
8585 : // 2. Standard CIE high-turbidity clear sky
8586 : // 3. CIE intermediate sky
8587 : // 4. CIE overcast sky
8588 :
8589 : // METHODOLOGY EMPLOYED:
8590 :
8591 : // REFERENCES:
8592 : // Based on DOE-2.1E subroutine DSKYLU, which did only clear and overcast skies.
8593 :
8594 : // OTHER NOTES:
8595 : // THSKY ranges from 0 to 2Pi starting with 0 directly East and rotating clockwise.
8596 : // PHSKY ranges from 0 to Pi starting with 0 at the horizon and Pi/2 at the zenith.
8597 :
8598 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
8599 257402164 : auto const &dl = state.dataDayltg;
8600 :
8601 257402164 : Real64 G = 0.0; // Angle between sun and element of sky (radians)
8602 257402164 : Real64 COSG = 0.0; // Cosine of G
8603 :
8604 257402164 : Real64 SPHSKY = max(std::sin(PHSKY), 0.01); // Prevent floating point underflows
8605 257402164 : Real64 Z = Constant::PiOvr2 - dl->sunAngles.phi;
8606 257402164 : if (sky != SkyType::Overcast) { // Following not needed for overcast sky
8607 193051623 : COSG = SPHSKY * dl->sunAngles.sinPhi + std::cos(PHSKY) * dl->sunAngles.cosPhi * std::cos(THSKY - dl->sunAngles.theta);
8608 193051623 : COSG = max(DataPrecisionGlobals::constant_minusone, min(COSG, 1.0)); // Prevent out of range due to roundoff
8609 193051623 : G = std::acos(COSG);
8610 : }
8611 :
8612 257402164 : switch (sky) {
8613 64350541 : case SkyType::Clear: {
8614 64350541 : Real64 Z1 = 0.910 + 10.0 * std::exp(-3.0 * G) + 0.45 * COSG * COSG;
8615 64350541 : Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
8616 64350541 : Real64 Z3 = 0.27385 * (0.91 + 10.0 * std::exp(-3.0 * Z) + 0.45 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
8617 64350541 : return Z1 * Z2 / Z3;
8618 :
8619 : } break;
8620 64350541 : case SkyType::ClearTurbid: {
8621 64350541 : Real64 Z1 = 0.856 + 16.0 * std::exp(-3.0 * G) + 0.3 * COSG * COSG;
8622 64350541 : Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
8623 64350541 : Real64 Z3 = 0.27385 * (0.856 + 16.0 * std::exp(-3.0 * Z) + 0.3 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
8624 64350541 : return Z1 * Z2 / Z3;
8625 :
8626 : } break;
8627 :
8628 64350541 : case SkyType::Intermediate: {
8629 64350541 : Real64 Z1 = (1.35 * (std::sin(3.59 * PHSKY - 0.009) + 2.31) * std::sin(2.6 * dl->sunAngles.phi + 0.316) + PHSKY + 4.799) / 2.326;
8630 64350541 : Real64 Z2 = std::exp(-G * 0.563 * ((dl->sunAngles.phi - 0.008) * (PHSKY + 1.059) + 0.812));
8631 64350541 : Real64 Z3 = 0.99224 * std::sin(2.6 * dl->sunAngles.phi + 0.316) + 2.73852;
8632 64350541 : Real64 Z4 = std::exp(-Z * 0.563 * ((dl->sunAngles.phi - 0.008) * 2.6298 + 0.812));
8633 64350541 : return Z1 * Z2 / (Z3 * Z4);
8634 : } break;
8635 64350541 : case SkyType::Overcast: {
8636 64350541 : return (1.0 + 2.0 * SPHSKY) / 3.0;
8637 : } break;
8638 0 : default:
8639 0 : assert(false);
8640 : return 0.0;
8641 : }
8642 : }
8643 :
8644 76902 : Real64 ProfileAngle(EnergyPlusData &state,
8645 : int const SurfNum, // Surface number
8646 : Vector3<Real64> const &CosDirSun, // Solar direction cosines
8647 : DataWindowEquivalentLayer::Orientation const HorOrVert // If HORIZONTAL, calculates ProfileAngHor
8648 : )
8649 : {
8650 :
8651 : // SUBROUTINE INFORMATION:
8652 : // AUTHOR Fred Winkelmann
8653 : // DATE WRITTEN May 2001
8654 :
8655 : // PURPOSE OF THIS SUBROUTINE:
8656 : // Calculates profile angle for a surface.
8657 :
8658 : // Locals
8659 : // SUBROUTINE ARGUMENT DEFINITIONS:
8660 : // For HorOrVert = HORIZONTAL,
8661 : // this is the incidence angle in a plane that is normal to the window
8662 : // and parallel to the Y-axis of the window (the axis along
8663 : // which the height of the window is measured).
8664 : // For HorOrVert = VERTICAL,
8665 : // this is the incidence angle in a plane that is normal to the window
8666 : // and parallel to the X-axis of the window (the axis along
8667 : // which the width of the window is measured).
8668 : // If VERTICAL, calculates ProfileAngVert
8669 76902 : auto &s_surf = state.dataSurface;
8670 :
8671 76902 : auto const &surf = s_surf->Surface(SurfNum);
8672 76902 : if (HorOrVert == DataWindowEquivalentLayer::Orientation::Horizontal) { // Profile angle for horizontal structures
8673 76902 : Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad; // Window elevation: angle between outward normal and horizontal (radians)
8674 76902 : Real64 AzimWin = (90.0 - surf.Azimuth) * Constant::DegToRad; // Window azimuth (radians)
8675 76902 : Real64 ElevSun = std::asin(CosDirSun.z); // Sun elevation; angle between sun and horizontal (radians)
8676 76902 : Real64 AzimSun = std::atan2(CosDirSun.y, CosDirSun.x); // Sun azimuth (radians)
8677 76902 : return std::atan(std::sin(ElevSun) / std::abs(std::cos(ElevSun) * std::cos(AzimWin - AzimSun))) - ElevWin;
8678 : } else { // Profile angle for vertical structures
8679 0 : Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad;
8680 0 : Real64 AzimWin = surf.Azimuth * Constant::DegToRad; // 7952
8681 0 : Real64 AzimSun = std::atan2(CosDirSun.x, CosDirSun.y); // 7952
8682 :
8683 : Real64 ProfileAng;
8684 0 : if (std::abs(ElevWin) < 0.1) { // Near-vertical window
8685 0 : ProfileAng = AzimWin - AzimSun; // CR7952 allow sign changes.
8686 : } else {
8687 0 : Vector3<Real64> WinNorm = surf.OutNormVec; // Window outward normal unit vector
8688 0 : Real64 ThWin = AzimWin - Constant::PiOvr2;
8689 0 : Real64 const sin_ElevWin = std::sin(ElevWin);
8690 : // Cross product of WinNorm and vector along window baseline
8691 0 : Vector3<Real64> WinNormCrossBase = {-sin_ElevWin * std::cos(ThWin), sin_ElevWin * std::sin(ThWin), std::cos(ElevWin)};
8692 : // Projection of sun vector onto plane (perpendicular to window plane) determined
8693 : // by WinNorm and vector along baseline of window
8694 0 : Vector3<Real64> SunPrime = CosDirSun - WinNormCrossBase * dot(CosDirSun, WinNormCrossBase);
8695 0 : ProfileAng = std::abs(std::acos(dot(WinNorm, SunPrime) / SunPrime.magnitude()));
8696 : // CR7952 correct sign of result for vertical slats
8697 0 : if ((AzimWin - AzimSun) < 0.0) {
8698 0 : ProfileAng = -1.0 * ProfileAng;
8699 : }
8700 0 : }
8701 : // Constrain to 0 to pi
8702 0 : if (ProfileAng > Constant::Pi) {
8703 0 : ProfileAng = 2.0 * Constant::Pi - ProfileAng;
8704 : }
8705 0 : return ProfileAng;
8706 : }
8707 : }
8708 :
8709 36320 : void DayltgClosestObstruction(EnergyPlusData &state,
8710 : Vector3<Real64> const &RecPt, // Point on window from which ray emanates (m)
8711 : Vector3<Real64> const &RayVec, // Unit vector along ray pointing away from window (m)
8712 : int &NearestHitSurfNum, // Surface number of nearest obstruction that is hit by ray;
8713 : Vector3<Real64> &NearestHitPt // Ray's hit point on nearest obstruction (m)
8714 : )
8715 : {
8716 :
8717 : // SUBROUTINE INFORMATION:
8718 : // AUTHOR Fred Winkelmann
8719 : // DATE WRITTEN November 2003
8720 :
8721 : // PURPOSE OF THIS SUBROUTINE:
8722 : // Determines surface number and hit point of closest exterior obstruction hit
8723 : // by a ray from a window. If no obstruction is hit, NearestHitSurfNum = 0.
8724 :
8725 : // Locals
8726 : // SUBROUTINE ARGUMENT DEFINITIONS:
8727 : // = 0 if no obstruction is hit.
8728 36320 : auto &s_surf = state.dataSurface;
8729 :
8730 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8731 36320 : Vector3<Real64> HitPt; // Hit point on an obstruction (m)
8732 : bool hit; // True iff obstruction is hit
8733 :
8734 36320 : NearestHitSurfNum = 0;
8735 36320 : Real64 NearestHitDistance_sq(std::numeric_limits<Real64>::max()); // Distance squared from receiving point to nearest hit point for a ray (m^2)
8736 36320 : NearestHitPt = 0.0;
8737 36320 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
8738 :
8739 181600 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
8740 : // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
8741 145280 : hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
8742 145280 : if (!hit) { // Ray pierces surface
8743 108960 : continue;
8744 : }
8745 :
8746 : // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
8747 : // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
8748 36320 : if ((s_surf->Surface(ObsSurfNum).Class == SurfaceClass::Window) && (s_surf->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum)) {
8749 0 : NearestHitSurfNum = ObsSurfNum;
8750 : } else {
8751 : // Distance squared from receiving point to hit point
8752 36320 : Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
8753 : // Reset NearestHitSurfNum and NearestHitDistance_sq if this hit point is closer than previous closest
8754 36320 : if (HitDistance_sq < NearestHitDistance_sq) {
8755 36320 : NearestHitDistance_sq = HitDistance_sq;
8756 36320 : NearestHitSurfNum = ObsSurfNum;
8757 36320 : NearestHitPt = HitPt;
8758 : }
8759 : } // End of check if obstruction was hit
8760 36320 : } // for (ObsSurfNum)
8761 :
8762 : } else { // Surface octree search
8763 :
8764 0 : SurfaceData const *nearestHitSurface(nullptr);
8765 :
8766 : // Lambda function for the octree to test for surface hit
8767 0 : auto surfaceHit = [&s_surf, &RecPt, &RayVec, &hit, &NearestHitDistance_sq, &nearestHitSurface, &NearestHitPt](SurfaceData const &surface) {
8768 0 : if (surface.IsShadowPossibleObstruction) {
8769 0 : Vector3<Real64> HitPt;
8770 : // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
8771 0 : hit = PierceSurface(surface, RecPt, RayVec, HitPt); // Check if ray pierces surface
8772 0 : if (!hit) {
8773 0 : return;
8774 : }
8775 :
8776 : // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
8777 : // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
8778 0 : if ((surface.Class == SurfaceClass::Window) && (surface.BaseSurf > 0) && (&s_surf->Surface(surface.BaseSurf) == nearestHitSurface)) {
8779 0 : nearestHitSurface = &surface;
8780 : } else {
8781 : // Distance squared from receiving point to hit point
8782 0 : Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
8783 : // Reset nearestHitSurface and NearestHitDistance_sq if this hit point is closer than previous closest
8784 0 : if (HitDistance_sq < NearestHitDistance_sq) {
8785 0 : NearestHitDistance_sq = HitDistance_sq;
8786 0 : nearestHitSurface = &surface;
8787 0 : NearestHitPt = HitPt;
8788 : }
8789 : } // End of check if obstruction was hit
8790 0 : }
8791 0 : };
8792 :
8793 : // Process octree surface candidates
8794 0 : Vector3<Real64> const RayVec_inv(SurfaceOctreeCube::safe_inverse(RayVec));
8795 0 : state.dataHeatBalMgr->surfaceOctree.processSurfaceRayIntersectsCube(RecPt, RayVec, RayVec_inv, surfaceHit);
8796 0 : if (nearestHitSurface != nullptr) { // Find surface number: This is inefficient: Improve when surfaces know their own number
8797 0 : for (int i = 1; i <= s_surf->TotSurfaces; ++i) {
8798 0 : if (&s_surf->Surface(i) == nearestHitSurface) {
8799 0 : NearestHitSurfNum = i;
8800 0 : break;
8801 : }
8802 : }
8803 0 : assert(NearestHitSurfNum != 0);
8804 : }
8805 0 : }
8806 36320 : } // DayltgClosestObstruction()
8807 :
8808 36320 : Real64 DayltgSurfaceLumFromSun(EnergyPlusData &state,
8809 : int const IHR, // Hour number
8810 : Vector3<Real64> const &Ray, // Ray from window to reflecting surface (m)
8811 : int const ReflSurfNum, // Number of surface for which luminance is being calculated
8812 : Vector3<Real64> const &ReflHitPt // Point on ReflSurfNum for luminance calculation (m)
8813 : )
8814 : {
8815 :
8816 : // SUBROUTINE INFORMATION:
8817 : // AUTHOR Fred Winkelmann
8818 : // DATE WRITTEN November 2003
8819 :
8820 : // PURPOSE OF THIS SUBROUTINE:
8821 : // Calculates exterior surface luminance due to beam solar diffuse reflection.
8822 :
8823 : // Locals
8824 : // SUBROUTINE ARGUMENT DEFINITIONS:
8825 : // beam normal illuminance (cd/m2)
8826 :
8827 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8828 36320 : Vector3<Real64> SurfaceLumFromSunReflNorm; // Unit normal to reflecting surface (m)
8829 36320 : Vector3<Real64> SurfaceLumFromSunObsHitPt; // Hit point on obstruction (m)
8830 : bool hitObs; // True iff obstruction is hit
8831 : Real64 DiffVisRefl; // Diffuse visible reflectance of ReflSurfNum
8832 :
8833 36320 : auto &s_surf = state.dataSurface;
8834 : // Skip daylighting shelves since reflection from these is separately calculated
8835 36320 : if (s_surf->SurfDaylightingShelfInd(ReflSurfNum) > 0) {
8836 0 : return 0.0;
8837 : }
8838 :
8839 36320 : auto const &reflSurf = s_surf->Surface(ReflSurfNum);
8840 :
8841 : // Normal to reflecting surface in hemisphere containing window element
8842 36320 : SurfaceLumFromSunReflNorm = reflSurf.OutNormVec;
8843 36320 : if (reflSurf.IsShadowing) {
8844 36320 : if (dot(SurfaceLumFromSunReflNorm, Ray) > 0.0) {
8845 36320 : SurfaceLumFromSunReflNorm *= -1.0;
8846 : }
8847 : }
8848 : // Cosine of angle of incidence of sun at HitPt if sun were to reach HitPt
8849 36320 : Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
8850 36320 : Real64 CosIncAngAtHitPt = dot(SurfaceLumFromSunReflNorm, SUNCOS_IHR);
8851 : // Require that the sun be in front of this surface relative to window element
8852 36320 : if (CosIncAngAtHitPt <= 0.0) {
8853 36320 : return 0.0; // Sun is in back of reflecting surface
8854 : }
8855 : // Sun reaches ReflHitPt if vector from ReflHitPt to sun is unobstructed
8856 0 : hitObs = false;
8857 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
8858 : // Exclude as a possible obstructor ReflSurfNum and its base surface (if it has one)
8859 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == reflSurf.BaseSurf) {
8860 0 : continue;
8861 : }
8862 0 : hitObs = PierceSurface(state, ObsSurfNum, ReflHitPt, SUNCOS_IHR, SurfaceLumFromSunObsHitPt);
8863 0 : if (hitObs) {
8864 0 : break;
8865 : }
8866 0 : }
8867 :
8868 0 : if (hitObs) {
8869 0 : return 0.0; // Obstruction was hit, blocking s auto surfaceHit = [&state, &GroundHitPtun
8870 : }
8871 : // Obstruction was not hit; sun reaches ReflHitPt.
8872 : // Calculate luminance at ReflHitPt due to beam solar reflection (for unit beam normal illuminance)
8873 0 : if (reflSurf.IsShadowing) {
8874 0 : DiffVisRefl = s_surf->SurfShadowDiffuseVisRefl(ReflSurfNum);
8875 : // Note that if the shadowing surface has a non-zero glazing fraction (e.g., neighboring bldg) that the above is
8876 : // (1 - glazing fraction) * (vis refl of opaque part of shadowing surface); specular reflection is
8877 : // excluded in this value of DiffVisRefl.
8878 : } else { // Exterior building surface
8879 0 : if (!state.dataConstruction->Construct(reflSurf.Construction).TypeIsWindow) {
8880 0 : DiffVisRefl = 1.0 - state.dataConstruction->Construct(reflSurf.Construction).OutsideAbsorpSolar;
8881 : } else {
8882 : // Window; assume bare so no beam-to-diffuse reflection
8883 0 : DiffVisRefl = 0.0;
8884 : }
8885 : }
8886 0 : return CosIncAngAtHitPt * DiffVisRefl / Constant::Pi;
8887 36320 : }
8888 :
8889 996072 : void DayltgInteriorMapIllum(EnergyPlusData &state)
8890 : {
8891 :
8892 : // *****super modified version of DayltgInteriorIllum by Peter Graham Ellis
8893 : // *****removes all control code, just calculates illum with previously determined control settings
8894 : // *****this should be packaged into a subroutine called from 2 places
8895 :
8896 : // SUBROUTINE INFORMATION:
8897 : // AUTHOR Fred Winkelmann
8898 : // DATE WRITTEN July 1997
8899 : // MODIFIED March 2000, FW: interpolate clear-sky daylight factors using
8900 : // HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
8901 : // only HourOfDay was used
8902 : // Jan 2001, FW: interpolate in slat angle for windows with blinds
8903 : // that have movable slats
8904 : // Dec 2003, FW: fix bug--even though between-glass shade/blind is on
8905 : // daylight illum at ref pt was calculated as though it was off
8906 : // June 2009, TH: modified for thermochromic windows
8907 : // March 2010, TH: fix bug (CR 8057) for electrochromic windows
8908 : // RE-ENGINEERED na
8909 :
8910 : // PURPOSE OF THIS SUBROUTINE:
8911 : // Using daylighting factors and exterior illuminance, determine
8912 : // the current-hour interior daylight illuminance and glare index
8913 : // at each reference point in a space.
8914 :
8915 : // Called by InitSurfaceHeatBalance.
8916 :
8917 : // REFERENCES:
8918 : // Based on DOE-2.1E subroutine DINTIL.
8919 996072 : auto &dl = state.dataDayltg;
8920 :
8921 : // Locals
8922 996072 : Array1D<Real64> daylight_illum;
8923 :
8924 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8925 : int iSky1; // Sky type index values for averaging two sky types
8926 : int iSky2;
8927 : Real64 SkyWeight; // Weighting factor used to average two different sky types
8928 : Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
8929 : // luminous efficacy and horizontal illuminance from averaged sky
8930 :
8931 996072 : if (state.dataGlobal->WarmupFlag) {
8932 748588 : return;
8933 : }
8934 :
8935 247484 : auto &s_surf = state.dataSurface;
8936 :
8937 247484 : daylight_illum.allocate(MaxMapRefPoints);
8938 :
8939 : // Initialize reference point illuminance and window background luminance
8940 :
8941 249415 : for (auto &thisMap : dl->illumMaps) {
8942 1931 : int enclNum = thisMap.enclIndex;
8943 1931 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
8944 :
8945 1931 : int NREFPT = thisMap.TotalMapRefPoints; // Number of daylighting map reference points
8946 :
8947 1931 : daylight_illum = 0.0;
8948 :
8949 1931 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
8950 1419 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
8951 1419 : iSky1 = (int)SkyType::Clear;
8952 1419 : iSky2 = (int)SkyType::ClearTurbid;
8953 512 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
8954 198 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
8955 198 : iSky1 = (int)SkyType::ClearTurbid;
8956 198 : iSky2 = (int)SkyType::Intermediate;
8957 : } else { // Sky is average of intermediate and overcast
8958 314 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
8959 314 : iSky1 = (int)SkyType::Intermediate;
8960 314 : iSky2 = (int)SkyType::Overcast;
8961 : }
8962 :
8963 : // First loop over windows in this space.
8964 : // Find contribution of each window to the daylight illum
8965 : // and to the glare numerator at each reference point.
8966 : // Use shading flags set in WindowShadingManager.
8967 :
8968 1931 : auto &daylFacHrCurr = thisMap.daylFac[state.dataGlobal->HourOfDay];
8969 1931 : auto &daylFacHrPrev = thisMap.daylFac[state.dataGlobal->PreviousHour];
8970 :
8971 12439 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
8972 10508 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
8973 :
8974 : // Added TH 6/29/2009 for thermochromic windows
8975 10508 : Real64 VTRatio = 1.0;
8976 10508 : if (NREFPT > 0) {
8977 10508 : int IConst = s_surf->Surface(IWin).Construction;
8978 10508 : auto const &construction = state.dataConstruction->Construct(IConst);
8979 10508 : if (construction.isTCWindow) {
8980 : // For thermochromic windows, daylight and glare factors are always calculated
8981 : // based on the master construction. They need to be adjusted by the VTRatio, including:
8982 : // ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
8983 : // DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
8984 0 : Real64 VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
8985 0 : Real64 VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
8986 0 : VTRatio = VTNow / VTMaster;
8987 : }
8988 : }
8989 :
8990 10508 : Real64 wgtThisHr = state.dataGlobal->WeightNow;
8991 10508 : Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
8992 :
8993 10508 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
8994 :
8995 10508 : auto &dfhr = DFHR[iWinCover_Bare];
8996 10508 : auto &dfhrSh = DFHR[iWinCover_Shaded];
8997 :
8998 10508 : auto &surfShade = s_surf->surfShades(IWin);
8999 : // Loop over reference points
9000 993558 : for (int ILB = 1; ILB <= NREFPT; ++ILB) {
9001 : // if (ILB != 5) continue;
9002 983050 : auto const &illSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Bare];
9003 983050 : auto const &illSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Bare];
9004 983050 : auto const &illShSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Shaded];
9005 983050 : auto const &illShSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Shaded];
9006 :
9007 : // Daylight factors for current sun position
9008 4915250 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
9009 : // ===Bare window===
9010 3932200 : dfhr.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr.sky[iSky] + wgtPrevHr * illSkyPrev.sky[iSky]);
9011 :
9012 8526200 : if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
9013 4594000 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
9014 :
9015 : // ===Shaded window===
9016 : // Shade, screen, blind with fixed slats, or diffusing glass
9017 3270400 : dfhrSh.sky[iSky] = VTRatio * (wgtThisHr * illShSkyCurr.sky[iSky] + wgtPrevHr * illShSkyPrev.sky[iSky]);
9018 : } // End of check if window is shaded or has diffusing glass
9019 : } // for (iSky)
9020 :
9021 : // Sun daylight factor for bare/shaded window
9022 983050 : std::array<Illums, (int)DataSurfaces::WinCover::Num> tmpDFHR;
9023 1966100 : tmpDFHR[iWinCover_Bare].sun =
9024 983050 : VTRatio * (wgtThisHr * (illSkyCurr.sun + illSkyCurr.sunDisk) + wgtPrevHr * (illSkyPrev.sun + illSkyPrev.sunDisk));
9025 :
9026 2131550 : if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
9027 1148500 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
9028 :
9029 : // ===Shaded window===
9030 : // Shade, screen, blind with fixed slats, or diffusing glass
9031 817600 : tmpDFHR[iWinCover_Shaded].sun = VTRatio * (wgtThisHr * illShSkyCurr.sun + wgtPrevHr * illShSkyPrev.sun);
9032 :
9033 817600 : if (!surfShade.blind.slatBlockBeam) {
9034 817600 : tmpDFHR[iWinCover_Shaded].sun += VTRatio * (wgtThisHr * illShSkyCurr.sunDisk + wgtPrevHr * illShSkyPrev.sunDisk);
9035 : }
9036 : } // End of check if window is shaded or has diffusing glass
9037 :
9038 : // Get illuminance at ref point from bare and shaded window by
9039 : // multiplying daylight factors by exterior horizontal illuminance
9040 :
9041 : // Adding 0.001 in the following prevents zero DayltgInteriorMapIllumHorIllSky in early morning or late evening when sun
9042 : // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
9043 983050 : Illums tmpHorIll; // Horizontal illuminance for different sky types
9044 983050 : auto const &gilCurr = dl->horIllum[state.dataGlobal->HourOfDay];
9045 983050 : auto const &gilPrev = dl->horIllum[state.dataGlobal->PreviousHour];
9046 4915250 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
9047 3932200 : tmpHorIll.sky[iSky] = wgtThisHr * gilCurr.sky[iSky] + wgtPrevHr * gilPrev.sky[iSky] + 0.001;
9048 : }
9049 :
9050 : // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
9051 : // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
9052 : // also calculated in DayltgLuminousEfficacy.
9053 983050 : HorIllSkyFac = state.dataEnvrn->HISKF / ((1.0 - SkyWeight) * tmpHorIll.sky[iSky2] + SkyWeight * tmpHorIll.sky[iSky1]);
9054 :
9055 2783700 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
9056 1966100 : if (iWinCover == iWinCover_Shaded) {
9057 983050 : if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
9058 0 : break;
9059 : }
9060 983050 : if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) && !s_surf->SurfWinSolarDiffusing(IWin)) {
9061 165450 : break;
9062 : }
9063 : }
9064 1800650 : auto const &dfhr3 = DFHR[iWinCover];
9065 :
9066 3601300 : thisMap.refPts(ILB).winLums(loop)[iWinCover] = tmpDFHR[iWinCover].sun * state.dataEnvrn->HISUNF +
9067 1800650 : HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * tmpHorIll.sky[iSky1] +
9068 1800650 : dfhr3.sky[iSky2] * (1.0 - SkyWeight) * tmpHorIll.sky[iSky2]);
9069 : }
9070 :
9071 : } // End of reference point loop
9072 : } // End of first loop over windows
9073 :
9074 : // Second loop over windows. Find total daylight illuminance
9075 : // and background luminance for each ref pt from all windows in
9076 : // the space. Use shading flags.
9077 :
9078 12439 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
9079 10508 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
9080 10508 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
9081 :
9082 10508 : WinCover winCover = findWinShadingStatus(state, IWin);
9083 :
9084 : // CR 8057. 3/17/2010.
9085 : // Switchable windows may be in partially switched state rather than fully dark state
9086 10508 : Real64 VTMULT = 1.0;
9087 :
9088 10508 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
9089 10508 : if (s_surf->Surface(IWin).HasShadeControl) {
9090 8815 : if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
9091 0 : s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
9092 : // switchable windows in partial or fully switched state,
9093 : // get its intermediate VT calculated in DayltgInteriorIllum
9094 0 : int IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
9095 0 : if (IConstShaded > 0) {
9096 : // Visible transmittance (VT) of electrochromic (EC) windows in fully dark state
9097 0 : Real64 VTDark = Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
9098 0 : if (VTDark > 0) {
9099 0 : VTMULT = s_surf->SurfWinVisTransSelected(IWin) / VTDark;
9100 : }
9101 : }
9102 : }
9103 : }
9104 :
9105 993558 : for (int IL = 1; IL <= NREFPT; ++IL) {
9106 : // Determine if illuminance contribution is from bare or shaded window
9107 983050 : daylight_illum(IL) += VTMULT * thisMap.refPts(IL).winLums(loop)[(int)winCover];
9108 : }
9109 : } // End of second window loop
9110 :
9111 : // Variables for reporting
9112 161156 : for (int IL = 1; IL <= NREFPT; ++IL) {
9113 159225 : thisMap.refPts(IL).lums[iLum_Illum] = max(daylight_illum(IL), 0.0);
9114 : }
9115 : } // End loop over maps
9116 996072 : } // DayltgInteriorMapIllum()
9117 :
9118 1649 : void ReportIllumMap(EnergyPlusData &state, int const MapNum)
9119 : {
9120 :
9121 : // SUBROUTINE INFORMATION:
9122 : // AUTHOR Peter Ellis
9123 : // DATE WRITTEN May 2003
9124 :
9125 : // PURPOSE OF THIS SUBROUTINE:
9126 : // This subroutine produces the Daylighting Illuminance Map output. Each separate map (by zone)
9127 : // is placed on a temporary file and later (see CloseReportIllumMaps) coallesced into a single
9128 : // output file.
9129 :
9130 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
9131 :
9132 1649 : std::string MapNoString;
9133 1649 : auto &dl = state.dataDayltg;
9134 :
9135 1649 : if (dl->ReportIllumMap_firstTime) {
9136 6 : dl->ReportIllumMap_firstTime = false;
9137 6 : dl->FirstTimeMaps.dimension((int)dl->illumMaps.size(), true);
9138 6 : dl->EnvrnPrint.dimension((int)dl->illumMaps.size(), true);
9139 6 : dl->SavedMnDy.allocate((int)dl->illumMaps.size());
9140 : }
9141 :
9142 1649 : auto &illumMap = dl->illumMaps(MapNum);
9143 :
9144 1649 : if (dl->FirstTimeMaps(MapNum)) {
9145 :
9146 9 : dl->FirstTimeMaps(MapNum) = false;
9147 :
9148 9 : auto openMapFile = [&](const fs::path &filePath) -> InputOutputFile & {
9149 9 : auto &outputFile = *illumMap.mapFile;
9150 9 : outputFile.filePath = FileSystem::appendSuffixToPath(filePath, fmt::to_string(MapNum));
9151 9 : outputFile.ensure_open(state, "ReportIllumMap");
9152 9 : return outputFile;
9153 9 : };
9154 9 : if (dl->MapColSep == DataStringGlobals::CharTab) {
9155 0 : if (!openMapFile(state.files.outputMapTabFilePath).good()) {
9156 0 : return;
9157 : }
9158 : // CommaDelimited = false; //Unused Set but never used
9159 9 : } else if (dl->MapColSep == DataStringGlobals::CharComma) {
9160 9 : if (!openMapFile(state.files.outputMapCsvFilePath).good()) {
9161 0 : return;
9162 : }
9163 : // CommaDelimited = true; //Unused Set but never used
9164 : } else {
9165 0 : if (!openMapFile(state.files.outputMapTxtFilePath).good()) {
9166 0 : return;
9167 : }
9168 : // CommaDelimited = false; //Unused Set but never used
9169 : }
9170 :
9171 9 : dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
9172 :
9173 9 : illumMap.Name = format("{} at {:.2R}m", illumMap.Name, illumMap.Z);
9174 : }
9175 1649 : if (dl->SavedMnDy(MapNum) != state.dataEnvrn->CurMnDyHr.substr(0, 5)) {
9176 13 : dl->EnvrnPrint(MapNum) = true;
9177 13 : dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
9178 : }
9179 :
9180 1649 : illumMap.pointsHeader = "";
9181 1649 : int rCount = 0;
9182 3685 : for (auto &thisDayltgCtrl : dl->daylightControl) {
9183 2036 : if (thisDayltgCtrl.zoneIndex != illumMap.zoneIndex) {
9184 387 : continue;
9185 : }
9186 :
9187 3654 : for (int R = 1; R <= thisDayltgCtrl.TotalDaylRefPoints; ++R) {
9188 2005 : ++rCount;
9189 2005 : auto const &refPt = thisDayltgCtrl.refPts(R);
9190 2005 : illumMap.pointsHeader += format(" RefPt{}=({:.2R}:{:.2R}:{:.2R}),", rCount, refPt.absCoords.x, refPt.absCoords.y, refPt.absCoords.z);
9191 : }
9192 : }
9193 :
9194 1649 : if (rCount > 0) {
9195 : // Remove trailing comma
9196 1649 : illumMap.pointsHeader.pop_back();
9197 : }
9198 1649 : if (dl->EnvrnPrint(MapNum)) {
9199 22 : WriteDaylightMapTitle(
9200 22 : state, MapNum, *illumMap.mapFile, illumMap.Name, state.dataEnvrn->EnvironmentName, illumMap.zoneIndex, illumMap.pointsHeader, illumMap.Z);
9201 22 : dl->EnvrnPrint(MapNum) = false;
9202 : }
9203 :
9204 1649 : if (!state.dataGlobal->WarmupFlag) {
9205 1649 : if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) { // Report only hourly
9206 :
9207 304 : int linelen = 0;
9208 : // Write X scale column header
9209 304 : std::string mapLine = format(" {} {:02}:00", dl->SavedMnDy(MapNum), state.dataGlobal->HourOfDay);
9210 304 : if (illumMap.HeaderXLineLengthNeeded) {
9211 9 : linelen = int(len(mapLine));
9212 : }
9213 304 : int RefPt = 1;
9214 2944 : for (int X = 1; X <= illumMap.Xnum; ++X) {
9215 : const std::string AddXorYString =
9216 2640 : format("{}({:.2R};{:.2R})=", dl->MapColSep, illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
9217 2640 : if (illumMap.HeaderXLineLengthNeeded) {
9218 65 : linelen += int(len(AddXorYString));
9219 : }
9220 2640 : mapLine += AddXorYString;
9221 2640 : ++RefPt;
9222 2640 : } // X
9223 :
9224 304 : if (illumMap.HeaderXLineLengthNeeded) {
9225 9 : illumMap.HeaderXLineLength = linelen;
9226 9 : if (static_cast<std::string::size_type>(illumMap.HeaderXLineLength) > len(mapLine)) {
9227 0 : ShowWarningError(state,
9228 0 : format("ReportIllumMap: Map=\"{}\" -- the X Header overflows buffer -- will be truncated at {} characters.",
9229 0 : illumMap.Name,
9230 0 : int(len(mapLine))));
9231 0 : ShowContinueError(state, format("...needed {} characters. Please contact EnergyPlus support.", illumMap.HeaderXLineLength));
9232 : }
9233 9 : illumMap.HeaderXLineLengthNeeded = false;
9234 : }
9235 :
9236 304 : print(*illumMap.mapFile, "{}\n", mapLine);
9237 :
9238 : // Write Y scale prefix and illuminance values
9239 304 : RefPt = 1;
9240 3264 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
9241 2960 : mapLine = format("({:.2R};{:.2R})=", illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
9242 28960 : for (int R = RefPt; R <= RefPt + illumMap.Xnum - 1; ++R) {
9243 26000 : int IllumOut = nint(illumMap.refPts(R).lumsHr[iLum_Illum]);
9244 26000 : std::string String = fmt::to_string(IllumOut);
9245 : ;
9246 26000 : if (!illumMap.refPts(R).inBounds) {
9247 0 : String = "*" + String;
9248 : }
9249 26000 : mapLine += dl->MapColSep + String;
9250 26000 : }
9251 :
9252 2960 : print(*illumMap.mapFile, "{}\n", mapLine);
9253 :
9254 2960 : RefPt += illumMap.Xnum;
9255 : } // X
9256 :
9257 304 : if (state.dataSQLiteProcedures->sqlite) {
9258 16 : if (dl->SQFirstTime) {
9259 1 : int const nX(maxval(dl->illumMaps, &IllumMap::Xnum));
9260 1 : int const nY(maxval(dl->illumMaps, &IllumMap::Ynum));
9261 1 : dl->XValue.allocate(nX);
9262 1 : dl->YValue.allocate(nY);
9263 1 : dl->IllumValue.allocate(nX, nY);
9264 1 : dl->SQFirstTime = false;
9265 : }
9266 :
9267 176 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
9268 160 : dl->YValue(Y) = illumMap.Ymin + (Y - 1) * illumMap.Yinc;
9269 1760 : for (int X = 1; X <= illumMap.Xnum; ++X) {
9270 1600 : dl->XValue(X) = illumMap.Xmin + (X - 1) * illumMap.Xinc;
9271 1600 : int IllumIndex = X + (Y - 1) * illumMap.Xnum;
9272 1600 : dl->IllumValue(X, Y) = nint(illumMap.refPts(IllumIndex).lumsHr[iLum_Illum]);
9273 1600 : if (!illumMap.refPts(IllumIndex).inBounds) {
9274 0 : dl->IllumValue(X, Y) = -dl->IllumValue(X, Y);
9275 : }
9276 : } // X Loop
9277 : } // Y Loop
9278 :
9279 : // We need DataGlobals::CalendarYear, and not DataEnvironment::Year because
9280 : // otherwise if you run a TMY file, you'll get for eg 1977, 1981, etc
9281 32 : state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMap(MapNum,
9282 16 : state.dataGlobal->CalendarYear,
9283 16 : state.dataEnvrn->Month,
9284 16 : state.dataEnvrn->DayOfMonth,
9285 16 : state.dataGlobal->HourOfDay,
9286 : illumMap.Xnum,
9287 16 : dl->XValue,
9288 : illumMap.Ynum,
9289 16 : dl->YValue,
9290 16 : dl->IllumValue);
9291 :
9292 : } // WriteOutputToSQLite
9293 304 : } // end time step
9294 : } // not Warmup
9295 1649 : }
9296 :
9297 801 : void CloseReportIllumMaps(EnergyPlusData &state)
9298 : {
9299 :
9300 : // SUBROUTINE INFORMATION:
9301 : // AUTHOR Linda K. Lawrie
9302 : // DATE WRITTEN June 2003
9303 :
9304 : // PURPOSE OF THIS SUBROUTINE:
9305 : // This subroutine "closes" out the created daylight illuminance maps by merging them
9306 : // into the "eplusout.map" file.
9307 :
9308 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
9309 801 : auto &dl = state.dataDayltg;
9310 :
9311 801 : if ((int)dl->illumMaps.size() > 0) {
9312 : // Write map header
9313 6 : if (dl->MapColSep == DataStringGlobals::CharTab) {
9314 0 : state.files.map.filePath = state.files.outputMapTabFilePath;
9315 6 : } else if (dl->MapColSep == DataStringGlobals::CharComma) {
9316 6 : state.files.map.filePath = state.files.outputMapCsvFilePath;
9317 : } else {
9318 0 : state.files.map.filePath = state.files.outputMapTxtFilePath;
9319 : }
9320 :
9321 12 : state.files.map.ensure_open(state, "CloseReportIllumMaps");
9322 :
9323 15 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
9324 9 : auto &illumMap = dl->illumMaps(MapNum);
9325 9 : if (!illumMap.mapFile->good()) {
9326 0 : continue; // fatal error processing
9327 : }
9328 :
9329 9 : const std::vector<std::string> mapLines = illumMap.mapFile->getLines();
9330 9 : if (mapLines.empty()) {
9331 0 : ShowSevereError(state, format("CloseReportIllumMaps: IllumMap=\"{}\" is empty.", illumMap.Name));
9332 0 : break;
9333 : }
9334 3295 : for (const std::string &mapLine : mapLines) {
9335 3286 : print(state.files.map, "{}\n", mapLine);
9336 9 : }
9337 9 : illumMap.mapFile->del();
9338 9 : }
9339 :
9340 6 : if (!dl->mapResultsReported && !state.dataErrTracking->AbortProcessing) {
9341 0 : const std::string message = "CloseReportIllumMaps: Illuminance maps requested but no data ever reported. Likely cause is no solar.";
9342 0 : ShowSevereError(state, message);
9343 0 : print(state.files.map, "{}\n", message);
9344 0 : }
9345 : }
9346 801 : }
9347 :
9348 801 : void CloseDFSFile(EnergyPlusData &state)
9349 : {
9350 :
9351 : // SUBROUTINE INFORMATION:
9352 : // AUTHOR Linda Lawrie
9353 : // DATE WRITTEN August 2010
9354 :
9355 : // PURPOSE OF THIS SUBROUTINE:
9356 : // Make sure DFSFile is closed at exit time. Do not rely on operating system to
9357 : // take care of it.
9358 :
9359 801 : state.files.dfs.close();
9360 801 : }
9361 :
9362 65 : void DayltgSetupAdjZoneListsAndPointers(EnergyPlusData &state)
9363 : {
9364 :
9365 : // SUBROUTINE INFORMATION:
9366 : // AUTHOR Fred Winkelmann
9367 : // DATE WRITTEN Feb. 2004
9368 : // MODIFIED: June 2010;LKL - Merged two routines.
9369 :
9370 : // PURPOSE OF THIS SUBROUTINE:
9371 : // For each Daylighting:Detailed enclosure, creates a list of adjacent enclosures,
9372 : // that have one or more exterior windows and that share one or more interior
9373 : // windows with Z. Used in calculation of daylighting through interior windows.
9374 :
9375 : // Sets the daylighting factor pointers for each Daylighting:Detailed control. The pointer
9376 : // may be associated with an exterior window in a daylit target zone's enclosure or an exterior window in
9377 : // an adjacent enclosure, daylit or not, that shares interior windows with the target zone's enclosure.
9378 :
9379 : // Count number of exterior Windows (use to allocate arrays)
9380 65 : auto &dl = state.dataDayltg;
9381 65 : auto &s_surf = state.dataSurface;
9382 :
9383 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9384 851 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9385 851 : thisEnclDaylight.TotalExtWindows = 0;
9386 :
9387 : // Count exterior windows in this solar enclosure
9388 9345 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9389 8494 : auto const &surf = s_surf->Surface(surfNum);
9390 8494 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9391 7191 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9392 1303 : ++thisEnclDaylight.TotalExtWindows;
9393 : }
9394 : }
9395 : } // End of primary enclosure loop
9396 :
9397 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9398 851 : int NumList = 0;
9399 851 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
9400 514 : continue;
9401 : }
9402 337 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9403 337 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9404 3 : continue;
9405 : }
9406 : // This is a Daylighting:Detailed enclosure
9407 : // Find adjacent zones/enclosures
9408 13529 : for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
9409 13195 : if (adjEnclNum == enclNum) {
9410 334 : continue;
9411 : }
9412 : // Require that adjEnclNum have a least one exterior window
9413 12861 : bool AdjEnclHasExtWins = false;
9414 115022 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9415 120472 : if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
9416 9156 : (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
9417 9155 : AdjEnclHasExtWins = true;
9418 9155 : break;
9419 : }
9420 : }
9421 12861 : if (!AdjEnclHasExtWins) {
9422 3706 : continue;
9423 : }
9424 : // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to ZoneNum
9425 104153 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9426 94999 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9427 94999 : if ((surfAdj.Class == SurfaceClass::Window) && (surfAdj.ExtBoundCond >= 1)) {
9428 : // This is an interior window in ZoneNumAdj
9429 1 : if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex == enclNum) {
9430 : // This interior window is adjacent to ZoneNum
9431 1 : ++NumList;
9432 1 : break;
9433 : }
9434 : }
9435 : }
9436 : }
9437 334 : thisEnclDaylight.AdjIntWinEnclNums.allocate(NumList);
9438 334 : thisEnclDaylight.AdjIntWinEnclNums = 0;
9439 : } // End of primary enclosure loop
9440 :
9441 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9442 851 : int NumList = 0;
9443 851 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
9444 514 : continue;
9445 : }
9446 337 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9447 337 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9448 3 : continue;
9449 : }
9450 : // This is a Daylighting:Detailed enclosure
9451 : // Find adjacent zones/enclosures
9452 13529 : for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
9453 13195 : if (adjEnclNum == enclNum) {
9454 334 : continue;
9455 : }
9456 : // Require that adjEnclNum have a least one exterior window
9457 12861 : bool AdjEnclHasExtWins = false;
9458 115022 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9459 111316 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9460 111316 : if (surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) {
9461 9155 : AdjEnclHasExtWins = true;
9462 9155 : break;
9463 : }
9464 : }
9465 12861 : if (!AdjEnclHasExtWins) {
9466 3706 : continue;
9467 : }
9468 : // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to enclNum
9469 104153 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9470 94999 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9471 94999 : if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond < 1) {
9472 94998 : continue;
9473 : }
9474 :
9475 : // This is an interior window in adjEnclNum
9476 1 : if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex != enclNum) {
9477 0 : continue;
9478 : }
9479 :
9480 : // This interior window is adjacent to ZoneNum
9481 1 : ++NumList;
9482 1 : int enclNumAdj = surfAdj.SolarEnclIndex;
9483 1 : thisEnclDaylight.AdjIntWinEnclNums(NumList) = enclNumAdj;
9484 1 : dl->enclDaylight(enclNumAdj).adjEnclHasDayltgCtrl = true;
9485 1 : break;
9486 : }
9487 : }
9488 334 : thisEnclDaylight.NumOfIntWinAdjEncls = NumList;
9489 : } // End of primary enclosure loop
9490 :
9491 : // now fill out information on relationship between adjacent exterior windows and associated interior windows
9492 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9493 851 : auto &enclDayl = dl->enclDaylight(enclNum);
9494 : // first find count of exterior windows
9495 851 : if (enclDayl.NumOfIntWinAdjEncls <= 0) {
9496 850 : enclDayl.NumOfIntWinAdjEnclExtWins = 0;
9497 850 : continue;
9498 : }
9499 2 : for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
9500 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9501 10 : if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
9502 2 : (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
9503 1 : ++enclDayl.NumOfIntWinAdjEnclExtWins;
9504 : }
9505 : }
9506 : }
9507 : // now allocate nested struct based on exterior window count
9508 1 : enclDayl.IntWinAdjEnclExtWin.allocate(enclDayl.NumOfIntWinAdjEnclExtWins);
9509 :
9510 : // now fill nested structure
9511 1 : int ExtWinIndex = 0;
9512 2 : for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
9513 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9514 8 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9515 8 : if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond != ExternalEnvironment) {
9516 7 : continue;
9517 : }
9518 :
9519 1 : ++ExtWinIndex;
9520 1 : auto &intWinAdjEnclExtWin = enclDayl.IntWinAdjEnclExtWin(ExtWinIndex);
9521 1 : intWinAdjEnclExtWin.SurfNum = SurfNumAdj;
9522 :
9523 : // now count interior windows shared by both zones
9524 1 : int NumOfIntWindowsCount = 0;
9525 9 : for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9526 8 : auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
9527 8 : if ((surfAdj2.Class == SurfaceClass::Window) && (surfAdj2.ExtBoundCond >= 1)) {
9528 : // This is an interior window in ZoneNumAdj
9529 1 : if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
9530 : // This interior window is adjacent to ZoneNum and associated with this
9531 1 : ++NumOfIntWindowsCount;
9532 : }
9533 : }
9534 : } // for (SurfNumAdj2)
9535 :
9536 : // allocate nested array
9537 1 : intWinAdjEnclExtWin.IntWinNum.allocate(NumOfIntWindowsCount);
9538 1 : intWinAdjEnclExtWin.IntWinNum = 0;
9539 1 : int IntWinIndex = 0;
9540 9 : for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9541 8 : auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
9542 8 : if (surfAdj2.Class != SurfaceClass::Window || surfAdj2.ExtBoundCond < 1) {
9543 7 : continue;
9544 : }
9545 :
9546 : // This is an interior window in ZoneNumAdj
9547 1 : if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
9548 : // This interior window is adjacent to ZoneNum and associated with this
9549 1 : intWinAdjEnclExtWin.IntWinNum(++IntWinIndex) = SurfNumAdj2;
9550 : }
9551 : } // for (SurfNumAdj2)
9552 : } // for (SurfNumAdj)
9553 : } // for (adjEnclNum)
9554 : } // End of primary enclosure loop
9555 :
9556 65 : Array1D_int enclExtWin;
9557 65 : enclExtWin.dimension(state.dataViewFactor->NumOfSolarEnclosures, 0);
9558 :
9559 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9560 851 : enclExtWin(enclNum) = 0;
9561 851 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
9562 514 : continue;
9563 : }
9564 337 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9565 337 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9566 3 : continue;
9567 : }
9568 : // This is a Daylighting:Detailed zone
9569 :
9570 : // Get exterior windows in this solar enclosure
9571 3896 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9572 3562 : auto const &surf = s_surf->Surface(surfNum);
9573 3562 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9574 2653 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9575 909 : ++enclExtWin(enclNum);
9576 : }
9577 : }
9578 :
9579 : // Get exterior windows in adjacent enclosures that share interior windows with enclNum
9580 334 : if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) {
9581 333 : continue;
9582 : }
9583 :
9584 2 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9585 : // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
9586 : // it would not be an "AdjIntWinEncl"
9587 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9588 8 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9589 8 : if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
9590 7 : surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
9591 1 : ++enclExtWin(enclNum);
9592 : }
9593 : }
9594 : } // for (adjEnclNum)
9595 : } // for (enclNum)
9596 :
9597 65 : dl->maxShadeDeployOrderExtWins = 0;
9598 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9599 851 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9600 851 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9601 517 : continue;
9602 : }
9603 334 : thisEnclDaylight.NumOfDayltgExtWins = 0;
9604 334 : int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
9605 334 : if (thisEnclNumRefPoints > 0) {
9606 : // This is a Daylighting:Detailed enclosure
9607 :
9608 : // Get exterior windows in this enclosure
9609 334 : if (enclExtWin(enclNum) == 0) {
9610 0 : continue;
9611 : }
9612 334 : thisEnclDaylight.DayltgExtWinSurfNums.allocate(enclExtWin(enclNum));
9613 334 : thisEnclDaylight.DayltgExtWinSurfNums = 0;
9614 669 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9615 335 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9616 335 : thisDayltgCtrl.MapShdOrdToLoopNum.allocate(enclExtWin(enclNum));
9617 335 : thisDayltgCtrl.MapShdOrdToLoopNum = 0;
9618 :
9619 335 : assert((int)thisDayltgCtrl.refPts.size() == thisDayltgCtrl.TotalDaylRefPoints);
9620 846 : for (auto &refPt : thisDayltgCtrl.refPts) {
9621 511 : refPt.extWins.allocate(enclExtWin(enclNum));
9622 2065 : for (auto &extWin : refPt.extWins) {
9623 1554 : new (&extWin) DaylRefPtExtWin();
9624 : }
9625 : }
9626 334 : }
9627 :
9628 334 : int enclExtWinCtr = 0;
9629 :
9630 3896 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9631 3562 : auto const &surf = s_surf->Surface(surfNum);
9632 3562 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9633 2653 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9634 909 : ++enclExtWinCtr;
9635 909 : thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = surfNum;
9636 : }
9637 : }
9638 :
9639 : // Get exterior windows in adjacent enclosures that share interior windows with enclNum
9640 334 : if (thisEnclDaylight.NumOfIntWinAdjEncls > 0) {
9641 2 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9642 : // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
9643 : // it would not be an "AdjIntWinEncl"
9644 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9645 8 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9646 8 : if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
9647 7 : surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
9648 1 : ++enclExtWinCtr;
9649 1 : thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = SurfNumAdj;
9650 :
9651 1 : auto &surfWinAdj = s_surf->SurfaceWindow(SurfNumAdj);
9652 : // If no daylighting in the adjacent enclosure, set up variables anyway:
9653 2 : if (state.dataViewFactor->EnclSolInfo(adjEnclNum).TotalEnclosureDaylRefPoints == 0 &&
9654 1 : !s_surf->SurfWinSurfDayLightInit(SurfNumAdj)) {
9655 1 : surfWinAdj.refPts.allocate(thisEnclNumRefPoints);
9656 2 : for (auto &refPt : surfWinAdj.refPts) {
9657 1 : new (&refPt) SurfaceWindowRefPt();
9658 : }
9659 1 : s_surf->SurfWinSurfDayLightInit(SurfNumAdj) = true;
9660 : }
9661 : }
9662 : } // for (SurfNumAdj)
9663 : } // for (adjEnclNum)
9664 : } // if (thisEnclDaylight.NumOfIntWinAdjEncls > 0)
9665 :
9666 334 : thisEnclDaylight.NumOfDayltgExtWins = enclExtWin(enclNum);
9667 334 : int winSize = enclExtWin(enclNum);
9668 669 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9669 335 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9670 335 : int refSize = thisDayltgCtrl.TotalDaylRefPoints;
9671 8375 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
9672 8040 : thisDayltgCtrl.daylFac[iHr].allocate(winSize, refSize);
9673 : }
9674 334 : }
9675 : } // if (thisEncl.NumOfRefPoints > 0)
9676 :
9677 334 : if (s_surf->TotWinShadingControl > 0) {
9678 22 : CreateShadeDeploymentOrder(state, enclNum);
9679 : }
9680 : } // for (enclNum)
9681 :
9682 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9683 851 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9684 851 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9685 517 : continue;
9686 : }
9687 334 : int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
9688 334 : if (thisEnclNumRefPoints > 0) {
9689 334 : if (s_surf->TotWinShadingControl > 0) {
9690 22 : MapShadeDeploymentOrderToLoopNumber(state, enclNum);
9691 : }
9692 : }
9693 : }
9694 :
9695 74 : for (auto &illumMap : dl->illumMaps) {
9696 9 : assert((int)illumMap.refPts.size() == illumMap.TotalMapRefPoints);
9697 9 : if (illumMap.TotalMapRefPoints == 0) {
9698 0 : continue;
9699 : }
9700 :
9701 9 : int numExtWin = enclExtWin(illumMap.enclIndex);
9702 9 : if (numExtWin == 0) {
9703 0 : continue;
9704 : }
9705 :
9706 634 : for (auto &refPt : illumMap.refPts) {
9707 625 : refPt.winLums.allocate(numExtWin);
9708 2475 : for (auto &winLums : refPt.winLums) {
9709 1850 : winLums = {0.0, 0.0};
9710 : }
9711 : }
9712 :
9713 225 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
9714 216 : illumMap.daylFac[iHr].allocate(numExtWin, illumMap.TotalMapRefPoints);
9715 : }
9716 :
9717 : } // End of map loop
9718 :
9719 65 : dl->dirIllum.allocate(Constant::iHoursInDay);
9720 65 : dl->reflIllum.allocate(Constant::iHoursInDay);
9721 65 : dl->winLum.allocate(Constant::iHoursInDay);
9722 65 : dl->avgWinLum.allocate(Constant::iHoursInDay);
9723 :
9724 : static constexpr std::string_view Format_700("! <Enclosure/Window Adjacency Daylighting Counts>, Enclosure Name, Number of Exterior Windows, "
9725 : "Number of Exterior Windows in Adjacent Enclosures\n");
9726 65 : print(state.files.eio, Format_700);
9727 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9728 851 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9729 851 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9730 517 : continue;
9731 : }
9732 334 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
9733 0 : continue;
9734 : }
9735 : static constexpr std::string_view Format_701("Enclosure/Window Adjacency Daylighting Counts, {},{},{}\n");
9736 334 : print(state.files.eio,
9737 : Format_701,
9738 334 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
9739 334 : thisEnclDaylight.TotalExtWindows,
9740 668 : (thisEnclDaylight.NumOfDayltgExtWins - thisEnclDaylight.TotalExtWindows));
9741 : }
9742 : static constexpr std::string_view Format_702(
9743 : "! <Enclosure/Window Adjacency Daylighting Matrix>, Enclosure Name, Number of Adjacent Enclosures with Windows,Adjacent "
9744 : "Enclosure Names - 1st 100 (max)\n");
9745 65 : print(state.files.eio, Format_702);
9746 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9747 851 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9748 851 : if (!thisEnclDaylight.hasSplitFluxDaylighting) {
9749 517 : continue;
9750 : }
9751 334 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
9752 0 : continue;
9753 : }
9754 : static constexpr std::string_view Format_703("Enclosure/Window Adjacency Daylighting Matrix, {},{}");
9755 334 : print(state.files.eio, Format_703, state.dataViewFactor->EnclSolInfo(enclNum).Name, thisEnclDaylight.NumOfIntWinAdjEncls);
9756 335 : for (int loop = 1, loop_end = min(thisEnclDaylight.NumOfIntWinAdjEncls, 100); loop <= loop_end; ++loop) {
9757 1 : print(state.files.eio, ",{}", state.dataViewFactor->EnclSolInfo(thisEnclDaylight.AdjIntWinEnclNums(loop)).Name);
9758 : }
9759 334 : print(state.files.eio, "\n");
9760 : }
9761 :
9762 65 : enclExtWin.deallocate();
9763 65 : }
9764 :
9765 22 : void CreateShadeDeploymentOrder(EnergyPlusData &state, int const enclNum)
9766 : {
9767 : // J. Glazer - 2018
9768 : // create sorted list for shade deployment order
9769 : // first step is to create a sortable list of WindowShadingControl objects by sequence
9770 22 : auto &dl = state.dataDayltg;
9771 22 : auto &s_surf = state.dataSurface;
9772 :
9773 22 : std::vector<std::pair<int, int>> shadeControlSequence; // sequence, WindowShadingControl
9774 55 : for (int iShadeCtrl = 1; iShadeCtrl <= s_surf->TotWinShadingControl; ++iShadeCtrl) {
9775 33 : auto &winShadeControl = s_surf->WindowShadingControl(iShadeCtrl);
9776 41 : for (int spaceNum : state.dataHeatBal->Zone(winShadeControl.ZoneIndex).spaceIndexes) {
9777 33 : int shadeCtrlEnclNum = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
9778 33 : if (shadeCtrlEnclNum == enclNum) {
9779 25 : shadeControlSequence.push_back(std::make_pair(winShadeControl.SequenceNumber, iShadeCtrl));
9780 25 : break;
9781 : }
9782 33 : }
9783 : }
9784 : // sort the WindowShadingControl objects based on sequence number
9785 22 : sort(shadeControlSequence.begin(), shadeControlSequence.end());
9786 : // now make the deployment list of lists.
9787 : // each sublist is a group of surfaces that should be deployed together
9788 : // often the sublist is just a single item.
9789 22 : dl->maxShadeDeployOrderExtWins = 0;
9790 44 : for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
9791 22 : auto &thisDaylightCtrl = dl->daylightControl(controlNum);
9792 47 : for (auto sequence : shadeControlSequence) { // This is an iterator (THIS_AUTO_OK)
9793 25 : int curShadeControlNum = sequence.second;
9794 25 : auto const &winShadeControl = s_surf->WindowShadingControl(curShadeControlNum);
9795 25 : if (winShadeControl.multiSurfaceControl == MultiSurfaceControl::Group) {
9796 : // add a group of surfaces since they should be deployed as a group
9797 4 : std::vector<int> group;
9798 13 : for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
9799 9 : group.push_back(winShadeControl.FenestrationIndex(i));
9800 : }
9801 4 : thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(group);
9802 4 : } else {
9803 : // add each individual surface as a separate list so they are deployed individually
9804 46 : for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
9805 25 : std::vector<int> singleMemberVector;
9806 25 : singleMemberVector.push_back(winShadeControl.FenestrationIndex(i));
9807 25 : thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(singleMemberVector);
9808 25 : }
9809 : }
9810 22 : }
9811 22 : dl->maxShadeDeployOrderExtWins = max(dl->maxShadeDeployOrderExtWins, (int)thisDaylightCtrl.ShadeDeployOrderExtWins.size());
9812 22 : }
9813 :
9814 22 : dl->maxDayltgExtWins = 0;
9815 117 : for (auto const &enclDayl : dl->enclDaylight) {
9816 95 : dl->maxDayltgExtWins = max(dl->maxDayltgExtWins, enclDayl.NumOfDayltgExtWins);
9817 : }
9818 :
9819 22 : } // CreateShadeDeploymentOrder()
9820 :
9821 22 : void MapShadeDeploymentOrderToLoopNumber(EnergyPlusData &state, int const enclNum)
9822 : {
9823 : // J. Glazer - 2018
9824 : // Allow a way to map back to the original "loop" index that is used in many other places in the
9825 : // ZoneDayLight data structure when traversing the list in the order of the window shaded deployment
9826 22 : auto &dl = state.dataDayltg;
9827 22 : auto &s_surf = state.dataSurface;
9828 :
9829 22 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9830 22 : auto const &thisEnclSol = state.dataViewFactor->EnclSolInfo(enclNum);
9831 :
9832 22 : if (thisEnclSol.TotalEnclosureDaylRefPoints == 0 || thisEnclDaylight.NumOfDayltgExtWins == 0) {
9833 0 : return;
9834 : }
9835 :
9836 44 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9837 22 : auto &thisDaylightCtrl = dl->daylightControl(controlNum);
9838 22 : if (thisDaylightCtrl.ShadeDeployOrderExtWins.size() == 0) {
9839 1 : continue;
9840 : }
9841 :
9842 21 : int count = 0;
9843 21 : bool showOnce = true;
9844 50 : for (auto const &listOfExtWin : thisDaylightCtrl.ShadeDeployOrderExtWins) {
9845 63 : for (int IWinShdOrd : listOfExtWin) {
9846 34 : ++count;
9847 34 : if (count > thisEnclDaylight.NumOfDayltgExtWins) {
9848 0 : if (showOnce) {
9849 0 : ShowWarningError(
9850 : state,
9851 0 : format("MapShadeDeploymentOrderToLoopNumber: too many controlled shaded windows in enclosure {}", thisEnclSol.Name));
9852 0 : ShowContinueError(state,
9853 : "Check the Zone Name in the WindowShadingControl that references the following fenestration surfaces:");
9854 0 : showOnce = false;
9855 : }
9856 0 : ShowContinueError(state, format(" - {}", s_surf->Surface(IWinShdOrd).Name));
9857 : }
9858 86 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
9859 86 : int IWinLoop = thisEnclDaylight.DayltgExtWinSurfNums(loop);
9860 86 : if (IWinShdOrd == IWinLoop) {
9861 34 : thisDaylightCtrl.MapShdOrdToLoopNum(count) = loop;
9862 34 : break;
9863 : }
9864 : }
9865 29 : }
9866 21 : } // for (listOfExtWin)
9867 22 : } // for (controlNum)
9868 : } // MapShadeDeploymentOrderToLoopNumber()
9869 :
9870 1015 : void DayltgInterReflIllFrIntWins(EnergyPlusData &state, int const enclNum)
9871 : {
9872 :
9873 : // SUBROUTINE INFORMATION:
9874 : // AUTHOR Fred Winkelmann
9875 : // DATE WRITTEN Mar. 2004
9876 :
9877 : // PURPOSE OF THIS SUBROUTINE:
9878 : // Calculates the inter-reflected illuminance in a daylit zone from beam
9879 : // and diffuse daylight entering the zone through interior windows. This illuminance
9880 : // is determined by the split-flux method and is assumed to be uniform, i.e., the same
9881 : // at all reference points.
9882 :
9883 1015 : auto &dl = state.dataDayltg;
9884 1015 : auto &s_surf = state.dataSurface;
9885 :
9886 1015 : auto &enclDayl = dl->enclDaylight(enclNum);
9887 1015 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
9888 :
9889 1015 : enclDayl.InterReflIllFrIntWins = 0.0;
9890 :
9891 8120 : for (int const IWin : enclSol.SurfacePtr) {
9892 7105 : auto &surf = s_surf->Surface(IWin);
9893 7105 : if (surf.Class != SurfaceClass::Window || surf.ExtBoundCond < 1) {
9894 6090 : continue;
9895 : }
9896 1015 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
9897 : // This is an interior window in ZoneNum
9898 1015 : int const ConstrNum = surf.Construction;
9899 1015 : int const adjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
9900 : // Luminous flux transmitted through an int win from adjacent zone's enclosure (lumens)
9901 1015 : Real64 QDifTrans = state.dataHeatBal->EnclSolQSDifSol(adjEnclNum) * state.dataConstruction->Construct(ConstrNum).TransDiffVis * surf.Area *
9902 1015 : state.dataEnvrn->PDIFLW;
9903 1015 : Real64 QDifTransUp = QDifTrans * surfWin.fractionUpgoing; // Upgoing part of QDifTrans (lumens)
9904 1015 : Real64 QDifTransDn = QDifTrans * (1.0 - surfWin.fractionUpgoing); // Downgoing part of QDifTrans (lumens)
9905 1015 : if (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect) != 0.0) {
9906 1015 : enclDayl.InterReflIllFrIntWins += (QDifTransDn * surfWin.rhoFloorWall + QDifTransUp * surfWin.rhoCeilingWall) /
9907 1015 : (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
9908 : }
9909 : } // for (iWin)
9910 :
9911 : // Add inter-reflected illuminance from beam solar entering enclosure through interior windows
9912 : // TH, CR 7873, 9/17/2009
9913 1015 : if (dl->enclDaylight(enclNum).totInsSurfArea > 0) {
9914 1015 : enclDayl.InterReflIllFrIntWins +=
9915 1015 : (state.dataHeatBal->EnclSolDBIntWin(enclNum) * state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW * enclDayl.floorVisRefl) /
9916 1015 : (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
9917 : }
9918 1015 : } // DayltgInterReflIllFrIntWins()
9919 :
9920 65 : void CalcMinIntWinSolidAngs(EnergyPlusData &state)
9921 : {
9922 :
9923 : // SUBROUTINE INFORMATION:
9924 : // AUTHOR Fred Winkelmann
9925 : // DATE WRITTEN Feb. 2004
9926 :
9927 : // PURPOSE OF THIS SUBROUTINE:
9928 : // For each Daylighting:Detailed zone finds the minimum solid angle subtended
9929 : // by interior windows through which daylight can pass from adjacent zones with
9930 : // exterior windows.
9931 :
9932 65 : auto &dl = state.dataDayltg;
9933 65 : auto &s_surf = state.dataSurface;
9934 :
9935 916 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9936 851 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9937 851 : thisEnclDaylight.MinIntWinSolidAng = 2.0 * Constant::Pi;
9938 851 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) {
9939 514 : continue;
9940 : }
9941 337 : if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) {
9942 336 : continue;
9943 : }
9944 :
9945 8 : for (int IWin : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9946 7 : auto const &surf = s_surf->Surface(IWin);
9947 :
9948 7 : if ((surf.Class != SurfaceClass::Window) || (surf.ExtBoundCond < 1)) {
9949 6 : continue;
9950 : }
9951 :
9952 : // This is an interior window in enclNum
9953 1 : int const winAdjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
9954 1 : bool IntWinNextToIntWinAdjZone = false; // True if an interior window is next to a zone with one or more exterior windows
9955 1 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9956 1 : if (winAdjEnclNum == adjEnclNum) {
9957 1 : IntWinNextToIntWinAdjZone = true;
9958 1 : break;
9959 : }
9960 : }
9961 :
9962 1 : if (!IntWinNextToIntWinAdjZone) {
9963 0 : continue;
9964 : }
9965 :
9966 2 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9967 1 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9968 2 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
9969 : // Reference point in absolute coordinate system
9970 1 : Vector3<Real64> RREF = thisDayltgCtrl.refPts(IL).absCoords;
9971 1 : bool is_Triangle = (surf.Sides == 3);
9972 1 : bool is_Rectangle = (surf.Sides == 4);
9973 :
9974 1 : Vector3<Real64> W1, W2, W3;
9975 1 : if (is_Rectangle) {
9976 : // Vertices of window numbered counter-clockwise starting at upper left as viewed
9977 : // from inside of room. Assumes original vertices are numbered counter-clockwise from
9978 : // upper left as viewed from outside.
9979 1 : W3 = surf.Vertex(2);
9980 1 : W2 = surf.Vertex(3);
9981 1 : W1 = surf.Vertex(4);
9982 0 : } else if (is_Triangle) {
9983 0 : W3 = surf.Vertex(2);
9984 0 : W2 = surf.Vertex(3);
9985 0 : W1 = surf.Vertex(1);
9986 : }
9987 : // Unit vectors from window vertex 2 to 1 and 2 to 3, center point of window,
9988 : // and vector from ref pt to center of window
9989 1 : Vector3<Real64> W21 = W1 - W2;
9990 1 : Vector3<Real64> W23 = W3 - W2;
9991 1 : Real64 HW = W21.magnitude();
9992 1 : Real64 WW = W23.magnitude();
9993 1 : Vector3<Real64> WC = (is_Rectangle) ? (W2 + (W23 + W21) / 2.0) : (W2 + (W23 + W21) / 3.0);
9994 :
9995 : // Vector from ref point to center of window
9996 1 : Vector3<Real64> REFWC = WC - RREF;
9997 1 : W21 /= HW;
9998 1 : W23 /= WW;
9999 : // Unit vector normal to window (pointing away from room)
10000 1 : Vector3<Real64> WNORM = surf.OutNormVec;
10001 : // Distance from ref point to center of window
10002 1 : Real64 DIS = REFWC.magnitude();
10003 : // Unit vector from ref point to center of window
10004 1 : Vector3<Real64> Ray = REFWC / DIS;
10005 : // Cosine of angle between ray from ref pt to center of window and window outward normal
10006 1 : Real64 COSB = dot(WNORM, Ray);
10007 1 : if (COSB > 0.01765) { // 0 <= B < 89 deg
10008 : // Above test avoids case where ref point cannot receive daylight directly from the
10009 : // interior window
10010 1 : Real64 IntWinSolidAng = COSB * surf.Area / (pow_2(DIS) + 0.001);
10011 1 : thisEnclDaylight.MinIntWinSolidAng = min(thisEnclDaylight.MinIntWinSolidAng, IntWinSolidAng);
10012 : }
10013 1 : } // for (IL)
10014 1 : } // for (controlNum)
10015 : } // for (IWin)
10016 : } // for (enclNum)
10017 65 : }
10018 :
10019 130 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
10020 : {
10021 :
10022 : // SUBROUTINE INFORMATION:
10023 : // AUTHOR Linda Lawrie
10024 : // DATE WRITTEN February 2009
10025 :
10026 : // PURPOSE OF THIS SUBROUTINE:
10027 : // check for geometrytransform in the daylighting access for reference and map points
10028 :
10029 : // METHODOLOGY EMPLOYED:
10030 : // once reference points have been converted to WCS,
10031 : // change them to reflect a different aspect
10032 : // ratio for the entire building based on user input.
10033 :
10034 : // SUBROUTINE PARAMETER DEFINITIONS:
10035 : static constexpr std::string_view CurrentModuleObject = "GeometryTransform";
10036 :
10037 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
10038 130 : Array1D_string cAlphas(1);
10039 130 : Array1D<Real64> rNumerics;
10040 :
10041 : // begin execution
10042 : // get user input...
10043 130 : doTransform = false;
10044 130 : OldAspectRatio = 1.0;
10045 130 : NewAspectRatio = 1.0;
10046 :
10047 130 : auto &ip = state.dataInputProcessing->inputProcessor;
10048 130 : auto const &s_ipsc = state.dataIPShortCut;
10049 130 : auto &s_surf = state.dataSurface;
10050 :
10051 130 : if (ip->getNumObjectsFound(state, CurrentModuleObject) == 1) {
10052 : int NAlphas;
10053 : int NNum;
10054 : int IOStat;
10055 0 : ip->getObjectItem(state,
10056 : CurrentModuleObject,
10057 : 1,
10058 : cAlphas,
10059 : NAlphas,
10060 : rNumerics,
10061 : NNum,
10062 : IOStat,
10063 0 : s_ipsc->lNumericFieldBlanks,
10064 0 : s_ipsc->lAlphaFieldBlanks,
10065 0 : s_ipsc->cAlphaFieldNames,
10066 0 : s_ipsc->cNumericFieldNames);
10067 0 : OldAspectRatio = rNumerics(1);
10068 0 : NewAspectRatio = rNumerics(2);
10069 0 : std::string transformPlane = cAlphas(1);
10070 0 : if (transformPlane != "XY") {
10071 0 : ShowWarningError(state, format("{}: invalid {}=\"{}...ignored.", CurrentModuleObject, s_ipsc->cAlphaFieldNames(1), cAlphas(1)));
10072 : }
10073 0 : doTransform = true;
10074 0 : s_surf->AspectTransform = true;
10075 0 : }
10076 130 : if (s_surf->WorldCoordSystem) {
10077 56 : doTransform = false;
10078 56 : s_surf->AspectTransform = false;
10079 : }
10080 130 : }
10081 :
10082 22 : void WriteDaylightMapTitle(EnergyPlusData &state,
10083 : int const mapNum,
10084 : InputOutputFile &mapFile,
10085 : std::string const &mapName,
10086 : std::string const &environmentName,
10087 : int const ZoneNum,
10088 : std::string const &refPts,
10089 : Real64 const zcoord)
10090 : {
10091 : // SUBROUTINE INFORMATION:
10092 : // AUTHOR Greg Stark
10093 : // DATE WRITTEN Sept 2008
10094 :
10095 : // PURPOSE OF THIS SUBROUTINE:
10096 : // The purpose of the routine is to allow the daylighting map data to be written in various formats
10097 :
10098 : // must add correct number of commas at end
10099 22 : auto &dl = state.dataDayltg;
10100 :
10101 22 : std::string fullmapName = fmt::format("{}:{}:{} Illuminance [lux] (Hourly)", state.dataHeatBal->Zone(ZoneNum).Name, environmentName, mapName);
10102 22 : print(mapFile, "Date/Time{}{}{}{}{}{}\n", dl->MapColSep, fullmapName, dl->MapColSep, refPts, dl->MapColSep, dl->MapColSep);
10103 :
10104 22 : if (state.dataSQLiteProcedures->sqlite) {
10105 1 : state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMapTitle(mapNum, fullmapName, environmentName, ZoneNum, refPts, zcoord);
10106 : }
10107 22 : } // WritDaylightMapTitle()
10108 :
10109 : } // namespace EnergyPlus::Dayltg
|