Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <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 287 : 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 287 : auto &dl = state.dataDayltg;
175 :
176 : // Total inside surface area, including windows
177 287 : Real64 AInsTot = 0.0;
178 : // Sum of products of inside surface area * vis reflectance
179 287 : Real64 ARHTOT = 0.0;
180 :
181 : // Area sum and area * reflectance sum for different orientations
182 287 : std::array<Real64, (int)FWC::Num> AR = {0.0, 0.0, 0.0};
183 287 : std::array<Real64, (int)FWC::Num> ARH = {0.0, 0.0, 0.0};
184 : // Loop over surfaces in the zone's enclosure
185 :
186 287 : auto &thisEnclosure = state.dataViewFactor->EnclSolInfo(enclNum);
187 3450 : for (int ISurf : thisEnclosure.SurfacePtr) {
188 3163 : auto const &surf = state.dataSurface->Surface(ISurf);
189 :
190 3163 : SurfaceClass IType = surf.Class;
191 : // Error if window has multiplier > 1 since this causes incorrect illuminance calc
192 3163 : if (IType == SurfaceClass::Window && surf.Multiplier > 1.0) {
193 0 : if (thisEnclosure.TotalEnclosureDaylRefPoints > 0) {
194 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
195 0 : ShowContinueError(state, "...not allowed since it is in a zone or enclosure with daylighting.");
196 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
197 : } else {
198 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
199 0 : ShowContinueError(state, "...an adjacent Zone has daylighting. Simulation cannot proceed.");
200 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
201 : }
202 : }
203 3163 : if (IType == SurfaceClass::Wall || IType == SurfaceClass::Floor || IType == SurfaceClass::Roof || IType == SurfaceClass::Window ||
204 : IType == SurfaceClass::Door) {
205 2918 : Real64 AREA = surf.Area;
206 : // In following, FrameArea and DividerArea can be non-zero only for exterior windows
207 2918 : AInsTot += AREA + state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) +
208 2918 : state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf));
209 2918 : ARHTOT += AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
210 2918 : state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) *
211 2918 : (1.0 - state.dataSurface->SurfWinFrameSolAbsorp(ISurf)) +
212 2918 : state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf)) *
213 2918 : (1.0 - state.dataSurface->SurfWinDividerSolAbsorp(ISurf));
214 :
215 2918 : FWC fwc = FWC::Ceiling; // Ceiling
216 2918 : if (surf.Tilt > 10.0 && surf.Tilt < 170.0) fwc = FWC::Wall; // Wall
217 2918 : if (surf.Tilt >= 170.0) fwc = FWC::Floor; // Floor
218 2918 : AR[(int)fwc] += AREA + state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) +
219 2918 : state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf));
220 5836 : ARH[(int)fwc] += AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
221 2918 : state.dataSurface->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(ISurf)) *
222 2918 : (1.0 - state.dataSurface->SurfWinFrameSolAbsorp(ISurf)) +
223 2918 : state.dataSurface->SurfWinDividerArea(ISurf) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(ISurf)) *
224 2918 : (1.0 - state.dataSurface->SurfWinDividerSolAbsorp(ISurf));
225 : }
226 : }
227 :
228 : // Average inside surface reflectance of enclosure
229 287 : if (AInsTot <= 0.0) {
230 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Total opaque surface area is <=0.0 in solar enclosure={}", thisEnclosure.Name));
231 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
232 : }
233 287 : dl->enclDaylight(enclNum).aveVisDiffReflect = ARHTOT / AInsTot;
234 : // Total inside surface area of enclosure
235 287 : dl->enclDaylight(enclNum).totInsSurfArea = AInsTot;
236 : // Average floor visible reflectance
237 287 : dl->enclDaylight(enclNum).floorVisRefl = ARH[iFWC_Ceiling] / (AR[iFWC_Ceiling] + 1.e-6);
238 :
239 3450 : for (int ISurf : thisEnclosure.SurfacePtr) {
240 3163 : auto const &surf = state.dataSurface->Surface(ISurf);
241 3163 : if (surf.Class != SurfaceClass::Wall && surf.Class != SurfaceClass::Floor && surf.Class != SurfaceClass::Roof) continue;
242 :
243 : // Remove this surface from the space inside surface area and area*reflectivity
244 : // The resulting areas are AP(ITILT). The resulting area*reflectivity is ARHP(ITILT).
245 : // Initialize gross area of surface (including subsurfaces)
246 1865 : Real64 ATWL = surf.Area; // This is the surface area less subsurfaces
247 : // Area * reflectance for this surface, excluding attached windows and doors
248 1865 : Real64 ARHTWL = surf.Area * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack;
249 :
250 1865 : FWC fwc = (surf.Tilt > 45.0 && surf.Tilt < 135.0) ? FWC::Wall : ((surf.Tilt >= 135.0) ? FWC::Floor : FWC::Ceiling);
251 :
252 : // Loop over windows and doors on this wall
253 23833 : for (int IWinDr : thisEnclosure.SurfacePtr) {
254 21968 : auto const &surfWinDr = state.dataSurface->Surface(IWinDr);
255 21968 : if ((surfWinDr.Class != SurfaceClass::Window && surfWinDr.Class != SurfaceClass::Door) || surfWinDr.BaseSurf != ISurf) continue;
256 :
257 1053 : ATWL += surfWinDr.Area + state.dataSurface->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(IWinDr)) +
258 1053 : state.dataSurface->SurfWinDividerArea(IWinDr) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(IWinDr));
259 1053 : ARHTWL += surfWinDr.Area * state.dataConstruction->Construct(surfWinDr.Construction).ReflectVisDiffBack +
260 1053 : state.dataSurface->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * state.dataSurface->SurfWinProjCorrFrIn(IWinDr)) *
261 1053 : (1.0 - state.dataSurface->SurfWinFrameSolAbsorp(IWinDr)) +
262 1053 : state.dataSurface->SurfWinDividerArea(IWinDr) * (1.0 + state.dataSurface->SurfWinProjCorrDivIn(IWinDr)) *
263 1053 : (1.0 - state.dataSurface->SurfWinDividerSolAbsorp(IWinDr));
264 : }
265 :
266 : std::array<Real64, (int)FWC::Num> AP;
267 : std::array<Real64, (int)FWC::Num> ARHP;
268 : // Inside surface area of floor, walls and ceilings, minus surface ISurf and its subsurfaces
269 7460 : for (int iFWC = iFWC_Floor; iFWC < (int)FWC::Num; ++iFWC) {
270 5595 : if (iFWC == (int)fwc) {
271 1865 : AP[iFWC] = AR[iFWC] - ATWL;
272 1865 : ARHP[iFWC] = ARH[iFWC] - ARHTWL;
273 : } else {
274 3730 : AP[iFWC] = AR[iFWC];
275 3730 : ARHP[iFWC] = ARH[iFWC];
276 : }
277 : }
278 1865 : state.dataSurface->SurfaceWindow(ISurf).EnclAreaMinusThisSurf = AP;
279 1865 : state.dataSurface->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf = ARHP;
280 : } // for (ISurf)
281 :
282 3450 : for (int IWin : thisEnclosure.SurfacePtr) {
283 3163 : auto const &surf = state.dataSurface->Surface(IWin);
284 3163 : if (surf.Class != SurfaceClass::Window) continue;
285 :
286 847 : auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
287 847 : auto const &zone = state.dataHeatBal->Zone(surf.Zone);
288 847 : int ISurf = surf.BaseSurf;
289 :
290 : // Ratio of floor-to-window-center height and average floor-to-ceiling height
291 847 : Real64 ETA = max(0.0, min(1.0, (surfWin.WinCenter.z - zone.OriginZ) * zone.FloorArea / zone.Volume));
292 :
293 847 : std::array<Real64, (int)FWC::Num> AP = state.dataSurface->SurfaceWindow(ISurf).EnclAreaMinusThisSurf;
294 847 : std::array<Real64, (int)FWC::Num> ARHP = state.dataSurface->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf;
295 : // Average reflectance seen by light moving up (RhoCeilingWall) and down (RhoFloorWall)
296 : // across horizontal plane through center of window
297 847 : surfWin.rhoCeilingWall = (ARHP[iFWC_Wall] * (1.0 - ETA) + ARHP[iFWC_Ceiling]) / (AP[iFWC_Wall] * (1.0 - ETA) + AP[iFWC_Ceiling] + 1.0e-5);
298 847 : surfWin.rhoFloorWall = (ARHP[iFWC_Wall] * ETA + ARHP[iFWC_Floor]) / (AP[iFWC_Wall] * ETA + AP[iFWC_Floor] + 1.e-9);
299 :
300 : // Angle factor for windows with diffusing shades. SurfaceWindow(IWin)%FractionUpgoing is
301 : // fraction of light from the shade that goes up toward ceiling and upper part of walls.
302 : // 1 - SurfaceWindow(IWin)%FractionUpgoing is fraction that goes down toward floor and lower part of walls.
303 847 : surfWin.fractionUpgoing = surf.Tilt / 180.0;
304 :
305 : // Daylighting shelf simplification: All light goes up to the ceiling regardless of orientation of shelf
306 847 : if (state.dataSurface->SurfDaylightingShelfInd(IWin) > 0) {
307 1 : if (state.dataDaylightingDevicesData->Shelf(state.dataSurface->SurfDaylightingShelfInd(IWin)).InSurf > 0) surfWin.fractionUpgoing = 1.0;
308 : }
309 : } // for (IWin)
310 287 : } // DayltgAveInteriorReflectance()
311 :
312 14627 : void CalcDayltgCoefficients(EnergyPlusData &state)
313 : {
314 :
315 : // SUBROUTINE INFORMATION:
316 : // AUTHOR Fred Winkelmann
317 : // DATE WRITTEN July 1997
318 : // MODIFIED FW, Jan 2002: add variable slat angle blinds
319 : // FW, Mar 2002: add triangular windows
320 : // FW, Oct 2002: remove warning on window discretization relative to
321 : // reference point distance to window plane
322 : // FW, Jan 2003: add between-glass shades and blinds
323 : // FW, Apr 2003: initialize shading type to 'NOSHADE' in window loop
324 : // PE, May 2003: add light pipes (tubular daylighting devices)
325 : // FW, Jul 2003: account for possible non-zero transmittance of
326 : // shading surfaces (previously all shading surfaces were
327 : // assumed to be opaque)
328 : // PE, Aug 2003: add daylighting shelves
329 : // FW, Sep 2003: write the bare-window overcast sky daylight factors to the eio file
330 : // FW, Nov 2003: add exterior beam and sky solar diffuse reflection from obstructions;
331 : // add beam solar and sky solar reflection from ground with obstructions.
332 : // FW, Nov 2003: change expression for NDIVX, NDIVY (no. of window elements in X,Y) to
333 : // round up to nearest integer rather than down
334 : // FW, Nov 2003: add specular reflection of beam solar from obstructions
335 : // RJH, Jan 2004: add alternative daylighting analysis using DElight
336 : // All modifications demarked with RJH (Rob Hitchcock)
337 : // FW, Feb 2004: add daylighting through interior windows
338 : // FW, Apr 2004: add light well efficiency that multiplies glazing transmittance
339 : // FW, Apr 2004: add diffusing glazing
340 : // RJH, Jul 2004: add error handling for warnings/errors returned from DElight
341 : // LKL, Oct 2004: Separate "map" and "ref" point calculations -- move some input routines to
342 : // separate routines.
343 :
344 : // PURPOSE OF THIS SUBROUTINE:
345 : // Calculates daylighting factors for later use in the time-step loop.
346 :
347 : // METHODOLOGY EMPLOYED:
348 :
349 : // For each combination of exterior window and reference point in a zone,
350 : // calculates daylighting factors (interior illuminance / exterior illuminance)
351 : // and glare factors for clear and overcast skies and for windows with and
352 : // without shading devices. These factors are calculated for each hourly
353 : // sun position for design days and for selected days throughout the year.
354 :
355 : // If a target zone has one or more interior windows, also calculates daylighting
356 : // factors for the target zone that are associated with exterior windows in adjacent
357 : // zones that share interior windows with the target zone.
358 :
359 : // The daylight illuminance at a reference point from a window is determined
360 : // by dividing the window into rectangular elements and calculating the illuminance
361 : // reaching the reference point directly from each element. The illumination
362 : // from an element can come from the sky or ground if the window is unshaded, or from
363 : // a shading device illuminated by solar radiation. Also considered are the
364 : // illuminance contribution from interreflection among the zone's interior surfaces
365 : // and sunlight striking the reference point.
366 :
367 : // In calculating sky-related interior illuminance and luminance quantities,
368 : // the sky luminance for the different sky types are determined from distributions
369 : // in which the zenith luminance is normalized to 1.0 cd/m2. Similarly, sun-related
370 : // illuminance and luminance quantities are based on beam normal solar illuminance
371 : // normalized to 1.0 lux.
372 : // The daylight and glare factors calculated in this subroutine are used in DayltgInteriorIllum
373 : // to get the daylight illuminance and glare at each time step.
374 : // Based on this information and user-input lighting setpoint and type of lighting
375 : // control system, DayltgElecLightingControl then determines how much the overhead electric lighting
376 : // can be reduced.
377 :
378 : // REFERENCES:
379 : // Based on DOE-2.1E subroutine DCOF.
380 :
381 14627 : auto &dl = state.dataDayltg;
382 :
383 14627 : if (dl->CalcDayltghCoefficients_firstTime) {
384 796 : GetDaylightingParametersInput(state);
385 796 : CheckTDDsAndLightShelvesInDaylitZones(state);
386 796 : AssociateWindowShadingControlWithDaylighting(state);
387 796 : dl->CalcDayltghCoefficients_firstTime = false;
388 : } // End of check if firstTime
389 :
390 : // Find the total number of exterior windows associated with all Daylighting:Detailed enclosures.
391 : // An exterior window is associated with such a enclosure if (1) it is an exterior window in the enclosure, or
392 : // (2) it is an exterior window in an adjacent enclosure that shares an interior window with the enclosure.
393 : // Note that exterior windows in category (2) may be counted more than once if an adjacent enclosure
394 : // is adjacent to more than one daylit enclosure with which the adjacent enclosure shares interior windows.
395 : // If there are no interior windows in a building, than TotWindowsWithDayl is just the total number of
396 : // exterior windows in Daylighting:Detailed enclosures. Note that it is possible for a
397 : // Daylighting:Detailed enclosure to have zero exterior windows of its own, but it may have an interior
398 : // through which daylight passes from adjacent enclosures with exterior windows.
399 14627 : if ((int)dl->DaylRefPt.size() == 0) return;
400 541 : if (state.dataGlobal->BeginSimFlag) {
401 64 : dl->TotWindowsWithDayl = 0;
402 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
403 721 : dl->TotWindowsWithDayl += dl->enclDaylight(enclNum).NumOfDayltgExtWins;
404 : }
405 : }
406 :
407 541 : if (dl->TotWindowsWithDayl == 0) return;
408 :
409 : //-----------------------------------------!
410 : // Detailed daylighting factor calculation !
411 : //-----------------------------------------!
412 541 : if (!state.dataSysVars->DetailedSolarTimestepIntegration && !state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) {
413 225 : if (state.dataGlobal->WarmupFlag) {
414 207 : DisplayString(state, "Calculating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
415 : } else {
416 18 : DisplayString(state, "Updating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
417 : }
418 : }
419 :
420 541 : if (state.dataGlobal->BeginSimFlag) {
421 :
422 : // Find minimum solid angle subtended by an interior window in Daylighting:Detailed zones.
423 : // Used in calculating daylighting through interior windows.
424 64 : CalcMinIntWinSolidAngs(state);
425 :
426 : // Warning if detailed daylighting has been requested for a zone with no associated exterior windows.
427 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
428 721 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
429 721 : if (thisEnclDaylight.NumOfDayltgExtWins == 0 && thisEnclDaylight.TotalExtWindows == 0) {
430 249 : for (int daylightCtrlNum : thisEnclDaylight.daylightControlIndexes) {
431 0 : if (dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints > 0) {
432 0 : ShowWarningError(
433 : state,
434 0 : format("Detailed daylighting will not be done for Daylighting:Controls={}", dl->daylightControl(daylightCtrlNum).Name));
435 0 : ShowContinueError(state, "because it has no associated exterior windows.");
436 : }
437 249 : }
438 : }
439 :
440 : // Find area and reflectance quantities used in calculating inter-reflected illuminance.
441 : // TH 9/10/2009. Need to calculate for zones without daylighting controls (TotalDaylRefPoints = 0)
442 : // but with adjacent zones having daylighting controls.
443 721 : if ((thisEnclDaylight.NumOfDayltgExtWins > 0) || thisEnclDaylight.adjEnclHasDayltgCtrl) {
444 287 : DayltgAveInteriorReflectance(state, enclNum);
445 : }
446 : }
447 : }
448 :
449 541 : int numTDD = (int)state.dataDaylightingDevicesData->TDDPipe.size();
450 : // Zero daylighting factor arrays
451 541 : if (numTDD > 0) {
452 5 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
453 5 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
454 125 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
455 360 : for (int iTDD = 1; iTDD <= numTDD; ++iTDD) {
456 240 : dl->TDDTransVisBeam(iHr, iTDD) = 0.0;
457 960 : dl->TDDFluxInc(iHr, iTDD) = Illums();
458 960 : dl->TDDFluxTrans(iHr, iTDD) = Illums();
459 : }
460 : }
461 : }
462 :
463 541 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
464 541 : if (state.dataGlobal->BeginDayFlag) {
465 : // Calculate hourly sun angles, clear sky zenith luminance, and exterior horizontal illuminance
466 541 : dl->sunAngles = SunAngles();
467 541 : dl->sunAnglesHr = {SunAngles()};
468 541 : dl->horIllum = {Illums()};
469 13525 : for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
470 12984 : auto const &surfSunCosHr = state.dataSurface->SurfSunCosHourly(IHR);
471 12984 : if (surfSunCosHr.z < DataEnvironment::SunIsUpValue)
472 8287 : continue; // Skip if sun is below horizon //Autodesk SurfSunCosHourly was uninitialized here
473 :
474 4697 : Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
475 4697 : Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
476 4697 : dl->sunAngles = dl->sunAnglesHr[IHR] = {phi, std::sin(phi), std::cos(phi), theta};
477 :
478 4697 : DayltgExtHorizIllum(state, dl->horIllum[IHR]);
479 : }
480 : }
481 : } else { // timestep integrated calculations
482 0 : dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {SunAngles()};
483 0 : dl->horIllum[state.dataGlobal->HourOfDay] = Illums();
484 0 : auto const &surfSunCosHr = state.dataSurface->SurfSunCosHourly(state.dataGlobal->HourOfDay);
485 0 : if (!(surfSunCosHr.z < DataEnvironment::SunIsUpValue)) { // Skip if sun is below horizon
486 0 : Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
487 0 : Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
488 0 : dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {phi, std::sin(phi), std::cos(phi), theta};
489 0 : DayltgExtHorizIllum(state, dl->horIllum[state.dataGlobal->HourOfDay]);
490 : }
491 : }
492 :
493 541 : CalcDayltgCoeffsRefMapPoints(state);
494 :
495 605 : if (dl->doSkyReporting && (!state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) &&
496 64 : (dl->FirstTimeDaylFacCalc && dl->TotWindowsWithDayl > 0 &&
497 64 : (!state.dataSysVars->DetailedSolarTimestepIntegration || state.dataGlobal->HourOfDay == 12))) {
498 : // Write the bare-window four sky daylight factors at noon time to the eio file; this is done only
499 : // for first time that daylight factors are calculated and so is insensitive to possible variation
500 : // due to change in ground reflectance from month to month, or change in storm window status.
501 : static constexpr std::string_view Format_700("! <Sky Daylight Factors>, Sky Type, MonthAndDay, Daylighting Control Name, Enclosure Name, "
502 : "Window Name, Reference Point, Daylight Factor\n");
503 64 : print(state.files.eio, Format_700);
504 354 : for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
505 290 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
506 290 : int enclNum = thisDayltgCtrl.enclIndex;
507 290 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
508 :
509 290 : if (thisEnclDaylight.NumOfDayltgExtWins == 0 || !thisEnclDaylight.hasSplitFluxDaylighting) continue;
510 1135 : for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
511 848 : int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
512 : // For this report, do not include ext wins in zone adjacent to ZoneNum since the inter-reflected
513 : // component will not be calculated for these windows until the time-step loop.
514 848 : if (state.dataSurface->Surface(windowSurfNum).SolarEnclIndex != enclNum) continue;
515 : // Output for each reference point, for each sky. Group by sky type first
516 :
517 : static constexpr std::array<std::string_view, (int)SkyType::Num> skyTypeStrings = {
518 : "Clear Sky", "Clear Turbid Sky", "Intermediate Sky", "Overcast Sky"};
519 :
520 4235 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
521 9340 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
522 5952 : Real64 DaylFac = thisDayltgCtrl.daylFac[12](windowCounter, refPtNum, 1)[iLum_Illum].sky[iSky];
523 5952 : print(state.files.eio,
524 : " Sky Daylight Factors,{},{},{},{},{},{},{:.4R}\n",
525 5952 : skyTypeStrings[iSky],
526 5952 : state.dataEnvrn->CurMnDy,
527 5952 : thisDayltgCtrl.Name,
528 5952 : state.dataViewFactor->EnclSolInfo(thisDayltgCtrl.enclIndex).Name,
529 5952 : state.dataSurface->Surface(windowSurfNum).Name,
530 5952 : dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
531 : DaylFac);
532 : } // for (refPtNum)
533 : } // for (iSky)
534 : } // for (windowCounter)
535 : } // for (controlNum)
536 64 : dl->FirstTimeDaylFacCalc = false;
537 64 : dl->doSkyReporting = false;
538 : } // if (detailedIntegration etc.)
539 :
540 : // Skip if no daylight windows
541 541 : if (dl->TotWindowsWithDayl == 0) return;
542 :
543 : // Skip if no request of reporting
544 541 : if ((!dl->DFSReportSizingDays) && (!dl->DFSReportAllShadowCalculationDays)) return;
545 :
546 : // Skip duplicate calls
547 9 : if (state.dataGlobal->KickOffSizing) return;
548 7 : if (state.dataGlobal->DoingSizing) return;
549 5 : if (state.dataGlobal->KickOffSimulation) return;
550 :
551 2 : if (dl->DFSReportSizingDays) {
552 2 : if (state.dataGlobal->DoWeathSim && state.dataGlobal->DoDesDaySim) {
553 0 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) return;
554 : }
555 : }
556 :
557 2 : if (dl->DFSReportAllShadowCalculationDays) {
558 0 : if (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather) return;
559 : }
560 :
561 : // open a new file eplusout.dfs for saving the daylight factors
562 2 : if (dl->CreateDFSReportFile) {
563 1 : InputOutputFile &dfs = state.files.dfs.ensure_open(state, "CalcDayltgCoefficients", state.files.outputControl.dfs);
564 1 : print(dfs, "{}\n", "This file contains daylight factors for all exterior windows of daylight enclosures.");
565 1 : print(dfs, "{}\n", "MonthAndDay,Enclosure Name,Zone Name,Window Name,Window State");
566 1 : print(dfs,
567 : "{}\n",
568 : "Hour,Reference Point,Daylight Factor for Clear Sky,Daylight Factor for Clear Turbid Sky,"
569 : "Daylight Factor for Intermediate Sky,Daylight Factor for Overcast Sky");
570 1 : dl->CreateDFSReportFile = false;
571 : }
572 :
573 26 : for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
574 24 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
575 24 : int enclNum = thisDayltgCtrl.enclIndex;
576 24 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
577 24 : if (thisEnclDaylight.NumOfDayltgExtWins == 0) continue;
578 :
579 48 : for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
580 24 : int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
581 :
582 : // For this report, do not include ext wins in zone/enclosure adjacent to ZoneNum since the inter-reflected
583 : // component will not be calculated for these windows until the time-step loop.
584 24 : if (state.dataSurface->Surface(windowSurfNum).SolarEnclIndex == enclNum) {
585 :
586 : int ISA;
587 24 : if (state.dataSurface->SurfWinMovableSlats(windowSurfNum)) {
588 : // variable slat angle - MaxSlatangle sets
589 0 : ISA = Material::MaxSlatAngs + 1;
590 24 : } else if (state.dataSurface->Surface(windowSurfNum).HasShadeControl) {
591 : // window shade or blind with fixed slat angle
592 0 : ISA = 2;
593 : } else {
594 : // base window
595 24 : ISA = 1;
596 : }
597 :
598 : // loop over each slat angle
599 48 : for (int ISlatAngle = 1; ISlatAngle <= ISA; ++ISlatAngle) {
600 24 : if (ISlatAngle == 1) {
601 : // base window without shades, screens, or blinds
602 24 : print(state.files.dfs,
603 : "{},{},{},{},Base Window\n",
604 24 : state.dataEnvrn->CurMnDy,
605 24 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
606 24 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
607 24 : state.dataSurface->Surface(windowSurfNum).Name);
608 0 : } else if (ISlatAngle == 2 && ISA == 2) {
609 : // window shade or blind with fixed slat angle
610 0 : print(state.files.dfs,
611 : "{},{},{},{},Blind or Slat Applied\n",
612 0 : state.dataEnvrn->CurMnDy,
613 0 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
614 0 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
615 0 : state.dataSurface->Surface(windowSurfNum).Name);
616 : } else {
617 : // blind with variable slat angle
618 0 : Real64 SlatAngle = 180.0 / double(Material::MaxSlatAngs - 1) * double(ISlatAngle - 2);
619 0 : print(state.files.dfs,
620 : "{},{},{},{},{:.1R}\n",
621 0 : state.dataEnvrn->CurMnDy,
622 0 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
623 0 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
624 0 : state.dataSurface->Surface(windowSurfNum).Name,
625 : SlatAngle);
626 : }
627 :
628 600 : for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
629 : // For each Daylight Reference Point
630 576 : auto &daylFacHr = thisDayltgCtrl.daylFac[IHR];
631 1152 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
632 576 : auto &illums = daylFacHr(windowCounter, refPtNum, ISlatAngle)[iLum_Illum];
633 :
634 : // write daylight factors - 4 sky types for each daylight ref point
635 576 : print(state.files.dfs,
636 : "{},{},{:.5R},{:.5R},{:.5R},{:.5R}\n",
637 : IHR,
638 576 : dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
639 576 : illums.sky[(int)SkyType::Clear],
640 576 : illums.sky[(int)SkyType::ClearTurbid],
641 576 : illums.sky[(int)SkyType::Intermediate],
642 576 : illums.sky[(int)SkyType::Overcast]);
643 :
644 : } // for (refPtNum) Reference Point
645 : } // for (IHR) hour
646 : } // for (ISlatAngle) slat angle
647 : } // if (SolarEnclIndex == enclNum)
648 : } // for (windowCounter) exterior windows in enclosure
649 : } // for (controlNum) daylighting control
650 : } // CalcDayltgCoefficients()
651 :
652 541 : void CalcDayltgCoeffsRefMapPoints(EnergyPlusData &state)
653 : {
654 :
655 : // SUBROUTINE INFORMATION:
656 : // AUTHOR Linda Lawrie
657 : // DATE WRITTEN October 2004
658 : // MODIFIED May 2006 (RR): added exterior window screens
659 : // April 2012 (LKL); change to allow multiple maps per zone
660 :
661 : // PURPOSE OF THIS SUBROUTINE:
662 : // This subroutine does the daylighting coefficient calculation for the
663 : // daylighting and illuminance map reference points.
664 541 : auto &dl = state.dataDayltg;
665 :
666 541 : if (dl->VeryFirstTime) {
667 : // make sure all necessary surfaces match to pipes
668 64 : bool ErrorsFound = false;
669 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
670 1566 : for (int loopwin = 1; loopwin <= dl->enclDaylight(enclNum).NumOfDayltgExtWins; ++loopwin) {
671 845 : int IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
672 845 : if (state.dataSurface->Surface(IWin).OriginalClass != SurfaceClass::TDD_Diffuser) continue;
673 : // Look up the TDD:DOME object
674 2 : int PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
675 2 : if (PipeNum == 0) {
676 0 : ShowSevereError(state,
677 0 : format("GetTDDInput: Surface={}, TDD:Dome object does not reference a valid Diffuser object.",
678 0 : state.dataSurface->Surface(IWin).Name));
679 0 : ShowContinueError(state, "...needs DaylightingDevice:Tubular of same name as Surface.");
680 0 : ErrorsFound = true;
681 : }
682 : }
683 : }
684 :
685 64 : if (ErrorsFound) {
686 0 : ShowFatalError(state, "Not all TubularDaylightDome objects have corresponding DaylightingDevice:Tubular objects. Program terminates.");
687 : }
688 64 : dl->VeryFirstTime = false;
689 : }
690 :
691 : // Calc for daylighting reference points for daylighting controls that use SplitFlux method
692 3331 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
693 2790 : if (dl->daylightControl(daylightCtrlNum).DaylightMethod != DaylightingMethod::SplitFlux) continue;
694 : // Skip enclosures with no exterior windows or in adjacent enclosure(s) with which an interior window is shared
695 2775 : if (dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex).NumOfDayltgExtWins == 0) continue;
696 2775 : CalcDayltgCoeffsRefPoints(state, daylightCtrlNum);
697 : }
698 541 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
699 : // Calc for illuminance maps
700 155 : if ((int)dl->illumMaps.size() > 0) {
701 50 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
702 28 : int mapZoneNum = dl->illumMaps(MapNum).zoneIndex;
703 28 : std::string name = format("Zone={}", state.dataHeatBal->Zone(mapZoneNum).Name);
704 28 : int mapSpaceNum = dl->illumMaps(MapNum).spaceIndex;
705 28 : if (mapSpaceNum > 0) {
706 2 : name = format("Space={}", state.dataHeatBal->space(mapSpaceNum).Name);
707 : }
708 28 : if (state.dataGlobal->WarmupFlag) {
709 28 : DisplayString(state, format("Calculating Daylighting Coefficients (Map Points), {}", name));
710 : } else {
711 0 : DisplayString(state, format("Updating Daylighting Coefficients (Map Points), {}", name));
712 : }
713 28 : CalcDayltgCoeffsMapPoints(state, MapNum);
714 28 : }
715 : }
716 : }
717 541 : } // CalcDayltgCoeffsRefMapPoints()
718 :
719 2775 : void CalcDayltgCoeffsRefPoints(EnergyPlusData &state, int const daylightCtrlNum)
720 : {
721 :
722 : // SUBROUTINE INFORMATION:
723 : // AUTHOR Linda Lawrie
724 : // DATE WRITTEN April 2012
725 : // MODIFIED November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
726 :
727 : // PURPOSE OF THIS SUBROUTINE:
728 : // Provides calculations for Daylighting Coefficients for daylighting reference points
729 2775 : auto &dl = state.dataDayltg;
730 :
731 : // glare calculation (radians)
732 : int IConst; // Construction counter
733 : int ICtrl; // Window control counter
734 : int IWin; // Window counter
735 : int IWin2; // Secondary window counter (for TDD:DOME object, if exists)
736 : int InShelfSurf; // Inside daylighting shelf surface number
737 : WinShadingType ShType; // Window shading type
738 : int BlNum; // Window Blind Number
739 : int LSHCAL; // Interior shade calculation flag: 0=not yet
740 : // calculated, 1=already calculated
741 : int NWX; // Number of window elements in x direction for dayltg calc
742 : int NWY; // Number of window elements in y direction for dayltg calc
743 : int NWYlim; // For triangle, largest NWY for a given IX
744 : Real64 COSB; // Cosine of angle between window outward normal and ray from
745 : // reference point to window element
746 : Real64 PHRAY; // Altitude of ray from reference point to window element (radians)
747 : Real64 THRAY; // Azimuth of ray from reference point to window element (radians)
748 : Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
749 : Real64 TVISB; // Visible transmittance of window for COSB angle of incidence (times light well
750 : // efficiency, if appropriate)
751 : int ISunPos; // Sun position counter; used to avoid calculating various
752 : // quantities that do not depend on sun position.
753 : Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
754 : // from reference point through a window element
755 : bool is_Rectangle; // True if window is rectangular
756 : bool is_Triangle; // True if window is triangular
757 : Real64 DWX; // Horizontal dimension of window element (m)
758 : Real64 DWY; // Vertical dimension of window element (m)
759 : Real64 DAXY; // Area of window element
760 : Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
761 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
762 : int BRef;
763 : int ILB;
764 : bool hitIntObs; // True iff interior obstruction hit
765 : bool hitExtObs; // True iff ray from ref pt to ext win hits an exterior obstruction
766 : Real64 TVISIntWin; // Visible transmittance of int win at COSBIntWin for light from ext win
767 : Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
768 :
769 2775 : Vector3<Real64> W2;
770 2775 : Vector3<Real64> W3;
771 2775 : Vector3<Real64> W21;
772 2775 : Vector3<Real64> W23;
773 2775 : Vector3<Real64> RREF2;
774 2775 : Vector3<Real64> RWIN;
775 2775 : Vector3<Real64> RWIN2;
776 2775 : Vector3<Real64> Ray;
777 2775 : Vector3<Real64> WNORM2;
778 2775 : Vector3<Real64> VIEWVC;
779 2775 : Vector3<Real64> U2;
780 2775 : Vector3<Real64> U21;
781 2775 : Vector3<Real64> U23;
782 2775 : Vector3<Real64> VIEWVC2;
783 :
784 : int WinEl; // Current window element
785 :
786 2775 : if (dl->refFirstTime && (dl->maxControlRefPoints > 0)) {
787 64 : dl->RefErrIndex.allocate(dl->maxControlRefPoints, state.dataSurface->TotSurfaces);
788 64 : dl->RefErrIndex = 0;
789 64 : dl->refFirstTime = false;
790 : }
791 :
792 2775 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
793 2775 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
794 2775 : int zoneNum = thisDayltgCtrl.zoneIndex;
795 : // Azimuth of view vector in absolute coord sys
796 2775 : Real64 AZVIEW = (thisDayltgCtrl.ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth + state.dataHeatBal->BuildingAzimuth +
797 2775 : state.dataHeatBal->BuildingRotationAppendixG) *
798 2775 : Constant::DegToRadians;
799 : // View vector components in absolute coord sys
800 2775 : VIEWVC = {std::sin(AZVIEW), std::cos(AZVIEW), 0.0};
801 :
802 7395 : for (auto &refPt : thisDayltgCtrl.refPts) {
803 4620 : refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
804 4620 : refPt.glareIndex = 0.0; // Glare index at reference points
805 19776 : for (auto &extWin : refPt.extWins) {
806 15156 : extWin.solidAng = extWin.solidAngWtd = 0.0;
807 15156 : extWin.lums[iLum_Illum] = extWin.lums[iLum_Back] = extWin.lums[iLum_Source] = {0.0, 0.0};
808 : }
809 : }
810 :
811 2775 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
812 2775 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
813 2775 : int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
814 2775 : int numRefPts = thisDayltgCtrl.TotalDaylRefPoints;
815 2775 : int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
816 :
817 69375 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
818 66600 : auto &daylFacHr = thisDayltgCtrl.daylFac[iHr];
819 269616 : for (int iWin = 1; iWin <= numExtWins; ++iWin) {
820 566760 : for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
821 1091232 : for (int iSlatAng = 1; iSlatAng <= numSlatAngs; ++iSlatAng) {
822 727488 : auto &daylFac = daylFacHr(iWin, iRefPt, iSlatAng);
823 2909952 : daylFac[iLum_Illum] = Illums();
824 2909952 : daylFac[iLum_Source] = Illums();
825 2909952 : daylFac[iLum_Back] = Illums();
826 : } // for (iSlatAng)
827 : } // for (iRefPt)
828 : } // for (iWin)
829 : } // for (iHr)
830 :
831 2775 : BRef = 0;
832 :
833 7395 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
834 4620 : auto &refPt = thisDayltgCtrl.refPts(IL);
835 : // Reference point in absolute coordinate system
836 4620 : Vector3<Real64> RREF = refPt.absCoords;
837 :
838 : // -------------
839 : // ---------- WINDOW LOOP ----------
840 : // -------------
841 19776 : for (int loopwin = 1; loopwin <= thisEnclDaylight.NumOfDayltgExtWins; ++loopwin) {
842 :
843 15156 : FigureDayltgCoeffsAtPointsSetupForWindow(state,
844 : daylightCtrlNum,
845 : IL,
846 : loopwin,
847 : CalledFor::RefPoint,
848 : RREF,
849 : VIEWVC,
850 : IWin,
851 : IWin2,
852 : NWX,
853 : NWY,
854 : W2,
855 : W3,
856 : W21,
857 : W23,
858 : LSHCAL,
859 : InShelfSurf,
860 : ICtrl,
861 : ShType,
862 : BlNum,
863 : WNORM2,
864 : extWinType,
865 : IConst,
866 : RREF2,
867 : DWX,
868 : DWY,
869 : DAXY,
870 : U2,
871 : U23,
872 : U21,
873 : VIEWVC2,
874 : is_Rectangle,
875 : is_Triangle);
876 : // ---------------------
877 : // ---------- WINDOW ELEMENT LOOP ----------
878 : // ---------------------
879 :
880 15156 : WinEl = 0;
881 :
882 206362 : for (int IX = 1; IX <= NWX; ++IX) {
883 191206 : if (is_Rectangle) {
884 191206 : NWYlim = NWY;
885 0 : } else if (is_Triangle) {
886 0 : NWYlim = NWY - IX + 1;
887 : }
888 :
889 772700 : for (int IY = 1; IY <= NWYlim; ++IY) {
890 :
891 581494 : ++WinEl;
892 :
893 581494 : FigureDayltgCoeffsAtPointsForWindowElements(state,
894 : daylightCtrlNum,
895 : IL,
896 : loopwin,
897 : CalledFor::RefPoint,
898 : WinEl,
899 : IWin,
900 : IWin2,
901 : IX,
902 : IY,
903 : SkyObstructionMult,
904 : W2,
905 : W21,
906 : W23,
907 : RREF,
908 : NWYlim,
909 : VIEWVC2,
910 : DWX,
911 : DWY,
912 : DAXY,
913 : U2,
914 : U23,
915 : U21,
916 : RWIN,
917 : RWIN2,
918 : Ray,
919 : PHRAY,
920 : LSHCAL,
921 : COSB,
922 : ObTrans,
923 : TVISB,
924 : DOMEGA,
925 : THRAY,
926 : hitIntObs,
927 : hitExtObs,
928 : WNORM2,
929 : extWinType,
930 : IConst,
931 : RREF2,
932 : is_Triangle,
933 : TVISIntWin,
934 : TVISIntWinDisk);
935 :
936 : // -------------------
937 : // ---------- SUN POSITION LOOP ----------
938 : // -------------------
939 :
940 : // Sun position counter. Used to avoid calculating various quantities
941 : // that do not depend on sun position.
942 :
943 581494 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
944 581494 : ISunPos = 0;
945 14537350 : for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
946 :
947 13955856 : FigureDayltgCoeffsAtPointsForSunPosition(state,
948 : daylightCtrlNum,
949 : IL,
950 : IX,
951 : NWX,
952 : IY,
953 : NWYlim,
954 : WinEl,
955 : IWin,
956 : IWin2,
957 : IHR,
958 : ISunPos,
959 : SkyObstructionMult,
960 : RWIN2,
961 : Ray,
962 : PHRAY,
963 : LSHCAL,
964 : InShelfSurf,
965 : COSB,
966 : ObTrans,
967 : TVISB,
968 : DOMEGA,
969 : ICtrl,
970 : ShType,
971 : BlNum,
972 : THRAY,
973 : WNORM2,
974 : extWinType,
975 : IConst,
976 : AZVIEW,
977 : RREF2,
978 : hitIntObs,
979 : hitExtObs,
980 : CalledFor::RefPoint,
981 : TVISIntWin,
982 : TVISIntWinDisk);
983 :
984 : } // End of hourly sun position loop, IHR
985 : } else { // timestep integrated
986 0 : if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
987 0 : ISunPos = 0;
988 0 : dl->MySunIsUpFlag = true;
989 0 : } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
990 0 : ISunPos = 1;
991 0 : } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
992 0 : dl->MySunIsUpFlag = false;
993 0 : ISunPos = -1;
994 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
995 0 : ISunPos = -1;
996 : }
997 :
998 0 : FigureDayltgCoeffsAtPointsForSunPosition(state,
999 : daylightCtrlNum,
1000 : IL,
1001 : IX,
1002 : NWX,
1003 : IY,
1004 : NWYlim,
1005 : WinEl,
1006 : IWin,
1007 : IWin2,
1008 0 : state.dataGlobal->HourOfDay,
1009 : ISunPos,
1010 : SkyObstructionMult,
1011 : RWIN2,
1012 : Ray,
1013 : PHRAY,
1014 : LSHCAL,
1015 : InShelfSurf,
1016 : COSB,
1017 : ObTrans,
1018 : TVISB,
1019 : DOMEGA,
1020 : ICtrl,
1021 : ShType,
1022 : BlNum,
1023 : THRAY,
1024 : WNORM2,
1025 : extWinType,
1026 : IConst,
1027 : AZVIEW,
1028 : RREF2,
1029 : hitIntObs,
1030 : hitExtObs,
1031 : CalledFor::RefPoint,
1032 : TVISIntWin,
1033 : TVISIntWinDisk);
1034 : }
1035 :
1036 : } // End of window Y-element loop, IY
1037 : } // End of window X-element loop, IX
1038 :
1039 : // Loop again over hourly sun positions and calculate daylight factors by adding
1040 : // direct and inter-reflected illum components, then dividing by exterior horiz illum.
1041 : // Also calculate corresponding glare factors.
1042 :
1043 15156 : ILB = BRef + IL;
1044 :
1045 15156 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1046 15156 : ISunPos = 0;
1047 378900 : for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
1048 363744 : FigureRefPointDayltgFactorsToAddIllums(state, daylightCtrlNum, ILB, IHR, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
1049 :
1050 : } // End of sun position loop, IHR
1051 : } else {
1052 0 : if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1053 0 : ISunPos = 0;
1054 0 : dl->MySunIsUpFlag = true;
1055 0 : } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1056 0 : ISunPos = 1;
1057 0 : } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1058 0 : dl->MySunIsUpFlag = false;
1059 0 : ISunPos = -1;
1060 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1061 0 : ISunPos = -1;
1062 : }
1063 0 : FigureRefPointDayltgFactorsToAddIllums(
1064 0 : state, daylightCtrlNum, ILB, state.dataGlobal->HourOfDay, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
1065 : }
1066 : } // End of window loop, loopwin - IWin
1067 :
1068 4620 : } // End of reference point loop, IL
1069 2775 : }
1070 :
1071 28 : void CalcDayltgCoeffsMapPoints(EnergyPlusData &state, int const mapNum)
1072 : {
1073 :
1074 : // SUBROUTINE INFORMATION:
1075 : // AUTHOR Linda Lawrie
1076 : // DATE WRITTEN April 2012
1077 : // MODIFIED November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
1078 :
1079 : // PURPOSE OF THIS SUBROUTINE:
1080 : // Provides calculations for Daylighting Coefficients for map illuminance points
1081 :
1082 : // METHODOLOGY EMPLOYED:
1083 : // Was previously part of CalcDayltgCoeffsRefMapPoints -- broken out to all multiple
1084 : // maps per zone
1085 28 : auto &dl = state.dataDayltg;
1086 :
1087 : // In the following four variables, I=1 for clear sky, 2 for overcast.
1088 : int numRefPts; // Number of daylighting reference points in a zone
1089 : int IL; // Reference point counter
1090 : // glare calculation (radians)
1091 : int IConst; // Construction counter
1092 : int ICtrl; // Window control counter
1093 : int IWin; // Window counter
1094 : int IWin2; // Secondary window counter (for TDD:DOME object, if exists)
1095 : int InShelfSurf; // Inside daylighting shelf surface number
1096 : WinShadingType ShType; // Window shading type
1097 : int BlNum; // Window Blind Number
1098 : int LSHCAL; // Interior shade calculation flag: 0=not yet
1099 : // calculated, 1=already calculated
1100 : int NWX; // Number of window elements in x direction for dayltg calc
1101 : int NWY; // Number of window elements in y direction for dayltg calc
1102 : int NWYlim; // For triangle, largest NWY for a given IX
1103 : Real64 DWX; // Horizontal dimension of window element (m)
1104 : Real64 DWY; // Vertical dimension of window element (m)
1105 : Real64 COSB; // Cosine of angle between window outward normal and ray from
1106 : // reference point to window element
1107 : Real64 PHRAY; // Altitude of ray from reference point to window element (radians)
1108 : Real64 THRAY; // Azimuth of ray from reference point to window element (radians)
1109 : Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
1110 : Real64 TVISB; // Visible transmittance of window for COSB angle of incidence (times light well
1111 : // efficiency, if appropriate)
1112 : int ISunPos; // Sun position counter; used to avoid calculating various
1113 : // quantities that do not depend on sun position.
1114 : Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
1115 : // from reference point through a window element
1116 : bool is_Rectangle; // True if window is rectangular
1117 : bool is_Triangle; // True if window is triangular
1118 : Real64 DAXY; // Area of window element
1119 : Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
1120 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1121 : int ILB;
1122 : bool hitIntObs; // True iff interior obstruction hit
1123 : bool hitExtObs; // True iff ray from ref pt to ext win hits an exterior obstruction
1124 : Real64 TVISIntWin; // Visible transmittance of int win at COSBIntWin for light from ext win
1125 : Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
1126 : int WinEl; // window elements counter
1127 :
1128 28 : Vector3<Real64> W2;
1129 28 : Vector3<Real64> W3;
1130 28 : Vector3<Real64> W21;
1131 28 : Vector3<Real64> W23;
1132 28 : Vector3<Real64> RREF2;
1133 28 : Vector3<Real64> RWIN;
1134 28 : Vector3<Real64> RWIN2;
1135 28 : Vector3<Real64> Ray;
1136 28 : Vector3<Real64> WNORM2;
1137 28 : Vector3<Real64> VIEWVC;
1138 28 : Vector3<Real64> U2;
1139 28 : Vector3<Real64> U21;
1140 28 : Vector3<Real64> U23;
1141 28 : Vector3<Real64> VIEWVC2;
1142 :
1143 28 : if (dl->mapFirstTime && (int)dl->illumMaps.size() > 0) {
1144 6 : IL = -999;
1145 15 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
1146 9 : IL = max(IL, dl->illumMaps(MapNum).TotalMapRefPoints);
1147 : }
1148 6 : dl->MapErrIndex.dimension(IL, state.dataSurface->TotSurfaces, 0);
1149 6 : dl->mapFirstTime = false;
1150 : }
1151 :
1152 28 : auto &illumMap = dl->illumMaps(mapNum);
1153 28 : int enclNum = illumMap.enclIndex;
1154 28 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
1155 :
1156 : // Azimuth of view vector in absolute coord sys - set to zero here, because glare isn't calculated for map points
1157 : // but these are arguments to some of the functions that are shared with regular reference points, so initalize here.
1158 28 : Real64 AZVIEW = 0.0;
1159 : // View vector components in absolute coord sys
1160 28 : VIEWVC = {0.0, 0.0, 0.0};
1161 :
1162 28 : numRefPts = illumMap.TotalMapRefPoints;
1163 28 : int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
1164 28 : int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
1165 :
1166 2278 : for (auto &refPt : illumMap.refPts) {
1167 2250 : refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
1168 15950 : for (int iExtWin = 1; iExtWin <= numExtWins; ++iExtWin) {
1169 13700 : refPt.winLums(iExtWin) = {0.0, 0.0};
1170 : }
1171 : }
1172 :
1173 28 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
1174 28 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
1175 :
1176 700 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
1177 672 : auto &daylFacHr = illumMap.daylFac[iHr];
1178 4224 : for (int iWin = 1; iWin <= numExtWins; ++iWin) {
1179 332352 : for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
1180 986400 : for (int iSlatAng = 1; iSlatAng <= numSlatAngs; ++iSlatAng) {
1181 2630400 : daylFacHr(iWin, iRefPt, iSlatAng) = Illums();
1182 : }
1183 : }
1184 : }
1185 : }
1186 :
1187 2278 : for (int IL = 1; IL <= numRefPts; ++IL) {
1188 2250 : auto &refPt = illumMap.refPts(IL);
1189 2250 : Vector3<Real64> RREF = refPt.absCoords;
1190 :
1191 : // -------------
1192 : // ---------- WINDOW LOOP ----------
1193 : // -------------
1194 :
1195 15950 : for (int loopwin = 1; loopwin <= numExtWins; ++loopwin) {
1196 :
1197 : // daylightingCtrlNum parameter is unused for map points
1198 13700 : FigureDayltgCoeffsAtPointsSetupForWindow(state,
1199 : 0,
1200 : IL,
1201 : loopwin,
1202 : CalledFor::MapPoint,
1203 : RREF,
1204 : VIEWVC,
1205 : IWin,
1206 : IWin2,
1207 : NWX,
1208 : NWY,
1209 : W2,
1210 : W3,
1211 : W21,
1212 : W23,
1213 : LSHCAL,
1214 : InShelfSurf,
1215 : ICtrl,
1216 : ShType,
1217 : BlNum,
1218 : WNORM2,
1219 : extWinType,
1220 : IConst,
1221 : RREF2,
1222 : DWX,
1223 : DWY,
1224 : DAXY,
1225 : U2,
1226 : U23,
1227 : U21,
1228 : VIEWVC2,
1229 : is_Rectangle,
1230 : is_Triangle,
1231 : mapNum);
1232 : // ---------------------
1233 : // ---------- WINDOW ELEMENT LOOP ----------
1234 : // ---------------------
1235 13700 : WinEl = 0;
1236 :
1237 90726 : for (int IX = 1; IX <= NWX; ++IX) {
1238 77026 : if (is_Rectangle) {
1239 77026 : NWYlim = NWY;
1240 0 : } else if (is_Triangle) {
1241 0 : NWYlim = NWY - IX + 1;
1242 : }
1243 :
1244 2309108 : for (int IY = 1; IY <= NWYlim; ++IY) {
1245 :
1246 2232082 : ++WinEl;
1247 :
1248 : // daylightingCtrlNum parameter is unused for map points
1249 2232082 : FigureDayltgCoeffsAtPointsForWindowElements(state,
1250 : 0,
1251 : IL,
1252 : loopwin,
1253 : CalledFor::MapPoint,
1254 : WinEl,
1255 : IWin,
1256 : IWin2,
1257 : IX,
1258 : IY,
1259 : SkyObstructionMult,
1260 : W2,
1261 : W21,
1262 : W23,
1263 : RREF,
1264 : NWYlim,
1265 : VIEWVC2,
1266 : DWX,
1267 : DWY,
1268 : DAXY,
1269 : U2,
1270 : U23,
1271 : U21,
1272 : RWIN,
1273 : RWIN2,
1274 : Ray,
1275 : PHRAY,
1276 : LSHCAL,
1277 : COSB,
1278 : ObTrans,
1279 : TVISB,
1280 : DOMEGA,
1281 : THRAY,
1282 : hitIntObs,
1283 : hitExtObs,
1284 : WNORM2,
1285 : extWinType,
1286 : IConst,
1287 : RREF2,
1288 : is_Triangle,
1289 : TVISIntWin,
1290 : TVISIntWinDisk,
1291 : mapNum);
1292 : // -------------------
1293 : // ---------- SUN POSITION LOOP ----------
1294 : // -------------------
1295 :
1296 : // Sun position counter. Used to avoid calculating various quantities
1297 : // that do not depend on sun position.
1298 2232082 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1299 2232082 : ISunPos = 0;
1300 55802050 : for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
1301 : // daylightingCtrlNum parameter is unused for map points
1302 53569968 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1303 : 0,
1304 : IL,
1305 : IX,
1306 : NWX,
1307 : IY,
1308 : NWYlim,
1309 : WinEl,
1310 : IWin,
1311 : IWin2,
1312 : IHR,
1313 : ISunPos,
1314 : SkyObstructionMult,
1315 : RWIN2,
1316 : Ray,
1317 : PHRAY,
1318 : LSHCAL,
1319 : InShelfSurf,
1320 : COSB,
1321 : ObTrans,
1322 : TVISB,
1323 : DOMEGA,
1324 : ICtrl,
1325 : ShType,
1326 : BlNum,
1327 : THRAY,
1328 : WNORM2,
1329 : extWinType,
1330 : IConst,
1331 : AZVIEW,
1332 : RREF2,
1333 : hitIntObs,
1334 : hitExtObs,
1335 : CalledFor::MapPoint,
1336 : TVISIntWin,
1337 : TVISIntWinDisk,
1338 : mapNum);
1339 : } // End of hourly sun position loop, IHR
1340 : } else {
1341 0 : if (state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1342 0 : ISunPos = 0;
1343 0 : dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = true;
1344 0 : } else if (state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1345 0 : ISunPos = 1;
1346 0 : } else if (!state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1347 0 : dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = false;
1348 0 : ISunPos = -1;
1349 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1350 0 : ISunPos = -1;
1351 : }
1352 : // daylightingCtrlNum parameter is unused for map points
1353 0 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1354 : 0,
1355 : IL,
1356 : IX,
1357 : NWX,
1358 : IY,
1359 : NWYlim,
1360 : WinEl,
1361 : IWin,
1362 : IWin2,
1363 0 : state.dataGlobal->HourOfDay,
1364 : ISunPos,
1365 : SkyObstructionMult,
1366 : RWIN2,
1367 : Ray,
1368 : PHRAY,
1369 : LSHCAL,
1370 : InShelfSurf,
1371 : COSB,
1372 : ObTrans,
1373 : TVISB,
1374 : DOMEGA,
1375 : ICtrl,
1376 : ShType,
1377 : BlNum,
1378 : THRAY,
1379 : WNORM2,
1380 : extWinType,
1381 : IConst,
1382 : AZVIEW,
1383 : RREF2,
1384 : hitIntObs,
1385 : hitExtObs,
1386 : CalledFor::MapPoint,
1387 : TVISIntWin,
1388 : TVISIntWinDisk,
1389 : mapNum);
1390 : }
1391 : } // End of window Y-element loop, IY
1392 : } // End of window X-element loop, IX
1393 :
1394 13700 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1395 : // Loop again over hourly sun positions and calculate daylight factors by adding
1396 : // direct and inter-reflected illum components, then dividing by exterior horiz illum.
1397 : // Also calculate corresponding glare factors.
1398 13700 : ILB = IL;
1399 342500 : for (int IHR = 1; IHR <= Constant::HoursInDay; ++IHR) {
1400 328800 : FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, IHR, IWin, loopwin, ICtrl);
1401 : } // End of sun position loop, IHR
1402 : } else {
1403 0 : ILB = IL;
1404 0 : FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, state.dataGlobal->HourOfDay, IWin, loopwin, ICtrl);
1405 : }
1406 :
1407 : } // End of window loop, loopwin - IWin
1408 :
1409 2250 : } // End of reference point loop, IL
1410 28 : }
1411 :
1412 28856 : void FigureDayltgCoeffsAtPointsSetupForWindow(EnergyPlusData &state,
1413 : int const daylightCtrlNum, // zero if called for map points
1414 : int const iRefPoint,
1415 : int const loopwin,
1416 : CalledFor const CalledFrom, // indicate which type of routine called this routine
1417 : Vector3<Real64> const &RREF, // Location of a reference point in absolute coordinate system
1418 : Vector3<Real64> const &VIEWVC, // View vector in absolute coordinate system
1419 : int &IWin,
1420 : int &IWin2,
1421 : int &NWX,
1422 : int &NWY,
1423 : Vector3<Real64> &W2, // Second vertex of window
1424 : Vector3<Real64> &W3, // Third vertex of window
1425 : Vector3<Real64> &W21, // Vector from window vertex 2 to window vertex 1
1426 : Vector3<Real64> &W23, // Vector from window vertex 2 to window vertex 3
1427 : int &LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
1428 : int &InShelfSurf, // Inside daylighting shelf surface number
1429 : int &ICtrl, // Window control counter
1430 : WinShadingType &ShType, // Window shading type
1431 : int &BlNum, // Window blind number
1432 : Vector3<Real64> &WNORM2, // Unit vector normal to window
1433 : ExtWinType &extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1434 : int &IConst, // Construction counter
1435 : Vector3<Real64> &RREF2, // Location of virtual reference point in absolute coordinate system
1436 : Real64 &DWX, // Horizontal dimension of window element (m)
1437 : Real64 &DWY, // Vertical dimension of window element (m)
1438 : Real64 &DAXY, // Area of window element
1439 : Vector3<Real64> &U2, // Second vertex of window for TDD:DOME (if exists)
1440 : Vector3<Real64> &U23, // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
1441 : Vector3<Real64> &U21, // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
1442 : Vector3<Real64> &VIEWVC2, // Virtual view vector in absolute coordinate system
1443 : bool &is_Rectangle, // True if window is rectangular
1444 : bool &is_Triangle, // True if window is triangular
1445 : int const MapNum)
1446 : {
1447 : // SUBROUTINE INFORMATION:
1448 : // AUTHOR B. Griffith
1449 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
1450 :
1451 : // PURPOSE OF THIS SUBROUTINE:
1452 : // collect code to setup calculations for each window for daylighting coefficients
1453 :
1454 : // METHODOLOGY EMPLOYED:
1455 : // switch as need to serve both reference points and map points based on calledFrom
1456 28856 : auto &dl = state.dataDayltg;
1457 :
1458 : int ShelfNum; // Daylighting shelf object number
1459 : int NDIVX; // Number of window x divisions for daylighting calc
1460 : int NDIVY; // Number of window y divisions for daylighting calc
1461 : Real64 ALF; // Distance from reference point to window plane (m)
1462 : Real64 D1a; // Projection of vector from window origin to reference
1463 : // on window X axis (m)
1464 : Real64 D1b; // Projection of vector from window origin to reference
1465 : // on window Y axis (m)
1466 : Real64 SolidAngExtWin; // Approx. solid angle subtended by an ext. window wrt ref pt
1467 : Real64 SolidAngMinIntWin; // Approx. smallest solid angle subtended by an int. window wrt ref pt
1468 : Real64 SolidAngRatio; // Ratio of SolidAngExtWin and SolidAngMinIntWin
1469 : Real64 SinCornerAng; // For triangle, sine of corner angle of window element
1470 :
1471 28856 : int zoneNum = 0; // zone number
1472 28856 : int enclNum = 0; // enclosure number
1473 :
1474 28856 : Vector3<Real64> W1 = {0.0, 0.0, 0.0};
1475 28856 : Vector3<Real64> WC = {0.0, 0.0, 0.0};
1476 :
1477 28856 : if (CalledFrom == CalledFor::RefPoint) {
1478 15156 : auto &daylCtrl = dl->daylightControl(daylightCtrlNum);
1479 15156 : daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAng = 0.0;
1480 15156 : daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAngWtd = 0.0;
1481 15156 : zoneNum = daylCtrl.zoneIndex;
1482 15156 : enclNum = daylCtrl.enclIndex;
1483 13700 : } else if (CalledFrom == CalledFor::MapPoint) {
1484 13700 : assert(MapNum > 0);
1485 13700 : auto const &illumMap = dl->illumMaps(MapNum);
1486 13700 : zoneNum = illumMap.zoneIndex;
1487 13700 : enclNum = illumMap.enclIndex;
1488 : }
1489 28856 : IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
1490 :
1491 28856 : auto &surf = state.dataSurface->Surface(IWin);
1492 28856 : auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
1493 :
1494 28856 : if (state.dataSurface->Surface(surf.BaseSurf).SolarEnclIndex == enclNum) {
1495 28652 : extWinType = ExtWinType::InZone;
1496 : } else {
1497 204 : extWinType = ExtWinType::AdjZone;
1498 : }
1499 :
1500 28856 : IConst = state.dataSurface->SurfActiveConstruction(IWin);
1501 :
1502 : // For thermochromic windows, the daylight and glare factors are calculated for a base window cosntruction
1503 : // at base TC layer temperature. During each time step calculations at DayltgInteriorIllum,
1504 : // DayltgInteriorMapIllum, and DayltgGlare, the daylight and glare factors are adjusted by the visible
1505 : // transmittance ratio = VT of actual TC window based on last hour TC layer temperature / VT of the base TC window
1506 28856 : if (state.dataConstruction->Construct(IConst).TCFlag == 1) {
1507 : // For thermochromic windows, use the base window construction at base temperature of the TC layer
1508 0 : IConst = state.dataConstruction->Construct(IConst).TCMasterConst;
1509 : }
1510 :
1511 28856 : ICtrl = surf.activeWindowShadingControl;
1512 28856 : ShType = WinShadingType::NoShade; // 'NOSHADE'
1513 28856 : BlNum = 0;
1514 : // ScNum = 0; //Unused Set but never used
1515 28856 : if (surf.HasShadeControl) ShType = state.dataSurface->WindowShadingControl(ICtrl).ShadingType;
1516 28856 : BlNum = state.dataSurface->SurfWinBlindNumber(IWin);
1517 : // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
1518 :
1519 28856 : ShelfNum = state.dataSurface->SurfDaylightingShelfInd(IWin);
1520 28856 : if (ShelfNum > 0) {
1521 210 : InShelfSurf = state.dataDaylightingDevicesData->Shelf(state.dataSurface->SurfDaylightingShelfInd(IWin))
1522 210 : .InSurf; // Inside daylighting shelf present if > 0
1523 : } else {
1524 28646 : InShelfSurf = 0;
1525 : }
1526 :
1527 28856 : is_Rectangle = false;
1528 28856 : is_Triangle = false;
1529 28856 : if (surf.Sides == 3) is_Triangle = true;
1530 28856 : if (surf.Sides == 4) is_Rectangle = true;
1531 :
1532 28856 : if (is_Rectangle) {
1533 : // Vertices of window (numbered counter-clockwise starting at upper left as viewed
1534 : // from inside of room). Assumes original vertices are numbered counter-clockwise from
1535 : // upper left as viewed from outside.
1536 28856 : W3 = surf.Vertex(2);
1537 28856 : W2 = surf.Vertex(3);
1538 28856 : W1 = surf.Vertex(4);
1539 0 : } else if (is_Triangle) {
1540 0 : W3 = surf.Vertex(2);
1541 0 : W2 = surf.Vertex(3);
1542 0 : W1 = surf.Vertex(1);
1543 : }
1544 :
1545 : // Shade/blind calculation flag
1546 28856 : LSHCAL = 0;
1547 :
1548 : // Visible transmittance at normal incidence
1549 28856 : state.dataSurface->SurfWinVisTransSelected(IWin) =
1550 28856 : General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
1551 : // For windows with switchable glazing, ratio of visible transmittance at normal
1552 : // incidence for fully switched (dark) state to that of unswitched state
1553 28856 : state.dataSurface->SurfWinVisTransRatio(IWin) = 1.0;
1554 28856 : if (ICtrl > 0) {
1555 12662 : if (ShType == WinShadingType::SwitchableGlazing) {
1556 12562 : int IConstShaded = surf.activeShadedConstruction; // Shaded construction counter
1557 12562 : state.dataSurface->SurfWinVisTransRatio(IWin) =
1558 12562 : General::SafeDivide(General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef),
1559 12562 : General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef));
1560 : }
1561 : }
1562 :
1563 : // Unit vectors from window vertex 2 to 1 and 2 to 3,
1564 : // center point of window, and vector from ref pt to center of window
1565 28856 : W21 = W1 - W2;
1566 28856 : W23 = W3 - W2;
1567 28856 : Real64 HW = W21.magnitude();
1568 28856 : Real64 WW = W23.magnitude();
1569 28856 : if (is_Rectangle) {
1570 28856 : WC = W2 + (W23 + W21) / 2.0;
1571 0 : } else if (is_Triangle) {
1572 0 : WC = W2 + (W23 + W21) / 3.0;
1573 : }
1574 28856 : state.dataSurface->SurfaceWindow(IWin).WinCenter = WC;
1575 28856 : Vector3<Real64> REFWC = WC - RREF;
1576 : // Unit vectors
1577 28856 : W21 /= HW;
1578 28856 : W23 /= WW;
1579 :
1580 : // Unit vector normal to window (pointing away from room)
1581 28856 : Vector3<Real64> WNORM = surf.lcsz;
1582 :
1583 : // Initialize number of window elements
1584 28856 : NDIVX = 40;
1585 28856 : NDIVY = 40;
1586 :
1587 : // Distance from ref point to window plane
1588 28856 : ALF = std::abs(dot(WNORM, REFWC));
1589 28856 : if (CalledFrom == CalledFor::RefPoint) {
1590 : // Check if ref point to close to window due to input error (0.1524 m below is 0.5 ft)
1591 15156 : if (ALF < 0.1524 && extWinType == ExtWinType::InZone) {
1592 : // Ref pt is close to window plane. Get vector from window
1593 : // origin to projection of ref pt on window plane.
1594 0 : Vector3<Real64> W2REF = RREF + ALF * WNORM - W2;
1595 :
1596 0 : D1a = dot(W2REF, W23);
1597 0 : D1b = dot(W2REF, W21);
1598 :
1599 : // ! Error message if ref pt is too close to window.
1600 0 : if (D1a > 0.0 && D1b > 0.0 && D1b <= HW && D1a <= WW) {
1601 0 : ShowSevereError(
1602 : state,
1603 0 : format("CalcDaylightCoeffRefPoints: Daylighting calculation cannot be done for Daylighting:Controls={} because reference point "
1604 : "#{} is less than 0.15m (6\") from window plane {}",
1605 0 : dl->daylightControl(daylightCtrlNum).Name,
1606 : iRefPoint,
1607 0 : surf.Name));
1608 0 : ShowContinueError(state, format("Distance=[{:.5R}]. This is too close; check position of reference point.", ALF));
1609 0 : ShowFatalError(state, "Program terminates due to preceding condition.");
1610 : }
1611 15156 : } else if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
1612 0 : if (dl->RefErrIndex(iRefPoint, IWin) == 0) { // only show error message once
1613 0 : ShowWarningError(state,
1614 0 : format("CalcDaylightCoeffRefPoints: For Daylghting:Controls=\"{}\" External Window=\"{}\"in Zone=\"{}\" reference "
1615 : "point is less than 0.15m (6\") from window plane ",
1616 0 : dl->daylightControl(daylightCtrlNum).Name,
1617 0 : surf.Name,
1618 0 : state.dataHeatBal->Zone(surf.Zone).Name));
1619 0 : ShowContinueError(state,
1620 0 : format("Distance=[{:.1R} m] to ref point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Daylighting Calcs may result.",
1621 : ALF,
1622 0 : RREF.x,
1623 0 : RREF.y,
1624 0 : RREF.z));
1625 0 : dl->RefErrIndex(iRefPoint, IWin) = 1;
1626 : }
1627 : }
1628 13700 : } else if (CalledFrom == CalledFor::MapPoint) {
1629 13700 : if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
1630 0 : if (dl->MapErrIndex(iRefPoint, IWin) == 0) { // only show error message once
1631 0 : ShowWarningError(state,
1632 0 : format("CalcDaylightCoeffMapPoints: For Zone=\"{}\" External Window=\"{}\"in Zone=\"{}\" map point is less than "
1633 : "0.15m (6\") from window plane ",
1634 0 : state.dataHeatBal->Zone(zoneNum).Name,
1635 0 : surf.Name,
1636 0 : state.dataHeatBal->Zone(surf.Zone).Name));
1637 0 : ShowContinueError(
1638 : state,
1639 0 : format("Distance=[{:.1R} m] map point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Map Calcs may result.", ALF, RREF.x, RREF.y, RREF.z));
1640 0 : dl->MapErrIndex(iRefPoint, IWin) = 1;
1641 : }
1642 : }
1643 : }
1644 : // Number of window elements in X and Y for daylighting calculation
1645 28856 : if (ALF > 0.1524) {
1646 27576 : NDIVX = 1 + int(4.0 * WW / ALF);
1647 27576 : NDIVY = 1 + int(4.0 * HW / ALF);
1648 : }
1649 :
1650 28856 : if (extWinType == ExtWinType::AdjZone) {
1651 : // Adjust number of exterior window elements to give acceptable number of rays through
1652 : // interior windows in the zone (for accuracy of interior window daylighting calculation)
1653 204 : SolidAngExtWin = General::SafeDivide(((surf.Area + state.dataSurface->SurfWinDividerArea(IWin)) / surf.Multiplier), pow_2(ALF));
1654 204 : SolidAngMinIntWin = dl->enclDaylight(enclNum).MinIntWinSolidAng;
1655 204 : SolidAngRatio = max(1.0, SolidAngExtWin / SolidAngMinIntWin);
1656 204 : NDIVX *= std::sqrt(SolidAngRatio);
1657 204 : NDIVY *= std::sqrt(SolidAngRatio);
1658 : }
1659 :
1660 28856 : NWX = min(40, NDIVX);
1661 28856 : NWY = min(40, NDIVY);
1662 :
1663 : // Discretization of triangle is simpler if NWX = NWY
1664 28856 : if (is_Triangle) {
1665 0 : NWX = max(NWX, NWY);
1666 0 : NWY = NWX;
1667 : }
1668 :
1669 : // Edge lengths of window elements
1670 28856 : DWX = WW / NWX;
1671 28856 : DWY = HW / NWY;
1672 :
1673 : // Azimuth and altitude of window normal
1674 28856 : surfWin.phi = std::asin(WNORM.z);
1675 28856 : surfWin.theta = (std::abs(WNORM.x) > 1.0e-5 || std::abs(WNORM.y) > 1.0e-5) ? std::atan2(WNORM.y, WNORM.x) : 0.0;
1676 :
1677 : // Recalculation of values for TDD:DOME
1678 28856 : if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
1679 :
1680 : // Look up the TDD:DOME object
1681 420 : int PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
1682 420 : IWin2 = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
1683 :
1684 420 : auto &surf2 = state.dataSurface->Surface(IWin2);
1685 420 : auto &surfWin2 = state.dataSurface->SurfaceWindow(IWin2);
1686 :
1687 : // Calculate reference point coords relative to the diffuser coordinate system
1688 : // W21, W23, and WNORM are the unit vectors
1689 420 : Vector3<Real64> REFD = {dot(REFWC, W21), dot(REFWC, W23), dot(REFWC, WNORM)};
1690 :
1691 : // Calculate view vector coords relative to the diffuser coordinate system
1692 420 : Vector3<Real64> VIEWVD = {dot(VIEWVC, W21), dot(VIEWVC, W23), dot(VIEWVC, WNORM)};
1693 :
1694 420 : Vector3<Real64> U3 = surf2.Vertex(2);
1695 420 : U2 = surf2.Vertex(3);
1696 420 : Vector3<Real64> U1;
1697 :
1698 420 : if (surf2.Sides == 4) {
1699 : // Vertices of window (numbered counter-clockwise starting
1700 : // at upper left as viewed from inside of room)
1701 : // Assumes original vertices are numbered counter-clockwise from
1702 : // upper left as viewed from outside.
1703 420 : U3 = surf2.Vertex(2);
1704 420 : U2 = surf2.Vertex(3);
1705 420 : U1 = surf2.Vertex(4);
1706 0 : } else if (surf2.Sides == 3) {
1707 0 : U3 = surf2.Vertex(2);
1708 0 : U2 = surf2.Vertex(3);
1709 0 : U1 = surf2.Vertex(1);
1710 : }
1711 :
1712 : // Unit vectors from window vertex 2 to 1 and 2 to 3,
1713 : // center point of window, and vector from ref pt to center of window
1714 420 : U21 = U1 - U2;
1715 420 : U23 = U3 - U2;
1716 420 : HW = U21.magnitude();
1717 420 : WW = U23.magnitude();
1718 420 : if (surf2.Sides == 4) {
1719 420 : WC = U2 + (U23 + U21) / 2.0;
1720 0 : } else if (surf2.Sides == 3) {
1721 0 : WC = U2 + (U23 + U21) / 3.0;
1722 : }
1723 420 : state.dataSurface->SurfaceWindow(IWin2).WinCenter = WC;
1724 : // Unit vectors
1725 420 : U21 /= HW;
1726 420 : U23 /= WW;
1727 :
1728 : // Unit vector normal to dome (pointing away from TDD)
1729 : // These are specific to the exterior.
1730 : // NOTE: Preserve WNORM for later in the code.
1731 420 : WNORM2 = cross(U21, U23).normalize();
1732 :
1733 : // Azimuth and altitude of dome normal
1734 : // These are specific to the exterior.
1735 420 : surfWin2.phi = std::asin(WNORM2.z);
1736 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;
1737 :
1738 : // Calculate new virtual reference point coords relative to dome coord system
1739 : // W21, W23, and WNORM2 are now the unit vectors for the dome coord system
1740 420 : REFWC = REFD.x * U21 + REFD.y * U23 + REFD.z * WNORM2;
1741 420 : RREF2 = WC - REFWC;
1742 :
1743 : // Calculate new virtual view vector coords relative to dome coord system
1744 420 : VIEWVC2 = VIEWVD.x * U21 + VIEWVD.y * U23 + VIEWVD.z * WNORM2;
1745 :
1746 : // Copy several values from the diffuser so that DayltgInterReflectedIllum works correctly
1747 : // These are specific to the interior.
1748 420 : surfWin2.rhoCeilingWall = surfWin.rhoCeilingWall;
1749 420 : surfWin2.rhoFloorWall = surfWin.rhoFloorWall;
1750 420 : surfWin2.fractionUpgoing = surfWin.fractionUpgoing;
1751 420 : surfWin2.glazedFrac = surfWin.glazedFrac;
1752 :
1753 420 : } else {
1754 : // This is not a TDD:DIFFUSER. Make sure nothing is messed up for a regular window.
1755 28436 : IWin2 = IWin;
1756 28436 : WNORM2 = WNORM;
1757 28436 : RREF2 = RREF;
1758 28436 : VIEWVC2 = VIEWVC;
1759 :
1760 28436 : U2 = W2;
1761 28436 : U21 = W21;
1762 28436 : U23 = W23;
1763 : }
1764 :
1765 : // Initialize bsdf daylighting coefficients here. Only one time initialization
1766 28856 : if (state.dataSurface->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
1767 18 : if (!state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized) {
1768 18 : int NRefPts = 0;
1769 18 : if (CalledFrom == CalledFor::MapPoint) {
1770 0 : NRefPts = dl->illumMaps(MapNum).TotalMapRefPoints;
1771 18 : } else if (CalledFrom == CalledFor::RefPoint) {
1772 18 : NRefPts = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
1773 : }
1774 18 : InitializeCFSDaylighting(state, daylightCtrlNum, IWin, NWX, NWY, RREF, NRefPts, iRefPoint, CalledFrom, MapNum);
1775 : // if ((WinEl == (NWX * NWY)).and.(CalledFrom == CalledForMapPoint).and.(NRefPts == iRefPoint)) then
1776 18 : if ((CalledFrom == CalledFor::MapPoint) && (NRefPts == iRefPoint)) {
1777 0 : state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized = true;
1778 : }
1779 : }
1780 : }
1781 :
1782 28856 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
1783 28856 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::HoursInDay;
1784 28856 : int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
1785 :
1786 721400 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
1787 2077632 : for (int iSlatAng = 1; iSlatAng <= numSlatAngs; ++iSlatAng) {
1788 : // Initialize sky and sun components of direct illuminance (arrays EDIRSK, EDIRSU, EDIRSUdisk)
1789 : // and average window luminance (arrays AVWLSK, AVWLSU, AVWLSUdisk), at ref pt.
1790 5540352 : dl->dirIllum(iHr, iSlatAng) = Illums();
1791 5540352 : dl->avgWinLum(iHr, iSlatAng) = Illums();
1792 : }
1793 : }
1794 :
1795 28856 : if (CalledFrom == CalledFor::RefPoint) {
1796 : // Initialize solid angle subtended by window wrt ref pt
1797 : // and solid angle weighted by glare position factor
1798 15156 : state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAng = 0.0;
1799 15156 : state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd = 0.0;
1800 : }
1801 : // Area of window element
1802 28856 : if (is_Rectangle) {
1803 28856 : DAXY = DWX * DWY;
1804 0 : } else if (is_Triangle) {
1805 0 : SinCornerAng = std::sqrt(1.0 - pow_2(dot(W21, W23)));
1806 0 : DAXY = DWX * DWY * SinCornerAng;
1807 : }
1808 28856 : }
1809 :
1810 2813576 : void FigureDayltgCoeffsAtPointsForWindowElements(
1811 : EnergyPlusData &state,
1812 : int const daylightCtrlNum, // Current daylighting control number (only used when called from RefPoint)
1813 : int const iRefPoint,
1814 : int const loopwin,
1815 : CalledFor const CalledFrom, // indicate which type of routine called this routine
1816 : int const WinEl, // Current window element number
1817 : int const IWin,
1818 : int const IWin2,
1819 : int const iXelement,
1820 : int const iYelement,
1821 : Real64 &SkyObstructionMult,
1822 : Vector3<Real64> const &W2, // Second vertex of window
1823 : Vector3<Real64> const &W21, // Vector from window vertex 2 to window vertex 1
1824 : Vector3<Real64> const &W23, // Vector from window vertex 2 to window vertex 3
1825 : Vector3<Real64> const &RREF, // Location of a reference point in absolute coordinate system
1826 : int const NWYlim, // For triangle, largest NWY for a given IX
1827 : Vector3<Real64> const &VIEWVC2, // Virtual view vector in absolute coordinate system
1828 : Real64 const DWX, // Horizontal dimension of window element (m)
1829 : Real64 const DWY, // Vertical dimension of window element (m)
1830 : Real64 const DAXY, // Area of window element
1831 : Vector3<Real64> const &U2, // Second vertex of window for TDD:DOME (if exists)
1832 : Vector3<Real64> const &U23, // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
1833 : Vector3<Real64> const &U21, // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
1834 : Vector3<Real64> &RWIN, // Center of a window element for TDD:DOME (if exists) in abs coord sys
1835 : Vector3<Real64> &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
1836 : Vector3<Real64> &Ray, // Unit vector along ray from reference point to window element
1837 : Real64 &PHRAY, // Altitude of ray from reference point to window element (radians)
1838 : int &LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
1839 : Real64 &COSB, // Cosine of angle between window outward normal and ray from reference point to window element
1840 : Real64 &ObTrans, // Product of solar transmittances of exterior obstructions hit by ray
1841 : Real64 &TVISB, // Visible transmittance of window for COSB angle of incidence (times light well
1842 : Real64 &DOMEGA, // Solid angle subtended by window element wrt reference point (steradians)
1843 : Real64 &THRAY, // Azimuth of ray from reference point to window element (radians)
1844 : bool &hitIntObs, // True iff interior obstruction hit
1845 : bool &hitExtObs, // True iff ray from ref pt to ext win hits an exterior obstruction
1846 : Vector3<Real64> const &WNORM2, // Unit vector normal to window
1847 : ExtWinType const extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1848 : int const IConst, // Construction counter
1849 : Vector3<Real64> const &RREF2, // Location of virtual reference point in absolute coordinate system
1850 : bool const is_Triangle,
1851 : Real64 &TVISIntWin, // Visible transmittance of int win at COSBIntWin for light from ext win
1852 : Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
1853 : int const MapNum)
1854 : {
1855 :
1856 : // SUBROUTINE INFORMATION:
1857 : // AUTHOR B. Griffith
1858 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
1859 :
1860 : // PURPOSE OF THIS SUBROUTINE:
1861 : // collect code to do calculations for each window element for daylighting coefficients
1862 :
1863 : // REFERENCES:
1864 : // switch as need to serve both reference points and map points based on calledFrom
1865 2813576 : auto &dl = state.dataDayltg;
1866 :
1867 : Real64 RR; // Distance from ref point to intersection of view vector
1868 : // and plane normal to view vector and window element (m)
1869 : Real64 ASQ; // Square of distance from above intersection to window element (m2)
1870 : Real64 YD; // Vertical displacement of window element wrt ref point
1871 :
1872 : Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
1873 : // exterior window element or between ref pt and sun
1874 :
1875 : // Local complex fenestration variables
1876 : Real64 TransBeam; // Obstructions transmittance for incoming BSDF rays (temporary variable)
1877 :
1878 2813576 : auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
1879 :
1880 2813576 : ++LSHCAL;
1881 2813576 : SkyObstructionMult = 1.0;
1882 :
1883 : // Center of win element in absolute coord sys
1884 2813576 : RWIN = W2 + (double(iXelement) - 0.5) * W23 * DWX + (double(iYelement) - 0.5) * W21 * DWY;
1885 :
1886 : // Center of win element on TDD:DOME in absolute coord sys
1887 : // If no TDD, RWIN2 = RWIN
1888 2813576 : RWIN2 = U2 + (double(iXelement) - 0.5) * U23 * DWX + (double(iYelement) - 0.5) * U21 * DWY;
1889 :
1890 : // Distance between ref pt and window element
1891 2813576 : Real64 DIS = distance(RWIN, RREF);
1892 :
1893 : // Unit vector along ray from ref pt to element
1894 2813576 : Ray = (RWIN - RREF) / DIS;
1895 :
1896 : // Cosine of angle between ray and window outward normal
1897 2813576 : COSB = dot(WNORM2, Ray);
1898 :
1899 : // If COSB > 0, direct light from window can reach ref pt. Otherwise go to loop
1900 : // over sun position and calculate inter-reflected component of illuminance
1901 2813576 : if (COSB <= 0.0) return;
1902 :
1903 : // Azimuth (-pi to pi) and altitude (-pi/2 to pi/2) of ray. Azimuth = 0 is along east.
1904 2813576 : PHRAY = std::asin(Ray.z);
1905 2813576 : if (std::abs(Ray.x) > 1.0e-5 || std::abs(Ray.y) > 1.0e-5) {
1906 2813571 : THRAY = std::atan2(Ray.y, Ray.x);
1907 : } else {
1908 5 : THRAY = 0.0;
1909 : }
1910 :
1911 : // Solid angle subtended by element wrt ref pt.
1912 2813576 : Real64 DAXY1 = DAXY; // For triangle, area of window element at end of column
1913 : // For triangle, at end of Y column only one half of parallelopiped's area contributes
1914 2813576 : if (is_Triangle && iYelement == NWYlim) DAXY1 = 0.5 * DAXY;
1915 2813576 : DOMEGA = DAXY1 * COSB / (DIS * DIS);
1916 :
1917 : // Calculate position factor (used in glare calculation) for this
1918 : // win element / ref pt / view-vector combination
1919 2813576 : Real64 POSFAC = 0.0;
1920 :
1921 : // Distance from ref pt to intersection of view vector and plane
1922 : // normal to view vector containing the window element
1923 :
1924 2813576 : if (CalledFrom == CalledFor::RefPoint) {
1925 581494 : RR = DIS * dot(Ray, VIEWVC2);
1926 581494 : if (RR > 0.0) {
1927 : // Square of distance from above intersection point to win element
1928 528371 : ASQ = DIS * DIS - RR * RR;
1929 : // Vertical displacement of win element wrt ref pt
1930 528371 : YD = RWIN2.z - RREF2.z;
1931 : // Horizontal and vertical displacement ratio and position factor
1932 528371 : Real64 XR = std::sqrt(std::abs(ASQ - YD * YD)) / RR;
1933 528371 : Real64 YR = std::abs(YD / RR);
1934 528371 : POSFAC = DayltgGlarePositionFactor(XR, YR);
1935 : }
1936 : }
1937 :
1938 2813576 : hitIntObs = false;
1939 2813576 : int IntWinHitNum = 0; // Surface number of interior window that is intersected
1940 2813576 : bool hitIntWin = false; // Ray from ref pt passes through interior window
1941 2813576 : TVISIntWinDisk = 0.0; // Init Value
1942 2813576 : TVISIntWin = 0.0;
1943 :
1944 2813576 : Vector3<Real64> HitPtIntWin = {0.0, 0.0, 0.0};
1945 2813576 : auto &surf = state.dataSurface->Surface(IWin);
1946 2813576 : if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
1947 : // Look up the TDD:DOME object
1948 420 : int PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
1949 : // Unshaded visible transmittance of TDD for a single ray from sky/ground element
1950 420 : TVISB = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
1951 :
1952 : } else { // Regular window
1953 2813156 : if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
1954 : // Vis trans of glass for COSB incidence angle
1955 2813012 : TVISB = General::POLYF(COSB, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
1956 : } else {
1957 : // Complex fenestration needs to use different equation for visible transmittance. That will be calculated later
1958 : // in the code since it depends on different incoming directions. For now, just put zero to differentiate from
1959 : // regular windows
1960 144 : TVISB = 0.0;
1961 : }
1962 2813156 : if (extWinType == ExtWinType::AdjZone) {
1963 23680 : int zoneNum = 0;
1964 23680 : if (CalledFrom == CalledFor::RefPoint) {
1965 40 : zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
1966 23640 : } else if (CalledFrom == CalledFor::MapPoint) {
1967 23640 : assert(MapNum > 0);
1968 23640 : zoneNum = dl->illumMaps(MapNum).zoneIndex;
1969 : }
1970 : // Does ray pass through an interior window in zone (ZoneNum) containing the ref point?
1971 47360 : for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
1972 23680 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
1973 26948 : for (int IntWin = thisSpace.WindowSurfaceFirst; IntWin <= thisSpace.WindowSurfaceLast; ++IntWin) {
1974 23680 : auto const &surfIntWin = state.dataSurface->Surface(IntWin);
1975 : // in develop this was Surface(IntWin).Class == SurfaceClass::Window && Surface(IntWin).ExtBoundCond >= 1
1976 23680 : if (surfIntWin.ExtBoundCond < 1) continue;
1977 :
1978 23680 : if (state.dataSurface->Surface(surfIntWin.ExtBoundCond).Zone != surf.Zone) continue;
1979 :
1980 23680 : hitIntWin = PierceSurface(state, IntWin, RREF, Ray, HitPtIntWin);
1981 23680 : if (hitIntWin) {
1982 20412 : IntWinHitNum = IntWin;
1983 20412 : COSBIntWin = dot(surfIntWin.OutNormVec, Ray);
1984 20412 : if (COSBIntWin <= 0.0) {
1985 0 : hitIntWin = false;
1986 0 : IntWinHitNum = 0;
1987 0 : continue;
1988 : }
1989 20412 : TVISIntWin = General::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWin.Construction).TransVisBeamCoef);
1990 20412 : TVISB *= TVISIntWin;
1991 20412 : break; // Ray passes thru interior window; exit from DO loop
1992 : }
1993 : }
1994 23680 : } // End of loop over surfaces in zone ZoneNum
1995 :
1996 23680 : if (!hitIntWin) {
1997 : // Ray does not pass through an int win in ZoneNum. Therefore, it hits the opaque part
1998 : // of a surface between ref point in ZoneNum and ext win element in adjacent zone.
1999 3268 : hitIntObs = true;
2000 : }
2001 : } // End of check if this is an ext win in an adjacent zone
2002 : } // End of check if TDD:Diffuser or regular exterior window or complex fenestration
2003 :
2004 : // Check for interior obstructions
2005 2813576 : if (extWinType == ExtWinType::InZone && !hitIntObs) {
2006 : // Check for obstruction between reference point and window element
2007 : // Returns hitIntObs = true iff obstruction is hit
2008 : // (Example of interior obstruction is a wall in an L-shaped room that lies
2009 : // between reference point and window.)
2010 2789896 : hitIntObs = DayltgHitInteriorObstruction(state, IWin, RREF, RWIN);
2011 : }
2012 :
2013 2813576 : if (extWinType == ExtWinType::AdjZone && IntWinHitNum > 0 && !hitIntObs) {
2014 : // Check for obstruction between ref point and interior window through which ray passes
2015 20412 : hitIntObs = DayltgHitInteriorObstruction(state, IntWinHitNum, RREF, HitPtIntWin);
2016 20412 : if (!hitIntObs) {
2017 : // Check for obstruction between intersection point on int window and ext win element
2018 20412 : hitIntObs = DayltgHitBetWinObstruction(state, IntWinHitNum, IWin, HitPtIntWin, RWIN);
2019 : }
2020 : }
2021 2813576 : if (CalledFrom == CalledFor::RefPoint) {
2022 : // Glare calculations only done for regular reference points, not for maps
2023 581494 : if (!hitIntObs) {
2024 581424 : if (extWinType == ExtWinType::InZone || (extWinType == ExtWinType::AdjZone && hitIntWin)) {
2025 : // Increment solid angle subtended by portion of window above ref pt
2026 581424 : surfWin.refPts(iRefPoint).solidAng += DOMEGA;
2027 581424 : dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAng += DOMEGA;
2028 : // Increment position-factor-modified solid angle
2029 581424 : surfWin.refPts(iRefPoint).solidAngWtd += DOMEGA * POSFAC;
2030 581424 : dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAngWtd += DOMEGA * POSFAC;
2031 : }
2032 : }
2033 : }
2034 2813576 : if (hitIntObs) ObTrans = 0.0;
2035 :
2036 2813576 : hitExtObs = false;
2037 2813576 : if (!hitIntObs) {
2038 : // No interior obstruction was hit.
2039 : // Check for exterior obstructions between window element and sky/ground.
2040 : // Get product of transmittances of obstructions hit by ray.
2041 : // ObTrans = 1.0 will be returned if no exterior obstructions are hit.
2042 :
2043 2778990 : if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
2044 : // the IHR (now HourOfDay) here is/was not correct, this is outside of hour loop
2045 : // the hour is used to query schedule for transmission , not sure what to do
2046 : // it will work for detailed and never did work correctly before.
2047 2778846 : ObTrans = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin2, RWIN2, Ray);
2048 2778846 : if (ObTrans < 1.0) hitExtObs = true;
2049 : } else {
2050 : // Transmittance from exterior obstruction surfaces is calculated here. This needs to be done for each timestep
2051 : // in order to account for changes in exterior surface transmittances
2052 144 : int CplxFenState = surfWin.ComplexFen.CurrentState;
2053 144 : auto &complexWinDayltgGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CplxFenState);
2054 144 : int NReflSurf = 0; // Number of blocked beams for complex fenestration
2055 144 : if (CalledFrom == CalledFor::RefPoint) {
2056 144 : NReflSurf = complexWinDayltgGeom.RefPoint(iRefPoint).NReflSurf(WinEl);
2057 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2058 0 : NReflSurf = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).NReflSurf(WinEl);
2059 : }
2060 : int RayIndex;
2061 144 : for (int ICplxFen = 1; ICplxFen <= NReflSurf; ++ICplxFen) {
2062 0 : if (CalledFrom == CalledFor::RefPoint) {
2063 0 : RayIndex = complexWinDayltgGeom.RefPoint(iRefPoint).RefSurfIndex(ICplxFen, WinEl);
2064 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2065 0 : RayIndex = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefSurfIndex(ICplxFen, WinEl);
2066 : }
2067 0 : Vector3<Real64> RayVector = state.dataBSDFWindow->ComplexWind(IWin).Geom(CplxFenState).sInc(RayIndex);
2068 : // It will get product of all transmittances
2069 0 : TransBeam = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin, RWIN, RayVector);
2070 : // IF (TransBeam > 0.0d0) ObTrans = TransBeam
2071 0 : if (CalledFrom == CalledFor::RefPoint) {
2072 0 : complexWinDayltgGeom.RefPoint(iRefPoint).TransOutSurf(ICplxFen, WinEl) = TransBeam;
2073 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2074 0 : complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).TransOutSurf(ICplxFen, WinEl) = TransBeam;
2075 : }
2076 0 : }
2077 : // This will avoid obstruction multiplier calculations for non-CFS window
2078 144 : ObTrans = 0.0;
2079 : }
2080 : }
2081 :
2082 2813576 : if (state.dataSurface->CalcSolRefl && PHRAY < 0.0 && ObTrans > 1.0e-6) {
2083 : // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
2084 : // by the ray. This effect is given by the ratio SkyObstructionMult =
2085 : // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
2086 : // This ratio is calculated for an isotropic sky.
2087 : // Ground point hit by the ray:
2088 220 : Real64 Alfa = std::acos(-Ray.z);
2089 220 : Real64 Beta = std::atan2(Ray.y, Ray.x);
2090 : // Distance between ground hit point and proj'n of center of window element onto ground (m)
2091 220 : Real64 HorDis = (RWIN2.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
2092 220 : Vector3<Real64> GroundHitPt = {RWIN2.x + HorDis * std::cos(Beta), RWIN2.y + HorDis * std::sin(Beta), state.dataSurface->GroundLevelZ};
2093 :
2094 220 : SkyObstructionMult =
2095 220 : CalcObstrMultiplier(state, GroundHitPt, DataSurfaces::AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
2096 220 : } // End of check if solar reflection calculation is in effect
2097 2813576 : } // FigureDayltgCoeffsAtPointsForWindowElements()
2098 :
2099 18 : void InitializeCFSDaylighting(EnergyPlusData &state,
2100 : int const daylightCtrlNum, // Current daylighting control number
2101 : int const IWin, // Complex fenestration number
2102 : int const NWX, // Number of horizontal divisions
2103 : int const NWY, // Number of vertical divisions
2104 : Vector3<Real64> const &RefPoint, // reference point coordinates
2105 : int const NRefPts, // Number of reference points
2106 : int const iRefPoint, // Reference points counter
2107 : CalledFor const CalledFrom,
2108 : int const MapNum)
2109 : {
2110 : // SUBROUTINE INFORMATION:
2111 : // AUTHOR Simon Vidanovic
2112 : // DATE WRITTEN April 2013
2113 :
2114 : // PURPOSE OF THIS SUBROUTINE:
2115 : // For incoming BSDF window direction calculates whether bin is coming from sky, ground or reflected surface.
2116 : // Routine also calculates intersection points with ground and exterior reflection surfaces.
2117 18 : auto &dl = state.dataDayltg;
2118 :
2119 : // Object Data
2120 18 : DataBSDFWindow::BSDFDaylghtPosition elPos; // altitude and azimuth of intersection element
2121 18 : Vector Vec; // temporary vector variable
2122 :
2123 18 : int NumOfWinEl = NWX * NWY; // Number of window elements
2124 :
2125 18 : auto &surf = state.dataSurface->Surface(IWin);
2126 18 : Real64 DWX = surf.Width / NWX; // Window element width
2127 18 : Real64 DWY = surf.Height / NWY; // Window element height
2128 :
2129 18 : int zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
2130 18 : Real64 AZVIEW = (dl->daylightControl(daylightCtrlNum).ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth +
2131 18 : state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) *
2132 18 : Constant::DegToRadians;
2133 :
2134 : // Perform necessary calculations for window coordinates and vectors. This will be used to calculate centroids for
2135 : // each window element
2136 18 : Vector3<Real64> W1 = {0.0, 0.0, 0.0};
2137 18 : Vector3<Real64> W2 = {0.0, 0.0, 0.0};
2138 18 : Vector3<Real64> W3 = {0.0, 0.0, 0.0};
2139 :
2140 18 : if (surf.Sides == 4) {
2141 18 : W3 = surf.Vertex(2);
2142 18 : W2 = surf.Vertex(3);
2143 18 : W1 = surf.Vertex(4);
2144 0 : } else if (surf.Sides == 3) {
2145 0 : W3 = surf.Vertex(2);
2146 0 : W2 = surf.Vertex(3);
2147 0 : W1 = surf.Vertex(1);
2148 : }
2149 :
2150 18 : Vector3<Real64> W21 = W1 - W2;
2151 18 : W21 /= surf.Height;
2152 18 : Vector3<Real64> W23 = W3 - W2;
2153 18 : W23 /= surf.Width;
2154 18 : Vector3<Real64> WNorm = surf.lcsz;
2155 :
2156 18 : Real64 WinElArea = DWX * DWY;
2157 18 : if (surf.Sides == 3) {
2158 0 : WinElArea *= std::sqrt(1.0 - pow_2(dot(W21, W23)));
2159 : }
2160 :
2161 18 : auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
2162 :
2163 18 : if (CalledFrom == CalledFor::MapPoint) {
2164 :
2165 0 : if (!allocated(complexWin.IlluminanceMap)) {
2166 0 : complexWin.IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
2167 : }
2168 :
2169 0 : AllocateForCFSRefPointsGeometry(complexWin.IlluminanceMap(iRefPoint, MapNum), NumOfWinEl);
2170 :
2171 18 : } else if (CalledFrom == CalledFor::RefPoint) {
2172 18 : if (!allocated(complexWin.RefPoint)) {
2173 2 : complexWin.RefPoint.allocate(NRefPts);
2174 : }
2175 :
2176 18 : AllocateForCFSRefPointsGeometry(complexWin.RefPoint(iRefPoint), NumOfWinEl);
2177 : }
2178 :
2179 : //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2180 : //! Allocation for each complex fenestration state reference points
2181 : //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2182 18 : if (!allocated(complexWin.DaylghtGeom)) {
2183 2 : complexWin.DaylghtGeom.allocate(state.dataBSDFWindow->ComplexWind(IWin).NumStates);
2184 : }
2185 :
2186 : // Calculation needs to be performed for each state
2187 36 : for (int CurFenState = 1; CurFenState <= complexWin.NumStates; ++CurFenState) {
2188 : // number of incident basis directions for current state
2189 18 : int NBasis = complexWin.Geom(CurFenState).Inc.NBasis;
2190 : // number of outgoing basis directions for current state
2191 18 : int NTrnBasis = complexWin.Geom(CurFenState).Trn.NBasis;
2192 :
2193 18 : if (CalledFrom == CalledFor::MapPoint) {
2194 0 : if ((int)dl->illumMaps.size() > 0) {
2195 : // illuminance map for each state
2196 0 : if (!allocated(complexWin.DaylghtGeom(CurFenState).IlluminanceMap)) {
2197 0 : complexWin.DaylghtGeom(CurFenState).IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
2198 : }
2199 :
2200 0 : AllocateForCFSRefPointsState(
2201 0 : state, complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum), NumOfWinEl, NBasis, NTrnBasis);
2202 :
2203 0 : InitializeCFSStateData(state,
2204 0 : complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum),
2205 : complexWin.IlluminanceMap(iRefPoint, MapNum),
2206 : daylightCtrlNum,
2207 : IWin,
2208 : RefPoint,
2209 : CurFenState,
2210 : NBasis,
2211 : NTrnBasis,
2212 : AZVIEW,
2213 : NWX,
2214 : NWY,
2215 : W2,
2216 : W21,
2217 : W23,
2218 : DWX,
2219 : DWY,
2220 : WNorm,
2221 : WinElArea);
2222 : }
2223 :
2224 18 : } else if (CalledFrom == CalledFor::RefPoint) {
2225 18 : if (!allocated(complexWin.DaylghtGeom(CurFenState).RefPoint)) {
2226 2 : complexWin.DaylghtGeom(CurFenState).RefPoint.allocate(NRefPts);
2227 : }
2228 :
2229 18 : AllocateForCFSRefPointsState(state, complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint), NumOfWinEl, NBasis, NTrnBasis);
2230 :
2231 36 : InitializeCFSStateData(state,
2232 18 : complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint),
2233 : complexWin.RefPoint(iRefPoint),
2234 : daylightCtrlNum,
2235 : IWin,
2236 : RefPoint,
2237 : CurFenState,
2238 : NBasis,
2239 : NTrnBasis,
2240 : AZVIEW,
2241 : NWX,
2242 : NWY,
2243 : W2,
2244 : W21,
2245 : W23,
2246 : DWX,
2247 : DWY,
2248 : WNorm,
2249 : WinElArea);
2250 : }
2251 : }
2252 18 : } // InitializeCFSDaylighting()
2253 :
2254 18 : void InitializeCFSStateData(EnergyPlusData &state,
2255 : DataBSDFWindow::BSDFRefPoints &StateRefPoint,
2256 : DataBSDFWindow::BSDFRefPointsGeomDescr &DaylghtGeomDescr,
2257 : [[maybe_unused]] int const daylightCtrlNum, // Current daylighting control number
2258 : int const iWin,
2259 : Vector3<Real64> const &RefPoint, // reference point
2260 : int const CurFenState,
2261 : int const NBasis,
2262 : int const NTrnBasis,
2263 : Real64 const AZVIEW,
2264 : int const NWX,
2265 : int const NWY,
2266 : Vector3<Real64> const &W2,
2267 : Vector3<Real64> const &W21,
2268 : Vector3<Real64> const &W23,
2269 : Real64 const DWX,
2270 : Real64 const DWY,
2271 : Vector3<Real64> const &WNorm, // unit vector from window (point towards outside)
2272 : Real64 const WinElArea)
2273 : {
2274 : // SUBROUTINE INFORMATION:
2275 : // AUTHOR Simon Vidanovic
2276 : // DATE WRITTEN June 2013
2277 :
2278 : // PURPOSE OF THIS SUBROUTINE:
2279 : // Initialize daylight state data for current
2280 :
2281 : // SUBROUTINE LOCAL VARIABLES
2282 : int curWinEl;
2283 : bool hit;
2284 : int TotHits;
2285 : Real64 DotProd; // Temporary variable for manipulating dot product .dot.
2286 : int NSky;
2287 : int NGnd;
2288 : int NReflSurf;
2289 : int MaxTotHits;
2290 : Real64 LeastHitDsq; // dist^2 from window element center to hit point
2291 : Real64 HitDsq;
2292 : Real64 TransRSurf;
2293 : int J;
2294 :
2295 18 : Vector3<Real64> RWin;
2296 18 : Vector3<Real64> V;
2297 18 : Vector3<Real64> GroundHitPt;
2298 :
2299 : // temporary arrays for surfaces
2300 : // Each complex fenestration state can have different number of basis elements
2301 : // This is the reason for making these temporary arrays local
2302 18 : Array1D_int TmpSkyInd(NBasis, 0); // Temporary sky index list
2303 18 : Array1D_int TmpGndInd(NBasis, 0); // Temporary gnd index list
2304 18 : Array1D<Real64> TmpGndMultiplier(NBasis, 0.0); // Temporary ground obstruction multiplier
2305 18 : Array1D_int TmpRfSfInd(NBasis, 0); // Temporary RefSurfIndex
2306 18 : Array1D_int TmpRfRyNH(NBasis, 0); // Temporary RefRayNHits
2307 36 : Array2D_int TmpHSurfNo(state.dataSurface->TotSurfaces, NBasis, 0); // Temporary HitSurfNo
2308 36 : Array2D<Real64> TmpHSurfDSq(state.dataSurface->TotSurfaces, NBasis, 0.0); // Temporary HitSurfDSq
2309 :
2310 : // Object Data
2311 18 : Vector3<Real64> Centroid; // current window element centroid
2312 18 : Vector3<Real64> HitPt; // surface hit point
2313 36 : Array1D<Vector3<Real64>> TmpGndPt(NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary ground intersection list
2314 36 : Array2D<Vector3<Real64>> TmpHitPt(state.dataSurface->TotSurfaces, NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary HitPt
2315 :
2316 18 : CFSRefPointPosFactor(state, RefPoint, StateRefPoint, iWin, CurFenState, NTrnBasis, AZVIEW);
2317 :
2318 18 : auto &surf = state.dataSurface->Surface(iWin);
2319 :
2320 18 : curWinEl = 0;
2321 : // loop through window elements. This will calculate sky, ground and reflection bins for each window element
2322 54 : for (int IX = 1; IX <= NWX; ++IX) {
2323 180 : for (int IY = 1; IY <= NWY; ++IY) {
2324 :
2325 144 : ++curWinEl;
2326 :
2327 : // centroid coordinates for current window element
2328 144 : Centroid = W2 + (double(IX) - 0.5) * W23 * DWX + (double(IY) - 0.5) * W21 * DWY;
2329 144 : RWin = Centroid;
2330 :
2331 144 : CFSRefPointSolidAngle(state, RefPoint, RWin, WNorm, StateRefPoint, DaylghtGeomDescr, iWin, CurFenState, NTrnBasis, curWinEl, WinElArea);
2332 :
2333 144 : NSky = 0;
2334 144 : NGnd = 0;
2335 144 : NReflSurf = 0;
2336 144 : MaxTotHits = 0;
2337 : // Calculation of potential surface obstruction for each incoming direction
2338 21024 : for (int IRay = 1; IRay <= NBasis; ++IRay) {
2339 :
2340 20880 : hit = false;
2341 20880 : TotHits = 0;
2342 187920 : for (int JSurf = 1; JSurf <= state.dataSurface->TotSurfaces; ++JSurf) {
2343 167040 : auto &surf2 = state.dataSurface->Surface(JSurf);
2344 :
2345 : // the following test will cycle on anything except exterior surfaces and shading surfaces
2346 167040 : if (surf2.HeatTransSurf && surf2.ExtBoundCond != ExternalEnvironment) continue;
2347 : // skip the base surface containing the window and any other subsurfaces of that surface
2348 41760 : if (JSurf == surf.BaseSurf || surf2.BaseSurf == surf.BaseSurf) continue;
2349 : // skip surfaces that face away from the window
2350 0 : DotProd = dot(state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), surf2.NewellSurfaceNormalVector);
2351 0 : if (DotProd >= 0) continue;
2352 0 : hit = PierceSurface(state, JSurf, Centroid, state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), HitPt);
2353 0 : if (!hit) continue; // Miss: Try next surface
2354 0 : if (TotHits == 0) {
2355 : // First hit for this ray
2356 0 : TotHits = 1;
2357 0 : ++NReflSurf;
2358 0 : TmpRfSfInd(NReflSurf) = IRay;
2359 0 : TmpRfRyNH(NReflSurf) = 1;
2360 0 : TmpHSurfNo(1, NReflSurf) = JSurf;
2361 0 : TmpHitPt(1, NReflSurf) = HitPt;
2362 0 : V = HitPt - Centroid; // vector array from window ctr to hit pt
2363 0 : LeastHitDsq = V.magnitude_squared(); // dist^2 window ctr to hit pt
2364 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
2365 0 : if (!surf2.HeatTransSurf && surf2.SchedShadowSurfIndex != 0) {
2366 0 : TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance, treat it here as completely transparent
2367 : } else {
2368 0 : TransRSurf = 0.0;
2369 : }
2370 : } else {
2371 0 : V = HitPt - Centroid;
2372 0 : HitDsq = V.magnitude_squared();
2373 0 : if (HitDsq >= LeastHitDsq) {
2374 0 : if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
2375 0 : J = TotHits + 1;
2376 0 : if (TotHits > 1) {
2377 0 : for (int I = 2; I <= TotHits; ++I) {
2378 0 : if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
2379 0 : J = I;
2380 0 : break;
2381 : }
2382 : }
2383 0 : if (!surf2.HeatTransSurf && surf2.SchedShadowSurfIndex == 0) {
2384 : // The new hit is opaque, so we can drop all the hits further away
2385 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
2386 0 : TmpHitPt(J, NReflSurf) = HitPt;
2387 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
2388 0 : TotHits = J;
2389 : } else {
2390 : // The new hit is scheduled (presumed transparent), so keep the more distant hits
2391 : // Note that all the hists in the list will be transparent except the last,
2392 : // which may be either transparent or opaque
2393 0 : if (TotHits >= J) {
2394 0 : for (int I = TotHits; I >= J; --I) {
2395 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
2396 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
2397 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
2398 : }
2399 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
2400 0 : TmpHitPt(J, NReflSurf) = HitPt;
2401 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
2402 0 : ++TotHits;
2403 : }
2404 : } // if (.NOT.Surface(JSurf)%HeatTransSurf .AND. Surface(JSurf)%SchedShadowSurfIndex == 0) then
2405 : } // if (TotHits > 1) then
2406 : } // if (TransRSurf > 0.0d0) then
2407 : } else { // if (HitDsq >= LeastHitDsq) then
2408 : // A new closest hit. If it is opaque, drop the current hit list,
2409 : // otherwise add it at the front
2410 0 : LeastHitDsq = HitDsq;
2411 0 : if (!surf2.HeatTransSurf && surf2.SchedShadowSurfIndex != 0) {
2412 0 : TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
2413 0 : for (int I = TotHits; I >= 1; --I) {
2414 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
2415 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
2416 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
2417 0 : ++TotHits;
2418 : }
2419 0 : } else {
2420 0 : TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
2421 0 : TotHits = 1;
2422 : }
2423 0 : TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
2424 0 : TmpHitPt(1, NReflSurf) = HitPt;
2425 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
2426 : }
2427 : }
2428 : } // do JSurf = 1, TotSurfaces
2429 20880 : if (TotHits <= 0) {
2430 20880 : auto &sIncRay = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay);
2431 : // This ray reached the sky or ground unobstructed
2432 20880 : if (sIncRay.z < 0.0) {
2433 : // A ground ray
2434 9216 : ++NGnd;
2435 9216 : TmpGndInd(NGnd) = IRay;
2436 9216 : TmpGndPt(NGnd).x = Centroid.x - (sIncRay.x / sIncRay.z) * Centroid.z;
2437 9216 : TmpGndPt(NGnd).y = Centroid.y - (sIncRay.y / sIncRay.z) * Centroid.z;
2438 9216 : TmpGndPt(NGnd).z = 0.0;
2439 :
2440 : // for solar reflectance calculations, need to precalculate obstruction multipliers
2441 9216 : if (state.dataSurface->CalcSolRefl) {
2442 9216 : GroundHitPt = TmpGndPt(NGnd);
2443 9216 : TmpGndMultiplier(NGnd) =
2444 9216 : CalcObstrMultiplier(state, GroundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
2445 : }
2446 : } else {
2447 : // A sky ray
2448 11664 : ++NSky;
2449 11664 : TmpSkyInd(NSky) = IRay;
2450 : }
2451 : } else {
2452 : // Save the number of hits for this ray
2453 0 : TmpRfRyNH(NReflSurf) = TotHits;
2454 : }
2455 20880 : MaxTotHits = max(MaxTotHits, TotHits);
2456 : } // do IRay = 1, ComplexWind(IWin)%Geom(CurFenState)%Inc%NBasis
2457 :
2458 : // Fill up state data for current window element data
2459 144 : StateRefPoint.NSky(curWinEl) = NSky;
2460 144 : StateRefPoint.SkyIndex({1, NSky}, curWinEl) = TmpSkyInd({1, NSky});
2461 :
2462 144 : StateRefPoint.NGnd(curWinEl) = NGnd;
2463 144 : StateRefPoint.GndIndex({1, NGnd}, curWinEl) = TmpGndInd({1, NGnd});
2464 144 : StateRefPoint.GndPt({1, NGnd}, curWinEl) = TmpGndPt({1, NGnd});
2465 144 : StateRefPoint.GndObstrMultiplier({1, NGnd}, curWinEl) = TmpGndMultiplier({1, NGnd});
2466 :
2467 144 : StateRefPoint.NReflSurf(curWinEl) = NReflSurf;
2468 144 : StateRefPoint.RefSurfIndex({1, NReflSurf}, curWinEl) = TmpRfSfInd({1, NReflSurf});
2469 144 : StateRefPoint.RefRayNHits({1, NReflSurf}, curWinEl) = TmpRfRyNH({1, NReflSurf});
2470 144 : StateRefPoint.HitSurfNo({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfNo({1, MaxTotHits}, {1, NReflSurf});
2471 144 : StateRefPoint.HitSurfDSq({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfDSq({1, MaxTotHits}, {1, NReflSurf});
2472 144 : StateRefPoint.HitPt({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHitPt({1, MaxTotHits}, {1, NReflSurf});
2473 : } // do IY = 1, NWY
2474 : } // do IX = 1, NWX
2475 18 : }
2476 :
2477 18 : void AllocateForCFSRefPointsState(
2478 : EnergyPlusData &state, DataBSDFWindow::BSDFRefPoints &StateRefPoint, int const NumOfWinEl, int const NBasis, int const NTrnBasis)
2479 : {
2480 : // SUBROUTINE INFORMATION:
2481 : // AUTHOR Simon Vidanovic
2482 : // DATE WRITTEN June 2013
2483 :
2484 : // PURPOSE OF THIS SUBROUTINE:
2485 : // Memory allocation for complex fenestration systems reference points geometry
2486 :
2487 18 : if (!allocated(StateRefPoint.NSky)) {
2488 2 : StateRefPoint.NSky.allocate(NumOfWinEl);
2489 2 : StateRefPoint.NSky = 0;
2490 : }
2491 :
2492 18 : if (!allocated(StateRefPoint.SkyIndex)) {
2493 2 : StateRefPoint.SkyIndex.allocate(NBasis, NumOfWinEl);
2494 2 : StateRefPoint.SkyIndex = 0;
2495 : }
2496 :
2497 18 : if (!allocated(StateRefPoint.NGnd)) {
2498 2 : StateRefPoint.NGnd.allocate(NumOfWinEl);
2499 2 : StateRefPoint.NGnd = 0;
2500 : }
2501 :
2502 18 : if (!allocated(StateRefPoint.GndIndex)) {
2503 2 : StateRefPoint.GndIndex.allocate(NBasis, NumOfWinEl);
2504 2 : StateRefPoint.GndIndex = 0;
2505 : }
2506 :
2507 18 : if (!allocated(StateRefPoint.GndPt)) {
2508 2 : StateRefPoint.GndPt.allocate(NBasis, NumOfWinEl);
2509 2 : StateRefPoint.GndPt = Vector(0.0, 0.0, 0.0);
2510 : }
2511 :
2512 18 : if (!allocated(StateRefPoint.GndObstrMultiplier)) {
2513 2 : StateRefPoint.GndObstrMultiplier.allocate(NBasis, NumOfWinEl);
2514 2 : StateRefPoint.GndObstrMultiplier = 0.0;
2515 : }
2516 :
2517 18 : if (!allocated(StateRefPoint.NReflSurf)) {
2518 2 : StateRefPoint.NReflSurf.allocate(NumOfWinEl);
2519 2 : StateRefPoint.NReflSurf = 0;
2520 : }
2521 :
2522 18 : if (!allocated(StateRefPoint.RefSurfIndex)) {
2523 2 : StateRefPoint.RefSurfIndex.allocate(NBasis, NumOfWinEl);
2524 2 : StateRefPoint.RefSurfIndex = 0;
2525 : }
2526 :
2527 18 : if (!allocated(StateRefPoint.TransOutSurf)) {
2528 2 : StateRefPoint.TransOutSurf.allocate(NBasis, NumOfWinEl);
2529 2 : StateRefPoint.TransOutSurf = 1.0;
2530 : }
2531 :
2532 18 : if (!allocated(StateRefPoint.RefRayNHits)) {
2533 2 : StateRefPoint.RefRayNHits.allocate(NBasis, NumOfWinEl);
2534 2 : StateRefPoint.RefRayNHits = 0;
2535 : }
2536 :
2537 18 : if (!allocated(StateRefPoint.HitSurfNo)) {
2538 2 : StateRefPoint.HitSurfNo.allocate(state.dataSurface->TotSurfaces, NBasis, NumOfWinEl);
2539 2 : StateRefPoint.HitSurfNo = 0;
2540 : }
2541 :
2542 18 : if (!allocated(StateRefPoint.HitSurfDSq)) {
2543 2 : StateRefPoint.HitSurfDSq.allocate(state.dataSurface->TotSurfaces, NBasis, NumOfWinEl);
2544 2 : StateRefPoint.HitSurfDSq = 0.0;
2545 : }
2546 :
2547 18 : if (!allocated(StateRefPoint.HitPt)) {
2548 2 : StateRefPoint.HitPt.allocate(state.dataSurface->TotSurfaces, NBasis, NumOfWinEl);
2549 2 : StateRefPoint.HitPt = Vector(0.0, 0.0, 0.0);
2550 : }
2551 :
2552 18 : if (!allocated(StateRefPoint.RefPointIndex)) {
2553 2 : StateRefPoint.RefPointIndex.allocate(NumOfWinEl);
2554 2 : StateRefPoint.RefPointIndex = 0;
2555 : }
2556 :
2557 18 : if (!allocated(StateRefPoint.RefPointIntersection)) {
2558 2 : StateRefPoint.RefPointIntersection.allocate(NTrnBasis);
2559 2 : StateRefPoint.RefPointIntersection = false;
2560 : }
2561 :
2562 18 : if (!allocated(StateRefPoint.RefPtIntPosFac)) {
2563 2 : StateRefPoint.RefPtIntPosFac.allocate(NTrnBasis);
2564 2 : StateRefPoint.RefPtIntPosFac = 0.0;
2565 : }
2566 18 : }
2567 :
2568 18 : void AllocateForCFSRefPointsGeometry(DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointsGeomDescr, int const NumOfWinEl)
2569 : {
2570 : // SUBROUTINE INFORMATION:
2571 : // AUTHOR Simon Vidanovic
2572 : // DATE WRITTEN June 2013
2573 :
2574 : // PURPOSE OF THIS SUBROUTINE:
2575 : // Memory allocation for complex fenestration systems reference points geometry
2576 :
2577 : // SUBROUTINE LOCAL VARIABLES
2578 :
2579 18 : if (!allocated(RefPointsGeomDescr.SolidAngle)) {
2580 2 : RefPointsGeomDescr.SolidAngle.allocate(NumOfWinEl);
2581 2 : RefPointsGeomDescr.SolidAngle = 0.0;
2582 : }
2583 :
2584 18 : if (!allocated(RefPointsGeomDescr.SolidAngleVec)) {
2585 2 : RefPointsGeomDescr.SolidAngleVec.allocate(NumOfWinEl);
2586 2 : RefPointsGeomDescr.SolidAngleVec = Vector(0.0, 0.0, 0.0);
2587 : }
2588 18 : }
2589 :
2590 144 : void CFSRefPointSolidAngle(EnergyPlusData &state,
2591 : Vector3<Real64> const &RefPoint,
2592 : Vector3<Real64> const &RWin,
2593 : Vector3<Real64> const &WNorm,
2594 : DataBSDFWindow::BSDFRefPoints &RefPointMap,
2595 : DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointGeomMap,
2596 : int const iWin,
2597 : int const CurFenState,
2598 : int const NTrnBasis,
2599 : int const curWinEl,
2600 : Real64 const WinElArea)
2601 : {
2602 : // SUBROUTINE INFORMATION:
2603 : // AUTHOR Simon Vidanovic
2604 : // DATE WRITTEN June 2013
2605 :
2606 : // PURPOSE OF THIS SUBROUTINE:
2607 : // Calculate position factor for given reference point.
2608 :
2609 : // calculate vector from center of window element to the current reference point
2610 144 : Vector3<Real64> Ray = RefPoint - RWin;
2611 :
2612 : // figure out outgoing beam direction from current reference point
2613 144 : Real64 BestMatch = 0.0;
2614 21024 : for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
2615 20880 : Vector3<Real64> const &V = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn(iTrnRay);
2616 20880 : Real64 temp = dot(Ray, V);
2617 20880 : if (temp > BestMatch) {
2618 702 : BestMatch = temp;
2619 702 : RefPointMap.RefPointIndex(curWinEl) = iTrnRay;
2620 : }
2621 : }
2622 :
2623 : // calculate solid view angle
2624 144 : Real64 Dist = Ray.magnitude();
2625 144 : Vector3<Real64> RayNorm = Ray / (-Dist);
2626 144 : RefPointGeomMap.SolidAngleVec(curWinEl) = RayNorm;
2627 144 : Real64 CosB = dot(WNorm, RayNorm);
2628 144 : RefPointGeomMap.SolidAngle(curWinEl) = WinElArea * CosB / (Dist * Dist);
2629 144 : }
2630 :
2631 18 : void CFSRefPointPosFactor(EnergyPlusData &state,
2632 : Vector3<Real64> const &RefPoint,
2633 : DataBSDFWindow::BSDFRefPoints &RefPointMap,
2634 : int const iWin,
2635 : int const CurFenState,
2636 : int const NTrnBasis,
2637 : Real64 const AZVIEW)
2638 : {
2639 : // SUBROUTINE INFORMATION:
2640 : // AUTHOR Simon Vidanovic
2641 : // DATE WRITTEN June 2013
2642 :
2643 : // PURPOSE OF THIS SUBROUTINE:
2644 : // Calculate position factor for given reference point.
2645 :
2646 18 : auto const &sTrn = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn;
2647 2628 : for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
2648 2610 : Vector3<Real64> V = sTrn(iTrnRay);
2649 2610 : V.negate();
2650 :
2651 2610 : Vector3<Real64> InterPoint;
2652 :
2653 2610 : bool hit = PierceSurface(state, iWin, RefPoint, V, InterPoint);
2654 2610 : if (hit) {
2655 234 : RefPointMap.RefPointIntersection(iTrnRay) = true;
2656 :
2657 234 : DataBSDFWindow::BSDFDaylghtPosition elPos = WindowComplexManager::DaylghtAltAndAzimuth(V);
2658 :
2659 234 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - elPos.Azimuth) + 0.001);
2660 234 : Real64 YR = std::tan(elPos.Altitude + 0.001);
2661 234 : RefPointMap.RefPtIntPosFac(iTrnRay) = DayltgGlarePositionFactor(XR, YR);
2662 : }
2663 2610 : }
2664 18 : } // CFSRefPointPosFactor()
2665 :
2666 15516 : Real64 CalcObstrMultiplier(EnergyPlusData &state,
2667 : Vector3<Real64> const &GroundHitPt, // Coordinates of point that ray hits ground (m)
2668 : int const AltSteps, // Number of steps in altitude angle for solar reflection calc
2669 : int const AzimSteps // Number of steps in azimuth angle of solar reflection calc
2670 : )
2671 : {
2672 :
2673 : // SUBROUTINE INFORMATION:
2674 : // AUTHOR Simon Vidanovic
2675 : // DATE WRITTEN April 2013, refactor from legacy code by Fred Winklemann
2676 :
2677 : // PURPOSE OF THIS SUBROUTINE:
2678 : // collect code to do obstruction multiplier from ground point
2679 :
2680 : // METHODOLOGY EMPLOYED:
2681 : // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
2682 : // Divide hemisphere centered at ground hit point into elements of altitude Phi and
2683 : // azimuth Theta and create upward-going ground ray unit vector at each Phi,Theta pair.
2684 : // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
2685 :
2686 : // Locals
2687 15516 : auto &dl = state.dataDayltg;
2688 :
2689 : bool hitObs; // True iff obstruction is hit
2690 :
2691 15516 : Vector3<Real64> URay; // Unit vector in (Phi,Theta) direction
2692 15516 : Vector3<Real64> ObsHitPt; // Unit vector in (Phi,Theta) direction
2693 :
2694 15516 : assert(AzimSteps <= DataSurfaces::AzimAngStepsForSolReflCalc);
2695 :
2696 15516 : Real64 DPhi = Constant::PiOvr2 / (AltSteps / 2.0); // Phi increment (radians)
2697 15516 : Real64 DTheta = Constant::Pi / AzimSteps; // Theta increment (radians)
2698 :
2699 : // Tuned Precompute Phi trig table
2700 15516 : if (AltSteps != dl->AltSteps_last) {
2701 18 : for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
2702 15 : Real64 Phi = (IPhi - 0.5) * DPhi;
2703 15 : dl->cos_Phi[IPhi] = std::cos(Phi);
2704 15 : dl->sin_Phi[IPhi] = std::sin(Phi);
2705 : }
2706 3 : dl->AltSteps_last = AltSteps;
2707 : }
2708 : // Tuned Precompute Theta trig table
2709 15516 : if (AzimSteps != dl->AzimSteps_last) {
2710 57 : for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
2711 54 : Real64 Theta = (ITheta - 0.5) * DTheta;
2712 54 : dl->cos_Theta[ITheta] = std::cos(Theta);
2713 54 : dl->sin_Theta[ITheta] = std::sin(Theta);
2714 : }
2715 3 : dl->AzimSteps_last = AzimSteps;
2716 : }
2717 :
2718 15516 : Real64 SkyGndObs = 0.0; // Obstructed sky irradiance at a ground point
2719 15516 : Real64 SkyGndUnObs = 0.0; // Unobstructed sky irradiance at a ground point
2720 :
2721 : // Altitude loop
2722 93096 : for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
2723 77580 : Real64 sinPhi = dl->sin_Phi[IPhi]; // sinPhi
2724 77580 : Real64 cosPhi = dl->cos_Phi[IPhi]; // cosPhi
2725 :
2726 : // Third component of ground ray unit vector in (Theta,Phi) direction
2727 77580 : URay.z = sinPhi;
2728 77580 : Real64 dOmegaGnd = cosPhi * DTheta * DPhi; // Solid angle element of ray from ground point (steradians)
2729 : // Cosine of angle of incidence of ground ray on ground plane
2730 77580 : Real64 CosIncAngURay = sinPhi;
2731 77580 : Real64 IncAngSolidAngFac = CosIncAngURay * dOmegaGnd / Constant::Pi; // CosIncAngURay*dOmegaGnd/Pi
2732 : // Azimuth loop
2733 1474020 : for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
2734 1396440 : URay.x = cosPhi * dl->cos_Theta[ITheta];
2735 1396440 : URay.y = cosPhi * dl->sin_Theta[ITheta];
2736 1396440 : SkyGndUnObs += IncAngSolidAngFac;
2737 : // Does this ground ray hit an obstruction?
2738 1396440 : hitObs = false;
2739 1396440 : if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
2740 :
2741 3919492 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
2742 2876654 : hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
2743 2876654 : if (hitObs) break;
2744 1396440 : }
2745 :
2746 : } else { // Surface octree search
2747 :
2748 : // Lambda function for the octree to test for surface hit
2749 0 : auto surfaceHit = [&GroundHitPt, &hitObs, &URay, &ObsHitPt](SurfaceData const &surface) -> bool {
2750 0 : if (surface.IsShadowPossibleObstruction) {
2751 0 : hitObs = PierceSurface(surface, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
2752 0 : return hitObs;
2753 : } else {
2754 0 : return false;
2755 : }
2756 0 : };
2757 :
2758 : // Check octree surface candidates until a hit is found, if any
2759 0 : Vector3<Real64> const URay_inv(SurfaceOctreeCube::safe_inverse(URay));
2760 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceRayIntersectsCube(GroundHitPt, URay, URay_inv, surfaceHit);
2761 0 : }
2762 :
2763 1396440 : if (hitObs) continue; // Obstruction hit
2764 : // Sky is hit
2765 1042838 : SkyGndObs += IncAngSolidAngFac;
2766 : } // End of azimuth loop
2767 : } // End of altitude loop
2768 :
2769 : // in case ground point is surrounded by obstructions (SkyGndUnObs == 0), then multiplier will be equal to zero
2770 : // This should not happen anyway because in that case ray would not be able to reach ground point
2771 31032 : return (SkyGndUnObs != 0.0) ? (SkyGndObs / SkyGndUnObs) : 0.0;
2772 15516 : } // CalcObstrMultiplier()
2773 :
2774 67525824 : void FigureDayltgCoeffsAtPointsForSunPosition(
2775 : EnergyPlusData &state,
2776 : int const daylightCtrlNum, // Daylighting control index
2777 : int const iRefPoint,
2778 : int const iXelement,
2779 : int const NWX, // Number of window elements in x direction for dayltg calc
2780 : int const iYelement,
2781 : int const NWY, // Number of window elements in y direction for dayltg calc
2782 : int const WinEl, // Current window element counter
2783 : int const IWin,
2784 : int const IWin2,
2785 : int const iHour,
2786 : int &ISunPos,
2787 : Real64 const SkyObstructionMult,
2788 : Vector3<Real64> const &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
2789 : Vector3<Real64> const &Ray, // Unit vector along ray from reference point to window element
2790 : Real64 const PHRAY, // Altitude of ray from reference point to window element (radians)
2791 : int const LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
2792 : int const InShelfSurf, // Inside daylighting shelf surface number
2793 : Real64 const COSB, // Cosine of angle between window outward normal and ray from reference point to window element
2794 : Real64 const ObTrans, // Product of solar transmittances of exterior obstructions hit by ray from reference point through a window element
2795 : Real64 const TVISB, // Visible transmittance of window for COSB angle of incidence (times light well efficiency, if appropriate)
2796 : Real64 const DOMEGA, // Solid angle subtended by window element wrt reference point (steradians)
2797 : int const ICtrl, // Window control counter
2798 : WinShadingType const ShType, // Window shading type
2799 : int const BlNum, // Window blind number
2800 : Real64 const THRAY, // Azimuth of ray from reference point to window element (radians)
2801 : Vector3<Real64> const &WNORM2, // Unit vector normal to window
2802 : ExtWinType const extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
2803 : int const IConst, // Construction counter
2804 : Real64 const AZVIEW, // Azimuth of view vector in absolute coord system for glare calculation (radians)
2805 : Vector3<Real64> const &RREF2, // Location of virtual reference point in absolute coordinate system
2806 : bool const hitIntObs, // True iff interior obstruction hit
2807 : bool const hitExtObs, // True iff ray from ref pt to ext win hits an exterior obstruction
2808 : CalledFor const CalledFrom, // indicate which type of routine called this routine
2809 : Real64 TVISIntWin, // Visible transmittance of int win at COSBIntWin for light from ext win
2810 : Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
2811 : int const MapNum)
2812 : {
2813 :
2814 : // SUBROUTINE INFORMATION:
2815 : // AUTHOR B. Griffith
2816 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
2817 :
2818 : // PURPOSE OF THIS SUBROUTINE:
2819 : // collect code for calculations sun position aspects for daylighting coefficients
2820 :
2821 : // METHODOLOGY EMPLOYED:
2822 : // switch as need to serve both reference points and map points based on calledFrom
2823 :
2824 67917672 : if (state.dataSurface->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
2825 :
2826 33061172 : auto &dl = state.dataDayltg;
2827 :
2828 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2829 33061172 : Vector3<Real64> RREF{0.0, 0.0, 0.0}; // Location of a reference point in absolute coordinate system //Autodesk Was used uninitialized:
2830 :
2831 : Real64 ObTransDisk; // Product of solar transmittances of exterior obstructions hit by ray from reference point to sun
2832 : Real64 LumAtHitPtFrSun; // Luminance at hit point of obstruction by reflection of direct light from sun (cd/m2)
2833 :
2834 : Real64 TVISS; // Direct solar visible transmittance of window at given angle of incidence
2835 : // (times light well efficiency, if appropriate)
2836 : Real64 XAVWL; // XAVWL*TVISS is contribution of window luminance from solar disk (cd/m2)
2837 :
2838 : bool hitObs; // True iff obstruction is hit
2839 : Real64 ObsVisRefl; // Visible reflectance of obstruction
2840 : Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by
2841 :
2842 : Real64 SpecReflectance; // Specular reflectance of a reflecting surface
2843 : Real64 TVisRefl; // Bare window vis trans for reflected beam
2844 : // (times light well efficiency, if appropriate)
2845 : Real64 PHSUNrefl; // Altitude angle of reflected sun (radians)
2846 : Real64 THSUNrefl; // Azimuth anggle of reflected sun (radians)
2847 :
2848 : Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
2849 : // exterior window element or between ref pt and sun
2850 : Real64 TVisIntWinMult; // Interior window vis trans multiplier for ext win in adjacent zone
2851 : Real64 TVisIntWinDiskMult; // Interior window vis trans solar disk multiplier for ext win in adj zone
2852 : Real64 WindowSolidAngleDaylightPoint;
2853 :
2854 33061172 : ++ISunPos;
2855 :
2856 : // Altitude of sun (degrees)
2857 33061172 : dl->sunAngles = dl->sunAnglesHr[iHour];
2858 :
2859 : // First time through, call routine to calculate inter-reflected illuminance
2860 : // at reference point and luminance of window with shade, screen or blind.
2861 :
2862 : // Rob/TH - Not sure whether this call is necessary for interior zones with interior windows only.
2863 : // new code would be -
2864 : // IF (LSHCAL == 1 .AND. ExtWinType /= AdjZoneExtWin) CALL DayltgInterReflectedIllum(ISunPos,IHR,ZoneNum,IWin2)
2865 33061172 : int enclNum = 0; // enclosure index
2866 33061172 : int zoneNum = 0; // zone index
2867 33061172 : if (CalledFrom == CalledFor::RefPoint) {
2868 6276188 : zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
2869 6276188 : enclNum = dl->daylightControl(daylightCtrlNum).enclIndex;
2870 26784984 : } else if (CalledFrom == CalledFor::MapPoint) {
2871 26784984 : assert(MapNum > 0);
2872 26784984 : zoneNum = dl->illumMaps(MapNum).zoneIndex;
2873 26784984 : enclNum = dl->illumMaps(MapNum).enclIndex;
2874 : }
2875 33061172 : if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
2876 33059684 : if (LSHCAL == 1) DayltgInterReflectedIllum(state, ISunPos, iHour, enclNum, IWin2);
2877 : } else {
2878 1488 : if (LSHCAL == 1) DayltgInterReflectedIllumComplexFenestration(state, IWin2, WinEl, iHour, daylightCtrlNum, iRefPoint, CalledFrom, MapNum);
2879 1488 : if (COSB <= 0.0) return;
2880 1488 : DayltgDirectIllumComplexFenestration(state, IWin, WinEl, iHour, iRefPoint, CalledFrom, MapNum);
2881 : // Call direct sun component only once since calculation is done for entire window
2882 1488 : if (WinEl == (NWX * NWY)) {
2883 186 : DayltgDirectSunDiskComplexFenestration(state, IWin2, iHour, iRefPoint, WinEl, AZVIEW, CalledFrom, MapNum);
2884 : }
2885 1488 : return;
2886 : }
2887 :
2888 : // Daylighting shelf simplification: The shelf completely blocks all view of the window,
2889 : // only interrelflected illumination is allowed (see DayltgInterReflectedIllum above).
2890 : // Everything else in this loop has to do with direct luminance from the window.
2891 33059684 : if (InShelfSurf > 0) return;
2892 :
2893 32669324 : if (COSB <= 0.0) return;
2894 :
2895 32669324 : auto &surfWin = state.dataSurface->SurfaceWindow(IWin);
2896 :
2897 32669324 : Illums XDirIllum;
2898 32669324 : Illums XAvgWinLum;
2899 32669324 : Real64 const Ray_3 = Ray.z;
2900 32669324 : Real64 const DOMEGA_Ray_3 = DOMEGA * Ray_3;
2901 :
2902 : // Add contribution of this window element to glare and to
2903 : // direct illuminance at reference point
2904 :
2905 : // The I,J,K indices for sky and sun components of direct illuminance
2906 : // (EDIRSK, EDIRSU) and average window luminance (AVWLSK, AVWLSU) are:
2907 : // I=1 for clear sky, =2 Clear turbid, =3 Intermediate, =4 Overcast;
2908 : // J=1 for bare window, =2 for window with shade or fixed slat-angle blind;
2909 : // = 2,3,...,MaxSlatAngs+1 for window with variable slat-angle blind;
2910 : // K = sun position index.
2911 :
2912 : // ----- CASE I -- BARE WINDOW (no shading device)
2913 :
2914 : // Beam solar and sky solar reflected from nearest obstruction.
2915 : // In the following hitIntObs == false ==> no interior obstructions hit, and
2916 : // hitExtObs == true ==> one or more exterior obstructions hit.
2917 32669324 : if (state.dataSurface->CalcSolRefl && !hitIntObs && hitExtObs) {
2918 : int NearestHitSurfNum; // Surface number of nearest obstruction
2919 0 : Vector3<Real64> NearestHitPt; // Hit point of ray on nearest obstruction
2920 : // One or more exterior obstructions was hit; get contribution of reflection
2921 : // from nearest obstruction.
2922 : // Find obstruction whose hit point is closest to this ray's window element
2923 0 : DayltgClosestObstruction(state, RWIN2, Ray, NearestHitSurfNum, NearestHitPt);
2924 0 : if (NearestHitSurfNum > 0) {
2925 :
2926 : // Beam solar reflected from nearest obstruction
2927 :
2928 0 : LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, iHour, Ray, NearestHitSurfNum, NearestHitPt);
2929 0 : dl->avgWinLum(iHour, 1).sun += LumAtHitPtFrSun * TVISB;
2930 0 : if (PHRAY >= 0.0) dl->dirIllum(iHour, 1).sun += LumAtHitPtFrSun * DOMEGA_Ray_3 * TVISB;
2931 :
2932 : // Sky solar reflected from nearest obstruction
2933 :
2934 0 : int const ObsConstrNum = state.dataSurface->SurfActiveConstruction(NearestHitSurfNum);
2935 0 : if (ObsConstrNum > 0) {
2936 : // Exterior building surface is nearest hit
2937 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
2938 : // Obstruction is not a window, i.e., is an opaque surface
2939 0 : ObsVisRefl = 1.0 - dynamic_cast<const Material::MaterialChild *>(
2940 0 : state.dataMaterial->Material(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1)))
2941 0 : ->AbsorpVisible;
2942 : } else {
2943 : // Obstruction is a window; assume it is bare
2944 0 : ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
2945 : }
2946 : } else {
2947 : // Shadowing surface is nearest hit
2948 0 : if (state.dataSurface->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
2949 : // This is a daylighting shelf, for which reflection is separately calculated
2950 0 : ObsVisRefl = 0.0;
2951 : } else {
2952 0 : ObsVisRefl = state.dataSurface->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
2953 0 : if (state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
2954 0 : ObsVisRefl +=
2955 0 : state.dataSurface->SurfShadowGlazingFrac(NearestHitSurfNum) *
2956 0 : state.dataConstruction->Construct(state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
2957 : }
2958 : }
2959 : // Surface number to use when obstruction is a shadowing surface
2960 0 : int NearestHitSurfNumX = NearestHitSurfNum;
2961 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
2962 : // The following gets the correct side of a shadowing surface for reflection.
2963 0 : if (state.dataSurface->Surface(NearestHitSurfNum).IsShadowing) {
2964 0 : if (dot(Ray, state.dataSurface->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
2965 : }
2966 0 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
2967 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
2968 0 : SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
2969 0 : state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
2970 : } else {
2971 0 : SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
2972 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, iHour, NearestHitSurfNumX) / Constant::Pi;
2973 : }
2974 0 : assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
2975 0 : auto &gilsk = dl->horIllum[iHour];
2976 0 : auto &avwlsk = dl->avgWinLum(iHour, 1);
2977 0 : auto &edirsk = dl->dirIllum(iHour, 1);
2978 :
2979 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
2980 0 : XAvgWinLum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum;
2981 0 : avwlsk.sky[iSky] += XAvgWinLum.sky[iSky] * TVISB;
2982 0 : if (PHRAY >= 0.0) {
2983 0 : XDirIllum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum * DOMEGA_Ray_3;
2984 0 : edirsk.sky[iSky] += XDirIllum.sky[iSky] * TVISB;
2985 : }
2986 : }
2987 : }
2988 0 : } // End of check if solar reflection calculation is in effect
2989 :
2990 32669324 : if (ObTrans > 1.e-6) {
2991 : // Ray did not hit an obstruction or the transmittance product of hit obstructions is non-zero.
2992 : // Contribution of sky or ground luminance in cd/m2
2993 31751288 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
2994 : // Make all transmitted light diffuse for a TDD with a bare diffuser
2995 2448 : assert(equal_dimensions(dl->avgWinLum, dl->winLum));
2996 2448 : assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
2997 2448 : auto &avwlsk = dl->avgWinLum(iHour, 1);
2998 2448 : auto &edirsk = dl->dirIllum(iHour, 1);
2999 2448 : auto &wlumsk = dl->winLum(iHour, 1);
3000 12240 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3001 9792 : avwlsk.sky[iSky] += wlumsk.sky[iSky];
3002 9792 : if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3;
3003 : }
3004 :
3005 2448 : dl->avgWinLum(iHour, 1).sun += dl->winLum(iHour, 1).sun;
3006 2448 : dl->avgWinLum(iHour, 1).sunDisk += dl->winLum(iHour, 1).sunDisk;
3007 :
3008 2448 : if (PHRAY > 0.0) dl->dirIllum(iHour, 1).sun += dl->winLum(iHour, 1).sun * DOMEGA_Ray_3;
3009 : } else { // Bare window
3010 31748840 : Vector3<Real64> GroundHitPt; // Coordinates of point that ray hits ground (m)
3011 : // Tuned Hoisted operations out of loop and linear indexing
3012 31748840 : if (state.dataSurface->CalcSolRefl) { // Coordinates of ground point hit by the ray
3013 7264 : Real64 Alfa = std::acos(-Ray_3);
3014 7264 : Real64 const Ray_1(Ray.x);
3015 7264 : Real64 const Ray_2(Ray.y);
3016 : // Beta = std::atan2( Ray_2, Ray_1 ); //Unused Tuning below eliminated use
3017 : // Distance between ground hit point and proj'n of center of window element onto ground (m)
3018 7264 : Real64 HorDis = (RWIN2.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
3019 7264 : GroundHitPt.z = state.dataSurface->GroundLevelZ;
3020 : // Tuned Replaced by below: sqrt is faster than sincos
3021 : // GroundHitPt( 1 ) = RWIN2( 1 ) + HorDis * std::cos( Beta );
3022 : // GroundHitPt( 2 ) = RWIN2( 2 ) + HorDis * std::sin( Beta );
3023 7264 : Real64 const Ray_r(std::sqrt(square(Ray_1) + square(Ray_2)));
3024 7264 : if (Ray_r > 0.0) {
3025 7264 : HorDis /= Ray_r;
3026 7264 : GroundHitPt.x = RWIN2.x + HorDis * Ray_1;
3027 7264 : GroundHitPt.y = RWIN2.y + HorDis * Ray_2;
3028 : } else { // Treat as angle==0
3029 0 : GroundHitPt.x = RWIN2.x + HorDis;
3030 0 : GroundHitPt.y = RWIN2.y;
3031 : }
3032 : }
3033 31748840 : Real64 const GILSK_mult((state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * ObTrans * SkyObstructionMult);
3034 31748840 : Real64 const TVISB_ObTrans(TVISB * ObTrans);
3035 31748840 : Real64 const AVWLSU_add(TVISB_ObTrans * dl->horIllum[iHour].sun * (state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi));
3036 31748840 : Vector3<Real64> const SUNCOS_iHour(state.dataSurface->SurfSunCosHourly(iHour));
3037 31748840 : assert(equal_dimensions(dl->dirIllum, dl->avgWinLum));
3038 31748840 : auto &edirsk = dl->dirIllum(iHour, 1);
3039 31748840 : auto &avwlsk = dl->avgWinLum(iHour, 1);
3040 :
3041 158744200 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3042 126995360 : if (PHRAY > 0.0) { // Ray heads upward to sky
3043 125938612 : Real64 ELUM = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), THRAY, PHRAY); // Sky or ground luminance (cd/m2)
3044 125938612 : XDirIllum.sky[iSky] = ELUM * DOMEGA_Ray_3;
3045 125938612 : Real64 DEDIR = XDirIllum.sky[iSky] * TVISB; // Illuminance contribution at reference point from window element (lux)
3046 125938612 : edirsk.sky[iSky] += DEDIR * ObTrans;
3047 125938612 : avwlsk.sky[iSky] += ELUM * TVISB_ObTrans;
3048 125938612 : XAvgWinLum.sky[iSky] = ELUM * ObTrans;
3049 : } else { // PHRAY <= 0.
3050 : // Ray heads downward to ground.
3051 : // Contribution from sky diffuse reflected from ground
3052 1056748 : XAvgWinLum.sky[iSky] = dl->horIllum[iHour].sky[iSky] * GILSK_mult;
3053 1056748 : avwlsk.sky[iSky] += TVISB * XAvgWinLum.sky[iSky];
3054 : // Contribution from beam solar reflected from ground (beam reaching ground point
3055 : // can be obstructed [SunObstructionMult < 1.0] if CalcSolRefl = .TRUE.)
3056 : } // End of check if ray is going up or down
3057 : } // for (iSky)
3058 :
3059 31748840 : if (PHRAY <= 0.0) {
3060 : // SunObstructionMult = 1.0; //Tuned
3061 264187 : if (state.dataSurface->CalcSolRefl) { // Coordinates of ground point hit by the ray
3062 : // Sun reaches ground point if vector from this point to the sun is unobstructed
3063 2270 : hitObs = false;
3064 2270 : Vector3<Real64> ObsHitPt; // Coordinates of hit point on an obstruction (m)
3065 10957 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
3066 8912 : hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, SUNCOS_iHour, ObsHitPt);
3067 8912 : if (hitObs) break;
3068 2270 : }
3069 : // if ( hitObs ) SunObstructionMult = 0.0;
3070 2270 : if (!hitObs) dl->avgWinLum(iHour, 1).sun += AVWLSU_add;
3071 2270 : } else {
3072 261917 : dl->avgWinLum(iHour, 1).sun += AVWLSU_add;
3073 : }
3074 : } // (PHRAY <= 0.0)
3075 31748840 : }
3076 : } // End of check if bare window or TDD:DIFFUSER
3077 :
3078 : // Illuminance from beam solar (without interior reflection)
3079 : // Just run this once on the last pass
3080 32669324 : if (iXelement == NWX && iYelement == NWY) { // Last pass
3081 :
3082 : // Beam solar reaching reference point directly without exterior reflection
3083 :
3084 : // Unit vector from ref. pt. to sun
3085 322356 : Vector3<Real64> RAYCOS;
3086 322356 : RAYCOS.x = dl->sunAngles.cosPhi * std::cos(dl->sunAngles.theta);
3087 322356 : RAYCOS.y = dl->sunAngles.cosPhi * std::sin(dl->sunAngles.theta);
3088 322356 : RAYCOS.z = dl->sunAngles.sinPhi;
3089 :
3090 : // Is sun on front side of exterior window?
3091 322356 : Real64 COSI = dot(WNORM2, RAYCOS); // Cosine of angle between direct sun and window outward normal
3092 : bool hit; // True if ray from ref point thru window element hits an obstruction
3093 : bool hitWin; // True if ray passes thru window
3094 322356 : Vector3<Real64> HP;
3095 322356 : if (COSI > 0.0) {
3096 :
3097 : // Does RAYCOS pass thru exterior window? HP is point that RAYCOS intersects window plane.
3098 243067 : hitWin = PierceSurface(state, IWin2, RREF2, RAYCOS, HP);
3099 : // True if ray from ref pt to sun hits an interior obstruction
3100 243067 : if (hitWin) {
3101 8571 : bool hitIntObsDisk = false;
3102 8571 : if (extWinType == ExtWinType::InZone) {
3103 : // Check for interior obstructions between reference point and HP.
3104 8345 : hitIntObsDisk = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
3105 : }
3106 8571 : ObTransDisk = 0.0; // Init value
3107 : // Init flag for vector from RP to sun passing through interior window
3108 8571 : bool hitIntWinDisk = false;
3109 8571 : if (extWinType == ExtWinType::AdjZone) { // This block is for RPs in zones with interior windows
3110 : // adjacent to zones with exterior windows
3111 : // Does RAYCOS pass through interior window in zone containing RP?
3112 : // Loop over zone surfaces looking for interior windows between reference point and sun
3113 : // Surface number of int window intersected by ray betw ref pt and sun
3114 : int IntWinDiskHitNum;
3115 : // Intersection point on an interior window for ray from ref pt to sun (m)
3116 226 : Vector3<Real64> HitPtIntWinDisk;
3117 226 : auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
3118 452 : for (int const spaceNum : thisZone.spaceIndexes) {
3119 226 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
3120 452 : for (int IntWinDisk = thisSpace.WindowSurfaceFirst, IntWinDisk_end = thisSpace.WindowSurfaceLast;
3121 452 : IntWinDisk <= IntWinDisk_end;
3122 : ++IntWinDisk) {
3123 226 : auto const &surfIntWinDisk = state.dataSurface->Surface(IntWinDisk);
3124 226 : if (surfIntWinDisk.ExtBoundCond < 1) continue;
3125 :
3126 226 : if (state.dataSurface->Surface(surfIntWinDisk.ExtBoundCond).Zone != state.dataSurface->Surface(IWin2).Zone) continue;
3127 :
3128 226 : hitIntWinDisk = PierceSurface(state, IntWinDisk, RREF, RAYCOS, HitPtIntWinDisk);
3129 226 : if (!hitIntWinDisk) continue;
3130 :
3131 0 : IntWinDiskHitNum = IntWinDisk;
3132 0 : COSBIntWin = dot(surfIntWinDisk.OutNormVec, RAYCOS);
3133 0 : if (COSBIntWin <= 0.0) {
3134 0 : hitIntWinDisk = false;
3135 0 : IntWinDiskHitNum = 0;
3136 0 : continue;
3137 : }
3138 0 : TVISIntWinDisk =
3139 0 : General::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWinDisk.Construction).TransVisBeamCoef);
3140 0 : break;
3141 : } // for (IntWinDisk)
3142 226 : } // for (spaceNum)
3143 :
3144 226 : if (!hitIntWinDisk) { // Vector from RP to sun does not pass through interior window
3145 226 : ObTransDisk = 0.0;
3146 226 : hit = true; //! fcw Is this needed?
3147 : }
3148 :
3149 : // Check for interior obstructions between ref point and interior window
3150 226 : hitIntObsDisk = false;
3151 226 : if (hitIntWinDisk) {
3152 0 : hitIntObsDisk = DayltgHitInteriorObstruction(state, IntWinDiskHitNum, RREF, HitPtIntWinDisk);
3153 : // If no obstruction between RP and hit int win, check for obstruction
3154 : // between int win and ext win
3155 0 : if (!hitIntObsDisk) {
3156 0 : hitIntObsDisk = DayltgHitBetWinObstruction(state, IntWinDiskHitNum, IWin2, HitPtIntWinDisk, HP);
3157 : }
3158 : }
3159 226 : if (hitIntObsDisk) ObTransDisk = 0.0;
3160 226 : } // case where RP is in zone with interior window adjacent to zone with exterior window
3161 :
3162 : // hitExtObsDisk = false; //Unused Set but never used
3163 : // RJH 08-25-07 hitIntWinDisk should not be reset to false here, and should be tested below.
3164 : // This is to correct logic flaw causing direct solar to reach adjacent zone refpt
3165 : // when vector to sun does not pass through interior window
3166 : // hitIntWinDisk = false
3167 8571 : if (!hitIntObsDisk) { // No interior obstruction was hit
3168 : // Net transmittance of exterior obstructions encountered by RAYCOS
3169 : // ObTransDisk = 1.0 will be returned if no exterior obstructions are hit.
3170 8567 : ObTransDisk = DayltgHitObstruction(state, iHour, IWin2, RREF2, RAYCOS);
3171 : // if ( ObTransDisk < 1.0 ) hitExtObsDisk = true; //Unused Set but never used
3172 : // RJH 08-26-07 However, if this is a case of interior window
3173 : // and vector to sun does not pass through interior window
3174 : // then reset ObTransDisk to 0.0 since it is the key test for adding
3175 : // contribution of sun to RP below.
3176 8567 : if ((extWinType == ExtWinType::AdjZone) && (!hitIntWinDisk)) {
3177 226 : ObTransDisk = 0.0;
3178 : }
3179 : }
3180 :
3181 : // PETER: need side wall mounted TDD to test this
3182 : // PETER: probably need to replace RREF2 with RWIN2
3183 : // PETER: need to check for interior obstructions too.
3184 :
3185 8571 : if (ObTransDisk > 1.e-6) {
3186 :
3187 : // Sun reaches reference point; increment illuminance.
3188 : // Direct normal illuminance is normalized to 1.0
3189 :
3190 7969 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
3191 : // No beam is transmitted. Takes care of TDD with a bare diffuser and all types of blinds.
3192 0 : TVISS = 0.0;
3193 : } else {
3194 : // Beam transmittance for bare window and all types of blinds
3195 7969 : TVISS = General::POLYF(COSI, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
3196 7969 : surfWin.lightWellEff;
3197 7969 : if (extWinType == ExtWinType::AdjZone && hitIntWinDisk) TVISS *= TVISIntWinDisk;
3198 : }
3199 :
3200 7969 : dl->dirIllum(iHour, 1).sunDisk = RAYCOS.z * TVISS * ObTransDisk; // Bare window
3201 :
3202 : std::array<Real64, (int)Material::MaxSlatAngs + 1> transBmBmMult;
3203 7969 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
3204 :
3205 7969 : if (ANY_BLIND(ShType)) {
3206 0 : auto const &blind = state.dataMaterial->Blind(BlNum);
3207 :
3208 0 : Real64 ProfAng = ProfileAngle(state, IWin, RAYCOS, blind.SlatOrientation);
3209 : // Contribution of beam passing through slats and reaching reference point
3210 0 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
3211 : // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3212 0 : Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? ((JB - 1) * Constant::Pi / (Material::MaxSlatAngs - 1))
3213 0 : : (blind.SlatAngle * Constant::DegToRadians);
3214 0 : transBmBmMult[JB] =
3215 0 : Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
3216 0 : dl->dirIllum(iHour, JB + 1).sunDisk = RAYCOS.z * TVISS * transBmBmMult[JB] * ObTransDisk;
3217 :
3218 : // do this only once for fixed slat blinds
3219 0 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
3220 : }
3221 7969 : } else if (ShType == WinShadingType::ExtScreen) {
3222 : // pass angle from sun to window normal here using PHSUN and THSUN from above and surface angles
3223 : // SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
3224 : // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
3225 0 : auto const *screen = dynamic_cast<Material::MaterialScreen *>(state.dataMaterial->Material(surfWin.screenNum));
3226 0 : assert(screen != nullptr);
3227 :
3228 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
3229 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
3230 : int ip1, ip2, it1, it2;
3231 : General::BilinearInterpCoeffs coeffs;
3232 0 : Material::NormalizePhiTheta(phi, theta);
3233 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
3234 0 : GetBilinearInterpCoeffs(
3235 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
3236 0 : transBmBmMult[1] = BilinearInterp(screen->btars[ip1][it1].BmTrans,
3237 0 : screen->btars[ip1][it2].BmTrans,
3238 0 : screen->btars[ip2][it1].BmTrans,
3239 0 : screen->btars[ip2][it2].BmTrans,
3240 : coeffs);
3241 :
3242 0 : dl->dirIllum(iHour, 2).sunDisk = RAYCOS.z * TVISS * transBmBmMult[1] * ObTransDisk;
3243 : }
3244 :
3245 7969 : if (CalledFrom == CalledFor::RefPoint) {
3246 : // Glare from solar disk
3247 :
3248 : // Position factor for sun (note that AZVIEW is wrt y-axis and THSUN is wrt
3249 : // x-axis of absolute coordinate system.
3250 5981 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
3251 5981 : Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
3252 : Real64 POSFAC =
3253 5981 : DayltgGlarePositionFactor(XR, YR); // Position factor for a window element / ref point / view vector combination
3254 :
3255 5981 : WindowSolidAngleDaylightPoint = state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd;
3256 :
3257 5981 : if (POSFAC != 0.0 && WindowSolidAngleDaylightPoint > 0.000001) {
3258 : // Increment window luminance. Luminance of solar disk (cd/m2)
3259 : // is 1.47*10^4*(direct normal solar illuminance) for direct normal solar
3260 : // illuminance in lux (lumens/m2). For purposes of calculating daylight factors
3261 : // direct normal solar illuminance = 1.0.
3262 : // Solid angle subtended by sun is 0.000068 steradians
3263 :
3264 3799 : XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) / std::pow(WindowSolidAngleDaylightPoint, 0.8);
3265 3799 : dl->avgWinLum(iHour, 1).sunDisk = XAVWL * TVISS * ObTransDisk; // Bare window
3266 :
3267 3799 : if (ANY_BLIND(ShType)) {
3268 0 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
3269 : // IF (.NOT. SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3270 0 : dl->avgWinLum(iHour, JB + 1).sunDisk = XAVWL * TVISS * transBmBmMult[JB] * ObTransDisk;
3271 0 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
3272 : }
3273 3799 : } else if (ShType == WinShadingType::ExtScreen) {
3274 0 : dl->avgWinLum(iHour, 2).sunDisk = XAVWL * TVISS * transBmBmMult[1] * ObTransDisk;
3275 : }
3276 : } // Position Factor
3277 : } // if (calledFrom == RefPt)
3278 : } // if (ObTransDisk > 1e-6) // Beam avoids all obstructions
3279 : } // if (hitWin)
3280 : } // if (COSI > 0.0) // Sun on front side
3281 :
3282 : // Beam solar reaching reference point after beam-beam (specular) reflection from
3283 : // an exterior surface
3284 :
3285 322356 : if (state.dataSurface->CalcSolRefl) {
3286 : // Receiving surface number corresponding this window
3287 908 : int RecSurfNum = state.dataSurface->SurfShadowRecSurfNum(IWin2);
3288 908 : if (RecSurfNum > 0) { // interior windows do not apply
3289 908 : if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
3290 : bool hitRefl; // True iff ray hits reflecting surface
3291 908 : Vector3<Real64> HitPtRefl; // Point that ray hits reflecting surface
3292 908 : Vector3<Real64> SunVecMir; // Sun ray mirrored in reflecting surface
3293 908 : Vector3<Real64> ReflNorm; // Normal vector to reflecting surface
3294 908 : Vector3<Real64> TransBmBmMultRefl;
3295 : // This window has associated obstructions that could reflect beam onto the window
3296 2724 : for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end;
3297 : ++loop) {
3298 1816 : int ReflSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop);
3299 1816 : int ReflSurfNumX = ReflSurfNum;
3300 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
3301 : // The following gets the correct side of a shadowing surface for reflection.
3302 1816 : if (state.dataSurface->Surface(ReflSurfNum).IsShadowing) {
3303 1816 : if (dot(RAYCOS, state.dataSurface->Surface(ReflSurfNum).OutNormVec) < 0.0) ReflSurfNumX = ReflSurfNum + 1;
3304 : }
3305 : // Require that the surface can have specular reflection
3306 3632 : if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window ||
3307 1816 : state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) > 0.0) {
3308 0 : ReflNorm = state.dataSurface->Surface(ReflSurfNumX).OutNormVec;
3309 : // Vector to sun that is mirrored in obstruction
3310 0 : SunVecMir = RAYCOS - 2.0 * dot(RAYCOS, ReflNorm) * ReflNorm;
3311 : // Skip if reflecting surface is not sunlit
3312 0 : if (state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNumX) < 0.01) continue;
3313 : // Skip if altitude angle of mirrored sun is negative since reflected sun cannot
3314 : // reach reference point in this case
3315 0 : if (SunVecMir.z <= 0.0) continue;
3316 : // Cosine of incidence angle of reflected beam on window
3317 0 : Real64 CosIncAngRec = dot(state.dataSurface->Surface(IWin2).OutNormVec, SunVecMir);
3318 0 : if (CosIncAngRec <= 0.0) continue;
3319 : // Does ray from ref. pt. along SunVecMir pass through window?
3320 0 : hitWin = PierceSurface(state, IWin2, RREF2, SunVecMir, HP);
3321 0 : if (!hitWin) continue; // Ray did not pass through window
3322 : // Check if this ray hits interior obstructions
3323 0 : hit = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
3324 0 : if (hit) continue; // Interior obstruction was hit
3325 : // Does ray hit this reflecting surface?
3326 0 : hitRefl = PierceSurface(state, ReflSurfNum, RREF2, SunVecMir, HitPtRefl);
3327 0 : if (!hitRefl) continue; // Ray did not hit this reflecting surface
3328 0 : Real64 ReflDistanceSq = distance_squared(HitPtRefl, RREF2);
3329 0 : Real64 ReflDistance = std::sqrt(ReflDistanceSq);
3330 : // Is ray from ref. pt. to reflection point (HitPtRefl) obstructed?
3331 0 : bool hitObsRefl = false;
3332 0 : Vector3<Real64> HitPtObs; // Hit point on obstruction
3333 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
3334 0 : loop2 <= loop2_end;
3335 : ++loop2) {
3336 0 : int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
3337 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == state.dataSurface->Surface(ReflSurfNum).BaseSurf) continue;
3338 0 : hitObs = PierceSurface(state, ObsSurfNum, RREF2, SunVecMir, ReflDistance, HitPtObs); // ReflDistance cutoff added
3339 0 : if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
3340 0 : if (distance_squared(HitPtObs, RREF2) < ReflDistanceSq) { // Distance squared from ref pt to reflection point
3341 0 : hitObsRefl = true;
3342 0 : break;
3343 : }
3344 : }
3345 : }
3346 0 : if (hitObsRefl) continue; // Obstruction closer than reflection pt. was hit; go to next obstruction
3347 : // There is no obstruction for this ray between ref pt and hit pt on reflecting surface.
3348 : // See if ray from hit pt on reflecting surface to original (unmirrored) sun position is obstructed
3349 0 : hitObs = false;
3350 0 : if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
3351 : // Reflecting surface is a window.
3352 : // Receiving surface number for this reflecting window.
3353 0 : int ReflSurfRecNum = state.dataSurface->SurfShadowRecSurfNum(ReflSurfNum);
3354 0 : if (ReflSurfRecNum > 0) {
3355 : // Loop over possible obstructions for this reflecting window
3356 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
3357 0 : loop2 <= loop2_end;
3358 : ++loop2) {
3359 : int const ObsSurfNum =
3360 0 : state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
3361 0 : hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
3362 0 : if (hitObs) break;
3363 : }
3364 : }
3365 : } else {
3366 : // Reflecting surface is a building shade
3367 0 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
3368 0 : if (ObsSurfNum == ReflSurfNum) continue;
3369 0 : hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
3370 0 : if (hitObs) break;
3371 0 : }
3372 : } // End of check if reflector is a window or shadowing surface
3373 :
3374 0 : if (hitObs) continue; // Obstruction hit between reflection hit point and sun; go to next obstruction
3375 :
3376 : // No obstructions. Calculate reflected beam illuminance at ref. pt. from this reflecting surface.
3377 0 : SpecReflectance = 0.0;
3378 0 : Real64 CosIncAngRefl = std::abs(dot(RAYCOS, ReflNorm)); // Cos of angle of incidence of beam on reflecting surface
3379 0 : if (state.dataSurface->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
3380 0 : int const ConstrNumRefl = state.dataSurface->SurfActiveConstruction(ReflSurfNum);
3381 : SpecReflectance =
3382 0 : General::POLYF(std::abs(CosIncAngRefl), state.dataConstruction->Construct(ConstrNumRefl).ReflSolBeamFrontCoef);
3383 : }
3384 0 : if (state.dataSurface->Surface(ReflSurfNum).IsShadowing && state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum) > 0)
3385 0 : SpecReflectance =
3386 0 : state.dataSurface->SurfShadowGlazingFrac(ReflSurfNum) *
3387 0 : General::POLYF(std::abs(CosIncAngRefl),
3388 0 : state.dataConstruction->Construct(state.dataSurface->SurfShadowGlazingConstruct(ReflSurfNum))
3389 0 : .ReflSolBeamFrontCoef);
3390 0 : TVisRefl = General::POLYF(CosIncAngRec, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
3391 0 : surfWin.lightWellEff;
3392 0 : dl->dirIllum(iHour, 1).sunDisk += SunVecMir.z * SpecReflectance * TVisRefl; // Bare window
3393 :
3394 0 : TransBmBmMultRefl = 0.0;
3395 0 : if (ANY_BLIND(ShType)) {
3396 0 : auto const &blind = state.dataMaterial->Blind(BlNum);
3397 0 : Real64 ProfAng = ProfileAngle(state, IWin, SunVecMir, blind.SlatOrientation);
3398 : // Contribution of reflected beam passing through slats and reaching reference point
3399 0 : Real64 const Pi_SlatAng_fac(Constant::Pi / (Material::MaxSlatAngs - 1));
3400 0 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
3401 : // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3402 0 : Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? (double(JB - 1) * Pi_SlatAng_fac)
3403 0 : : (blind.SlatAngle * Constant::DegToRadians);
3404 :
3405 0 : TransBmBmMultRefl(JB) =
3406 0 : Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
3407 0 : dl->dirIllum(iHour, JB + 1).sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl(JB);
3408 0 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
3409 : }
3410 0 : } else if (ShType == WinShadingType::ExtScreen) {
3411 : // pass angle from sun to window normal here using PHSUN and THSUN from above and
3412 : // surface angles SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
3413 : // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
3414 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(state.dataMaterial->Material(surfWin.screenNum));
3415 0 : assert(screen != nullptr);
3416 :
3417 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
3418 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
3419 : int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
3420 : General::BilinearInterpCoeffs coeffs;
3421 0 : Material::NormalizePhiTheta(phi, theta);
3422 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
3423 0 : General::GetBilinearInterpCoeffs(
3424 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
3425 :
3426 0 : TransBmBmMultRefl(1) = General::BilinearInterp(screen->btars[ip1][it1].BmTrans,
3427 0 : screen->btars[ip1][it2].BmTrans,
3428 0 : screen->btars[ip2][it1].BmTrans,
3429 0 : screen->btars[ip2][it2].BmTrans,
3430 : coeffs);
3431 0 : dl->dirIllum(iHour, 2).sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl(1);
3432 : } // End of check if window has a blind or screen
3433 :
3434 : // Glare from reflected solar disk
3435 :
3436 0 : PHSUNrefl = SunVecMir.z;
3437 0 : THSUNrefl = std::atan2(SunVecMir.y, SunVecMir.x);
3438 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - THSUNrefl) + 0.001);
3439 0 : Real64 YR = std::tan(PHSUNrefl + 0.001);
3440 0 : Real64 POSFAC = DayltgGlarePositionFactor(XR, YR);
3441 0 : if (POSFAC != 0.0 && state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd > 0.000001) {
3442 0 : XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) /
3443 0 : std::pow(state.dataSurface->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd, 0.8);
3444 0 : dl->avgWinLum(iHour, 1).sunDisk += XAVWL * TVisRefl * SpecReflectance; // Bare window
3445 0 : if (ANY_BLIND(ShType)) {
3446 0 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
3447 : // IF(.NOT. SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3448 0 : dl->avgWinLum(iHour, JB + 1).sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl(JB);
3449 0 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) break;
3450 : }
3451 0 : } else if (ShType == WinShadingType::ExtScreen) {
3452 0 : dl->avgWinLum(iHour, 2).sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl(1);
3453 : }
3454 : }
3455 0 : } // End of check that obstruction can specularly reflect
3456 : } // End of loop over obstructions associated with this window
3457 :
3458 908 : } // End of check if this window has associated obstructions
3459 : } // End of check to see if this is exterior type window
3460 : } // End of check if exterior reflection calculation is in effect
3461 :
3462 322356 : } // Last pass
3463 :
3464 32669324 : if ((ICtrl > 0 && (ANY_BLIND(ShType) || ANY_SHADE_SCREEN(ShType))) || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
3465 :
3466 : // ----- CASE II -- WINDOW WITH SCREEN, SHADE, BLIND, OR DIFFUSING WINDOW
3467 :
3468 : // Interior window visible transmittance multiplier for exterior window in adjacent zone
3469 12496 : TVisIntWinMult = 1.0;
3470 12496 : TVisIntWinDiskMult = 1.0;
3471 12496 : if (state.dataSurface->Surface(IWin).SolarEnclIndex != dl->daylightControl(daylightCtrlNum).enclIndex) {
3472 0 : TVisIntWinMult = TVISIntWin;
3473 0 : TVisIntWinDiskMult = TVISIntWinDisk;
3474 : }
3475 :
3476 12496 : Real64 const DOMEGA_Ray_3_TVisIntWinMult(DOMEGA_Ray_3 * TVisIntWinMult);
3477 24992 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
3478 : // Sometimes this is JB > 2 and sometimes it's JB > 1, what gives?
3479 24992 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
3480 :
3481 12496 : auto &wlumsk = dl->winLum(iHour, JB + 1);
3482 12496 : auto &edirsk = dl->dirIllum(iHour, JB + 1);
3483 12496 : auto &avwlsk = dl->avgWinLum(iHour, JB + 1);
3484 62480 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3485 : // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3486 49984 : avwlsk.sky[iSky] += wlumsk.sky[iSky] * TVisIntWinMult;
3487 49984 : if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3_TVisIntWinMult;
3488 : } // for (iSky)
3489 :
3490 12496 : dl->avgWinLum(iHour, JB + 1).sun += dl->winLum(iHour, JB + 1).sun * TVisIntWinMult;
3491 12496 : dl->avgWinLum(iHour, JB + 1).sunDisk += dl->winLum(iHour, JB + 1).sunDisk * TVisIntWinDiskMult;
3492 :
3493 12496 : if (PHRAY > 0.0) {
3494 9914 : dl->dirIllum(iHour, JB + 1).sun += dl->winLum(iHour, JB + 1).sun * DOMEGA_Ray_3_TVisIntWinMult;
3495 : }
3496 : } // for (JB)
3497 : }
3498 33061172 : } // FigureDayltgCoeffsAtPointsForSunPosition()
3499 :
3500 363744 : void FigureRefPointDayltgFactorsToAddIllums(EnergyPlusData &state,
3501 : int const daylightCtrlNum, // Current daylighting control number
3502 : int const iRefPoint,
3503 : int const iHour,
3504 : int &ISunPos,
3505 : int const IWin,
3506 : int const loopwin,
3507 : int const NWX, // Number of window elements in x direction for dayltg calc
3508 : int const NWY, // Number of window elements in y direction for dayltg calc
3509 : int const ICtrl // Window control counter
3510 : )
3511 : {
3512 :
3513 : // SUBROUTINE INFORMATION:
3514 : // AUTHOR B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann
3515 : // DATE WRITTEN Oct. 2012
3516 :
3517 : // PURPOSE OF THIS SUBROUTINE:
3518 : // calculation worker routine to fill daylighting coefficients
3519 :
3520 : // METHODOLOGY EMPLOYED:
3521 : // this version is just for reference points.
3522 :
3523 : // SUBROUTINE PARAMETER DEFINITIONS:
3524 363744 : Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating the daylighting and glare factors
3525 :
3526 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3527 363744 : auto &dl = state.dataDayltg;
3528 :
3529 363744 : if (state.dataSurface->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
3530 :
3531 160590 : ++ISunPos;
3532 :
3533 : // Altitude of sun (degrees)
3534 160590 : dl->sunAngles = dl->sunAnglesHr[iHour];
3535 :
3536 160590 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
3537 160590 : int const enclNum = state.dataSurface->Surface(IWin).SolarEnclIndex;
3538 :
3539 : // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
3540 : // 2 to Material::MaxSlatAngs+1 for variable slat-angle blind)
3541 :
3542 : // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
3543 : // related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
3544 : // and interior surfaces with high visible reflectance.
3545 : // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
3546 : // the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
3547 :
3548 160590 : auto &daylFacHr = thisDayltgCtrl.daylFac[iHour];
3549 :
3550 481770 : for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
3551 481770 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
3552 :
3553 321180 : auto const &gilsk = dl->horIllum[iHour];
3554 321180 : auto const &edirsk = dl->dirIllum(iHour, JSH);
3555 321180 : auto const &eintsk = dl->reflIllum(iHour, JSH);
3556 321180 : auto const &avwlsk = dl->avgWinLum(iHour, JSH);
3557 :
3558 321180 : auto &daylFac = daylFacHr(loopwin, iRefPoint, JSH);
3559 321180 : auto &illFac = daylFac[iLum_Illum];
3560 321180 : auto &sourceFac = daylFac[iLum_Source];
3561 321180 : auto &backFac = daylFac[iLum_Back];
3562 :
3563 1605900 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
3564 :
3565 1284720 : if (gilsk.sky[iSky] > tmpDFCalc) {
3566 1284720 : illFac.sky[iSky] = (edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky];
3567 1284720 : sourceFac.sky[iSky] = avwlsk.sky[iSky] / (NWX * NWY * gilsk.sky[iSky]);
3568 1284720 : backFac.sky[iSky] = eintsk.sky[iSky] * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * gilsk.sky[iSky]);
3569 : } else {
3570 0 : illFac.sky[iSky] = 0.0;
3571 0 : sourceFac.sky[iSky] = 0.0;
3572 0 : backFac.sky[iSky] = 0.0;
3573 : }
3574 :
3575 : } // for (iSky)
3576 :
3577 321180 : if (dl->horIllum[iHour].sun > tmpDFCalc) {
3578 303974 : auto &daylFac = daylFacHr(loopwin, iRefPoint, JSH);
3579 303974 : daylFac[iLum_Illum].sun = (dl->dirIllum(iHour, JSH).sun + dl->reflIllum(iHour, JSH).sun) / (dl->horIllum[iHour].sun + 0.0001);
3580 303974 : daylFac[iLum_Illum].sunDisk = (dl->dirIllum(iHour, JSH).sunDisk + dl->reflIllum(iHour, JSH).sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
3581 303974 : daylFac[iLum_Source].sun = dl->avgWinLum(iHour, JSH).sun / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
3582 303974 : daylFac[iLum_Source].sunDisk = dl->avgWinLum(iHour, JSH).sunDisk / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
3583 607948 : daylFac[iLum_Back].sun =
3584 303974 : dl->reflIllum(iHour, JSH).sun * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
3585 303974 : daylFac[iLum_Back].sunDisk =
3586 303974 : dl->reflIllum(iHour, JSH).sunDisk * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
3587 : } else {
3588 17206 : daylFac[iLum_Illum].sun = 0.0;
3589 17206 : daylFac[iLum_Illum].sunDisk = 0.0;
3590 :
3591 17206 : daylFac[iLum_Source].sun = 0.0;
3592 17206 : daylFac[iLum_Source].sunDisk = 0.0;
3593 :
3594 17206 : daylFac[iLum_Back].sun = 0.0;
3595 17206 : daylFac[iLum_Back].sunDisk = 0.0;
3596 : }
3597 : } // for (jSH)
3598 :
3599 : // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
3600 160590 : if (ICtrl > 0 && state.dataSurface->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
3601 :
3602 2090 : Real64 VTR = state.dataSurface->SurfWinVisTransRatio(IWin); // Ratio of Tvis of fully-switched state to that of the unswitched state
3603 2090 : auto &daylFac2 = daylFacHr(loopwin, iRefPoint, 2);
3604 2090 : auto const &daylFac1 = daylFacHr(loopwin, iRefPoint, 1);
3605 :
3606 10450 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3607 8360 : daylFac2[iLum_Illum].sky[iSky] = daylFac1[iLum_Illum].sky[iSky] * VTR;
3608 8360 : daylFac2[iLum_Source].sky[iSky] = daylFac1[iLum_Source].sky[iSky] * VTR;
3609 8360 : daylFac2[iLum_Back].sky[iSky] = daylFac1[iLum_Back].sky[iSky] * VTR;
3610 : } // for (iSky)
3611 :
3612 2090 : daylFac2[iLum_Illum].sun = daylFac1[iLum_Illum].sun * VTR;
3613 2090 : daylFac2[iLum_Source].sun = daylFac1[iLum_Source].sun * VTR;
3614 2090 : daylFac2[iLum_Back].sun = daylFac1[iLum_Back].sun * VTR;
3615 2090 : daylFac2[iLum_Illum].sunDisk = daylFac1[iLum_Illum].sunDisk * VTR;
3616 2090 : daylFac2[iLum_Source].sunDisk = daylFac1[iLum_Source].sunDisk * VTR;
3617 2090 : daylFac2[iLum_Back].sunDisk = daylFac1[iLum_Back].sunDisk * VTR;
3618 : } // ICtrl > 0
3619 : } // FigureRefPointDayltgFactorsToAddIllums()
3620 :
3621 328800 : void FigureMapPointDayltgFactorsToAddIllums(EnergyPlusData &state,
3622 : int const MapNum,
3623 : int const iMapPoint,
3624 : int const iHour,
3625 : int const IWin,
3626 : int const loopwin,
3627 : int const ICtrl // Window control counter
3628 : )
3629 : {
3630 :
3631 : // SUBROUTINE INFORMATION:
3632 : // AUTHOR B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann, Peter Ellis, Linda Lawrie
3633 : // DATE WRITTEN Nov. 2012
3634 :
3635 : // PURPOSE OF THIS SUBROUTINE:
3636 : // calculation worker routine to fill daylighting coefficients
3637 :
3638 : // METHODOLOGY EMPLOYED:
3639 : // this version is just for map points.
3640 :
3641 : // SUBROUTINE PARAMETER DEFINITIONS:
3642 328800 : Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating
3643 : // the daylighting and glare factors
3644 :
3645 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3646 328800 : auto &dl = state.dataDayltg;
3647 :
3648 328800 : if (state.dataSurface->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
3649 :
3650 : // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
3651 : // 2 to Material::MaxSlatAngs+1 for variable slat-angle blind)
3652 :
3653 : // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
3654 : // related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
3655 : // and interior surfaces with high visible reflectance.
3656 : // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
3657 : // the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
3658 :
3659 164400 : auto &illumMap = dl->illumMaps(MapNum);
3660 164400 : auto &daylFacHr = illumMap.daylFac[iHour];
3661 493200 : for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
3662 493200 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
3663 :
3664 328800 : auto const &gilsk = dl->horIllum[iHour];
3665 328800 : auto const &edirsk = dl->dirIllum(iHour, JSH);
3666 328800 : auto const &eintsk = dl->reflIllum(iHour, JSH);
3667 328800 : auto &illSky = daylFacHr(loopwin, iMapPoint, JSH);
3668 :
3669 1644000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
3670 1315200 : illSky.sky[iSky] = (gilsk.sky[iSky] > tmpDFCalc) ? ((edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky]) : 0.0;
3671 : } // for (iSky)
3672 :
3673 328800 : if (dl->horIllum[iHour].sun > tmpDFCalc) {
3674 630200 : daylFacHr(loopwin, iMapPoint, JSH).sun =
3675 315100 : (dl->dirIllum(iHour, JSH).sun + dl->reflIllum(iHour, JSH).sun) / (dl->horIllum[iHour].sun + 0.0001);
3676 315100 : daylFacHr(loopwin, iMapPoint, JSH).sunDisk =
3677 315100 : (dl->dirIllum(iHour, JSH).sunDisk + dl->reflIllum(iHour, JSH).sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
3678 : } else {
3679 13700 : daylFacHr(loopwin, iMapPoint, JSH).sun = 0.0;
3680 13700 : daylFacHr(loopwin, iMapPoint, JSH).sunDisk = 0.0;
3681 : }
3682 : } // for (jSH)
3683 :
3684 : // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
3685 164400 : if (ICtrl > 0 && state.dataSurface->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
3686 146400 : Real64 VTR = state.dataSurface->SurfWinVisTransRatio(IWin); // ratio of Tvis of switched to unswitched state
3687 146400 : auto &illSky2 = daylFacHr(loopwin, iMapPoint, 2);
3688 146400 : auto const &illSky1 = daylFacHr(loopwin, iMapPoint, 1);
3689 732000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3690 585600 : illSky2.sky[iSky] = illSky1.sky[iSky] * VTR;
3691 : }
3692 :
3693 146400 : daylFacHr(loopwin, iMapPoint, 2).sun = daylFacHr(loopwin, iMapPoint, 1).sun * VTR;
3694 146400 : daylFacHr(loopwin, iMapPoint, 2).sunDisk = daylFacHr(loopwin, iMapPoint, 1).sunDisk * VTR;
3695 : } // ICtrl > 0
3696 : }
3697 :
3698 796 : void GetDaylightingParametersInput(EnergyPlusData &state)
3699 : {
3700 :
3701 : // SUBROUTINE INFORMATION:
3702 : // AUTHOR Linda Lawrie
3703 : // DATE WRITTEN Oct 2004
3704 :
3705 : // PURPOSE OF THIS SUBROUTINE:
3706 : // This subroutine provides a simple structure to get all daylighting
3707 : // parameters.
3708 796 : auto &dl = state.dataDayltg;
3709 :
3710 796 : if (!dl->getDaylightingParametersInputFlag) return;
3711 796 : dl->getDaylightingParametersInputFlag = false;
3712 :
3713 796 : auto const &ipsc = state.dataIPShortCut;
3714 796 : ipsc->cCurrentModuleObject = "Daylighting:Controls";
3715 796 : bool ErrorsFound = false;
3716 796 : int TotDaylightingControls = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3717 796 : if (TotDaylightingControls > 0) {
3718 64 : dl->enclDaylight.allocate(state.dataViewFactor->NumOfSolarEnclosures);
3719 64 : GetInputDayliteRefPt(state, ErrorsFound);
3720 64 : GetDaylightingControls(state, ErrorsFound);
3721 64 : GeometryTransformForDaylighting(state);
3722 64 : GetInputIlluminanceMap(state, ErrorsFound);
3723 64 : GetLightWellData(state, ErrorsFound);
3724 64 : if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons, related to DAYLIGHTING");
3725 64 : DayltgSetupAdjZoneListsAndPointers(state);
3726 : }
3727 :
3728 796 : dl->maxNumRefPtInAnyDaylCtrl = 0;
3729 796 : dl->maxNumRefPtInAnyEncl = 0;
3730 : // Loop through all daylighting controls to find total reference points in each enclosure
3731 1086 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
3732 290 : int numRefPoints = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
3733 290 : dl->maxNumRefPtInAnyDaylCtrl = max(numRefPoints, dl->maxNumRefPtInAnyDaylCtrl);
3734 : }
3735 5851 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
3736 5055 : dl->maxNumRefPtInAnyEncl = max(state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints, dl->maxNumRefPtInAnyEncl);
3737 : }
3738 :
3739 796 : dl->maxEnclSubSurfaces = max(maxval(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::NumSubSurfaces),
3740 796 : maxval(dl->enclDaylight, &EnclDaylightCalc::NumOfDayltgExtWins));
3741 :
3742 7013 : for (int SurfNum : state.dataSurface->AllHTWindowSurfaceList) {
3743 6217 : auto const &surf = state.dataSurface->Surface(SurfNum);
3744 6217 : int const surfEnclNum = surf.SolarEnclIndex;
3745 6217 : int const numEnclRefPoints = state.dataViewFactor->EnclSolInfo(surfEnclNum).TotalEnclosureDaylRefPoints;
3746 6217 : auto &surfWin = state.dataSurface->SurfaceWindow(SurfNum);
3747 6217 : if (numEnclRefPoints > 0) {
3748 848 : if (!state.dataSurface->SurfWinSurfDayLightInit(SurfNum)) {
3749 848 : surfWin.refPts.allocate(numEnclRefPoints);
3750 2343 : for (auto &refPt : surfWin.refPts) {
3751 1495 : new (&refPt) SurfaceWindowRefPt();
3752 : }
3753 :
3754 848 : state.dataSurface->SurfWinSurfDayLightInit(SurfNum) = true;
3755 : }
3756 : } else {
3757 5369 : int SurfNumAdj = surf.ExtBoundCond;
3758 5369 : if (SurfNumAdj > 0) {
3759 13 : int const adjSurfEnclNum = state.dataSurface->Surface(SurfNumAdj).SolarEnclIndex;
3760 13 : int const numAdjEnclRefPoints = state.dataViewFactor->EnclSolInfo(adjSurfEnclNum).TotalEnclosureDaylRefPoints;
3761 13 : if (numAdjEnclRefPoints > 0) {
3762 1 : if (!state.dataSurface->SurfWinSurfDayLightInit(SurfNum)) {
3763 1 : surfWin.refPts.allocate(numAdjEnclRefPoints);
3764 2 : for (auto &refPt : surfWin.refPts) {
3765 1 : new (&refPt) SurfaceWindowRefPt();
3766 : }
3767 1 : state.dataSurface->SurfWinSurfDayLightInit(SurfNum) = true;
3768 : }
3769 : }
3770 : }
3771 : }
3772 :
3773 6217 : if (surf.ExtBoundCond != ExternalEnvironment) continue;
3774 :
3775 6203 : if (!surf.HasShadeControl) continue;
3776 :
3777 151 : auto &thisSurfEnclosure(state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex));
3778 151 : if (state.dataSurface->WindowShadingControl(surf.activeWindowShadingControl).GlareControlIsActive) {
3779 : // Error if GlareControlIsActive but window is not in a Daylighting:Detailed zone
3780 30 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
3781 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3782 0 : ShowContinueError(state, "GlareControlIsActive = Yes but it is not in a Daylighting zone or enclosure.");
3783 0 : ShowContinueError(state, format("Zone or enclosure indicated={}", state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex).Name));
3784 0 : ErrorsFound = true;
3785 : }
3786 : // Error if GlareControlIsActive and window is in a Daylighting:Detailed zone/enclosure with
3787 : // an interior window adjacent to another Daylighting:Detailed zone/enclosure
3788 30 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3789 357 : for (int const intWin : thisSurfEnclosure.SurfacePtr) {
3790 327 : int const SurfNumAdj = state.dataSurface->Surface(intWin).ExtBoundCond;
3791 327 : if (state.dataSurface->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
3792 0 : auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(state.dataSurface->Surface(SurfNumAdj).SolarEnclIndex));
3793 0 : if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3794 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3795 0 : ShowContinueError(state, "GlareControlIsActive = Yes and is in a Daylighting zone or enclosure");
3796 0 : ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
3797 0 : ShowContinueError(state, format("Adjacent Zone or Enclosure indicated={}", adjSurfEnclosure.Name));
3798 0 : ErrorsFound = true;
3799 : }
3800 : }
3801 : }
3802 : }
3803 : }
3804 :
3805 151 : if (state.dataSurface->WindowShadingControl(surf.activeWindowShadingControl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp)
3806 149 : continue;
3807 :
3808 : // Error if window has shadingControlType = MeetDaylightingIlluminanceSetpoint &
3809 : // but is not in a Daylighting:Detailed zone
3810 2 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
3811 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3812 0 : ShowContinueError(state, "MeetDaylightingIlluminanceSetpoint but it is not in a Daylighting zone or enclosure.");
3813 0 : ShowContinueError(state, format("Zone or enclosure indicated={}", thisSurfEnclosure.Name));
3814 0 : ErrorsFound = true;
3815 0 : continue;
3816 : }
3817 :
3818 : // Error if window has shadingControlType = MeetDaylightIlluminanceSetpoint and is in a &
3819 : // Daylighting:Detailed zone with an interior window adjacent to another Daylighting:Detailed zone
3820 22 : for (int const intWin : thisSurfEnclosure.SurfacePtr) {
3821 20 : int const SurfNumAdj = state.dataSurface->Surface(intWin).ExtBoundCond;
3822 20 : if (state.dataSurface->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
3823 0 : auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(state.dataSurface->Surface(SurfNumAdj).SolarEnclIndex));
3824 0 : if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3825 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3826 0 : ShowContinueError(state, "MeetDaylightIlluminanceSetpoint and is in a Daylighting zone or enclosure");
3827 0 : ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
3828 0 : ShowContinueError(state, format("Adjacent Zone or enclosure indicated={}", adjSurfEnclosure.Name));
3829 0 : ErrorsFound = true;
3830 : }
3831 : }
3832 : }
3833 796 : } // for (SurfNum)
3834 :
3835 796 : if (!state.dataHeatBal->AnyAirBoundary) {
3836 46524 : for (int SurfLoop = 1; SurfLoop <= state.dataSurface->TotSurfaces; ++SurfLoop) {
3837 45733 : auto const &surf = state.dataSurface->Surface(SurfLoop);
3838 45733 : if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
3839 :
3840 6171 : int const enclOfSurf = surf.SolarEnclIndex;
3841 6171 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclOfSurf);
3842 6171 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow || !dl->enclDaylight(enclOfSurf).hasSplitFluxDaylighting)
3843 5336 : continue;
3844 :
3845 835 : auto &surfWin = state.dataSurface->SurfaceWindow(SurfLoop);
3846 2309 : for (int refPtNum = 1; refPtNum <= enclSol.TotalEnclosureDaylRefPoints; ++refPtNum) {
3847 1474 : auto &refPt = surfWin.refPts(refPtNum);
3848 4422 : SetupOutputVariable(state,
3849 2948 : format("Daylighting Window Reference Point {} Illuminance", refPtNum),
3850 : Constant::Units::lux,
3851 1474 : refPt.illumFromWinRep,
3852 : OutputProcessor::TimeStepType::Zone,
3853 : OutputProcessor::StoreType::Average,
3854 1474 : surf.Name);
3855 4422 : SetupOutputVariable(state,
3856 2948 : format("Daylighting Window Reference Point {} View Luminance", refPtNum),
3857 : Constant::Units::cd_m2,
3858 1474 : refPt.lumWinRep,
3859 : OutputProcessor::TimeStepType::Zone,
3860 : OutputProcessor::StoreType::Average,
3861 1474 : surf.Name);
3862 : }
3863 : }
3864 : } else {
3865 34 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
3866 29 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
3867 302 : for (int const enclSurfNum : enclSol.SurfacePtr) {
3868 273 : auto const &surf = state.dataSurface->Surface(enclSurfNum);
3869 273 : auto &surfWindow = state.dataSurface->SurfaceWindow(enclSurfNum);
3870 :
3871 273 : if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
3872 :
3873 30 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow) continue;
3874 :
3875 7 : auto const &enclDayltg = dl->enclDaylight(enclNum);
3876 7 : if (!enclDayltg.hasSplitFluxDaylighting) continue;
3877 :
3878 7 : int refPtCount = 0;
3879 17 : for (int controlNum : enclDayltg.daylightControlIndexes) {
3880 10 : auto const &control = dl->daylightControl(controlNum);
3881 20 : for (int refPtNum = 1; refPtNum <= control.TotalDaylRefPoints; ++refPtNum) {
3882 10 : ++refPtCount; // Count reference points across each daylighting control in the same enclosure
3883 10 : auto &refPt = surfWindow.refPts(refPtCount);
3884 10 : std::string varKey = format("{} to {}", surf.Name, state.dataDayltg->DaylRefPt(control.refPts(refPtNum).num).Name);
3885 20 : SetupOutputVariable(state,
3886 : "Daylighting Window Reference Point Illuminance",
3887 : Constant::Units::lux,
3888 10 : refPt.illumFromWinRep,
3889 : OutputProcessor::TimeStepType::Zone,
3890 : OutputProcessor::StoreType::Average,
3891 : varKey);
3892 20 : SetupOutputVariable(state,
3893 : "Daylighting Window Reference Point View Luminance",
3894 : Constant::Units::cd_m2,
3895 10 : refPt.lumWinRep,
3896 : OutputProcessor::TimeStepType::Zone,
3897 : OutputProcessor::StoreType::Average,
3898 : varKey);
3899 10 : }
3900 7 : } // for (controlNum)
3901 : } // for (enclSurfNum)
3902 : } // for (enclNum)
3903 : }
3904 :
3905 : // RJH DElight Modification Begin - Calls to DElight preprocessing subroutines
3906 796 : if (doesDayLightingUseDElight(state)) {
3907 3 : Real64 dLatitude = state.dataEnvrn->Latitude;
3908 3 : DisplayString(state, "Calculating DElight Daylighting Factors");
3909 3 : DElightManagerF::DElightInputGenerator(state);
3910 : // Init Error Flag to 0 (no Warnings or Errors)
3911 3 : DisplayString(state, "ReturnFrom DElightInputGenerator");
3912 3 : int iErrorFlag = 0;
3913 3 : DisplayString(state, "Calculating DElight DaylightCoefficients");
3914 3 : DElightManagerF::GenerateDElightDaylightCoefficients(dLatitude, iErrorFlag);
3915 : // Check Error Flag for Warnings or Errors returning from DElight
3916 : // RJH 2008-03-07: open file for READWRITE and DELETE file after processing
3917 3 : DisplayString(state, "ReturnFrom DElight DaylightCoefficients Calc");
3918 3 : if (iErrorFlag != 0) {
3919 : // Open DElight Daylight Factors Error File for reading
3920 0 : auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
3921 :
3922 : // Sequentially read lines in DElight Daylight Factors Error File
3923 : // and process them using standard EPlus warning/error handling calls
3924 : // Process all error/warning messages first
3925 : // Then, if any error has occurred, ShowFatalError to terminate processing
3926 0 : bool bEndofErrFile = !iDElightErrorFile.good();
3927 0 : std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
3928 0 : while (!bEndofErrFile) {
3929 0 : auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
3930 0 : if (cErrorLine.eof) {
3931 0 : bEndofErrFile = true;
3932 0 : continue;
3933 : }
3934 : // Is the current line a Warning message?
3935 0 : if (has_prefix(cErrorLine.data, "WARNING: ")) {
3936 0 : cErrorMsg = cErrorLine.data.substr(9);
3937 0 : ShowWarningError(state, cErrorMsg);
3938 : }
3939 : // Is the current line an Error message?
3940 0 : if (has_prefix(cErrorLine.data, "ERROR: ")) {
3941 0 : cErrorMsg = cErrorLine.data.substr(7);
3942 0 : ShowSevereError(state, cErrorMsg);
3943 0 : iErrorFlag = 1;
3944 : }
3945 0 : }
3946 :
3947 : // Close and Delete DElight Error File
3948 0 : if (iDElightErrorFile.is_open()) {
3949 0 : iDElightErrorFile.close();
3950 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
3951 : }
3952 :
3953 : // If any DElight Error occurred then ShowFatalError to terminate
3954 0 : if (iErrorFlag > 0) {
3955 0 : ErrorsFound = true;
3956 : }
3957 0 : } else {
3958 3 : if (FileSystem::fileExists(state.files.outputDelightDfdmpFilePath.filePath)) {
3959 3 : FileSystem::removeFile(state.files.outputDelightDfdmpFilePath.filePath);
3960 : }
3961 : }
3962 : }
3963 : // RJH DElight Modification End - Calls to DElight preprocessing subroutines
3964 :
3965 : // TH 6/3/2010, added to report daylight factors
3966 796 : ipsc->cCurrentModuleObject = "Output:DaylightFactors";
3967 796 : int NumReports = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
3968 796 : if (NumReports > 0) {
3969 : int NumNames;
3970 : int NumNumbers;
3971 : int IOStat;
3972 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3973 1 : ipsc->cCurrentModuleObject,
3974 : 1,
3975 1 : ipsc->cAlphaArgs,
3976 : NumNames,
3977 1 : ipsc->rNumericArgs,
3978 : NumNumbers,
3979 : IOStat,
3980 1 : ipsc->lNumericFieldBlanks,
3981 1 : ipsc->lAlphaFieldBlanks,
3982 1 : ipsc->cAlphaFieldNames,
3983 1 : ipsc->cNumericFieldNames);
3984 1 : if (has_prefix(ipsc->cAlphaArgs(1), "SIZINGDAYS")) {
3985 1 : dl->DFSReportSizingDays = true;
3986 0 : } else if (has_prefix(ipsc->cAlphaArgs(1), "ALLSHADOWCALCULATIONDAYS")) {
3987 0 : dl->DFSReportAllShadowCalculationDays = true;
3988 : }
3989 : }
3990 :
3991 796 : if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons");
3992 : } // FigureMapPointDayltgFactorsToAddIllums()
3993 :
3994 64 : void GetInputIlluminanceMap(EnergyPlusData &state, bool &ErrorsFound)
3995 : {
3996 : // Perform the GetInput function for the Output:IlluminanceMap
3997 : // Glazer - June 2016 (moved from GetDaylightingControls)
3998 64 : auto &dl = state.dataDayltg;
3999 :
4000 64 : Array1D_bool ZoneMsgDone;
4001 :
4002 64 : Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
4003 64 : Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
4004 : // these are only for Building Rotation for Appendix G when using world coordinate system
4005 64 : Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
4006 64 : Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
4007 :
4008 64 : bool doTransform = false;
4009 64 : Real64 OldAspectRatio = 1.0;
4010 64 : Real64 NewAspectRatio = 1.0;
4011 :
4012 64 : CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
4013 :
4014 64 : auto &ip = state.dataInputProcessing->inputProcessor;
4015 64 : auto const &ipsc = state.dataIPShortCut;
4016 64 : ipsc->cCurrentModuleObject = "Output:IlluminanceMap";
4017 64 : int TotIllumMaps = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
4018 :
4019 64 : dl->illumMaps.allocate(TotIllumMaps);
4020 :
4021 64 : if (TotIllumMaps > 0) {
4022 : int IOStat;
4023 : int NumAlpha;
4024 : int NumNumber;
4025 15 : for (int MapNum = 1; MapNum <= TotIllumMaps; ++MapNum) {
4026 18 : ip->getObjectItem(state,
4027 9 : ipsc->cCurrentModuleObject,
4028 : MapNum,
4029 9 : ipsc->cAlphaArgs,
4030 : NumAlpha,
4031 9 : ipsc->rNumericArgs,
4032 : NumNumber,
4033 : IOStat,
4034 9 : ipsc->lNumericFieldBlanks,
4035 9 : ipsc->lAlphaFieldBlanks,
4036 9 : ipsc->cAlphaFieldNames,
4037 9 : ipsc->cNumericFieldNames);
4038 :
4039 9 : auto &illumMap = dl->illumMaps(MapNum);
4040 9 : illumMap.Name = ipsc->cAlphaArgs(1);
4041 9 : int const zoneNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
4042 9 : if (zoneNum > 0) {
4043 8 : illumMap.zoneIndex = zoneNum;
4044 : // set enclosure index for first space in zone
4045 8 : int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
4046 8 : illumMap.enclIndex = enclNum;
4047 : // check that all spaces in the zone are in the same enclosure
4048 8 : for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
4049 0 : int spaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
4050 0 : if (enclNum != state.dataHeatBal->space(spaceNum).solarEnclosureNum) {
4051 0 : ShowSevereError(state,
4052 0 : format("{}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting illuminance maps.",
4053 0 : ipsc->cCurrentModuleObject,
4054 0 : ipsc->cAlphaArgs(1)));
4055 0 : ShowContinueError(
4056 0 : state, format("Zone=\"{}\" spans multiple enclosures. Use a Space Name instead.", state.dataHeatBal->Zone(zoneNum).Name));
4057 0 : ErrorsFound = true;
4058 0 : break;
4059 : }
4060 : }
4061 : } else {
4062 1 : int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
4063 1 : if (spaceNum == 0) {
4064 0 : ShowSevereError(state,
4065 0 : format("{}=\"{}\", invalid {}=\"{}\".",
4066 0 : ipsc->cCurrentModuleObject,
4067 0 : ipsc->cAlphaArgs(1),
4068 0 : ipsc->cAlphaFieldNames(2),
4069 0 : ipsc->cAlphaArgs(2)));
4070 0 : ErrorsFound = true;
4071 : } else {
4072 1 : illumMap.spaceIndex = spaceNum;
4073 1 : illumMap.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
4074 1 : illumMap.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
4075 1 : assert(illumMap.enclIndex > 0);
4076 : }
4077 : }
4078 :
4079 9 : illumMap.Z = ipsc->rNumericArgs(1);
4080 9 : illumMap.Xmin = ipsc->rNumericArgs(2);
4081 9 : illumMap.Xmax = ipsc->rNumericArgs(3);
4082 9 : if (ipsc->rNumericArgs(2) > ipsc->rNumericArgs(3)) {
4083 0 : ShowSevereError(state, format("{}=\"{}\", invalid entry.", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
4084 0 : ShowContinueError(state,
4085 0 : format("...{} {:.2R} must be <= {} {:.2R}.",
4086 0 : ipsc->cNumericFieldNames(2),
4087 0 : ipsc->rNumericArgs(2),
4088 0 : ipsc->cNumericFieldNames(3),
4089 0 : ipsc->rNumericArgs(3)));
4090 0 : ErrorsFound = true;
4091 : }
4092 9 : illumMap.Xnum = ipsc->rNumericArgs(4);
4093 9 : illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
4094 :
4095 9 : illumMap.Ymin = ipsc->rNumericArgs(5);
4096 9 : illumMap.Ymax = ipsc->rNumericArgs(6);
4097 9 : if (ipsc->rNumericArgs(5) > ipsc->rNumericArgs(6)) {
4098 0 : ShowSevereError(state, format("{}=\"{}\", invalid entry.", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
4099 0 : ShowContinueError(state,
4100 0 : format("...{} {:.2R} must be <= {} {:.2R}.",
4101 0 : ipsc->cNumericFieldNames(5),
4102 0 : ipsc->rNumericArgs(5),
4103 0 : ipsc->cNumericFieldNames(6),
4104 0 : ipsc->rNumericArgs(6)));
4105 0 : ErrorsFound = true;
4106 : }
4107 9 : illumMap.Ynum = ipsc->rNumericArgs(7);
4108 9 : illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
4109 :
4110 9 : if (illumMap.Xnum * illumMap.Ynum > MaxMapRefPoints) {
4111 0 : ShowSevereError(state, format("{}=\"{}\", too many map points specified.", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
4112 0 : ShowContinueError(state,
4113 0 : format("...{}[{}] * {}[{}].= [{}] must be <= [{}].",
4114 0 : ipsc->cNumericFieldNames(4),
4115 0 : illumMap.Xnum,
4116 0 : ipsc->cNumericFieldNames(7),
4117 0 : illumMap.Ynum,
4118 0 : illumMap.Xnum * illumMap.Ynum,
4119 : MaxMapRefPoints));
4120 0 : ErrorsFound = true;
4121 : }
4122 : } // MapNum
4123 6 : ipsc->cCurrentModuleObject = "OutputControl:IlluminanceMap:Style";
4124 6 : int MapStyleIn = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
4125 :
4126 6 : if (MapStyleIn == 0) {
4127 5 : ipsc->cAlphaArgs(1) = "COMMA";
4128 5 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4129 1 : } else if (MapStyleIn == 1) {
4130 2 : ip->getObjectItem(state,
4131 1 : ipsc->cCurrentModuleObject,
4132 : 1,
4133 1 : ipsc->cAlphaArgs,
4134 : NumAlpha,
4135 1 : ipsc->rNumericArgs,
4136 : NumNumber,
4137 : IOStat,
4138 1 : ipsc->lNumericFieldBlanks,
4139 1 : ipsc->lAlphaFieldBlanks,
4140 1 : ipsc->cAlphaFieldNames,
4141 1 : ipsc->cNumericFieldNames);
4142 1 : if (ipsc->cAlphaArgs(1) == "COMMA") {
4143 1 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4144 0 : } else if (ipsc->cAlphaArgs(1) == "TAB") {
4145 0 : dl->MapColSep = DataStringGlobals::CharTab; // tab
4146 0 : } else if (ipsc->cAlphaArgs(1) == "FIXED" || ipsc->cAlphaArgs(1) == "SPACE") {
4147 0 : dl->MapColSep = DataStringGlobals::CharSpace; // space
4148 : } else {
4149 0 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4150 0 : ShowWarningError(state,
4151 0 : format("{}: invalid {}=\"{}\", Commas will be used to separate fields.",
4152 0 : ipsc->cCurrentModuleObject,
4153 0 : ipsc->cAlphaFieldNames(1),
4154 0 : ipsc->cAlphaArgs(1)));
4155 0 : ipsc->cAlphaArgs(1) = "COMMA";
4156 : }
4157 : }
4158 6 : print(state.files.eio, "! <Daylighting:Illuminance Maps>,#Maps,Style\n");
4159 6 : ConvertCaseToLower(ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2));
4160 6 : ipsc->cAlphaArgs(1).erase(1);
4161 6 : ipsc->cAlphaArgs(1) += ipsc->cAlphaArgs(2).substr(1);
4162 6 : print(state.files.eio, "Daylighting:Illuminance Maps,{},{}\n", TotIllumMaps, ipsc->cAlphaArgs(1));
4163 : }
4164 :
4165 : // Check for illuminance maps associated with this zone
4166 73 : for (auto &illumMap : dl->illumMaps) {
4167 :
4168 9 : if (illumMap.zoneIndex == 0) continue;
4169 :
4170 9 : auto &zone = state.dataHeatBal->Zone(illumMap.zoneIndex);
4171 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
4172 9 : Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRadians);
4173 9 : Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRadians);
4174 :
4175 9 : if (illumMap.Xnum * illumMap.Ynum == 0) continue;
4176 :
4177 : // Add additional daylighting reference points for map
4178 9 : illumMap.TotalMapRefPoints = illumMap.Xnum * illumMap.Ynum;
4179 9 : illumMap.refPts.allocate(illumMap.TotalMapRefPoints);
4180 634 : for (auto &refPt : illumMap.refPts) {
4181 625 : new (&refPt) DaylMapPt();
4182 : }
4183 :
4184 9 : if (illumMap.TotalMapRefPoints > MaxMapRefPoints) {
4185 0 : ShowSevereError(state, "GetDaylighting Parameters: Total Map Reference points entered is greater than maximum allowed.");
4186 0 : ShowContinueError(state, format("Occurs in Zone={}", zone.Name));
4187 0 : ShowContinueError(state,
4188 0 : format("Maximum reference points allowed={}, entered amount ( when error first occurred )={}",
4189 : MaxMapRefPoints,
4190 0 : illumMap.TotalMapRefPoints));
4191 0 : ErrorsFound = true;
4192 0 : break;
4193 : }
4194 :
4195 : // Calc cos and sin of Zone Relative North values for later use in transforming Map Point coordinates
4196 : // CosZoneRelNorth = std::cos( -zone.RelNorth * DegToRadians ); //Tuned These should not be changing
4197 : // SinZoneRelNorth = std::sin( -zone.RelNorth * DegToRadians );
4198 9 : illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
4199 9 : illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
4200 :
4201 : // Map points and increments are stored in AbsCoord and then that is operated on if relative coords entered.
4202 94 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
4203 710 : for (int X = 1; X <= illumMap.Xnum; ++X) {
4204 625 : int iRefPt = (Y - 1) * illumMap.Xnum + X;
4205 625 : auto &refPt = illumMap.refPts(iRefPt);
4206 625 : refPt.absCoords = {illumMap.Xmin + (X - 1) * illumMap.Xinc, illumMap.Ymin + (Y - 1) * illumMap.Yinc, illumMap.Z};
4207 : }
4208 : }
4209 :
4210 94 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
4211 710 : for (int X = 1; X <= illumMap.Xnum; ++X) {
4212 625 : int iRefPt = (Y - 1) * illumMap.Xnum + X;
4213 625 : auto &refPt = illumMap.refPts(iRefPt);
4214 :
4215 625 : if (!state.dataSurface->DaylRefWorldCoordSystem) {
4216 425 : Real64 Xb = refPt.absCoords.x * CosZoneRelNorth - refPt.absCoords.y * SinZoneRelNorth + zone.OriginX;
4217 425 : Real64 Yb = refPt.absCoords.x * SinZoneRelNorth + refPt.absCoords.y * CosZoneRelNorth + zone.OriginY;
4218 425 : refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
4219 425 : refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
4220 425 : refPt.absCoords.z += zone.OriginZ;
4221 425 : if (doTransform) {
4222 0 : Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
4223 0 : Real64 Yo = refPt.absCoords.y;
4224 : // next derotate the building
4225 0 : Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
4226 0 : Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
4227 : // translate
4228 0 : Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
4229 0 : Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
4230 : // rerotate
4231 0 : refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
4232 :
4233 0 : refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
4234 : }
4235 : } else {
4236 200 : Real64 Xb = refPt.absCoords.x;
4237 200 : Real64 Yb = refPt.absCoords.y;
4238 200 : refPt.absCoords.x = Xb * CosBldgRotAppGonly - Yb * SinBldgRotAppGonly;
4239 200 : refPt.absCoords.y = Xb * SinBldgRotAppGonly + Yb * CosBldgRotAppGonly;
4240 : }
4241 625 : if (iRefPt == 1) {
4242 9 : illumMap.Xmin = refPt.absCoords.x;
4243 9 : illumMap.Ymin = refPt.absCoords.y;
4244 9 : illumMap.Xmax = refPt.absCoords.x;
4245 9 : illumMap.Ymax = refPt.absCoords.y;
4246 9 : illumMap.Z = refPt.absCoords.z;
4247 : }
4248 625 : illumMap.Xmin = min(illumMap.Xmin, refPt.absCoords.x);
4249 625 : illumMap.Ymin = min(illumMap.Ymin, refPt.absCoords.y);
4250 625 : illumMap.Xmax = max(illumMap.Xmax, refPt.absCoords.x);
4251 625 : illumMap.Ymax = max(illumMap.Ymax, refPt.absCoords.y);
4252 625 : if ((refPt.absCoords.x < zone.MinimumX && (zone.MinimumX - refPt.absCoords.x) > 0.001) ||
4253 625 : (refPt.absCoords.x > zone.MaximumX && (refPt.absCoords.x - zone.MaximumX) > 0.001) ||
4254 625 : (refPt.absCoords.y < zone.MinimumY && (zone.MinimumY - refPt.absCoords.y) > 0.001) ||
4255 625 : (refPt.absCoords.y > zone.MaximumY && (refPt.absCoords.y - zone.MaximumY) > 0.001) ||
4256 625 : (refPt.absCoords.z < zone.MinimumZ && (zone.MinimumZ - refPt.absCoords.z) > 0.001) ||
4257 625 : (refPt.absCoords.z > zone.MaximumZ && (refPt.absCoords.z - zone.MaximumZ) > 0.001)) {
4258 0 : refPt.inBounds = false;
4259 : }
4260 :
4261 : // Test extremes of Map Points against Zone Min/Max
4262 643 : if (iRefPt != 1 && iRefPt != illumMap.TotalMapRefPoints) continue;
4263 :
4264 18 : if (refPt.inBounds) continue;
4265 :
4266 0 : if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
4267 0 : ShowWarningError(
4268 : state,
4269 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], X Value outside Zone Min/Max X, Zone={}", iRefPt, zone.Name));
4270 0 : ShowContinueError(state,
4271 0 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
4272 0 : refPt.absCoords.x,
4273 0 : zone.MinimumX,
4274 0 : zone.MaximumX));
4275 0 : ShowContinueError(
4276 : state,
4277 0 : format("...X Reference Distance Outside MinimumX= {:.4R} m.",
4278 0 : (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
4279 : }
4280 0 : if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
4281 0 : ShowWarningError(
4282 : state,
4283 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], Y Value outside Zone Min/Max Y, Zone={}", iRefPt, zone.Name));
4284 0 : ShowContinueError(state,
4285 0 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
4286 0 : refPt.absCoords.y,
4287 0 : zone.MinimumY,
4288 0 : zone.MaximumY));
4289 0 : ShowContinueError(
4290 : state,
4291 0 : format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
4292 0 : (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
4293 : }
4294 0 : if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
4295 0 : ShowWarningError(
4296 : state,
4297 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], Z Value outside Zone Min/Max Z, Zone={}", iRefPt, zone.Name));
4298 0 : ShowContinueError(state,
4299 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
4300 0 : refPt.absCoords.z,
4301 0 : zone.MinimumZ,
4302 0 : zone.MaximumZ));
4303 0 : ShowContinueError(
4304 : state,
4305 0 : format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
4306 0 : (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
4307 : }
4308 : } // for (X)
4309 : } // for (Y)
4310 : } // for (MapNum)
4311 :
4312 64 : ZoneMsgDone.dimension(state.dataGlobal->NumOfZones, false);
4313 73 : for (auto const &illumMap : dl->illumMaps) {
4314 9 : if (illumMap.zoneIndex == 0) continue;
4315 9 : int enclNum = illumMap.enclIndex;
4316 9 : if (!dl->enclDaylight(enclNum).hasSplitFluxDaylighting && !ZoneMsgDone(illumMap.zoneIndex)) {
4317 0 : ShowSevereError(state,
4318 0 : format("Zone Name in Output:IlluminanceMap is not used for Daylighting:Controls={}",
4319 0 : state.dataHeatBal->Zone(illumMap.zoneIndex).Name));
4320 0 : ErrorsFound = true;
4321 : }
4322 : }
4323 64 : ZoneMsgDone.deallocate();
4324 64 : if (ErrorsFound) return;
4325 :
4326 64 : if (TotIllumMaps > 0) {
4327 6 : print(state.files.eio,
4328 : "! <Daylighting:Illuminance Maps:Detail>,Name,Zone,XMin {{m}},XMax {{m}},Xinc {{m}},#X Points,YMin "
4329 : "{{m}},YMax {{m}},Yinc {{m}},#Y Points,Z {{m}}\n");
4330 : }
4331 73 : for (auto const &illumMap : dl->illumMaps) {
4332 9 : print(state.files.eio,
4333 : "Daylighting:Illuminance Maps:Detail,{},{},{:.2R},{:.2R},{:.2R},{},{:.2R},{:.2R},{:.2R},{},{:.2R}\n",
4334 9 : illumMap.Name,
4335 9 : state.dataHeatBal->Zone(illumMap.zoneIndex).Name,
4336 9 : illumMap.Xmin,
4337 9 : illumMap.Xmax,
4338 9 : illumMap.Xinc,
4339 9 : illumMap.Xnum,
4340 9 : illumMap.Ymin,
4341 9 : illumMap.Ymax,
4342 9 : illumMap.Yinc,
4343 9 : illumMap.Ynum,
4344 9 : illumMap.Z);
4345 : }
4346 :
4347 64 : } // GetInputIlluminanceMap()
4348 :
4349 64 : void GetDaylightingControls(EnergyPlusData &state, bool &ErrorsFound)
4350 : {
4351 : // AUTHOR Fred Winkelmann
4352 : // DATE WRITTEN March 2002
4353 : // MODIFIED Glazer - July 2016 - Move geometry transformation portion, rearrange input, allow more than three reference points
4354 : // Obtain the user input data for Daylighting:Controls object in the input file.
4355 :
4356 64 : auto &dl = state.dataDayltg;
4357 :
4358 : int IOStat;
4359 : int NumAlpha;
4360 : int NumNumber;
4361 :
4362 : // Smallest deviation from unity for the sum of all fractions
4363 : // Accept approx 4 to 8 ULP error (technically abs(1.0 + sumFracs) should be close to 2)
4364 : // constexpr Real64 FractionTolerance(4 * std::numeric_limits<Real64>::epsilon());
4365 : // Instead, we use a 0.001 = 0.1% tolerance
4366 64 : constexpr Real64 FractionTolerance(0.001);
4367 :
4368 64 : auto &ip = state.dataInputProcessing->inputProcessor;
4369 64 : auto const &ipsc = state.dataIPShortCut;
4370 64 : ipsc->cCurrentModuleObject = "Daylighting:Controls";
4371 64 : int totDaylightingControls = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
4372 64 : dl->daylightControl.allocate(totDaylightingControls);
4373 64 : Array1D<bool> spaceHasDaylightingControl;
4374 64 : spaceHasDaylightingControl.dimension(state.dataGlobal->numSpaces, false);
4375 : // Reset to zero in case this is called more than once in unit tests
4376 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
4377 721 : state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints = 0;
4378 : }
4379 354 : for (int controlNum = 1; controlNum <= totDaylightingControls; ++controlNum) {
4380 290 : ipsc->cAlphaArgs = "";
4381 290 : ipsc->rNumericArgs = 0.0;
4382 580 : ip->getObjectItem(state,
4383 290 : ipsc->cCurrentModuleObject,
4384 : controlNum,
4385 290 : ipsc->cAlphaArgs,
4386 : NumAlpha,
4387 290 : ipsc->rNumericArgs,
4388 : NumNumber,
4389 : IOStat,
4390 290 : ipsc->lNumericFieldBlanks,
4391 290 : ipsc->lAlphaFieldBlanks,
4392 290 : ipsc->cAlphaFieldNames,
4393 290 : ipsc->cNumericFieldNames);
4394 290 : auto &daylightControl = dl->daylightControl(controlNum);
4395 290 : daylightControl.Name = ipsc->cAlphaArgs(1);
4396 :
4397 : // Is it a zone or space name?
4398 290 : int const zoneNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->Zone);
4399 290 : if (zoneNum > 0) {
4400 289 : daylightControl.zoneIndex = zoneNum;
4401 : // set enclosure index for first space in zone
4402 289 : int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
4403 289 : daylightControl.enclIndex = enclNum;
4404 : // check that all spaces in the zone are in the same enclosure
4405 289 : for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
4406 0 : int zoneSpaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
4407 0 : if (daylightControl.enclIndex != state.dataHeatBal->space(zoneSpaceNum).solarEnclosureNum) {
4408 0 : ShowSevereError(state,
4409 0 : format("{}: invalid {}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting.",
4410 0 : ipsc->cCurrentModuleObject,
4411 0 : ipsc->cAlphaFieldNames(2),
4412 0 : ipsc->cAlphaArgs(2)));
4413 0 : ErrorsFound = true;
4414 0 : break;
4415 : }
4416 : }
4417 578 : for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
4418 : // Check if this is a duplicate
4419 289 : if (spaceHasDaylightingControl(zoneSpaceNum)) {
4420 0 : ShowWarningError(state,
4421 0 : format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
4422 0 : ipsc->cCurrentModuleObject,
4423 0 : daylightControl.Name,
4424 0 : state.dataHeatBal->space(zoneSpaceNum).Name,
4425 0 : ipsc->cCurrentModuleObject));
4426 0 : ShowContinueError(state, "This control will override the lighting power factor for this space.");
4427 : }
4428 289 : spaceHasDaylightingControl(zoneSpaceNum) = true;
4429 289 : }
4430 : } else {
4431 1 : int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
4432 1 : if (spaceNum == 0) {
4433 0 : ShowSevereError(state, format("{}: invalid {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2)));
4434 0 : ErrorsFound = true;
4435 0 : continue;
4436 : } else {
4437 1 : daylightControl.spaceIndex = spaceNum;
4438 1 : daylightControl.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
4439 1 : daylightControl.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
4440 : // Check if this is a duplicate
4441 1 : if (spaceHasDaylightingControl(spaceNum)) {
4442 0 : ShowWarningError(state,
4443 0 : format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
4444 0 : ipsc->cCurrentModuleObject,
4445 0 : daylightControl.Name,
4446 0 : state.dataHeatBal->space(spaceNum).Name,
4447 0 : ipsc->cCurrentModuleObject));
4448 0 : ShowContinueError(state, "This control will override the lighting power factor for this space.");
4449 : }
4450 1 : spaceHasDaylightingControl(spaceNum) = true;
4451 : }
4452 : }
4453 :
4454 290 : dl->enclDaylight(daylightControl.enclIndex).daylightControlIndexes.emplace_back(controlNum);
4455 290 : daylightControl.ZoneName = state.dataHeatBal->Zone(daylightControl.zoneIndex).Name;
4456 :
4457 290 : if (ipsc->lAlphaFieldBlanks(3)) {
4458 0 : daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
4459 : } else {
4460 290 : daylightControl.DaylightMethod =
4461 290 : static_cast<DaylightingMethod>(getEnumValue(DaylightingMethodNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(3))));
4462 :
4463 290 : if (daylightControl.DaylightMethod == DaylightingMethod::Invalid) {
4464 0 : daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
4465 0 : ShowWarningError(state,
4466 0 : format("Invalid {} = {}, occurs in {}object for {}=\"{}",
4467 0 : ipsc->cAlphaFieldNames(3),
4468 0 : ipsc->cAlphaArgs(3),
4469 0 : ipsc->cCurrentModuleObject,
4470 0 : ipsc->cCurrentModuleObject,
4471 0 : ipsc->cAlphaArgs(1)));
4472 0 : ShowContinueError(state, "SplitFlux assumed, and the simulation continues.");
4473 : }
4474 : }
4475 290 : dl->enclDaylight(daylightControl.enclIndex).hasSplitFluxDaylighting |= (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux);
4476 :
4477 290 : if (!ipsc->lAlphaFieldBlanks(4)) { // Field: Availability Schedule Name
4478 6 : daylightControl.AvailSchedNum = ScheduleManager::GetScheduleIndex(state, ipsc->cAlphaArgs(4));
4479 6 : if (daylightControl.AvailSchedNum == 0) {
4480 0 : ShowWarningError(state,
4481 0 : format("Invalid {} = {}, occurs in {}object for {}=\"{}",
4482 0 : ipsc->cAlphaFieldNames(4),
4483 0 : ipsc->cAlphaArgs(4),
4484 0 : ipsc->cCurrentModuleObject,
4485 0 : ipsc->cCurrentModuleObject,
4486 0 : ipsc->cAlphaArgs(1)));
4487 0 : ShowContinueError(state, "Schedule was not found so controls will always be available, and the simulation continues.");
4488 0 : daylightControl.AvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
4489 : }
4490 : } else {
4491 284 : daylightControl.AvailSchedNum = ScheduleManager::ScheduleAlwaysOn;
4492 : }
4493 :
4494 290 : daylightControl.LightControlType = static_cast<LtgCtrlType>(getEnumValue(LtgCtrlTypeNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(5))));
4495 290 : if (daylightControl.LightControlType == LtgCtrlType::Invalid) {
4496 0 : ShowWarningError(state,
4497 0 : format("Invalid {} = {}, occurs in {}object for {}=\"{}",
4498 0 : ipsc->cAlphaFieldNames(5),
4499 0 : ipsc->cAlphaArgs(5),
4500 0 : ipsc->cCurrentModuleObject,
4501 0 : ipsc->cCurrentModuleObject,
4502 0 : ipsc->cAlphaArgs(1)));
4503 0 : ShowContinueError(state, "Continuous assumed, and the simulation continues.");
4504 : }
4505 :
4506 290 : daylightControl.MinPowerFraction = ipsc->rNumericArgs(1); // Field: Minimum Input Power Fraction for Continuous Dimming Control
4507 290 : daylightControl.MinLightFraction = ipsc->rNumericArgs(2); // Field: Minimum Light Output Fraction for Continuous Dimming Control
4508 290 : daylightControl.LightControlSteps = ipsc->rNumericArgs(3); // Field: Number of Stepped Control Steps
4509 290 : daylightControl.LightControlProbability =
4510 290 : ipsc->rNumericArgs(4); // Field: Probability Lighting will be Reset When Needed in Manual Stepped Control
4511 :
4512 290 : if (!ipsc->lAlphaFieldBlanks(6)) { // Field: Glare Calculation Daylighting Reference Point Name
4513 286 : daylightControl.glareRefPtNumber = Util::FindItemInList(ipsc->cAlphaArgs(6),
4514 286 : dl->DaylRefPt,
4515 : &RefPointData::Name); // Field: Glare Calculation Daylighting Reference Point Name
4516 286 : if (daylightControl.glareRefPtNumber == 0) {
4517 0 : ShowSevereError(state,
4518 0 : format("{}: invalid {}=\"{}\" for object named: {}",
4519 0 : ipsc->cCurrentModuleObject,
4520 0 : ipsc->cAlphaFieldNames(6),
4521 0 : ipsc->cAlphaArgs(6),
4522 0 : ipsc->cAlphaArgs(1)));
4523 0 : ErrorsFound = true;
4524 0 : continue;
4525 : }
4526 4 : } else if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
4527 1 : ShowWarningError(state, format("No {} provided for object named: {}", ipsc->cAlphaFieldNames(6), ipsc->cAlphaArgs(1)));
4528 1 : ShowContinueError(state, "No glare calculation performed, and the simulation continues.");
4529 : }
4530 :
4531 : // Field: Glare Calculation Azimuth Angle of View Direction Clockwise from Zone y-Axis
4532 290 : daylightControl.ViewAzimuthForGlare = !ipsc->lNumericFieldBlanks(5) ? ipsc->rNumericArgs(5) : 0.0;
4533 :
4534 290 : daylightControl.MaxGlareallowed = ipsc->rNumericArgs(6); // Field: Maximum Allowable Discomfort Glare Index
4535 290 : daylightControl.DElightGriddingResolution = ipsc->rNumericArgs(7); // Field: DElight Gridding Resolution
4536 :
4537 290 : int curTotalDaylRefPts = NumAlpha - 6; // first six alpha fields are not part of extensible group
4538 290 : daylightControl.TotalDaylRefPoints = curTotalDaylRefPts;
4539 290 : state.dataViewFactor->EnclSolInfo(daylightControl.enclIndex).TotalEnclosureDaylRefPoints += curTotalDaylRefPts;
4540 290 : dl->ZoneDaylight(daylightControl.zoneIndex).totRefPts += curTotalDaylRefPts;
4541 290 : dl->maxControlRefPoints = max(dl->maxControlRefPoints, curTotalDaylRefPts);
4542 290 : if ((NumNumber - 7) / 2 != daylightControl.TotalDaylRefPoints) {
4543 0 : ShowSevereError(state,
4544 0 : format("{}The number of extensible numeric fields and alpha fields is inconsistent for: {}",
4545 0 : ipsc->cCurrentModuleObject,
4546 0 : ipsc->cAlphaArgs(1)));
4547 0 : ShowContinueError(state,
4548 0 : format("For each field: {} there needs to be the following fields: Fraction Controlled by Reference Point and "
4549 : "Illuminance Setpoint at Reference Point",
4550 0 : ipsc->cAlphaFieldNames(NumAlpha)));
4551 0 : ErrorsFound = true;
4552 : }
4553 :
4554 290 : daylightControl.refPts.allocate(curTotalDaylRefPts);
4555 :
4556 759 : for (auto &refPt : daylightControl.refPts) {
4557 469 : refPt = DaylRefPt();
4558 : }
4559 :
4560 290 : int countRefPts = 0;
4561 759 : for (int refPtNum = 1; refPtNum <= curTotalDaylRefPts; ++refPtNum) {
4562 469 : auto &refPt = daylightControl.refPts(refPtNum);
4563 469 : refPt.num =
4564 469 : Util::FindItemInList(ipsc->cAlphaArgs(6 + refPtNum), dl->DaylRefPt, &RefPointData::Name); // Field: Daylighting Reference Point Name
4565 469 : if (refPt.num == 0) {
4566 0 : ShowSevereError(state,
4567 0 : format("{}: invalid {}=\"{}\" for object named: {}",
4568 0 : ipsc->cCurrentModuleObject,
4569 0 : ipsc->cAlphaFieldNames(6 + refPtNum),
4570 0 : ipsc->cAlphaArgs(6 + refPtNum),
4571 0 : ipsc->cAlphaArgs(1)));
4572 0 : ErrorsFound = true;
4573 0 : continue;
4574 : } else {
4575 469 : ++countRefPts;
4576 : }
4577 469 : refPt.fracZoneDaylit = ipsc->rNumericArgs(6 + refPtNum * 2); // Field: Fraction Controlled by Reference Point
4578 469 : refPt.illumSetPoint = ipsc->rNumericArgs(7 + refPtNum * 2); // Field: Illuminance Setpoint at Reference Point
4579 :
4580 469 : if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
4581 1389 : SetupOutputVariable(state,
4582 926 : format("Daylighting Reference Point {} Illuminance", refPtNum),
4583 : Constant::Units::lux,
4584 463 : refPt.lums[iLum_Illum],
4585 : OutputProcessor::TimeStepType::Zone,
4586 : OutputProcessor::StoreType::Average,
4587 463 : daylightControl.Name);
4588 1389 : SetupOutputVariable(state,
4589 926 : format("Daylighting Reference Point {} Daylight Illuminance Setpoint Exceeded Time", refPtNum),
4590 : Constant::Units::hr,
4591 463 : refPt.timeExceedingDaylightIlluminanceSetPoint,
4592 : OutputProcessor::TimeStepType::Zone,
4593 : OutputProcessor::StoreType::Sum,
4594 463 : daylightControl.Name);
4595 1389 : SetupOutputVariable(state,
4596 926 : format("Daylighting Reference Point {} Glare Index", refPtNum),
4597 : Constant::Units::None,
4598 463 : refPt.glareIndex,
4599 : OutputProcessor::TimeStepType::Zone,
4600 : OutputProcessor::StoreType::Average,
4601 463 : daylightControl.Name);
4602 1389 : SetupOutputVariable(state,
4603 926 : format("Daylighting Reference Point {} Glare Index Setpoint Exceeded Time", refPtNum),
4604 : Constant::Units::hr,
4605 463 : refPt.timeExceedingGlareIndexSetPoint,
4606 : OutputProcessor::TimeStepType::Zone,
4607 : OutputProcessor::StoreType::Sum,
4608 463 : daylightControl.Name);
4609 : } // if (DaylightMethod == SplitFlux)
4610 : } // for (RefPtNum)
4611 :
4612 : // Register Error if 0 DElight RefPts have been input for valid DElight object
4613 290 : if (countRefPts < 1) {
4614 0 : ShowSevereError(state, format("No Reference Points input for {} zone ={}", ipsc->cCurrentModuleObject, daylightControl.ZoneName));
4615 0 : ErrorsFound = true;
4616 : }
4617 :
4618 290 : Real64 sumFracs = 0.0;
4619 759 : for (auto const &refPt : daylightControl.refPts)
4620 469 : sumFracs += refPt.fracZoneDaylit;
4621 :
4622 290 : daylightControl.sumFracLights = sumFracs;
4623 290 : if ((1.0 - sumFracs) > FractionTolerance) {
4624 166 : ShowWarningError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is < 1.0.");
4625 332 : ShowContinueError(state,
4626 332 : format("..discovered in {}=\"{}\", only {:.3R} of the zone or space is controlled.",
4627 166 : ipsc->cCurrentModuleObject,
4628 166 : daylightControl.Name,
4629 : sumFracs));
4630 124 : } else if ((sumFracs - 1.0) > FractionTolerance) {
4631 0 : ShowSevereError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is > 1.0.");
4632 0 : ShowContinueError(state,
4633 0 : format("..discovered in {}=\"{}\", trying to control {:.3R} of the zone or space.",
4634 0 : ipsc->cCurrentModuleObject,
4635 0 : daylightControl.Name,
4636 : sumFracs));
4637 0 : ErrorsFound = true;
4638 : }
4639 :
4640 290 : if (daylightControl.LightControlType == LtgCtrlType::Stepped && daylightControl.LightControlSteps <= 0) {
4641 0 : ShowWarningError(state, "GetDaylightingControls: For Stepped Control, the number of steps must be > 0");
4642 0 : ShowContinueError(state, format("..discovered in \"{}\" for Zone=\"{}\", will use 1", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(2)));
4643 0 : daylightControl.LightControlSteps = 1;
4644 : }
4645 580 : SetupOutputVariable(state,
4646 : "Daylighting Lighting Power Multiplier",
4647 : Constant::Units::None,
4648 290 : daylightControl.PowerReductionFactor,
4649 : OutputProcessor::TimeStepType::Zone,
4650 : OutputProcessor::StoreType::Average,
4651 290 : daylightControl.Name);
4652 : } // for (controlNum)
4653 64 : } // GetDaylightingControls()
4654 :
4655 64 : void GeometryTransformForDaylighting(EnergyPlusData &state)
4656 : {
4657 : // AUTHOR Fred Winkelmann
4658 : // DATE WRITTEN March 2002
4659 : // MODIFIED Glazer - July 2016 - separated this from GetInput function
4660 : // For splitflux daylighting, transform the geometry
4661 64 : auto &dl = state.dataDayltg;
4662 :
4663 : // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
4664 64 : Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
4665 64 : Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRadians);
4666 : // these are only for Building Rotation for Appendix G when using world coordinate system
4667 64 : Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
4668 64 : Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRadians);
4669 :
4670 64 : bool doTransform = false;
4671 64 : Real64 OldAspectRatio = 1.0;
4672 64 : Real64 NewAspectRatio = 1.0;
4673 :
4674 64 : CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
4675 354 : for (auto &daylCntrl : dl->daylightControl) {
4676 290 : auto &zone = state.dataHeatBal->Zone(daylCntrl.zoneIndex);
4677 :
4678 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
4679 290 : Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRadians);
4680 290 : Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRadians);
4681 :
4682 290 : Real64 rLightLevel = InternalHeatGains::GetDesignLightingLevelForZone(state, daylCntrl.zoneIndex);
4683 290 : InternalHeatGains::CheckLightsReplaceableMinMaxForZone(state, daylCntrl.zoneIndex);
4684 :
4685 759 : for (int refPtNum = 1; refPtNum <= daylCntrl.TotalDaylRefPoints; ++refPtNum) {
4686 469 : auto &refPt = daylCntrl.refPts(refPtNum);
4687 469 : auto &curRefPt = dl->DaylRefPt(refPt.num); // get the active daylighting:referencepoint
4688 469 : curRefPt.indexToFracAndIllum = refPtNum; // back reference to the index to the ZoneDaylight structure arrays related to reference points
4689 469 : if (state.dataSurface->DaylRefWorldCoordSystem) {
4690 : // transform only by appendix G rotation
4691 7 : refPt.absCoords.x = curRefPt.coords.x * CosBldgRotAppGonly - curRefPt.coords.y * SinBldgRotAppGonly;
4692 7 : refPt.absCoords.y = curRefPt.coords.x * SinBldgRotAppGonly + curRefPt.coords.y * CosBldgRotAppGonly;
4693 7 : refPt.absCoords.z = curRefPt.coords.z;
4694 : } else {
4695 : // Transform reference point coordinates into building coordinate system
4696 462 : Real64 Xb = curRefPt.coords.x * CosZoneRelNorth - curRefPt.coords.y * SinZoneRelNorth + zone.OriginX;
4697 462 : Real64 Yb = curRefPt.coords.x * SinZoneRelNorth + curRefPt.coords.y * CosZoneRelNorth + zone.OriginY;
4698 : // Transform into World Coordinate System
4699 462 : refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
4700 462 : refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
4701 462 : refPt.absCoords.z = curRefPt.coords.z + zone.OriginZ;
4702 462 : if (doTransform) {
4703 0 : Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
4704 0 : Real64 Yo = refPt.absCoords.y;
4705 : // next derotate the building
4706 0 : Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
4707 0 : Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
4708 : // translate
4709 0 : Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
4710 0 : Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
4711 : // rerotate
4712 0 : refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
4713 0 : refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
4714 : }
4715 : }
4716 :
4717 469 : auto &orp = state.dataOutRptPredefined;
4718 469 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtZone, curRefPt.Name, daylCntrl.ZoneName);
4719 469 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlName, curRefPt.Name, daylCntrl.Name);
4720 469 : if (daylCntrl.DaylightMethod == DaylightingMethod::SplitFlux) {
4721 463 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "SplitFlux");
4722 : } else {
4723 6 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "DElight");
4724 : }
4725 : // ( 1=continuous, 2=stepped, 3=continuous/off )
4726 469 : if (daylCntrl.LightControlType == LtgCtrlType::Continuous) {
4727 60 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous");
4728 409 : } else if (daylCntrl.LightControlType == LtgCtrlType::Stepped) {
4729 131 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Stepped");
4730 278 : } else if (daylCntrl.LightControlType == LtgCtrlType::ContinuousOff) {
4731 278 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous/Off");
4732 : }
4733 469 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtFrac, curRefPt.Name, refPt.fracZoneDaylit);
4734 469 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWInst, curRefPt.Name, rLightLevel);
4735 469 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWCtrl, curRefPt.Name, rLightLevel * refPt.fracZoneDaylit);
4736 :
4737 469 : if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
4738 0 : refPt.inBounds = false;
4739 0 : ShowWarningError(state,
4740 0 : format("GeometryTransformForDaylighting: Reference point X Value outside Zone Min/Max X, Zone={}", zone.Name));
4741 0 : ShowContinueError(state,
4742 0 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
4743 0 : refPt.absCoords.x,
4744 0 : zone.MinimumX,
4745 0 : zone.MaximumX));
4746 0 : ShowContinueError(
4747 : state,
4748 0 : format("...X Reference Distance Outside MinimumX= {:.4R} m.",
4749 0 : (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
4750 : }
4751 469 : if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
4752 0 : refPt.inBounds = false;
4753 0 : ShowWarningError(state,
4754 0 : format("GeometryTransformForDaylighting: Reference point Y Value outside Zone Min/Max Y, Zone={}", zone.Name));
4755 0 : ShowContinueError(state,
4756 0 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
4757 0 : refPt.absCoords.x,
4758 0 : zone.MinimumY,
4759 0 : zone.MaximumY));
4760 0 : ShowContinueError(
4761 : state,
4762 0 : format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
4763 0 : (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
4764 : }
4765 469 : if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
4766 0 : refPt.inBounds = false;
4767 0 : ShowWarningError(state,
4768 0 : format("GeometryTransformForDaylighting: Reference point Z Value outside Zone Min/Max Z, Zone={}", zone.Name));
4769 0 : ShowContinueError(state,
4770 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
4771 0 : refPt.absCoords.z,
4772 0 : zone.MinimumZ,
4773 0 : zone.MaximumZ));
4774 0 : ShowContinueError(
4775 : state,
4776 0 : format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
4777 0 : (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
4778 : }
4779 : } // for (refPt)
4780 : } // for (daylightCtrl)
4781 64 : } // GeometryTransformForDaylighting()
4782 :
4783 64 : void GetInputDayliteRefPt(EnergyPlusData &state, bool &ErrorsFound)
4784 : {
4785 : // Perform GetInput function for the Daylighting:ReferencePoint object
4786 : // Glazer - July 2016
4787 64 : auto &dl = state.dataDayltg;
4788 64 : auto &ip = state.dataInputProcessing->inputProcessor;
4789 64 : auto const &ipsc = state.dataIPShortCut;
4790 64 : ipsc->cCurrentModuleObject = "Daylighting:ReferencePoint";
4791 :
4792 64 : int RefPtNum = 0;
4793 : int IOStat;
4794 : int NumAlpha;
4795 : int NumNumber;
4796 :
4797 64 : int TotRefPoints = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
4798 :
4799 64 : dl->DaylRefPt.allocate(TotRefPoints);
4800 533 : for (auto &pt : dl->DaylRefPt) {
4801 938 : ip->getObjectItem(state,
4802 469 : ipsc->cCurrentModuleObject,
4803 : ++RefPtNum,
4804 469 : ipsc->cAlphaArgs,
4805 : NumAlpha,
4806 469 : ipsc->rNumericArgs,
4807 : NumNumber,
4808 : IOStat,
4809 469 : ipsc->lNumericFieldBlanks,
4810 469 : ipsc->lAlphaFieldBlanks,
4811 469 : ipsc->cAlphaFieldNames,
4812 469 : ipsc->cNumericFieldNames);
4813 469 : pt.Name = ipsc->cAlphaArgs(1);
4814 469 : pt.ZoneNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
4815 469 : if (pt.ZoneNum == 0) {
4816 1 : int spaceNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataHeatBal->space);
4817 1 : if (spaceNum == 0) {
4818 0 : ShowSevereError(state,
4819 0 : format("{}=\"{}\", invalid {}=\"{}\".",
4820 0 : ipsc->cCurrentModuleObject,
4821 0 : ipsc->cAlphaArgs(1),
4822 0 : ipsc->cAlphaFieldNames(2),
4823 0 : ipsc->cAlphaArgs(2)));
4824 0 : ErrorsFound = true;
4825 : } else {
4826 1 : pt.ZoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
4827 : }
4828 : }
4829 469 : pt.coords = {ipsc->rNumericArgs(1), ipsc->rNumericArgs(2), ipsc->rNumericArgs(3)};
4830 : }
4831 64 : }
4832 :
4833 1063 : bool doesDayLightingUseDElight(EnergyPlusData &state)
4834 : {
4835 1063 : auto &dl = state.dataDayltg;
4836 1347 : for (auto const &znDayl : dl->daylightControl) {
4837 287 : if (znDayl.DaylightMethod == DaylightingMethod::DElight) {
4838 3 : return true;
4839 : }
4840 : }
4841 1060 : return false;
4842 : }
4843 :
4844 796 : void CheckTDDsAndLightShelvesInDaylitZones(EnergyPlusData &state)
4845 : {
4846 : // SUBROUTINE INFORMATION:
4847 : // AUTHOR Brent Griffith
4848 : // DATE WRITTEN Dec 2007
4849 :
4850 : // PURPOSE OF THIS SUBROUTINE:
4851 : // This subroutine checks daylighting input for TDDs and light shelfs
4852 : // which need to be checked after daylighting input has been read in (CR 7145)
4853 : // (eventually this should be changed once/if implementations change to decouple from daylighting calcs so that
4854 : // these devices can be used in models without daylighting controls
4855 : // CR 7145 was for TDDs, but also implenting check for light shelves, the other "daylighting device"
4856 :
4857 : // METHODOLOGY EMPLOYED:
4858 : // loop thru daylighting devices and check that their zones have daylight controls
4859 :
4860 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4861 796 : bool ErrorsFound = false;
4862 :
4863 798 : for (auto const &pipe : state.dataDaylightingDevicesData->TDDPipe) {
4864 2 : int SurfNum = pipe.Diffuser;
4865 2 : if (SurfNum > 0) {
4866 2 : int const pipeEnclNum = state.dataSurface->Surface(SurfNum).SolarEnclIndex;
4867 2 : if (state.dataViewFactor->EnclSolInfo(pipeEnclNum).TotalEnclosureDaylRefPoints == 0) {
4868 0 : ShowWarningError(state,
4869 0 : format("DaylightingDevice:Tubular = {}: is not connected to a Zone that has Daylighting, no visible transmittance "
4870 : "will be modeled through the daylighting device.",
4871 0 : pipe.Name));
4872 : }
4873 : } else { // SurfNum == 0
4874 : // should not come here (would have already been caught in TDD get input), but is an error
4875 0 : ShowSevereError(state, format("DaylightingDevice:Tubular = {}: Diffuser surface not found ", pipe.Name));
4876 0 : ErrorsFound = true;
4877 : }
4878 : } // for (pipe)
4879 :
4880 797 : for (auto const &shelf : state.dataDaylightingDevicesData->Shelf) {
4881 1 : if (shelf.Window == 0) {
4882 : // should not come here (would have already been caught in shelf get input), but is an error
4883 0 : ShowSevereError(state, format("DaylightingDevice:Shelf = {}: window not found ", shelf.Name));
4884 0 : ErrorsFound = true;
4885 : }
4886 : } // for (shelf)
4887 :
4888 796 : if (ErrorsFound) ShowFatalError(state, "CheckTDDsAndLightShelvesInDaylitZones: Errors in DAYLIGHTING input.");
4889 796 : }
4890 :
4891 796 : void AssociateWindowShadingControlWithDaylighting(EnergyPlusData &state)
4892 : {
4893 796 : auto &dl = state.dataDayltg;
4894 869 : for (auto &winShadeControl : state.dataSurface->WindowShadingControl) {
4895 73 : if (winShadeControl.DaylightingControlName.empty()) continue;
4896 25 : int found = -1;
4897 27 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
4898 27 : if (Util::SameString(winShadeControl.DaylightingControlName, dl->daylightControl(daylightCtrlNum).Name)) {
4899 25 : found = daylightCtrlNum;
4900 25 : break;
4901 : }
4902 : }
4903 25 : if (found > 0) {
4904 25 : winShadeControl.DaylightControlIndex = found;
4905 : } else {
4906 0 : ShowWarningError(state, "AssociateWindowShadingControlWithDaylighting: Daylighting object name used in WindowShadingControl not found.");
4907 0 : ShowContinueError(state,
4908 0 : format("..The WindowShadingControl object=\"{}\" and referenes an object named: \"{}\"",
4909 0 : winShadeControl.Name,
4910 0 : winShadeControl.DaylightingControlName));
4911 : }
4912 796 : }
4913 796 : } // AssociateWindowShadingControlWithDaylighting()
4914 :
4915 64 : void GetLightWellData(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
4916 : {
4917 :
4918 : // SUBROUTINE INFORMATION:
4919 : // AUTHOR Fred Winkelmann
4920 : // DATE WRITTEN Apr 2004
4921 :
4922 : // PURPOSE OF THIS SUBROUTINE:
4923 : // Gets data for a light well associated with a rectangular exterior window.
4924 : // Calculates light well efficiency, defined as the ratio of the amount of visible
4925 : // solar radiation leaving a well to the amount entering the well.
4926 :
4927 : // METHODOLOGY EMPLOYED:
4928 : // Based on fit to Fig. 8-21, "Efficiency factors for various depths of light wells
4929 : // based on well-interreflectance values," Lighting Handbook, 8th Edition, Illuminating
4930 : // Engineering Society of North America, 1993.
4931 :
4932 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4933 :
4934 : int IOStat; // IO Status when calling get input subroutine
4935 : int NumAlpha; // Number of alpha names being passed
4936 : int NumProp; // Number of properties being passed
4937 : int TotLightWells; // Total Light Well objects
4938 :
4939 64 : auto &ip = state.dataInputProcessing->inputProcessor;
4940 64 : auto const &ipsc = state.dataIPShortCut;
4941 :
4942 : // Get the total number of Light Well objects
4943 64 : ipsc->cCurrentModuleObject = "DaylightingDevice:LightWell";
4944 64 : TotLightWells = ip->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
4945 64 : if (TotLightWells == 0) return;
4946 :
4947 3 : for (int loop = 1; loop <= TotLightWells; ++loop) {
4948 :
4949 4 : ip->getObjectItem(state,
4950 2 : ipsc->cCurrentModuleObject,
4951 : loop,
4952 2 : ipsc->cAlphaArgs,
4953 : NumAlpha,
4954 2 : ipsc->rNumericArgs,
4955 : NumProp,
4956 : IOStat,
4957 2 : ipsc->lNumericFieldBlanks,
4958 2 : ipsc->lAlphaFieldBlanks,
4959 2 : ipsc->cAlphaFieldNames,
4960 2 : ipsc->cNumericFieldNames);
4961 :
4962 2 : int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(1), state.dataSurface->Surface);
4963 2 : if (SurfNum == 0) {
4964 0 : ShowSevereError(state,
4965 0 : format("{}: invalid {}=\"{}\" not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1)));
4966 0 : ErrorsFound = true;
4967 0 : continue;
4968 : }
4969 :
4970 2 : auto const &surf = state.dataSurface->Surface(SurfNum);
4971 2 : auto &surfWin = state.dataSurface->SurfaceWindow(SurfNum);
4972 : // Check that associated surface is an exterior window
4973 : // True if associated surface is not an exterior window
4974 2 : if (surf.Class != SurfaceClass::Window && surf.ExtBoundCond != ExternalEnvironment) {
4975 0 : ShowSevereError(
4976 : state,
4977 0 : format(
4978 0 : "{}: invalid {}=\"{}\" - not an exterior window.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1)));
4979 0 : ErrorsFound = true;
4980 0 : continue;
4981 : }
4982 :
4983 : // Associated surface is an exterior window; calculate light well efficiency.
4984 2 : surfWin.lightWellEff = 1.0;
4985 2 : Real64 HeightWell = ipsc->rNumericArgs(1); // Well height (from window to bottom of well) (m)
4986 2 : Real64 PerimWell = ipsc->rNumericArgs(2); // Well perimeter (at bottom of well) (m)
4987 2 : Real64 AreaWell = ipsc->rNumericArgs(3); // Well area (at bottom of well) (m2)
4988 2 : Real64 VisReflWell = ipsc->rNumericArgs(4); // Area-weighted visible reflectance of well walls
4989 :
4990 : // Warning if light well area is less than window area
4991 2 : if (AreaWell < (surf.Area + state.dataSurface->SurfWinDividerArea(SurfNum) - 0.1)) {
4992 0 : ShowSevereError(state,
4993 0 : format("{}: invalid {}=\"{}\" - Areas.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), ipsc->cAlphaArgs(1)));
4994 0 : ShowContinueError(state, format("has Area of Bottom of Well={:.1R} that is less than window area={:.1R}", surf.Area, AreaWell));
4995 : }
4996 :
4997 2 : if (HeightWell >= 0.0 && PerimWell > 0.0 && AreaWell > 0.0) {
4998 2 : Real64 WellCavRatio = 2.5 * HeightWell * PerimWell / AreaWell;
4999 2 : surfWin.lightWellEff = std::exp(-WellCavRatio * (0.16368 - 0.14467 * VisReflWell));
5000 : }
5001 : } // End of loop over light well objects
5002 : } // GetLightWellData()
5003 :
5004 4102827 : inline int findWinShadingStatus(EnergyPlusData &state, int const IWin)
5005 : {
5006 : // Return the window shading status, 1=unshaded, 2=shaded
5007 4102827 : bool WinShadedNoGlareControl = IS_SHADED_NO_GLARE_CTRL(state.dataSurface->SurfWinShadingFlag(IWin));
5008 :
5009 8200222 : return ((state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
5010 3828233 : (WinShadedNoGlareControl || state.dataSurface->SurfWinSolarDiffusing(IWin)))
5011 8200222 : ? 2
5012 4102827 : : 1;
5013 : }
5014 :
5015 1018666 : Real64 DayltgGlare(EnergyPlusData &state,
5016 : int IL, // Reference point index: 1=first ref pt, 2=second ref pt
5017 : Real64 BLUM, // Window background (surround) luminance (cd/m2)
5018 : int const daylightCtrlNum // Current daylighting control number
5019 : )
5020 : {
5021 :
5022 : // SUBROUTINE INFORMATION:
5023 : // AUTHOR Fred Winkelmann
5024 : // DATE WRITTEN July 1997
5025 :
5026 : // PURPOSE OF THIS SUBROUTINE:
5027 : // CALCULATE GLARE INDEX.
5028 :
5029 : // METHODOLOGY EMPLOYED:
5030 : // Called from DayltgInteriorIllum. Finds glare index at reference
5031 : // point no. IL in a space using the Cornell/BRS large source
5032 : // glare formula. BLUM is the background luminance (cd/m**2).
5033 : // TH comment 1/21/2010: The SurfaceWindow(IWin)%ShadingFlag has to be set
5034 : // before calling this subroutine. For switchable glazings this is tricky
5035 : // because the ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop)
5036 : // may change every time step to represent intermediate switched state.
5037 :
5038 : // REFERENCES:
5039 : // Based on DOE-2.1E subroutine DGLARE.
5040 :
5041 1018666 : Real64 GTOT = 0.0; // Glare constant
5042 :
5043 1018666 : auto &dl = state.dataDayltg;
5044 :
5045 : // Loop over exterior windows associated with zone
5046 1018666 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5047 1018666 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
5048 3672935 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5049 2654269 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5050 2654269 : int WinShadingIndex = findWinShadingStatus(state, IWin);
5051 : // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
5052 : // below, which is (0.2936)**0.6
5053 2654269 : auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
5054 2654269 : Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][WinShadingIndex - 1], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
5055 2654269 : Real64 GTOT2 = BLUM + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][WinShadingIndex - 1];
5056 2654269 : GTOT += GTOT1 / (GTOT2 + 0.000001);
5057 : }
5058 :
5059 : // Glare index (adding 0.000001 prevents LOG10 (0))
5060 1018666 : return max(0.0, 10.0 * std::log10(GTOT + 0.000001));
5061 : }
5062 :
5063 1015 : void DayltgGlareWithIntWins(EnergyPlusData &state,
5064 : int const daylightCtrlNum // Current daylighting control number
5065 : )
5066 : {
5067 :
5068 : // SUBROUTINE INFORMATION:
5069 : // AUTHOR Fred Winkelmann
5070 : // DATE WRITTEN March 2004
5071 :
5072 : // PURPOSE OF THIS SUBROUTINE:
5073 : // Calculate daylighting glare index for zones with interior windows.
5074 :
5075 : // METHODOLOGY EMPLOYED:
5076 : // Finds glare index at reference point IL in a daylit zone using the Cornell/BRS large source
5077 : // glare formula. Takes into account inter-reflected illuminance from light entering
5078 : // the zone through interior windows
5079 :
5080 : // REFERENCES:
5081 : // Based on subroutine DayltgGlare.
5082 :
5083 1015 : Real64 GTOT = 0.0; // Glare constant(?) // TODO: does this need to be reset for every refPt?
5084 :
5085 : // Calculate background luminance including effect of inter-reflected illuminance from light
5086 : // entering zone through its interior windows
5087 1015 : auto &dl = state.dataDayltg;
5088 1015 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5089 1015 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
5090 1015 : int RefPoints = thisDayltgCtrl.TotalDaylRefPoints; // Number of daylighting reference points in zone
5091 2030 : for (int IL = 1; IL <= RefPoints; ++IL) {
5092 1015 : auto &refPt = thisDayltgCtrl.refPts(IL);
5093 :
5094 1015 : Real64 BackgroundLum = refPt.lums[iLum_Back] + thisEnclDaylight.InterReflIllFrIntWins * thisEnclDaylight.aveVisDiffReflect / Constant::Pi;
5095 1015 : BackgroundLum = max(refPt.illumSetPoint * thisEnclDaylight.aveVisDiffReflect / Constant::Pi, BackgroundLum);
5096 :
5097 : // Loop over exterior windows associated with zone
5098 2030 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5099 1015 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5100 1015 : int WinShadingIndex = findWinShadingStatus(state, IWin);
5101 : // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
5102 : // below, which is (0.2936)**0.6
5103 1015 : auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
5104 1015 : Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][WinShadingIndex - 1], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
5105 1015 : Real64 GTOT2 = BackgroundLum + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][WinShadingIndex - 1];
5106 1015 : GTOT += GTOT1 / (GTOT2 + 0.000001);
5107 : }
5108 :
5109 : // Glare index
5110 1015 : refPt.glareIndex = max(0.0, 10.0 * std::log10(GTOT + 0.000001));
5111 : } // for (IL)
5112 1015 : } // DaylGlareWithIntWins()
5113 :
5114 4697 : void DayltgExtHorizIllum(EnergyPlusData &state,
5115 : Illums &HI // Horizontal illuminance from sky for different sky types
5116 : )
5117 : {
5118 :
5119 : // SUBROUTINE INFORMATION:
5120 : // AUTHOR Fred Winkelmann
5121 : // DATE WRITTEN July 1997
5122 :
5123 : // PURPOSE OF THIS SUBROUTINE:
5124 : // Calculates exterior daylight illuminance.
5125 :
5126 : // METHODOLOGY EMPLOYED:
5127 : // Called by CalcDayltgCoefficients. Calculates illuminance
5128 : // on unobstructed horizontal surface by integrating
5129 : // over the luminance distribution of standard CIE skies.
5130 : // Calculates horizontal beam illuminance.
5131 : // REFERENCES:
5132 : // Based on DOE-2.1E subroutine DHILL.
5133 :
5134 : // Argument array dimensioning
5135 :
5136 : // SUBROUTINE PARAMETER DEFINITIONS:
5137 4697 : Real64 constexpr DTH = (2.0 * Constant::Pi) / double(NTH); // Sky integration azimuth stepsize (radians)
5138 4697 : Real64 constexpr DPH = Constant::PiOvr2 / double(NPH); // Sky integration altitude stepsize (radians)
5139 :
5140 : // Integrate to obtain illuminance from sky.
5141 : // The contribution in lumens/m2 from a patch of sky at altitude PH and azimuth TH
5142 : // is L(TH,PH)*SIN(PH)*COS(PH)*DTH*DPH, where L(TH,PH) is the luminance
5143 : // of the patch in cd/m2.
5144 4697 : auto &dl = state.dataDayltg;
5145 :
5146 : // Init
5147 4697 : if (dl->DayltgExtHorizIllum_firstTime) {
5148 576 : for (int IPH = 1; IPH <= NPH; ++IPH) {
5149 512 : dl->PH[IPH] = (IPH - 0.5) * DPH;
5150 512 : dl->SPHCPH[IPH] = std::sin(dl->PH[IPH]) * std::cos(dl->PH[IPH]); // DA = COS(PH)*DTH*DPH
5151 : }
5152 1216 : for (int ITH = 1; ITH <= NTH; ++ITH) {
5153 1152 : dl->TH[ITH] = (ITH - 0.5) * DTH;
5154 : }
5155 64 : dl->DayltgExtHorizIllum_firstTime = false;
5156 : }
5157 :
5158 18788 : HI = Illums();
5159 :
5160 : // Sky integration
5161 42273 : for (int IPH = 1; IPH <= NPH; ++IPH) {
5162 37576 : Real64 const PH_IPH = dl->PH[IPH];
5163 37576 : Real64 const SPHCPH_IPH = dl->SPHCPH[IPH];
5164 713944 : for (int ITH = 1; ITH <= NTH; ++ITH) {
5165 676368 : Real64 const TH_ITH = dl->TH[ITH];
5166 3381840 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5167 2705472 : HI.sky[iSky] += DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH_ITH, PH_IPH) * SPHCPH_IPH;
5168 : }
5169 : }
5170 : }
5171 :
5172 23485 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5173 18788 : HI.sky[iSky] *= DTH * DPH;
5174 : }
5175 :
5176 : // Direct solar horizontal illum (for unit direct normal illuminance)
5177 4697 : HI.sun = dl->sunAngles.sinPhi * 1.0;
5178 4697 : } // DayltgExtHorizIllum()
5179 :
5180 : // Product of solar transmittances of exterior obstructions
5181 6890773 : Real64 DayltgHitObstruction(EnergyPlusData &state,
5182 : int const IHOUR, // Hour number
5183 : int const IWin, // Window index
5184 : Vector3<Real64> const &R1, // Origin of ray (m)
5185 : Vector3<Real64> const &RN // Unit vector along ray
5186 : )
5187 : {
5188 :
5189 : // SUBROUTINE INFORMATION:
5190 : // AUTHOR Fred Winkelmann
5191 : // DATE WRITTEN July 1997
5192 : // MODIFIED FCW, May 2003: update list of surface classes that qualify as obstructions;
5193 : // add interior surfaces as possible obstructors;
5194 : // return from DO loop over surfaces as soon as any obstruction is hit;
5195 : // FCW, July 2003: change from returning whether an obstruction is hit or not
5196 : // to product of solar transmittances of hit obstructions.
5197 : // FCW, Nov 2003: remove interior surfaces as possible obstructors since there
5198 : // is now a separate check for interior obstructions; exclude windows and
5199 : // doors as obstructors since if they are obstructors their base surfaces will
5200 : // also be obstructors
5201 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5202 :
5203 : // PURPOSE OF THIS SUBROUTINE:
5204 : // Determines the product of the solar transmittances of the obstructions hit by a ray
5205 : // from R1 in the direction of vector RN.
5206 :
5207 : // REFERENCES:
5208 : // Based on DOE-2.1E subroutine DHITSH.
5209 :
5210 : // Local declarations
5211 : bool hit; // True iff a particular obstruction is hit
5212 :
5213 6890773 : Real64 ObTrans = 1.0;
5214 :
5215 6890773 : auto const &window = state.dataSurface->Surface(IWin);
5216 6890773 : int const window_iBaseSurf = window.BaseSurf;
5217 :
5218 6890773 : Vector3<Real64> DayltgHitObstructionHP;
5219 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5220 : // Building elements are assumed to be opaque
5221 : // A shadowing surface is opaque unless its transmittance schedule value is non-zero
5222 6890773 : if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5223 :
5224 58062399 : for (int ISurf : state.dataSurface->AllShadowPossObstrSurfaceList) {
5225 53412214 : auto const &surface = state.dataSurface->Surface(ISurf);
5226 53412214 : SurfaceClass IType = surface.Class;
5227 53412214 : if ((IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf)) {
5228 48010663 : hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
5229 48010663 : if (hit) { // Building element is hit (assumed opaque)
5230 45108 : ObTrans = 0.0;
5231 45108 : break;
5232 : }
5233 5401551 : } else if (surface.IsShadowing) {
5234 706392 : hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
5235 706392 : if (hit) { // Shading surface is hit
5236 : // Get solar transmittance of the shading surface
5237 : Real64 const Trans(
5238 75782 : surface.SchedShadowSurfIndex > 0 ? ScheduleManager::LookUpScheduleValue(state, surface.SchedShadowSurfIndex, IHOUR, 1) : 0.0);
5239 75782 : if (Trans < 1.e-6) {
5240 75782 : ObTrans = 0.0;
5241 75782 : break;
5242 : } else {
5243 0 : ObTrans *= Trans;
5244 : }
5245 : }
5246 : }
5247 4771075 : }
5248 :
5249 : } else { // Surface octree search
5250 :
5251 2119698 : auto const &window_base(window_iBaseSurf > 0 ? state.dataSurface->Surface(window_iBaseSurf) : window);
5252 2119698 : auto const *window_base_p(&window_base);
5253 :
5254 : // Lambda function for the octree to test for surface hit and update transmittance if hit
5255 64744377 : auto solarTransmittance = [=, &state, &R1, &RN, &hit, &ObTrans](SurfaceData const &surface) -> bool {
5256 299776131 : if (!surface.IsShadowPossibleObstruction) return false; // Do Consider separate octree without filtered surfaces
5257 66829301 : DataSurfaces::SurfaceClass const sClass(surface.Class);
5258 66829301 : Vector3<Real64> HP;
5259 66829301 : if ((sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && (&surface != window_base_p)) {
5260 64697724 : hit = PierceSurface(surface, R1, RN, HP);
5261 64697724 : if (hit) { // Building element is hit (assumed opaque)
5262 72303 : ObTrans = 0.0;
5263 72303 : return true;
5264 : }
5265 2131577 : } else if (surface.IsShadowing) {
5266 46653 : hit = PierceSurface(surface, R1, RN, HP);
5267 46653 : if (hit) { // Shading surface is hit
5268 : // Get solar transmittance of the shading surface
5269 : Real64 const Trans(
5270 0 : surface.SchedShadowSurfIndex > 0 ? ScheduleManager::LookUpScheduleValue(state, surface.SchedShadowSurfIndex, IHOUR, 1) : 0.0);
5271 0 : if (Trans < 1.e-6) {
5272 0 : ObTrans = 0.0;
5273 0 : return true;
5274 : } else {
5275 0 : ObTrans *= Trans;
5276 0 : return ObTrans == 0.0;
5277 : }
5278 : }
5279 : }
5280 66756998 : return false;
5281 66829301 : };
5282 :
5283 : // Check octree surface candidates for hits: short circuits if zero transmittance reached
5284 2119698 : Vector3<Real64> const RN_inv(SurfaceOctreeCube::safe_inverse(RN));
5285 2119698 : state.dataHeatBalMgr->surfaceOctree.processSomeSurfaceRayIntersectsCube(state, R1, RN, RN_inv, solarTransmittance);
5286 2119698 : }
5287 :
5288 6890773 : return ObTrans;
5289 6890773 : } // DayltgHitObstruction()
5290 :
5291 2818653 : bool DayltgHitInteriorObstruction(EnergyPlusData &state,
5292 : int const IWin, // Window index
5293 : Vector3<Real64> const &R1, // Origin of ray (m)
5294 : Vector3<Real64> const &R2 // Destination of ray (m)
5295 : )
5296 : {
5297 :
5298 : // SUBROUTINE INFORMATION:
5299 : // AUTHOR Fred Winkelmann
5300 : // DATE WRITTEN July 1997
5301 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5302 :
5303 : // PURPOSE OF THIS SUBROUTINE:
5304 : // This subroutine checks for interior obstructions between reference point and window element.
5305 :
5306 : // Preconditions
5307 2818653 : assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
5308 :
5309 2818653 : bool hit = false;
5310 2818653 : Vector3<Real64> RN = (R2 - R1).normalize(); // Make unit vector
5311 2818653 : Real64 const d12 = distance(R1, R2); // Distance between R1 and R2
5312 :
5313 2818653 : auto const &window = state.dataSurface->Surface(IWin);
5314 2818653 : int const window_Enclosure = window.SolarEnclIndex;
5315 2818653 : int const window_iBaseSurf = window.BaseSurf;
5316 2818653 : auto const &window_base = window_iBaseSurf > 0 ? state.dataSurface->Surface(window_iBaseSurf) : window;
5317 2818653 : int const window_base_iExtBoundCond = window_base.ExtBoundCond;
5318 :
5319 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5320 2818653 : if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5321 : // Hit coordinates, if ray hits an obstruction
5322 2308235 : Vector3<Real64> DayltgHitInteriorObstructionHP;
5323 :
5324 76265487 : for (int ISurf = 1; ISurf <= state.dataSurface->TotSurfaces; ++ISurf) {
5325 73988574 : auto const &surface = state.dataSurface->Surface(ISurf);
5326 73988574 : SurfaceClass IType = surface.Class;
5327 73988574 : if ((surface.IsShadowing) || // Shadowing surface
5328 73360072 : ((surface.SolarEnclIndex == window_Enclosure) && // Wall/ceiling/floor is in same zone as window
5329 34720785 : (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf) &&
5330 : (ISurf != window_base_iExtBoundCond))) // Exclude window's base or base-adjacent surfaces
5331 : {
5332 12351769 : hit = PierceSurface(state, ISurf, R1, RN, d12, DayltgHitInteriorObstructionHP); // Check if R2-R1 segment pierces surface
5333 12351769 : if (hit) break; // Segment pierces surface: Don't check the rest
5334 : }
5335 : }
5336 :
5337 2308235 : } else { // Surface octree search
5338 :
5339 510418 : auto const *window_base_p = &window_base;
5340 510418 : auto const &window_base_adjacent = window_base_iExtBoundCond > 0 ? state.dataSurface->Surface(window_base_iExtBoundCond) : window_base;
5341 510418 : auto const *window_base_adjacent_p = &window_base_adjacent;
5342 :
5343 : // Lambda function for the octree to test for surface hit
5344 81568322 : auto surfaceHit = [=, &R1, &hit](SurfaceData const &surface) -> bool {
5345 76735020 : DataSurfaces::SurfaceClass const sClass = surface.Class;
5346 76735020 : Vector3<Real64> HP; // Hit point
5347 76735020 : if ((surface.IsShadowing) || // Shadowing surface
5348 76728114 : ((surface.SolarEnclIndex == window_Enclosure) && // Surface is in same zone as window
5349 2896963 : (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5350 2920163 : (&surface != window_base_p) && (&surface != window_base_adjacent_p))) // Exclude window's base or base-adjacent surfaces
5351 : {
5352 2416651 : hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5353 2416651 : return hit;
5354 : } else {
5355 74318369 : return false;
5356 : }
5357 77245438 : };
5358 :
5359 : // Check octree surface candidates until a hit is found, if any
5360 510418 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
5361 510418 : }
5362 :
5363 2818653 : return hit;
5364 2818653 : } // DayltgHitInteriorObstruction()
5365 :
5366 20412 : bool DayltgHitBetWinObstruction(EnergyPlusData &state,
5367 : int const IWin1, // Surface number of origin window
5368 : int const IWin2, // Surface number of destination window
5369 : Vector3<Real64> const &R1, // Origin of ray (on IWin1) (m)
5370 : Vector3<Real64> const &R2 // Destination of ray (on IWin2) (m)
5371 : )
5372 : {
5373 :
5374 : // SUBROUTINE INFORMATION:
5375 : // AUTHOR Fred Winkelmann
5376 : // DATE WRITTEN Feb 2004
5377 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5378 :
5379 : // PURPOSE OF THIS SUBROUTINE:
5380 : // Determines if a ray from point R1 on window IWin1 to point R2
5381 : // on window IWin2 hits an obstruction
5382 :
5383 : // Preconditions
5384 20412 : assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
5385 :
5386 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5387 : SurfaceClass IType; // Surface type/class
5388 :
5389 20412 : bool hit = false;
5390 20412 : Vector3<Real64> RN = (R2 - R1).normalize(); // Unit vector
5391 :
5392 20412 : Real64 const d12 = distance(R1, R2); // Distance between R1 and R2 (m)
5393 :
5394 20412 : auto const &window1 = state.dataSurface->Surface(IWin1);
5395 20412 : int const window1_iBaseSurf = window1.BaseSurf;
5396 20412 : auto const &window1_base = window1_iBaseSurf > 0 ? state.dataSurface->Surface(window1_iBaseSurf) : window1;
5397 20412 : int const window1_base_iExtBoundCond = window1_base.ExtBoundCond;
5398 :
5399 20412 : auto const &window2 = state.dataSurface->Surface(IWin2);
5400 20412 : int const window2_Enclosure = window2.SolarEnclIndex;
5401 20412 : int const window2_iBaseSurf = window2.BaseSurf;
5402 20412 : auto const &window2_base = window2_iBaseSurf > 0 ? state.dataSurface->Surface(window2_iBaseSurf) : window2;
5403 20412 : int const window2_base_iExtBoundCond = window2_base.ExtBoundCond;
5404 :
5405 : // Preconditions
5406 : // assert( window1.Zone == window2_Zone ); //? This is violated in PurchAirWithDoubleFacadeDaylighting so then why the asymmetry
5407 : // of only checking for wall/roof/floor for window2 zone below?
5408 :
5409 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5410 20412 : if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5411 :
5412 612360 : for (int ISurf = 1; ISurf <= state.dataSurface->TotSurfaces; ++ISurf) {
5413 591948 : auto const &surface = state.dataSurface->Surface(ISurf);
5414 591948 : IType = surface.Class;
5415 591948 : if ((surface.IsShadowing) || // Shadowing surface
5416 591948 : ((surface.SolarEnclIndex == window2_Enclosure) && // Wall/ceiling/floor is in same zone as windows
5417 163296 : (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5418 122472 : (ISurf != window1_iBaseSurf) && (ISurf != window2_iBaseSurf) && // Exclude windows' base surfaces
5419 81648 : (ISurf != window1_base_iExtBoundCond) && (ISurf != window2_base_iExtBoundCond))) // Exclude windows' base-adjacent surfaces
5420 : {
5421 81648 : Vector3<Real64> HP;
5422 81648 : hit = PierceSurface(state, ISurf, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5423 81648 : if (hit) break; // Segment pierces surface: Don't check the rest
5424 81648 : }
5425 : }
5426 :
5427 : } else { // Surface octree search
5428 :
5429 0 : auto const *window1_base_p = &window1_base;
5430 0 : auto const &window1_base_adjacent = window1_base_iExtBoundCond > 0 ? state.dataSurface->Surface(window1_base_iExtBoundCond) : window1_base;
5431 0 : auto const *window1_base_adjacent_p = &window1_base_adjacent;
5432 :
5433 0 : auto const *window2_base_p = &window2_base;
5434 0 : auto const &window2_base_adjacent = (window2_base_iExtBoundCond > 0) ? state.dataSurface->Surface(window2_base_iExtBoundCond) : window2_base;
5435 0 : auto const *window2_base_adjacent_p = &window2_base_adjacent;
5436 :
5437 : // Lambda function for the octree to test for surface hit
5438 0 : auto surfaceHit = [=, &R1, &RN, &hit](SurfaceData const &surface) -> bool {
5439 0 : DataSurfaces::SurfaceClass const sClass = surface.Class;
5440 0 : Vector3<Real64> HP;
5441 0 : if ((surface.IsShadowing) || // Shadowing surface
5442 0 : ((surface.SolarEnclIndex == window2_Enclosure) && // Surface is in same zone as window
5443 0 : (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5444 0 : (&surface != window1_base_p) && (&surface != window2_base_p) && // Exclude windows' base surfaces
5445 0 : (&surface != window1_base_adjacent_p) && (&surface != window2_base_adjacent_p))) // Exclude windows' base-adjacent surfaces
5446 : {
5447 0 : hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5448 0 : return hit;
5449 : } else {
5450 0 : return false;
5451 : }
5452 0 : };
5453 :
5454 : // Check octree surface candidates until a hit is found, if any
5455 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
5456 : }
5457 :
5458 20412 : return hit;
5459 20412 : } // DayltingHitBetWinObstruction()
5460 :
5461 2804678 : void initDaylighting(EnergyPlusData &state, bool const initSurfaceHeatBalancefirstTime)
5462 : {
5463 : // For daylit zones, calculate interior daylight illuminance at reference points and
5464 : // simulate lighting control system to get overhead electric lighting reduction
5465 : // factor due to daylighting.
5466 2804678 : auto &dl = state.dataDayltg;
5467 :
5468 25996598 : for (int SurfNum : state.dataSurface->AllExtSolWindowSurfaceList) {
5469 27709728 : for (auto &refPt : state.dataSurface->SurfaceWindow(SurfNum).refPts) {
5470 4517808 : refPt.illumFromWinRep = refPt.lumWinRep = 0.0;
5471 : }
5472 2804678 : }
5473 :
5474 : // Reset space power reduction factors
5475 22720622 : for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
5476 19915944 : dl->spacePowerReductionFactor(spaceNum) = 1.0;
5477 : }
5478 4262759 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5479 1458081 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5480 1458081 : thisDayltgCtrl.PowerReductionFactor = 1.0;
5481 1458081 : if (state.dataEnvrn->PreviousSolRadPositive) {
5482 : // Reset to zero only if there was solar in the previous timestep, otherwise these are already zero
5483 430121 : dl->enclDaylight(thisDayltgCtrl.enclIndex).InterReflIllFrIntWins = 0.0; // inter-reflected illuminance from interior windows
5484 1006994 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
5485 576873 : auto &refPt = thisDayltgCtrl.refPts(refPtNum);
5486 576873 : refPt.lums[iLum_Illum] = 0.0;
5487 576873 : refPt.glareIndex = 0.0;
5488 576873 : refPt.timeExceedingGlareIndexSetPoint = 0.0;
5489 576873 : refPt.timeExceedingDaylightIlluminanceSetPoint = 0.0;
5490 : }
5491 : }
5492 :
5493 1458081 : if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0) {
5494 722471 : if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination");
5495 722471 : DayltgInteriorIllum(state, daylightCtrlNum);
5496 : }
5497 : }
5498 :
5499 : // The following report variables are valid only for daylit zones/enclosures without interior windows
5500 2804678 : if (state.dataEnvrn->SunIsUp) {
5501 11354044 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
5502 10672942 : if ((state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) ||
5503 720434 : (state.dataViewFactor->EnclSolInfo(enclNum).HasInterZoneWindow))
5504 9233089 : continue;
5505 :
5506 719419 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
5507 2147788 : for (int extWinNum = 1; extWinNum <= thisEnclDaylight.NumOfDayltgExtWins; ++extWinNum) {
5508 1428369 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(extWinNum);
5509 1428369 : WinCover winCover = WinCover::Bare;
5510 4198635 : if (state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
5511 2770266 : (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin))) {
5512 81040 : winCover = WinCover::Shaded;
5513 : }
5514 1428369 : int refPtCount = 0;
5515 2862849 : for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
5516 1434480 : auto &daylCtrl = dl->daylightControl(controlNum);
5517 1434480 : if (daylCtrl.DaylightMethod != DaylightingMethod::SplitFlux) continue;
5518 :
5519 3659411 : for (int refPtNum = 1; refPtNum <= daylCtrl.TotalDaylRefPoints; ++refPtNum) {
5520 2224931 : ++refPtCount; // Count reference points across each daylighting control in the same enclosure
5521 2224931 : auto &refPt = state.dataSurface->SurfaceWindow(IWin).refPts(refPtCount);
5522 2224931 : auto const &daylCtrlRefPt = daylCtrl.refPts(refPtNum);
5523 2224931 : refPt.illumFromWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Illum][(int)winCover];
5524 2224931 : refPt.lumWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Source][(int)winCover];
5525 : }
5526 1428369 : } // for (controlNum)
5527 : } // for (extWinNum)
5528 : } // for (enclNum)
5529 : } // if (SunIsUp)
5530 :
5531 2804678 : if (state.dataEnvrn->SunIsUp && (int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
5532 1015 : if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination for TDD pipes");
5533 1015 : DayltgInteriorTDDIllum(state);
5534 : }
5535 :
5536 4262759 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5537 1458081 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5538 :
5539 : // RJH DElight Modification Begin - Call to DElight electric lighting control subroutine
5540 : // Check if the sun is up and the current Thermal Zone hosts a Daylighting:DElight object
5541 1458081 : if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0 && (thisDayltgCtrl.DaylightMethod == DaylightingMethod::DElight)) {
5542 2037 : int zoneNum = thisDayltgCtrl.zoneIndex;
5543 : // Call DElight interior illuminance and electric lighting control subroutine
5544 2037 : Real64 dPowerReducFac = 1.0; // Return value Electric Lighting Power Reduction Factor for current Zone and Timestep
5545 2037 : Real64 dHISKFFC = state.dataEnvrn->HISKF * DataDElight::LUX2FC;
5546 2037 : Real64 dHISUNFFC = state.dataEnvrn->HISUNF * DataDElight::LUX2FC;
5547 2037 : Real64 dSOLCOS1 = state.dataEnvrn->SOLCOS.x;
5548 2037 : Real64 dSOLCOS2 = state.dataEnvrn->SOLCOS.y;
5549 2037 : Real64 dSOLCOS3 = state.dataEnvrn->SOLCOS.z;
5550 2037 : Real64 dLatitude = state.dataEnvrn->Latitude;
5551 2037 : Real64 dCloudFraction = state.dataEnvrn->CloudFraction;
5552 : // Init Error Flag to 0 (no Warnings or Errors) (returned from DElight)
5553 2037 : int iErrorFlag = 0;
5554 :
5555 2037 : DElightManagerF::DElightElecLtgCtrl(len(state.dataHeatBal->Zone(zoneNum).Name),
5556 2037 : state.dataHeatBal->Zone(zoneNum).Name,
5557 : dLatitude,
5558 : dHISKFFC,
5559 : dHISUNFFC,
5560 : dCloudFraction,
5561 : dSOLCOS1,
5562 : dSOLCOS2,
5563 : dSOLCOS3,
5564 : dPowerReducFac,
5565 : iErrorFlag);
5566 : // Check Error Flag for Warnings or Errors returning from DElight
5567 : // RJH 2008-03-07: If no warnings/errors then read refpt illuminances for standard output reporting
5568 2037 : if (iErrorFlag != 0) {
5569 0 : std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
5570 : // Open DElight Electric Lighting Error File for reading
5571 0 : auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
5572 0 : bool elOpened = iDElightErrorFile.good();
5573 :
5574 : // Sequentially read lines in DElight Electric Lighting Error File
5575 : // and process them using standard EPlus warning/error handling calls
5576 0 : bool bEndofErrFile = false;
5577 0 : while (!bEndofErrFile && elOpened) {
5578 0 : auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
5579 0 : if (cErrorLine.eof) {
5580 0 : bEndofErrFile = true;
5581 0 : continue;
5582 : }
5583 :
5584 : // Is the current line a Warning message?
5585 0 : if (has_prefix(cErrorLine.data, "WARNING: ")) {
5586 0 : cErrorMsg = cErrorLine.data.substr(9);
5587 0 : ShowWarningError(state, cErrorMsg);
5588 : }
5589 : // Is the current line an Error message?
5590 0 : if (has_prefix(cErrorLine.data, "ERROR: ")) {
5591 0 : cErrorMsg = cErrorLine.data.substr(7);
5592 0 : ShowSevereError(state, cErrorMsg);
5593 0 : iErrorFlag = 1;
5594 : }
5595 0 : }
5596 :
5597 : // Close DElight Error File and delete
5598 :
5599 0 : if (elOpened) {
5600 0 : iDElightErrorFile.close();
5601 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
5602 : }
5603 : // If any DElight Error occurred then ShowFatalError to terminate
5604 0 : if (iErrorFlag > 0) {
5605 0 : ShowFatalError(state, "End of DElight Error Messages");
5606 : }
5607 0 : } else { // RJH 2008-03-07: No errors
5608 : // extract reference point illuminance values from DElight Electric Lighting dump file for reporting
5609 : // Open DElight Electric Lighting Dump File for reading
5610 2037 : auto iDElightErrorFile = state.files.outputDelightEldmpFilePath.try_open(state.files.outputControl.delighteldmp); // (THIS_AUTO_OK)
5611 2037 : bool elOpened = iDElightErrorFile.is_open();
5612 :
5613 : // Sequentially read lines in DElight Electric Lighting Dump File
5614 : // and extract refpt illuminances for standard EPlus output handling
5615 2037 : bool bEndofErrFile = false;
5616 2037 : int iDElightRefPt = 0; // Reference Point number for reading DElight Dump File (eplusout.delighteldmp)
5617 8148 : while (!bEndofErrFile && elOpened) {
5618 6111 : auto line = iDElightErrorFile.read<Real64>(); // (THIS_AUTO_OK)
5619 6111 : Real64 dRefPtIllum = line.data; // tmp var for reading RefPt illuminance
5620 6111 : if (line.eof) {
5621 2037 : bEndofErrFile = true;
5622 2037 : continue;
5623 : }
5624 : // Increment refpt counter
5625 4074 : ++iDElightRefPt;
5626 : // Assure refpt index does not exceed number of refpts in this zone
5627 4074 : if (iDElightRefPt <= thisDayltgCtrl.TotalDaylRefPoints) {
5628 4074 : thisDayltgCtrl.refPts(iDElightRefPt).lums[iLum_Illum] = dRefPtIllum;
5629 : }
5630 : }
5631 :
5632 : // Close DElight Electric Lighting Dump File and delete
5633 2037 : if (elOpened) {
5634 2037 : iDElightErrorFile.close();
5635 2037 : FileSystem::removeFile(iDElightErrorFile.filePath);
5636 : };
5637 2037 : }
5638 : // Store the calculated total zone Power Reduction Factor due to DElight daylighting
5639 : // in the ZoneDaylight structure for later use
5640 2037 : thisDayltgCtrl.PowerReductionFactor = dPowerReducFac;
5641 : }
5642 : // RJH DElight Modification End - Call to DElight electric lighting control subroutine
5643 : }
5644 :
5645 2804678 : if (state.dataEnvrn->SunIsUp && !state.dataGlobal->DoingSizing) {
5646 990056 : DayltgInteriorMapIllum(state);
5647 : }
5648 22672022 : for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
5649 39783288 : for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
5650 19915944 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
5651 43139106 : for (int SurfNum = thisSpace.WindowSurfaceFirst; SurfNum <= thisSpace.WindowSurfaceLast; ++SurfNum) {
5652 23223162 : state.dataSurface->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
5653 23223162 : if (IS_SHADED(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
5654 286781 : state.dataSurface->SurfWinFracTimeShadingDeviceOn(SurfNum) = 1.0;
5655 : } else {
5656 22936381 : state.dataSurface->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
5657 : }
5658 : }
5659 19867344 : }
5660 : }
5661 2804678 : }
5662 :
5663 2804678 : void manageDaylighting(EnergyPlusData &state)
5664 : {
5665 2804678 : auto &dl = state.dataDayltg;
5666 :
5667 2804678 : if (state.dataEnvrn->SunIsUp && (state.dataEnvrn->BeamSolarRad + state.dataEnvrn->GndSolarRad + state.dataEnvrn->DifSolarRad > 0.0)) {
5668 7240173 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
5669 6323710 : auto &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
5670 6323710 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || !enclSol.HasInterZoneWindow) continue;
5671 :
5672 1015 : DayltgInterReflIllFrIntWins(state, enclNum);
5673 2030 : for (int daylightCtrlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
5674 1015 : DayltgGlareWithIntWins(state, daylightCtrlNum);
5675 1015 : }
5676 : }
5677 916463 : DayltgElecLightingControl(state);
5678 1888215 : } else if (dl->mapResultsToReport && state.dataGlobal->TimeStep == state.dataGlobal->NumOfTimeStepInHour) {
5679 41 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
5680 22 : ReportIllumMap(state, MapNum);
5681 : }
5682 19 : dl->mapResultsToReport = false;
5683 : }
5684 2804678 : } // manageDaylighting()
5685 :
5686 722471 : void DayltgInteriorIllum(EnergyPlusData &state,
5687 : int const daylightCtrlNum) // Daylighting:Controls number
5688 : {
5689 :
5690 : // SUBROUTINE INFORMATION:
5691 : // AUTHOR Fred Winkelmann
5692 : // DATE WRITTEN July 1997
5693 : // MODIFIED March 2000, FCW: interpolate clear-sky daylight factors using
5694 : // HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
5695 : // only HourOfDay was used
5696 : // Jan 2001, FCW: interpolate in slat angle for windows with blinds
5697 : // that have movable slats
5698 : // Oct 2002, LKL: changed interpolation steps to HourOfDay/WeightNow
5699 : // LastHour/WeightPreviousHour
5700 : // Aug 2003, FCW: fix bug that prevented shadingControlType =
5701 : // MEETDAYLIGHTILLUMINANCESETPOINT from working
5702 : // Mar 2004, FCW: fix bug in calc of illuminance setpoint contribution
5703 : // to background luminance: now it is divided by pi to give cd/m2
5704 : // Mar 2004, FCW: modify to handle daylighting through interior windows
5705 : // June 2009, TH: modified for thermochromic windows
5706 : // Jan 2010, TH (CR 7984): added iterations for switchable windows with shading
5707 : // control of MeetDaylightIlluminanceSetpoint and glare control is active
5708 : // Also corrected bugs (CR 7988) for switchable glazings not related to CR 7984
5709 :
5710 : // PURPOSE OF THIS SUBROUTINE:
5711 : // Using daylighting factors and exterior illuminance, determine
5712 : // the current-hour interior daylight illuminance and glare index
5713 : // at each reference point in a space. Deploy window shading window by window
5714 : // if glare control is active for window and if the acceptable glare index
5715 : // is exceeded at both reference points.
5716 :
5717 : // Called by InitSurfaceHeatBalance.
5718 :
5719 : // REFERENCES:
5720 : // Based on DOE-2.1E subroutine DINTIL.
5721 722471 : auto &dl = state.dataDayltg;
5722 :
5723 722471 : Real64 constexpr tmpSWIterStep(0.05); // step of switching factor, assuming maximum of 20 switching states
5724 :
5725 : int NREFPT; // Number of daylighting reference points
5726 : int iSky1; // Sky type index values for averaging two sky types
5727 : int iSky2;
5728 722471 : Array1D<Real64> SetPnt; // Illuminance setpoint at reference points (lux)
5729 722471 : Array1D<Real64> GLRNEW; // New glare index at reference point
5730 :
5731 722471 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5732 722471 : int enclNum = thisDayltgCtrl.enclIndex;
5733 722471 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
5734 : int ISWFLG; // Switchable glazing flag: =1 if one or more windows in a zone
5735 : // has switchable glazing that adjusts visible transmittance to just meet
5736 : // daylighting setpoint; =0 otherwise.
5737 : int ICtrl; // Window shading control pointer
5738 : Real64 VTRAT; // Ratio between switched and unswitched visible transmittance at normal incidence
5739 : Real64 BACL; // Window background (surround) luminance for glare calc (cd/m2)
5740 : Real64 SkyWeight; // Weighting factor used to average two different sky types
5741 : Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
5742 : // luminous efficacy and horizontal illuminance from averaged sky
5743 : bool GlareFlag; // True if maximum glare is exceeded
5744 :
5745 : Real64 VTRatio; // VT (visible transmittance) ratio = VTNow / VTMaster
5746 : Real64 VTNow; // VT of the time step actual TC window
5747 : Real64 VTMaster; // VT of the base/master TC window
5748 :
5749 722471 : Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> tmpDaylFromWinAtRefPt;
5750 :
5751 722471 : bool breakOuterLoop(false);
5752 722471 : bool continueOuterLoop(false);
5753 :
5754 : struct ShadeGroupLums
5755 : {
5756 : Array1D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> WDAYIL; // Illuminance from window at ref-point
5757 : Array1D<std::array<Real64, (int)Lum::Num>> RDAYIL; // Illuminance from window at ref-point after closing shade
5758 : Real64 switchedWinLum;
5759 : Real64 unswitchedWinLum;
5760 : Real64 switchedTvis;
5761 : Real64 unswitchedTvis;
5762 : Real64 lumRatio;
5763 : };
5764 :
5765 722471 : Array1D<ShadeGroupLums> shadeGroupsLums;
5766 :
5767 : // Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, iLum_Num>> WDAYIL; // Illuminance from window at reference point
5768 : // (second index)
5769 : // the number of shade deployment groups (third index)
5770 : // Array2D<std::array<Real64, (int)DataSurfaces::WinCover::Num>> WBACLU; // Background illuminance from window at reference point (second index)
5771 : // the number of shade deployment groups (third index)
5772 : // Array2D<std::array<Real64, iLum_Num>> RDAYIL; // Illuminance from window at reference point after closing shade
5773 : // Array2D<Real64> RBACLU; // Background illuminance from window at reference point after closing shade
5774 : // Array1D<Real64> DILLSW; // Illuminance a ref point from a group of windows that can be switched together,
5775 : // Array1D<Real64> DILLUN; // and from those that aren't (lux)
5776 : // Array1D<Real64> TVIS1; // Visible transmittance at normal incidence of unswitched glazing
5777 : // Array1D<Real64> TVIS2; // Visible transmittance at normal incidence of fully-switched glazing
5778 : // Array1D<Real64> ASETIL; // Illuminance ratio (lux)
5779 :
5780 722471 : if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) return;
5781 :
5782 720434 : NREFPT = thisDayltgCtrl.TotalDaylRefPoints;
5783 :
5784 720434 : if (dl->DayltgInteriorIllum_firstTime) {
5785 64 : dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5786 64 : dl->DayltgInteriorIllum_firstTime = false;
5787 : }
5788 :
5789 : // size these for the maximum of the shade deployment order
5790 720434 : shadeGroupsLums.allocate(dl->maxShadeDeployOrderExtWins);
5791 825315 : for (auto &shadeGroupLums : shadeGroupsLums) {
5792 104881 : shadeGroupLums.WDAYIL.allocate(dl->maxControlRefPoints);
5793 104881 : shadeGroupLums.RDAYIL.allocate(dl->maxControlRefPoints);
5794 : }
5795 :
5796 : // Three arrays to save original clear and dark (fully switched) states'
5797 : // zone/window daylighting properties.
5798 720434 : tmpDaylFromWinAtRefPt.allocate(dl->maxNumRefPtInAnyDaylCtrl, dl->maxEnclSubSurfaces);
5799 :
5800 720434 : SetPnt.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5801 720434 : dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5802 720434 : GLRNEW.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5803 :
5804 1683884 : for (int iRefPt = 1; iRefPt <= (int)dl->maxNumRefPtInAnyDaylCtrl; ++iRefPt) {
5805 11332598 : for (int iExtWin = 1; iExtWin <= (int)dl->maxEnclSubSurfaces; ++iExtWin) {
5806 10369148 : auto &tmpDayl = tmpDaylFromWinAtRefPt(iRefPt, iExtWin);
5807 10369148 : tmpDayl[iLum_Illum] = tmpDayl[iLum_Back] = tmpDayl[iLum_Source] = {0.0, 0.0};
5808 : }
5809 : }
5810 :
5811 : // Initialize reference point illuminance and window background luminance
5812 1677232 : for (int IL = 1; IL <= NREFPT; ++IL) {
5813 956798 : auto &refPt = thisDayltgCtrl.refPts(IL);
5814 956798 : SetPnt(IL) = refPt.illumSetPoint;
5815 956798 : dl->DaylIllum(IL) = 0.0;
5816 956798 : refPt.lums[iLum_Back] = 0.0;
5817 : }
5818 :
5819 720434 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
5820 337939 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
5821 337939 : iSky1 = (int)SkyType::Clear;
5822 337939 : iSky2 = (int)SkyType::ClearTurbid;
5823 382495 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
5824 78949 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
5825 78949 : iSky1 = (int)SkyType::ClearTurbid;
5826 78949 : iSky2 = (int)SkyType::Intermediate;
5827 : } else { // Sky is average of intermediate and overcast
5828 303546 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
5829 303546 : iSky1 = (int)SkyType::Intermediate;
5830 303546 : iSky2 = (int)SkyType::Overcast;
5831 : }
5832 :
5833 : // First loop over exterior windows associated with this zone. The window may be an exterior window in
5834 : // the zone or an exterior window in an adjacent zone that shares an interior window with the zone.
5835 : // Find contribution of each window to the daylight illum and to the glare numerator at each reference point.
5836 : // Use shading flags set in WindowShadingManager.
5837 2155929 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5838 1435495 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5839 :
5840 : // Added TH 6/29/2009 for thermochromic windows
5841 1435495 : VTRatio = 1.0;
5842 1435495 : if (NREFPT > 0) {
5843 1435495 : int const IConst = state.dataSurface->Surface(IWin).Construction;
5844 1435495 : auto const &construction = state.dataConstruction->Construct(IConst);
5845 1435495 : if (construction.TCFlag == 1) {
5846 : // For thermochromic windows, daylight and glare factors are always calculated
5847 : // based on the master construction. They need to be adjusted by the VTRatio, including:
5848 : // ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
5849 : // DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
5850 0 : VTNow = General::POLYF(1.0, construction.TransVisBeamCoef);
5851 0 : VTMaster = General::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConst).TransVisBeamCoef);
5852 0 : VTRatio = VTNow / VTMaster;
5853 : }
5854 : }
5855 :
5856 4177794 : bool ShadedOrDiffusingGlassWin = state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
5857 2742299 : (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin));
5858 :
5859 1435495 : Real64 wgtCurrHr = state.dataGlobal->WeightNow;
5860 1435495 : Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
5861 :
5862 1435495 : std::array<Illums, (int)DataSurfaces::WinCover::Num> SFHR; // Sky source luminance factor for sky type, bare/shaded window
5863 1435495 : std::array<Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
5864 1435495 : std::array<Illums, (int)DataSurfaces::WinCover::Num> BFHR; // Sky background luminance factor for sky type, bare/shaded window
5865 :
5866 : // Loop over reference points
5867 3661441 : for (int IL = 1; IL <= NREFPT; ++IL) {
5868 :
5869 2225946 : auto const &daylFacCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, 1);
5870 2225946 : auto const &daylFacPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, 1);
5871 : // Daylight factors for current sun position
5872 2225946 : auto const &illFacCurr = daylFacCurr[iLum_Illum];
5873 2225946 : auto const &illFacPrev = daylFacPrev[iLum_Illum];
5874 2225946 : auto &dfhr = DFHR[iWinCover_Bare];
5875 2225946 : auto const &backFacCurr = daylFacCurr[iLum_Back];
5876 2225946 : auto const &backFacPrev = daylFacPrev[iLum_Back];
5877 2225946 : auto &bfhr = BFHR[iWinCover_Bare];
5878 2225946 : auto const &sourceFacCurr = daylFacCurr[iLum_Source];
5879 2225946 : auto const &sourceFacPrev = daylFacPrev[iLum_Source];
5880 2225946 : auto &sfhr = SFHR[iWinCover_Bare];
5881 :
5882 2225946 : auto const &daylFac2Curr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, 2);
5883 2225946 : auto const &daylFac2Prev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, 2);
5884 :
5885 2225946 : auto const &illFac2Curr = daylFac2Curr[iLum_Illum];
5886 2225946 : auto const &illFac2Prev = daylFac2Prev[iLum_Illum];
5887 2225946 : auto &dfhr2 = DFHR[iWinCover_Shaded];
5888 2225946 : auto const &backFac2Curr = daylFac2Curr[iLum_Back];
5889 2225946 : auto const &backFac2Prev = daylFac2Prev[iLum_Back];
5890 2225946 : auto &bfhr2 = BFHR[iWinCover_Shaded];
5891 2225946 : auto const &sourceFac2Curr = daylFac2Curr[iLum_Source];
5892 2225946 : auto const &sourceFac2Prev = daylFac2Prev[iLum_Source];
5893 2225946 : auto &sfhr2 = SFHR[iWinCover_Shaded];
5894 :
5895 2225946 : int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
5896 2225946 : int slatAngLo = SurfWinSlatsAngIndex + 1;
5897 2225946 : int slatAngHi = min(Material::MaxSlatAngs + 1, slatAngLo + 1);
5898 2225946 : Real64 interpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
5899 :
5900 2225946 : auto const &daylFacLoCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, slatAngLo);
5901 2225946 : auto const &daylFacLoPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, slatAngLo);
5902 2225946 : auto const &daylFacHiCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL, slatAngHi);
5903 2225946 : auto const &daylFacHiPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL, slatAngHi);
5904 :
5905 2225946 : auto const &illFacLoCurr = daylFacLoCurr[iLum_Illum];
5906 2225946 : auto const &illFacLoPrev = daylFacLoPrev[iLum_Illum];
5907 2225946 : auto const &illFacHiCurr = daylFacHiCurr[iLum_Illum];
5908 2225946 : auto const &illFacHiPrev = daylFacHiPrev[iLum_Illum];
5909 :
5910 2225946 : auto const &backFacLoCurr = daylFacLoCurr[iLum_Back];
5911 2225946 : auto const &backFacLoPrev = daylFacLoPrev[iLum_Back];
5912 2225946 : auto const &backFacHiCurr = daylFacHiCurr[iLum_Back];
5913 2225946 : auto const &backFacHiPrev = daylFacHiPrev[iLum_Back];
5914 :
5915 2225946 : auto const &sourceFacLoCurr = daylFacLoCurr[iLum_Source];
5916 2225946 : auto const &sourceFacLoPrev = daylFacLoPrev[iLum_Source];
5917 2225946 : auto const &sourceFacHiCurr = daylFacHiCurr[iLum_Source];
5918 2225946 : auto const &sourceFacHiPrev = daylFacHiPrev[iLum_Source];
5919 :
5920 11129730 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5921 :
5922 : // ===Bare window===
5923 : // Sky daylight factor for sky type (second index), bare/shaded window (first index)
5924 8903784 : dfhr.sky[iSky] = VTRatio * (wgtCurrHr * illFacCurr.sky[iSky] + wgtPrevHr * illFacPrev.sky[iSky]);
5925 8903784 : bfhr.sky[iSky] = VTRatio * (wgtCurrHr * backFacCurr.sky[iSky] + wgtPrevHr * backFacPrev.sky[iSky]);
5926 8903784 : sfhr.sky[iSky] = VTRatio * (wgtCurrHr * sourceFacCurr.sky[iSky] + wgtPrevHr * sourceFacPrev.sky[iSky]);
5927 :
5928 8903784 : if (ShadedOrDiffusingGlassWin) {
5929 :
5930 : // ===Shaded window or window with diffusing glass===
5931 638724 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
5932 : // Shade, screen, blind with fixed slats, or diffusing glass
5933 638724 : dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illFac2Curr.sky[iSky] + wgtPrevHr * illFac2Prev.sky[iSky]);
5934 638724 : bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backFac2Curr.sky[iSky] + wgtPrevHr * backFac2Prev.sky[iSky]);
5935 638724 : sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceFac2Curr.sky[iSky] + wgtPrevHr * sourceFac2Prev.sky[iSky]);
5936 :
5937 : } else { // Blind with movable slats
5938 0 : Real64 illSkyCurr = General::Interp(illFacLoCurr.sky[iSky], illFacHiCurr.sky[iSky], interpFac);
5939 0 : Real64 backSkyCurr = General::Interp(backFacLoCurr.sky[iSky], backFacHiCurr.sky[iSky], interpFac);
5940 0 : Real64 sourceSkyCurr = General::Interp(sourceFacLoCurr.sky[iSky], sourceFacHiCurr.sky[iSky], interpFac);
5941 :
5942 0 : Real64 illSkyPrev = General::Interp(illFacLoPrev.sky[iSky], illFacHiPrev.sky[iSky], interpFac);
5943 0 : Real64 backSkyPrev = General::Interp(backFacLoPrev.sky[iSky], backFacHiPrev.sky[iSky], interpFac);
5944 0 : Real64 sourceSkyPrev = General::Interp(sourceFacLoPrev.sky[iSky], sourceFacHiPrev.sky[iSky], interpFac);
5945 :
5946 0 : dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illSkyCurr + wgtPrevHr * illSkyPrev);
5947 0 : bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backSkyCurr + wgtPrevHr * backSkyPrev);
5948 0 : sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceSkyCurr + wgtPrevHr * sourceSkyPrev);
5949 :
5950 : } // End of check if window has blind with movable slats
5951 : } // End of check if window is shaded or has diffusing glass
5952 : } // for (iSky)
5953 :
5954 : // Sun daylight factor for bare/shaded window
5955 4451892 : DFHR[iWinCover_Bare].sun =
5956 2225946 : VTRatio * (wgtCurrHr * (illFacCurr.sun + illFacCurr.sunDisk) + wgtPrevHr * (illFacPrev.sun + illFacPrev.sunDisk));
5957 :
5958 : // Sun background luminance factor for bare/shaded window
5959 4451892 : BFHR[iWinCover_Bare].sun =
5960 2225946 : VTRatio * (wgtCurrHr * (backFacCurr.sun + backFacCurr.sunDisk) + wgtPrevHr * (backFacPrev.sun + backFacPrev.sunDisk));
5961 :
5962 : // Sun source luminance factor for bare/shaded window
5963 4451892 : SFHR[iWinCover_Bare].sun =
5964 2225946 : VTRatio * (wgtCurrHr * (sourceFacCurr.sun + sourceFacCurr.sunDisk) + wgtPrevHr * (sourceFacPrev.sun + sourceFacPrev.sunDisk));
5965 :
5966 2225946 : if (ShadedOrDiffusingGlassWin) {
5967 :
5968 : // ===Shaded window or window with diffusing glass===
5969 159681 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
5970 : // Shade, screen, blind with fixed slats, or diffusing glass
5971 159681 : DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * illFac2Curr.sun + wgtPrevHr * illFac2Prev.sun);
5972 159681 : BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * backFac2Curr.sun + wgtPrevHr * backFac2Prev.sun);
5973 159681 : SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * sourceFac2Curr.sun + wgtPrevHr * sourceFac2Prev.sun);
5974 :
5975 159681 : if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
5976 159681 : DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * illFac2Curr.sunDisk + wgtPrevHr * illFac2Prev.sunDisk);
5977 159681 : BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * backFac2Curr.sunDisk + wgtPrevHr * backFac2Prev.sunDisk);
5978 159681 : SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * sourceFac2Curr.sunDisk + wgtPrevHr * sourceFac2Prev.sunDisk);
5979 : }
5980 :
5981 : } else { // Blind with movable slats
5982 : // int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
5983 : // int slatAngLo = SurfWinSlatsAngIndex + 1;
5984 : // int slatAngHi = min(Material::MaxSlatAngs + 1, slatAngLo + 1);
5985 0 : Real64 SurfWinSlatsAngInterpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
5986 :
5987 0 : Real64 DaylIllFacSunNow = General::Interp(illFacLoCurr.sun, illFacHiCurr.sun, SurfWinSlatsAngInterpFac);
5988 0 : Real64 DaylBackFacSunNow = General::Interp(backFacLoCurr.sun, backFacHiCurr.sun, SurfWinSlatsAngInterpFac);
5989 0 : Real64 DaylSourceFacSunNow = General::Interp(sourceFacLoCurr.sun, sourceFacHiCurr.sun, SurfWinSlatsAngInterpFac);
5990 0 : Real64 DaylIllFacSunPrev = General::Interp(illFacLoPrev.sun, illFacHiPrev.sun, SurfWinSlatsAngInterpFac);
5991 0 : Real64 DaylBackFacSunPrev = General::Interp(backFacLoPrev.sun, backFacHiPrev.sun, SurfWinSlatsAngInterpFac);
5992 0 : Real64 DaylSourceFacSunPrev = General::Interp(sourceFacLoPrev.sun, sourceFacHiPrev.sun, SurfWinSlatsAngInterpFac);
5993 0 : DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * DaylIllFacSunNow + wgtPrevHr * DaylIllFacSunPrev);
5994 0 : BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * DaylBackFacSunNow + wgtPrevHr * DaylBackFacSunPrev);
5995 0 : SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * DaylSourceFacSunNow + wgtPrevHr * DaylSourceFacSunPrev);
5996 :
5997 : // We add the contribution from the solar disk if slats do not block beam solar
5998 : // TH CR 8010, DaylIllFacSunDisk needs to be interpolated
5999 0 : if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
6000 0 : Real64 DaylIllFacSunDiskNow = General::Interp(illFacLoCurr.sunDisk, illFacHiCurr.sunDisk, SurfWinSlatsAngInterpFac);
6001 0 : Real64 DaylBackFacSunDiskNow = General::Interp(backFacLoCurr.sunDisk, backFacHiCurr.sunDisk, SurfWinSlatsAngInterpFac);
6002 0 : Real64 DaylSourceFacSunDiskNow = General::Interp(sourceFacLoCurr.sunDisk, sourceFacHiCurr.sunDisk, SurfWinSlatsAngInterpFac);
6003 0 : Real64 DaylIllFacSunDiskPrev = General::Interp(illFacLoPrev.sunDisk, illFacHiPrev.sunDisk, SurfWinSlatsAngInterpFac);
6004 0 : Real64 DaylBackFacSunDiskPrev = General::Interp(backFacLoPrev.sunDisk, backFacHiPrev.sunDisk, SurfWinSlatsAngInterpFac);
6005 0 : Real64 DaylSourceFacSunDiskPrev = General::Interp(sourceFacLoPrev.sunDisk, sourceFacHiPrev.sunDisk, SurfWinSlatsAngInterpFac);
6006 0 : DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * DaylIllFacSunDiskNow + wgtPrevHr * DaylIllFacSunDiskPrev);
6007 0 : BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * DaylBackFacSunDiskNow + wgtPrevHr * DaylBackFacSunDiskPrev);
6008 0 : SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * DaylSourceFacSunDiskNow + wgtPrevHr * DaylSourceFacSunDiskPrev);
6009 : }
6010 : } // End of check if window has blind with movable slats
6011 : } // End of check if window is shaded or has diffusing glass
6012 :
6013 : // Get illuminance at ref point from bare and shaded window by
6014 : // multiplying daylight factors by exterior horizontal illuminance
6015 :
6016 : // Adding 0.001 in the following prevents zero HorIllSky in early morning or late evening when sun
6017 : // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
6018 2225946 : auto const &gilskCurr = dl->horIllum[state.dataGlobal->HourOfDay];
6019 2225946 : auto const &gilskPrev = dl->horIllum[state.dataGlobal->PreviousHour];
6020 :
6021 : // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
6022 : // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
6023 : // also calculated in DayltgLuminousEfficacy.
6024 : Real64 horIllSky1 =
6025 2225946 : state.dataGlobal->WeightNow * gilskCurr.sky[iSky1] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky1] + 0.001;
6026 : Real64 horIllSky2 =
6027 2225946 : state.dataGlobal->WeightNow * gilskCurr.sky[iSky2] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky2] + 0.001;
6028 :
6029 2225946 : HorIllSkyFac = state.dataEnvrn->HISKF / ((1 - SkyWeight) * horIllSky2 + SkyWeight * horIllSky1);
6030 :
6031 2225946 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6032 2225946 : auto &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6033 4611573 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
6034 4451892 : auto const &dfhr = DFHR[iWinCover];
6035 4451892 : auto const &bfhr = BFHR[iWinCover];
6036 4451892 : auto const &sfhr = SFHR[iWinCover];
6037 :
6038 : // What is this?
6039 4451892 : if (iWinCover == iWinCover_Shaded && !ShadedOrDiffusingGlassWin) break;
6040 :
6041 2385627 : daylFromWinAtRefPt[iLum_Illum][iWinCover] =
6042 2385627 : dfhr.sun * state.dataEnvrn->HISUNF +
6043 2385627 : HorIllSkyFac * (dfhr.sky[iSky1] * SkyWeight * horIllSky1 + dfhr.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
6044 2385627 : daylFromWinAtRefPt[iLum_Back][iWinCover] =
6045 2385627 : bfhr.sun * state.dataEnvrn->HISUNF +
6046 2385627 : HorIllSkyFac * (bfhr.sky[iSky1] * SkyWeight * horIllSky1 + bfhr.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
6047 2385627 : daylFromWinAtRefPt[iLum_Source][iWinCover] =
6048 2385627 : sfhr.sun * state.dataEnvrn->HISUNF +
6049 2385627 : HorIllSkyFac * (sfhr.sky[iSky1] * SkyWeight * horIllSky1 + sfhr.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
6050 :
6051 2385627 : daylFromWinAtRefPt[iLum_Source][iWinCover] = max(daylFromWinAtRefPt[iLum_Source][iWinCover], 0.0);
6052 :
6053 : // Added TH 1/21/2010 - save the original clear and dark (fully switched) states'
6054 : // zone daylighting values, needed for switachable glazings
6055 2385627 : tmpDayl[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
6056 2385627 : tmpDayl[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
6057 2385627 : tmpDayl[iLum_Source][iWinCover] = daylFromWinAtRefPt[iLum_Source][iWinCover];
6058 : } // for for (iWinCover)
6059 :
6060 : } // End of reference point loop, IL
6061 : } // End of first loop over exterior windows associated with this zone
6062 :
6063 : // Initialize flag that one or more windows has switchable glazing
6064 : // control that adjusts visible transmittance to just meet dayltg setpoint
6065 : // (and the window has not already been switched)
6066 720434 : ISWFLG = 0;
6067 :
6068 : // Second loop over windows. Find total daylight illuminance and background luminance
6069 : // for each ref pt from all exterior windows associated with the zone. Use shading flags.
6070 : // This illuminance excludes contribution of inter-reflected illuminance produced by solar
6071 : // entering the zone through interior windows (which is calculated in DayltgInterReflIllFrIntWins.
6072 :
6073 2155929 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
6074 1435495 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
6075 1435495 : int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
6076 1435495 : if (state.dataSurface->Surface(IWin).HasShadeControl && ISWFLG == 0) {
6077 121929 : if (state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
6078 665 : state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
6079 665 : ISWFLG = 1;
6080 : }
6081 :
6082 : // Determine if illuminance contribution is from bare or shaded window
6083 : // For switchable glazings with shading control type of WSCT_MeetDaylIlumSetp,
6084 : // the shading flag is initialized at GlassConditionallyLightened (20), and
6085 : // the window is initialized at clear state: IS = 1
6086 : // For other windows with glare control, the shading flag is initialized at >10, to be determined
6087 1435495 : int IS = findWinShadingStatus(state, IWin);
6088 :
6089 3661441 : for (int IL = 1; IL <= NREFPT; ++IL) {
6090 2225946 : auto &refPt = thisDayltgCtrl.refPts(IL);
6091 2225946 : dl->DaylIllum(IL) += refPt.extWins(loop).lums[iLum_Illum][IS - 1];
6092 2225946 : refPt.lums[iLum_Back] += refPt.extWins(loop).lums[iLum_Back][IS - 1];
6093 : }
6094 : } // End of second window loop over exterior windows associated with this zone
6095 :
6096 : // Optical switching control (e.g. electrochromic glass) to adjust
6097 : // window's vis trans downward so daylight level equals or is as
6098 : // close as possible to the illuminance setpoint at first reference point.
6099 : // Assumes vis trans in the fully switched state is less than that in the
6100 : // unswitched state. Assumes some windows in a space may have this control and
6101 : // others not.
6102 :
6103 720434 : int count = 0;
6104 :
6105 : // If daylight illuminance is above setpoint, allow switching
6106 720434 : if (ISWFLG != 0 && dl->DaylIllum(1) > SetPnt(1)) {
6107 :
6108 : // array of flags to indicate that previously groups would have already shaded this window
6109 385 : Array1D_bool previously_shaded;
6110 385 : previously_shaded.dimension(dl->maxDayltgExtWins, false);
6111 :
6112 : // Third loop over windows. Get illuminance at ref pt 1 from
6113 : // windows that can be switched (DILLSW) with a group and those that can't (DILLUN).
6114 : // Windows that can be switched are initially in the unswitched state. For subsequent
6115 : // groups the windows in previous groups are fully switched.
6116 1155 : for (auto &shadeGroupLum : shadeGroupsLums) {
6117 770 : shadeGroupLum.switchedWinLum = shadeGroupLum.unswitchedWinLum = 0.0;
6118 : }
6119 :
6120 1155 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6121 770 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6122 770 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6123 2310 : for (const int IWin : listOfExtWin) {
6124 1540 : ++count;
6125 : // need to map back to the original order of the "loop" to not change all the other data structures
6126 1540 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6127 1540 : if (loop == 0) continue;
6128 :
6129 1540 : if (!state.dataSurface->Surface(IWin).HasShadeControl) continue;
6130 :
6131 1540 : int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
6132 1540 : int IS = findWinShadingStatus(state, IWin);
6133 :
6134 1540 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Illum];
6135 1540 : if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened &&
6136 2310 : state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
6137 770 : !previously_shaded(loop)) {
6138 770 : shadeGroupLums.switchedWinLum += daylFromWinAtRefPt[IS - 1];
6139 770 : previously_shaded(loop) = true;
6140 : } else {
6141 770 : shadeGroupLums.unswitchedWinLum += !previously_shaded(loop) ? daylFromWinAtRefPt[IS - 1] : daylFromWinAtRefPt[iWinCover_Shaded];
6142 : }
6143 770 : } // for (IWin)
6144 : } // for (igroup)
6145 :
6146 : // Transmittance multiplier
6147 1155 : for (auto &shadeGroupLums : shadeGroupsLums) {
6148 770 : shadeGroupLums.lumRatio = (SetPnt(1) - shadeGroupLums.unswitchedWinLum) / (shadeGroupLums.switchedWinLum + 0.00001);
6149 : }
6150 :
6151 : // ASETIL < 1 means there's enough light, so check for switching
6152 :
6153 : // Fourth loop over windows to determine which to switch
6154 : // iterate in the order that the shades are specified in WindowShadeControl
6155 385 : count = 0;
6156 385 : breakOuterLoop = false;
6157 385 : continueOuterLoop = false;
6158 1155 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6159 770 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6160 770 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6161 :
6162 2310 : for (const int IWin : listOfExtWin) {
6163 1540 : ++count;
6164 1540 : auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
6165 : // need to map back to the original order of the "loop" to not change all the other data structures
6166 1540 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6167 1540 : if (loop > 0 && shadeGroupLums.lumRatio < 1.0) {
6168 :
6169 1022 : int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
6170 1022 : if (!state.dataSurface->Surface(IWin).HasShadeControl) {
6171 0 : continueOuterLoop = true;
6172 0 : continue;
6173 : }
6174 1764 : if (state.dataSurface->SurfWinShadingFlag(IWin) != WinShadingType::GlassConditionallyLightened ||
6175 742 : state.dataSurface->WindowShadingControl(ICtrl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
6176 280 : continueOuterLoop = true;
6177 280 : continue;
6178 : }
6179 :
6180 742 : int const IConst = state.dataSurface->SurfActiveConstruction(IWin);
6181 : // Vis trans at normal incidence of unswitched glass
6182 742 : shadeGroupLums.unswitchedTvis =
6183 742 : General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
6184 :
6185 : // Vis trans at normal incidence of fully switched glass
6186 742 : int const IConstShaded = state.dataSurface->Surface(IWin).activeShadedConstruction;
6187 742 : shadeGroupLums.switchedTvis =
6188 742 : General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
6189 :
6190 : // Reset shading flag to indicate that window is shaded by being partially or fully switched
6191 742 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
6192 :
6193 : // ASETIL < 0 means illuminance from non-daylight-switchable windows exceeds setpoint,
6194 : // so completely switch all daylight-switchable windows to minimize solar gain
6195 742 : if (shadeGroupLums.lumRatio <= 0.0) {
6196 0 : state.dataSurface->SurfWinSwitchingFactor(IWin) = 1.0;
6197 0 : state.dataSurface->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
6198 : } else {
6199 : // Case where 0 < ASETIL < 1: darken glass in all
6200 : // daylight-switchable windows to just meet illuminance setpoint
6201 : // From this equation: SETPNT(1) = DILLUN + DILLSW/TVIS1 * VisTransSelected
6202 742 : state.dataSurface->SurfWinVisTransSelected(IWin) =
6203 742 : max(shadeGroupLums.switchedTvis, shadeGroupLums.lumRatio * shadeGroupLums.unswitchedTvis) + 0.000001;
6204 742 : state.dataSurface->SurfWinSwitchingFactor(IWin) =
6205 742 : (shadeGroupLums.unswitchedTvis - state.dataSurface->SurfWinVisTransSelected(IWin)) /
6206 742 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis + 0.000001);
6207 : // bound switching factor between 0 and 1
6208 742 : state.dataSurface->SurfWinSwitchingFactor(IWin) = min(1.0, state.dataSurface->SurfWinSwitchingFactor(IWin));
6209 742 : state.dataSurface->SurfWinSwitchingFactor(IWin) = max(0.0, state.dataSurface->SurfWinSwitchingFactor(IWin));
6210 : }
6211 :
6212 : // Adjust daylight quantities based on ratio between switched and unswitched visible transmittance
6213 1484 : for (int IL = 1; IL <= NREFPT; ++IL) {
6214 : // DaylIllum(IL) and BacLum(IL) were calculated at the clear state: IS = 1,
6215 : // and need to adjusted for intermediate switched state at VisTransSelected: IS = 2
6216 742 : int IS = 1;
6217 :
6218 742 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6219 742 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6220 :
6221 742 : VTRAT = state.dataSurface->SurfWinVisTransSelected(IWin) / (shadeGroupLums.unswitchedTvis + 0.000001);
6222 742 : dl->DaylIllum(IL) += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Illum][IS - 1];
6223 742 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Back][IS - 1];
6224 :
6225 : // Adjust illum, background illum and source luminance for this window in intermediate switched state
6226 : // for later use in the DayltgGlare calc because SurfaceWindow(IWin)%ShadingFlag = WinShadingType::SwitchableGlazing = 2
6227 742 : IS = 2;
6228 742 : VTRAT = state.dataSurface->SurfWinVisTransSelected(IWin) / (shadeGroupLums.switchedTvis + 0.000001);
6229 742 : daylFromWinAtRefPt[iLum_Illum][IS - 1] = VTRAT * tmpDayl[iLum_Illum][IS - 1];
6230 742 : daylFromWinAtRefPt[iLum_Back][IS - 1] = VTRAT * tmpDayl[iLum_Back][IS - 1];
6231 742 : daylFromWinAtRefPt[iLum_Source][IS - 1] = VTRAT * tmpDayl[iLum_Source][IS - 1];
6232 : } // for (IL)
6233 : } // if (loop > 0 && ASETIL < 1)
6234 : // If new daylight does not exceed the illuminance setpoint, done, no more checking other groups of switchable glazings
6235 1260 : if (dl->DaylIllum(1) <= SetPnt(1)) {
6236 0 : breakOuterLoop = true;
6237 0 : break;
6238 : }
6239 770 : } // for (Win)
6240 770 : if (breakOuterLoop) break;
6241 770 : if (continueOuterLoop) continue;
6242 : } // for (igroup)
6243 :
6244 385 : } // ISWFLG /= 0 .AND. DaylIllum(1) > SETPNT(1)
6245 :
6246 : // loop over windows to do luminance based control
6247 720434 : count = 0;
6248 823985 : for (int igroup = 1; igroup <= (int)thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6249 226810 : for (int const IWin : thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1]) {
6250 123259 : ++count;
6251 123259 : int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
6252 123259 : WindowShadingControlType shCtrlType = state.dataSurface->WindowShadingControl(ICtrl).shadingControlType;
6253 123259 : if (!((shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffMidNight) ||
6254 : (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffSunset) ||
6255 : (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffNextMorning)))
6256 121929 : continue;
6257 : // need to map back to the original order of the "loop" to not change all the other data structures
6258 1330 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6259 1330 : if (loop == 0) continue;
6260 :
6261 1330 : WinShadingType currentFlag = state.dataSurface->SurfWinShadingFlag(IWin);
6262 1330 : WinShadingType ShType = state.dataSurface->WindowShadingControl(ICtrl).ShadingType;
6263 1330 : if ((currentFlag != WinShadingType::IntShadeConditionallyOff) && (currentFlag != WinShadingType::GlassConditionallyLightened) &&
6264 714 : (currentFlag != WinShadingType::ExtShadeConditionallyOff) && (currentFlag != WinShadingType::IntBlindConditionallyOff) &&
6265 714 : (currentFlag != WinShadingType::ExtBlindConditionallyOff) && (currentFlag != WinShadingType::BGShadeConditionallyOff) &&
6266 : (currentFlag != WinShadingType::BGBlindConditionallyOff))
6267 714 : continue;
6268 :
6269 616 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums;
6270 616 : if (daylFromWinAtRefPt[iLum_Source][iWinCover_Bare] > state.dataSurface->WindowShadingControl(ICtrl).SetPoint2) {
6271 : // shade on if luminance of this window is above setpoint
6272 0 : state.dataSurface->SurfWinShadingFlag(IWin) = ShType;
6273 : // update total illuminance and background luminance
6274 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6275 0 : dl->DaylIllum(IL) += daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
6276 0 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] +=
6277 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
6278 : }
6279 : } else {
6280 : // shade off if luminance is below setpoint
6281 616 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6282 : }
6283 103551 : } // for (IWin)
6284 : } // for (igroup)
6285 :
6286 : // Calculate glare index at each reference point assuming the daylight illuminance setpoint is
6287 : // met at both reference points, either by daylight or electric lights
6288 1677232 : for (int IL = 1; IL <= NREFPT; ++IL) {
6289 956798 : auto &refPt = thisDayltgCtrl.refPts(IL);
6290 956798 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, refPt.lums[iLum_Back]);
6291 : // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,1,loop) for unshaded windows, and
6292 : // ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded windows
6293 956798 : refPt.glareIndex = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6294 : }
6295 :
6296 : // Check if glare level is less than maximum allowed at each ref pt. If maximum
6297 : // is exceeded at either ref pt, attempt to reduce glare to acceptable level by closing
6298 : // shading device on windows that have shades that have not already been closed.
6299 720434 : GlareFlag = false;
6300 1531519 : for (auto const &refPt : thisDayltgCtrl.refPts) {
6301 925380 : if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) {
6302 114295 : GlareFlag = true;
6303 114295 : break;
6304 : }
6305 : }
6306 :
6307 720434 : if (GlareFlag) {
6308 : bool blnCycle;
6309 : bool GlareOK;
6310 : Real64 tmpMult;
6311 : // Glare is too high at a ref pt. Loop through windows.
6312 114295 : count = 0;
6313 :
6314 114295 : continueOuterLoop = false;
6315 158423 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6316 57952 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6317 57952 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6318 :
6319 57952 : int countBeforeListOfExtWinLoop = count;
6320 57952 : bool atLeastOneGlareControlIsActive = false;
6321 :
6322 132912 : for (const int IWin : listOfExtWin) {
6323 74960 : ++count;
6324 : // need to map back to the original order of the "loop" to not change all the other data structures
6325 74960 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6326 74960 : if (loop == 0) continue;
6327 :
6328 74960 : auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
6329 : // Check if window is eligible for glare control
6330 : // TH 1/21/2010. Switchable glazings already in partially switched state
6331 : // should be allowed to further dim to control glare
6332 : // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
6333 149920 : if (NOT_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(state.dataSurface->SurfWinShadingFlag(IWin)) ||
6334 74960 : ANY_BLIND(state.dataSurface->SurfWinShadingFlag(IWin))) {
6335 0 : continueOuterLoop = false;
6336 0 : continue;
6337 : }
6338 74960 : ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
6339 74960 : if (!state.dataSurface->Surface(IWin).HasShadeControl) {
6340 0 : continueOuterLoop = false;
6341 0 : continue;
6342 : }
6343 74960 : if (state.dataSurface->WindowShadingControl(ICtrl).GlareControlIsActive) {
6344 74960 : atLeastOneGlareControlIsActive = true;
6345 :
6346 : // Illuminance (WDAYIL) and background luminance (WBACLU) contribution from this
6347 : // window without shading (IS=1) and with shading (IS=2) for each ref pt
6348 : // For switchable windows, this may be partially switched rather than fully dark
6349 153836 : for (int IL = 1; IL <= NREFPT; ++IL) {
6350 78876 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6351 236628 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
6352 157752 : shadeGroupLums.WDAYIL(IL)[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
6353 157752 : shadeGroupLums.WDAYIL(IL)[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
6354 : }
6355 : }
6356 :
6357 : // Recalculate illuminance and glare with shading on this window.
6358 : // For switchable glazings, this is the fully switched (dark) state
6359 153836 : for (int IL = 1; IL <= NREFPT; ++IL) {
6360 78876 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6361 78876 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6362 78876 : auto const &refPt = thisDayltgCtrl.refPts(IL);
6363 :
6364 78876 : if (state.dataSurface->SurfWinShadingFlag(IWin) != WinShadingType::SwitchableGlazing) {
6365 : // for non switchable glazings or switchable glazings not switched yet (still in clear state)
6366 : // SurfaceWindow(IWin)%ShadingFlag = WinShadingFlag::GlassConditionallyLightened
6367 78876 : rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Bare] + wdayil[iLum_Illum][iWinCover_Shaded];
6368 78876 : rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Bare] + wdayil[iLum_Back][iWinCover_Shaded];
6369 : } else {
6370 : // switchable glazings already in partially switched state when calc the RDAYIL(IL) & RBACLU(IL)
6371 0 : auto &tmpDayl = tmpDaylFromWinAtRefPt(loop, IL);
6372 0 : rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Shaded] + tmpDayl[iLum_Illum][iWinCover_Shaded];
6373 0 : rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Shaded] + tmpDayl[iLum_Back][iWinCover_Shaded];
6374 : }
6375 : } // for (IL)
6376 :
6377 74960 : if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
6378 71044 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
6379 3916 : else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff)
6380 0 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::IntShade;
6381 3916 : else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff)
6382 3916 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ExtShade;
6383 0 : else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff)
6384 0 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::IntBlind;
6385 0 : else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff)
6386 0 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ExtBlind;
6387 0 : else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::BGShadeConditionallyOff)
6388 0 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::BGShade;
6389 0 : else if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::BGBlindConditionallyOff)
6390 0 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::BGBlind;
6391 :
6392 : // For switchable glazings, it is switched to fully dark state,
6393 : // update ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for use in DayltgGlare
6394 74960 : if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6395 142088 : for (int IL = 1; IL <= NREFPT; ++IL) {
6396 71044 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6397 71044 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6398 :
6399 71044 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6400 71044 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6401 71044 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6402 : }
6403 :
6404 71044 : int const IConst = state.dataSurface->SurfActiveConstruction(IWin);
6405 : // Vis trans at normal incidence of unswitched glass
6406 71044 : shadeGroupLums.unswitchedTvis =
6407 71044 : General::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
6408 :
6409 : // Vis trans at normal incidence of fully switched glass
6410 71044 : int const IConstShaded = state.dataSurface->Surface(IWin).activeShadedConstruction;
6411 71044 : shadeGroupLums.switchedTvis =
6412 71044 : General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
6413 : } // if (switchableGlazing)
6414 : } // if (GlareControlIsActive)
6415 57952 : } // for (IWin)
6416 57952 : if (continueOuterLoop) continue;
6417 :
6418 57952 : if (atLeastOneGlareControlIsActive) {
6419 :
6420 : // Re-calc daylight and glare at shaded state. For switchable glazings, it is the fully dark state.
6421 119820 : for (int IL = 1; IL <= NREFPT; ++IL) {
6422 61868 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, shadeGroupLums.RDAYIL(IL)[iLum_Back]);
6423 : // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded state
6424 61868 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6425 : }
6426 :
6427 : // Check if the shading did not improve the glare conditions
6428 : //
6429 : // blnCycle when true resets the specific window to its non-shaded condition. A later comment says
6430 : // Shading this window has not improved the glare situation.
6431 : // Reset shading flag to no shading condition, go to next window.
6432 : //
6433 : // 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
6434 : // reset it. For each reference point, if the original glare was too high but ok at other reference points and the glare gets
6435 : // lower at the reference and stays ok at the other reference points it is good, don't reset it.
6436 : //
6437 : // The old comments when there were only two reference points were:
6438 : // One ref pt; go to next window if glare has increased.
6439 : // Two ref pts. There are three cases depending on glare values.
6440 : // (1) Initial glare too high at both ref pts. Deploy shading on
6441 : // this window if this decreases glare at both ref pts.
6442 : // (2) Initial glare too high only at first ref pt. Deploy shading
6443 : // on this window if glare at first ref pt decreases and
6444 : // glare at second ref pt stays below max.
6445 : // (3) Initial glare too high at second ref pt. Deploy shading if glare
6446 : // at second ref pt decreases and glare at first ref pt stays below max.
6447 : //
6448 : // The approach taken is just to count the number of reference points that fulfill the individual requirements and see if it
6449 : // covers all the reference points.
6450 57952 : int numRefPtOldAboveMaxNewBelowOld = 0;
6451 57952 : int numRefPtOldBelowMaxNewBelowMax = 0;
6452 119820 : for (int IL = 1; IL <= NREFPT; ++IL) {
6453 61868 : auto const &refPt = thisDayltgCtrl.refPts(IL);
6454 :
6455 61868 : if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= refPt.glareIndex)
6456 57852 : ++numRefPtOldAboveMaxNewBelowOld;
6457 4016 : else if (refPt.glareIndex <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= thisDayltgCtrl.MaxGlareallowed)
6458 3916 : ++numRefPtOldBelowMaxNewBelowMax;
6459 : }
6460 57952 : blnCycle = true;
6461 57952 : if ((numRefPtOldAboveMaxNewBelowOld + numRefPtOldBelowMaxNewBelowMax) == NREFPT) blnCycle = false;
6462 : }
6463 :
6464 : // restore the count to the value prior to the last loop through the group of exterior windows
6465 57952 : count = countBeforeListOfExtWinLoop;
6466 57952 : breakOuterLoop = false;
6467 :
6468 117668 : for (const int IWin : listOfExtWin) {
6469 73540 : ++count;
6470 : // need to map back to the original order of the "loop" to not change all the other data structures
6471 73540 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6472 73540 : if (loop == 0) continue;
6473 :
6474 : // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
6475 143164 : if (NOT_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(state.dataSurface->SurfWinShadingFlag(IWin)) ||
6476 69624 : ANY_BLIND(state.dataSurface->SurfWinShadingFlag(IWin)))
6477 3916 : continue;
6478 :
6479 69624 : ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
6480 69624 : if (!state.dataSurface->Surface(IWin).HasShadeControl) continue;
6481 69624 : if (state.dataSurface->WindowShadingControl(ICtrl).GlareControlIsActive) {
6482 :
6483 : // Shading this window has not improved the glare situation.
6484 : // Reset shading flag to no shading condition, go to next window.
6485 69624 : if (blnCycle) {
6486 : // for switchable glazings, reset properties to clear state or partial switched state?
6487 0 : if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6488 0 : state.dataSurface->SurfWinSwitchingFactor(IWin) = 0.0;
6489 0 : state.dataSurface->SurfWinVisTransSelected(IWin) = shadeGroupLums.unswitchedTvis;
6490 :
6491 : // RESET properties for fully dark state
6492 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6493 0 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6494 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6495 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6496 0 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6497 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6498 : }
6499 : }
6500 :
6501 0 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6502 0 : continue;
6503 0 : }
6504 :
6505 : // Shading this window has improved the glare situation.
6506 : // Reset background luminance, glare index, and daylight illuminance at each ref pt.
6507 : // For switchable glazings, this is fully switched, dark state
6508 139248 : for (int IL = 1; IL <= NREFPT; ++IL) {
6509 69624 : auto &refPt = thisDayltgCtrl.refPts(IL);
6510 69624 : refPt.lums[iLum_Back] = shadeGroupLums.RDAYIL(IL)[iLum_Back];
6511 69624 : refPt.glareIndex = GLRNEW(IL);
6512 69624 : dl->DaylIllum(IL) = shadeGroupLums.RDAYIL(IL)[iLum_Illum];
6513 : }
6514 :
6515 : // TH comments (5/22/2009): seems for EC windows, if the calculated glare exceeds the max setpoint,
6516 : // the EC windows will be reset to fully dark state which significantly reduces the available daylight.
6517 : // A better way is to dim the EC windows as necessary just to meet the glare index, which will still
6518 : // provide more daylight while not exceeding the max glare! The question is then how to set the
6519 : // SwitchingFactor to just meet the glare index.
6520 : // This was addressed in CR 7984 for E+ 5.0. 1/19/2010
6521 :
6522 : // If switchable glazing, set switching factor to 1: fully switched.
6523 69624 : if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6524 : // tmpSWFactor0 = SurfaceWindow( IWin ).SwitchingFactor; // save original
6525 : // switching factor
6526 : ////Unused Set but never used
6527 69624 : state.dataSurface->SurfWinSwitchingFactor(IWin) = 1.0;
6528 69624 : state.dataSurface->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
6529 :
6530 : // restore fully dark values
6531 139248 : for (int IL = 1; IL <= NREFPT; ++IL) {
6532 69624 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6533 69624 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6534 69624 : auto &wdayil = shadeGroupLums.WDAYIL(IL);
6535 69624 : wdayil[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6536 69624 : wdayil[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6537 69624 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6538 69624 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6539 69624 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6540 : }
6541 : }
6542 :
6543 : // Check if glare now acceptable at each ref pt.
6544 69624 : GlareOK = false;
6545 69624 : if (NREFPT == 1) {
6546 69624 : if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
6547 0 : } else if (NREFPT > 1) {
6548 0 : if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed &&
6549 0 : thisDayltgCtrl.refPts(2).glareIndex <= thisDayltgCtrl.MaxGlareallowed)
6550 0 : GlareOK = true;
6551 : }
6552 :
6553 69624 : if (GlareOK) {
6554 27648 : if (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing &&
6555 13824 : state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp) {
6556 : // Added TH 1/14/2010
6557 : // Only for switchable glazings with MeetDaylightIlluminanceSetpoint control
6558 : // The glazing is in fully dark state, it might lighten a bit to provide more daylight
6559 : // while meeting maximum discomfort glare index
6560 : // Iteration to find the right switching factor meeting the glare index
6561 :
6562 : // get fully dark state values
6563 0 : Real64 tmpSWSL1 = tmpDaylFromWinAtRefPt(1, loop)[iLum_Source][iWinCover_Shaded];
6564 0 : Real64 tmpSWSL2 = (NREFPT > 1) ? tmpDaylFromWinAtRefPt(2, loop)[iLum_Source][iWinCover_Shaded] : 0.0;
6565 :
6566 : // use simple fixed step search in iteraction, can be improved in future
6567 0 : Real64 tmpSWFactor = 1.0 - tmpSWIterStep;
6568 0 : while (tmpSWFactor > 0) {
6569 : // calc new glare at new switching state
6570 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6571 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6572 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6573 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6574 0 : rdayil[iLum_Illum] =
6575 0 : dl->DaylIllum(IL) +
6576 0 : (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6577 0 : rdayil[iLum_Back] =
6578 0 : refPt.lums[iLum_Back] +
6579 0 : (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6580 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
6581 : // needs to update SourceLumFromWinAtRefPt(IL,2,loop) before re-calc DayltgGlare
6582 0 : tmpMult = (shadeGroupLums.unswitchedTvis -
6583 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6584 0 : shadeGroupLums.switchedTvis;
6585 0 : refPt.extWins(loop).lums[iLum_Source][iWinCover_Shaded] = ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
6586 : // Calc new glare
6587 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6588 : } // for (IL)
6589 :
6590 : // Check whether new glare is OK
6591 0 : GlareOK = false;
6592 0 : if (NREFPT == 1) {
6593 0 : if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
6594 0 : } else if (NREFPT > 1) {
6595 0 : if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(2) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
6596 : }
6597 :
6598 0 : if (GlareOK) {
6599 0 : if (tmpSWFactor >= tmpSWIterStep) {
6600 : // Continue to lighten the glazing
6601 0 : tmpSWFactor -= tmpSWIterStep;
6602 0 : continue;
6603 : } else {
6604 : // Glare still OK but glazing already in clear state, no more lighten
6605 0 : breakOuterLoop = true;
6606 0 : break;
6607 : }
6608 : } else {
6609 : // Glare too high, exit and use previous switching state
6610 0 : tmpSWFactor += tmpSWIterStep;
6611 0 : breakOuterLoop = true;
6612 0 : break;
6613 : }
6614 : } // if (tmpSWFactor > 0)
6615 :
6616 : // Final re-calculation if needed
6617 0 : if (!GlareOK) {
6618 : // Glare too high, use previous state and re-calc
6619 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6620 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6621 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6622 0 : rdayil[iLum_Illum] =
6623 0 : dl->DaylIllum(IL) +
6624 0 : (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6625 0 : rdayil[iLum_Back] =
6626 0 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] +
6627 0 : (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6628 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
6629 :
6630 : // needs to update SourceLumFromWinAtRefPt(IL,2,IWin) before re-calc DayltgGlare
6631 0 : tmpMult = (shadeGroupLums.unswitchedTvis -
6632 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6633 0 : shadeGroupLums.switchedTvis;
6634 0 : thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Source][iWinCover_Shaded] =
6635 0 : ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
6636 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6637 : }
6638 : }
6639 :
6640 : // Update final results
6641 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6642 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6643 0 : auto const &rdayil = shadeGroupLums.RDAYIL(IL);
6644 0 : refPt.lums[iLum_Back] = rdayil[iLum_Back];
6645 0 : refPt.glareIndex = GLRNEW(IL);
6646 0 : dl->DaylIllum(IL) = rdayil[iLum_Illum];
6647 :
6648 0 : tmpMult =
6649 0 : (shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6650 0 : shadeGroupLums.switchedTvis;
6651 : // update report variables
6652 0 : auto &daylFromWinAtRefPt = refPt.extWins(loop).lums;
6653 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6654 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded] * tmpMult;
6655 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded] * tmpMult;
6656 : }
6657 0 : state.dataSurface->SurfWinSwitchingFactor(IWin) = tmpSWFactor;
6658 0 : state.dataSurface->SurfWinVisTransSelected(IWin) =
6659 0 : shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor;
6660 :
6661 : } else {
6662 : // For un-switchable glazing or switchable glazing but not MeetDaylightIlluminaceSetpoint control,
6663 : // it is in shaded state and glare is ok - job is done, exit the window loop - IWin
6664 13824 : breakOuterLoop = true;
6665 13824 : break;
6666 : }
6667 : } // if (glareOK)
6668 : } // if (glareControlIsActive)
6669 57952 : } // for (IWin)
6670 57952 : if (breakOuterLoop) break;
6671 : } // for (igroup)
6672 : } // if (GlareFlag)
6673 :
6674 : // Loop again over windows and reset remaining shading flags that
6675 : // are 10 or higher (i.e., conditionally off) to off
6676 1444942 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6677 724508 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
6678 2151855 : for (int IWin = thisSpace.WindowSurfaceFirst; IWin <= thisSpace.WindowSurfaceLast; ++IWin) {
6679 1427347 : if (state.dataSurface->Surface(IWin).ExtBoundCond != ExternalEnvironment) continue;
6680 1426332 : bool anyGlareControl = (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) ||
6681 1426332 : (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) ||
6682 1412611 : (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) ||
6683 4237393 : (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) ||
6684 1384729 : (state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff);
6685 1426332 : if (anyGlareControl) {
6686 41603 : state.dataSurface->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6687 : }
6688 : }
6689 720434 : }
6690 :
6691 : // Variables for reporting
6692 1677232 : for (int IL = 1; IL <= NREFPT; ++IL) {
6693 956798 : auto &refPt = thisDayltgCtrl.refPts(IL);
6694 956798 : refPt.lums[iLum_Illum] = dl->DaylIllum(IL);
6695 :
6696 : // added TH 12/2/2008
6697 956798 : refPt.timeExceedingGlareIndexSetPoint = (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) ? state.dataGlobal->TimeStepZone : 0.0;
6698 : // added TH 7/6/2009
6699 956798 : refPt.timeExceedingDaylightIlluminanceSetPoint = (dl->DaylIllum(IL) > refPt.illumSetPoint) ? state.dataGlobal->TimeStepZone : 0.0;
6700 : }
6701 728582 : } // DayltgInteriorIllum()
6702 :
6703 1015 : void DayltgInteriorTDDIllum(EnergyPlusData &state)
6704 : {
6705 :
6706 : // SUBROUTINE INFORMATION:
6707 : // AUTHOR Linda Lawrie
6708 : // DATE WRITTEN October 2006
6709 :
6710 : // PURPOSE OF THIS SUBROUTINE:
6711 : // Calculate the TDD Pipe illuminance values
6712 1015 : auto &dl = state.dataDayltg;
6713 :
6714 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
6715 : int iSky1; // Sky type index values for averaging two sky types
6716 : int iSky2;
6717 : Real64 SkyWeight; // Weighting factor used to average two different sky types
6718 :
6719 1015 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
6720 518 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
6721 518 : iSky1 = (int)SkyType::Clear;
6722 518 : iSky2 = (int)SkyType::ClearTurbid;
6723 497 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
6724 91 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
6725 91 : iSky1 = (int)SkyType::ClearTurbid;
6726 91 : iSky2 = (int)SkyType::Intermediate;
6727 : } else { // Sky is average of intermediate and overcast
6728 406 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
6729 406 : iSky1 = (int)SkyType::Intermediate;
6730 406 : iSky2 = (int)SkyType::Overcast;
6731 : }
6732 :
6733 : // Calculate and report TDD visible transmittances
6734 3045 : for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
6735 :
6736 2030 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam =
6737 2030 : state.dataGlobal->WeightNow * dl->TDDTransVisBeam(state.dataGlobal->HourOfDay, PipeNum) +
6738 2030 : state.dataGlobal->WeightPreviousHour * dl->TDDTransVisBeam(state.dataGlobal->PreviousHour, PipeNum);
6739 :
6740 2030 : auto const &tddFluxIncCurr = dl->TDDFluxInc(state.dataGlobal->HourOfDay, PipeNum);
6741 2030 : auto const &tddFluxIncPrev = dl->TDDFluxInc(state.dataGlobal->PreviousHour, PipeNum);
6742 :
6743 2030 : auto const &tddFluxTransCurr = dl->TDDFluxTrans(state.dataGlobal->HourOfDay, PipeNum);
6744 2030 : auto const &tddFluxTransPrev = dl->TDDFluxTrans(state.dataGlobal->PreviousHour, PipeNum);
6745 :
6746 2030 : Illums TDDTransVisDiff;
6747 6090 : for (int iSky = iSky1; iSky <= iSky2; ++iSky) {
6748 4060 : Real64 tddTransVisDiffCurr = (tddFluxIncCurr.sky[iSky] > 0.0) ? (tddFluxTransCurr.sky[iSky] / tddFluxIncCurr.sky[iSky]) : 0.0;
6749 4060 : Real64 tddTransVisDiffPrev = (tddFluxIncPrev.sky[iSky] > 0.0) ? (tddFluxTransPrev.sky[iSky] / tddFluxIncPrev.sky[iSky]) : 0.0;
6750 :
6751 4060 : TDDTransVisDiff.sky[iSky] =
6752 4060 : state.dataGlobal->WeightNow * tddTransVisDiffCurr + state.dataGlobal->WeightPreviousHour * tddTransVisDiffPrev;
6753 : } // for (iSky)
6754 :
6755 2030 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff =
6756 2030 : SkyWeight * TDDTransVisDiff.sky[iSky1] + (1.0 - SkyWeight) * TDDTransVisDiff.sky[iSky2];
6757 : } // for (PipeNum)
6758 1015 : } // DayltgInteriorTDDIllum()
6759 :
6760 916463 : void DayltgElecLightingControl(EnergyPlusData &state)
6761 : {
6762 :
6763 : // SUBROUTINE INFORMATION:
6764 : // AUTHOR Fred Winkelmann
6765 : // DATE WRITTEN July 1997
6766 : // MODIFIED Mar 2004, FCW: add inter-reflected illuminance from interior windows to DaylIllum
6767 : // Apr 2004, FCW: move CALL ReportIllumMap from DayltgInteriorIllum2 (DayltgInteriorMapIllum)
6768 : // Apr 2010, BG NREL: remove inter-reflected illuminance to stop double counting
6769 : // Aug 2012, BG NREL: added availability schedule logic
6770 :
6771 : // PURPOSE OF THIS SUBROUTINE:
6772 : // For a daylit space, determines lighting power reduction factor due to
6773 : // daylighting for different lighting control systems.
6774 :
6775 : // Called by InitSurfaceHeatBalance.
6776 :
6777 : // REFERENCES:
6778 : // Based on DOE-2.1E subroutine DLTSYS.
6779 916463 : auto &dl = state.dataDayltg;
6780 :
6781 916463 : if (dl->daylightControl.empty()) {
6782 817243 : return;
6783 : }
6784 : // Reset space power reduction factors
6785 953130 : for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
6786 853910 : dl->spacePowerReductionFactor(spaceNum) = 1.0;
6787 : }
6788 :
6789 529341 : for (auto &thisDayltgCtrl : dl->daylightControl) {
6790 :
6791 430121 : if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
6792 : // Set space power reduction factors
6793 2037 : if (thisDayltgCtrl.PowerReductionFactor < 1.0) {
6794 2016 : if (thisDayltgCtrl.spaceIndex > 0) {
6795 : // This is a space-level daylighting control
6796 0 : dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = thisDayltgCtrl.PowerReductionFactor;
6797 : } else {
6798 : // This is a zone-level daylighting control
6799 4032 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6800 2016 : dl->spacePowerReductionFactor(spaceNum) = thisDayltgCtrl.PowerReductionFactor;
6801 2016 : }
6802 : }
6803 : }
6804 2037 : continue;
6805 2037 : }
6806 :
6807 : // Electric lighting power reduction factor for a given daylighting control
6808 428084 : Real64 &TotReduction = thisDayltgCtrl.PowerReductionFactor;
6809 428084 : TotReduction = 0.0;
6810 428084 : Real64 ZFTOT = 0.0;
6811 :
6812 : // check if scheduled to be available
6813 428084 : if (ScheduleManager::GetCurrentScheduleValue(state, thisDayltgCtrl.AvailSchedNum) > 0.0) {
6814 :
6815 : // Loop over reference points
6816 992623 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
6817 568669 : auto &refPt = thisDayltgCtrl.refPts(IL);
6818 : // Total fraction of zone that is daylit
6819 568669 : ZFTOT += refPt.fracZoneDaylit;
6820 :
6821 568669 : dl->DaylIllum(IL) = refPt.lums[iLum_Illum];
6822 568669 : Real64 FL = 0.0;
6823 568669 : if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
6824 143729 : FL = (refPt.illumSetPoint - dl->DaylIllum(IL)) / refPt.illumSetPoint;
6825 : }
6826 :
6827 : // BRANCH ON LIGHTING SYSTEM TYPE
6828 568669 : LtgCtrlType LSYSTP = thisDayltgCtrl.LightControlType;
6829 568669 : Real64 FP = 0.0;
6830 568669 : if (LSYSTP != LtgCtrlType::Stepped) {
6831 : // Continuously dimmable system with linear power curve
6832 : // Fractional output power required to meet setpoint
6833 322161 : FP = 1.0;
6834 : // LIGHT-CTRL-TYPE = CONTINUOUS (LSYSTP = 1)
6835 322161 : if (FL <= thisDayltgCtrl.MinLightFraction) {
6836 236773 : FP = thisDayltgCtrl.MinPowerFraction;
6837 : }
6838 : // LIGHT-CTRL-TYPE = CONTINUOUS/OFF (LSYSTP = 3)
6839 322161 : if (FL <= thisDayltgCtrl.MinLightFraction && LSYSTP == LtgCtrlType::ContinuousOff) {
6840 219328 : FP = 0.0;
6841 : }
6842 322161 : if (FL > thisDayltgCtrl.MinLightFraction && FL < 1.0) {
6843 84941 : FP = (FL + (1.0 - FL) * thisDayltgCtrl.MinPowerFraction - thisDayltgCtrl.MinLightFraction) /
6844 84941 : (1.0 - thisDayltgCtrl.MinLightFraction);
6845 : }
6846 :
6847 : } else { // LSYSTP = 2
6848 : // Stepped system
6849 246508 : FP = 0.0;
6850 : // #9060: Use a tolerance, otherwise at very low (< 1e-12) daylighting conditions, you can get a multiplier > 1.0
6851 246508 : if (dl->DaylIllum(IL) < 0.1) {
6852 3602 : FP = 1.0;
6853 242906 : } else if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
6854 33315 : FP = double(int(thisDayltgCtrl.LightControlSteps * FL) + 1) / double(thisDayltgCtrl.LightControlSteps);
6855 : }
6856 :
6857 246508 : if (thisDayltgCtrl.LightControlProbability < 1.0) {
6858 : // Manual operation. Occupant sets lights one level too high a fraction of the time equal to
6859 : // 1. - ZoneDaylight(ZoneNum)%LightControlProbability. RANDOM_NUMBER returns a random number
6860 : // between 0 and 1.
6861 : Real64 XRAN;
6862 0 : RANDOM_NUMBER(XRAN);
6863 0 : if (XRAN >= thisDayltgCtrl.LightControlProbability) {
6864 : // Set level one higher
6865 0 : if (FP < 1.0) {
6866 0 : FP += (1.0 / double(thisDayltgCtrl.LightControlSteps));
6867 : }
6868 : } // XRAN
6869 : } // Light Control Probability < 1
6870 : } // Lighting System Type
6871 :
6872 568669 : refPt.powerReductionFactor = FP;
6873 :
6874 : // Accumulate net ltg power reduction factor for entire zone
6875 568669 : TotReduction += refPt.powerReductionFactor * refPt.fracZoneDaylit;
6876 :
6877 : } // End of loop over reference points, IL
6878 :
6879 : // Correct for fraction of zone (1-ZFTOT) not controlled by
6880 : // the reference points. For this fraction (which is usually zero),
6881 : // the electric lighting is unaffected and the power reduction
6882 : // factor is therefore 1.0.
6883 423954 : TotReduction += (1.0 - ZFTOT);
6884 : } else { // controls not currently available
6885 4130 : TotReduction = 1.0;
6886 : }
6887 :
6888 : // Set space power reduction factors
6889 428084 : if (thisDayltgCtrl.spaceIndex > 0) {
6890 : // This is a space-level daylighting control
6891 1239 : dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = TotReduction;
6892 : } else {
6893 : // This is a zone-level daylighting control
6894 853690 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6895 426845 : dl->spacePowerReductionFactor(spaceNum) = TotReduction;
6896 426845 : }
6897 : }
6898 : } // end daylighting control loop
6899 :
6900 : // IF(TotIllumMaps > 0 .and. .not. DoingSizing .and. .not. WarmupFlag .and. .not. KickoffSimulation) THEN
6901 99220 : if ((int)dl->illumMaps.size() > 0 && !state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
6902 3019 : for (int mapNum = 1; mapNum <= (int)dl->illumMaps.size(); ++mapNum) {
6903 1627 : auto &illumMap = dl->illumMaps(mapNum);
6904 1627 : if (state.dataGlobal->TimeStep == 1) dl->mapResultsToReport = false;
6905 143752 : for (auto &refPt : illumMap.refPts) {
6906 142125 : refPt.lumsHr[iLum_Illum] += refPt.lums[iLum_Illum] / double(state.dataGlobal->NumOfTimeStepInHour);
6907 142125 : if (refPt.lumsHr[iLum_Illum] > 0.0) {
6908 142125 : dl->mapResultsToReport = true;
6909 142125 : dl->mapResultsReported = true;
6910 : }
6911 : }
6912 1627 : ReportIllumMap(state, mapNum);
6913 1627 : if (state.dataGlobal->TimeStep == state.dataGlobal->NumOfTimeStepInHour) {
6914 24357 : for (auto &refPt : illumMap.refPts) {
6915 24075 : refPt.lumsHr[iLum_Illum] = refPt.lums[iLum_Illum] = 0.0;
6916 : }
6917 : }
6918 : } // for (mapNum)
6919 : } // if (MapSize > 0)
6920 : } // DayltgElecLightingControl()
6921 :
6922 534586 : Real64 DayltgGlarePositionFactor(Real64 X, // Lateral and vertical distance of luminous window element from
6923 : Real64 Y)
6924 : {
6925 :
6926 : // SUBROUTINE INFORMATION:
6927 : // AUTHOR Fred Winkelmann
6928 : // DATE WRITTEN July 1997
6929 :
6930 : // PURPOSE OF THIS SUBROUTINE:
6931 : // by table interpolation, evaluates the
6932 : // Hopkinson position factor used in glare calculation
6933 : // (Hopkinson, Petherbridge, AND Longmore -- Daylighting,
6934 : // London, 1966, PP 307, 323). X (Y) is the lateral
6935 : // (vertical) distance of luminous window element from
6936 : // horizontal line of vision, divided by horizontal distance
6937 : // from eye of observer. The array PF contains values of
6938 : // the position factor for X = 0, 0.5, 1.0, 1.5, 2.0, 2.5,
6939 : // and 3.0 and Y = 0, 0.5, 1.0, 1.5, 2.0. Called by CalcDayltgCoefficients.
6940 :
6941 : // REFERENCES:
6942 : // Based on DOE-2.1E subroutine DPFAC.
6943 :
6944 : // Position factor array
6945 : static constexpr std::array<std::array<Real64, 7>, 5> PF = {{
6946 : {1.00, 0.492, 0.226, 0.128, 0.081, 0.061, 0.057},
6947 : {0.123, 0.119, 0.065, 0.043, 0.029, 0.026, 0.023},
6948 : {0.019, 0.026, 0.019, 0.016, 0.014, 0.011, 0.011},
6949 : {0.008, 0.008, 0.008, 0.008, 0.008, 0.006, 0.006},
6950 : {0.0, 0.0, 0.003, 0.003, 0.003, 0.003, 0.003},
6951 : }};
6952 :
6953 534586 : if (X < 0.0 || X >= 3.0) return 0.0;
6954 183827 : if (Y < 0.0 || Y >= 2.0) return 0.0;
6955 :
6956 183274 : int IX = 1 + int(2.0 * X);
6957 183274 : int IY = 1 + int(2.0 * Y);
6958 183274 : Real64 X1 = 0.5 * double(IX - 1);
6959 183274 : Real64 Y1 = 0.5 * double(IY - 1);
6960 183274 : Real64 FA = PF[IY - 1][IX - 1] + 2.0 * (X - X1) * (PF[IY - 1][IX] - PF[IY - 1][IX - 1]);
6961 183274 : Real64 FB = PF[IY][IX - 1] + 2.0 * (X - X1) * (PF[IY][IX] - PF[IY][IX - 1]);
6962 183274 : return FA + 2.0 * (Y - Y1) * (FB - FA);
6963 : } // DayltgGlarePositionFactor()
6964 :
6965 324804 : void DayltgInterReflectedIllum(EnergyPlusData &state,
6966 : int const ISunPos, // Sun position counter; used to avoid calculating various
6967 : int const IHR, // Hour of day
6968 : int const enclNum, // Daylighting enclosure index
6969 : int const IWin // Window index
6970 : )
6971 : {
6972 :
6973 : // SUBROUTINE INFORMATION:
6974 : // AUTHOR Fred Winkelmann
6975 : // DATE WRITTEN July 1997
6976 : // MODIFIED FCW December 1998
6977 : // FCW June 2001: Add blind calculations
6978 : // FCW Jan 2001: Add blinds with movable slats
6979 : // FCW Jan 2003: Add between-glass blinds
6980 : // FCW Jul 2003: account for transmittance of shading surfaces
6981 : // (previously these were assumed opaque even if transmittance schedule
6982 : // value was non-zero)
6983 : // FCW Aug 2003: modify initialization of WinLum from WinLum = 0. TO
6984 : // WinLum(:,:,IHR) = 0. Otherwise values calculated in previous
6985 : // call are incorrectly zeroed. Result was that window luminance with
6986 : // shade or blind included only contribution from first window element
6987 : // in window element loop in CalcDayltgCoefficients, thus seriously
6988 : // undercalculating window luminance for windows with more than one
6989 : // window element. Similarly, modified initialization of WLUMSU from
6990 : // WLUMSU = 0. to WLUMSU(:,IHR) = 0., and of WLUMSUdisk from
6991 : // WLUMSUdisk = 0. to WLUMSUdisk(:,IHR) = 0.
6992 : // PGE Aug 2003: Add daylighting shelves.
6993 : // FCW Nov 2003: Add beam solar and sky solar reflected from obstructions;
6994 : // add beam solar reflected from ground accounting for obstructions.
6995 : // FCW Nov 2003: increase NPHMAX from 9 to 10 to avoid rays with altitude angle = 0
6996 : // for vertical surfaces.
6997 : // FCW Nov 2003: fix the expression for min and max limits of azimuth; old expression
6998 : // broke down for window normals with negative altitude angle
6999 : // FCW Nov 2003: add specular reflection from exterior obstructions
7000 : // FCW Apr 2004: add light well efficiency multiplying window transmittance
7001 : // FCW Apr 2004: add diffusing glazing
7002 : // RAR (FSEC) May 2006: add exterior window screen
7003 : // B. Griffith NREL April 2010: CR7869 add adjacent zone area if window is not on this zone
7004 : // apply interior window transmission and blocking to beam transmission from ext win
7005 :
7006 : // PURPOSE OF THIS SUBROUTINE:
7007 : // Called from CalcDayltgCoefficients for each window and reference point in a daylit
7008 : // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
7009 : // to internally reflected light by integrating to determine the amount of flux from
7010 : // sky and ground (and beam reflected from obstructions) transmitted through
7011 : // the center of the window and then reflecting this
7012 : // light from the inside surfaces of the space. The "split-flux" method is used
7013 : // (Lynes, Principles of Natural Lighting, 1968). EINT is determined for
7014 : // different sky types and for window with and without shades, screens or blinds.
7015 : // Also finds luminance (WinLum and WLUMSU) of window with shade or blind, &
7016 : // or with diffusing glass, for different sky types.
7017 :
7018 : // REFERENCES:
7019 : // Based on DOE-2.1E subroutine DREFLT.
7020 :
7021 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
7022 : // In the following I,J arrays:
7023 : // I = sky type;
7024 : // J = 1 for bare window, 2 and above for window with shade or blind.
7025 324804 : Illums ZSK; // Sky-related and sun-related illuminance on window from sky/ground
7026 324804 : Vector3<Real64> U; // Unit vector in (PH,TH) direction
7027 324804 : Vector3<Real64> nearestHitPt; // Hit point of ray on nearest obstruction (m)
7028 324804 : Vector3<Real64> obsHitPt; // Coordinates of hit point on an obstruction (m)
7029 324804 : Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
7030 : // std::array<Real64, Material::MaxSlatAngs+1> FLFWSU = {0.0}; // Sun-related downgoing luminous flux, excluding entering beam
7031 : // std::array<Real64, Material::MaxSlatAngs+1> FLFWSUdisk = {0.0}; // Sun-related downgoing luminous flux, due to entering beam
7032 : // std::array<Real64, Material::MaxSlatAngs+1> FLCWSU = {0.0}; // Sun-related upgoing luminous flux
7033 324804 : std::array<Dayltg::Illums, Material::MaxSlatAngs + 1> FLCW = {Illums()}; // Sky-related upgoing luminous flux
7034 324804 : std::array<Dayltg::Illums, Material::MaxSlatAngs + 1> FLFW = {Illums()}; // Sky-related downgoing luminous flux
7035 : std::array<Real64, (int)Material::MaxSlatAngs + 1> transMult;
7036 : std::array<Real64, (int)Material::MaxSlatAngs + 1> transBmBmMult;
7037 :
7038 : // 3=intermediate, 4=overcast
7039 : Real64 DPH; // Sky/ground element altitude and azimuth increments (radians)
7040 : Real64 DTH;
7041 : Real64 PH; // Sky/ground element altitude and azimuth (radians)
7042 : Real64 TH;
7043 : Real64 SPH; // Sine and cosine of PH
7044 : Real64 CPH;
7045 : Real64 PHMIN; // Limits of altitude integration (radians)
7046 : Real64 PHMAX;
7047 : Real64 ThMin; // Limits of azimuth integration (radians)
7048 : Real64 ThMax;
7049 : Real64 PhWin; // Altitude, azimuth angle of window normal (radians)
7050 : Real64 ThWin;
7051 : Real64 ACosTanTan; // ACOS(-TAN(Ph)*TAN(PhWin))
7052 : Real64 DA; // CPH*DTH*DPH
7053 : Real64 COSB; // Cosine of angle of incidence of light from sky or ground
7054 : Real64 TVISBR; // Transmittance of window without shading at COSB
7055 : // (times light well efficiency, if appropriate)
7056 : Real64 ZSU;
7057 : // element for clear and overcast sky
7058 : Real64 ObTrans; // Product of solar transmittances of obstructions seen by a light ray
7059 :
7060 : // unused REAL(r64) :: HitPointLumFrClearSky ! Luminance of obstruction from clear sky (cd/m2)
7061 : // unused REAL(r64) :: HitPointLumFrOvercSky ! Luminance of obstruction from overcast sky (cd/m2)
7062 : // unused REAL(r64) :: HitPointLumFrSun ! Luminance of obstruction from sun (cd/m2)
7063 : int ICtrl; // Window control pointer
7064 : Real64 COSBSun; // Cosine of angle of incidence of direct sun on window
7065 : Real64 TVISBSun; // Window's visible transmittance at COSBSun
7066 : // (times light well efficiency, if appropriate)
7067 : Real64 ZSU1; // Transmitted direct normal illuminance (lux)
7068 : // CHARACTER(len=32) :: ShType ! Window shading device type
7069 : bool ShadeOn; // True if exterior or interior window shade present
7070 : bool BlindOn; // True if exterior or interior window blind present
7071 : bool ScreenOn; // True if exterior window screen present
7072 : int BlNum; // Blind number
7073 : // int ScNum; // Screen number //Unused Set but never used
7074 : int PipeNum; // TDD pipe object number
7075 : int ShelfNum; // Daylighting shelf object number
7076 : int InShelfSurf; // Inside daylighting shelf surface number
7077 : int OutShelfSurf; // Outside daylighting shelf surface number
7078 : Real64 TransBlBmDiffFront; // Isolated blind vis beam-diffuse front transmittance
7079 : Real64 TransScBmDiffFront; // Isolated screen vis beam-diffuse front transmittance
7080 : Real64 ReflGlDiffDiffBack; // Bare glazing system vis diffuse back reflectance
7081 : Real64 ReflGlDiffDiffFront; // Bare glazing system vis diffuse front reflectance
7082 : Real64 ReflBlBmDiffFront; // Isolated blind vis beam-diffuse front reflectance
7083 : Real64 TransBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front transmittance
7084 : Real64 ReflBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front reflectance
7085 : Real64 ReflBlDiffDiffBack; // Isolated blind vis diffuse-diffuse back reflectance
7086 : Real64 ReflScDiffDiffBack; // Isolated screen vis diffuse-diffuse back reflectance
7087 :
7088 : Real64 td2; // Diffuse-diffuse vis trans of bare glass layers 2 and 3
7089 : Real64 td3;
7090 : Real64 rbd1; // Beam-diffuse back vis reflectance of bare glass layers 1 and 2
7091 : Real64 rbd2;
7092 : Real64 rfd2; // Beam-diffuse front vis reflectance of bare glass layers 2 and 3
7093 : Real64 rfd3;
7094 : Real64 tfshd; // Diffuse-diffuse front vis trans of bare blind
7095 : Real64 rbshd; // Diffuse-diffuse back vis reflectance of bare blind
7096 : Real64 ZSUObsRefl; // Illuminance on window from beam solar reflected by an
7097 : // obstruction (for unit beam normal illuminance)
7098 : int NearestHitSurfNum; // Surface number of nearest obstruction
7099 : int NearestHitSurfNumX; // Surface number to use when obstruction is a shadowing surface
7100 : Real64 LumAtHitPtFrSun; // Luminance at hit point on obstruction from solar reflection
7101 : // for unit beam normal illuminance (cd/m2)
7102 : Real64 SunObstructionMult; // = 1 if sun hits a ground point; otherwise = 0
7103 : bool hitObs; // True iff obstruction is hit
7104 : Real64 ObsVisRefl; // Visible reflectance of obstruction
7105 : Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by unobstructed sky
7106 : // diffuse horizontal illuminance [(cd/m2)/lux]
7107 : Real64 dReflObsSky; // Contribution to sky-related illuminance on window due to sky diffuse
7108 : // reflection from an obstruction
7109 : Real64 TVisSunRefl; // Diffuse vis trans of bare window for beam reflection calc
7110 : // (times light well efficiency, if appropriate)
7111 : Real64 ZSU1refl; // Beam normal illuminance times ZSU1refl = illuminance on window
7112 : // due to specular reflection from exterior surfaces
7113 :
7114 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
7115 : Real64 EnclInsideSurfArea; // temporary for calculations, total surface area of enclosure surfaces m2
7116 : int IntWinAdjZoneExtWinNum; // the index of the exterior window in IntWinAdjZoneExtWin nested struct
7117 : int IntWinNum; // window index for interior windows associated with exterior windows
7118 : Real64 COSBintWin;
7119 :
7120 : WinShadingType ShType;
7121 :
7122 324804 : auto &dl = state.dataDayltg;
7123 324804 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
7124 324804 : auto const &surf = state.dataSurface->Surface(IWin);
7125 324804 : auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
7126 324804 : int const enclNumThisWin = state.dataSurface->Surface(surf.BaseSurf).SolarEnclIndex;
7127 : // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea was calculated in subr DayltgAveInteriorReflectance
7128 :
7129 324804 : if (enclNumThisWin == enclNum) {
7130 317484 : extWinType = ExtWinType::InZone;
7131 317484 : EnclInsideSurfArea = dl->enclDaylight(enclNumThisWin).totInsSurfArea;
7132 317484 : IntWinAdjZoneExtWinNum = 0;
7133 : } else {
7134 7320 : extWinType = ExtWinType::AdjZone;
7135 : // If window is exterior window in adjacent zone, then use areas of both enclosures
7136 7320 : EnclInsideSurfArea = dl->enclDaylight(enclNum).totInsSurfArea + dl->enclDaylight(enclNumThisWin).totInsSurfArea;
7137 : // find index in IntWinAdjZoneExtWin
7138 7320 : for (int AdjExtWinLoop = 1; AdjExtWinLoop <= thisEnclDaylight.NumOfIntWinAdjEnclExtWins; ++AdjExtWinLoop) {
7139 2424 : if (IWin == thisEnclDaylight.IntWinAdjEnclExtWin(AdjExtWinLoop).SurfNum) { // found it
7140 2424 : IntWinAdjZoneExtWinNum = AdjExtWinLoop;
7141 2424 : break; // added TH 4/13/2010
7142 : }
7143 : }
7144 : }
7145 :
7146 : // Initialize window luminance and fluxes for split-flux calculation
7147 1299216 : dl->winLum(IHR, _) = Illums();
7148 : // dl->WLUMSU(IHR, _) = 0.0;
7149 : // dl->WLUMSUdisk(IHR, _) = 0.0;
7150 :
7151 324804 : int const IConst = state.dataSurface->SurfActiveConstruction(IWin);
7152 324804 : auto const &construct = state.dataConstruction->Construct(IConst);
7153 :
7154 324804 : BlindOn = false;
7155 324804 : ShadeOn = false;
7156 324804 : ScreenOn = false;
7157 :
7158 324804 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7159 4896 : PipeNum = state.dataSurface->SurfWinTDDPipeNum(IWin);
7160 : }
7161 :
7162 324804 : ShelfNum = state.dataSurface->SurfDaylightingShelfInd(IWin);
7163 324804 : if (ShelfNum > 0) {
7164 2448 : InShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf; // Inside daylighting shelf present if > 0
7165 2448 : OutShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf; // Outside daylighting shelf present if > 0
7166 : } else {
7167 322356 : InShelfSurf = 0;
7168 322356 : OutShelfSurf = 0;
7169 : }
7170 :
7171 : // Divide sky and ground into elements of altitude PH and
7172 : // azimuth TH, and add the contribution of light coming from each
7173 : // element to the transmitted flux at the center of the window
7174 : // Azimuth ranges over a maximum of 2 Pi radians.
7175 : // Altitude ranges over a maximum of Pi/2 radians between -Pi/2 < PH < +Pi/2, so that elements are not counted twice
7176 : // PH = 0 at the horizon; PH = Pi/2 at the zenith
7177 324804 : PHMIN = max(-Constant::PiOvr2, surfWin.phi - Constant::PiOvr2);
7178 324804 : PHMAX = min(Constant::PiOvr2, surfWin.phi + Constant::PiOvr2);
7179 324804 : DPH = (PHMAX - PHMIN) / double(NPHMAX);
7180 :
7181 : // Sky/ground element altitude integration
7182 324804 : Vector3<Real64> const SUNCOS_IHR(state.dataSurface->SurfSunCosHourly(IHR));
7183 3572844 : for (int IPH = 1; IPH <= NPHMAX; ++IPH) {
7184 3248040 : PH = PHMIN + (double(IPH) - 0.5) * DPH;
7185 :
7186 3248040 : SPH = std::sin(PH);
7187 3248040 : CPH = std::cos(PH);
7188 : // Third component of unit vector in (TH,PH) direction
7189 3248040 : U.z = SPH;
7190 :
7191 : // Limits of azimuth integration
7192 3248040 : PhWin = surfWin.phi;
7193 3248040 : ThWin = surfWin.theta;
7194 3248040 : if (PhWin >= 0.0) {
7195 3248040 : if (PH >= Constant::PiOvr2 - PhWin) {
7196 633828 : ThMin = -Constant::Pi;
7197 633828 : ThMax = Constant::Pi;
7198 : } else {
7199 2614212 : ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
7200 2614212 : ThMin = ThWin - std::abs(ACosTanTan);
7201 2614212 : ThMax = ThWin + std::abs(ACosTanTan);
7202 : }
7203 :
7204 : } else { // PhiSurf < 0.0
7205 0 : if (PH <= -PhWin - Constant::PiOvr2) {
7206 0 : ThMin = -Constant::Pi;
7207 0 : ThMax = Constant::Pi;
7208 : } else {
7209 0 : ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
7210 0 : ThMin = ThWin - std::abs(ACosTanTan);
7211 0 : ThMax = ThWin + std::abs(ACosTanTan);
7212 : }
7213 : }
7214 :
7215 3248040 : DTH = (ThMax - ThMin) / double(NTHMAX);
7216 3248040 : DA = CPH * DTH * DPH;
7217 :
7218 : // Sky/ground element azimuth integration
7219 3248040 : Real64 const sin_window_phi(std::sin(surfWin.phi));
7220 3248040 : Real64 const cos_window_phi(std::cos(surfWin.phi));
7221 55216680 : for (int ITH = 1; ITH <= NTHMAX; ++ITH) {
7222 51968640 : TH = ThMin + (double(ITH) - 0.5) * DTH;
7223 51968640 : U.x = CPH * std::cos(TH);
7224 51968640 : U.y = CPH * std::sin(TH);
7225 : // Cosine of angle of incidence of light from sky or ground element
7226 51968640 : COSB = SPH * sin_window_phi + CPH * cos_window_phi * std::cos(TH - surfWin.theta);
7227 51968640 : if (COSB < 0.0) continue; // Sky/ground elements behind window (although there shouldn't be any)
7228 :
7229 : // Initialize illuminance on window for this sky/ground element
7230 207874560 : ZSK = Illums();
7231 51968640 : ZSU = 0.0;
7232 : // Initialize illuminance on window from beam solar reflection if ray hits an obstruction
7233 51968640 : ZSUObsRefl = 0.0;
7234 :
7235 51968640 : if (ISunPos == 1) { // Intersection calculation has to be done only for first sun position
7236 : // Determine net transmittance of obstructions that the ray hits. ObTrans will be 1.0
7237 : // if no obstructions are hit.
7238 4103360 : ObTrans = DayltgHitObstruction(state, IHR, IWin, state.dataSurface->SurfaceWindow(IWin).WinCenter, U);
7239 4103360 : dl->ObTransM[IPH][ITH] = ObTrans;
7240 4103360 : dl->SkyObstructionMult[IPH][ITH] = 1.0;
7241 : }
7242 :
7243 : // SKY AND GROUND RADIATION ON WINDOW
7244 :
7245 : // Contribution is from sky if PH > 0 (ray goes upward), and from ground if PH < 0 (ray goes downward)
7246 : // (There may also be contributions from reflection from obstructions; see 'BEAM SOLAR AND SKY SOLAR
7247 : // REFLECTED FROM NEAREST OBSTRUCTION,' below.)
7248 :
7249 51968640 : if (PH > 0.0) { // Contribution is from sky
7250 155274720 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7251 124219776 : ZSK.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH, PH) * COSB * DA * dl->ObTransM[IPH][ITH];
7252 : }
7253 : } else { // PH <= 0.0; contribution is from ground
7254 20913696 : if (state.dataSurface->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
7255 : // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
7256 : // by the ray. This effect is given by the ratio SkyObstructionMult =
7257 : // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
7258 : // This ratio is calculated for an isotropic sky.
7259 : // Ground point hit by the ray:
7260 6080 : Real64 Alfa = std::acos(-U.z);
7261 6080 : Real64 Beta = std::atan2(U.y, U.x);
7262 6080 : Real64 HorDis = (state.dataSurface->SurfaceWindow(IWin).WinCenter.z - state.dataSurface->GroundLevelZ) * std::tan(Alfa);
7263 6080 : groundHitPt.z = state.dataSurface->GroundLevelZ;
7264 6080 : groundHitPt.x = state.dataSurface->SurfaceWindow(IWin).WinCenter.x + HorDis * std::cos(Beta);
7265 6080 : groundHitPt.y = state.dataSurface->SurfaceWindow(IWin).WinCenter.y + HorDis * std::sin(Beta);
7266 :
7267 6080 : dl->SkyObstructionMult[IPH][ITH] =
7268 6080 : CalcObstrMultiplier(state, groundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
7269 : } // End of check if solar reflection calc is in effect
7270 :
7271 20913696 : auto const &gilsk = dl->horIllum[IHR];
7272 104568480 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7273 : // Below, luminance of ground in cd/m2 is illuminance on ground in lumens/m2
7274 : // times ground reflectance, divided by pi, times obstruction multiplier.
7275 167309568 : ZSK.sky[iSky] = (gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
7276 83654784 : dl->SkyObstructionMult[IPH][ITH];
7277 : }
7278 : // Determine if sun illuminates the point that ray hits the ground. If the solar reflection
7279 : // calculation has been requested (CalcSolRefl = .TRUE.) shading by obstructions, including
7280 : // the building itself, is considered in determining whether sun hits the ground point.
7281 : // Otherwise this shading is ignored and the sun always hits the ground point.
7282 20913696 : SunObstructionMult = 1.0;
7283 20913696 : if (state.dataSurface->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
7284 : // Sun reaches ground point if vector from this point to the sun is unobstructed
7285 6080 : hitObs = false;
7286 27444 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
7287 23806 : hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
7288 23806 : if (hitObs) break;
7289 6080 : }
7290 6080 : if (hitObs) SunObstructionMult = 0.0;
7291 : }
7292 20913696 : ZSU = (dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
7293 : SunObstructionMult;
7294 : }
7295 : // BEAM SOLAR AND SKY SOLAR REFLECTED FROM NEAREST OBSTRUCTION
7296 :
7297 51968640 : if (state.dataSurface->CalcSolRefl && dl->ObTransM[IPH][ITH] < 1.0) {
7298 : // Find obstruction whose hit point is closest to the center of the window
7299 36320 : DayltgClosestObstruction(state, state.dataSurface->SurfaceWindow(IWin).WinCenter, U, NearestHitSurfNum, nearestHitPt);
7300 36320 : if (NearestHitSurfNum > 0) {
7301 :
7302 : // Beam solar reflected from nearest obstruction.
7303 36320 : LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, IHR, U, NearestHitSurfNum, nearestHitPt);
7304 36320 : ZSUObsRefl = LumAtHitPtFrSun * COSB * DA;
7305 36320 : ZSU += ZSUObsRefl;
7306 :
7307 : // Sky solar reflected from nearest obstruction.
7308 36320 : int const ObsConstrNum = state.dataSurface->Surface(NearestHitSurfNum).Construction;
7309 36320 : if (ObsConstrNum > 0) {
7310 : // Exterior building surface is nearest hit
7311 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
7312 : // Obstruction is not a window, i.e., is an opaque surface
7313 0 : ObsVisRefl = 1.0 - dynamic_cast<const Material::MaterialChild *>(
7314 0 : state.dataMaterial->Material(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1)))
7315 0 : ->AbsorpVisible;
7316 : } else {
7317 : // Obstruction is a window; assume it is bare
7318 0 : ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
7319 : }
7320 : } else {
7321 : // Shadowing surface is nearest hit
7322 36320 : if (state.dataSurface->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
7323 : // Skip daylighting shelves, whose reflection is separately calculated
7324 0 : ObsVisRefl = 0.0;
7325 : } else {
7326 36320 : ObsVisRefl = state.dataSurface->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
7327 36320 : if (state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
7328 0 : ObsVisRefl += state.dataSurface->SurfShadowGlazingFrac(NearestHitSurfNum) *
7329 0 : state.dataConstruction->Construct(state.dataSurface->SurfShadowGlazingConstruct(NearestHitSurfNum))
7330 0 : .ReflectVisDiffFront;
7331 : // Note in the above that ShadowSurfDiffuseVisRefl is the reflectance of opaque part of
7332 : // shadowing surface times (1 - ShadowSurfGlazingFrac)
7333 : }
7334 : }
7335 36320 : NearestHitSurfNumX = NearestHitSurfNum;
7336 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
7337 : // The following gets the correct side of a shadowing surface for reflection.
7338 36320 : if (state.dataSurface->Surface(NearestHitSurfNum).IsShadowing) {
7339 36320 : if (dot(U, state.dataSurface->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
7340 : }
7341 36320 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
7342 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
7343 36320 : SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
7344 36320 : state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
7345 : } else {
7346 0 : SkyReflVisLum = ObsVisRefl * state.dataSurface->Surface(NearestHitSurfNumX).ViewFactorSky *
7347 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, IHR, NearestHitSurfNumX) / Constant::Pi;
7348 : }
7349 36320 : dReflObsSky = SkyReflVisLum * COSB * DA;
7350 :
7351 36320 : auto const &gilsk = dl->horIllum[IHR];
7352 181600 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7353 145280 : ZSK.sky[iSky] += gilsk.sky[iSky] * dReflObsSky;
7354 : }
7355 : }
7356 : } // End of check if exterior solar reflection calculation is active
7357 :
7358 : // ===Bare window (no shade or blind; non-diffusing glass)===
7359 :
7360 : // Increment flux entering space and window luminance (cd/m2).
7361 : // FLCW--(I,J) = part of incoming flux (in lumens) that goes up to ceiling and upper part of walls.
7362 : // FLFW--(I,J) = part that goes down to floor and lower part of walls
7363 :
7364 51968640 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7365 : // Unshaded visible transmittance of TDD for a single ray from sky/ground element
7366 783360 : TVISBR = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
7367 :
7368 : // Make all transmitted light diffuse for a TDD with a bare diffuser
7369 783360 : auto &wlumsk = dl->winLum(IHR, 1);
7370 783360 : auto &flfwsk = FLFW[1];
7371 783360 : auto &flcwsk = FLCW[1];
7372 :
7373 783360 : auto &tddFluxInc = dl->TDDFluxInc(IHR, PipeNum);
7374 783360 : auto &tddFluxTrans = dl->TDDFluxTrans(IHR, PipeNum);
7375 3916800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7376 3133440 : wlumsk.sky[iSky] += ZSK.sky[iSky] * TVISBR / Constant::Pi;
7377 3133440 : flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * (1.0 - surfWin.fractionUpgoing);
7378 3133440 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7379 :
7380 : // For later calculation of diffuse visible transmittance
7381 3133440 : tddFluxInc.sky[iSky] += ZSK.sky[iSky];
7382 3133440 : tddFluxTrans.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7383 :
7384 : } // for (iSky)
7385 :
7386 783360 : tddFluxInc.sky[(int)SkyType::Clear] += ZSU;
7387 783360 : tddFluxTrans.sky[(int)SkyType::Clear] += ZSU * TVISBR;
7388 :
7389 783360 : dl->winLum(IHR, 1).sun += ZSU * TVISBR / Constant::Pi;
7390 783360 : FLFW[1].sun += ZSU * TVISBR * (1.0 - surfWin.fractionUpgoing);
7391 783360 : FLCW[1].sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7392 :
7393 : } else { // Bare window
7394 : // Transmittance of bare window for this sky/ground element
7395 51185280 : TVISBR = General::POLYF(COSB, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
7396 :
7397 51185280 : if (InShelfSurf > 0) { // Inside daylighting shelf
7398 : // Daylighting shelf simplification: All light is diffuse
7399 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7400 391680 : auto &flcwsk = FLCW[1];
7401 1958400 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7402 1566720 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7403 : }
7404 391680 : FLCW[1].sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7405 :
7406 : } else { // Normal window
7407 :
7408 : // CR 7869 correct TVISBR if disk beam passes thru interior window
7409 50793600 : if (extWinType == ExtWinType::AdjZone) {
7410 : // modify TVISBR by second window transmission
7411 : // first determine if ray from point passes thru any interior window
7412 387840 : hitObs = false;
7413 387840 : for (int IntWinLoop = 1; IntWinLoop <= thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).NumOfIntWindows;
7414 : ++IntWinLoop) {
7415 0 : IntWinNum = thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).IntWinNum(IntWinLoop);
7416 0 : auto const &surfIntWin = state.dataSurface->SurfaceWindow(IntWinNum);
7417 0 : hitObs = PierceSurface(state, IntWinNum, surfIntWin.WinCenter, SUNCOS_IHR, obsHitPt);
7418 0 : if (hitObs) { // disk passes thru
7419 : // cosine of incidence angle of light from sky or ground element for
7420 0 : COSBintWin = SPH * std::sin(surfIntWin.phi) + CPH * std::cos(surfIntWin.phi) * std::cos(TH - surfIntWin.theta);
7421 0 : TVISBR *= General::POLYF(
7422 : COSBintWin,
7423 0 : state.dataConstruction->Construct(state.dataSurface->Surface(IntWinNum).Construction).TransVisBeamCoef);
7424 0 : break;
7425 : }
7426 : }
7427 387840 : if (!hitObs) { // blocked by opaque parts, beam does not actually pass thru interior window to reach zone
7428 387840 : TVISBR = 0.0;
7429 : }
7430 : }
7431 :
7432 50793600 : auto &flfwsk = FLFW[1];
7433 50793600 : auto &flcwsk = FLCW[1];
7434 253968000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7435 : // IF (PH < 0.0d0) THEN
7436 : // Fixed by FCW, Nov. 2003:
7437 203174400 : if (PH > 0.0) {
7438 120616320 : flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7439 : } else {
7440 82558080 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7441 : }
7442 : } // for (iSky)
7443 :
7444 50793600 : if (PH > 0.0) {
7445 30154080 : FLFW[1].sun += ZSU * TVISBR;
7446 : } else {
7447 20639520 : FLCW[1].sun += ZSU * TVISBR;
7448 : }
7449 :
7450 : } // End of check if window with daylighting shelf or normal window
7451 : } // End of check if TDD:DOME or bare window
7452 :
7453 : // Check if window has shade or blind
7454 51968640 : ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
7455 51968640 : if (state.dataSurface->Surface(IWin).HasShadeControl) {
7456 23911360 : ShType = state.dataSurface->WindowShadingControl(ICtrl).ShadingType;
7457 23911360 : BlNum = state.dataSurface->SurfWinBlindNumber(IWin);
7458 : // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
7459 :
7460 23911360 : ShadeOn = ANY_SHADE(ShType);
7461 23911360 : BlindOn = ANY_BLIND(ShType);
7462 23911360 : ScreenOn = (ShType == WinShadingType::ExtScreen);
7463 : }
7464 :
7465 51968640 : if (ShadeOn || BlindOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
7466 :
7467 : // ===Window with interior or exterior shade or blind, exterior screen, or with diffusing glass===
7468 :
7469 : // Increment flux entering space and window luminance. Shades and diffusing glass are
7470 : // assumed to be perfect diffusers, i.e., the transmittance is independent of angle of
7471 : // incidence and the transmitted light is isotropic. The transmittance of a blind is
7472 : // assumed to depend on profile angle and slat angle; the diffuse light entering the room from
7473 : // the slats of the blind is assumed to be isotropic. With blinds, light can also enter
7474 : // the room by passing between the slats without reflection. The beam transmittance of a screen
7475 : // is assumed to depend on sun azimuth and azimuth angle.
7476 :
7477 : // For light from a shade, or from diffusing glass, or from the slats of a blind, a flux fraction,
7478 : // SurfaceWindow(IWin)%FractionUpgoing (determined by window tilt), goes up toward
7479 : // ceiling and upper part of walls, and 1-Surfacewindow(iwin)%FractionUpgoing
7480 : // goes down toward floor and lower part of walls. For a blind, the light passing
7481 : // between the slats goes either up or down depending on the altitude angle of the
7482 : // element from which the light came. For a screen, the light passing
7483 : // between the screen's cylinders goes either up or down depending on the altitude angle of the
7484 : // element from which the light came.
7485 :
7486 152960 : int IConstShaded = state.dataSurface->SurfWinActiveShadedConstruction(IWin);
7487 152960 : if (state.dataSurface->SurfWinSolarDiffusing(IWin)) IConstShaded = state.dataSurface->Surface(IWin).Construction;
7488 :
7489 : // Transmittance of window including shade, screen or blind
7490 152960 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
7491 152960 : std::fill(transMult.begin(), transMult.end(), 0.0);
7492 :
7493 152960 : if (ShadeOn) { // Shade
7494 152960 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7495 : // Shaded visible transmittance of TDD for a single ray from sky/ground element
7496 0 : transMult[1] = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
7497 : } else { // Shade only, no TDD
7498 : // Calculate transmittance of the combined window and shading device for this sky/ground element
7499 305920 : transMult[1] = General::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7500 152960 : surfWin.lightWellEff;
7501 : }
7502 :
7503 0 : } else if (ScreenOn) { // Screen: get beam-beam, beam-diffuse and diffuse-diffuse vis trans/ref of screen and glazing system
7504 0 : auto const *screen = dynamic_cast<Material::MaterialScreen *>(state.dataMaterial->Material(surfWin.screenNum));
7505 0 : assert(screen != nullptr);
7506 :
7507 0 : Real64 phi = std::abs(PH - surfWin.phi);
7508 0 : Real64 theta = std::abs(TH - surfWin.theta);
7509 : int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
7510 : General::BilinearInterpCoeffs coeffs;
7511 :
7512 0 : Material::NormalizePhiTheta(phi, theta);
7513 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
7514 0 : General::GetBilinearInterpCoeffs(
7515 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
7516 :
7517 0 : ReflGlDiffDiffFront = state.dataConstruction->Construct(IConst).ReflectVisDiffFront;
7518 0 : ReflScDiffDiffBack = screen->DfRefVis;
7519 :
7520 0 : auto const &b11 = screen->btars[ip1][it1];
7521 0 : auto const &b12 = screen->btars[ip1][it2];
7522 0 : auto const &b21 = screen->btars[ip2][it1];
7523 0 : auto const &b22 = screen->btars[ip2][it2];
7524 :
7525 0 : TransScBmDiffFront = General::BilinearInterp(b11.DfTransVis, b12.DfTransVis, b21.DfTransVis, b22.DfTransVis, coeffs);
7526 :
7527 0 : transMult[1] = TransScBmDiffFront * surfWin.glazedFrac * state.dataConstruction->Construct(IConst).TransDiffVis /
7528 0 : (1 - ReflGlDiffDiffFront * ReflScDiffDiffBack) * surfWin.lightWellEff;
7529 :
7530 0 : transBmBmMult[1] = General::BilinearInterp(b11.BmTransVis, b12.BmTransVis, b21.BmTransVis, b22.BmTransVis, coeffs);
7531 :
7532 0 : } else if (BlindOn) { // Blind: get beam-diffuse and beam-beam vis trans of blind+glazing system
7533 : // PETER: As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
7534 : // for TDDs because it is based on TVISBR which is correctly calculated for TDDs above.
7535 0 : auto const &blind = state.dataMaterial->Blind(BlNum);
7536 0 : Real64 ProfAng = ProfileAngle(state, IWin, U, blind.SlatOrientation);
7537 :
7538 0 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
7539 0 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
7540 :
7541 0 : TransBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
7542 :
7543 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7544 0 : ReflGlDiffDiffBack = construct.ReflectVisDiffBack;
7545 0 : ReflBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
7546 0 : ReflBlDiffDiffFront = blind.VisFrontDiffDiffRefl(JB);
7547 0 : TransBlDiffDiffFront = blind.VisFrontDiffDiffTrans(JB);
7548 0 : transMult[JB] = TVISBR * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7549 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7550 :
7551 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7552 0 : ReflGlDiffDiffFront = construct.ReflectVisDiffFront;
7553 0 : ReflBlDiffDiffBack = blind.VisBackDiffDiffRefl(JB);
7554 0 : transMult[JB] = TransBlBmDiffFront * surfWin.glazedFrac * construct.TransDiffVis /
7555 0 : (1.0 - ReflGlDiffDiffFront * ReflBlDiffDiffBack) * surfWin.lightWellEff;
7556 :
7557 : } else { // Between-glass blind
7558 0 : Real64 t1 = General::POLYF(COSB, construct.tBareVisCoef(1));
7559 0 : td2 = construct.tBareVisDiff(2);
7560 0 : rbd1 = construct.rbBareVisDiff(1);
7561 0 : rfd2 = construct.rfBareVisDiff(2);
7562 0 : Real64 tfshBd = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
7563 0 : tfshd = blind.VisFrontDiffDiffTrans(JB);
7564 0 : Real64 rfshB = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
7565 0 : rbshd = blind.VisFrontDiffDiffRefl(JB);
7566 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7567 0 : transMult[JB] = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7568 : } else { // 3 glass layers; blind between layers 2 and 3
7569 0 : Real64 t2 = General::POLYF(COSB, construct.tBareVisCoef(2));
7570 0 : td3 = construct.tBareVisDiff(3);
7571 0 : rfd3 = construct.rfBareVisDiff(3);
7572 0 : rbd2 = construct.rbBareVisDiff(2);
7573 0 : transMult[JB] = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7574 0 : surfWin.lightWellEff;
7575 : }
7576 : }
7577 :
7578 0 : Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? ((JB - 1) * Constant::Pi / (Material::MaxSlatAngs - 1))
7579 0 : : (blind.SlatAngle * Constant::DegToRadians);
7580 :
7581 0 : transBmBmMult[JB] =
7582 0 : TVISBR * Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
7583 : } // End of loop over slat angles
7584 :
7585 : } else { // Diffusing glass
7586 0 : transMult[1] = General::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7587 0 : surfWin.lightWellEff;
7588 : } // End of check if shade, blind or diffusing glass
7589 :
7590 152960 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7591 : // No beam is transmitted. This takes care of all types of screens and blinds.
7592 0 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
7593 : }
7594 :
7595 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7596 152960 : if (InShelfSurf > 0) { // Inside daylighting shelf
7597 0 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
7598 : }
7599 :
7600 : // DayltgInterReflectedIllumTransBmBmMult is used in the following for windows with blinds or screens to get contribution from light
7601 : // passing directly between slats or between screen material without reflection.
7602 :
7603 305920 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
7604 : // EXIT after first pass if not movable slats or exterior window screen
7605 305920 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
7606 :
7607 152960 : auto &wlumsk = dl->winLum(IHR, JB + 1);
7608 152960 : auto &flfwsk = FLFW[JB + 1];
7609 152960 : auto &flcwsk = FLCW[JB + 1];
7610 :
7611 764800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7612 :
7613 611840 : wlumsk.sky[iSky] += ZSK.sky[iSky] * transMult[JB] / Constant::Pi;
7614 611840 : flfwsk.sky[iSky] += ZSK.sky[iSky] * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
7615 611840 : flcwsk.sky[iSky] += ZSK.sky[iSky] * transMult[JB] * surfWin.fractionUpgoing;
7616 :
7617 611840 : if (BlindOn || ScreenOn) {
7618 0 : if (PH > 0.0) {
7619 0 : flfwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult[JB];
7620 : } else {
7621 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult[JB];
7622 : }
7623 : }
7624 : }
7625 :
7626 152960 : dl->winLum(IHR, JB + 1).sun += ZSU * transMult[JB] / Constant::Pi;
7627 152960 : FLFW[JB + 1].sun += ZSU * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
7628 152960 : FLCW[JB + 1].sun += ZSU * transMult[JB] * surfWin.fractionUpgoing;
7629 152960 : if (BlindOn || ScreenOn) {
7630 0 : if (PH > 0.0) {
7631 0 : FLFW[JB + 1].sun += ZSU * transBmBmMult[JB];
7632 : } else {
7633 0 : FLCW[JB + 1].sun += ZSU * transBmBmMult[JB];
7634 : }
7635 : }
7636 : }
7637 : } // End of window with shade, screen, blind or diffusing glass
7638 :
7639 : } // End of azimuth integration loop, ITH
7640 : } // End of altitude integration loop, IPH
7641 :
7642 324804 : if (OutShelfSurf > 0) { // Outside daylighting shelf
7643 : // Add exterior diffuse illuminance due to outside shelf
7644 : // Since all of the illuminance is added to the zone as upgoing diffuse, it can be added as a lump sum here
7645 :
7646 2448 : TVISBR = construct.TransDiffVis; // Assume diffuse transmittance for shelf illuminance
7647 :
7648 2448 : auto const &gilsk = dl->horIllum[IHR];
7649 2448 : auto &flcwsk = FLCW[1];
7650 12240 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7651 : // This is only an estimate because the anisotropic sky view of the shelf is not yet taken into account.
7652 : // SurfAnisoSkyMult would be great to use but it is not available until the heat balance starts up.
7653 9792 : ZSK.sky[iSky] = gilsk.sky[iSky] * 1.0 * state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis *
7654 9792 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
7655 :
7656 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7657 9792 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7658 : } // ISKY
7659 :
7660 2448 : ZSU = dl->horIllum[IHR].sun * state.dataHeatBal->SurfSunlitFracHR(IHR, OutShelfSurf) *
7661 2448 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis * state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
7662 2448 : FLCW[1].sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7663 : }
7664 :
7665 : // Sky-related portion of internally reflected illuminance.
7666 : // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea, and ZoneDaylight(ZoneNum)%aveVisDiffReflect,
7667 : // were calculated in subr DayltgAveInteriorReflectance.
7668 :
7669 974412 : for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
7670 974412 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
7671 :
7672 649608 : auto &eintsk = dl->reflIllum(IHR, JSH);
7673 649608 : auto const &flfwsk = FLFW[JSH];
7674 649608 : auto const &flcwsk = FLCW[JSH];
7675 :
7676 3248040 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7677 : // Full area of window is used in following since effect of dividers on reducing
7678 : // effective window transmittance has already been accounted for in calc of FLFWSK and FLCWSK.
7679 5196864 : eintsk.sky[iSky] = (flfwsk.sky[iSky] * surfWin.rhoFloorWall + flcwsk.sky[iSky] * surfWin.rhoCeilingWall) *
7680 2598432 : (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - dl->enclDaylight(enclNum).aveVisDiffReflect));
7681 : } // for (iSky)
7682 : } // for (jSH)
7683 :
7684 : // BEAM SOLAR RADIATION ON WINDOW
7685 :
7686 : // Beam reaching window directly (without specular reflection from exterior obstructions)
7687 :
7688 324804 : if (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0) {
7689 : // Cos of angle of incidence
7690 243329 : COSBSun = dl->sunAngles.sinPhi * std::sin(surfWin.phi) +
7691 243329 : dl->sunAngles.cosPhi * std::cos(surfWin.phi) * std::cos(dl->sunAngles.theta - surfWin.theta);
7692 :
7693 243329 : if (COSBSun > 0.0) {
7694 : // Multiply direct normal illuminance (normalized to 1.0 lux)
7695 : // by incident angle factor and by fraction of window that is sunlit.
7696 : // Note that in the following SurfSunlitFracHR accounts for possibly non-zero transmittance of
7697 : // shading surfaces.
7698 :
7699 243329 : ZSU1 = COSBSun * state.dataHeatBal->SurfSunlitFracHR(IHR, IWin);
7700 :
7701 : // Contribution to window luminance and downgoing flux
7702 :
7703 : // -- Bare window
7704 :
7705 243329 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7706 : // Unshaded visible transmittance of TDD for collimated beam from the sun
7707 4488 : TVISBSun = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
7708 4488 : dl->TDDTransVisBeam(IHR, PipeNum) = TVISBSun;
7709 :
7710 4488 : FLFW[1].sunDisk = 0.0; // Diffuse light only
7711 :
7712 4488 : dl->winLum(IHR, 1).sun += ZSU1 * TVISBSun / Constant::Pi;
7713 4488 : FLFW[1].sun += ZSU1 * TVISBSun * (1.0 - surfWin.fractionUpgoing);
7714 4488 : FLCW[1].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
7715 :
7716 : } else { // Bare window
7717 238841 : TVISBSun = General::POLYF(COSBSun, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
7718 :
7719 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7720 238841 : if (InShelfSurf > 0) { // Inside daylighting shelf
7721 1836 : FLFW[1].sunDisk = 0.0; // Diffuse light only
7722 :
7723 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7724 : // WLUMSU(1,IHR) = WLUMSU(1,IHR) + ZSU1 * TVISBSun / PI
7725 : // FLFWSU(1) = FLFWSU(1) + ZSU1 * TVISBSun * (1.0 - SurfaceWindow(IWin)%FractionUpgoing)
7726 1836 : FLCW[1].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
7727 : } else { // Normal window
7728 237005 : FLFW[1].sunDisk = ZSU1 * TVISBSun;
7729 : }
7730 : }
7731 :
7732 : // -- Window with shade, screen, blind or diffusing glass
7733 243329 : if (ShadeOn || BlindOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
7734 472 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
7735 472 : std::fill(transMult.begin(), transMult.end(), 0.0);
7736 :
7737 : // TH 7/7/2010 moved from inside the loop: DO JB = 1,MaxSlatAngs
7738 : Real64 ProfAng;
7739 472 : if (BlindOn) {
7740 0 : auto const &blind = state.dataMaterial->Blind(BlNum);
7741 0 : ProfAng = ProfileAngle(state, IWin, state.dataSurface->SurfSunCosHourly(IHR), blind.SlatOrientation);
7742 : }
7743 :
7744 944 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
7745 944 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
7746 :
7747 472 : if (ShadeOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) { // Shade or screen on or diffusing glass
7748 472 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7749 : // Shaded visible transmittance of TDD for collimated beam from the sun
7750 0 : transMult[1] = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
7751 : } else {
7752 472 : if (ScreenOn) {
7753 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(state.dataMaterial->Material(surfWin.screenNum));
7754 0 : assert(screen != nullptr);
7755 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
7756 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
7757 : int ip1, ip2, it1, it2;
7758 : General::BilinearInterpCoeffs coeffs;
7759 0 : Material::NormalizePhiTheta(phi, theta);
7760 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
7761 0 : General::GetBilinearInterpCoeffs(
7762 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
7763 0 : Real64 BmBmTransVis = General::BilinearInterp(screen->btars[ip1][it1].BmTransVis,
7764 0 : screen->btars[ip1][it2].BmTransVis,
7765 0 : screen->btars[ip2][it1].BmTransVis,
7766 0 : screen->btars[ip2][it2].BmTransVis,
7767 : coeffs);
7768 :
7769 0 : transMult[1] = BmBmTransVis * surfWin.glazedFrac * surfWin.lightWellEff;
7770 : } else {
7771 472 : int IConstShaded = state.dataSurface->SurfWinActiveShadedConstruction(IWin);
7772 472 : if (state.dataSurface->SurfWinSolarDiffusing(IWin)) IConstShaded = surf.Construction;
7773 944 : transMult[1] = General::POLYF(COSBSun, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) *
7774 472 : surfWin.glazedFrac * surfWin.lightWellEff;
7775 : }
7776 : }
7777 :
7778 : } else { // Blind on
7779 :
7780 : // As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
7781 : // for TDDs because it is based on TVISBSun which is correctly calculated for TDDs above.
7782 0 : auto const &blind = state.dataMaterial->Blind(BlNum);
7783 :
7784 0 : Real64 TransBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
7785 :
7786 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7787 : // TH CR 8121, 7/7/2010
7788 : // ReflBlBmDiffFront = WindowManager::InterpProfAng(ProfAng,Blind(BlNum)%VisFrontBeamDiffRefl)
7789 0 : Real64 ReflBlBmDiffFront = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
7790 :
7791 : // TH added 7/12/2010 for CR 8121
7792 0 : Real64 ReflBlDiffDiffFront = blind.VisFrontDiffDiffRefl(JB);
7793 0 : Real64 TransBlDiffDiffFront = blind.VisFrontDiffDiffTrans(JB);
7794 :
7795 0 : transMult[JB] = TVISBSun * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7796 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7797 :
7798 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7799 0 : transMult[JB] = TransBlBmDiffFront *
7800 0 : (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * blind.VisBackDiffDiffRefl(JB))) *
7801 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7802 :
7803 : } else { // Between-glass blind
7804 0 : Real64 t1 = General::POLYF(COSBSun, construct.tBareVisCoef(1));
7805 0 : Real64 tfshBd = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffTrans(JB, {1, 37}));
7806 0 : Real64 rfshB = Window::InterpProfAng(ProfAng, blind.VisFrontBeamDiffRefl(JB, {1, 37}));
7807 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7808 0 : transMult[JB] = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7809 : } else { // 3 glass layers; blind between layers 2 and 3
7810 0 : Real64 t2 = General::POLYF(COSBSun, construct.tBareVisCoef(2));
7811 0 : transMult[JB] = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7812 0 : surfWin.lightWellEff;
7813 : }
7814 : }
7815 :
7816 0 : Real64 SlatAng = (state.dataSurface->SurfWinMovableSlats(IWin)) ? ((JB - 1) * Constant::Pi / (Material::MaxSlatAngs - 1))
7817 0 : : (blind.SlatAngle * Constant::DegToRadians);
7818 :
7819 0 : transBmBmMult[JB] =
7820 0 : TVISBSun * Window::BlindBeamBeamTrans(ProfAng, SlatAng, blind.SlatWidth, blind.SlatSeparation, blind.SlatThickness);
7821 : } // ShadeOn/ScreenOn/BlindOn/Diffusing glass
7822 :
7823 472 : if (state.dataSurface->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7824 0 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0); // No beam, diffuse only
7825 : }
7826 :
7827 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7828 472 : if (InShelfSurf > 0) { // Inside daylighting shelf
7829 0 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0); // No beam, diffuse only
7830 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7831 : }
7832 :
7833 472 : dl->winLum(IHR, JB + 1).sun += ZSU1 * transMult[JB] / Constant::Pi;
7834 472 : dl->winLum(IHR, JB + 1).sunDisk = ZSU1 * transBmBmMult[JB] / Constant::Pi;
7835 472 : FLFW[JB + 1].sun += ZSU1 * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
7836 472 : FLFW[JB + 1].sunDisk = ZSU1 * transBmBmMult[JB];
7837 472 : FLCW[JB + 1].sun += ZSU1 * transMult[JB] * surfWin.fractionUpgoing;
7838 : } // for (JB)
7839 : } // if (BlindOn || ShadeOn)
7840 : } // if (COSBSun > 0)
7841 : } // if (SurfSunlitFracHR > 0)
7842 :
7843 : // Beam reaching window after specular reflection from exterior obstruction
7844 :
7845 : // In the following, Beam normal illuminance times ZSU1refl = illuminance on window due to
7846 : // specular reflection from exterior surfaces
7847 :
7848 324804 : if (state.dataSurface->CalcSolRefl && state.dataSurface->Surface(IWin).OriginalClass != SurfaceClass::TDD_Dome) {
7849 :
7850 908 : ZSU1refl = state.dataSurface->SurfReflFacBmToBmSolObs(IHR, IWin);
7851 :
7852 908 : if (ZSU1refl > 0.0) {
7853 : // Contribution to window luminance and downgoing flux
7854 :
7855 : // -- Bare window. We use diffuse-diffuse transmittance here rather than beam-beam to avoid
7856 : // complications due to specular reflection from multiple exterior surfaces
7857 :
7858 0 : TVisSunRefl = construct.TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
7859 : // In the following it is assumed that all reflected beam is going downward, as it would be in the
7860 : // important case of reflection from a highly glazed facade of a neighboring building. However, in
7861 : // rare cases (such as upward specular reflection from a flat horizontal skylight) it may
7862 : // actually be going upward.
7863 0 : FLFW[1].sunDisk += ZSU1refl * TVisSunRefl;
7864 :
7865 : // -- Window with shade, blind or diffusing glass
7866 :
7867 0 : if (ShadeOn || BlindOn || ScreenOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) {
7868 0 : std::fill(transBmBmMult.begin(), transBmBmMult.end(), 0.0);
7869 0 : std::fill(transMult.begin(), transMult.end(), 0.0);
7870 :
7871 0 : for (int JB = 1; JB <= Material::MaxSlatAngs; ++JB) {
7872 0 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JB > 1) break;
7873 :
7874 0 : if (ShadeOn || state.dataSurface->SurfWinSolarDiffusing(IWin)) { // Shade on or diffusing glass
7875 0 : int IConstShaded = state.dataSurface->SurfWinActiveShadedConstruction(IWin);
7876 0 : if (state.dataSurface->SurfWinSolarDiffusing(IWin)) IConstShaded = state.dataSurface->Surface(IWin).Construction;
7877 0 : transMult[1] = state.dataConstruction->Construct(IConstShaded).TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
7878 :
7879 0 : } else if (ScreenOn) { // Exterior screen on
7880 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(state.dataMaterial->Material(surfWin.screenNum));
7881 0 : Real64 TransScDiffDiffFront = screen->DfTransVis;
7882 :
7883 0 : transMult[1] = TransScDiffDiffFront *
7884 0 : (state.dataConstruction->Construct(IConst).TransDiffVis / (1.0 - ReflGlDiffDiffFront * ReflScDiffDiffBack)) *
7885 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7886 :
7887 : } else { // Blind on
7888 :
7889 0 : auto const &blind = state.dataMaterial->Blind(BlNum);
7890 0 : TransBlDiffDiffFront = state.dataMaterial->Blind(BlNum).VisFrontDiffDiffTrans(JB);
7891 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7892 0 : ReflBlDiffDiffFront = blind.VisFrontDiffDiffRefl(JB);
7893 0 : transMult[JB] = TVisSunRefl * (TransBlDiffDiffFront + ReflBlDiffDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7894 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7895 :
7896 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7897 0 : transMult[JB] = TransBlDiffDiffFront *
7898 0 : (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * blind.VisBackDiffDiffRefl(JB))) *
7899 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7900 :
7901 : } else { // Between-glass blind
7902 0 : Real64 t1 = construct.tBareVisDiff(1);
7903 0 : Real64 tfshBd = blind.VisFrontDiffDiffTrans(JB);
7904 0 : Real64 rfshB = blind.VisFrontDiffDiffRefl(JB);
7905 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7906 0 : transMult[JB] = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7907 : } else { // 3 glass layers; blind between layers 2 and 3
7908 0 : Real64 t2 = construct.tBareVisDiff(2);
7909 0 : transMult[JB] = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7910 0 : surfWin.lightWellEff;
7911 : }
7912 : } // End of check of interior/exterior/between-glass blind
7913 : } // if (Blind)
7914 :
7915 0 : dl->winLum(IHR, JB + 1).sun += ZSU1refl * transMult[JB] / Constant::Pi;
7916 0 : FLFW[JB + 1].sun += ZSU1refl * transMult[JB] * (1.0 - surfWin.fractionUpgoing);
7917 0 : FLCW[JB + 1].sun += ZSU1refl * transMult[JB] * surfWin.fractionUpgoing;
7918 : } // End of loop over slat angles
7919 : } // End of check if window has shade, blind or diffusing glass
7920 : } // End of check if ZSU1refl > 0.0
7921 : } // End of check if solar reflections are in effect
7922 :
7923 : // Sun-related portion of internally reflected illuminance
7924 :
7925 974412 : for (int JSH = 1; JSH <= Material::MaxSlatAngs + 1; ++JSH) {
7926 974412 : if (!state.dataSurface->SurfWinMovableSlats(IWin) && JSH > 2) break;
7927 :
7928 : // Full area of window is used in following since effect of dividers on reducing
7929 : // effective window transmittance already accounted for in calc of FLFWSU and FLCWSU
7930 : // CR 7869 added effect of intervening interior windows on transmittance and
7931 : // added inside surface area of adjacent zone
7932 1299216 : dl->reflIllum(IHR, JSH).sun = (FLFW[JSH].sun * surfWin.rhoFloorWall + FLCW[JSH].sun * surfWin.rhoCeilingWall) *
7933 649608 : (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
7934 :
7935 1299216 : dl->reflIllum(IHR, JSH).sunDisk = FLFW[JSH].sunDisk * surfWin.rhoFloorWall * (surf.Area / surfWin.glazedFrac) /
7936 649608 : (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
7937 : }
7938 324804 : } // DayltgInterReflectedIllum()
7939 :
7940 1674 : void ComplexFenestrationLuminances(EnergyPlusData &state,
7941 : int const IWin,
7942 : int const WinEl,
7943 : int const NBasis,
7944 : int const IHR,
7945 : int const iRefPoint,
7946 : Array1D<Illums> &ElementLuminance, // luminance at window element (exterior side)
7947 : CalledFor const CalledFrom,
7948 : int const MapNum)
7949 : {
7950 :
7951 : // SUBROUTINE INFORMATION:
7952 : // AUTHOR Simon Vidanovic
7953 : // DATE WRITTEN June 2013
7954 :
7955 1674 : Vector3<Real64> obsHitPt; // Coordinates of hit point on an obstruction (m)
7956 1674 : Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
7957 :
7958 1674 : auto &dl = state.dataDayltg;
7959 :
7960 1674 : int CurCplxFenState = state.dataSurface->SurfaceWindow(IWin).ComplexFen.CurrentState;
7961 1674 : auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
7962 : // Calculate luminance from sky and sun excluding exterior obstruction transmittances and obstruction multipliers
7963 1674 : int SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
7964 244404 : for (int iIncElem = 1; iIncElem <= NBasis; ++iIncElem) {
7965 242730 : Real64 LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
7966 : // COSB = ComplexWind(IWin)%Geom(CurCplxFenState)%CosInc(iIncElem)
7967 : // DA = ComplexWind(IWin)%Geom(CurCplxFenState)%DAInc(iIncElem)
7968 242730 : Real64 Altitude = complexWinGeom.pInc(iIncElem).Altitude;
7969 242730 : Real64 Azimuth = complexWinGeom.pInc(iIncElem).Azimuth;
7970 242730 : auto &elemLum = ElementLuminance(iIncElem);
7971 242730 : auto const &gilsk = dl->horIllum[IHR];
7972 :
7973 242730 : if (Altitude > 0.0) {
7974 : // Ray from sky element
7975 535680 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7976 428544 : elemLum.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc;
7977 : }
7978 135594 : } else if (Altitude < 0.0) {
7979 : // Ray from ground element
7980 : // BeamObstrMultiplier = ComplexWind(IWin)%DaylghtGeom(CurCplxFenState)%GndObstrMultiplier(WinEl, iIncElem)
7981 535680 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7982 428544 : elemLum.sky[iSky] = gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7983 : }
7984 107136 : elemLum.sun = dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7985 : } else {
7986 : // Ray from the element which is half sky and half ground
7987 142290 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7988 : // in this case half of the pach is coming from the sky and half from the ground
7989 227664 : elemLum.sky[iSky] = 0.5 * DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc +
7990 113832 : 0.5 * gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7991 : }
7992 28458 : elemLum.sun = 0.5 * dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7993 : }
7994 : // Sun beam calculations
7995 242730 : if ((SolBmIndex == iIncElem) && (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0)) {
7996 1008 : elemLum.sunDisk = 1.0;
7997 : }
7998 : }
7999 :
8000 1674 : auto const &complexWinDaylightGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CurCplxFenState);
8001 :
8002 1674 : if (CalledFrom == CalledFor::RefPoint) {
8003 1674 : auto const &complexWinRefPoint = complexWinDaylightGeom.RefPoint(iRefPoint);
8004 : // add exterior obstructions transmittances to calculated luminances
8005 1674 : for (int iReflElem = 1; iReflElem <= complexWinRefPoint.NReflSurf(WinEl); ++iReflElem) {
8006 0 : Real64 ObstrTrans = complexWinRefPoint.TransOutSurf(iReflElem, WinEl);
8007 0 : int iReflElemIndex = complexWinRefPoint.RefSurfIndex(iReflElem, WinEl);
8008 :
8009 0 : auto &elemLum = ElementLuminance(iReflElemIndex);
8010 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8011 0 : elemLum.sky[iSky] *= ObstrTrans;
8012 : }
8013 0 : elemLum.sun *= ObstrTrans;
8014 0 : elemLum.sunDisk *= ObstrTrans;
8015 : }
8016 :
8017 : // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
8018 : // sun reaches the ground for that point
8019 1674 : Vector3<Real64> const SUNCOS_IHR = state.dataSurface->SurfSunCosHourly(IHR);
8020 108810 : for (int iGndElem = 1; iGndElem <= complexWinRefPoint.NGnd(WinEl); ++iGndElem) {
8021 : // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
8022 : // were hit in the process
8023 :
8024 107136 : Real64 BeamObstrMultiplier = complexWinRefPoint.GndObstrMultiplier(iGndElem, WinEl);
8025 107136 : int iGndElemIndex = complexWinRefPoint.GndIndex(iGndElem, WinEl);
8026 :
8027 107136 : auto &elemLum = ElementLuminance(iGndElemIndex);
8028 535680 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8029 428544 : elemLum.sky[iSky] *= BeamObstrMultiplier;
8030 : }
8031 :
8032 : // direct sun disk reflect off the ground
8033 107136 : Real64 SunObstrMultiplier = 1.0;
8034 107136 : if (state.dataSurface->CalcSolRefl) {
8035 : // Sun reaches ground point if vector from this point to the sun is unobstructed
8036 204042 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
8037 107136 : groundHitPt = complexWinRefPoint.GndPt(iGndElem, WinEl);
8038 107136 : bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
8039 107136 : if (hitObs) {
8040 10230 : SunObstrMultiplier = 0.0;
8041 10230 : break;
8042 : }
8043 107136 : }
8044 : }
8045 107136 : elemLum.sun *= SunObstrMultiplier;
8046 : }
8047 :
8048 1674 : } else { // if (CalledFrom != RefPoint)
8049 :
8050 0 : auto const &complexWinIllumMap = complexWinDaylightGeom.IlluminanceMap(iRefPoint, MapNum);
8051 : // add exterior obstructions transmittances to calculated luminances
8052 0 : for (int iReflElem = 1; iReflElem <= complexWinIllumMap.NReflSurf(WinEl); ++iReflElem) {
8053 0 : Real64 ObstrTrans = complexWinIllumMap.TransOutSurf(iReflElem, WinEl);
8054 0 : int iReflElemIndex = complexWinIllumMap.RefSurfIndex(iReflElem, WinEl);
8055 0 : auto &elemLum = ElementLuminance(iReflElemIndex);
8056 :
8057 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8058 0 : elemLum.sky[iSky] *= ObstrTrans;
8059 : }
8060 0 : elemLum.sun *= ObstrTrans;
8061 0 : elemLum.sunDisk *= ObstrTrans;
8062 : }
8063 :
8064 : // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
8065 : // sun reaches the ground for that point
8066 0 : Vector3<Real64> const SUNCOS_IHR = state.dataSurface->SurfSunCosHourly(IHR);
8067 0 : for (int iGndElem = 1; iGndElem <= complexWinIllumMap.NGnd(WinEl); ++iGndElem) {
8068 : // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
8069 : // were hit in the process
8070 0 : Real64 BeamObstrMultiplier = complexWinIllumMap.GndObstrMultiplier(iGndElem, WinEl);
8071 0 : int iGndElemIndex = complexWinIllumMap.GndIndex(iGndElem, WinEl);
8072 :
8073 0 : auto &elemLum = ElementLuminance(iGndElemIndex);
8074 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8075 0 : elemLum.sky[iSky] *= BeamObstrMultiplier;
8076 : }
8077 :
8078 : // direct sun disk reflect off the ground
8079 0 : Real64 SunObstrMultiplier = 1.0;
8080 0 : if (state.dataSurface->CalcSolRefl) {
8081 : // Sun reaches ground point if vector from this point to the sun is unobstructed
8082 0 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
8083 0 : groundHitPt = complexWinIllumMap.GndPt(iGndElem, WinEl);
8084 :
8085 0 : bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
8086 0 : if (hitObs) {
8087 0 : SunObstrMultiplier = 0.0;
8088 0 : break;
8089 : }
8090 0 : }
8091 : }
8092 0 : elemLum.sun *= SunObstrMultiplier;
8093 : }
8094 0 : } // if (CalledFrom == RefPoint)
8095 1674 : } // ComplexFenestrationLuminances()
8096 :
8097 186 : void DayltgInterReflectedIllumComplexFenestration(EnergyPlusData &state,
8098 : int const IWin, // Window index
8099 : int const WinEl, // Current window element counter
8100 : int const IHR, // Hour of day
8101 : int const daylightCtrlNum, // Daylighting control number
8102 : int const iRefPoint, // reference point counter
8103 : CalledFor const CalledFrom,
8104 : int const MapNum)
8105 : {
8106 :
8107 : // SUBROUTINE INFORMATION:
8108 : // AUTHOR Simon Vidanovic
8109 : // DATE WRITTEN April 2013
8110 :
8111 : // PURPOSE OF THIS SUBROUTINE:
8112 : // Called from CalcDayltgCoefficients for each complex (bsdf) fenestration and reference point in a daylit
8113 : // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
8114 : // to internally reflected light by integrating to determine the amount of flux from
8115 : // sky and ground (and beam reflected from obstructions) transmitted through
8116 : // the center of the window and then reflecting this
8117 : // light from the inside surfaces of the space.
8118 :
8119 186 : auto &dl = state.dataDayltg;
8120 :
8121 186 : Array1D<Illums> FL; // Sky related luminous flux
8122 : // Array1D<Real64> FLSU; // Sun related luminous flux, excluding entering beam
8123 : // Array1D<Real64> FLSUdisk; // Sun related luminous flux, due to entering beam
8124 :
8125 186 : Array1D<Illums> FirstFlux; // Sky related first reflected flux
8126 : // Array1D<Real64> FirstFluxSU; // Sun related first reflected flux, excluding entering beam
8127 : // Array1D<Real64> FirstFluxSUdisk; // Sun related first reflected flux, due to entering beam
8128 :
8129 186 : Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
8130 : // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side), exluding beam
8131 : // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side), due to sun beam
8132 186 : Illums FLTot;
8133 : // Real64 FLSUTot;
8134 : // Real64 FLSUdiskTot;
8135 :
8136 : // Total for first relflected fluxes
8137 186 : Illums FFTot = Illums();
8138 : // Real64 FFSUTot;
8139 : // Real64 FFSUdiskTot;
8140 :
8141 : int NIncBasis;
8142 : int SolBmIndex; // index of current sun position
8143 :
8144 : Real64 LambdaInc; // current lambda value for incoming direction
8145 : // REAL(r64) :: LambdaTrn ! current lambda value for incoming direction
8146 : Real64 dirTrans; // directional bsdf transmittance
8147 :
8148 186 : auto const &surf = state.dataSurface->Surface(IWin);
8149 186 : auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
8150 :
8151 186 : int CurCplxFenState = surfWin.ComplexFen.CurrentState;
8152 186 : auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
8153 186 : int iConst = surfWin.ComplexFen.State(CurCplxFenState).Konst;
8154 186 : int NTrnBasis = complexWinGeom.Trn.NBasis;
8155 :
8156 186 : if (!allocated(FL)) FL.allocate(NTrnBasis);
8157 744 : FL = Illums();
8158 : // if (!allocated(FLSU)) FLSU.dimension(NTrnBasis, 0.0);
8159 : // if (!allocated(FLSUdisk)) FLSUdisk.dimension(NTrnBasis, 0.0);
8160 :
8161 186 : if (!allocated(FirstFlux)) FirstFlux.allocate(NTrnBasis);
8162 744 : FirstFlux = Illums();
8163 : // if (!allocated(FirstFluxSU)) FirstFluxSU.dimension(NTrnBasis, 0.0);
8164 : // if (!allocated(FirstFluxSUdisk)) FirstFluxSUdisk.dimension(NTrnBasis, 0.0);
8165 :
8166 186 : NIncBasis = complexWinGeom.Inc.NBasis;
8167 186 : if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
8168 744 : ElementLuminance = Illums();
8169 : // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
8170 : // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
8171 :
8172 : // Integration over sky/ground/sun elements is done over window incoming basis element and flux is calculated for each
8173 : // outgoing direction. This is used to calculate first reflected flux
8174 :
8175 186 : ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
8176 :
8177 : // luminance from sun disk needs to include fraction of sunlit area
8178 186 : SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
8179 186 : Real64 COSIncSun = (SolBmIndex > 0) ? complexWinGeom.CosInc(SolBmIndex) : 0.0;
8180 :
8181 27156 : for (int i = 1; i <= (int)ElementLuminance.size(); ++i)
8182 26970 : ElementLuminance(i).sunDisk *= state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) * COSIncSun;
8183 :
8184 : // FLSKTot = 0.0;
8185 186 : FLTot.sun = 0.0;
8186 186 : FLTot.sunDisk = 0.0;
8187 186 : FFTot.sun = 0.0;
8188 186 : FFTot.sunDisk = 0.0;
8189 : // now calculate flux into each outgoing direction by integrating over all incoming directions
8190 27156 : for (int iBackElem = 1; iBackElem <= NTrnBasis; ++iBackElem) {
8191 3937620 : for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
8192 3910650 : LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
8193 3910650 : dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iBackElem, iIncElem);
8194 :
8195 3910650 : auto &fl = FL(iBackElem);
8196 3910650 : auto const &elemLum = ElementLuminance(iIncElem);
8197 19553250 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8198 15642600 : fl.sky[iSky] += dirTrans * LambdaInc * elemLum.sky[iSky];
8199 : }
8200 :
8201 3910650 : fl.sun += dirTrans * LambdaInc * elemLum.sun;
8202 3910650 : fl.sunDisk += dirTrans * LambdaInc * elemLum.sunDisk;
8203 : }
8204 :
8205 26970 : auto &firstFlux = FirstFlux(iBackElem);
8206 26970 : auto const &fl = FL(iBackElem);
8207 134850 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8208 107880 : firstFlux.sky[iSky] = fl.sky[iSky] * complexWinGeom.AveRhoVisOverlap(iBackElem);
8209 107880 : FFTot.sky[iSky] += firstFlux.sky[iSky];
8210 : // FLSKTot( iSky ) += FLSK( iSky, iBackElem );
8211 : }
8212 26970 : firstFlux.sun = fl.sun * complexWinGeom.AveRhoVisOverlap(iBackElem);
8213 26970 : FFTot.sun += firstFlux.sun;
8214 26970 : FLTot.sun += fl.sun;
8215 :
8216 26970 : firstFlux.sunDisk = fl.sunDisk * complexWinGeom.AveRhoVisOverlap(iBackElem);
8217 26970 : FFTot.sunDisk += firstFlux.sunDisk;
8218 26970 : FLTot.sunDisk += fl.sunDisk;
8219 : }
8220 :
8221 186 : auto const &thisEnclDaylight = dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex);
8222 186 : Real64 EnclInsideSurfArea = thisEnclDaylight.totInsSurfArea;
8223 :
8224 186 : auto &eintsk = dl->reflIllum(IHR, 1);
8225 930 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8226 744 : eintsk.sky[iSky] = FFTot.sky[iSky] * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8227 : } // for (iSky)
8228 :
8229 186 : dl->reflIllum(IHR, 1).sun = FFTot.sun * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8230 186 : dl->reflIllum(IHR, 1).sunDisk =
8231 186 : FFTot.sunDisk * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8232 :
8233 186 : if (allocated(FL)) FL.deallocate();
8234 : // if (allocated(FLSU)) FLSU.deallocate();
8235 : // if (allocated(FLSUdisk)) FLSUdisk.deallocate();
8236 :
8237 186 : if (allocated(FirstFlux)) FirstFlux.deallocate();
8238 : // if (allocated(FirstFluxSU)) FirstFluxSU.deallocate();
8239 : // if (allocated(FirstFluxSUdisk)) FirstFluxSUdisk.deallocate();
8240 :
8241 186 : if (allocated(ElementLuminance)) ElementLuminance.deallocate();
8242 : // if (allocated(ElementLuminanceSun)) ElementLuminanceSun.deallocate();
8243 : // if (allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.deallocate();
8244 186 : }
8245 :
8246 1488 : void DayltgDirectIllumComplexFenestration(EnergyPlusData &state,
8247 : int const IWin, // Window index
8248 : int const WinEl, // Current window element counter
8249 : int const IHR, // Hour of day
8250 : int const iRefPoint, // reference point index
8251 : CalledFor const CalledFrom,
8252 : int const MapNum)
8253 : {
8254 :
8255 : // SUBROUTINE INFORMATION:
8256 : // AUTHOR Simon Vidanovic
8257 : // DATE WRITTEN June 2013
8258 :
8259 1488 : auto &dl = state.dataDayltg;
8260 :
8261 : // Luminances from different sources to the window
8262 1488 : Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
8263 : // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side),
8264 : // exluding beam
8265 : // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side),
8266 : // due to sun beam
8267 :
8268 : int RefPointIndex; // reference point patch number
8269 :
8270 : Real64 dirTrans; // directional BSDF transmittance
8271 : Real64 dOmega; // solid view angle of current element
8272 : Real64 zProjection; // z-axe projection of solid view angle (used to calculate amount of light at horizontal surface
8273 : // laying at reference point)
8274 :
8275 1488 : int CurCplxFenState = state.dataSurface->SurfaceWindow(IWin).ComplexFen.CurrentState;
8276 1488 : auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
8277 1488 : int iConst = state.dataSurface->SurfaceWindow(IWin).ComplexFen.State(CurCplxFenState).Konst;
8278 1488 : int NIncBasis = complexWin.Geom(CurCplxFenState).Inc.NBasis;
8279 :
8280 1488 : if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
8281 5952 : ElementLuminance = Illums();
8282 : // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
8283 : // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
8284 :
8285 1488 : ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
8286 :
8287 : // find number of outgoing basis towards current reference point
8288 1488 : if (CalledFrom == CalledFor::RefPoint) {
8289 1488 : RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).RefPoint(iRefPoint).RefPointIndex(WinEl);
8290 1488 : dOmega = complexWin.RefPoint(iRefPoint).SolidAngle(WinEl);
8291 1488 : zProjection = complexWin.RefPoint(iRefPoint).SolidAngleVec(WinEl).z;
8292 0 : } else if (CalledFrom == CalledFor::MapPoint) {
8293 0 : assert(MapNum > 0);
8294 0 : RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).IlluminanceMap(iRefPoint, MapNum).RefPointIndex(WinEl);
8295 0 : dOmega = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngle(WinEl);
8296 0 : zProjection = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngleVec(WinEl).z;
8297 : }
8298 :
8299 1488 : Illums WinLum = Illums();
8300 1488 : Illums EDir = Illums();
8301 :
8302 217248 : for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
8303 : // LambdaInc = ComplexWind(IWin)%Geom(CurCplxFenState)%Inc%Lamda(iIncElem)
8304 215760 : dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(RefPointIndex, iIncElem);
8305 :
8306 215760 : auto const &elemLum = ElementLuminance(iIncElem);
8307 1078800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8308 863040 : WinLum.sky[iSky] += dirTrans * elemLum.sky[iSky];
8309 : }
8310 :
8311 215760 : WinLum.sun += dirTrans * elemLum.sun;
8312 :
8313 : // For sun disk need to go throug outgoing directions and see which directions actually contain reference point
8314 : }
8315 :
8316 1488 : if (zProjection > 0.0) {
8317 5580 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8318 4464 : EDir.sky[iSky] = WinLum.sky[iSky] * dOmega * zProjection;
8319 : }
8320 1116 : EDir.sun = WinLum.sun * dOmega * zProjection;
8321 : }
8322 :
8323 : // Store solution in global variables
8324 1488 : auto &avwlsk = dl->avgWinLum(IHR, 1);
8325 1488 : auto &edirsk = dl->dirIllum(IHR, 1);
8326 :
8327 7440 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8328 5952 : avwlsk.sky[iSky] += WinLum.sky[iSky];
8329 5952 : edirsk.sky[iSky] += EDir.sky[iSky];
8330 : }
8331 :
8332 1488 : dl->avgWinLum(IHR, 1).sun += WinLum.sun;
8333 1488 : dl->dirIllum(IHR, 1).sun += EDir.sun;
8334 : // AVWLSUdisk(1,IHR) = AVWLSUdisk(1,IHR) + WinLumSUdisk
8335 1488 : } // DayltgDirectIllumComplexFenestration()
8336 :
8337 186 : void DayltgDirectSunDiskComplexFenestration(EnergyPlusData &state,
8338 : int const iWin, // Window index
8339 : int const iHour, // Hour of day
8340 : int const iRefPoint,
8341 : int const NumEl, // Total number of window elements
8342 : Real64 const AZVIEW, // Azimuth of view vector in absolute coord system for
8343 : CalledFor const CalledFrom, // indicate which type of routine called this routine
8344 : int const MapNum)
8345 : {
8346 :
8347 : // SUBROUTINE INFORMATION:
8348 : // AUTHOR Simon Vidanovic
8349 : // DATE WRITTEN June 2013
8350 :
8351 : // PURPOSE OF THIS SUBROUTINE:
8352 : // Calculate illuminance from sun disk for complex fenestration systems
8353 :
8354 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8355 186 : auto &dl = state.dataDayltg;
8356 :
8357 186 : assert(CalledFrom != CalledFor::MapPoint || MapNum > 0);
8358 :
8359 186 : auto const &window = state.dataSurface->SurfaceWindow(iWin);
8360 186 : int CurCplxFenState = window.ComplexFen.CurrentState;
8361 186 : int iConst = window.ComplexFen.State(CurCplxFenState).Konst;
8362 :
8363 186 : auto const &complexWindow = state.dataBSDFWindow->ComplexWind(iWin);
8364 186 : auto const &complexWindowGeom = complexWindow.Geom(CurCplxFenState);
8365 186 : auto const &complexWindowDayltgGeom = complexWindow.DaylghtGeom(CurCplxFenState);
8366 186 : int SolBmIndex = complexWindowGeom.SolBmIndex(iHour, state.dataGlobal->TimeStep);
8367 :
8368 186 : Real64 WindowSolidAngleDaylightPoint = (CalledFrom == CalledFor::RefPoint) ? window.refPts(iRefPoint).solidAngWtd : 0.0;
8369 186 : if (WindowSolidAngleDaylightPoint < 1e-6) return;
8370 :
8371 0 : Illums WinLum;
8372 0 : Illums ElemLum;
8373 :
8374 0 : int NTrnBasis = complexWindowGeom.Trn.NBasis;
8375 0 : for (int iTrnElem = 1; iTrnElem <= NTrnBasis; ++iTrnElem) {
8376 : // if ray from any part of the window can reach reference point
8377 : int refPointIntersect = (CalledFrom == CalledFor::RefPoint)
8378 0 : ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPointIntersection(iTrnElem)
8379 0 : : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPointIntersection(iTrnElem);
8380 :
8381 0 : if (refPointIntersect == 0) continue;
8382 :
8383 0 : Real64 PosFac = (CalledFrom == CalledFor::RefPoint) ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPtIntPosFac(iTrnElem)
8384 0 : : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPtIntPosFac(iTrnElem);
8385 :
8386 0 : Real64 RayZ = -complexWindowGeom.sTrn(iTrnElem).z;
8387 :
8388 : // Need to recalculate position factor for dominant direction in case of specular bsdf. Otherwise this will produce
8389 : // very inaccurate results because of position factor of the sun and bsdf pach can vary by lot
8390 0 : if (iTrnElem == SolBmIndex) {
8391 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
8392 0 : Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
8393 0 : PosFac = DayltgGlarePositionFactor(XR, YR);
8394 0 : RayZ = dl->sunAngles.sinPhi;
8395 : }
8396 :
8397 0 : if (PosFac == 0.0) continue;
8398 :
8399 0 : Real64 dirTrans = (SolBmIndex > 0) ? state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iTrnElem, SolBmIndex) : 0.0;
8400 0 : Real64 LambdaTrn = complexWindowGeom.Trn.Lamda(iTrnElem);
8401 0 : Vector3<Real64> V = -complexWindowGeom.sTrn(iTrnElem);
8402 0 : Vector3<Real64> RWin = state.dataSurface->Surface(iWin).Centroid;
8403 0 : Real64 TransBeam = DayltgHitObstruction(state, iHour, iWin, RWin, V);
8404 :
8405 0 : WinLum.sunDisk += (14700.0 * std::sqrt(0.000068 * PosFac) * double(NumEl) / std::pow(WindowSolidAngleDaylightPoint, 0.8)) * dirTrans *
8406 0 : LambdaTrn * TransBeam;
8407 :
8408 0 : ElemLum.sunDisk += RayZ * dirTrans * LambdaTrn * TransBeam;
8409 0 : } // for (iTrnElem)
8410 :
8411 0 : dl->avgWinLum(iHour, 1).sunDisk = WinLum.sunDisk;
8412 0 : dl->dirIllum(iHour, 1).sunDisk = ElemLum.sunDisk;
8413 : }
8414 :
8415 253406236 : Real64 DayltgSkyLuminance(EnergyPlusData const &state,
8416 : SkyType sky, // Sky type: 1=clear, 2=clear turbid, 3=intermediate, 4=overcast
8417 : Real64 const THSKY, // Azimuth and altitude of sky element (radians)
8418 : Real64 const PHSKY)
8419 : {
8420 :
8421 : // SUBROUTINE INFORMATION:
8422 : // AUTHOR Fred Winkelmann
8423 : // DATE WRITTEN July 1997
8424 :
8425 : // PURPOSE OF THIS SUBROUTINE:
8426 : // Called by CalcDayltgCoefficients, DayltgExtHorizIllum AND DayltgInterReflectedIllum. gives
8427 : // luminance in cd/m2 for four different sky types, as described in R.Perez, P.Ineichen,
8428 : // R.Seals, J.Michalsky and R.Stewart, "Modeling daylight availability and irradiance
8429 : // components from direct and global irradiance," Solar Energy 44, 1990, 271-289.
8430 : // The luminance distributions in this routine are normalized such that
8431 : // the zenith luminance is 1.0, i.e., DayltgSkyLuminance =
8432 : // (sky luminance at THSKY, PHSKY)/(zenith luminance), which is dimensionless.
8433 : // The sky types are:
8434 : // 1. Standard CIE clear sky
8435 : // 2. Standard CIE high-turbidity clear sky
8436 : // 3. CIE intermediate sky
8437 : // 4. CIE overcast sky
8438 :
8439 : // METHODOLOGY EMPLOYED:
8440 :
8441 : // REFERENCES:
8442 : // Based on DOE-2.1E subroutine DSKYLU, which did only clear and overcast skies.
8443 :
8444 : // OTHER NOTES:
8445 : // THSKY ranges from 0 to 2Pi starting with 0 directly East and rotating clockwise.
8446 : // PHSKY ranges from 0 to Pi starting with 0 at the horizon and Pi/2 at the zenith.
8447 :
8448 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
8449 253406236 : auto &dl = state.dataDayltg;
8450 :
8451 253406236 : Real64 G = 0.0; // Angle between sun and element of sky (radians)
8452 253406236 : Real64 COSG = 0.0; // Cosine of G
8453 :
8454 253406236 : Real64 SPHSKY = max(std::sin(PHSKY), 0.01); // Prevent floating point underflows
8455 253406236 : Real64 Z = Constant::PiOvr2 - dl->sunAngles.phi;
8456 253406236 : if (sky != SkyType::Overcast) { // Following not needed for overcast sky
8457 190054677 : COSG = SPHSKY * dl->sunAngles.sinPhi + std::cos(PHSKY) * dl->sunAngles.cosPhi * std::cos(THSKY - dl->sunAngles.theta);
8458 190054677 : COSG = max(DataPrecisionGlobals::constant_minusone, min(COSG, 1.0)); // Prevent out of range due to roundoff
8459 190054677 : G = std::acos(COSG);
8460 : }
8461 :
8462 253406236 : switch (sky) {
8463 63351559 : case SkyType::Clear: {
8464 63351559 : Real64 Z1 = 0.910 + 10.0 * std::exp(-3.0 * G) + 0.45 * COSG * COSG;
8465 63351559 : Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
8466 63351559 : Real64 Z3 = 0.27385 * (0.91 + 10.0 * std::exp(-3.0 * Z) + 0.45 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
8467 63351559 : return Z1 * Z2 / Z3;
8468 :
8469 : } break;
8470 63351559 : case SkyType::ClearTurbid: {
8471 63351559 : Real64 Z1 = 0.856 + 16.0 * std::exp(-3.0 * G) + 0.3 * COSG * COSG;
8472 63351559 : Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
8473 63351559 : Real64 Z3 = 0.27385 * (0.856 + 16.0 * std::exp(-3.0 * Z) + 0.3 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
8474 63351559 : return Z1 * Z2 / Z3;
8475 :
8476 : } break;
8477 :
8478 63351559 : case SkyType::Intermediate: {
8479 63351559 : 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;
8480 63351559 : Real64 Z2 = std::exp(-G * 0.563 * ((dl->sunAngles.phi - 0.008) * (PHSKY + 1.059) + 0.812));
8481 63351559 : Real64 Z3 = 0.99224 * std::sin(2.6 * dl->sunAngles.phi + 0.316) + 2.73852;
8482 63351559 : Real64 Z4 = std::exp(-Z * 0.563 * ((dl->sunAngles.phi - 0.008) * 2.6298 + 0.812));
8483 63351559 : return Z1 * Z2 / (Z3 * Z4);
8484 : } break;
8485 63351559 : case SkyType::Overcast: {
8486 63351559 : return (1.0 + 2.0 * SPHSKY) / 3.0;
8487 : } break;
8488 0 : default:
8489 0 : assert(false);
8490 : return 0.0;
8491 : }
8492 : }
8493 :
8494 76902 : Real64 ProfileAngle(EnergyPlusData &state,
8495 : int const SurfNum, // Surface number
8496 : Vector3<Real64> const &CosDirSun, // Solar direction cosines
8497 : DataWindowEquivalentLayer::Orientation const HorOrVert // If HORIZONTAL, calculates ProfileAngHor
8498 : )
8499 : {
8500 :
8501 : // SUBROUTINE INFORMATION:
8502 : // AUTHOR Fred Winkelmann
8503 : // DATE WRITTEN May 2001
8504 :
8505 : // PURPOSE OF THIS SUBROUTINE:
8506 : // Calculates profile angle for a surface.
8507 :
8508 : // Locals
8509 : // SUBROUTINE ARGUMENT DEFINITIONS:
8510 : // For HorOrVert = HORIZONTAL,
8511 : // this is the incidence angle in a plane that is normal to the window
8512 : // and parallel to the Y-axis of the window (the axis along
8513 : // which the height of the window is measured).
8514 : // For HorOrVert = VERTICAL,
8515 : // this is the incidence angle in a plane that is normal to the window
8516 : // and parallel to the X-axis of the window (the axis along
8517 : // which the width of the window is measured).
8518 : // If VERTICAL, calculates ProfileAngVert
8519 :
8520 76902 : auto const &surf = state.dataSurface->Surface(SurfNum);
8521 76902 : if (HorOrVert == DataWindowEquivalentLayer::Orientation::Horizontal) { // Profile angle for horizontal structures
8522 76902 : Real64 ElevWin =
8523 76902 : Constant::PiOvr2 - surf.Tilt * Constant::DegToRadians; // Window elevation: angle between outward normal and horizontal (radians)
8524 76902 : Real64 AzimWin = (90.0 - surf.Azimuth) * Constant::DegToRadians; // Window azimuth (radians)
8525 76902 : Real64 ElevSun = std::asin(CosDirSun.z); // Sun elevation; angle between sun and horizontal (radians)
8526 76902 : Real64 AzimSun = std::atan2(CosDirSun.y, CosDirSun.x); // Sun azimuth (radians)
8527 76902 : return std::atan(std::sin(ElevSun) / std::abs(std::cos(ElevSun) * std::cos(AzimWin - AzimSun))) - ElevWin;
8528 : } else { // Profile angle for vertical structures
8529 0 : Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRadians;
8530 0 : Real64 AzimWin = surf.Azimuth * Constant::DegToRadians; // 7952
8531 0 : Real64 AzimSun = std::atan2(CosDirSun.x, CosDirSun.y); // 7952
8532 :
8533 : Real64 ProfileAng;
8534 0 : if (std::abs(ElevWin) < 0.1) { // Near-vertical window
8535 0 : ProfileAng = AzimWin - AzimSun; // CR7952 allow sign changes.
8536 : } else {
8537 0 : Vector3<Real64> WinNorm = surf.OutNormVec; // Window outward normal unit vector
8538 0 : Real64 ThWin = AzimWin - Constant::PiOvr2;
8539 0 : Real64 const sin_ElevWin = std::sin(ElevWin);
8540 : // Cross product of WinNorm and vector along window baseline
8541 0 : Vector3<Real64> WinNormCrossBase = {-sin_ElevWin * std::cos(ThWin), sin_ElevWin * std::sin(ThWin), std::cos(ElevWin)};
8542 : // Projection of sun vector onto plane (perpendicular to window plane) determined
8543 : // by WinNorm and vector along baseline of window
8544 0 : Vector3<Real64> SunPrime = CosDirSun - WinNormCrossBase * dot(CosDirSun, WinNormCrossBase);
8545 0 : ProfileAng = std::abs(std::acos(dot(WinNorm, SunPrime) / SunPrime.magnitude()));
8546 : // CR7952 correct sign of result for vertical slats
8547 0 : if ((AzimWin - AzimSun) < 0.0) ProfileAng = -1.0 * ProfileAng;
8548 0 : }
8549 : // Constrain to 0 to pi
8550 0 : if (ProfileAng > Constant::Pi) ProfileAng = 2.0 * Constant::Pi - ProfileAng;
8551 0 : return ProfileAng;
8552 : }
8553 : }
8554 :
8555 36320 : void DayltgClosestObstruction(EnergyPlusData &state,
8556 : Vector3<Real64> const &RecPt, // Point on window from which ray emanates (m)
8557 : Vector3<Real64> const &RayVec, // Unit vector along ray pointing away from window (m)
8558 : int &NearestHitSurfNum, // Surface number of nearest obstruction that is hit by ray;
8559 : Vector3<Real64> &NearestHitPt // Ray's hit point on nearest obstruction (m)
8560 : )
8561 : {
8562 :
8563 : // SUBROUTINE INFORMATION:
8564 : // AUTHOR Fred Winkelmann
8565 : // DATE WRITTEN November 2003
8566 :
8567 : // PURPOSE OF THIS SUBROUTINE:
8568 : // Determines surface number and hit point of closest exterior obstruction hit
8569 : // by a ray from a window. If no obstruction is hit, NearestHitSurfNum = 0.
8570 :
8571 : // Locals
8572 : // SUBROUTINE ARGUMENT DEFINITIONS:
8573 : // = 0 if no obstruction is hit.
8574 :
8575 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8576 36320 : Vector3<Real64> HitPt; // Hit point on an obstruction (m)
8577 : bool hit; // True iff obstruction is hit
8578 :
8579 36320 : NearestHitSurfNum = 0;
8580 36320 : Real64 NearestHitDistance_sq(std::numeric_limits<Real64>::max()); // Distance squared from receiving point to nearest hit point for a ray (m^2)
8581 36320 : NearestHitPt = 0.0;
8582 36320 : if (state.dataSurface->TotSurfaces < octreeCrossover) { // Linear search through surfaces
8583 :
8584 181600 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
8585 : // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
8586 145280 : hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
8587 145280 : if (!hit) // Ray pierces surface
8588 108960 : continue;
8589 :
8590 : // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
8591 : // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
8592 36320 : if ((state.dataSurface->Surface(ObsSurfNum).Class == SurfaceClass::Window) &&
8593 0 : (state.dataSurface->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum)) {
8594 0 : NearestHitSurfNum = ObsSurfNum;
8595 : } else {
8596 : // Distance squared from receiving point to hit point
8597 36320 : Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
8598 : // Reset NearestHitSurfNum and NearestHitDistance_sq if this hit point is closer than previous closest
8599 36320 : if (HitDistance_sq < NearestHitDistance_sq) {
8600 36320 : NearestHitDistance_sq = HitDistance_sq;
8601 36320 : NearestHitSurfNum = ObsSurfNum;
8602 36320 : NearestHitPt = HitPt;
8603 : }
8604 : } // End of check if obstruction was hit
8605 36320 : } // for (ObsSurfNum)
8606 :
8607 : } else { // Surface octree search
8608 :
8609 0 : SurfaceData const *nearestHitSurface(nullptr);
8610 :
8611 : // Lambda function for the octree to test for surface hit
8612 0 : auto surfaceHit = [=, &state, &RecPt, &RayVec, &hit, &NearestHitDistance_sq, &nearestHitSurface, &NearestHitPt](SurfaceData const &surface) {
8613 0 : if (surface.IsShadowPossibleObstruction) {
8614 0 : Vector3<Real64> HitPt;
8615 : // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
8616 0 : hit = PierceSurface(surface, RecPt, RayVec, HitPt); // Check if ray pierces surface
8617 0 : if (!hit) return;
8618 :
8619 : // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
8620 : // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
8621 0 : if ((surface.Class == SurfaceClass::Window) && (surface.BaseSurf > 0) &&
8622 0 : (&state.dataSurface->Surface(surface.BaseSurf) == nearestHitSurface)) {
8623 0 : nearestHitSurface = &surface;
8624 : } else {
8625 : // Distance squared from receiving point to hit point
8626 0 : Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
8627 : // Reset nearestHitSurface and NearestHitDistance_sq if this hit point is closer than previous closest
8628 0 : if (HitDistance_sq < NearestHitDistance_sq) {
8629 0 : NearestHitDistance_sq = HitDistance_sq;
8630 0 : nearestHitSurface = &surface;
8631 0 : NearestHitPt = HitPt;
8632 : }
8633 : } // End of check if obstruction was hit
8634 0 : }
8635 0 : };
8636 :
8637 : // Process octree surface candidates
8638 0 : Vector3<Real64> const RayVec_inv(SurfaceOctreeCube::safe_inverse(RayVec));
8639 0 : state.dataHeatBalMgr->surfaceOctree.processSurfaceRayIntersectsCube(RecPt, RayVec, RayVec_inv, surfaceHit);
8640 0 : if (nearestHitSurface != nullptr) { // Find surface number: This is inefficient: Improve when surfaces know their own number
8641 0 : for (int i = 1; i <= state.dataSurface->TotSurfaces; ++i) {
8642 0 : if (&state.dataSurface->Surface(i) == nearestHitSurface) {
8643 0 : NearestHitSurfNum = i;
8644 0 : break;
8645 : }
8646 : }
8647 0 : assert(NearestHitSurfNum != 0);
8648 : }
8649 0 : }
8650 36320 : } // DayltgClosestObstruction()
8651 :
8652 36320 : Real64 DayltgSurfaceLumFromSun(EnergyPlusData &state,
8653 : int const IHR, // Hour number
8654 : Vector3<Real64> const &Ray, // Ray from window to reflecting surface (m)
8655 : int const ReflSurfNum, // Number of surface for which luminance is being calculated
8656 : Vector3<Real64> const &ReflHitPt // Point on ReflSurfNum for luminance calculation (m)
8657 : )
8658 : {
8659 :
8660 : // SUBROUTINE INFORMATION:
8661 : // AUTHOR Fred Winkelmann
8662 : // DATE WRITTEN November 2003
8663 :
8664 : // PURPOSE OF THIS SUBROUTINE:
8665 : // Calculates exterior surface luminance due to beam solar diffuse reflection.
8666 :
8667 : // Locals
8668 : // SUBROUTINE ARGUMENT DEFINITIONS:
8669 : // beam normal illuminance (cd/m2)
8670 :
8671 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8672 36320 : Vector3<Real64> SurfaceLumFromSunReflNorm; // Unit normal to reflecting surface (m)
8673 36320 : Vector3<Real64> SurfaceLumFromSunObsHitPt; // Hit point on obstruction (m)
8674 : bool hitObs; // True iff obstruction is hit
8675 : Real64 DiffVisRefl; // Diffuse visible reflectance of ReflSurfNum
8676 :
8677 : // Skip daylighting shelves since reflection from these is separately calculated
8678 36320 : if (state.dataSurface->SurfDaylightingShelfInd(ReflSurfNum) > 0) return 0.0;
8679 :
8680 36320 : auto const &reflSurf = state.dataSurface->Surface(ReflSurfNum);
8681 :
8682 : // Normal to reflecting surface in hemisphere containing window element
8683 36320 : SurfaceLumFromSunReflNorm = reflSurf.OutNormVec;
8684 36320 : if (reflSurf.IsShadowing) {
8685 36320 : if (dot(SurfaceLumFromSunReflNorm, Ray) > 0.0) {
8686 36320 : SurfaceLumFromSunReflNorm *= -1.0;
8687 : }
8688 : }
8689 : // Cosine of angle of incidence of sun at HitPt if sun were to reach HitPt
8690 36320 : Vector3<Real64> const SUNCOS_IHR = state.dataSurface->SurfSunCosHourly(IHR);
8691 36320 : Real64 CosIncAngAtHitPt = dot(SurfaceLumFromSunReflNorm, SUNCOS_IHR);
8692 : // Require that the sun be in front of this surface relative to window element
8693 36320 : if (CosIncAngAtHitPt <= 0.0) return 0.0; // Sun is in back of reflecting surface
8694 : // Sun reaches ReflHitPt if vector from ReflHitPt to sun is unobstructed
8695 0 : hitObs = false;
8696 0 : for (int ObsSurfNum : state.dataSurface->AllShadowPossObstrSurfaceList) {
8697 : // Exclude as a possible obstructor ReflSurfNum and its base surface (if it has one)
8698 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == reflSurf.BaseSurf) continue;
8699 0 : hitObs = PierceSurface(state, ObsSurfNum, ReflHitPt, SUNCOS_IHR, SurfaceLumFromSunObsHitPt);
8700 0 : if (hitObs) break;
8701 0 : }
8702 :
8703 0 : if (hitObs) return 0.0; // Obstruction was hit, blocking s auto surfaceHit = [&state, &GroundHitPtun
8704 : // Obstruction was not hit; sun reaches ReflHitPt.
8705 : // Calculate luminance at ReflHitPt due to beam solar reflection (for unit beam normal illuminance)
8706 0 : if (reflSurf.IsShadowing) {
8707 0 : DiffVisRefl = state.dataSurface->SurfShadowDiffuseVisRefl(ReflSurfNum);
8708 : // Note that if the shadowing surface has a non-zero glazing fraction (e.g., neighboring bldg) that the above is
8709 : // (1 - glazing fraction) * (vis refl of opaque part of shadowing surface); specular reflection is
8710 : // excluded in this value of DiffVisRefl.
8711 : } else { // Exterior building surface
8712 0 : if (!state.dataConstruction->Construct(reflSurf.Construction).TypeIsWindow) {
8713 0 : DiffVisRefl = 1.0 - state.dataConstruction->Construct(reflSurf.Construction).OutsideAbsorpSolar;
8714 : } else {
8715 : // Window; assume bare so no beam-to-diffuse reflection
8716 0 : DiffVisRefl = 0.0;
8717 : }
8718 : }
8719 0 : return CosIncAngAtHitPt * DiffVisRefl / Constant::Pi;
8720 36320 : }
8721 :
8722 990056 : void DayltgInteriorMapIllum(EnergyPlusData &state)
8723 : {
8724 :
8725 : // *****super modified version of DayltgInteriorIllum by Peter Graham Ellis
8726 : // *****removes all control code, just calculates illum with previously determined control settings
8727 : // *****this should be packaged into a subroutine called from 2 places
8728 :
8729 : // SUBROUTINE INFORMATION:
8730 : // AUTHOR Fred Winkelmann
8731 : // DATE WRITTEN July 1997
8732 : // MODIFIED March 2000, FW: interpolate clear-sky daylight factors using
8733 : // HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
8734 : // only HourOfDay was used
8735 : // Jan 2001, FW: interpolate in slat angle for windows with blinds
8736 : // that have movable slats
8737 : // Dec 2003, FW: fix bug--even though between-glass shade/blind is on
8738 : // daylight illum at ref pt was calculated as though it was off
8739 : // June 2009, TH: modified for thermochromic windows
8740 : // March 2010, TH: fix bug (CR 8057) for electrochromic windows
8741 : // RE-ENGINEERED na
8742 :
8743 : // PURPOSE OF THIS SUBROUTINE:
8744 : // Using daylighting factors and exterior illuminance, determine
8745 : // the current-hour interior daylight illuminance and glare index
8746 : // at each reference point in a space.
8747 :
8748 : // Called by InitSurfaceHeatBalance.
8749 :
8750 : // REFERENCES:
8751 : // Based on DOE-2.1E subroutine DINTIL.
8752 990056 : auto &dl = state.dataDayltg;
8753 :
8754 : // Locals
8755 990056 : Array1D<Real64> daylight_illum;
8756 :
8757 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8758 : int iSky1; // Sky type index values for averaging two sky types
8759 : int iSky2;
8760 : Real64 SkyWeight; // Weighting factor used to average two different sky types
8761 : Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
8762 : // luminous efficacy and horizontal illuminance from averaged sky
8763 :
8764 990056 : if (state.dataGlobal->WarmupFlag) return;
8765 :
8766 242675 : daylight_illum.allocate(MaxMapRefPoints);
8767 :
8768 : // Initialize reference point illuminance and window background luminance
8769 :
8770 244606 : for (auto &thisMap : dl->illumMaps) {
8771 1931 : int enclNum = thisMap.enclIndex;
8772 1931 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
8773 :
8774 1931 : int NREFPT = thisMap.TotalMapRefPoints; // Number of daylighting map reference points
8775 :
8776 1931 : daylight_illum = 0.0;
8777 :
8778 1931 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
8779 1419 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
8780 1419 : iSky1 = (int)SkyType::Clear;
8781 1419 : iSky2 = (int)SkyType::ClearTurbid;
8782 512 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
8783 198 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
8784 198 : iSky1 = (int)SkyType::ClearTurbid;
8785 198 : iSky2 = (int)SkyType::Intermediate;
8786 : } else { // Sky is average of intermediate and overcast
8787 314 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
8788 314 : iSky1 = (int)SkyType::Intermediate;
8789 314 : iSky2 = (int)SkyType::Overcast;
8790 : }
8791 :
8792 : // First loop over windows in this space.
8793 : // Find contribution of each window to the daylight illum
8794 : // and to the glare numerator at each reference point.
8795 : // Use shading flags set in WindowShadingManager.
8796 :
8797 1931 : auto &daylFacHrCurr = thisMap.daylFac[state.dataGlobal->HourOfDay];
8798 1931 : auto &daylFacHrPrev = thisMap.daylFac[state.dataGlobal->PreviousHour];
8799 :
8800 12439 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
8801 10508 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
8802 :
8803 : // Added TH 6/29/2009 for thermochromic windows
8804 10508 : Real64 VTRatio = 1.0;
8805 10508 : if (NREFPT > 0) {
8806 10508 : int IConst = state.dataSurface->Surface(IWin).Construction;
8807 10508 : auto const &construction = state.dataConstruction->Construct(IConst);
8808 10508 : if (construction.TCFlag == 1) {
8809 : // For thermochromic windows, daylight and glare factors are always calculated
8810 : // based on the master construction. They need to be adjusted by the VTRatio, including:
8811 : // ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
8812 : // DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
8813 0 : Real64 VTNow = General::POLYF(1.0, construction.TransVisBeamCoef);
8814 0 : Real64 VTMaster = General::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConst).TransVisBeamCoef);
8815 0 : VTRatio = VTNow / VTMaster;
8816 : }
8817 : }
8818 :
8819 10508 : Real64 wgtThisHr = state.dataGlobal->WeightNow;
8820 10508 : Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
8821 :
8822 10508 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
8823 :
8824 10508 : auto &dfhr = DFHR[iWinCover_Bare];
8825 10508 : auto &dfhr2 = DFHR[iWinCover_Shaded];
8826 :
8827 10508 : int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
8828 10508 : int slatAngLo = SurfWinSlatsAngIndex + 1;
8829 10508 : int slatAngHi = min(slatAngLo + 1, Material::MaxSlatAngs + 1);
8830 10508 : Real64 interpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
8831 :
8832 : // Loop over reference points
8833 993558 : for (int ILB = 1; ILB <= NREFPT; ++ILB) {
8834 :
8835 983050 : auto const &illSkyCurr = daylFacHrCurr(loop, ILB, 1);
8836 983050 : auto const &illSkyPrev = daylFacHrPrev(loop, ILB, 1);
8837 983050 : auto const &ill2SkyCurr = daylFacHrCurr(loop, ILB, 2);
8838 983050 : auto const &ill2SkyPrev = daylFacHrPrev(loop, ILB, 2);
8839 :
8840 983050 : auto const &illLoSkyCurr = daylFacHrCurr(loop, ILB, slatAngLo);
8841 983050 : auto const &illLoSkyPrev = daylFacHrPrev(loop, ILB, slatAngLo);
8842 983050 : auto const &illHiSkyCurr = daylFacHrCurr(loop, ILB, slatAngHi);
8843 983050 : auto const &illHiSkyPrev = daylFacHrPrev(loop, ILB, slatAngHi);
8844 :
8845 : // Daylight factors for current sun position
8846 4915250 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8847 : // ===Bare window===
8848 3932200 : dfhr.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr.sky[iSky] + wgtPrevHr * illSkyPrev.sky[iSky]);
8849 :
8850 8526200 : if ((state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
8851 4594000 : (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin))) {
8852 :
8853 : // ===Shaded window===
8854 3270400 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
8855 : // Shade, screen, blind with fixed slats, or diffusing glass
8856 3270400 : dfhr2.sky[iSky] = VTRatio * (wgtThisHr * ill2SkyCurr.sky[iSky] + wgtPrevHr * ill2SkyPrev.sky[iSky]);
8857 :
8858 : } else { // Blind with movable slats
8859 0 : Real64 illSkyCurr = General::Interp(illLoSkyCurr.sky[iSky], illHiSkyCurr.sky[iSky], interpFac);
8860 0 : Real64 illSkyPrev = General::Interp(illLoSkyPrev.sky[iSky], illHiSkyPrev.sky[iSky], interpFac);
8861 :
8862 0 : dfhr2.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr + wgtPrevHr * illSkyPrev);
8863 : } // End of check if window has blind with movable slats
8864 : } // End of check if window is shaded or has diffusing glass
8865 : } // for (iSky)
8866 :
8867 : // Sun daylight factor for bare/shaded window
8868 983050 : std::array<Illums, (int)DataSurfaces::WinCover::Num> tmpDFHR;
8869 983050 : tmpDFHR[iWinCover_Bare].sun = VTRatio * (wgtThisHr * (daylFacHrCurr(loop, ILB, 1).sun + daylFacHrCurr(loop, ILB, 1).sunDisk) +
8870 983050 : wgtPrevHr * (daylFacHrPrev(loop, ILB, 1).sun + daylFacHrPrev(loop, ILB, 1).sunDisk));
8871 :
8872 2131550 : if ((state.dataSurface->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
8873 1148500 : (IS_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) || state.dataSurface->SurfWinSolarDiffusing(IWin))) {
8874 :
8875 : // ===Shaded window===
8876 817600 : if (!state.dataSurface->SurfWinMovableSlats(IWin)) {
8877 : // Shade, screen, blind with fixed slats, or diffusing glass
8878 1635200 : tmpDFHR[iWinCover_Shaded].sun =
8879 817600 : VTRatio * (wgtThisHr * daylFacHrCurr(loop, ILB, 2).sun + wgtPrevHr * daylFacHrPrev(loop, ILB, 2).sun);
8880 :
8881 817600 : if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
8882 817600 : tmpDFHR[iWinCover_Shaded].sun +=
8883 817600 : VTRatio * (wgtThisHr * daylFacHrCurr(loop, ILB, 2).sunDisk + wgtPrevHr * daylFacHrPrev(loop, ILB, 2).sunDisk);
8884 : }
8885 : } else { // Blind with movable slats
8886 0 : int SurfWinSlatsAngIndex = state.dataSurface->SurfWinSlatsAngIndex(IWin);
8887 0 : int slatAngLo = SurfWinSlatsAngIndex + 1;
8888 0 : int slatAngHi = min(slatAngLo + 1, Material::MaxSlatAngs + 1);
8889 0 : Real64 interpFac = state.dataSurface->SurfWinSlatsAngInterpFac(IWin);
8890 :
8891 : Real64 DaylIllFacSunNow =
8892 0 : General::Interp(daylFacHrCurr(loop, ILB, slatAngLo).sun, daylFacHrCurr(loop, ILB, slatAngHi).sun, interpFac);
8893 : Real64 DaylIllFacSunPrev =
8894 0 : General::Interp(daylFacHrPrev(loop, ILB, slatAngLo).sun, daylFacHrPrev(loop, ILB, slatAngHi).sun, interpFac);
8895 0 : DFHR[iWinCover_Shaded].sun = VTRatio * (wgtThisHr * DaylIllFacSunNow + wgtPrevHr * DaylIllFacSunPrev);
8896 :
8897 : // We add the contribution from the solar disk if slats do not block beam solar
8898 : // TH CR 8010, DaylIllFacSunDisk needs to be interpolated
8899 0 : if (!state.dataSurface->SurfWinSlatsBlockBeam(IWin)) {
8900 : Real64 DaylIllFacSunDiskNow =
8901 0 : General::Interp(daylFacHrCurr(loop, ILB, slatAngLo).sunDisk, daylFacHrCurr(loop, ILB, slatAngHi).sunDisk, interpFac);
8902 : Real64 DaylIllFacSunDiskPrev =
8903 0 : General::Interp(daylFacHrPrev(loop, ILB, slatAngLo).sunDisk, daylFacHrPrev(loop, ILB, slatAngHi).sunDisk, interpFac);
8904 0 : DFHR[iWinCover_Shaded].sun += VTRatio * (wgtThisHr * DaylIllFacSunDiskNow + wgtPrevHr * DaylIllFacSunDiskPrev);
8905 : }
8906 : } // End of check if window has blind with movable slats
8907 : } // End of check if window is shaded or has diffusing glass
8908 :
8909 : // Get illuminance at ref point from bare and shaded window by
8910 : // multiplying daylight factors by exterior horizontal illuminance
8911 :
8912 : // Adding 0.001 in the following prevents zero DayltgInteriorMapIllumHorIllSky in early morning or late evening when sun
8913 : // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
8914 983050 : Illums tmpHorIll; // Horizontal illuminance for different sky types
8915 983050 : auto const &gilCurr = dl->horIllum[state.dataGlobal->HourOfDay];
8916 983050 : auto const &gilPrev = dl->horIllum[state.dataGlobal->PreviousHour];
8917 4915250 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8918 3932200 : tmpHorIll.sky[iSky] = wgtThisHr * gilCurr.sky[iSky] + wgtPrevHr * gilPrev.sky[iSky] + 0.001;
8919 : }
8920 :
8921 : // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
8922 : // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
8923 : // also calculated in DayltgLuminousEfficacy.
8924 983050 : HorIllSkyFac = state.dataEnvrn->HISKF / ((1.0 - SkyWeight) * tmpHorIll.sky[iSky2] + SkyWeight * tmpHorIll.sky[iSky1]);
8925 :
8926 2783700 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
8927 1966100 : if (iWinCover == iWinCover_Shaded) {
8928 983050 : if (state.dataSurface->SurfWinWindowModelType(IWin) == WindowModel::BSDF) break;
8929 983050 : if (NOT_SHADED(state.dataSurface->SurfWinShadingFlag(IWin)) && !state.dataSurface->SurfWinSolarDiffusing(IWin)) break;
8930 : }
8931 1800650 : auto const &dfhr = DFHR[iWinCover];
8932 :
8933 3601300 : thisMap.refPts(ILB).winLums(loop)[iWinCover] = tmpDFHR[iWinCover].sun * state.dataEnvrn->HISUNF +
8934 1800650 : HorIllSkyFac * (dfhr.sky[iSky1] * SkyWeight * tmpHorIll.sky[iSky1] +
8935 1800650 : dfhr.sky[iSky2] * (1.0 - SkyWeight) * tmpHorIll.sky[iSky2]);
8936 : }
8937 :
8938 : } // End of reference point loop
8939 : } // End of first loop over windows
8940 :
8941 : // Second loop over windows. Find total daylight illuminance
8942 : // and background luminance for each ref pt from all windows in
8943 : // the space. Use shading flags.
8944 :
8945 12439 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
8946 10508 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
8947 10508 : auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
8948 :
8949 10508 : int IS = findWinShadingStatus(state, IWin);
8950 :
8951 : // CR 8057. 3/17/2010.
8952 : // Switchable windows may be in partially switched state rather than fully dark state
8953 10508 : Real64 VTMULT = 1.0;
8954 :
8955 10508 : int ICtrl = state.dataSurface->Surface(IWin).activeWindowShadingControl;
8956 10508 : if (state.dataSurface->Surface(IWin).HasShadeControl) {
8957 8815 : if (state.dataSurface->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
8958 0 : state.dataSurface->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
8959 : // switchable windows in partial or fully switched state,
8960 : // get its intermediate VT calculated in DayltgInteriorIllum
8961 0 : int IConstShaded = state.dataSurface->Surface(IWin).activeShadedConstruction;
8962 0 : if (IConstShaded > 0) {
8963 : // Visible transmittance (VT) of electrochromic (EC) windows in fully dark state
8964 0 : Real64 VTDark = General::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
8965 0 : if (VTDark > 0) VTMULT = state.dataSurface->SurfWinVisTransSelected(IWin) / VTDark;
8966 : }
8967 : }
8968 : }
8969 :
8970 993558 : for (int IL = 1; IL <= NREFPT; ++IL) {
8971 : // Determine if illuminance contribution is from bare or shaded window
8972 983050 : daylight_illum(IL) += VTMULT * thisMap.refPts(IL).winLums(loop)[IS - 1];
8973 : }
8974 : } // End of second window loop
8975 :
8976 : // Variables for reporting
8977 161156 : for (int IL = 1; IL <= NREFPT; ++IL) {
8978 159225 : thisMap.refPts(IL).lums[iLum_Illum] = max(daylight_illum(IL), 0.0);
8979 : }
8980 : } // End loop over maps
8981 990056 : } // DayltgInteriorMapIllum()
8982 :
8983 1649 : void ReportIllumMap(EnergyPlusData &state, int const MapNum)
8984 : {
8985 :
8986 : // SUBROUTINE INFORMATION:
8987 : // AUTHOR Peter Ellis
8988 : // DATE WRITTEN May 2003
8989 :
8990 : // PURPOSE OF THIS SUBROUTINE:
8991 : // This subroutine produces the Daylighting Illuminance Map output. Each separate map (by zone)
8992 : // is placed on a temporary file and later (see CloseReportIllumMaps) coallesced into a single
8993 : // output file.
8994 :
8995 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8996 :
8997 1649 : std::string MapNoString;
8998 1649 : auto &dl = state.dataDayltg;
8999 :
9000 1649 : if (dl->ReportIllumMap_firstTime) {
9001 6 : dl->ReportIllumMap_firstTime = false;
9002 6 : dl->FirstTimeMaps.dimension((int)dl->illumMaps.size(), true);
9003 6 : dl->EnvrnPrint.dimension((int)dl->illumMaps.size(), true);
9004 6 : dl->SavedMnDy.allocate((int)dl->illumMaps.size());
9005 : }
9006 :
9007 1649 : auto &illumMap = dl->illumMaps(MapNum);
9008 :
9009 1649 : if (dl->FirstTimeMaps(MapNum)) {
9010 :
9011 9 : dl->FirstTimeMaps(MapNum) = false;
9012 :
9013 9 : auto openMapFile = [&](const fs::path &filePath) -> InputOutputFile & {
9014 9 : auto &outputFile = *illumMap.mapFile;
9015 9 : outputFile.filePath = FileSystem::appendSuffixToPath(filePath, fmt::to_string(MapNum));
9016 9 : outputFile.ensure_open(state, "ReportIllumMap");
9017 9 : return outputFile;
9018 9 : };
9019 9 : if (dl->MapColSep == DataStringGlobals::CharTab) {
9020 0 : if (!openMapFile(state.files.outputMapTabFilePath).good()) return;
9021 : // CommaDelimited = false; //Unused Set but never used
9022 9 : } else if (dl->MapColSep == DataStringGlobals::CharComma) {
9023 9 : if (!openMapFile(state.files.outputMapCsvFilePath).good()) return;
9024 : // CommaDelimited = true; //Unused Set but never used
9025 : } else {
9026 0 : if (!openMapFile(state.files.outputMapTxtFilePath).good()) return;
9027 : // CommaDelimited = false; //Unused Set but never used
9028 : }
9029 :
9030 9 : dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
9031 :
9032 9 : illumMap.Name = format("{} at {:.2R}m", illumMap.Name, illumMap.Z);
9033 : }
9034 1649 : if (dl->SavedMnDy(MapNum) != state.dataEnvrn->CurMnDyHr.substr(0, 5)) {
9035 13 : dl->EnvrnPrint(MapNum) = true;
9036 13 : dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
9037 : }
9038 :
9039 1649 : illumMap.pointsHeader = "";
9040 1649 : int rCount = 0;
9041 3685 : for (auto &thisDayltgCtrl : dl->daylightControl) {
9042 2036 : if (thisDayltgCtrl.zoneIndex != illumMap.zoneIndex) continue;
9043 :
9044 3654 : for (int R = 1; R <= thisDayltgCtrl.TotalDaylRefPoints; ++R) {
9045 2005 : ++rCount;
9046 2005 : auto const &refPt = thisDayltgCtrl.refPts(R);
9047 2005 : illumMap.pointsHeader += format(" RefPt{}=({:.2R}:{:.2R}:{:.2R}),", rCount, refPt.absCoords.x, refPt.absCoords.y, refPt.absCoords.z);
9048 : }
9049 : }
9050 :
9051 1649 : if (rCount > 0) {
9052 : // Remove trailing comma
9053 1649 : illumMap.pointsHeader.pop_back();
9054 : }
9055 1649 : if (dl->EnvrnPrint(MapNum)) {
9056 22 : WriteDaylightMapTitle(
9057 22 : state, MapNum, *illumMap.mapFile, illumMap.Name, state.dataEnvrn->EnvironmentName, illumMap.zoneIndex, illumMap.pointsHeader, illumMap.Z);
9058 22 : dl->EnvrnPrint(MapNum) = false;
9059 : }
9060 :
9061 1649 : if (!state.dataGlobal->WarmupFlag) {
9062 1649 : if (state.dataGlobal->TimeStep == state.dataGlobal->NumOfTimeStepInHour) { // Report only hourly
9063 :
9064 304 : int linelen = 0;
9065 : // Write X scale column header
9066 304 : std::string mapLine = format(" {} {:02}:00", dl->SavedMnDy(MapNum), state.dataGlobal->HourOfDay);
9067 304 : if (illumMap.HeaderXLineLengthNeeded) linelen = int(len(mapLine));
9068 304 : int RefPt = 1;
9069 2944 : for (int X = 1; X <= illumMap.Xnum; ++X) {
9070 : const std::string AddXorYString =
9071 2640 : format("{}({:.2R};{:.2R})=", dl->MapColSep, illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
9072 2640 : if (illumMap.HeaderXLineLengthNeeded) linelen += int(len(AddXorYString));
9073 2640 : mapLine += AddXorYString;
9074 2640 : ++RefPt;
9075 2640 : } // X
9076 :
9077 304 : if (illumMap.HeaderXLineLengthNeeded) {
9078 9 : illumMap.HeaderXLineLength = linelen;
9079 9 : if (static_cast<std::string::size_type>(illumMap.HeaderXLineLength) > len(mapLine)) {
9080 0 : ShowWarningError(state,
9081 0 : format("ReportIllumMap: Map=\"{}\" -- the X Header overflows buffer -- will be truncated at {} characters.",
9082 0 : illumMap.Name,
9083 0 : int(len(mapLine))));
9084 0 : ShowContinueError(state, format("...needed {} characters. Please contact EnergyPlus support.", illumMap.HeaderXLineLength));
9085 : }
9086 9 : illumMap.HeaderXLineLengthNeeded = false;
9087 : }
9088 :
9089 304 : print(*illumMap.mapFile, "{}\n", mapLine);
9090 :
9091 : // Write Y scale prefix and illuminance values
9092 304 : RefPt = 1;
9093 3264 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
9094 2960 : mapLine = format("({:.2R};{:.2R})=", illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
9095 28960 : for (int R = RefPt; R <= RefPt + illumMap.Xnum - 1; ++R) {
9096 26000 : int IllumOut = nint(illumMap.refPts(R).lumsHr[iLum_Illum]);
9097 26000 : std::string String = fmt::to_string(IllumOut);
9098 : ;
9099 26000 : if (!illumMap.refPts(R).inBounds) {
9100 0 : String = "*" + String;
9101 : }
9102 26000 : mapLine += dl->MapColSep + String;
9103 26000 : }
9104 :
9105 2960 : print(*illumMap.mapFile, "{}\n", mapLine);
9106 :
9107 2960 : RefPt += illumMap.Xnum;
9108 : } // X
9109 :
9110 304 : if (state.dataSQLiteProcedures->sqlite) {
9111 16 : if (dl->SQFirstTime) {
9112 1 : int const nX(maxval(dl->illumMaps, &IllumMap::Xnum));
9113 1 : int const nY(maxval(dl->illumMaps, &IllumMap::Ynum));
9114 1 : dl->XValue.allocate(nX);
9115 1 : dl->YValue.allocate(nY);
9116 1 : dl->IllumValue.allocate(nX, nY);
9117 1 : dl->SQFirstTime = false;
9118 : }
9119 :
9120 176 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
9121 160 : dl->YValue(Y) = illumMap.Ymin + (Y - 1) * illumMap.Yinc;
9122 1760 : for (int X = 1; X <= illumMap.Xnum; ++X) {
9123 1600 : dl->XValue(X) = illumMap.Xmin + (X - 1) * illumMap.Xinc;
9124 1600 : int IllumIndex = X + (Y - 1) * illumMap.Xnum;
9125 1600 : dl->IllumValue(X, Y) = nint(illumMap.refPts(IllumIndex).lumsHr[iLum_Illum]);
9126 1600 : if (!illumMap.refPts(IllumIndex).inBounds) {
9127 0 : dl->IllumValue(X, Y) = -dl->IllumValue(X, Y);
9128 : }
9129 : } // X Loop
9130 : } // Y Loop
9131 :
9132 : // We need DataGlobals::CalendarYear, and not DataEnvironment::Year because
9133 : // otherwise if you run a TMY file, you'll get for eg 1977, 1981, etc
9134 32 : state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMap(MapNum,
9135 16 : state.dataGlobal->CalendarYear,
9136 16 : state.dataEnvrn->Month,
9137 16 : state.dataEnvrn->DayOfMonth,
9138 16 : state.dataGlobal->HourOfDay,
9139 : illumMap.Xnum,
9140 16 : dl->XValue,
9141 : illumMap.Ynum,
9142 16 : dl->YValue,
9143 16 : dl->IllumValue);
9144 :
9145 : } // WriteOutputToSQLite
9146 304 : } // end time step
9147 : } // not Warmup
9148 1649 : }
9149 :
9150 796 : void CloseReportIllumMaps(EnergyPlusData &state)
9151 : {
9152 :
9153 : // SUBROUTINE INFORMATION:
9154 : // AUTHOR Linda K. Lawrie
9155 : // DATE WRITTEN June 2003
9156 :
9157 : // PURPOSE OF THIS SUBROUTINE:
9158 : // This subroutine "closes" out the created daylight illuminance maps by merging them
9159 : // into the "eplusout.map" file.
9160 :
9161 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
9162 796 : auto &dl = state.dataDayltg;
9163 :
9164 796 : if ((int)dl->illumMaps.size() > 0) {
9165 : // Write map header
9166 6 : if (dl->MapColSep == DataStringGlobals::CharTab) {
9167 0 : state.files.map.filePath = state.files.outputMapTabFilePath;
9168 6 : } else if (dl->MapColSep == DataStringGlobals::CharComma) {
9169 6 : state.files.map.filePath = state.files.outputMapCsvFilePath;
9170 : } else {
9171 0 : state.files.map.filePath = state.files.outputMapTxtFilePath;
9172 : }
9173 :
9174 6 : state.files.map.ensure_open(state, "CloseReportIllumMaps");
9175 :
9176 15 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
9177 9 : auto &illumMap = dl->illumMaps(MapNum);
9178 9 : if (!illumMap.mapFile->good()) continue; // fatal error processing
9179 :
9180 9 : const std::vector<std::string> mapLines = illumMap.mapFile->getLines();
9181 9 : if (mapLines.empty()) {
9182 0 : ShowSevereError(state, format("CloseReportIllumMaps: IllumMap=\"{}\" is empty.", illumMap.Name));
9183 0 : break;
9184 : }
9185 3295 : for (const std::string &mapLine : mapLines) {
9186 3286 : print(state.files.map, "{}\n", mapLine);
9187 9 : }
9188 9 : illumMap.mapFile->del();
9189 9 : }
9190 :
9191 6 : if (!dl->mapResultsReported && !state.dataErrTracking->AbortProcessing) {
9192 0 : const std::string message = "CloseReportIllumMaps: Illuminance maps requested but no data ever reported. Likely cause is no solar.";
9193 0 : ShowSevereError(state, message);
9194 0 : print(state.files.map, "{}\n", message);
9195 0 : }
9196 : }
9197 796 : }
9198 :
9199 796 : void CloseDFSFile(EnergyPlusData &state)
9200 : {
9201 :
9202 : // SUBROUTINE INFORMATION:
9203 : // AUTHOR Linda Lawrie
9204 : // DATE WRITTEN August 2010
9205 :
9206 : // PURPOSE OF THIS SUBROUTINE:
9207 : // Make sure DFSFile is closed at exit time. Do not rely on operating system to
9208 : // take care of it.
9209 :
9210 796 : state.files.dfs.close();
9211 796 : }
9212 :
9213 64 : void DayltgSetupAdjZoneListsAndPointers(EnergyPlusData &state)
9214 : {
9215 :
9216 : // SUBROUTINE INFORMATION:
9217 : // AUTHOR Fred Winkelmann
9218 : // DATE WRITTEN Feb. 2004
9219 : // MODIFIED: June 2010;LKL - Merged two routines.
9220 :
9221 : // PURPOSE OF THIS SUBROUTINE:
9222 : // For each Daylighting:Detailed enclosure, creates a list of adjacent enclosures,
9223 : // that have one or more exterior windows and that share one or more interior
9224 : // windows with Z. Used in calculation of daylighting through interior windows.
9225 :
9226 : // Sets the daylighting factor pointers for each Daylighting:Detailed control. The pointer
9227 : // may be associated with an exterior window in a daylit target zone's enclosure or an exterior window in
9228 : // an adjacent enclosure, daylit or not, that shares interior windows with the target zone's enclosure.
9229 :
9230 : // Count number of exterior Windows (use to allocate arrays)
9231 64 : auto &dl = state.dataDayltg;
9232 :
9233 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9234 721 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9235 721 : thisEnclDaylight.TotalExtWindows = 0;
9236 :
9237 : // Count exterior windows in this solar enclosure
9238 8165 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9239 7444 : auto const &surf = state.dataSurface->Surface(surfNum);
9240 7444 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9241 6266 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9242 1178 : ++thisEnclDaylight.TotalExtWindows;
9243 : }
9244 : }
9245 : } // End of primary enclosure loop
9246 :
9247 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9248 721 : int NumList = 0;
9249 721 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9250 289 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9251 289 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9252 : // This is a Daylighting:Detailed enclosure
9253 : // Find adjacent zones/enclosures
9254 7241 : for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
9255 6955 : if (adjEnclNum == enclNum) continue;
9256 : // Require that adjEnclNum have a least one exterior window
9257 6669 : bool AdjEnclHasExtWins = false;
9258 64772 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9259 68254 : if ((state.dataSurface->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
9260 5076 : (state.dataSurface->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
9261 5075 : AdjEnclHasExtWins = true;
9262 5075 : break;
9263 : }
9264 : }
9265 6669 : if (!AdjEnclHasExtWins) continue;
9266 : // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to ZoneNum
9267 64960 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9268 59886 : auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
9269 59886 : if ((surfAdj.Class == SurfaceClass::Window) && (surfAdj.ExtBoundCond >= 1)) {
9270 : // This is an interior window in ZoneNumAdj
9271 1 : if (state.dataSurface->Surface(surfAdj.ExtBoundCond).SolarEnclIndex == enclNum) {
9272 : // This interior window is adjacent to ZoneNum
9273 1 : ++NumList;
9274 1 : break;
9275 : }
9276 : }
9277 : }
9278 : }
9279 286 : thisEnclDaylight.AdjIntWinEnclNums.allocate(NumList);
9280 286 : thisEnclDaylight.AdjIntWinEnclNums = 0;
9281 : } // End of primary enclosure loop
9282 :
9283 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9284 721 : int NumList = 0;
9285 721 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9286 289 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9287 289 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9288 : // This is a Daylighting:Detailed enclosure
9289 : // Find adjacent zones/enclosures
9290 7241 : for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
9291 6955 : if (adjEnclNum == enclNum) continue;
9292 : // Require that adjEnclNum have a least one exterior window
9293 6669 : bool AdjEnclHasExtWins = false;
9294 64772 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9295 63178 : auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
9296 63178 : if (surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) {
9297 5075 : AdjEnclHasExtWins = true;
9298 5075 : break;
9299 : }
9300 : }
9301 6669 : if (!AdjEnclHasExtWins) continue;
9302 : // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to enclNum
9303 64960 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9304 59886 : auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
9305 59886 : if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond < 1) continue;
9306 :
9307 : // This is an interior window in adjEnclNum
9308 1 : if (state.dataSurface->Surface(surfAdj.ExtBoundCond).SolarEnclIndex != enclNum) continue;
9309 :
9310 : // This interior window is adjacent to ZoneNum
9311 1 : ++NumList;
9312 1 : int enclNumAdj = surfAdj.SolarEnclIndex;
9313 1 : thisEnclDaylight.AdjIntWinEnclNums(NumList) = enclNumAdj;
9314 1 : dl->enclDaylight(enclNumAdj).adjEnclHasDayltgCtrl = true;
9315 1 : break;
9316 : }
9317 : }
9318 286 : thisEnclDaylight.NumOfIntWinAdjEncls = NumList;
9319 : } // End of primary enclosure loop
9320 :
9321 : // now fill out information on relationship between adjacent exterior windows and associated interior windows
9322 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9323 721 : auto &enclDayl = dl->enclDaylight(enclNum);
9324 : // first find count of exterior windows
9325 721 : if (enclDayl.NumOfIntWinAdjEncls <= 0) {
9326 720 : enclDayl.NumOfIntWinAdjEnclExtWins = 0;
9327 720 : continue;
9328 : }
9329 2 : for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
9330 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9331 10 : if ((state.dataSurface->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
9332 2 : (state.dataSurface->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
9333 1 : ++enclDayl.NumOfIntWinAdjEnclExtWins;
9334 : }
9335 : }
9336 : }
9337 : // now allocate nested struct based on exterior window count
9338 1 : enclDayl.IntWinAdjEnclExtWin.allocate(enclDayl.NumOfIntWinAdjEnclExtWins);
9339 :
9340 : // now fill nested structure
9341 1 : int ExtWinIndex = 0;
9342 2 : for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
9343 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9344 8 : auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
9345 8 : if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond != ExternalEnvironment) continue;
9346 :
9347 1 : ++ExtWinIndex;
9348 1 : auto &intWinAdjEnclExtWin = enclDayl.IntWinAdjEnclExtWin(ExtWinIndex);
9349 1 : intWinAdjEnclExtWin.SurfNum = SurfNumAdj;
9350 :
9351 : // now count interior windows shared by both zones
9352 1 : int NumOfIntWindowsCount = 0;
9353 9 : for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9354 8 : auto const &surfAdj2 = state.dataSurface->Surface(SurfNumAdj2);
9355 8 : if ((surfAdj2.Class == SurfaceClass::Window) && (surfAdj2.ExtBoundCond >= 1)) {
9356 : // This is an interior window in ZoneNumAdj
9357 1 : if (state.dataSurface->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
9358 : // This interior window is adjacent to ZoneNum and associated with this
9359 1 : ++NumOfIntWindowsCount;
9360 : }
9361 : }
9362 : } // for (SurfNumAdj2)
9363 :
9364 : // allocate nested array
9365 1 : intWinAdjEnclExtWin.IntWinNum.allocate(NumOfIntWindowsCount);
9366 1 : intWinAdjEnclExtWin.IntWinNum = 0;
9367 1 : int IntWinIndex = 0;
9368 9 : for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9369 8 : auto const &surfAdj2 = state.dataSurface->Surface(SurfNumAdj2);
9370 8 : if (surfAdj2.Class != SurfaceClass::Window || surfAdj2.ExtBoundCond < 1) continue;
9371 :
9372 : // This is an interior window in ZoneNumAdj
9373 1 : if (state.dataSurface->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
9374 : // This interior window is adjacent to ZoneNum and associated with this
9375 1 : intWinAdjEnclExtWin.IntWinNum(++IntWinIndex) = SurfNumAdj2;
9376 : }
9377 : } // for (SurfNumAdj2)
9378 : } // for (SurfNumAdj)
9379 : } // for (adjEnclNum)
9380 : } // End of primary enclosure loop
9381 :
9382 64 : Array1D_int enclExtWin;
9383 64 : enclExtWin.dimension(state.dataViewFactor->NumOfSolarEnclosures, 0);
9384 :
9385 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9386 721 : enclExtWin(enclNum) = 0;
9387 721 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9388 289 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9389 289 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9390 : // This is a Daylighting:Detailed zone
9391 :
9392 : // Get exterior windows in this solar enclosure
9393 3441 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9394 3155 : auto const &surf = state.dataSurface->Surface(surfNum);
9395 3155 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9396 2311 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9397 844 : ++enclExtWin(enclNum);
9398 : }
9399 : }
9400 :
9401 : // Get exterior windows in adjacent enclosures that share interior windows with enclNum
9402 286 : if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
9403 :
9404 2 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9405 : // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
9406 : // it would not be an "AdjIntWinEncl"
9407 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9408 8 : auto const &surfAdj = state.dataSurface->Surface(SurfNumAdj);
9409 8 : if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
9410 7 : surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
9411 1 : ++enclExtWin(enclNum);
9412 : }
9413 : }
9414 : } // for (adjEnclNum)
9415 : } // for (enclNum)
9416 :
9417 64 : dl->maxShadeDeployOrderExtWins = 0;
9418 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9419 721 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9420 721 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9421 286 : thisEnclDaylight.NumOfDayltgExtWins = 0;
9422 286 : int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
9423 286 : if (thisEnclNumRefPoints > 0) {
9424 : // This is a Daylighting:Detailed enclosure
9425 :
9426 : // Get exterior windows in this enclosure
9427 286 : if (enclExtWin(enclNum) == 0) continue;
9428 286 : thisEnclDaylight.DayltgExtWinSurfNums.allocate(enclExtWin(enclNum));
9429 286 : thisEnclDaylight.DayltgExtWinSurfNums = 0;
9430 573 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9431 287 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9432 287 : thisDayltgCtrl.MapShdOrdToLoopNum.allocate(enclExtWin(enclNum));
9433 287 : thisDayltgCtrl.MapShdOrdToLoopNum = 0;
9434 :
9435 287 : assert((int)thisDayltgCtrl.refPts.size() == thisDayltgCtrl.TotalDaylRefPoints);
9436 750 : for (auto &refPt : thisDayltgCtrl.refPts) {
9437 463 : refPt.extWins.allocate(enclExtWin(enclNum));
9438 1952 : for (auto &extWin : refPt.extWins) {
9439 1489 : new (&extWin) DaylRefPtExtWin();
9440 : }
9441 : }
9442 286 : }
9443 :
9444 286 : int enclExtWinCtr = 0;
9445 :
9446 3441 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9447 3155 : auto const &surf = state.dataSurface->Surface(surfNum);
9448 3155 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9449 2311 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9450 844 : ++enclExtWinCtr;
9451 844 : thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = surfNum;
9452 : }
9453 : }
9454 :
9455 : // Get exterior windows in adjacent enclosures that share interior windows with enclNum
9456 286 : if (thisEnclDaylight.NumOfIntWinAdjEncls > 0) {
9457 2 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9458 : // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
9459 : // it would not be an "AdjIntWinEncl"
9460 9 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9461 8 : auto &surfAdj = state.dataSurface->Surface(SurfNumAdj);
9462 8 : if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
9463 7 : surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
9464 1 : ++enclExtWinCtr;
9465 1 : thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = SurfNumAdj;
9466 :
9467 1 : auto &surfWinAdj = state.dataSurface->SurfaceWindow(SurfNumAdj);
9468 : // If no daylighting in the adjacent enclosure, set up variables anyway:
9469 2 : if (state.dataViewFactor->EnclSolInfo(adjEnclNum).TotalEnclosureDaylRefPoints == 0 &&
9470 1 : !state.dataSurface->SurfWinSurfDayLightInit(SurfNumAdj)) {
9471 1 : surfWinAdj.refPts.allocate(thisEnclNumRefPoints);
9472 2 : for (auto &refPt : surfWinAdj.refPts) {
9473 1 : new (&refPt) SurfaceWindowRefPt();
9474 : }
9475 1 : state.dataSurface->SurfWinSurfDayLightInit(SurfNumAdj) = true;
9476 : }
9477 : }
9478 : } // for (SurfNumAdj)
9479 : } // for (adjEnclNum)
9480 : } // if (thisEnclDaylight.NumOfIntWinAdjEncls > 0)
9481 :
9482 286 : thisEnclDaylight.NumOfDayltgExtWins = enclExtWin(enclNum);
9483 286 : int winSize = enclExtWin(enclNum);
9484 286 : int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
9485 573 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9486 287 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9487 287 : int refSize = thisDayltgCtrl.TotalDaylRefPoints;
9488 7175 : for (int iHr = 1; iHr <= (int)Constant::HoursInDay; ++iHr) {
9489 6888 : thisDayltgCtrl.daylFac[iHr].allocate(winSize, refSize, numSlatAngs);
9490 : }
9491 286 : }
9492 : } // if (thisEncl.NumOfRefPoints > 0)
9493 :
9494 286 : if (state.dataSurface->TotWinShadingControl > 0) {
9495 22 : CreateShadeDeploymentOrder(state, enclNum);
9496 : }
9497 : } // for (enclNum)
9498 :
9499 64 : int numSlatAngs = state.dataSurface->actualMaxSlatAngs + 1;
9500 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9501 721 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9502 721 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9503 286 : int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
9504 286 : if (thisEnclNumRefPoints > 0) {
9505 286 : if (state.dataSurface->TotWinShadingControl > 0) {
9506 22 : MapShadeDeploymentOrderToLoopNumber(state, enclNum);
9507 : }
9508 : }
9509 : }
9510 :
9511 73 : for (auto &illumMap : dl->illumMaps) {
9512 9 : assert((int)illumMap.refPts.size() == illumMap.TotalMapRefPoints);
9513 9 : if (illumMap.TotalMapRefPoints == 0) continue;
9514 :
9515 9 : int numExtWin = enclExtWin(illumMap.enclIndex);
9516 9 : if (numExtWin == 0) continue;
9517 :
9518 634 : for (auto &refPt : illumMap.refPts) {
9519 625 : refPt.winLums.allocate(numExtWin);
9520 2475 : for (auto &winLums : refPt.winLums) {
9521 1850 : winLums = {0.0, 0.0};
9522 : }
9523 : }
9524 :
9525 225 : for (int iHr = 1; iHr <= (int)Constant::HoursInDay; ++iHr) {
9526 216 : illumMap.daylFac[iHr].allocate(numExtWin, illumMap.TotalMapRefPoints, numSlatAngs);
9527 : }
9528 :
9529 : } // End of map loop
9530 :
9531 256 : dl->dirIllum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
9532 256 : dl->reflIllum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
9533 256 : dl->winLum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
9534 256 : dl->avgWinLum.dimension(Constant::HoursInDay, numSlatAngs, Illums());
9535 :
9536 : static constexpr std::string_view Format_700("! <Enclosure/Window Adjacency Daylighting Counts>, Enclosure Name, Number of Exterior Windows, "
9537 : "Number of Exterior Windows in Adjacent Enclosures\n");
9538 64 : print(state.files.eio, Format_700);
9539 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9540 721 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9541 721 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9542 286 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9543 : static constexpr std::string_view Format_701("Enclosure/Window Adjacency Daylighting Counts, {},{},{}\n");
9544 286 : print(state.files.eio,
9545 : Format_701,
9546 286 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
9547 286 : thisEnclDaylight.TotalExtWindows,
9548 572 : (thisEnclDaylight.NumOfDayltgExtWins - thisEnclDaylight.TotalExtWindows));
9549 : }
9550 : static constexpr std::string_view Format_702(
9551 : "! <Enclosure/Window Adjacency Daylighting Matrix>, Enclosure Name, Number of Adjacent Enclosures with Windows,Adjacent "
9552 : "Enclosure Names - 1st 100 (max)\n");
9553 64 : print(state.files.eio, Format_702);
9554 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9555 721 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9556 721 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9557 286 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9558 : static constexpr std::string_view Format_703("Enclosure/Window Adjacency Daylighting Matrix, {},{}");
9559 286 : print(state.files.eio, Format_703, state.dataViewFactor->EnclSolInfo(enclNum).Name, thisEnclDaylight.NumOfIntWinAdjEncls);
9560 287 : for (int loop = 1, loop_end = min(thisEnclDaylight.NumOfIntWinAdjEncls, 100); loop <= loop_end; ++loop) {
9561 1 : print(state.files.eio, ",{}", state.dataViewFactor->EnclSolInfo(thisEnclDaylight.AdjIntWinEnclNums(loop)).Name);
9562 : }
9563 286 : print(state.files.eio, "\n");
9564 : }
9565 :
9566 64 : enclExtWin.deallocate();
9567 64 : }
9568 :
9569 22 : void CreateShadeDeploymentOrder(EnergyPlusData &state, int const enclNum)
9570 : {
9571 : // J. Glazer - 2018
9572 : // create sorted list for shade deployment order
9573 : // first step is to create a sortable list of WindowShadingControl objects by sequence
9574 22 : auto &dl = state.dataDayltg;
9575 :
9576 22 : std::vector<std::pair<int, int>> shadeControlSequence; // sequence, WindowShadingControl
9577 55 : for (int iShadeCtrl = 1; iShadeCtrl <= state.dataSurface->TotWinShadingControl; ++iShadeCtrl) {
9578 33 : auto &winShadeControl = state.dataSurface->WindowShadingControl(iShadeCtrl);
9579 41 : for (int spaceNum : state.dataHeatBal->Zone(winShadeControl.ZoneIndex).spaceIndexes) {
9580 33 : int shadeCtrlEnclNum = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
9581 33 : if (shadeCtrlEnclNum == enclNum) {
9582 25 : shadeControlSequence.push_back(std::make_pair(winShadeControl.SequenceNumber, iShadeCtrl));
9583 25 : break;
9584 : }
9585 33 : }
9586 : }
9587 : // sort the WindowShadingControl objects based on sequence number
9588 22 : sort(shadeControlSequence.begin(), shadeControlSequence.end());
9589 : // now make the deployment list of lists.
9590 : // each sublist is a group of surfaces that should be deployed together
9591 : // often the sublist is just a single item.
9592 22 : dl->maxShadeDeployOrderExtWins = 0;
9593 44 : for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
9594 22 : auto &thisDaylightCtrl = dl->daylightControl(controlNum);
9595 47 : for (auto sequence : shadeControlSequence) { // This is an iterator (THIS_AUTO_OK)
9596 25 : int curShadeControlNum = sequence.second;
9597 25 : auto const &winShadeControl = state.dataSurface->WindowShadingControl(curShadeControlNum);
9598 25 : if (winShadeControl.multiSurfaceControl == MultiSurfaceControl::Group) {
9599 : // add a group of surfaces since they should be deployed as a group
9600 4 : std::vector<int> group;
9601 13 : for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
9602 9 : group.push_back(winShadeControl.FenestrationIndex(i));
9603 : }
9604 4 : thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(group);
9605 4 : } else {
9606 : // add each individual surface as a separate list so they are deployed individually
9607 46 : for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
9608 25 : std::vector<int> singleMemberVector;
9609 25 : singleMemberVector.push_back(winShadeControl.FenestrationIndex(i));
9610 25 : thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(singleMemberVector);
9611 25 : }
9612 : }
9613 22 : }
9614 22 : dl->maxShadeDeployOrderExtWins = max(dl->maxShadeDeployOrderExtWins, (int)thisDaylightCtrl.ShadeDeployOrderExtWins.size());
9615 22 : }
9616 :
9617 22 : dl->maxDayltgExtWins = 0;
9618 117 : for (auto const &enclDayl : dl->enclDaylight) {
9619 95 : dl->maxDayltgExtWins = max(dl->maxDayltgExtWins, enclDayl.NumOfDayltgExtWins);
9620 : }
9621 :
9622 22 : } // CreateShadeDeploymentOrder()
9623 :
9624 22 : void MapShadeDeploymentOrderToLoopNumber(EnergyPlusData &state, int const enclNum)
9625 : {
9626 : // J. Glazer - 2018
9627 : // Allow a way to map back to the original "loop" index that is used in many other places in the
9628 : // ZoneDayLight data structure when traversing the list in the order of the window shaded deployment
9629 22 : auto &dl = state.dataDayltg;
9630 :
9631 22 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9632 22 : auto const &thisEnclSol = state.dataViewFactor->EnclSolInfo(enclNum);
9633 :
9634 22 : if (thisEnclSol.TotalEnclosureDaylRefPoints == 0 || thisEnclDaylight.NumOfDayltgExtWins == 0) return;
9635 :
9636 44 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9637 22 : auto &thisDaylightCtrl = dl->daylightControl(controlNum);
9638 22 : if (thisDaylightCtrl.ShadeDeployOrderExtWins.size() == 0) continue;
9639 :
9640 21 : int count = 0;
9641 21 : bool showOnce = true;
9642 50 : for (auto const &listOfExtWin : thisDaylightCtrl.ShadeDeployOrderExtWins) {
9643 63 : for (int IWinShdOrd : listOfExtWin) {
9644 34 : ++count;
9645 34 : if (count > thisEnclDaylight.NumOfDayltgExtWins) {
9646 0 : if (showOnce) {
9647 0 : ShowWarningError(
9648 : state,
9649 0 : format("MapShadeDeploymentOrderToLoopNumber: too many controlled shaded windows in enclosure {}", thisEnclSol.Name));
9650 0 : ShowContinueError(state,
9651 : "Check the Zone Name in the WindowShadingControl that references the following fenestration surfaces:");
9652 0 : showOnce = false;
9653 : }
9654 0 : ShowContinueError(state, format(" - {}", state.dataSurface->Surface(IWinShdOrd).Name));
9655 : }
9656 86 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
9657 86 : int IWinLoop = thisEnclDaylight.DayltgExtWinSurfNums(loop);
9658 86 : if (IWinShdOrd == IWinLoop) {
9659 34 : thisDaylightCtrl.MapShdOrdToLoopNum(count) = loop;
9660 34 : break;
9661 : }
9662 : }
9663 29 : }
9664 21 : } // for (listOfExtWin)
9665 22 : } // for (controlNum)
9666 : } // MapShadeDeploymentOrderToLoopNumber()
9667 :
9668 1015 : void DayltgInterReflIllFrIntWins(EnergyPlusData &state, int const enclNum)
9669 : {
9670 :
9671 : // SUBROUTINE INFORMATION:
9672 : // AUTHOR Fred Winkelmann
9673 : // DATE WRITTEN Mar. 2004
9674 :
9675 : // PURPOSE OF THIS SUBROUTINE:
9676 : // Calculates the inter-reflected illuminance in a daylit zone from beam
9677 : // and diffuse daylight entering the zone through interior windows. This illuminance
9678 : // is determined by the split-flux method and is assumed to be uniform, i.e., the same
9679 : // at all reference points.
9680 :
9681 1015 : auto &dl = state.dataDayltg;
9682 :
9683 1015 : auto &enclDayl = dl->enclDaylight(enclNum);
9684 1015 : auto &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
9685 :
9686 1015 : enclDayl.InterReflIllFrIntWins = 0.0;
9687 :
9688 8120 : for (int const IWin : enclSol.SurfacePtr) {
9689 7105 : auto &surf = state.dataSurface->Surface(IWin);
9690 7105 : if (surf.Class != SurfaceClass::Window || surf.ExtBoundCond < 1) continue;
9691 1015 : auto const &surfWin = state.dataSurface->SurfaceWindow(IWin);
9692 : // This is an interior window in ZoneNum
9693 1015 : int const ConstrNum = surf.Construction;
9694 1015 : int const adjEnclNum = state.dataSurface->Surface(surf.ExtBoundCond).SolarEnclIndex;
9695 : // Luminous flux transmitted through an int win from adjacent zone's enclosure (lumens)
9696 1015 : Real64 QDifTrans = state.dataHeatBal->EnclSolQSDifSol(adjEnclNum) * state.dataConstruction->Construct(ConstrNum).TransDiffVis * surf.Area *
9697 1015 : state.dataEnvrn->PDIFLW;
9698 1015 : Real64 QDifTransUp = QDifTrans * surfWin.fractionUpgoing; // Upgoing part of QDifTrans (lumens)
9699 1015 : Real64 QDifTransDn = QDifTrans * (1.0 - surfWin.fractionUpgoing); // Downgoing part of QDifTrans (lumens)
9700 1015 : if (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect) != 0.0) {
9701 1015 : enclDayl.InterReflIllFrIntWins += (QDifTransDn * surfWin.rhoFloorWall + QDifTransUp * surfWin.rhoCeilingWall) /
9702 1015 : (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
9703 : }
9704 : } // for (iWin)
9705 :
9706 : // Add inter-reflected illuminance from beam solar entering enclosure through interior windows
9707 : // TH, CR 7873, 9/17/2009
9708 1015 : if (dl->enclDaylight(enclNum).totInsSurfArea > 0) {
9709 1015 : enclDayl.InterReflIllFrIntWins +=
9710 1015 : (state.dataHeatBal->EnclSolDBIntWin(enclNum) * state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW * enclDayl.floorVisRefl) /
9711 1015 : (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
9712 : }
9713 1015 : } // DayltgInterReflIllFrIntWins()
9714 :
9715 64 : void CalcMinIntWinSolidAngs(EnergyPlusData &state)
9716 : {
9717 :
9718 : // SUBROUTINE INFORMATION:
9719 : // AUTHOR Fred Winkelmann
9720 : // DATE WRITTEN Feb. 2004
9721 :
9722 : // PURPOSE OF THIS SUBROUTINE:
9723 : // For each Daylighting:Detailed zone finds the minimum solid angle subtended
9724 : // by interior windows through which daylight can pass from adjacent zones with
9725 : // exterior windows.
9726 :
9727 64 : auto &dl = state.dataDayltg;
9728 :
9729 785 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9730 721 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9731 721 : thisEnclDaylight.MinIntWinSolidAng = 2.0 * Constant::Pi;
9732 721 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9733 289 : if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
9734 :
9735 8 : for (int IWin : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9736 7 : auto const &surf = state.dataSurface->Surface(IWin);
9737 :
9738 7 : if ((surf.Class != SurfaceClass::Window) || (surf.ExtBoundCond < 1)) continue;
9739 :
9740 : // This is an interior window in enclNum
9741 1 : int const winAdjEnclNum = state.dataSurface->Surface(surf.ExtBoundCond).SolarEnclIndex;
9742 1 : bool IntWinNextToIntWinAdjZone = false; // True if an interior window is next to a zone with one or more exterior windows
9743 1 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9744 1 : if (winAdjEnclNum == adjEnclNum) {
9745 1 : IntWinNextToIntWinAdjZone = true;
9746 1 : break;
9747 : }
9748 : }
9749 :
9750 1 : if (!IntWinNextToIntWinAdjZone) continue;
9751 :
9752 2 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9753 1 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9754 2 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
9755 : // Reference point in absolute coordinate system
9756 1 : Vector3<Real64> RREF = thisDayltgCtrl.refPts(IL).absCoords;
9757 1 : bool is_Triangle = (surf.Sides == 3);
9758 1 : bool is_Rectangle = (surf.Sides == 4);
9759 :
9760 1 : Vector3<Real64> W1, W2, W3;
9761 1 : if (is_Rectangle) {
9762 : // Vertices of window numbered counter-clockwise starting at upper left as viewed
9763 : // from inside of room. Assumes original vertices are numbered counter-clockwise from
9764 : // upper left as viewed from outside.
9765 1 : W3 = surf.Vertex(2);
9766 1 : W2 = surf.Vertex(3);
9767 1 : W1 = surf.Vertex(4);
9768 0 : } else if (is_Triangle) {
9769 0 : W3 = surf.Vertex(2);
9770 0 : W2 = surf.Vertex(3);
9771 0 : W1 = surf.Vertex(1);
9772 : }
9773 : // Unit vectors from window vertex 2 to 1 and 2 to 3, center point of window,
9774 : // and vector from ref pt to center of window
9775 1 : Vector3<Real64> W21 = W1 - W2;
9776 1 : Vector3<Real64> W23 = W3 - W2;
9777 1 : Real64 HW = W21.magnitude();
9778 1 : Real64 WW = W23.magnitude();
9779 : Vector3<Real64> WC =
9780 3 : (is_Rectangle) ? (W2 + (W23 + W21) / 2.0) : (is_Triangle ? (W2 + (W23 + W21) / 3.0) : (W2 + (W23 + W21) / 3.0));
9781 :
9782 : // Vector from ref point to center of window
9783 1 : Vector3<Real64> REFWC = WC - RREF;
9784 1 : W21 /= HW;
9785 1 : W23 /= WW;
9786 : // Unit vector normal to window (pointing away from room)
9787 1 : Vector3<Real64> WNORM = surf.OutNormVec;
9788 : // Distance from ref point to center of window
9789 1 : Real64 DIS = REFWC.magnitude();
9790 : // Unit vector from ref point to center of window
9791 1 : Vector3<Real64> Ray = REFWC / DIS;
9792 : // Cosine of angle between ray from ref pt to center of window and window outward normal
9793 1 : Real64 COSB = dot(WNORM, Ray);
9794 1 : if (COSB > 0.01765) { // 0 <= B < 89 deg
9795 : // Above test avoids case where ref point cannot receive daylight directly from the
9796 : // interior window
9797 1 : Real64 IntWinSolidAng = COSB * surf.Area / (pow_2(DIS) + 0.001);
9798 1 : thisEnclDaylight.MinIntWinSolidAng = min(thisEnclDaylight.MinIntWinSolidAng, IntWinSolidAng);
9799 : }
9800 1 : } // for (IL)
9801 1 : } // for (controlNum)
9802 : } // for (IWin)
9803 : } // for (enclNum)
9804 64 : }
9805 :
9806 128 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
9807 : {
9808 :
9809 : // SUBROUTINE INFORMATION:
9810 : // AUTHOR Linda Lawrie
9811 : // DATE WRITTEN February 2009
9812 :
9813 : // PURPOSE OF THIS SUBROUTINE:
9814 : // check for geometrytransform in the daylighting access for reference and map points
9815 :
9816 : // METHODOLOGY EMPLOYED:
9817 : // once reference points have been converted to WCS,
9818 : // change them to reflect a different aspect
9819 : // ratio for the entire building based on user input.
9820 :
9821 : // SUBROUTINE PARAMETER DEFINITIONS:
9822 : static constexpr std::string_view CurrentModuleObject = "GeometryTransform";
9823 :
9824 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
9825 128 : Array1D_string cAlphas(1);
9826 128 : Array1D<Real64> rNumerics;
9827 :
9828 : // begin execution
9829 : // get user input...
9830 128 : doTransform = false;
9831 128 : OldAspectRatio = 1.0;
9832 128 : NewAspectRatio = 1.0;
9833 :
9834 128 : auto &ip = state.dataInputProcessing->inputProcessor;
9835 128 : auto const &ipsc = state.dataIPShortCut;
9836 128 : if (ip->getNumObjectsFound(state, CurrentModuleObject) == 1) {
9837 : int NAlphas;
9838 : int NNum;
9839 : int IOStat;
9840 0 : ip->getObjectItem(state,
9841 : CurrentModuleObject,
9842 : 1,
9843 : cAlphas,
9844 : NAlphas,
9845 : rNumerics,
9846 : NNum,
9847 : IOStat,
9848 0 : ipsc->lNumericFieldBlanks,
9849 0 : ipsc->lAlphaFieldBlanks,
9850 0 : ipsc->cAlphaFieldNames,
9851 0 : ipsc->cNumericFieldNames);
9852 0 : OldAspectRatio = rNumerics(1);
9853 0 : NewAspectRatio = rNumerics(2);
9854 0 : std::string transformPlane = cAlphas(1);
9855 0 : if (transformPlane != "XY") {
9856 0 : ShowWarningError(state, format("{}: invalid {}=\"{}...ignored.", CurrentModuleObject, ipsc->cAlphaFieldNames(1), cAlphas(1)));
9857 : }
9858 0 : doTransform = true;
9859 0 : state.dataSurface->AspectTransform = true;
9860 0 : }
9861 128 : if (state.dataSurface->WorldCoordSystem) {
9862 56 : doTransform = false;
9863 56 : state.dataSurface->AspectTransform = false;
9864 : }
9865 128 : }
9866 :
9867 22 : void WriteDaylightMapTitle(EnergyPlusData &state,
9868 : int const mapNum,
9869 : InputOutputFile &mapFile,
9870 : std::string const &mapName,
9871 : std::string const &environmentName,
9872 : int const ZoneNum,
9873 : std::string const &refPts,
9874 : Real64 const zcoord)
9875 : {
9876 : // SUBROUTINE INFORMATION:
9877 : // AUTHOR Greg Stark
9878 : // DATE WRITTEN Sept 2008
9879 :
9880 : // PURPOSE OF THIS SUBROUTINE:
9881 : // The purpose of the routine is to allow the daylighting map data to be written in various formats
9882 :
9883 : // must add correct number of commas at end
9884 22 : auto &dl = state.dataDayltg;
9885 :
9886 22 : std::string fullmapName = fmt::format("{}:{}:{} Illuminance [lux] (Hourly)", state.dataHeatBal->Zone(ZoneNum).Name, environmentName, mapName);
9887 22 : print(mapFile, "Date/Time{}{}{}{}{}{}\n", dl->MapColSep, fullmapName, dl->MapColSep, refPts, dl->MapColSep, dl->MapColSep);
9888 :
9889 22 : if (state.dataSQLiteProcedures->sqlite) {
9890 1 : state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMapTitle(mapNum, fullmapName, environmentName, ZoneNum, refPts, zcoord);
9891 : }
9892 22 : } // WritDaylightMapTitle()
9893 :
9894 : } // namespace EnergyPlus::Dayltg
|