Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <algorithm>
50 : #include <cassert>
51 : #include <cmath>
52 : #include <string>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Array.functions.hh>
56 : #include <ObjexxFCL/Fmath.hh>
57 : #include <ObjexxFCL/Vector3.hh>
58 : // #include <ObjexxFCL/Vector4.hh>
59 : #include <ObjexxFCL/member.functions.hh>
60 : #include <ObjexxFCL/random.hh>
61 : #include <ObjexxFCL/string.functions.hh>
62 :
63 : // EnergyPlus Headers
64 : #include <EnergyPlus/DElightManagerF.hh>
65 : #include <EnergyPlus/Data/EnergyPlusData.hh>
66 : #include <EnergyPlus/DataBSDFWindow.hh>
67 : #include <EnergyPlus/DataDElight.hh>
68 : #include <EnergyPlus/DataDaylighting.hh>
69 : #include <EnergyPlus/DataDaylightingDevices.hh>
70 : #include <EnergyPlus/DataEnvironment.hh>
71 : #include <EnergyPlus/DataErrorTracking.hh>
72 : #include <EnergyPlus/DataHeatBalance.hh>
73 : #include <EnergyPlus/DataIPShortCuts.hh>
74 : #include <EnergyPlus/DataPrecisionGlobals.hh>
75 : #include <EnergyPlus/DataStringGlobals.hh>
76 : #include <EnergyPlus/DataSurfaces.hh>
77 : #include <EnergyPlus/DataSystemVariables.hh>
78 : #include <EnergyPlus/DataViewFactorInformation.hh>
79 : #include <EnergyPlus/DaylightingDevices.hh>
80 : #include <EnergyPlus/DaylightingManager.hh>
81 : #include <EnergyPlus/DisplayRoutines.hh>
82 : #include <EnergyPlus/FileSystem.hh>
83 : #include <EnergyPlus/General.hh>
84 : #include <EnergyPlus/HeatBalanceManager.hh>
85 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
86 : #include <EnergyPlus/InternalHeatGains.hh>
87 : #include <EnergyPlus/OutputProcessor.hh>
88 : #include <EnergyPlus/OutputReportPredefined.hh>
89 : #include <EnergyPlus/PierceSurface.hh>
90 : #include <EnergyPlus/SQLiteProcedures.hh>
91 : #include <EnergyPlus/ScheduleManager.hh>
92 : #include <EnergyPlus/SolarReflectionManager.hh>
93 : #include <EnergyPlus/SolarShading.hh>
94 : #include <EnergyPlus/SurfaceOctree.hh>
95 : #include <EnergyPlus/UtilityRoutines.hh>
96 : #include <EnergyPlus/WindowComplexManager.hh>
97 : #include <EnergyPlus/WindowManager.hh>
98 :
99 : namespace EnergyPlus::Dayltg {
100 :
101 : // MODULE INFORMATION
102 : // AUTHOR Fred Winkelmann
103 : // DATE WRITTEN July 1997, December 1998
104 : // MODIFIED Oct 2004; LKL -- Efficiencies and code restructure
105 : // Aug 2012: BG -- Added availability schedule
106 :
107 : // PURPOSE OF THIS MODULE:
108 : // Manages the daylighting calculations for each thermal zone that has an associated
109 : // Daylighting:Controls object.
110 :
111 : // Includes calculation of interior daylight illuminance and glare
112 : // from each of the windows in a zone, control of window shading devices
113 : // to reduce glare, and control of overhead electric lighting in response
114 : // to interior daylight illuminance level at one or two user-specified
115 : // reference points at which sensors are located.
116 :
117 : // METHODOLOGY EMPLOYED:
118 : // REFERENCES:
119 : // "Daylighting Calculation in DOE-2," F.C.Winkelmann, LBL-11353, May 1983
120 : // "Daylighting Simulation in the DOE-2 Building Energy Analysis Program,"
121 : // F.C. Winkelmann and S. Selkowitz, Energy and Buildings 8(1985)271-286
122 :
123 : // OTHER NOTES:
124 : // This module was created from DOE-2.1E subroutines.
125 :
126 : // Correspondence between DOE-2.1E and EnergyPlus subroutine names:
127 :
128 : // DOE-2.1E EnergyPlus In Module Called from Module
129 : // DAVREF DayltgAveInteriorReflectance DaylightingManager DaylightingManager
130 : // DCOF CalcDayltgCoefficients DaylightingManager DaylightingManager
131 : // DCROSS DayltgCrossProduct DaylightingManager DaylightingManager
132 : // DEXTIL DayltgCurrentExtHorizIllum WeatherManager WeatherManager
133 : // DGLARE DayltgGlare DaylightingManager DaylightingManager
134 : // DHILL DayltgExtHorizIllum DaylightingManager DaylightingManager
135 : // DHITSH DayltgHitObstruction DaylightingManager DaylightingManager
136 : // DINTIL DayltgInteriorIllum DaylightingManager HeatBalanceSurfaceManager
137 : // DLTSYS DayltgElecLightingControl DaylightingManager HeatBalanceSurfaceManager
138 : // DNSOL not used
139 : // DPFAC DayltgPositionFactor DaylightingManager DaylightingManager
140 : // DPIERC PierceSurface PierceSurface DaylightingManager
141 : // DREFLT DayltgInterReflectedIllum DaylightingManager DaylightingManager
142 : // DSKYLU DayltgSkyLuminance DaylightingManager DaylightingManager
143 : // DTHLIM DayltgAzimuthLimits DaylightingManager DaylightingManager
144 : // DLUMEF DayltgLuminousEfficacy WeatherManager WeatherManager
145 :
146 : // Using/Aliasing
147 : using namespace DataSurfaces;
148 :
149 7 : 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 7 : auto &dl = state.dataDayltg;
175 7 : auto &s_surf = state.dataSurface;
176 :
177 : // Total inside surface area, including windows
178 7 : Real64 AInsTot = 0.0;
179 : // Sum of products of inside surface area * vis reflectance
180 7 : Real64 ARHTOT = 0.0;
181 :
182 : // Area sum and area * reflectance sum for different orientations
183 7 : std::array<Real64, (int)FWC::Num> AR = {0.0, 0.0, 0.0};
184 7 : std::array<Real64, (int)FWC::Num> ARH = {0.0, 0.0, 0.0};
185 : // Loop over surfaces in the zone's enclosure
186 :
187 7 : auto &thisEnclosure = state.dataViewFactor->EnclSolInfo(enclNum);
188 48 : for (int ISurf : thisEnclosure.SurfacePtr) {
189 41 : auto const &surf = s_surf->Surface(ISurf);
190 :
191 41 : SurfaceClass IType = surf.Class;
192 : // Error if window has multiplier > 1 since this causes incorrect illuminance calc
193 41 : if (IType == SurfaceClass::Window && surf.Multiplier > 1.0) {
194 0 : if (thisEnclosure.TotalEnclosureDaylRefPoints > 0) {
195 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
196 0 : ShowContinueError(state, "...not allowed since it is in a zone or enclosure with daylighting.");
197 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
198 : } else {
199 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Multiplier > 1.0 for window {} in Zone={}", surf.Name, surf.ZoneName));
200 0 : ShowContinueError(state, "...an adjacent Zone has daylighting. Simulation cannot proceed.");
201 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
202 : }
203 : }
204 41 : if (IType == SurfaceClass::Wall || IType == SurfaceClass::Floor || IType == SurfaceClass::Roof || IType == SurfaceClass::Window ||
205 : IType == SurfaceClass::Door) {
206 41 : Real64 AREA = surf.Area;
207 : // In following, FrameArea and DividerArea can be non-zero only for exterior windows
208 41 : AInsTot += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
209 41 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
210 41 : ARHTOT +=
211 41 : AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
212 41 : s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
213 41 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
214 :
215 41 : FWC fwc = FWC::Ceiling; // Ceiling
216 41 : if (surf.Tilt > 10.0 && surf.Tilt < 170.0) fwc = FWC::Wall; // Wall
217 41 : if (surf.Tilt >= 170.0) fwc = FWC::Floor; // Floor
218 41 : AR[(int)fwc] += AREA + s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) +
219 41 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf));
220 41 : ARH[(int)fwc] +=
221 41 : AREA * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack +
222 41 : s_surf->SurfWinFrameArea(ISurf) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(ISurf)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(ISurf)) +
223 41 : s_surf->SurfWinDividerArea(ISurf) * (1.0 + s_surf->SurfWinProjCorrDivIn(ISurf)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(ISurf));
224 : }
225 : }
226 :
227 : // Average inside surface reflectance of enclosure
228 7 : if (AInsTot <= 0.0) {
229 0 : ShowSevereError(state, format("DayltgAveInteriorReflectance: Total opaque surface area is <=0.0 in solar enclosure={}", thisEnclosure.Name));
230 0 : ShowFatalError(state, "Program terminates due to preceding conditions.");
231 : }
232 7 : dl->enclDaylight(enclNum).aveVisDiffReflect = ARHTOT / AInsTot;
233 : // Total inside surface area of enclosure
234 7 : dl->enclDaylight(enclNum).totInsSurfArea = AInsTot;
235 : // Average floor visible reflectance
236 7 : dl->enclDaylight(enclNum).floorVisRefl = ARH[iFWC_Ceiling] / (AR[iFWC_Ceiling] + 1.e-6);
237 :
238 48 : for (int ISurf : thisEnclosure.SurfacePtr) {
239 41 : auto const &surf = s_surf->Surface(ISurf);
240 41 : if (surf.Class != SurfaceClass::Wall && surf.Class != SurfaceClass::Floor && surf.Class != SurfaceClass::Roof) continue;
241 :
242 : // Remove this surface from the space inside surface area and area*reflectivity
243 : // The resulting areas are AP(ITILT). The resulting area*reflectivity is ARHP(ITILT).
244 : // Initialize gross area of surface (including subsurfaces)
245 34 : Real64 ATWL = surf.Area; // This is the surface area less subsurfaces
246 : // Area * reflectance for this surface, excluding attached windows and doors
247 34 : Real64 ARHTWL = surf.Area * state.dataConstruction->Construct(surf.Construction).ReflectVisDiffBack;
248 :
249 34 : FWC fwc = (surf.Tilt > 45.0 && surf.Tilt < 135.0) ? FWC::Wall : ((surf.Tilt >= 135.0) ? FWC::Floor : FWC::Ceiling);
250 :
251 : // Loop over windows and doors on this wall
252 256 : for (int IWinDr : thisEnclosure.SurfacePtr) {
253 222 : auto const &surfWinDr = s_surf->Surface(IWinDr);
254 222 : if ((surfWinDr.Class != SurfaceClass::Window && surfWinDr.Class != SurfaceClass::Door) || surfWinDr.BaseSurf != ISurf) continue;
255 :
256 7 : ATWL += surfWinDr.Area + s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) +
257 7 : s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr));
258 7 : ARHTWL +=
259 7 : surfWinDr.Area * state.dataConstruction->Construct(surfWinDr.Construction).ReflectVisDiffBack +
260 7 : s_surf->SurfWinFrameArea(IWinDr) * (1.0 + 0.5 * s_surf->SurfWinProjCorrFrIn(IWinDr)) * (1.0 - s_surf->SurfWinFrameSolAbsorp(IWinDr)) +
261 7 : s_surf->SurfWinDividerArea(IWinDr) * (1.0 + s_surf->SurfWinProjCorrDivIn(IWinDr)) * (1.0 - s_surf->SurfWinDividerSolAbsorp(IWinDr));
262 : }
263 :
264 : std::array<Real64, (int)FWC::Num> AP;
265 : std::array<Real64, (int)FWC::Num> ARHP;
266 : // Inside surface area of floor, walls and ceilings, minus surface ISurf and its subsurfaces
267 136 : for (int iFWC = iFWC_Floor; iFWC < (int)FWC::Num; ++iFWC) {
268 102 : if (iFWC == (int)fwc) {
269 34 : AP[iFWC] = AR[iFWC] - ATWL;
270 34 : ARHP[iFWC] = ARH[iFWC] - ARHTWL;
271 : } else {
272 68 : AP[iFWC] = AR[iFWC];
273 68 : ARHP[iFWC] = ARH[iFWC];
274 : }
275 : }
276 34 : s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf = AP;
277 34 : s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf = ARHP;
278 : } // for (ISurf)
279 :
280 48 : for (int IWin : thisEnclosure.SurfacePtr) {
281 41 : auto const &surf = s_surf->Surface(IWin);
282 41 : if (surf.Class != SurfaceClass::Window) continue;
283 :
284 7 : auto &surfWin = s_surf->SurfaceWindow(IWin);
285 7 : auto const &zone = state.dataHeatBal->Zone(surf.Zone);
286 7 : int ISurf = surf.BaseSurf;
287 :
288 : // Ratio of floor-to-window-center height and average floor-to-ceiling height
289 7 : Real64 ETA = max(0.0, min(1.0, (surfWin.WinCenter.z - zone.OriginZ) * zone.FloorArea / zone.Volume));
290 :
291 7 : std::array<Real64, (int)FWC::Num> AP = s_surf->SurfaceWindow(ISurf).EnclAreaMinusThisSurf;
292 7 : std::array<Real64, (int)FWC::Num> ARHP = s_surf->SurfaceWindow(ISurf).EnclAreaReflProdMinusThisSurf;
293 : // Average reflectance seen by light moving up (RhoCeilingWall) and down (RhoFloorWall)
294 : // across horizontal plane through center of window
295 7 : surfWin.rhoCeilingWall = (ARHP[iFWC_Wall] * (1.0 - ETA) + ARHP[iFWC_Ceiling]) / (AP[iFWC_Wall] * (1.0 - ETA) + AP[iFWC_Ceiling] + 1.0e-5);
296 7 : surfWin.rhoFloorWall = (ARHP[iFWC_Wall] * ETA + ARHP[iFWC_Floor]) / (AP[iFWC_Wall] * ETA + AP[iFWC_Floor] + 1.e-9);
297 :
298 : // Angle factor for windows with diffusing shades. SurfaceWindow(IWin)%FractionUpgoing is
299 : // fraction of light from the shade that goes up toward ceiling and upper part of walls.
300 : // 1 - SurfaceWindow(IWin)%FractionUpgoing is fraction that goes down toward floor and lower part of walls.
301 7 : surfWin.fractionUpgoing = surf.Tilt / 180.0;
302 :
303 : // Daylighting shelf simplification: All light goes up to the ceiling regardless of orientation of shelf
304 7 : if (s_surf->SurfDaylightingShelfInd(IWin) > 0) {
305 0 : if (state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf > 0) surfWin.fractionUpgoing = 1.0;
306 : }
307 : } // for (IWin)
308 7 : } // DayltgAveInteriorReflectance()
309 :
310 1839 : void CalcDayltgCoefficients(EnergyPlusData &state)
311 : {
312 :
313 : // SUBROUTINE INFORMATION:
314 : // AUTHOR Fred Winkelmann
315 : // DATE WRITTEN July 1997
316 : // MODIFIED FW, Jan 2002: add variable slat angle blinds
317 : // FW, Mar 2002: add triangular windows
318 : // FW, Oct 2002: remove warning on window discretization relative to
319 : // reference point distance to window plane
320 : // FW, Jan 2003: add between-glass shades and blinds
321 : // FW, Apr 2003: initialize shading type to 'NOSHADE' in window loop
322 : // PE, May 2003: add light pipes (tubular daylighting devices)
323 : // FW, Jul 2003: account for possible non-zero transmittance of
324 : // shading surfaces (previously all shading surfaces were
325 : // assumed to be opaque)
326 : // PE, Aug 2003: add daylighting shelves
327 : // FW, Sep 2003: write the bare-window overcast sky daylight factors to the eio file
328 : // FW, Nov 2003: add exterior beam and sky solar diffuse reflection from obstructions;
329 : // add beam solar and sky solar reflection from ground with obstructions.
330 : // FW, Nov 2003: change expression for NDIVX, NDIVY (no. of window elements in X,Y) to
331 : // round up to nearest integer rather than down
332 : // FW, Nov 2003: add specular reflection of beam solar from obstructions
333 : // RJH, Jan 2004: add alternative daylighting analysis using DElight
334 : // All modifications demarked with RJH (Rob Hitchcock)
335 : // FW, Feb 2004: add daylighting through interior windows
336 : // FW, Apr 2004: add light well efficiency that multiplies glazing transmittance
337 : // FW, Apr 2004: add diffusing glazing
338 : // RJH, Jul 2004: add error handling for warnings/errors returned from DElight
339 : // LKL, Oct 2004: Separate "map" and "ref" point calculations -- move some input routines to
340 : // separate routines.
341 :
342 : // PURPOSE OF THIS SUBROUTINE:
343 : // Calculates daylighting factors for later use in the time-step loop.
344 :
345 : // METHODOLOGY EMPLOYED:
346 :
347 : // For each combination of exterior window and reference point in a zone,
348 : // calculates daylighting factors (interior illuminance / exterior illuminance)
349 : // and glare factors for clear and overcast skies and for windows with and
350 : // without shading devices. These factors are calculated for each hourly
351 : // sun position for design days and for selected days throughout the year.
352 :
353 : // If a target zone has one or more interior windows, also calculates daylighting
354 : // factors for the target zone that are associated with exterior windows in adjacent
355 : // zones that share interior windows with the target zone.
356 :
357 : // The daylight illuminance at a reference point from a window is determined
358 : // by dividing the window into rectangular elements and calculating the illuminance
359 : // reaching the reference point directly from each element. The illumination
360 : // from an element can come from the sky or ground if the window is unshaded, or from
361 : // a shading device illuminated by solar radiation. Also considered are the
362 : // illuminance contribution from interreflection among the zone's interior surfaces
363 : // and sunlight striking the reference point.
364 :
365 : // In calculating sky-related interior illuminance and luminance quantities,
366 : // the sky luminance for the different sky types are determined from distributions
367 : // in which the zenith luminance is normalized to 1.0 cd/m2. Similarly, sun-related
368 : // illuminance and luminance quantities are based on beam normal solar illuminance
369 : // normalized to 1.0 lux.
370 : // The daylight and glare factors calculated in this subroutine are used in DayltgInteriorIllum
371 : // to get the daylight illuminance and glare at each time step.
372 : // Based on this information and user-input lighting setpoint and type of lighting
373 : // control system, DayltgElecLightingControl then determines how much the overhead electric lighting
374 : // can be reduced.
375 :
376 : // REFERENCES:
377 : // Based on DOE-2.1E subroutine DCOF.
378 :
379 1839 : auto &dl = state.dataDayltg;
380 1839 : auto &s_surf = state.dataSurface;
381 :
382 1839 : if (dl->CalcDayltghCoefficients_firstTime) {
383 107 : GetDaylightingParametersInput(state);
384 107 : CheckTDDsAndLightShelvesInDaylitZones(state);
385 107 : AssociateWindowShadingControlWithDaylighting(state);
386 107 : dl->CalcDayltghCoefficients_firstTime = false;
387 : } // End of check if firstTime
388 :
389 : // Find the total number of exterior windows associated with all Daylighting:Detailed enclosures.
390 : // An exterior window is associated with such a enclosure if (1) it is an exterior window in the enclosure, or
391 : // (2) it is an exterior window in an adjacent enclosure that shares an interior window with the enclosure.
392 : // Note that exterior windows in category (2) may be counted more than once if an adjacent enclosure
393 : // is adjacent to more than one daylit enclosure with which the adjacent enclosure shares interior windows.
394 : // If there are no interior windows in a building, than TotWindowsWithDayl is just the total number of
395 : // exterior windows in Daylighting:Detailed enclosures. Note that it is possible for a
396 : // Daylighting:Detailed enclosure to have zero exterior windows of its own, but it may have an interior
397 : // through which daylight passes from adjacent enclosures with exterior windows.
398 1839 : if ((int)dl->DaylRefPt.size() == 0) return;
399 1358 : if (state.dataGlobal->BeginSimFlag) {
400 5 : dl->TotWindowsWithDayl = 0;
401 12 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
402 7 : dl->TotWindowsWithDayl += dl->enclDaylight(enclNum).NumOfDayltgExtWins;
403 : }
404 : }
405 :
406 1358 : if (dl->TotWindowsWithDayl == 0) return;
407 :
408 : //-----------------------------------------!
409 : // Detailed daylighting factor calculation !
410 : //-----------------------------------------!
411 1358 : if (!state.dataSysVars->DetailedSolarTimestepIntegration && !state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) {
412 3 : if (state.dataGlobal->WarmupFlag) {
413 1 : DisplayString(state, "Calculating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
414 : } else {
415 2 : DisplayString(state, "Updating Detailed Daylighting Factors, Start Date=" + state.dataEnvrn->CurMnDy);
416 : }
417 : }
418 :
419 1358 : if (state.dataGlobal->BeginSimFlag) {
420 :
421 : // Find minimum solid angle subtended by an interior window in Daylighting:Detailed zones.
422 : // Used in calculating daylighting through interior windows.
423 5 : CalcMinIntWinSolidAngs(state);
424 :
425 : // Warning if detailed daylighting has been requested for a zone with no associated exterior windows.
426 12 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
427 7 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
428 7 : if (thisEnclDaylight.NumOfDayltgExtWins == 0 && thisEnclDaylight.TotalExtWindows == 0) {
429 0 : for (int daylightCtrlNum : thisEnclDaylight.daylightControlIndexes) {
430 0 : if (dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints > 0) {
431 0 : ShowWarningError(
432 : state,
433 0 : format("Detailed daylighting will not be done for Daylighting:Controls={}", dl->daylightControl(daylightCtrlNum).Name));
434 0 : ShowContinueError(state, "because it has no associated exterior windows.");
435 : }
436 : }
437 : }
438 :
439 : // Find area and reflectance quantities used in calculating inter-reflected illuminance.
440 : // TH 9/10/2009. Need to calculate for zones without daylighting controls (TotalDaylRefPoints = 0)
441 : // but with adjacent zones having daylighting controls.
442 7 : if ((thisEnclDaylight.NumOfDayltgExtWins > 0) || thisEnclDaylight.adjEnclHasDayltgCtrl) {
443 7 : DayltgAveInteriorReflectance(state, enclNum);
444 : }
445 : }
446 : }
447 :
448 1358 : int numTDD = (int)state.dataDaylightingDevicesData->TDDPipe.size();
449 : // Zero daylighting factor arrays
450 1358 : if (numTDD > 0) {
451 0 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
452 0 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
453 0 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
454 0 : for (int iTDD = 1; iTDD <= numTDD; ++iTDD) {
455 0 : dl->TDDTransVisBeam(iHr, iTDD) = 0.0;
456 0 : dl->TDDFluxInc(iHr, iTDD) = Illums();
457 0 : dl->TDDFluxTrans(iHr, iTDD) = Illums();
458 : }
459 : }
460 : }
461 :
462 1358 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
463 4 : if (state.dataGlobal->BeginDayFlag) {
464 : // Calculate hourly sun angles, clear sky zenith luminance, and exterior horizontal illuminance
465 2 : dl->sunAngles = SunAngles();
466 2 : dl->sunAnglesHr = {SunAngles()};
467 2 : dl->horIllum = {Illums()};
468 50 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
469 48 : auto const &surfSunCosHr = s_surf->SurfSunCosHourly(IHR);
470 48 : if (surfSunCosHr.z < DataEnvironment::SunIsUpValue)
471 33 : continue; // Skip if sun is below horizon //Autodesk SurfSunCosHourly was uninitialized here
472 :
473 15 : Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
474 15 : Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
475 15 : dl->sunAngles = dl->sunAnglesHr[IHR] = {phi, std::sin(phi), std::cos(phi), theta};
476 :
477 15 : DayltgExtHorizIllum(state, dl->horIllum[IHR]);
478 : }
479 : }
480 : } else { // timestep integrated calculations
481 1354 : dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {SunAngles()};
482 5416 : dl->horIllum[state.dataGlobal->HourOfDay] = Illums();
483 1354 : auto const &surfSunCosHr = s_surf->SurfSunCosHourly(state.dataGlobal->HourOfDay);
484 1354 : if (!(surfSunCosHr.z < DataEnvironment::SunIsUpValue)) { // Skip if sun is below horizon
485 536 : Real64 phi = Constant::PiOvr2 - std::acos(surfSunCosHr.z);
486 536 : Real64 theta = std::atan2(surfSunCosHr.y, surfSunCosHr.x);
487 536 : dl->sunAngles = dl->sunAnglesHr[state.dataGlobal->HourOfDay] = {phi, std::sin(phi), std::cos(phi), theta};
488 536 : DayltgExtHorizIllum(state, dl->horIllum[state.dataGlobal->HourOfDay]);
489 : }
490 : }
491 :
492 1358 : CalcDayltgCoeffsRefMapPoints(state);
493 :
494 1451 : if (dl->doSkyReporting && (!state.dataGlobal->KickOffSizing && !state.dataGlobal->KickOffSimulation) &&
495 93 : (dl->FirstTimeDaylFacCalc && dl->TotWindowsWithDayl > 0 &&
496 93 : (!state.dataSysVars->DetailedSolarTimestepIntegration || state.dataGlobal->HourOfDay == 12))) {
497 : // Write the bare-window four sky daylight factors at noon time to the eio file; this is done only
498 : // for first time that daylight factors are calculated and so is insensitive to possible variation
499 : // due to change in ground reflectance from month to month, or change in storm window status.
500 : static constexpr std::string_view Format_700("! <Sky Daylight Factors>, Sky Type, MonthAndDay, Daylighting Control Name, Enclosure Name, "
501 : "Window Name, Reference Point, Daylight Factor\n");
502 5 : print(state.files.eio, Format_700);
503 12 : for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
504 7 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
505 7 : int enclNum = thisDayltgCtrl.enclIndex;
506 7 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
507 :
508 7 : if (thisEnclDaylight.NumOfDayltgExtWins == 0 || !thisEnclDaylight.hasSplitFluxDaylighting) continue;
509 14 : for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
510 7 : int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
511 : // For this report, do not include ext wins in zone adjacent to ZoneNum since the inter-reflected
512 : // component will not be calculated for these windows until the time-step loop.
513 7 : if (s_surf->Surface(windowSurfNum).SolarEnclIndex != enclNum) continue;
514 : // Output for each reference point, for each sky. Group by sky type first
515 :
516 : static constexpr std::array<std::string_view, (int)SkyType::Num> skyTypeStrings = {
517 : "Clear Sky", "Clear Turbid Sky", "Intermediate Sky", "Overcast Sky"};
518 :
519 35 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
520 72 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
521 44 : Real64 DaylFac = thisDayltgCtrl.daylFac[12](windowCounter, refPtNum)[iWinCover_Bare][iLum_Illum].sky[iSky];
522 44 : print(state.files.eio,
523 : " Sky Daylight Factors,{},{},{},{},{},{},{:.4R}\n",
524 44 : skyTypeStrings[iSky],
525 44 : state.dataEnvrn->CurMnDy,
526 44 : thisDayltgCtrl.Name,
527 44 : state.dataViewFactor->EnclSolInfo(thisDayltgCtrl.enclIndex).Name,
528 44 : s_surf->Surface(windowSurfNum).Name,
529 44 : dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
530 : DaylFac);
531 : } // for (refPtNum)
532 : } // for (iSky)
533 : } // for (windowCounter)
534 : } // for (controlNum)
535 5 : dl->FirstTimeDaylFacCalc = false;
536 5 : dl->doSkyReporting = false;
537 : } // if (detailedIntegration etc.)
538 :
539 : // Skip if no daylight windows
540 1358 : if (dl->TotWindowsWithDayl == 0) return;
541 :
542 : // Skip if no request of reporting
543 1358 : if ((!dl->DFSReportSizingDays) && (!dl->DFSReportAllShadowCalculationDays)) return;
544 :
545 : // Skip duplicate calls
546 1 : if (state.dataGlobal->KickOffSizing) return;
547 1 : if (state.dataGlobal->DoingSizing) return;
548 1 : if (state.dataGlobal->KickOffSimulation) return;
549 :
550 1 : if (dl->DFSReportSizingDays) {
551 1 : if (state.dataGlobal->DoWeathSim && state.dataGlobal->DoDesDaySim) {
552 1 : if (state.dataGlobal->KindOfSim == Constant::KindOfSim::RunPeriodWeather) return;
553 : }
554 : }
555 :
556 1 : if (dl->DFSReportAllShadowCalculationDays) {
557 0 : if (state.dataGlobal->KindOfSim != Constant::KindOfSim::RunPeriodWeather) return;
558 : }
559 :
560 : // open a new file eplusout.dfs for saving the daylight factors
561 1 : if (dl->CreateDFSReportFile) {
562 2 : InputOutputFile &dfs = state.files.dfs.ensure_open(state, "CalcDayltgCoefficients", state.files.outputControl.dfs);
563 1 : print(dfs, "{}\n", "This file contains daylight factors for all exterior windows of daylight enclosures.");
564 1 : print(dfs, "{}\n", "MonthAndDay,Enclosure Name,Zone Name,Window Name,Window State");
565 1 : print(dfs,
566 : "{}\n",
567 : "Hour,Reference Point,Daylight Factor for Clear Sky,Daylight Factor for Clear Turbid Sky,"
568 : "Daylight Factor for Intermediate Sky,Daylight Factor for Overcast Sky");
569 1 : dl->CreateDFSReportFile = false;
570 : }
571 :
572 3 : for (int controlNum = 1; controlNum <= (int)dl->daylightControl.size(); ++controlNum) {
573 2 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
574 2 : int enclNum = thisDayltgCtrl.enclIndex;
575 2 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
576 2 : if (thisEnclDaylight.NumOfDayltgExtWins == 0) continue;
577 :
578 4 : for (int windowCounter = 1; windowCounter <= thisEnclDaylight.NumOfDayltgExtWins; ++windowCounter) {
579 2 : int windowSurfNum = thisEnclDaylight.DayltgExtWinSurfNums(windowCounter);
580 2 : auto &surf = s_surf->Surface(windowSurfNum);
581 : // For this report, do not include ext wins in zone/enclosure adjacent to ZoneNum since the inter-reflected
582 : // component will not be calculated for these windows until the time-step loop.
583 2 : if (surf.SolarEnclIndex == enclNum) {
584 :
585 2 : int numWinCover = surf.HasShadeControl ? (int)WinCover::Num : 1;
586 :
587 : // loop over each slat angle
588 4 : for (int iWinCover = 0; iWinCover < numWinCover; ++iWinCover) {
589 2 : if (iWinCover == iWinCover_Bare) {
590 : // base window without shades, screens, or blinds
591 2 : print(state.files.dfs,
592 : "{},{},{},{},Base Window\n",
593 2 : state.dataEnvrn->CurMnDy,
594 2 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
595 2 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
596 2 : s_surf->Surface(windowSurfNum).Name);
597 0 : } else if (iWinCover == iWinCover_Shaded) {
598 : // window shade or blind with fixed slat angle
599 0 : print(state.files.dfs,
600 : "{},{},{},{},Blind or Slat Applied\n",
601 0 : state.dataEnvrn->CurMnDy,
602 0 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
603 0 : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).Name,
604 0 : s_surf->Surface(windowSurfNum).Name);
605 : }
606 :
607 50 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
608 : // For each Daylight Reference Point
609 48 : auto &daylFacHr = thisDayltgCtrl.daylFac[IHR];
610 144 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
611 96 : auto &illums = daylFacHr(windowCounter, refPtNum)[iWinCover][iLum_Illum];
612 :
613 : // write daylight factors - 4 sky types for each daylight ref point
614 96 : print(state.files.dfs,
615 : "{},{},{:.5R},{:.5R},{:.5R},{:.5R}\n",
616 : IHR,
617 96 : dl->DaylRefPt(thisDayltgCtrl.refPts(refPtNum).num).Name,
618 96 : illums.sky[(int)SkyType::Clear],
619 96 : illums.sky[(int)SkyType::ClearTurbid],
620 96 : illums.sky[(int)SkyType::Intermediate],
621 96 : illums.sky[(int)SkyType::Overcast]);
622 :
623 : } // for (refPtNum) Reference Point
624 : } // for (IHR) hour
625 : } // for (ISlatAngle) slat angle
626 : } // if (SolarEnclIndex == enclNum)
627 : } // for (windowCounter) exterior windows in enclosure
628 : } // for (controlNum) daylighting control
629 : } // CalcDayltgCoefficients()
630 :
631 1358 : void CalcDayltgCoeffsRefMapPoints(EnergyPlusData &state)
632 : {
633 :
634 : // SUBROUTINE INFORMATION:
635 : // AUTHOR Linda Lawrie
636 : // DATE WRITTEN October 2004
637 : // MODIFIED May 2006 (RR): added exterior window screens
638 : // April 2012 (LKL); change to allow multiple maps per zone
639 :
640 : // PURPOSE OF THIS SUBROUTINE:
641 : // This subroutine does the daylighting coefficient calculation for the
642 : // daylighting and illuminance map reference points.
643 1358 : auto &dl = state.dataDayltg;
644 1358 : auto const &s_surf = state.dataSurface;
645 :
646 1358 : if (dl->VeryFirstTime) {
647 : // make sure all necessary surfaces match to pipes
648 5 : bool ErrorsFound = false;
649 12 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
650 14 : for (int loopwin = 1; loopwin <= dl->enclDaylight(enclNum).NumOfDayltgExtWins; ++loopwin) {
651 7 : int IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
652 7 : if (s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Diffuser) continue;
653 : // Look up the TDD:DOME object
654 0 : int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
655 0 : if (PipeNum == 0) {
656 0 : ShowSevereError(
657 : state,
658 0 : format("GetTDDInput: Surface={}, TDD:Dome object does not reference a valid Diffuser object.", s_surf->Surface(IWin).Name));
659 0 : ShowContinueError(state, "...needs DaylightingDevice:Tubular of same name as Surface.");
660 0 : ErrorsFound = true;
661 : }
662 : }
663 : }
664 :
665 5 : if (ErrorsFound) {
666 0 : ShowFatalError(state, "Not all TubularDaylightDome objects have corresponding DaylightingDevice:Tubular objects. Program terminates.");
667 : }
668 5 : dl->VeryFirstTime = false;
669 : }
670 :
671 : // Calc for daylighting reference points for daylighting controls that use SplitFlux method
672 2718 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
673 1360 : if (dl->daylightControl(daylightCtrlNum).DaylightMethod != DaylightingMethod::SplitFlux) continue;
674 : // Skip enclosures with no exterior windows or in adjacent enclosure(s) with which an interior window is shared
675 1360 : if (dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex).NumOfDayltgExtWins == 0) continue;
676 1360 : CalcDayltgCoeffsRefPoints(state, daylightCtrlNum);
677 : }
678 1358 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->KickOffSimulation) {
679 : // Calc for illuminance maps
680 1351 : if ((int)dl->illumMaps.size() > 0) {
681 2 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
682 1 : int mapZoneNum = dl->illumMaps(MapNum).zoneIndex;
683 1 : std::string name = format("Zone={}", state.dataHeatBal->Zone(mapZoneNum).Name);
684 1 : int mapSpaceNum = dl->illumMaps(MapNum).spaceIndex;
685 1 : if (mapSpaceNum > 0) {
686 0 : name = format("Space={}", state.dataHeatBal->space(mapSpaceNum).Name);
687 : }
688 1 : if (state.dataGlobal->WarmupFlag) {
689 1 : DisplayString(state, format("Calculating Daylighting Coefficients (Map Points), {}", name));
690 : } else {
691 0 : DisplayString(state, format("Updating Daylighting Coefficients (Map Points), {}", name));
692 : }
693 1 : CalcDayltgCoeffsMapPoints(state, MapNum);
694 1 : }
695 : }
696 : }
697 1358 : } // CalcDayltgCoeffsRefMapPoints()
698 :
699 1360 : void CalcDayltgCoeffsRefPoints(EnergyPlusData &state, int const daylightCtrlNum)
700 : {
701 :
702 : // SUBROUTINE INFORMATION:
703 : // AUTHOR Linda Lawrie
704 : // DATE WRITTEN April 2012
705 : // MODIFIED November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
706 :
707 : // PURPOSE OF THIS SUBROUTINE:
708 : // Provides calculations for Daylighting Coefficients for daylighting reference points
709 1360 : auto &dl = state.dataDayltg;
710 1360 : auto const &s_surf = state.dataSurface;
711 :
712 : // glare calculation (radians)
713 : int IConst; // Construction counter
714 : int ICtrl; // Window control counter
715 : int IWin; // Window counter
716 : int IWin2; // Secondary window counter (for TDD:DOME object, if exists)
717 : int InShelfSurf; // Inside daylighting shelf surface number
718 : WinShadingType ShType; // Window shading type
719 : int BlNum; // Window Blind Number
720 : int LSHCAL; // Interior shade calculation flag: 0=not yet
721 : // calculated, 1=already calculated
722 : int NWX; // Number of window elements in x direction for dayltg calc
723 : int NWY; // Number of window elements in y direction for dayltg calc
724 : int NWYlim; // For triangle, largest NWY for a given IX
725 : Real64 COSB; // Cosine of angle between window outward normal and ray from
726 : // reference point to window element
727 : Real64 PHRAY; // Altitude of ray from reference point to window element (radians)
728 : Real64 THRAY; // Azimuth of ray from reference point to window element (radians)
729 : Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
730 : Real64 TVISB; // Visible transmittance of window for COSB angle of incidence (times light well
731 : // efficiency, if appropriate)
732 : int ISunPos; // Sun position counter; used to avoid calculating various
733 : // quantities that do not depend on sun position.
734 : Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
735 : // from reference point through a window element
736 : bool is_Rectangle; // True if window is rectangular
737 : bool is_Triangle; // True if window is triangular
738 : Real64 DWX; // Horizontal dimension of window element (m)
739 : Real64 DWY; // Vertical dimension of window element (m)
740 : Real64 DAXY; // Area of window element
741 : Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
742 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
743 : int BRef;
744 : int ILB;
745 : bool hitIntObs; // True iff interior obstruction hit
746 : bool hitExtObs; // True iff ray from ref pt to ext win hits an exterior obstruction
747 : Real64 TVISIntWin; // Visible transmittance of int win at COSBIntWin for light from ext win
748 : Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
749 :
750 1360 : Vector3<Real64> W2;
751 1360 : Vector3<Real64> W3;
752 1360 : Vector3<Real64> W21;
753 1360 : Vector3<Real64> W23;
754 1360 : Vector3<Real64> RREF2;
755 1360 : Vector3<Real64> RWIN;
756 1360 : Vector3<Real64> RWIN2;
757 1360 : Vector3<Real64> Ray;
758 1360 : Vector3<Real64> WNORM2;
759 1360 : Vector3<Real64> VIEWVC;
760 1360 : Vector3<Real64> U2;
761 1360 : Vector3<Real64> U21;
762 1360 : Vector3<Real64> U23;
763 1360 : Vector3<Real64> VIEWVC2;
764 :
765 : int WinEl; // Current window element
766 :
767 1360 : if (dl->refFirstTime && (dl->maxControlRefPoints > 0)) {
768 5 : dl->RefErrIndex.allocate(dl->maxControlRefPoints, s_surf->TotSurfaces);
769 5 : dl->RefErrIndex = 0;
770 5 : dl->refFirstTime = false;
771 : }
772 :
773 1360 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
774 1360 : auto const &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
775 1360 : int zoneNum = thisDayltgCtrl.zoneIndex;
776 : // Azimuth of view vector in absolute coord sys
777 1360 : Real64 AZVIEW = (thisDayltgCtrl.ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth + state.dataHeatBal->BuildingAzimuth +
778 1360 : state.dataHeatBal->BuildingRotationAppendixG) *
779 1360 : Constant::DegToRad;
780 : // View vector components in absolute coord sys
781 1360 : VIEWVC = {std::sin(AZVIEW), std::cos(AZVIEW), 0.0};
782 :
783 2725 : for (auto &refPt : thisDayltgCtrl.refPts) {
784 1365 : refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
785 1365 : refPt.glareIndex = 0.0; // Glare index at reference points
786 2730 : for (auto &extWin : refPt.extWins) {
787 1365 : extWin.solidAng = extWin.solidAngWtd = 0.0;
788 1365 : extWin.lums[iLum_Illum] = extWin.lums[iLum_Back] = extWin.lums[iLum_Source] = {0.0, 0.0};
789 : }
790 : }
791 :
792 1360 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
793 1360 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
794 1360 : int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
795 1360 : int numRefPts = thisDayltgCtrl.TotalDaylRefPoints;
796 :
797 2858 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
798 1498 : auto &daylFacHr = thisDayltgCtrl.daylFac[iHr];
799 2996 : for (int iWin = 1; iWin <= numExtWins; ++iWin) {
800 3116 : for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
801 4854 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
802 3236 : auto &daylFac = daylFacHr(iWin, iRefPt)[iWinCover];
803 12944 : daylFac[iLum_Illum] = Illums();
804 12944 : daylFac[iLum_Source] = Illums();
805 12944 : daylFac[iLum_Back] = Illums();
806 : } // for (iSlatAng)
807 : } // for (iRefPt)
808 : } // for (iWin)
809 : } // for (iHr)
810 :
811 1360 : BRef = 0;
812 :
813 2725 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
814 1365 : auto const &refPt = thisDayltgCtrl.refPts(IL);
815 : // Reference point in absolute coordinate system
816 1365 : Vector3<Real64> RREF = refPt.absCoords;
817 :
818 : // -------------
819 : // ---------- WINDOW LOOP ----------
820 : // -------------
821 2730 : for (int loopwin = 1; loopwin <= thisEnclDaylight.NumOfDayltgExtWins; ++loopwin) {
822 :
823 1365 : FigureDayltgCoeffsAtPointsSetupForWindow(state,
824 : daylightCtrlNum,
825 : IL,
826 : loopwin,
827 : CalledFor::RefPoint,
828 : RREF,
829 : VIEWVC,
830 : IWin,
831 : IWin2,
832 : NWX,
833 : NWY,
834 : W2,
835 : W3,
836 : W21,
837 : W23,
838 : LSHCAL,
839 : InShelfSurf,
840 : ICtrl,
841 : ShType,
842 : BlNum,
843 : WNORM2,
844 : extWinType,
845 : IConst,
846 : RREF2,
847 : DWX,
848 : DWY,
849 : DAXY,
850 : U2,
851 : U23,
852 : U21,
853 : VIEWVC2,
854 : is_Rectangle,
855 : is_Triangle);
856 : // ---------------------
857 : // ---------- WINDOW ELEMENT LOOP ----------
858 : // ---------------------
859 :
860 1365 : WinEl = 0;
861 :
862 23128 : for (int IX = 1; IX <= NWX; ++IX) {
863 21763 : if (is_Rectangle) {
864 21763 : NWYlim = NWY;
865 0 : } else if (is_Triangle) {
866 0 : NWYlim = NWY - IX + 1;
867 : }
868 :
869 368814 : for (int IY = 1; IY <= NWYlim; ++IY) {
870 :
871 347051 : ++WinEl;
872 :
873 347051 : FigureDayltgCoeffsAtPointsForWindowElements(state,
874 : daylightCtrlNum,
875 : IL,
876 : loopwin,
877 : CalledFor::RefPoint,
878 : WinEl,
879 : IWin,
880 : IWin2,
881 : IX,
882 : IY,
883 : SkyObstructionMult,
884 : W2,
885 : W21,
886 : W23,
887 : RREF,
888 : NWYlim,
889 : VIEWVC2,
890 : DWX,
891 : DWY,
892 : DAXY,
893 : U2,
894 : U23,
895 : U21,
896 : RWIN,
897 : RWIN2,
898 : Ray,
899 : PHRAY,
900 : LSHCAL,
901 : COSB,
902 : ObTrans,
903 : TVISB,
904 : DOMEGA,
905 : THRAY,
906 : hitIntObs,
907 : hitExtObs,
908 : WNORM2,
909 : extWinType,
910 : IConst,
911 : RREF2,
912 : is_Triangle,
913 : TVISIntWin,
914 : TVISIntWinDisk);
915 :
916 : // -------------------
917 : // ---------- SUN POSITION LOOP ----------
918 : // -------------------
919 :
920 : // Sun position counter. Used to avoid calculating various quantities
921 : // that do not depend on sun position.
922 :
923 347051 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
924 427 : ISunPos = 0;
925 10675 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
926 :
927 10248 : FigureDayltgCoeffsAtPointsForSunPosition(state,
928 : daylightCtrlNum,
929 : IL,
930 : IX,
931 : NWX,
932 : IY,
933 : NWYlim,
934 : WinEl,
935 : IWin,
936 : IWin2,
937 : IHR,
938 : ISunPos,
939 : SkyObstructionMult,
940 : RWIN2,
941 : Ray,
942 : PHRAY,
943 : LSHCAL,
944 : InShelfSurf,
945 : COSB,
946 : ObTrans,
947 : TVISB,
948 : DOMEGA,
949 : ICtrl,
950 : ShType,
951 : BlNum,
952 : THRAY,
953 : WNORM2,
954 : extWinType,
955 : IConst,
956 : AZVIEW,
957 : RREF2,
958 : hitIntObs,
959 : hitExtObs,
960 : CalledFor::RefPoint,
961 : TVISIntWin,
962 : TVISIntWinDisk);
963 :
964 : } // End of hourly sun position loop, IHR
965 : } else { // timestep integrated
966 346624 : if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
967 15 : ISunPos = 0;
968 15 : dl->MySunIsUpFlag = true;
969 346609 : } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
970 137201 : ISunPos = 1;
971 209408 : } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
972 14 : dl->MySunIsUpFlag = false;
973 14 : ISunPos = -1;
974 209394 : } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
975 209394 : ISunPos = -1;
976 : }
977 :
978 346624 : FigureDayltgCoeffsAtPointsForSunPosition(state,
979 : daylightCtrlNum,
980 : IL,
981 : IX,
982 : NWX,
983 : IY,
984 : NWYlim,
985 : WinEl,
986 : IWin,
987 : IWin2,
988 346624 : state.dataGlobal->HourOfDay,
989 : ISunPos,
990 : SkyObstructionMult,
991 : RWIN2,
992 : Ray,
993 : PHRAY,
994 : LSHCAL,
995 : InShelfSurf,
996 : COSB,
997 : ObTrans,
998 : TVISB,
999 : DOMEGA,
1000 : ICtrl,
1001 : ShType,
1002 : BlNum,
1003 : THRAY,
1004 : WNORM2,
1005 : extWinType,
1006 : IConst,
1007 : AZVIEW,
1008 : RREF2,
1009 : hitIntObs,
1010 : hitExtObs,
1011 : CalledFor::RefPoint,
1012 : TVISIntWin,
1013 : TVISIntWinDisk);
1014 : }
1015 :
1016 : } // End of window Y-element loop, IY
1017 : } // End of window X-element loop, IX
1018 :
1019 : // Loop again over hourly sun positions and calculate daylight factors by adding
1020 : // direct and inter-reflected illum components, then dividing by exterior horiz illum.
1021 : // Also calculate corresponding glare factors.
1022 :
1023 1365 : ILB = BRef + IL;
1024 :
1025 1365 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1026 11 : ISunPos = 0;
1027 275 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
1028 264 : FigureRefPointDayltgFactorsToAddIllums(state, daylightCtrlNum, ILB, IHR, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
1029 :
1030 : } // End of sun position loop, IHR
1031 : } else {
1032 1354 : if (state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1033 0 : ISunPos = 0;
1034 0 : dl->MySunIsUpFlag = true;
1035 1354 : } else if (state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1036 536 : ISunPos = 1;
1037 818 : } else if (!state.dataEnvrn->SunIsUp && dl->MySunIsUpFlag) {
1038 0 : dl->MySunIsUpFlag = false;
1039 0 : ISunPos = -1;
1040 818 : } else if (!state.dataEnvrn->SunIsUp && !dl->MySunIsUpFlag) {
1041 818 : ISunPos = -1;
1042 : }
1043 1354 : FigureRefPointDayltgFactorsToAddIllums(
1044 1354 : state, daylightCtrlNum, ILB, state.dataGlobal->HourOfDay, ISunPos, IWin, loopwin, NWX, NWY, ICtrl);
1045 : }
1046 : } // End of window loop, loopwin - IWin
1047 :
1048 1365 : } // End of reference point loop, IL
1049 1360 : }
1050 :
1051 1 : void CalcDayltgCoeffsMapPoints(EnergyPlusData &state, int const mapNum)
1052 : {
1053 :
1054 : // SUBROUTINE INFORMATION:
1055 : // AUTHOR Linda Lawrie
1056 : // DATE WRITTEN April 2012
1057 : // MODIFIED November 2012 (B. Griffith), refactor for detailed timestep integration and remove duplicate code
1058 :
1059 : // PURPOSE OF THIS SUBROUTINE:
1060 : // Provides calculations for Daylighting Coefficients for map illuminance points
1061 :
1062 : // METHODOLOGY EMPLOYED:
1063 : // Was previously part of CalcDayltgCoeffsRefMapPoints -- broken out to all multiple
1064 : // maps per zone
1065 1 : auto &dl = state.dataDayltg;
1066 1 : auto const &s_surf = state.dataSurface;
1067 :
1068 : // In the following four variables, I=1 for clear sky, 2 for overcast.
1069 : int numRefPts; // Number of daylighting reference points in a zone
1070 : // glare calculation (radians)
1071 : int IConst; // Construction counter
1072 : int ICtrl; // Window control counter
1073 : int IWin; // Window counter
1074 : int IWin2; // Secondary window counter (for TDD:DOME object, if exists)
1075 : int InShelfSurf; // Inside daylighting shelf surface number
1076 : WinShadingType ShType; // Window shading type
1077 : int BlNum; // Window Blind Number
1078 : int LSHCAL; // Interior shade calculation flag: 0=not yet
1079 : // calculated, 1=already calculated
1080 : int NWX; // Number of window elements in x direction for dayltg calc
1081 : int NWY; // Number of window elements in y direction for dayltg calc
1082 : int NWYlim; // For triangle, largest NWY for a given IX
1083 : Real64 DWX; // Horizontal dimension of window element (m)
1084 : Real64 DWY; // Vertical dimension of window element (m)
1085 : Real64 COSB; // Cosine of angle between window outward normal and ray from
1086 : // reference point to window element
1087 : Real64 PHRAY; // Altitude of ray from reference point to window element (radians)
1088 : Real64 THRAY; // Azimuth of ray from reference point to window element (radians)
1089 : Real64 DOMEGA; // Solid angle subtended by window element wrt reference point (steradians)
1090 : Real64 TVISB; // Visible transmittance of window for COSB angle of incidence (times light well
1091 : // efficiency, if appropriate)
1092 : int ISunPos; // Sun position counter; used to avoid calculating various
1093 : // quantities that do not depend on sun position.
1094 : Real64 ObTrans; // Product of solar transmittances of exterior obstructions hit by ray
1095 : // from reference point through a window element
1096 : bool is_Rectangle; // True if window is rectangular
1097 : bool is_Triangle; // True if window is triangular
1098 : Real64 DAXY; // Area of window element
1099 : Real64 SkyObstructionMult; // Ratio of obstructed to unobstructed sky diffuse at a ground point
1100 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1101 : int ILB;
1102 : bool hitIntObs; // True iff interior obstruction hit
1103 : bool hitExtObs; // True iff ray from ref pt to ext win hits an exterior obstruction
1104 : Real64 TVISIntWin; // Visible transmittance of int win at COSBIntWin for light from ext win
1105 : Real64 TVISIntWinDisk; // Visible transmittance of int win at COSBIntWin for sun
1106 : int WinEl; // window elements counter
1107 :
1108 1 : Vector3<Real64> W2;
1109 1 : Vector3<Real64> W3;
1110 1 : Vector3<Real64> W21;
1111 1 : Vector3<Real64> W23;
1112 1 : Vector3<Real64> RREF2;
1113 1 : Vector3<Real64> RWIN;
1114 1 : Vector3<Real64> RWIN2;
1115 1 : Vector3<Real64> Ray;
1116 1 : Vector3<Real64> WNORM2;
1117 1 : Vector3<Real64> VIEWVC;
1118 1 : Vector3<Real64> U2;
1119 1 : Vector3<Real64> U21;
1120 1 : Vector3<Real64> U23;
1121 1 : Vector3<Real64> VIEWVC2;
1122 :
1123 1 : if (dl->mapFirstTime && (int)dl->illumMaps.size() > 0) {
1124 1 : int IL = -999;
1125 2 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
1126 1 : IL = max(IL, dl->illumMaps(MapNum).TotalMapRefPoints);
1127 : }
1128 1 : dl->MapErrIndex.dimension(IL, s_surf->TotSurfaces, 0);
1129 1 : dl->mapFirstTime = false;
1130 : }
1131 :
1132 1 : auto &illumMap = dl->illumMaps(mapNum);
1133 1 : int enclNum = illumMap.enclIndex;
1134 1 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
1135 :
1136 : // Azimuth of view vector in absolute coord sys - set to zero here, because glare isn't calculated for map points
1137 : // but these are arguments to some of the functions that are shared with regular reference points, so initalize here.
1138 1 : Real64 AZVIEW = 0.0;
1139 : // View vector components in absolute coord sys
1140 1 : VIEWVC = {0.0, 0.0, 0.0};
1141 :
1142 1 : numRefPts = illumMap.TotalMapRefPoints;
1143 1 : int numExtWins = thisEnclDaylight.NumOfDayltgExtWins;
1144 :
1145 101 : for (auto &refPt : illumMap.refPts) {
1146 100 : refPt.lums[iLum_Illum] = 0.0; // Daylight illuminance at reference points (lux)
1147 200 : for (int iExtWin = 1; iExtWin <= numExtWins; ++iExtWin) {
1148 100 : refPt.winLums(iExtWin) = {0.0, 0.0};
1149 : }
1150 : }
1151 :
1152 1 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
1153 1 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
1154 :
1155 25 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
1156 24 : auto &daylFacHr = illumMap.daylFac[iHr];
1157 48 : for (int iWin = 1; iWin <= numExtWins; ++iWin) {
1158 2424 : for (int iRefPt = 1; iRefPt <= numRefPts; ++iRefPt) {
1159 7200 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
1160 19200 : daylFacHr(iWin, iRefPt)[iWinCover] = Illums();
1161 : }
1162 : }
1163 : }
1164 : }
1165 :
1166 101 : for (int IL = 1; IL <= numRefPts; ++IL) {
1167 100 : auto const &refPt = illumMap.refPts(IL);
1168 100 : Vector3<Real64> RREF = refPt.absCoords;
1169 :
1170 : // -------------
1171 : // ---------- WINDOW LOOP ----------
1172 : // -------------
1173 :
1174 200 : for (int loopwin = 1; loopwin <= numExtWins; ++loopwin) {
1175 :
1176 : // daylightingCtrlNum parameter is unused for map points
1177 100 : FigureDayltgCoeffsAtPointsSetupForWindow(state,
1178 : 0,
1179 : IL,
1180 : loopwin,
1181 : CalledFor::MapPoint,
1182 : RREF,
1183 : VIEWVC,
1184 : IWin,
1185 : IWin2,
1186 : NWX,
1187 : NWY,
1188 : W2,
1189 : W3,
1190 : W21,
1191 : W23,
1192 : LSHCAL,
1193 : InShelfSurf,
1194 : ICtrl,
1195 : ShType,
1196 : BlNum,
1197 : WNORM2,
1198 : extWinType,
1199 : IConst,
1200 : RREF2,
1201 : DWX,
1202 : DWY,
1203 : DAXY,
1204 : U2,
1205 : U23,
1206 : U21,
1207 : VIEWVC2,
1208 : is_Rectangle,
1209 : is_Triangle,
1210 : mapNum);
1211 : // ---------------------
1212 : // ---------- WINDOW ELEMENT LOOP ----------
1213 : // ---------------------
1214 100 : WinEl = 0;
1215 :
1216 1340 : for (int IX = 1; IX <= NWX; ++IX) {
1217 1240 : if (is_Rectangle) {
1218 1240 : NWYlim = NWY;
1219 0 : } else if (is_Triangle) {
1220 0 : NWYlim = NWY - IX + 1;
1221 : }
1222 :
1223 22290 : for (int IY = 1; IY <= NWYlim; ++IY) {
1224 :
1225 21050 : ++WinEl;
1226 :
1227 : // daylightingCtrlNum parameter is unused for map points
1228 21050 : FigureDayltgCoeffsAtPointsForWindowElements(state,
1229 : 0,
1230 : IL,
1231 : loopwin,
1232 : CalledFor::MapPoint,
1233 : WinEl,
1234 : IWin,
1235 : IWin2,
1236 : IX,
1237 : IY,
1238 : SkyObstructionMult,
1239 : W2,
1240 : W21,
1241 : W23,
1242 : RREF,
1243 : NWYlim,
1244 : VIEWVC2,
1245 : DWX,
1246 : DWY,
1247 : DAXY,
1248 : U2,
1249 : U23,
1250 : U21,
1251 : RWIN,
1252 : RWIN2,
1253 : Ray,
1254 : PHRAY,
1255 : LSHCAL,
1256 : COSB,
1257 : ObTrans,
1258 : TVISB,
1259 : DOMEGA,
1260 : THRAY,
1261 : hitIntObs,
1262 : hitExtObs,
1263 : WNORM2,
1264 : extWinType,
1265 : IConst,
1266 : RREF2,
1267 : is_Triangle,
1268 : TVISIntWin,
1269 : TVISIntWinDisk,
1270 : mapNum);
1271 : // -------------------
1272 : // ---------- SUN POSITION LOOP ----------
1273 : // -------------------
1274 :
1275 : // Sun position counter. Used to avoid calculating various quantities
1276 : // that do not depend on sun position.
1277 21050 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1278 21050 : ISunPos = 0;
1279 526250 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
1280 : // daylightingCtrlNum parameter is unused for map points
1281 505200 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1282 : 0,
1283 : IL,
1284 : IX,
1285 : NWX,
1286 : IY,
1287 : NWYlim,
1288 : WinEl,
1289 : IWin,
1290 : IWin2,
1291 : IHR,
1292 : ISunPos,
1293 : SkyObstructionMult,
1294 : RWIN2,
1295 : Ray,
1296 : PHRAY,
1297 : LSHCAL,
1298 : InShelfSurf,
1299 : COSB,
1300 : ObTrans,
1301 : TVISB,
1302 : DOMEGA,
1303 : ICtrl,
1304 : ShType,
1305 : BlNum,
1306 : THRAY,
1307 : WNORM2,
1308 : extWinType,
1309 : IConst,
1310 : AZVIEW,
1311 : RREF2,
1312 : hitIntObs,
1313 : hitExtObs,
1314 : CalledFor::MapPoint,
1315 : TVISIntWin,
1316 : TVISIntWinDisk,
1317 : mapNum);
1318 : } // End of hourly sun position loop, IHR
1319 : } else {
1320 0 : if (state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1321 0 : ISunPos = 0;
1322 0 : dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = true;
1323 0 : } else if (state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1324 0 : ISunPos = 1;
1325 0 : } else if (!state.dataEnvrn->SunIsUp && dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1326 0 : dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag = false;
1327 0 : ISunPos = -1;
1328 0 : } else if (!state.dataEnvrn->SunIsUp && !dl->CalcDayltgCoeffsMapPointsMySunIsUpFlag) {
1329 0 : ISunPos = -1;
1330 : }
1331 : // daylightingCtrlNum parameter is unused for map points
1332 0 : FigureDayltgCoeffsAtPointsForSunPosition(state,
1333 : 0,
1334 : IL,
1335 : IX,
1336 : NWX,
1337 : IY,
1338 : NWYlim,
1339 : WinEl,
1340 : IWin,
1341 : IWin2,
1342 0 : state.dataGlobal->HourOfDay,
1343 : ISunPos,
1344 : SkyObstructionMult,
1345 : RWIN2,
1346 : Ray,
1347 : PHRAY,
1348 : LSHCAL,
1349 : InShelfSurf,
1350 : COSB,
1351 : ObTrans,
1352 : TVISB,
1353 : DOMEGA,
1354 : ICtrl,
1355 : ShType,
1356 : BlNum,
1357 : THRAY,
1358 : WNORM2,
1359 : extWinType,
1360 : IConst,
1361 : AZVIEW,
1362 : RREF2,
1363 : hitIntObs,
1364 : hitExtObs,
1365 : CalledFor::MapPoint,
1366 : TVISIntWin,
1367 : TVISIntWinDisk,
1368 : mapNum);
1369 : }
1370 : } // End of window Y-element loop, IY
1371 : } // End of window X-element loop, IX
1372 :
1373 100 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
1374 : // Loop again over hourly sun positions and calculate daylight factors by adding
1375 : // direct and inter-reflected illum components, then dividing by exterior horiz illum.
1376 : // Also calculate corresponding glare factors.
1377 100 : ILB = IL;
1378 2500 : for (int IHR = 1; IHR <= Constant::iHoursInDay; ++IHR) {
1379 2400 : FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, IHR, IWin, loopwin, ICtrl);
1380 : } // End of sun position loop, IHR
1381 : } else {
1382 0 : ILB = IL;
1383 0 : FigureMapPointDayltgFactorsToAddIllums(state, mapNum, ILB, state.dataGlobal->HourOfDay, IWin, loopwin, ICtrl);
1384 : }
1385 :
1386 : } // End of window loop, loopwin - IWin
1387 :
1388 100 : } // End of reference point loop, IL
1389 1 : }
1390 :
1391 1465 : void FigureDayltgCoeffsAtPointsSetupForWindow(EnergyPlusData &state,
1392 : int const daylightCtrlNum, // zero if called for map points
1393 : int const iRefPoint,
1394 : int const loopwin,
1395 : CalledFor const CalledFrom, // indicate which type of routine called this routine
1396 : Vector3<Real64> const &RREF, // Location of a reference point in absolute coordinate system
1397 : Vector3<Real64> const &VIEWVC, // View vector in absolute coordinate system
1398 : int &IWin,
1399 : int &IWin2,
1400 : int &NWX,
1401 : int &NWY,
1402 : Vector3<Real64> &W2, // Second vertex of window
1403 : Vector3<Real64> &W3, // Third vertex of window
1404 : Vector3<Real64> &W21, // Vector from window vertex 2 to window vertex 1
1405 : Vector3<Real64> &W23, // Vector from window vertex 2 to window vertex 3
1406 : int &LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
1407 : int &InShelfSurf, // Inside daylighting shelf surface number
1408 : int &ICtrl, // Window control counter
1409 : WinShadingType &ShType, // Window shading type
1410 : int &BlNum, // Window blind number
1411 : Vector3<Real64> &WNORM2, // Unit vector normal to window
1412 : ExtWinType &extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1413 : int &IConst, // Construction counter
1414 : Vector3<Real64> &RREF2, // Location of virtual reference point in absolute coordinate system
1415 : Real64 &DWX, // Horizontal dimension of window element (m)
1416 : Real64 &DWY, // Vertical dimension of window element (m)
1417 : Real64 &DAXY, // Area of window element
1418 : Vector3<Real64> &U2, // Second vertex of window for TDD:DOME (if exists)
1419 : Vector3<Real64> &U23, // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
1420 : Vector3<Real64> &U21, // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
1421 : Vector3<Real64> &VIEWVC2, // Virtual view vector in absolute coordinate system
1422 : bool &is_Rectangle, // True if window is rectangular
1423 : bool &is_Triangle, // True if window is triangular
1424 : int const MapNum)
1425 : {
1426 : // SUBROUTINE INFORMATION:
1427 : // AUTHOR B. Griffith
1428 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
1429 :
1430 : // PURPOSE OF THIS SUBROUTINE:
1431 : // collect code to setup calculations for each window for daylighting coefficients
1432 :
1433 : // METHODOLOGY EMPLOYED:
1434 : // switch as need to serve both reference points and map points based on calledFrom
1435 1465 : auto &dl = state.dataDayltg;
1436 1465 : auto &s_surf = state.dataSurface;
1437 :
1438 : int ShelfNum; // Daylighting shelf object number
1439 : int NDIVX; // Number of window x divisions for daylighting calc
1440 : int NDIVY; // Number of window y divisions for daylighting calc
1441 : Real64 ALF; // Distance from reference point to window plane (m)
1442 : Real64 D1a; // Projection of vector from window origin to reference
1443 : // on window X axis (m)
1444 : Real64 D1b; // Projection of vector from window origin to reference
1445 : // on window Y axis (m)
1446 : Real64 SolidAngExtWin; // Approx. solid angle subtended by an ext. window wrt ref pt
1447 : Real64 SolidAngMinIntWin; // Approx. smallest solid angle subtended by an int. window wrt ref pt
1448 : Real64 SolidAngRatio; // Ratio of SolidAngExtWin and SolidAngMinIntWin
1449 : Real64 SinCornerAng; // For triangle, sine of corner angle of window element
1450 :
1451 1465 : int zoneNum = 0; // zone number
1452 1465 : int enclNum = 0; // enclosure number
1453 :
1454 1465 : Vector3<Real64> W1 = {0.0, 0.0, 0.0};
1455 1465 : Vector3<Real64> WC = {0.0, 0.0, 0.0};
1456 :
1457 1465 : if (CalledFrom == CalledFor::RefPoint) {
1458 1365 : auto &daylCtrl = dl->daylightControl(daylightCtrlNum);
1459 1365 : daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAng = 0.0;
1460 1365 : daylCtrl.refPts(iRefPoint).extWins(loopwin).solidAngWtd = 0.0;
1461 1365 : zoneNum = daylCtrl.zoneIndex;
1462 1365 : enclNum = daylCtrl.enclIndex;
1463 100 : } else if (CalledFrom == CalledFor::MapPoint) {
1464 100 : assert(MapNum > 0);
1465 100 : auto const &illumMap = dl->illumMaps(MapNum);
1466 100 : zoneNum = illumMap.zoneIndex;
1467 100 : enclNum = illumMap.enclIndex;
1468 : }
1469 1465 : IWin = dl->enclDaylight(enclNum).DayltgExtWinSurfNums(loopwin);
1470 :
1471 1465 : auto &surf = s_surf->Surface(IWin);
1472 1465 : auto &surfWin = s_surf->SurfaceWindow(IWin);
1473 :
1474 1465 : if (s_surf->Surface(surf.BaseSurf).SolarEnclIndex == enclNum) {
1475 1465 : extWinType = ExtWinType::InZone;
1476 : } else {
1477 0 : extWinType = ExtWinType::AdjZone;
1478 : }
1479 :
1480 1465 : IConst = s_surf->SurfActiveConstruction(IWin);
1481 :
1482 : // For thermochromic windows, the daylight and glare factors are calculated for a base window cosntruction
1483 : // at base TC layer temperature. During each time step calculations at DayltgInteriorIllum,
1484 : // DayltgInteriorMapIllum, and DayltgGlare, the daylight and glare factors are adjusted by the visible
1485 : // transmittance ratio = VT of actual TC window based on last hour TC layer temperature / VT of the base TC window
1486 1465 : if (state.dataConstruction->Construct(IConst).isTCWindow) {
1487 : // For thermochromic windows, use the base window construction at base temperature of the TC layer
1488 0 : IConst = state.dataConstruction->Construct(IConst).TCMasterConstrNum;
1489 : }
1490 :
1491 1465 : ICtrl = surf.activeWindowShadingControl;
1492 1465 : ShType = WinShadingType::NoShade; // 'NOSHADE'
1493 1465 : BlNum = 0;
1494 : // ScNum = 0; //Unused Set but never used
1495 1465 : if (surf.HasShadeControl) ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
1496 1465 : if (ANY_BLIND(ShType)) BlNum = s_surf->surfShades(IWin).blind.matNum;
1497 : // ScNum = SurfaceWindow( IWin ).ScreenNumber; //Unused Set but never used
1498 :
1499 1465 : ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
1500 1465 : if (ShelfNum > 0) {
1501 0 : InShelfSurf =
1502 0 : state.dataDaylightingDevicesData->Shelf(s_surf->SurfDaylightingShelfInd(IWin)).InSurf; // Inside daylighting shelf present if > 0
1503 : } else {
1504 1465 : InShelfSurf = 0;
1505 : }
1506 :
1507 1465 : is_Rectangle = false;
1508 1465 : is_Triangle = false;
1509 1465 : if (surf.Sides == 3) is_Triangle = true;
1510 1465 : if (surf.Sides == 4) is_Rectangle = true;
1511 :
1512 1465 : if (is_Rectangle) {
1513 : // Vertices of window (numbered counter-clockwise starting at upper left as viewed
1514 : // from inside of room). Assumes original vertices are numbered counter-clockwise from
1515 : // upper left as viewed from outside.
1516 1465 : W3 = surf.Vertex(2);
1517 1465 : W2 = surf.Vertex(3);
1518 1465 : W1 = surf.Vertex(4);
1519 0 : } else if (is_Triangle) {
1520 0 : W3 = surf.Vertex(2);
1521 0 : W2 = surf.Vertex(3);
1522 0 : W1 = surf.Vertex(1);
1523 : }
1524 :
1525 : // Shade/blind calculation flag
1526 1465 : LSHCAL = 0;
1527 :
1528 : // Visible transmittance at normal incidence
1529 1465 : s_surf->SurfWinVisTransSelected(IWin) = Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
1530 : // For windows with switchable glazing, ratio of visible transmittance at normal
1531 : // incidence for fully switched (dark) state to that of unswitched state
1532 1465 : s_surf->SurfWinVisTransRatio(IWin) = 1.0;
1533 1465 : if (ICtrl > 0) {
1534 0 : if (ShType == WinShadingType::SwitchableGlazing) {
1535 0 : int IConstShaded = surf.activeShadedConstruction; // Shaded construction counter
1536 0 : s_surf->SurfWinVisTransRatio(IWin) =
1537 0 : General::SafeDivide(Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef),
1538 0 : Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef));
1539 : }
1540 : }
1541 :
1542 : // Unit vectors from window vertex 2 to 1 and 2 to 3,
1543 : // center point of window, and vector from ref pt to center of window
1544 1465 : W21 = W1 - W2;
1545 1465 : W23 = W3 - W2;
1546 1465 : Real64 HW = W21.magnitude();
1547 1465 : Real64 WW = W23.magnitude();
1548 1465 : if (is_Rectangle) {
1549 1465 : WC = W2 + (W23 + W21) / 2.0;
1550 0 : } else if (is_Triangle) {
1551 0 : WC = W2 + (W23 + W21) / 3.0;
1552 : }
1553 1465 : s_surf->SurfaceWindow(IWin).WinCenter = WC;
1554 1465 : Vector3<Real64> REFWC = WC - RREF;
1555 : // Unit vectors
1556 1465 : W21 /= HW;
1557 1465 : W23 /= WW;
1558 :
1559 : // Unit vector normal to window (pointing away from room)
1560 1465 : Vector3<Real64> WNORM = surf.lcsz;
1561 :
1562 : // Initialize number of window elements
1563 1465 : NDIVX = 40; // Does this mean that windows are split into 1,600 points for daylighting? WHYYYYYY?
1564 1465 : NDIVY = 40;
1565 :
1566 : // Distance from ref point to window plane
1567 1465 : ALF = std::abs(dot(WNORM, REFWC));
1568 1465 : if (CalledFrom == CalledFor::RefPoint) {
1569 : // Check if ref point to close to window due to input error (0.1524 m below is 0.5 ft)
1570 1365 : if (ALF < 0.1524 && extWinType == ExtWinType::InZone) {
1571 : // Ref pt is close to window plane. Get vector from window
1572 : // origin to projection of ref pt on window plane.
1573 0 : Vector3<Real64> W2REF = RREF + ALF * WNORM - W2;
1574 :
1575 0 : D1a = dot(W2REF, W23);
1576 0 : D1b = dot(W2REF, W21);
1577 :
1578 : // ! Error message if ref pt is too close to window.
1579 0 : if (D1a > 0.0 && D1b > 0.0 && D1b <= HW && D1a <= WW) {
1580 0 : ShowSevereError(
1581 : state,
1582 0 : format("CalcDaylightCoeffRefPoints: Daylighting calculation cannot be done for Daylighting:Controls={} because reference point "
1583 : "#{} is less than 0.15m (6\") from window plane {}",
1584 0 : dl->daylightControl(daylightCtrlNum).Name,
1585 : iRefPoint,
1586 0 : surf.Name));
1587 0 : ShowContinueError(state, format("Distance=[{:.5R}]. This is too close; check position of reference point.", ALF));
1588 0 : ShowFatalError(state, "Program terminates due to preceding condition.");
1589 : }
1590 1365 : } else if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
1591 0 : if (dl->RefErrIndex(iRefPoint, IWin) == 0) { // only show error message once
1592 0 : ShowWarningError(state,
1593 0 : format("CalcDaylightCoeffRefPoints: For Daylghting:Controls=\"{}\" External Window=\"{}\"in Zone=\"{}\" reference "
1594 : "point is less than 0.15m (6\") from window plane ",
1595 0 : dl->daylightControl(daylightCtrlNum).Name,
1596 0 : surf.Name,
1597 0 : state.dataHeatBal->Zone(surf.Zone).Name));
1598 0 : ShowContinueError(state,
1599 0 : format("Distance=[{:.1R} m] to ref point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Daylighting Calcs may result.",
1600 : ALF,
1601 0 : RREF.x,
1602 0 : RREF.y,
1603 0 : RREF.z));
1604 0 : dl->RefErrIndex(iRefPoint, IWin) = 1;
1605 : }
1606 : }
1607 100 : } else if (CalledFrom == CalledFor::MapPoint) {
1608 100 : if (ALF < 0.1524 && extWinType == ExtWinType::AdjZone) {
1609 0 : if (dl->MapErrIndex(iRefPoint, IWin) == 0) { // only show error message once
1610 0 : ShowWarningError(state,
1611 0 : format("CalcDaylightCoeffMapPoints: For Zone=\"{}\" External Window=\"{}\"in Zone=\"{}\" map point is less than "
1612 : "0.15m (6\") from window plane ",
1613 0 : state.dataHeatBal->Zone(zoneNum).Name,
1614 0 : surf.Name,
1615 0 : state.dataHeatBal->Zone(surf.Zone).Name));
1616 0 : ShowContinueError(
1617 : state,
1618 0 : format("Distance=[{:.1R} m] map point=[{:.1R},{:.1R},{:.1R}], Inaccuracy in Map Calcs may result.", ALF, RREF.x, RREF.y, RREF.z));
1619 0 : dl->MapErrIndex(iRefPoint, IWin) = 1;
1620 : }
1621 : }
1622 : }
1623 : // Number of window elements in X and Y for daylighting calculation
1624 1465 : if (ALF > 0.1524) {
1625 1455 : NDIVX = 1 + int(4.0 * WW / ALF);
1626 1455 : NDIVY = 1 + int(4.0 * HW / ALF);
1627 : }
1628 :
1629 1465 : if (extWinType == ExtWinType::AdjZone) {
1630 : // Adjust number of exterior window elements to give acceptable number of rays through
1631 : // interior windows in the zone (for accuracy of interior window daylighting calculation)
1632 0 : SolidAngExtWin = General::SafeDivide(((surf.Area + s_surf->SurfWinDividerArea(IWin)) / surf.Multiplier), pow_2(ALF));
1633 0 : SolidAngMinIntWin = dl->enclDaylight(enclNum).MinIntWinSolidAng;
1634 0 : SolidAngRatio = max(1.0, SolidAngExtWin / SolidAngMinIntWin);
1635 0 : NDIVX *= std::sqrt(SolidAngRatio);
1636 0 : NDIVY *= std::sqrt(SolidAngRatio);
1637 : }
1638 :
1639 1465 : NWX = min(40, NDIVX);
1640 1465 : NWY = min(40, NDIVY);
1641 :
1642 : // Discretization of triangle is simpler if NWX = NWY
1643 1465 : if (is_Triangle) {
1644 0 : NWX = max(NWX, NWY);
1645 0 : NWY = NWX;
1646 : }
1647 :
1648 : // Edge lengths of window elements
1649 1465 : DWX = WW / NWX;
1650 1465 : DWY = HW / NWY;
1651 :
1652 : // Azimuth and altitude of window normal
1653 1465 : surfWin.phi = std::asin(WNORM.z);
1654 1465 : surfWin.theta = (std::abs(WNORM.x) > 1.0e-5 || std::abs(WNORM.y) > 1.0e-5) ? std::atan2(WNORM.y, WNORM.x) : 0.0;
1655 :
1656 : // Recalculation of values for TDD:DOME
1657 1465 : if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
1658 :
1659 : // Look up the TDD:DOME object
1660 0 : int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
1661 0 : IWin2 = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
1662 :
1663 0 : auto &surf2 = s_surf->Surface(IWin2);
1664 0 : auto &surfWin2 = s_surf->SurfaceWindow(IWin2);
1665 :
1666 : // Calculate reference point coords relative to the diffuser coordinate system
1667 : // W21, W23, and WNORM are the unit vectors
1668 0 : Vector3<Real64> REFD = {dot(REFWC, W21), dot(REFWC, W23), dot(REFWC, WNORM)};
1669 :
1670 : // Calculate view vector coords relative to the diffuser coordinate system
1671 0 : Vector3<Real64> VIEWVD = {dot(VIEWVC, W21), dot(VIEWVC, W23), dot(VIEWVC, WNORM)};
1672 :
1673 0 : Vector3<Real64> U3 = surf2.Vertex(2);
1674 0 : U2 = surf2.Vertex(3);
1675 0 : Vector3<Real64> U1;
1676 :
1677 0 : if (surf2.Sides == 4) {
1678 : // Vertices of window (numbered counter-clockwise starting
1679 : // at upper left as viewed from inside of room)
1680 : // Assumes original vertices are numbered counter-clockwise from
1681 : // upper left as viewed from outside.
1682 0 : U3 = surf2.Vertex(2);
1683 0 : U2 = surf2.Vertex(3);
1684 0 : U1 = surf2.Vertex(4);
1685 0 : } else if (surf2.Sides == 3) {
1686 0 : U3 = surf2.Vertex(2);
1687 0 : U2 = surf2.Vertex(3);
1688 0 : U1 = surf2.Vertex(1);
1689 : }
1690 :
1691 : // Unit vectors from window vertex 2 to 1 and 2 to 3,
1692 : // center point of window, and vector from ref pt to center of window
1693 0 : U21 = U1 - U2;
1694 0 : U23 = U3 - U2;
1695 0 : HW = U21.magnitude();
1696 0 : WW = U23.magnitude();
1697 0 : if (surf2.Sides == 4) {
1698 0 : WC = U2 + (U23 + U21) / 2.0;
1699 0 : } else if (surf2.Sides == 3) {
1700 0 : WC = U2 + (U23 + U21) / 3.0;
1701 : }
1702 0 : s_surf->SurfaceWindow(IWin2).WinCenter = WC;
1703 : // Unit vectors
1704 0 : U21 /= HW;
1705 0 : U23 /= WW;
1706 :
1707 : // Unit vector normal to dome (pointing away from TDD)
1708 : // These are specific to the exterior.
1709 : // NOTE: Preserve WNORM for later in the code.
1710 0 : WNORM2 = cross(U21, U23).normalize();
1711 :
1712 : // Azimuth and altitude of dome normal
1713 : // These are specific to the exterior.
1714 0 : surfWin2.phi = std::asin(WNORM2.z);
1715 0 : surfWin2.theta = (std::abs(WNORM2.x) > 1.0e-5 || std::abs(WNORM2.y) > 1.0e-5) ? std::atan2(WNORM2.y, WNORM2.x) : 0.0;
1716 :
1717 : // Calculate new virtual reference point coords relative to dome coord system
1718 : // W21, W23, and WNORM2 are now the unit vectors for the dome coord system
1719 0 : REFWC = REFD.x * U21 + REFD.y * U23 + REFD.z * WNORM2;
1720 0 : RREF2 = WC - REFWC;
1721 :
1722 : // Calculate new virtual view vector coords relative to dome coord system
1723 0 : VIEWVC2 = VIEWVD.x * U21 + VIEWVD.y * U23 + VIEWVD.z * WNORM2;
1724 :
1725 : // Copy several values from the diffuser so that DayltgInterReflectedIllum works correctly
1726 : // These are specific to the interior.
1727 0 : surfWin2.rhoCeilingWall = surfWin.rhoCeilingWall;
1728 0 : surfWin2.rhoFloorWall = surfWin.rhoFloorWall;
1729 0 : surfWin2.fractionUpgoing = surfWin.fractionUpgoing;
1730 0 : surfWin2.glazedFrac = surfWin.glazedFrac;
1731 :
1732 0 : } else {
1733 : // This is not a TDD:DIFFUSER. Make sure nothing is messed up for a regular window.
1734 1465 : IWin2 = IWin;
1735 1465 : WNORM2 = WNORM;
1736 1465 : RREF2 = RREF;
1737 1465 : VIEWVC2 = VIEWVC;
1738 :
1739 1465 : U2 = W2;
1740 1465 : U21 = W21;
1741 1465 : U23 = W23;
1742 : }
1743 :
1744 : // Initialize bsdf daylighting coefficients here. Only one time initialization
1745 1465 : if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) {
1746 0 : if (!state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized) {
1747 0 : int NRefPts = 0;
1748 0 : if (CalledFrom == CalledFor::MapPoint) {
1749 0 : NRefPts = dl->illumMaps(MapNum).TotalMapRefPoints;
1750 0 : } else if (CalledFrom == CalledFor::RefPoint) {
1751 0 : NRefPts = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
1752 : }
1753 0 : InitializeCFSDaylighting(state, daylightCtrlNum, IWin, NWX, NWY, RREF, NRefPts, iRefPoint, CalledFrom, MapNum);
1754 : // if ((WinEl == (NWX * NWY)).and.(CalledFrom == CalledForMapPoint).and.(NRefPts == iRefPoint)) then
1755 0 : if ((CalledFrom == CalledFor::MapPoint) && (NRefPts == iRefPoint)) {
1756 0 : state.dataBSDFWindow->ComplexWind(IWin).DaylightingInitialized = true;
1757 : }
1758 : }
1759 : }
1760 :
1761 1465 : int iHrBeg = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : 1;
1762 1465 : int iHrEnd = state.dataSysVars->DetailedSolarTimestepIntegration ? state.dataGlobal->HourOfDay : Constant::iHoursInDay;
1763 :
1764 5483 : for (int iHr = iHrBeg; iHr <= iHrEnd; ++iHr) {
1765 : // Initialize sky and sun components of direct illuminance (arrays EDIRSK, EDIRSU, EDIRSUdisk)
1766 : // and average window luminance (arrays AVWLSK, AVWLSU, AVWLSUdisk), at ref pt.
1767 16072 : dl->dirIllum(iHr)[iWinCover_Bare] = dl->dirIllum(iHr)[iWinCover_Shaded] = Illums();
1768 16072 : dl->avgWinLum(iHr)[iWinCover_Bare] = dl->avgWinLum(iHr)[iWinCover_Shaded] = Illums();
1769 : }
1770 :
1771 1465 : if (CalledFrom == CalledFor::RefPoint) {
1772 : // Initialize solid angle subtended by window wrt ref pt
1773 : // and solid angle weighted by glare position factor
1774 1365 : s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAng = 0.0;
1775 1365 : s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd = 0.0;
1776 : }
1777 : // Area of window element
1778 1465 : if (is_Rectangle) {
1779 1465 : DAXY = DWX * DWY;
1780 0 : } else if (is_Triangle) {
1781 0 : SinCornerAng = std::sqrt(1.0 - pow_2(dot(W21, W23)));
1782 0 : DAXY = DWX * DWY * SinCornerAng;
1783 : }
1784 1465 : }
1785 :
1786 368101 : void FigureDayltgCoeffsAtPointsForWindowElements(
1787 : EnergyPlusData &state,
1788 : int const daylightCtrlNum, // Current daylighting control number (only used when called from RefPoint)
1789 : int const iRefPoint,
1790 : int const loopwin,
1791 : CalledFor const CalledFrom, // indicate which type of routine called this routine
1792 : int const WinEl, // Current window element number
1793 : int const IWin,
1794 : int const IWin2,
1795 : int const iXelement,
1796 : int const iYelement,
1797 : Real64 &SkyObstructionMult,
1798 : Vector3<Real64> const &W2, // Second vertex of window
1799 : Vector3<Real64> const &W21, // Vector from window vertex 2 to window vertex 1
1800 : Vector3<Real64> const &W23, // Vector from window vertex 2 to window vertex 3
1801 : Vector3<Real64> const &RREF, // Location of a reference point in absolute coordinate system
1802 : int const NWYlim, // For triangle, largest NWY for a given IX
1803 : Vector3<Real64> const &VIEWVC2, // Virtual view vector in absolute coordinate system
1804 : Real64 const DWX, // Horizontal dimension of window element (m)
1805 : Real64 const DWY, // Vertical dimension of window element (m)
1806 : Real64 const DAXY, // Area of window element
1807 : Vector3<Real64> const &U2, // Second vertex of window for TDD:DOME (if exists)
1808 : Vector3<Real64> const &U23, // Vector from window vertex 2 to window vertex 3 for TDD:DOME (if exists)
1809 : Vector3<Real64> const &U21, // Vector from window vertex 2 to window vertex 1 for TDD:DOME (if exists)
1810 : Vector3<Real64> &RWIN, // Center of a window element for TDD:DOME (if exists) in abs coord sys
1811 : Vector3<Real64> &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
1812 : Vector3<Real64> &Ray, // Unit vector along ray from reference point to window element
1813 : Real64 &PHRAY, // Altitude of ray from reference point to window element (radians)
1814 : int &LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
1815 : Real64 &COSB, // Cosine of angle between window outward normal and ray from reference point to window element
1816 : Real64 &ObTrans, // Product of solar transmittances of exterior obstructions hit by ray
1817 : Real64 &TVISB, // Visible transmittance of window for COSB angle of incidence (times light well
1818 : Real64 &DOMEGA, // Solid angle subtended by window element wrt reference point (steradians)
1819 : Real64 &THRAY, // Azimuth of ray from reference point to window element (radians)
1820 : bool &hitIntObs, // True iff interior obstruction hit
1821 : bool &hitExtObs, // True iff ray from ref pt to ext win hits an exterior obstruction
1822 : Vector3<Real64> const &WNORM2, // Unit vector normal to window
1823 : ExtWinType const extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
1824 : int const IConst, // Construction counter
1825 : Vector3<Real64> const &RREF2, // Location of virtual reference point in absolute coordinate system
1826 : bool const is_Triangle,
1827 : Real64 &TVISIntWin, // Visible transmittance of int win at COSBIntWin for light from ext win
1828 : Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
1829 : int const MapNum)
1830 : {
1831 :
1832 : // SUBROUTINE INFORMATION:
1833 : // AUTHOR B. Griffith
1834 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
1835 :
1836 : // PURPOSE OF THIS SUBROUTINE:
1837 : // collect code to do calculations for each window element for daylighting coefficients
1838 :
1839 : // REFERENCES:
1840 : // switch as need to serve both reference points and map points based on calledFrom
1841 368101 : auto &dl = state.dataDayltg;
1842 368101 : auto &s_surf = state.dataSurface;
1843 :
1844 : Real64 RR; // Distance from ref point to intersection of view vector
1845 : // and plane normal to view vector and window element (m)
1846 : Real64 ASQ; // Square of distance from above intersection to window element (m2)
1847 : Real64 YD; // Vertical displacement of window element wrt ref point
1848 :
1849 : Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
1850 : // exterior window element or between ref pt and sun
1851 :
1852 : // Local complex fenestration variables
1853 : Real64 TransBeam; // Obstructions transmittance for incoming BSDF rays (temporary variable)
1854 :
1855 368101 : auto &surfWin = s_surf->SurfaceWindow(IWin);
1856 :
1857 368101 : ++LSHCAL;
1858 368101 : SkyObstructionMult = 1.0;
1859 :
1860 : // Center of win element in absolute coord sys
1861 368101 : RWIN = W2 + (double(iXelement) - 0.5) * W23 * DWX + (double(iYelement) - 0.5) * W21 * DWY;
1862 :
1863 : // Center of win element on TDD:DOME in absolute coord sys
1864 : // If no TDD, RWIN2 = RWIN
1865 368101 : RWIN2 = U2 + (double(iXelement) - 0.5) * U23 * DWX + (double(iYelement) - 0.5) * U21 * DWY;
1866 :
1867 : // Distance between ref pt and window element
1868 368101 : Real64 DIS = distance(RWIN, RREF);
1869 :
1870 : // Unit vector along ray from ref pt to element
1871 368101 : Ray = (RWIN - RREF) / DIS;
1872 :
1873 : // Cosine of angle between ray and window outward normal
1874 368101 : COSB = dot(WNORM2, Ray);
1875 :
1876 : // If COSB > 0, direct light from window can reach ref pt. Otherwise go to loop
1877 : // over sun position and calculate inter-reflected component of illuminance
1878 368101 : if (COSB <= 0.0) return;
1879 :
1880 : // Azimuth (-pi to pi) and altitude (-pi/2 to pi/2) of ray. Azimuth = 0 is along east.
1881 368021 : PHRAY = std::asin(Ray.z);
1882 368021 : if (std::abs(Ray.x) > 1.0e-5 || std::abs(Ray.y) > 1.0e-5) {
1883 368021 : THRAY = std::atan2(Ray.y, Ray.x);
1884 : } else {
1885 0 : THRAY = 0.0;
1886 : }
1887 :
1888 : // Solid angle subtended by element wrt ref pt.
1889 368021 : Real64 DAXY1 = DAXY; // For triangle, area of window element at end of column
1890 : // For triangle, at end of Y column only one half of parallelopiped's area contributes
1891 368021 : if (is_Triangle && iYelement == NWYlim) DAXY1 = 0.5 * DAXY;
1892 368021 : DOMEGA = DAXY1 * COSB / (DIS * DIS);
1893 :
1894 : // Calculate position factor (used in glare calculation) for this
1895 : // win element / ref pt / view-vector combination
1896 368021 : Real64 POSFAC = 0.0;
1897 :
1898 : // Distance from ref pt to intersection of view vector and plane
1899 : // normal to view vector containing the window element
1900 :
1901 368021 : if (CalledFrom == CalledFor::RefPoint) {
1902 346971 : RR = DIS * dot(Ray, VIEWVC2);
1903 346971 : if (RR > 0.0) {
1904 : // Square of distance from above intersection point to win element
1905 347 : ASQ = DIS * DIS - RR * RR;
1906 : // Vertical displacement of win element wrt ref pt
1907 347 : YD = RWIN2.z - RREF2.z;
1908 : // Horizontal and vertical displacement ratio and position factor
1909 347 : Real64 XR = std::sqrt(std::abs(ASQ - YD * YD)) / RR;
1910 347 : Real64 YR = std::abs(YD / RR);
1911 347 : POSFAC = DayltgGlarePositionFactor(XR, YR);
1912 : }
1913 : }
1914 :
1915 368021 : hitIntObs = false;
1916 368021 : int IntWinHitNum = 0; // Surface number of interior window that is intersected
1917 368021 : bool hitIntWin = false; // Ray from ref pt passes through interior window
1918 368021 : TVISIntWinDisk = 0.0; // Init Value
1919 368021 : TVISIntWin = 0.0;
1920 :
1921 368021 : Vector3<Real64> HitPtIntWin = {0.0, 0.0, 0.0};
1922 368021 : auto const &surf = s_surf->Surface(IWin);
1923 368021 : if (surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
1924 : // Look up the TDD:DOME object
1925 0 : int PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
1926 : // Unshaded visible transmittance of TDD for a single ray from sky/ground element
1927 0 : TVISB = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
1928 :
1929 : } else { // Regular window
1930 368021 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
1931 : // Vis trans of glass for COSB incidence angle
1932 368021 : TVISB = Window::POLYF(COSB, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
1933 : } else {
1934 : // Complex fenestration needs to use different equation for visible transmittance. That will be calculated later
1935 : // in the code since it depends on different incoming directions. For now, just put zero to differentiate from
1936 : // regular windows
1937 0 : TVISB = 0.0;
1938 : }
1939 368021 : if (extWinType == ExtWinType::AdjZone) {
1940 0 : int zoneNum = 0;
1941 0 : if (CalledFrom == CalledFor::RefPoint) {
1942 0 : zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
1943 0 : } else if (CalledFrom == CalledFor::MapPoint) {
1944 0 : assert(MapNum > 0);
1945 0 : zoneNum = dl->illumMaps(MapNum).zoneIndex;
1946 : }
1947 : // Does ray pass through an interior window in zone (ZoneNum) containing the ref point?
1948 0 : for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
1949 0 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
1950 0 : for (int IntWin = thisSpace.WindowSurfaceFirst; IntWin <= thisSpace.WindowSurfaceLast; ++IntWin) {
1951 0 : auto const &surfIntWin = s_surf->Surface(IntWin);
1952 : // in develop this was Surface(IntWin).Class == SurfaceClass::Window && Surface(IntWin).ExtBoundCond >= 1
1953 0 : if (surfIntWin.ExtBoundCond < 1) continue;
1954 :
1955 0 : if (s_surf->Surface(surfIntWin.ExtBoundCond).Zone != surf.Zone) continue;
1956 :
1957 0 : hitIntWin = PierceSurface(state, IntWin, RREF, Ray, HitPtIntWin);
1958 0 : if (hitIntWin) {
1959 0 : IntWinHitNum = IntWin;
1960 0 : COSBIntWin = dot(surfIntWin.OutNormVec, Ray);
1961 0 : if (COSBIntWin <= 0.0) {
1962 0 : hitIntWin = false;
1963 0 : IntWinHitNum = 0;
1964 0 : continue;
1965 : }
1966 0 : TVISIntWin = Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWin.Construction).TransVisBeamCoef);
1967 0 : TVISB *= TVISIntWin;
1968 0 : break; // Ray passes thru interior window; exit from DO loop
1969 : }
1970 : }
1971 : } // End of loop over surfaces in zone ZoneNum
1972 :
1973 0 : if (!hitIntWin) {
1974 : // Ray does not pass through an int win in ZoneNum. Therefore, it hits the opaque part
1975 : // of a surface between ref point in ZoneNum and ext win element in adjacent zone.
1976 0 : hitIntObs = true;
1977 : }
1978 : } // End of check if this is an ext win in an adjacent zone
1979 : } // End of check if TDD:Diffuser or regular exterior window or complex fenestration
1980 :
1981 : // Check for interior obstructions
1982 368021 : if (extWinType == ExtWinType::InZone && !hitIntObs) {
1983 : // Check for obstruction between reference point and window element
1984 : // Returns hitIntObs = true iff obstruction is hit
1985 : // (Example of interior obstruction is a wall in an L-shaped room that lies
1986 : // between reference point and window.)
1987 368021 : hitIntObs = DayltgHitInteriorObstruction(state, IWin, RREF, RWIN);
1988 : }
1989 :
1990 368021 : if (extWinType == ExtWinType::AdjZone && IntWinHitNum > 0 && !hitIntObs) {
1991 : // Check for obstruction between ref point and interior window through which ray passes
1992 0 : hitIntObs = DayltgHitInteriorObstruction(state, IntWinHitNum, RREF, HitPtIntWin);
1993 0 : if (!hitIntObs) {
1994 : // Check for obstruction between intersection point on int window and ext win element
1995 0 : hitIntObs = DayltgHitBetWinObstruction(state, IntWinHitNum, IWin, HitPtIntWin, RWIN);
1996 : }
1997 : }
1998 368021 : if (CalledFrom == CalledFor::RefPoint) {
1999 : // Glare calculations only done for regular reference points, not for maps
2000 346971 : if (!hitIntObs) {
2001 346971 : if (extWinType == ExtWinType::InZone || (extWinType == ExtWinType::AdjZone && hitIntWin)) {
2002 : // Increment solid angle subtended by portion of window above ref pt
2003 346971 : surfWin.refPts(iRefPoint).solidAng += DOMEGA;
2004 346971 : dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAng += DOMEGA;
2005 : // Increment position-factor-modified solid angle
2006 346971 : surfWin.refPts(iRefPoint).solidAngWtd += DOMEGA * POSFAC;
2007 346971 : dl->daylightControl(daylightCtrlNum).refPts(iRefPoint).extWins(loopwin).solidAngWtd += DOMEGA * POSFAC;
2008 : }
2009 : }
2010 : }
2011 368021 : if (hitIntObs) ObTrans = 0.0;
2012 :
2013 368021 : hitExtObs = false;
2014 368021 : if (!hitIntObs) {
2015 : // No interior obstruction was hit.
2016 : // Check for exterior obstructions between window element and sky/ground.
2017 : // Get product of transmittances of obstructions hit by ray.
2018 : // ObTrans = 1.0 will be returned if no exterior obstructions are hit.
2019 :
2020 368021 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
2021 : // the IHR (now HourOfDay) here is/was not correct, this is outside of hour loop
2022 : // the hour is used to query schedule for transmission , not sure what to do
2023 : // it will work for detailed and never did work correctly before.
2024 368021 : ObTrans = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin2, RWIN2, Ray);
2025 368021 : if (ObTrans < 1.0) hitExtObs = true;
2026 : } else {
2027 : // Transmittance from exterior obstruction surfaces is calculated here. This needs to be done for each timestep
2028 : // in order to account for changes in exterior surface transmittances
2029 0 : int CplxFenState = surfWin.ComplexFen.CurrentState;
2030 0 : auto &complexWinDayltgGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CplxFenState);
2031 0 : int NReflSurf = 0; // Number of blocked beams for complex fenestration
2032 0 : if (CalledFrom == CalledFor::RefPoint) {
2033 0 : NReflSurf = complexWinDayltgGeom.RefPoint(iRefPoint).NReflSurf(WinEl);
2034 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2035 0 : NReflSurf = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).NReflSurf(WinEl);
2036 : }
2037 : int RayIndex;
2038 0 : for (int ICplxFen = 1; ICplxFen <= NReflSurf; ++ICplxFen) {
2039 0 : if (CalledFrom == CalledFor::RefPoint) {
2040 0 : RayIndex = complexWinDayltgGeom.RefPoint(iRefPoint).RefSurfIndex(ICplxFen, WinEl);
2041 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2042 0 : RayIndex = complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefSurfIndex(ICplxFen, WinEl);
2043 : }
2044 0 : Vector3<Real64> RayVector = state.dataBSDFWindow->ComplexWind(IWin).Geom(CplxFenState).sInc(RayIndex);
2045 : // It will get product of all transmittances
2046 0 : TransBeam = DayltgHitObstruction(state, state.dataGlobal->HourOfDay, IWin, RWIN, RayVector);
2047 : // IF (TransBeam > 0.0d0) ObTrans = TransBeam
2048 0 : if (CalledFrom == CalledFor::RefPoint) {
2049 0 : complexWinDayltgGeom.RefPoint(iRefPoint).TransOutSurf(ICplxFen, WinEl) = TransBeam;
2050 0 : } else if (CalledFrom == CalledFor::MapPoint) {
2051 0 : complexWinDayltgGeom.IlluminanceMap(iRefPoint, MapNum).TransOutSurf(ICplxFen, WinEl) = TransBeam;
2052 : }
2053 0 : }
2054 : // This will avoid obstruction multiplier calculations for non-CFS window
2055 0 : ObTrans = 0.0;
2056 : }
2057 : }
2058 :
2059 368021 : if (s_surf->CalcSolRefl && PHRAY < 0.0 && ObTrans > 1.0e-6) {
2060 : // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
2061 : // by the ray. This effect is given by the ratio SkyObstructionMult =
2062 : // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
2063 : // This ratio is calculated for an isotropic sky.
2064 : // Ground point hit by the ray:
2065 0 : Real64 Alfa = std::acos(-Ray.z);
2066 0 : Real64 Beta = std::atan2(Ray.y, Ray.x);
2067 : // Distance between ground hit point and proj'n of center of window element onto ground (m)
2068 0 : Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
2069 0 : Vector3<Real64> GroundHitPt = {RWIN2.x + HorDis * std::cos(Beta), RWIN2.y + HorDis * std::sin(Beta), s_surf->GroundLevelZ};
2070 :
2071 0 : SkyObstructionMult =
2072 0 : CalcObstrMultiplier(state, GroundHitPt, DataSurfaces::AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
2073 0 : } // End of check if solar reflection calculation is in effect
2074 368021 : } // FigureDayltgCoeffsAtPointsForWindowElements()
2075 :
2076 0 : void InitializeCFSDaylighting(EnergyPlusData &state,
2077 : int const daylightCtrlNum, // Current daylighting control number
2078 : int const IWin, // Complex fenestration number
2079 : int const NWX, // Number of horizontal divisions
2080 : int const NWY, // Number of vertical divisions
2081 : Vector3<Real64> const &RefPoint, // reference point coordinates
2082 : int const NRefPts, // Number of reference points
2083 : int const iRefPoint, // Reference points counter
2084 : CalledFor const CalledFrom,
2085 : int const MapNum)
2086 : {
2087 : // SUBROUTINE INFORMATION:
2088 : // AUTHOR Simon Vidanovic
2089 : // DATE WRITTEN April 2013
2090 :
2091 : // PURPOSE OF THIS SUBROUTINE:
2092 : // For incoming BSDF window direction calculates whether bin is coming from sky, ground or reflected surface.
2093 : // Routine also calculates intersection points with ground and exterior reflection surfaces.
2094 0 : auto &dl = state.dataDayltg;
2095 0 : auto &s_surf = state.dataSurface;
2096 :
2097 : // Object Data
2098 0 : DataBSDFWindow::BSDFDaylghtPosition elPos; // altitude and azimuth of intersection element
2099 0 : Vector Vec; // temporary vector variable
2100 :
2101 0 : int NumOfWinEl = NWX * NWY; // Number of window elements
2102 :
2103 0 : auto &surf = s_surf->Surface(IWin);
2104 0 : Real64 DWX = surf.Width / NWX; // Window element width
2105 0 : Real64 DWY = surf.Height / NWY; // Window element height
2106 :
2107 0 : int zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
2108 0 : Real64 AZVIEW = (dl->daylightControl(daylightCtrlNum).ViewAzimuthForGlare + state.dataHeatBal->Zone(zoneNum).RelNorth +
2109 0 : state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) *
2110 0 : Constant::DegToRad;
2111 :
2112 : // Perform necessary calculations for window coordinates and vectors. This will be used to calculate centroids for
2113 : // each window element
2114 0 : Vector3<Real64> W1 = {0.0, 0.0, 0.0};
2115 0 : Vector3<Real64> W2 = {0.0, 0.0, 0.0};
2116 0 : Vector3<Real64> W3 = {0.0, 0.0, 0.0};
2117 :
2118 0 : if (surf.Sides == 4) {
2119 0 : W3 = surf.Vertex(2);
2120 0 : W2 = surf.Vertex(3);
2121 0 : W1 = surf.Vertex(4);
2122 0 : } else if (surf.Sides == 3) {
2123 0 : W3 = surf.Vertex(2);
2124 0 : W2 = surf.Vertex(3);
2125 0 : W1 = surf.Vertex(1);
2126 : }
2127 :
2128 0 : Vector3<Real64> W21 = W1 - W2;
2129 0 : W21 /= surf.Height;
2130 0 : Vector3<Real64> W23 = W3 - W2;
2131 0 : W23 /= surf.Width;
2132 0 : Vector3<Real64> WNorm = surf.lcsz;
2133 :
2134 0 : Real64 WinElArea = DWX * DWY;
2135 0 : if (surf.Sides == 3) {
2136 0 : WinElArea *= std::sqrt(1.0 - pow_2(dot(W21, W23)));
2137 : }
2138 :
2139 0 : auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
2140 :
2141 0 : if (CalledFrom == CalledFor::MapPoint) {
2142 :
2143 0 : if (!allocated(complexWin.IlluminanceMap)) {
2144 0 : complexWin.IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
2145 : }
2146 :
2147 0 : AllocateForCFSRefPointsGeometry(complexWin.IlluminanceMap(iRefPoint, MapNum), NumOfWinEl);
2148 :
2149 0 : } else if (CalledFrom == CalledFor::RefPoint) {
2150 0 : if (!allocated(complexWin.RefPoint)) {
2151 0 : complexWin.RefPoint.allocate(NRefPts);
2152 : }
2153 :
2154 0 : AllocateForCFSRefPointsGeometry(complexWin.RefPoint(iRefPoint), NumOfWinEl);
2155 : }
2156 :
2157 : //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2158 : //! Allocation for each complex fenestration state reference points
2159 : //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2160 0 : if (!allocated(complexWin.DaylghtGeom)) {
2161 0 : complexWin.DaylghtGeom.allocate(state.dataBSDFWindow->ComplexWind(IWin).NumStates);
2162 : }
2163 :
2164 : // Calculation needs to be performed for each state
2165 0 : for (int CurFenState = 1; CurFenState <= complexWin.NumStates; ++CurFenState) {
2166 : // number of incident basis directions for current state
2167 0 : int NBasis = complexWin.Geom(CurFenState).Inc.NBasis;
2168 : // number of outgoing basis directions for current state
2169 0 : int NTrnBasis = complexWin.Geom(CurFenState).Trn.NBasis;
2170 :
2171 0 : if (CalledFrom == CalledFor::MapPoint) {
2172 0 : if ((int)dl->illumMaps.size() > 0) {
2173 : // illuminance map for each state
2174 0 : if (!allocated(complexWin.DaylghtGeom(CurFenState).IlluminanceMap)) {
2175 0 : complexWin.DaylghtGeom(CurFenState).IlluminanceMap.allocate(NRefPts, (int)dl->illumMaps.size());
2176 : }
2177 :
2178 0 : AllocateForCFSRefPointsState(
2179 0 : state, complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum), NumOfWinEl, NBasis, NTrnBasis);
2180 :
2181 0 : InitializeCFSStateData(state,
2182 0 : complexWin.DaylghtGeom(CurFenState).IlluminanceMap(iRefPoint, MapNum),
2183 : complexWin.IlluminanceMap(iRefPoint, MapNum),
2184 : daylightCtrlNum,
2185 : IWin,
2186 : RefPoint,
2187 : CurFenState,
2188 : NBasis,
2189 : NTrnBasis,
2190 : AZVIEW,
2191 : NWX,
2192 : NWY,
2193 : W2,
2194 : W21,
2195 : W23,
2196 : DWX,
2197 : DWY,
2198 : WNorm,
2199 : WinElArea);
2200 : }
2201 :
2202 0 : } else if (CalledFrom == CalledFor::RefPoint) {
2203 0 : if (!allocated(complexWin.DaylghtGeom(CurFenState).RefPoint)) {
2204 0 : complexWin.DaylghtGeom(CurFenState).RefPoint.allocate(NRefPts);
2205 : }
2206 :
2207 0 : AllocateForCFSRefPointsState(state, complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint), NumOfWinEl, NBasis, NTrnBasis);
2208 :
2209 0 : InitializeCFSStateData(state,
2210 0 : complexWin.DaylghtGeom(CurFenState).RefPoint(iRefPoint),
2211 : complexWin.RefPoint(iRefPoint),
2212 : daylightCtrlNum,
2213 : IWin,
2214 : RefPoint,
2215 : CurFenState,
2216 : NBasis,
2217 : NTrnBasis,
2218 : AZVIEW,
2219 : NWX,
2220 : NWY,
2221 : W2,
2222 : W21,
2223 : W23,
2224 : DWX,
2225 : DWY,
2226 : WNorm,
2227 : WinElArea);
2228 : }
2229 : }
2230 0 : } // InitializeCFSDaylighting()
2231 :
2232 0 : void InitializeCFSStateData(EnergyPlusData &state,
2233 : DataBSDFWindow::BSDFRefPoints &StateRefPoint,
2234 : DataBSDFWindow::BSDFRefPointsGeomDescr &DaylghtGeomDescr,
2235 : [[maybe_unused]] int const daylightCtrlNum, // Current daylighting control number
2236 : int const iWin,
2237 : Vector3<Real64> const &RefPoint, // reference point
2238 : int const CurFenState,
2239 : int const NBasis,
2240 : int const NTrnBasis,
2241 : Real64 const AZVIEW,
2242 : int const NWX,
2243 : int const NWY,
2244 : Vector3<Real64> const &W2,
2245 : Vector3<Real64> const &W21,
2246 : Vector3<Real64> const &W23,
2247 : Real64 const DWX,
2248 : Real64 const DWY,
2249 : Vector3<Real64> const &WNorm, // unit vector from window (point towards outside)
2250 : Real64 const WinElArea)
2251 : {
2252 : // SUBROUTINE INFORMATION:
2253 : // AUTHOR Simon Vidanovic
2254 : // DATE WRITTEN June 2013
2255 :
2256 : // PURPOSE OF THIS SUBROUTINE:
2257 : // Initialize daylight state data for current
2258 0 : auto &s_surf = state.dataSurface;
2259 :
2260 : // SUBROUTINE LOCAL VARIABLES
2261 : int curWinEl;
2262 : bool hit;
2263 : int TotHits;
2264 : Real64 DotProd; // Temporary variable for manipulating dot product .dot.
2265 : int NSky;
2266 : int NGnd;
2267 : int NReflSurf;
2268 : int MaxTotHits;
2269 : Real64 LeastHitDsq; // dist^2 from window element center to hit point
2270 : Real64 HitDsq;
2271 : Real64 TransRSurf;
2272 : int J;
2273 :
2274 0 : Vector3<Real64> RWin;
2275 0 : Vector3<Real64> V;
2276 0 : Vector3<Real64> GroundHitPt;
2277 :
2278 : // temporary arrays for surfaces
2279 : // Each complex fenestration state can have different number of basis elements
2280 : // This is the reason for making these temporary arrays local
2281 0 : Array1D_int TmpSkyInd(NBasis, 0); // Temporary sky index list
2282 0 : Array1D_int TmpGndInd(NBasis, 0); // Temporary gnd index list
2283 0 : Array1D<Real64> TmpGndMultiplier(NBasis, 0.0); // Temporary ground obstruction multiplier
2284 0 : Array1D_int TmpRfSfInd(NBasis, 0); // Temporary RefSurfIndex
2285 0 : Array1D_int TmpRfRyNH(NBasis, 0); // Temporary RefRayNHits
2286 0 : Array2D_int TmpHSurfNo(s_surf->TotSurfaces, NBasis, 0); // Temporary HitSurfNo
2287 0 : Array2D<Real64> TmpHSurfDSq(s_surf->TotSurfaces, NBasis, 0.0); // Temporary HitSurfDSq
2288 :
2289 : // Object Data
2290 0 : Vector3<Real64> Centroid; // current window element centroid
2291 0 : Vector3<Real64> HitPt; // surface hit point
2292 0 : Array1D<Vector3<Real64>> TmpGndPt(NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary ground intersection list
2293 0 : Array2D<Vector3<Real64>> TmpHitPt(s_surf->TotSurfaces, NBasis, Vector3<Real64>(0.0, 0.0, 0.0)); // Temporary HitPt
2294 :
2295 0 : CFSRefPointPosFactor(state, RefPoint, StateRefPoint, iWin, CurFenState, NTrnBasis, AZVIEW);
2296 :
2297 0 : auto const &surf = s_surf->Surface(iWin);
2298 :
2299 0 : curWinEl = 0;
2300 : // loop through window elements. This will calculate sky, ground and reflection bins for each window element
2301 0 : for (int IX = 1; IX <= NWX; ++IX) {
2302 0 : for (int IY = 1; IY <= NWY; ++IY) {
2303 :
2304 0 : ++curWinEl;
2305 :
2306 : // centroid coordinates for current window element
2307 0 : Centroid = W2 + (double(IX) - 0.5) * W23 * DWX + (double(IY) - 0.5) * W21 * DWY;
2308 0 : RWin = Centroid;
2309 :
2310 0 : CFSRefPointSolidAngle(state, RefPoint, RWin, WNorm, StateRefPoint, DaylghtGeomDescr, iWin, CurFenState, NTrnBasis, curWinEl, WinElArea);
2311 :
2312 0 : NSky = 0;
2313 0 : NGnd = 0;
2314 0 : NReflSurf = 0;
2315 0 : MaxTotHits = 0;
2316 : // Calculation of potential surface obstruction for each incoming direction
2317 0 : for (int IRay = 1; IRay <= NBasis; ++IRay) {
2318 :
2319 0 : hit = false;
2320 0 : TotHits = 0;
2321 0 : for (int JSurf = 1; JSurf <= s_surf->TotSurfaces; ++JSurf) {
2322 0 : auto &surf2 = s_surf->Surface(JSurf);
2323 :
2324 : // the following test will cycle on anything except exterior surfaces and shading surfaces
2325 0 : if (surf2.HeatTransSurf && surf2.ExtBoundCond != ExternalEnvironment) continue;
2326 : // skip the base surface containing the window and any other subsurfaces of that surface
2327 0 : if (JSurf == surf.BaseSurf || surf2.BaseSurf == surf.BaseSurf) continue;
2328 : // skip surfaces that face away from the window
2329 0 : DotProd = dot(state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), surf2.NewellSurfaceNormalVector);
2330 0 : if (DotProd >= 0) continue;
2331 0 : hit = PierceSurface(state, JSurf, Centroid, state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay), HitPt);
2332 0 : if (!hit) continue; // Miss: Try next surface
2333 0 : if (TotHits == 0) {
2334 : // First hit for this ray
2335 0 : TotHits = 1;
2336 0 : ++NReflSurf;
2337 0 : TmpRfSfInd(NReflSurf) = IRay;
2338 0 : TmpRfRyNH(NReflSurf) = 1;
2339 0 : TmpHSurfNo(1, NReflSurf) = JSurf;
2340 0 : TmpHitPt(1, NReflSurf) = HitPt;
2341 0 : V = HitPt - Centroid; // vector array from window ctr to hit pt
2342 0 : LeastHitDsq = V.magnitude_squared(); // dist^2 window ctr to hit pt
2343 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
2344 0 : if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
2345 0 : TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance, treat it here as completely transparent
2346 : } else {
2347 0 : TransRSurf = 0.0;
2348 : }
2349 : } else {
2350 0 : V = HitPt - Centroid;
2351 0 : HitDsq = V.magnitude_squared();
2352 0 : if (HitDsq >= LeastHitDsq) {
2353 0 : if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
2354 0 : J = TotHits + 1;
2355 0 : if (TotHits > 1) {
2356 0 : for (int I = 2; I <= TotHits; ++I) {
2357 0 : if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
2358 0 : J = I;
2359 0 : break;
2360 : }
2361 : }
2362 0 : if (!surf2.HeatTransSurf && surf2.shadowSurfSched == nullptr) {
2363 : // The new hit is opaque, so we can drop all the hits further away
2364 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
2365 0 : TmpHitPt(J, NReflSurf) = HitPt;
2366 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
2367 0 : TotHits = J;
2368 : } else {
2369 : // The new hit is scheduled (presumed transparent), so keep the more distant hits
2370 : // Note that all the hists in the list will be transparent except the last,
2371 : // which may be either transparent or opaque
2372 0 : if (TotHits >= J) {
2373 0 : for (int I = TotHits; I >= J; --I) {
2374 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
2375 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
2376 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
2377 : }
2378 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
2379 0 : TmpHitPt(J, NReflSurf) = HitPt;
2380 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
2381 0 : ++TotHits;
2382 : }
2383 : } // if (.NOT.Surface(JSurf)%HeatTransSurf .AND. Surface(JSurf)%SchedShadowSurfIndex == 0) then
2384 : } // if (TotHits > 1) then
2385 : } // if (TransRSurf > 0.0d0) then
2386 : } else { // if (HitDsq >= LeastHitDsq) then
2387 : // A new closest hit. If it is opaque, drop the current hit list,
2388 : // otherwise add it at the front
2389 0 : LeastHitDsq = HitDsq;
2390 0 : if (!surf2.HeatTransSurf && surf2.shadowSurfSched != nullptr) {
2391 0 : TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
2392 0 : for (int I = TotHits; I >= 1; --I) {
2393 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
2394 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
2395 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
2396 0 : ++TotHits;
2397 : }
2398 0 : } else {
2399 0 : TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
2400 0 : TotHits = 1;
2401 : }
2402 0 : TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
2403 0 : TmpHitPt(1, NReflSurf) = HitPt;
2404 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
2405 : }
2406 : }
2407 : } // do JSurf = 1, TotSurfaces
2408 0 : if (TotHits <= 0) {
2409 0 : auto const &sIncRay = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sInc(IRay);
2410 : // This ray reached the sky or ground unobstructed
2411 0 : if (sIncRay.z < 0.0) {
2412 : // A ground ray
2413 0 : ++NGnd;
2414 0 : TmpGndInd(NGnd) = IRay;
2415 0 : TmpGndPt(NGnd).x = Centroid.x - (sIncRay.x / sIncRay.z) * Centroid.z;
2416 0 : TmpGndPt(NGnd).y = Centroid.y - (sIncRay.y / sIncRay.z) * Centroid.z;
2417 0 : TmpGndPt(NGnd).z = 0.0;
2418 :
2419 : // for solar reflectance calculations, need to precalculate obstruction multipliers
2420 0 : if (s_surf->CalcSolRefl) {
2421 0 : GroundHitPt = TmpGndPt(NGnd);
2422 0 : TmpGndMultiplier(NGnd) =
2423 0 : CalcObstrMultiplier(state, GroundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
2424 : }
2425 : } else {
2426 : // A sky ray
2427 0 : ++NSky;
2428 0 : TmpSkyInd(NSky) = IRay;
2429 : }
2430 : } else {
2431 : // Save the number of hits for this ray
2432 0 : TmpRfRyNH(NReflSurf) = TotHits;
2433 : }
2434 0 : MaxTotHits = max(MaxTotHits, TotHits);
2435 : } // do IRay = 1, ComplexWind(IWin)%Geom(CurFenState)%Inc%NBasis
2436 :
2437 : // Fill up state data for current window element data
2438 0 : StateRefPoint.NSky(curWinEl) = NSky;
2439 0 : StateRefPoint.SkyIndex({1, NSky}, curWinEl) = TmpSkyInd({1, NSky});
2440 :
2441 0 : StateRefPoint.NGnd(curWinEl) = NGnd;
2442 0 : StateRefPoint.GndIndex({1, NGnd}, curWinEl) = TmpGndInd({1, NGnd});
2443 0 : StateRefPoint.GndPt({1, NGnd}, curWinEl) = TmpGndPt({1, NGnd});
2444 0 : StateRefPoint.GndObstrMultiplier({1, NGnd}, curWinEl) = TmpGndMultiplier({1, NGnd});
2445 :
2446 0 : StateRefPoint.NReflSurf(curWinEl) = NReflSurf;
2447 0 : StateRefPoint.RefSurfIndex({1, NReflSurf}, curWinEl) = TmpRfSfInd({1, NReflSurf});
2448 0 : StateRefPoint.RefRayNHits({1, NReflSurf}, curWinEl) = TmpRfRyNH({1, NReflSurf});
2449 0 : StateRefPoint.HitSurfNo({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfNo({1, MaxTotHits}, {1, NReflSurf});
2450 0 : StateRefPoint.HitSurfDSq({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHSurfDSq({1, MaxTotHits}, {1, NReflSurf});
2451 0 : StateRefPoint.HitPt({1, MaxTotHits}, {1, NReflSurf}, curWinEl) = TmpHitPt({1, MaxTotHits}, {1, NReflSurf});
2452 : } // do IY = 1, NWY
2453 : } // do IX = 1, NWX
2454 0 : }
2455 :
2456 0 : void AllocateForCFSRefPointsState(
2457 : [[maybe_unused]] EnergyPlusData &state, DataBSDFWindow::BSDFRefPoints &StateRefPoint, int const NumOfWinEl, int const NBasis, int const NTrnBasis)
2458 : {
2459 : // SUBROUTINE INFORMATION:
2460 : // AUTHOR Simon Vidanovic
2461 : // DATE WRITTEN June 2013
2462 :
2463 : // PURPOSE OF THIS SUBROUTINE:
2464 : // Memory allocation for complex fenestration systems reference points geometry
2465 0 : auto &s_surf = state.dataSurface;
2466 :
2467 0 : if (!allocated(StateRefPoint.NSky)) {
2468 0 : StateRefPoint.NSky.allocate(NumOfWinEl);
2469 0 : StateRefPoint.NSky = 0;
2470 : }
2471 :
2472 0 : if (!allocated(StateRefPoint.SkyIndex)) {
2473 0 : StateRefPoint.SkyIndex.allocate(NBasis, NumOfWinEl);
2474 0 : StateRefPoint.SkyIndex = 0;
2475 : }
2476 :
2477 0 : if (!allocated(StateRefPoint.NGnd)) {
2478 0 : StateRefPoint.NGnd.allocate(NumOfWinEl);
2479 0 : StateRefPoint.NGnd = 0;
2480 : }
2481 :
2482 0 : if (!allocated(StateRefPoint.GndIndex)) {
2483 0 : StateRefPoint.GndIndex.allocate(NBasis, NumOfWinEl);
2484 0 : StateRefPoint.GndIndex = 0;
2485 : }
2486 :
2487 0 : if (!allocated(StateRefPoint.GndPt)) {
2488 0 : StateRefPoint.GndPt.allocate(NBasis, NumOfWinEl);
2489 0 : StateRefPoint.GndPt = Vector(0.0, 0.0, 0.0);
2490 : }
2491 :
2492 0 : if (!allocated(StateRefPoint.GndObstrMultiplier)) {
2493 0 : StateRefPoint.GndObstrMultiplier.allocate(NBasis, NumOfWinEl);
2494 0 : StateRefPoint.GndObstrMultiplier = 0.0;
2495 : }
2496 :
2497 0 : if (!allocated(StateRefPoint.NReflSurf)) {
2498 0 : StateRefPoint.NReflSurf.allocate(NumOfWinEl);
2499 0 : StateRefPoint.NReflSurf = 0;
2500 : }
2501 :
2502 0 : if (!allocated(StateRefPoint.RefSurfIndex)) {
2503 0 : StateRefPoint.RefSurfIndex.allocate(NBasis, NumOfWinEl);
2504 0 : StateRefPoint.RefSurfIndex = 0;
2505 : }
2506 :
2507 0 : if (!allocated(StateRefPoint.TransOutSurf)) {
2508 0 : StateRefPoint.TransOutSurf.allocate(NBasis, NumOfWinEl);
2509 0 : StateRefPoint.TransOutSurf = 1.0;
2510 : }
2511 :
2512 0 : if (!allocated(StateRefPoint.RefRayNHits)) {
2513 0 : StateRefPoint.RefRayNHits.allocate(NBasis, NumOfWinEl);
2514 0 : StateRefPoint.RefRayNHits = 0;
2515 : }
2516 :
2517 0 : if (!allocated(StateRefPoint.HitSurfNo)) {
2518 0 : StateRefPoint.HitSurfNo.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
2519 0 : StateRefPoint.HitSurfNo = 0;
2520 : }
2521 :
2522 0 : if (!allocated(StateRefPoint.HitSurfDSq)) {
2523 0 : StateRefPoint.HitSurfDSq.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
2524 0 : StateRefPoint.HitSurfDSq = 0.0;
2525 : }
2526 :
2527 0 : if (!allocated(StateRefPoint.HitPt)) {
2528 0 : StateRefPoint.HitPt.allocate(s_surf->TotSurfaces, NBasis, NumOfWinEl);
2529 0 : StateRefPoint.HitPt = Vector(0.0, 0.0, 0.0);
2530 : }
2531 :
2532 0 : if (!allocated(StateRefPoint.RefPointIndex)) {
2533 0 : StateRefPoint.RefPointIndex.allocate(NumOfWinEl);
2534 0 : StateRefPoint.RefPointIndex = 0;
2535 : }
2536 :
2537 0 : if (!allocated(StateRefPoint.RefPointIntersection)) {
2538 0 : StateRefPoint.RefPointIntersection.allocate(NTrnBasis);
2539 0 : StateRefPoint.RefPointIntersection = false;
2540 : }
2541 :
2542 0 : if (!allocated(StateRefPoint.RefPtIntPosFac)) {
2543 0 : StateRefPoint.RefPtIntPosFac.allocate(NTrnBasis);
2544 0 : StateRefPoint.RefPtIntPosFac = 0.0;
2545 : }
2546 0 : }
2547 :
2548 0 : void AllocateForCFSRefPointsGeometry(DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointsGeomDescr, int const NumOfWinEl)
2549 : {
2550 : // SUBROUTINE INFORMATION:
2551 : // AUTHOR Simon Vidanovic
2552 : // DATE WRITTEN June 2013
2553 :
2554 : // PURPOSE OF THIS SUBROUTINE:
2555 : // Memory allocation for complex fenestration systems reference points geometry
2556 :
2557 : // SUBROUTINE LOCAL VARIABLES
2558 :
2559 0 : if (!allocated(RefPointsGeomDescr.SolidAngle)) {
2560 0 : RefPointsGeomDescr.SolidAngle.allocate(NumOfWinEl);
2561 0 : RefPointsGeomDescr.SolidAngle = 0.0;
2562 : }
2563 :
2564 0 : if (!allocated(RefPointsGeomDescr.SolidAngleVec)) {
2565 0 : RefPointsGeomDescr.SolidAngleVec.allocate(NumOfWinEl);
2566 0 : RefPointsGeomDescr.SolidAngleVec = Vector(0.0, 0.0, 0.0);
2567 : }
2568 0 : }
2569 :
2570 0 : void CFSRefPointSolidAngle(EnergyPlusData &state,
2571 : Vector3<Real64> const &RefPoint,
2572 : Vector3<Real64> const &RWin,
2573 : Vector3<Real64> const &WNorm,
2574 : DataBSDFWindow::BSDFRefPoints &RefPointMap,
2575 : DataBSDFWindow::BSDFRefPointsGeomDescr &RefPointGeomMap,
2576 : int const iWin,
2577 : int const CurFenState,
2578 : int const NTrnBasis,
2579 : int const curWinEl,
2580 : Real64 const WinElArea)
2581 : {
2582 : // SUBROUTINE INFORMATION:
2583 : // AUTHOR Simon Vidanovic
2584 : // DATE WRITTEN June 2013
2585 :
2586 : // PURPOSE OF THIS SUBROUTINE:
2587 : // Calculate position factor for given reference point.
2588 :
2589 : // calculate vector from center of window element to the current reference point
2590 0 : Vector3<Real64> Ray = RefPoint - RWin;
2591 :
2592 : // figure out outgoing beam direction from current reference point
2593 0 : Real64 BestMatch = 0.0;
2594 0 : for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
2595 0 : Vector3<Real64> const &V = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn(iTrnRay);
2596 0 : Real64 temp = dot(Ray, V);
2597 0 : if (temp > BestMatch) {
2598 0 : BestMatch = temp;
2599 0 : RefPointMap.RefPointIndex(curWinEl) = iTrnRay;
2600 : }
2601 : }
2602 :
2603 : // calculate solid view angle
2604 0 : Real64 Dist = Ray.magnitude();
2605 0 : Vector3<Real64> RayNorm = Ray / (-Dist);
2606 0 : RefPointGeomMap.SolidAngleVec(curWinEl) = RayNorm;
2607 0 : Real64 CosB = dot(WNorm, RayNorm);
2608 0 : RefPointGeomMap.SolidAngle(curWinEl) = WinElArea * CosB / (Dist * Dist);
2609 0 : }
2610 :
2611 0 : void CFSRefPointPosFactor(EnergyPlusData &state,
2612 : Vector3<Real64> const &RefPoint,
2613 : DataBSDFWindow::BSDFRefPoints &RefPointMap,
2614 : int const iWin,
2615 : int const CurFenState,
2616 : int const NTrnBasis,
2617 : Real64 const AZVIEW)
2618 : {
2619 : // SUBROUTINE INFORMATION:
2620 : // AUTHOR Simon Vidanovic
2621 : // DATE WRITTEN June 2013
2622 :
2623 : // PURPOSE OF THIS SUBROUTINE:
2624 : // Calculate position factor for given reference point.
2625 :
2626 0 : auto const &sTrn = state.dataBSDFWindow->ComplexWind(iWin).Geom(CurFenState).sTrn;
2627 0 : for (int iTrnRay = 1; iTrnRay <= NTrnBasis; ++iTrnRay) {
2628 0 : Vector3<Real64> V = sTrn(iTrnRay);
2629 0 : V.negate();
2630 :
2631 0 : Vector3<Real64> InterPoint;
2632 :
2633 0 : bool hit = PierceSurface(state, iWin, RefPoint, V, InterPoint);
2634 0 : if (hit) {
2635 0 : RefPointMap.RefPointIntersection(iTrnRay) = true;
2636 :
2637 0 : DataBSDFWindow::BSDFDaylghtPosition elPos = WindowComplexManager::DaylghtAltAndAzimuth(V);
2638 :
2639 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - elPos.Azimuth) + 0.001);
2640 0 : Real64 YR = std::tan(elPos.Altitude + 0.001);
2641 0 : RefPointMap.RefPtIntPosFac(iTrnRay) = DayltgGlarePositionFactor(XR, YR);
2642 : }
2643 0 : }
2644 0 : } // CFSRefPointPosFactor()
2645 :
2646 0 : Real64 CalcObstrMultiplier(EnergyPlusData &state,
2647 : Vector3<Real64> const &GroundHitPt, // Coordinates of point that ray hits ground (m)
2648 : int const AltSteps, // Number of steps in altitude angle for solar reflection calc
2649 : int const AzimSteps // Number of steps in azimuth angle of solar reflection calc
2650 : )
2651 : {
2652 :
2653 : // SUBROUTINE INFORMATION:
2654 : // AUTHOR Simon Vidanovic
2655 : // DATE WRITTEN April 2013, refactor from legacy code by Fred Winklemann
2656 :
2657 : // PURPOSE OF THIS SUBROUTINE:
2658 : // collect code to do obstruction multiplier from ground point
2659 :
2660 : // METHODOLOGY EMPLOYED:
2661 : // Send rays upward from hit point and see which ones are unobstructed and so go to sky.
2662 : // Divide hemisphere centered at ground hit point into elements of altitude Phi and
2663 : // azimuth Theta and create upward-going ground ray unit vector at each Phi,Theta pair.
2664 : // Phi = 0 at the horizon; Phi = Pi/2 at the zenith.
2665 :
2666 : // Locals
2667 0 : auto const &dl = state.dataDayltg;
2668 0 : auto const &s_surf = state.dataSurface;
2669 :
2670 : bool hitObs; // True iff obstruction is hit
2671 :
2672 0 : Vector3<Real64> URay; // Unit vector in (Phi,Theta) direction
2673 0 : Vector3<Real64> ObsHitPt; // Unit vector in (Phi,Theta) direction
2674 :
2675 0 : assert(AzimSteps <= DataSurfaces::AzimAngStepsForSolReflCalc);
2676 :
2677 0 : Real64 DPhi = Constant::PiOvr2 / (AltSteps / 2.0); // Phi increment (radians)
2678 0 : Real64 DTheta = Constant::Pi / AzimSteps; // Theta increment (radians)
2679 :
2680 : // Tuned Precompute Phi trig table
2681 0 : if (AltSteps != dl->AltSteps_last) {
2682 0 : for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
2683 0 : Real64 Phi = (IPhi - 0.5) * DPhi;
2684 0 : dl->cos_Phi[IPhi] = std::cos(Phi);
2685 0 : dl->sin_Phi[IPhi] = std::sin(Phi);
2686 : }
2687 0 : dl->AltSteps_last = AltSteps;
2688 : }
2689 : // Tuned Precompute Theta trig table
2690 0 : if (AzimSteps != dl->AzimSteps_last) {
2691 0 : for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
2692 0 : Real64 Theta = (ITheta - 0.5) * DTheta;
2693 0 : dl->cos_Theta[ITheta] = std::cos(Theta);
2694 0 : dl->sin_Theta[ITheta] = std::sin(Theta);
2695 : }
2696 0 : dl->AzimSteps_last = AzimSteps;
2697 : }
2698 :
2699 0 : Real64 SkyGndObs = 0.0; // Obstructed sky irradiance at a ground point
2700 0 : Real64 SkyGndUnObs = 0.0; // Unobstructed sky irradiance at a ground point
2701 :
2702 : // Altitude loop
2703 0 : for (int IPhi = 1, IPhi_end = (AltSteps / 2); IPhi <= IPhi_end; ++IPhi) {
2704 0 : Real64 sinPhi = dl->sin_Phi[IPhi]; // sinPhi
2705 0 : Real64 cosPhi = dl->cos_Phi[IPhi]; // cosPhi
2706 :
2707 : // Third component of ground ray unit vector in (Theta,Phi) direction
2708 0 : URay.z = sinPhi;
2709 0 : Real64 dOmegaGnd = cosPhi * DTheta * DPhi; // Solid angle element of ray from ground point (steradians)
2710 : // Cosine of angle of incidence of ground ray on ground plane
2711 0 : Real64 CosIncAngURay = sinPhi;
2712 0 : Real64 IncAngSolidAngFac = CosIncAngURay * dOmegaGnd / Constant::Pi; // CosIncAngURay*dOmegaGnd/Pi
2713 : // Azimuth loop
2714 0 : for (int ITheta = 1; ITheta <= 2 * AzimSteps; ++ITheta) {
2715 0 : URay.x = cosPhi * dl->cos_Theta[ITheta];
2716 0 : URay.y = cosPhi * dl->sin_Theta[ITheta];
2717 0 : SkyGndUnObs += IncAngSolidAngFac;
2718 : // Does this ground ray hit an obstruction?
2719 0 : hitObs = false;
2720 0 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
2721 :
2722 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
2723 0 : hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
2724 0 : if (hitObs) break;
2725 : }
2726 :
2727 : } else { // Surface octree search
2728 :
2729 : // Lambda function for the octree to test for surface hit
2730 0 : auto surfaceHit = [&GroundHitPt, &hitObs, &URay, &ObsHitPt](SurfaceData const &surface) -> bool {
2731 0 : if (surface.IsShadowPossibleObstruction) {
2732 0 : hitObs = PierceSurface(surface, GroundHitPt, URay, ObsHitPt); // Check if ray pierces surface
2733 0 : return hitObs;
2734 : } else {
2735 0 : return false;
2736 : }
2737 0 : };
2738 :
2739 : // Check octree surface candidates until a hit is found, if any
2740 0 : Vector3<Real64> const URay_inv(SurfaceOctreeCube::safe_inverse(URay));
2741 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceRayIntersectsCube(GroundHitPt, URay, URay_inv, surfaceHit);
2742 0 : }
2743 :
2744 0 : if (hitObs) continue; // Obstruction hit
2745 : // Sky is hit
2746 0 : SkyGndObs += IncAngSolidAngFac;
2747 : } // End of azimuth loop
2748 : } // End of altitude loop
2749 :
2750 : // in case ground point is surrounded by obstructions (SkyGndUnObs == 0), then multiplier will be equal to zero
2751 : // This should not happen anyway because in that case ray would not be able to reach ground point
2752 0 : return (SkyGndUnObs != 0.0) ? (SkyGndObs / SkyGndUnObs) : 0.0;
2753 0 : } // CalcObstrMultiplier()
2754 :
2755 862072 : void FigureDayltgCoeffsAtPointsForSunPosition(
2756 : EnergyPlusData &state,
2757 : int const daylightCtrlNum, // Daylighting control index
2758 : int const iRefPoint,
2759 : int const iXelement,
2760 : int const NWX, // Number of window elements in x direction for dayltg calc
2761 : int const iYelement,
2762 : int const NWY, // Number of window elements in y direction for dayltg calc
2763 : int const WinEl, // Current window element counter
2764 : int const IWin,
2765 : int const IWin2,
2766 : int const iHour,
2767 : int &ISunPos,
2768 : Real64 const SkyObstructionMult,
2769 : Vector3<Real64> const &RWIN2, // Center of a window element for TDD:DOME (if exists) in abs coord sys
2770 : Vector3<Real64> const &Ray, // Unit vector along ray from reference point to window element
2771 : Real64 const PHRAY, // Altitude of ray from reference point to window element (radians)
2772 : int const LSHCAL, // Interior shade calculation flag: 0=not yet calculated, 1=already calculated
2773 : int const InShelfSurf, // Inside daylighting shelf surface number
2774 : Real64 const COSB, // Cosine of angle between window outward normal and ray from reference point to window element
2775 : Real64 const ObTrans, // Product of solar transmittances of exterior obstructions hit by ray from reference point through a window element
2776 : Real64 const TVISB, // Visible transmittance of window for COSB angle of incidence (times light well efficiency, if appropriate)
2777 : Real64 const DOMEGA, // Solid angle subtended by window element wrt reference point (steradians)
2778 : int const ICtrl, // Window control counter
2779 : WinShadingType const ShType, // Window shading type
2780 : [[maybe_unused]] int const BlNum, // Window blind number
2781 : Real64 const THRAY, // Azimuth of ray from reference point to window element (radians)
2782 : Vector3<Real64> const &WNORM2, // Unit vector normal to window
2783 : ExtWinType const extWinType, // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
2784 : int const IConst, // Construction counter
2785 : Real64 const AZVIEW, // Azimuth of view vector in absolute coord system for glare calculation (radians)
2786 : Vector3<Real64> const &RREF2, // Location of virtual reference point in absolute coordinate system
2787 : bool const hitIntObs, // True iff interior obstruction hit
2788 : bool const hitExtObs, // True iff ray from ref pt to ext win hits an exterior obstruction
2789 : CalledFor const CalledFrom, // indicate which type of routine called this routine
2790 : Real64 TVISIntWin, // Visible transmittance of int win at COSBIntWin for light from ext win
2791 : Real64 &TVISIntWinDisk, // Visible transmittance of int win at COSBIntWin for sun
2792 : int const MapNum)
2793 : {
2794 :
2795 : // SUBROUTINE INFORMATION:
2796 : // AUTHOR B. Griffith
2797 : // DATE WRITTEN November 2012, refactor from legacy code by Fred Winklemann
2798 :
2799 : // PURPOSE OF THIS SUBROUTINE:
2800 : // collect code for calculations sun position aspects for daylighting coefficients
2801 :
2802 : // METHODOLOGY EMPLOYED:
2803 : // switch as need to serve both reference points and map points based on calledFrom
2804 862072 : auto &s_surf = state.dataSurface;
2805 862072 : if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
2806 :
2807 453596 : auto &dl = state.dataDayltg;
2808 453596 : auto &s_mat = state.dataMaterial;
2809 :
2810 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2811 453596 : Vector3<Real64> RREF{0.0, 0.0, 0.0}; // Location of a reference point in absolute coordinate system //Autodesk Was used uninitialized:
2812 :
2813 : Real64 ObTransDisk; // Product of solar transmittances of exterior obstructions hit by ray from reference point to sun
2814 : Real64 LumAtHitPtFrSun; // Luminance at hit point of obstruction by reflection of direct light from sun (cd/m2)
2815 :
2816 : Real64 TVISS; // Direct solar visible transmittance of window at given angle of incidence
2817 : // (times light well efficiency, if appropriate)
2818 : Real64 XAVWL; // XAVWL*TVISS is contribution of window luminance from solar disk (cd/m2)
2819 :
2820 : bool hitObs; // True iff obstruction is hit
2821 : Real64 ObsVisRefl; // Visible reflectance of obstruction
2822 : Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by
2823 :
2824 : Real64 SpecReflectance; // Specular reflectance of a reflecting surface
2825 : Real64 TVisRefl; // Bare window vis trans for reflected beam
2826 : // (times light well efficiency, if appropriate)
2827 : Real64 PHSUNrefl; // Altitude angle of reflected sun (radians)
2828 : Real64 THSUNrefl; // Azimuth anggle of reflected sun (radians)
2829 :
2830 : Real64 COSBIntWin; // Cos of angle between int win outward normal and ray betw ref pt and
2831 : // exterior window element or between ref pt and sun
2832 : Real64 TVisIntWinMult; // Interior window vis trans multiplier for ext win in adjacent zone
2833 : Real64 TVisIntWinDiskMult; // Interior window vis trans solar disk multiplier for ext win in adj zone
2834 : Real64 WindowSolidAngleDaylightPoint;
2835 :
2836 453596 : ++ISunPos;
2837 :
2838 : // Altitude of sun (degrees)
2839 453596 : dl->sunAngles = dl->sunAnglesHr[iHour];
2840 :
2841 : // First time through, call routine to calculate inter-reflected illuminance
2842 : // at reference point and luminance of window with shade, screen or blind.
2843 :
2844 : // Rob/TH - Not sure whether this call is necessary for interior zones with interior windows only.
2845 : // new code would be -
2846 : // IF (LSHCAL == 1 .AND. ExtWinType /= AdjZoneExtWin) CALL DayltgInterReflectedIllum(ISunPos,IHR,ZoneNum,IWin2)
2847 453596 : int enclNum = 0; // enclosure index
2848 453596 : int zoneNum = 0; // zone index
2849 453596 : if (CalledFrom == CalledFor::RefPoint) {
2850 137846 : zoneNum = dl->daylightControl(daylightCtrlNum).zoneIndex;
2851 137846 : enclNum = dl->daylightControl(daylightCtrlNum).enclIndex;
2852 315750 : } else if (CalledFrom == CalledFor::MapPoint) {
2853 315750 : assert(MapNum > 0);
2854 315750 : zoneNum = dl->illumMaps(MapNum).zoneIndex;
2855 315750 : enclNum = dl->illumMaps(MapNum).enclIndex;
2856 : }
2857 453596 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) {
2858 453596 : if (LSHCAL == 1) DayltgInterReflectedIllum(state, ISunPos, iHour, enclNum, IWin2);
2859 : } else {
2860 0 : if (LSHCAL == 1) DayltgInterReflectedIllumComplexFenestration(state, IWin2, WinEl, iHour, daylightCtrlNum, iRefPoint, CalledFrom, MapNum);
2861 0 : if (COSB <= 0.0) return;
2862 0 : DayltgDirectIllumComplexFenestration(state, IWin, WinEl, iHour, iRefPoint, CalledFrom, MapNum);
2863 : // Call direct sun component only once since calculation is done for entire window
2864 0 : if (WinEl == (NWX * NWY)) {
2865 0 : DayltgDirectSunDiskComplexFenestration(state, IWin2, iHour, iRefPoint, WinEl, AZVIEW, CalledFrom, MapNum);
2866 : }
2867 0 : return;
2868 : }
2869 :
2870 : // Daylighting shelf simplification: The shelf completely blocks all view of the window,
2871 : // only interrelflected illumination is allowed (see DayltgInterReflectedIllum above).
2872 : // Everything else in this loop has to do with direct luminance from the window.
2873 453596 : if (InShelfSurf > 0) return;
2874 :
2875 453596 : if (COSB <= 0.0) return;
2876 :
2877 453596 : auto &surfWin = s_surf->SurfaceWindow(IWin);
2878 :
2879 453596 : Illums XDirIllum;
2880 453596 : Illums XAvgWinLum;
2881 453596 : Real64 const Ray_3 = Ray.z;
2882 453596 : Real64 const DOMEGA_Ray_3 = DOMEGA * Ray_3;
2883 :
2884 : // Add contribution of this window element to glare and to
2885 : // direct illuminance at reference point
2886 :
2887 : // The I,J,K indices for sky and sun components of direct illuminance
2888 : // (EDIRSK, EDIRSU) and average window luminance (AVWLSK, AVWLSU) are:
2889 : // I=1 for clear sky, =2 Clear turbid, =3 Intermediate, =4 Overcast;
2890 : // J=1 for bare window, =2 for window with shade or fixed slat-angle blind;
2891 : // K = sun position index.
2892 :
2893 : // ----- CASE I -- BARE WINDOW (no shading device)
2894 :
2895 : // Beam solar and sky solar reflected from nearest obstruction.
2896 : // In the following hitIntObs == false ==> no interior obstructions hit, and
2897 : // hitExtObs == true ==> one or more exterior obstructions hit.
2898 453596 : if (s_surf->CalcSolRefl && !hitIntObs && hitExtObs) {
2899 : int NearestHitSurfNum; // Surface number of nearest obstruction
2900 0 : Vector3<Real64> NearestHitPt; // Hit point of ray on nearest obstruction
2901 : // One or more exterior obstructions was hit; get contribution of reflection
2902 : // from nearest obstruction.
2903 : // Find obstruction whose hit point is closest to this ray's window element
2904 0 : DayltgClosestObstruction(state, RWIN2, Ray, NearestHitSurfNum, NearestHitPt);
2905 0 : if (NearestHitSurfNum > 0) {
2906 :
2907 : // Beam solar reflected from nearest obstruction
2908 :
2909 0 : LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, iHour, Ray, NearestHitSurfNum, NearestHitPt);
2910 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * TVISB;
2911 0 : if (PHRAY >= 0.0) dl->dirIllum(iHour)[iWinCover_Bare].sun += LumAtHitPtFrSun * DOMEGA_Ray_3 * TVISB;
2912 :
2913 : // Sky solar reflected from nearest obstruction
2914 :
2915 0 : int const ObsConstrNum = s_surf->SurfActiveConstruction(NearestHitSurfNum);
2916 0 : if (ObsConstrNum > 0) {
2917 : // Exterior building surface is nearest hit
2918 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
2919 : // Obstruction is not a window, i.e., is an opaque surface
2920 0 : ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
2921 : } else {
2922 : // Obstruction is a window; assume it is bare
2923 0 : ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
2924 : }
2925 : } else {
2926 : // Shadowing surface is nearest hit
2927 0 : if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
2928 : // This is a daylighting shelf, for which reflection is separately calculated
2929 0 : ObsVisRefl = 0.0;
2930 : } else {
2931 0 : ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
2932 0 : if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
2933 0 : ObsVisRefl += s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
2934 0 : state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
2935 : }
2936 : }
2937 : // Surface number to use when obstruction is a shadowing surface
2938 0 : int NearestHitSurfNumX = NearestHitSurfNum;
2939 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
2940 : // The following gets the correct side of a shadowing surface for reflection.
2941 0 : if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
2942 0 : if (dot(Ray, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
2943 : }
2944 0 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
2945 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
2946 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
2947 0 : state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
2948 : } else {
2949 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
2950 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, iHour, NearestHitSurfNumX) / Constant::Pi;
2951 : }
2952 0 : assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
2953 0 : auto &gilsk = dl->horIllum[iHour];
2954 0 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
2955 0 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
2956 :
2957 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
2958 0 : XAvgWinLum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum;
2959 0 : avwlsk.sky[iSky] += XAvgWinLum.sky[iSky] * TVISB;
2960 0 : if (PHRAY >= 0.0) {
2961 0 : XDirIllum.sky[iSky] = gilsk.sky[iSky] * SkyReflVisLum * DOMEGA_Ray_3;
2962 0 : edirsk.sky[iSky] += XDirIllum.sky[iSky] * TVISB;
2963 : }
2964 : }
2965 : }
2966 0 : } // End of check if solar reflection calculation is in effect
2967 :
2968 453596 : if (ObTrans > 1.e-6) {
2969 : // Ray did not hit an obstruction or the transmittance product of hit obstructions is non-zero.
2970 : // Contribution of sky or ground luminance in cd/m2
2971 453596 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
2972 : // Make all transmitted light diffuse for a TDD with a bare diffuser
2973 0 : assert(equal_dimensions(dl->avgWinLum, dl->winLum));
2974 0 : assert(equal_dimensions(dl->avgWinLum, dl->dirIllum));
2975 0 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
2976 0 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
2977 0 : auto &wlumsk = dl->winLum(iHour)[iWinCover_Bare];
2978 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
2979 0 : avwlsk.sky[iSky] += wlumsk.sky[iSky];
2980 0 : if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3;
2981 : }
2982 :
2983 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun;
2984 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += dl->winLum(iHour)[iWinCover_Bare].sunDisk;
2985 :
2986 0 : if (PHRAY > 0.0) dl->dirIllum(iHour)[iWinCover_Bare].sun += dl->winLum(iHour)[iWinCover_Bare].sun * DOMEGA_Ray_3;
2987 : } else { // Bare window
2988 453596 : Vector3<Real64> GroundHitPt; // Coordinates of point that ray hits ground (m)
2989 : // Tuned Hoisted operations out of loop and linear indexing
2990 453596 : if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
2991 0 : Real64 Alfa = std::acos(-Ray_3);
2992 0 : Real64 const Ray_1(Ray.x);
2993 0 : Real64 const Ray_2(Ray.y);
2994 : // Beta = std::atan2( Ray_2, Ray_1 ); //Unused Tuning below eliminated use
2995 : // Distance between ground hit point and proj'n of center of window element onto ground (m)
2996 0 : Real64 HorDis = (RWIN2.z - s_surf->GroundLevelZ) * std::tan(Alfa);
2997 0 : GroundHitPt.z = s_surf->GroundLevelZ;
2998 : // Tuned Replaced by below: sqrt is faster than sincos
2999 : // GroundHitPt( 1 ) = RWIN2( 1 ) + HorDis * std::cos( Beta );
3000 : // GroundHitPt( 2 ) = RWIN2( 2 ) + HorDis * std::sin( Beta );
3001 0 : Real64 const Ray_r(std::sqrt(square(Ray_1) + square(Ray_2)));
3002 0 : if (Ray_r > 0.0) {
3003 0 : HorDis /= Ray_r;
3004 0 : GroundHitPt.x = RWIN2.x + HorDis * Ray_1;
3005 0 : GroundHitPt.y = RWIN2.y + HorDis * Ray_2;
3006 : } else { // Treat as angle==0
3007 0 : GroundHitPt.x = RWIN2.x + HorDis;
3008 0 : GroundHitPt.y = RWIN2.y;
3009 : }
3010 : }
3011 453596 : Real64 const GILSK_mult((state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * ObTrans * SkyObstructionMult);
3012 453596 : Real64 const TVISB_ObTrans(TVISB * ObTrans);
3013 453596 : Real64 const AVWLSU_add(TVISB_ObTrans * dl->horIllum[iHour].sun * (state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi));
3014 453596 : Vector3<Real64> const SUNCOS_iHour(s_surf->SurfSunCosHourly(iHour));
3015 453596 : assert(equal_dimensions(dl->dirIllum, dl->avgWinLum));
3016 453596 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Bare];
3017 453596 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Bare];
3018 :
3019 2267980 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3020 1814384 : if (PHRAY > 0.0) { // Ray heads upward to sky
3021 1531440 : Real64 ELUM = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), THRAY, PHRAY); // Sky or ground luminance (cd/m2)
3022 1531440 : XDirIllum.sky[iSky] = ELUM * DOMEGA_Ray_3;
3023 1531440 : Real64 DEDIR = XDirIllum.sky[iSky] * TVISB; // Illuminance contribution at reference point from window element (lux)
3024 1531440 : edirsk.sky[iSky] += DEDIR * ObTrans;
3025 1531440 : avwlsk.sky[iSky] += ELUM * TVISB_ObTrans;
3026 1531440 : XAvgWinLum.sky[iSky] = ELUM * ObTrans;
3027 : } else { // PHRAY <= 0.
3028 : // Ray heads downward to ground.
3029 : // Contribution from sky diffuse reflected from ground
3030 282944 : XAvgWinLum.sky[iSky] = dl->horIllum[iHour].sky[iSky] * GILSK_mult;
3031 282944 : avwlsk.sky[iSky] += TVISB * XAvgWinLum.sky[iSky];
3032 : // Contribution from beam solar reflected from ground (beam reaching ground point
3033 : // can be obstructed [SunObstructionMult < 1.0] if CalcSolRefl = .TRUE.)
3034 : } // End of check if ray is going up or down
3035 : } // for (iSky)
3036 :
3037 453596 : if (PHRAY <= 0.0) {
3038 : // SunObstructionMult = 1.0; //Tuned
3039 70736 : if (s_surf->CalcSolRefl) { // Coordinates of ground point hit by the ray
3040 : // Sun reaches ground point if vector from this point to the sun is unobstructed
3041 0 : hitObs = false;
3042 0 : Vector3<Real64> ObsHitPt; // Coordinates of hit point on an obstruction (m)
3043 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
3044 0 : hitObs = PierceSurface(state, ObsSurfNum, GroundHitPt, SUNCOS_iHour, ObsHitPt);
3045 0 : if (hitObs) break;
3046 : }
3047 : // if ( hitObs ) SunObstructionMult = 0.0;
3048 0 : if (!hitObs) dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
3049 0 : } else {
3050 70736 : dl->avgWinLum(iHour)[iWinCover_Bare].sun += AVWLSU_add;
3051 : }
3052 : } // (PHRAY <= 0.0)
3053 453596 : }
3054 : } // End of check if bare window or TDD:DIFFUSER
3055 :
3056 : // Illuminance from beam solar (without interior reflection)
3057 : // Just run this once on the last pass
3058 453596 : if (iXelement == NWX && iYelement == NWY) { // Last pass
3059 :
3060 : // Beam solar reaching reference point directly without exterior reflection
3061 :
3062 : // Unit vector from ref. pt. to sun
3063 2066 : Vector3<Real64> RAYCOS;
3064 2066 : RAYCOS.x = dl->sunAngles.cosPhi * std::cos(dl->sunAngles.theta);
3065 2066 : RAYCOS.y = dl->sunAngles.cosPhi * std::sin(dl->sunAngles.theta);
3066 2066 : RAYCOS.z = dl->sunAngles.sinPhi;
3067 :
3068 : // Is sun on front side of exterior window?
3069 2066 : Real64 COSI = dot(WNORM2, RAYCOS); // Cosine of angle between direct sun and window outward normal
3070 : bool hit; // True if ray from ref point thru window element hits an obstruction
3071 : bool hitWin; // True if ray passes thru window
3072 2066 : Vector3<Real64> HP;
3073 2066 : if (COSI > 0.0) {
3074 :
3075 : // Does RAYCOS pass thru exterior window? HP is point that RAYCOS intersects window plane.
3076 1454 : hitWin = PierceSurface(state, IWin2, RREF2, RAYCOS, HP);
3077 : // True if ray from ref pt to sun hits an interior obstruction
3078 1454 : if (hitWin) {
3079 173 : bool hitIntObsDisk = false;
3080 173 : if (extWinType == ExtWinType::InZone) {
3081 : // Check for interior obstructions between reference point and HP.
3082 173 : hitIntObsDisk = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
3083 : }
3084 173 : ObTransDisk = 0.0; // Init value
3085 : // Init flag for vector from RP to sun passing through interior window
3086 173 : bool hitIntWinDisk = false;
3087 173 : if (extWinType == ExtWinType::AdjZone) { // This block is for RPs in zones with interior windows
3088 : // adjacent to zones with exterior windows
3089 : // Does RAYCOS pass through interior window in zone containing RP?
3090 : // Loop over zone surfaces looking for interior windows between reference point and sun
3091 : // Surface number of int window intersected by ray betw ref pt and sun
3092 : int IntWinDiskHitNum;
3093 : // Intersection point on an interior window for ray from ref pt to sun (m)
3094 0 : Vector3<Real64> HitPtIntWinDisk;
3095 0 : auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
3096 0 : for (int const spaceNum : thisZone.spaceIndexes) {
3097 0 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
3098 0 : for (int IntWinDisk = thisSpace.WindowSurfaceFirst, IntWinDisk_end = thisSpace.WindowSurfaceLast;
3099 0 : IntWinDisk <= IntWinDisk_end;
3100 : ++IntWinDisk) {
3101 0 : auto const &surfIntWinDisk = s_surf->Surface(IntWinDisk);
3102 0 : if (surfIntWinDisk.ExtBoundCond < 1) continue;
3103 :
3104 0 : if (s_surf->Surface(surfIntWinDisk.ExtBoundCond).Zone != s_surf->Surface(IWin2).Zone) continue;
3105 :
3106 0 : hitIntWinDisk = PierceSurface(state, IntWinDisk, RREF, RAYCOS, HitPtIntWinDisk);
3107 0 : if (!hitIntWinDisk) continue;
3108 :
3109 0 : IntWinDiskHitNum = IntWinDisk;
3110 0 : COSBIntWin = dot(surfIntWinDisk.OutNormVec, RAYCOS);
3111 0 : if (COSBIntWin <= 0.0) {
3112 0 : hitIntWinDisk = false;
3113 0 : IntWinDiskHitNum = 0;
3114 0 : continue;
3115 : }
3116 0 : TVISIntWinDisk =
3117 0 : Window::POLYF(COSBIntWin, state.dataConstruction->Construct(surfIntWinDisk.Construction).TransVisBeamCoef);
3118 0 : break;
3119 : } // for (IntWinDisk)
3120 : } // for (spaceNum)
3121 :
3122 0 : if (!hitIntWinDisk) { // Vector from RP to sun does not pass through interior window
3123 0 : ObTransDisk = 0.0;
3124 0 : hit = true; //! fcw Is this needed?
3125 : }
3126 :
3127 : // Check for interior obstructions between ref point and interior window
3128 0 : hitIntObsDisk = false;
3129 0 : if (hitIntWinDisk) {
3130 0 : hitIntObsDisk = DayltgHitInteriorObstruction(state, IntWinDiskHitNum, RREF, HitPtIntWinDisk);
3131 : // If no obstruction between RP and hit int win, check for obstruction
3132 : // between int win and ext win
3133 0 : if (!hitIntObsDisk) {
3134 0 : hitIntObsDisk = DayltgHitBetWinObstruction(state, IntWinDiskHitNum, IWin2, HitPtIntWinDisk, HP);
3135 : }
3136 : }
3137 0 : if (hitIntObsDisk) ObTransDisk = 0.0;
3138 0 : } // case where RP is in zone with interior window adjacent to zone with exterior window
3139 :
3140 : // hitExtObsDisk = false; //Unused Set but never used
3141 : // RJH 08-25-07 hitIntWinDisk should not be reset to false here, and should be tested below.
3142 : // This is to correct logic flaw causing direct solar to reach adjacent zone refpt
3143 : // when vector to sun does not pass through interior window
3144 : // hitIntWinDisk = false
3145 173 : if (!hitIntObsDisk) { // No interior obstruction was hit
3146 : // Net transmittance of exterior obstructions encountered by RAYCOS
3147 : // ObTransDisk = 1.0 will be returned if no exterior obstructions are hit.
3148 173 : ObTransDisk = DayltgHitObstruction(state, iHour, IWin2, RREF2, RAYCOS);
3149 : // if ( ObTransDisk < 1.0 ) hitExtObsDisk = true; //Unused Set but never used
3150 : // RJH 08-26-07 However, if this is a case of interior window
3151 : // and vector to sun does not pass through interior window
3152 : // then reset ObTransDisk to 0.0 since it is the key test for adding
3153 : // contribution of sun to RP below.
3154 173 : if ((extWinType == ExtWinType::AdjZone) && (!hitIntWinDisk)) {
3155 0 : ObTransDisk = 0.0;
3156 : }
3157 : }
3158 :
3159 : // PETER: need side wall mounted TDD to test this
3160 : // PETER: probably need to replace RREF2 with RWIN2
3161 : // PETER: need to check for interior obstructions too.
3162 :
3163 173 : if (ObTransDisk > 1.e-6) {
3164 :
3165 : // Sun reaches reference point; increment illuminance.
3166 : // Direct normal illuminance is normalized to 1.0
3167 :
3168 173 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Diffuser) {
3169 : // No beam is transmitted. Takes care of TDD with a bare diffuser and all types of blinds.
3170 0 : TVISS = 0.0;
3171 : } else {
3172 : // Beam transmittance for bare window and all types of blinds
3173 173 : TVISS = Window::POLYF(COSI, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
3174 173 : surfWin.lightWellEff;
3175 173 : if (extWinType == ExtWinType::AdjZone && hitIntWinDisk) TVISS *= TVISIntWinDisk;
3176 : }
3177 :
3178 173 : dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = RAYCOS.z * TVISS * ObTransDisk; // Bare window
3179 :
3180 173 : Real64 transBmBmMult = 0.0;
3181 :
3182 173 : if (ANY_BLIND(ShType)) {
3183 0 : auto const &surfShade = s_surf->surfShades(IWin);
3184 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
3185 0 : assert(matBlind != nullptr);
3186 :
3187 0 : Real64 ProfAng = ProfileAngle(state, IWin, RAYCOS, matBlind->SlatOrientation);
3188 0 : transBmBmMult = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
3189 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
3190 :
3191 173 : } else if (ShType == WinShadingType::ExtScreen) {
3192 : // pass angle from sun to window normal here using PHSUN and THSUN from above and surface angles
3193 : // SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
3194 : // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
3195 0 : auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
3196 0 : assert(screen != nullptr);
3197 :
3198 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
3199 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
3200 : int ip1, ip2, it1, it2;
3201 : BilinearInterpCoeffs coeffs;
3202 0 : Material::NormalizePhiTheta(phi, theta);
3203 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
3204 0 : GetBilinearInterpCoeffs(
3205 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
3206 0 : transBmBmMult = BilinearInterp(screen->btars[ip1][it1].BmTrans,
3207 0 : screen->btars[ip1][it2].BmTrans,
3208 0 : screen->btars[ip2][it1].BmTrans,
3209 0 : screen->btars[ip2][it2].BmTrans,
3210 : coeffs);
3211 :
3212 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk = RAYCOS.z * TVISS * transBmBmMult * ObTransDisk;
3213 : }
3214 :
3215 173 : if (CalledFrom == CalledFor::RefPoint) {
3216 : // Glare from solar disk
3217 :
3218 : // Position factor for sun (note that AZVIEW is wrt y-axis and THSUN is wrt
3219 : // x-axis of absolute coordinate system.
3220 112 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
3221 112 : Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
3222 : Real64 POSFAC =
3223 112 : DayltgGlarePositionFactor(XR, YR); // Position factor for a window element / ref point / view vector combination
3224 :
3225 112 : WindowSolidAngleDaylightPoint = s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd;
3226 :
3227 112 : if (POSFAC != 0.0 && WindowSolidAngleDaylightPoint > 0.000001) {
3228 : // Increment window luminance. Luminance of solar disk (cd/m2)
3229 : // is 1.47*10^4*(direct normal solar illuminance) for direct normal solar
3230 : // illuminance in lux (lumens/m2). For purposes of calculating daylight factors
3231 : // direct normal solar illuminance = 1.0.
3232 : // Solid angle subtended by sun is 0.000068 steradians
3233 :
3234 0 : XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) / std::pow(WindowSolidAngleDaylightPoint, 0.8);
3235 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = XAVWL * TVISS * ObTransDisk; // Bare window
3236 :
3237 0 : if (ANY_BLIND(ShType)) {
3238 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
3239 0 : } else if (ShType == WinShadingType::ExtScreen) {
3240 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk = XAVWL * TVISS * transBmBmMult * ObTransDisk;
3241 : }
3242 : } // Position Factor
3243 : } // if (calledFrom == RefPt)
3244 : } // if (ObTransDisk > 1e-6) // Beam avoids all obstructions
3245 : } // if (hitWin)
3246 : } // if (COSI > 0.0) // Sun on front side
3247 :
3248 : // Beam solar reaching reference point after beam-beam (specular) reflection from
3249 : // an exterior surface
3250 :
3251 2066 : if (s_surf->CalcSolRefl) {
3252 : // Receiving surface number corresponding this window
3253 0 : int RecSurfNum = s_surf->SurfShadowRecSurfNum(IWin2);
3254 0 : if (RecSurfNum > 0) { // interior windows do not apply
3255 0 : if (state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs > 0) {
3256 : bool hitRefl; // True iff ray hits reflecting surface
3257 0 : Vector3<Real64> HitPtRefl; // Point that ray hits reflecting surface
3258 0 : Vector3<Real64> SunVecMir; // Sun ray mirrored in reflecting surface
3259 0 : Vector3<Real64> ReflNorm; // Normal vector to reflecting surface
3260 : // This window has associated obstructions that could reflect beam onto the window
3261 0 : for (int loop = 1, loop_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs; loop <= loop_end;
3262 : ++loop) {
3263 0 : int ReflSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop);
3264 0 : int ReflSurfNumX = ReflSurfNum;
3265 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
3266 : // The following gets the correct side of a shadowing surface for reflection.
3267 0 : if (s_surf->Surface(ReflSurfNum).IsShadowing) {
3268 0 : if (dot(RAYCOS, s_surf->Surface(ReflSurfNum).OutNormVec) < 0.0) ReflSurfNumX = ReflSurfNum + 1;
3269 : }
3270 : // Require that the surface can have specular reflection
3271 0 : if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window || s_surf->SurfShadowGlazingFrac(ReflSurfNum) > 0.0) {
3272 0 : ReflNorm = s_surf->Surface(ReflSurfNumX).OutNormVec;
3273 : // Vector to sun that is mirrored in obstruction
3274 0 : SunVecMir = RAYCOS - 2.0 * dot(RAYCOS, ReflNorm) * ReflNorm;
3275 : // Skip if reflecting surface is not sunlit
3276 0 : if (state.dataHeatBal->SurfSunlitFrac(iHour, 1, ReflSurfNumX) < 0.01) continue;
3277 : // Skip if altitude angle of mirrored sun is negative since reflected sun cannot
3278 : // reach reference point in this case
3279 0 : if (SunVecMir.z <= 0.0) continue;
3280 : // Cosine of incidence angle of reflected beam on window
3281 0 : Real64 CosIncAngRec = dot(s_surf->Surface(IWin2).OutNormVec, SunVecMir);
3282 0 : if (CosIncAngRec <= 0.0) continue;
3283 : // Does ray from ref. pt. along SunVecMir pass through window?
3284 0 : hitWin = PierceSurface(state, IWin2, RREF2, SunVecMir, HP);
3285 0 : if (!hitWin) continue; // Ray did not pass through window
3286 : // Check if this ray hits interior obstructions
3287 0 : hit = DayltgHitInteriorObstruction(state, IWin2, RREF2, HP);
3288 0 : if (hit) continue; // Interior obstruction was hit
3289 : // Does ray hit this reflecting surface?
3290 0 : hitRefl = PierceSurface(state, ReflSurfNum, RREF2, SunVecMir, HitPtRefl);
3291 0 : if (!hitRefl) continue; // Ray did not hit this reflecting surface
3292 0 : Real64 ReflDistanceSq = distance_squared(HitPtRefl, RREF2);
3293 0 : Real64 ReflDistance = std::sqrt(ReflDistanceSq);
3294 : // Is ray from ref. pt. to reflection point (HitPtRefl) obstructed?
3295 0 : bool hitObsRefl = false;
3296 0 : Vector3<Real64> HitPtObs; // Hit point on obstruction
3297 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).NumPossibleObs;
3298 0 : loop2 <= loop2_end;
3299 : ++loop2) {
3300 0 : int const ObsSurfNum = state.dataSolarReflectionManager->SolReflRecSurf(RecSurfNum).PossibleObsSurfNums(loop2);
3301 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == s_surf->Surface(ReflSurfNum).BaseSurf) continue;
3302 0 : hitObs = PierceSurface(state, ObsSurfNum, RREF2, SunVecMir, ReflDistance, HitPtObs); // ReflDistance cutoff added
3303 0 : if (hitObs) { // => Could skip distance check (unless < vs <= ReflDistance really matters)
3304 0 : if (distance_squared(HitPtObs, RREF2) < ReflDistanceSq) { // Distance squared from ref pt to reflection point
3305 0 : hitObsRefl = true;
3306 0 : break;
3307 : }
3308 : }
3309 : }
3310 0 : if (hitObsRefl) continue; // Obstruction closer than reflection pt. was hit; go to next obstruction
3311 : // There is no obstruction for this ray between ref pt and hit pt on reflecting surface.
3312 : // See if ray from hit pt on reflecting surface to original (unmirrored) sun position is obstructed
3313 0 : hitObs = false;
3314 0 : if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
3315 : // Reflecting surface is a window.
3316 : // Receiving surface number for this reflecting window.
3317 0 : int ReflSurfRecNum = s_surf->SurfShadowRecSurfNum(ReflSurfNum);
3318 0 : if (ReflSurfRecNum > 0) {
3319 : // Loop over possible obstructions for this reflecting window
3320 0 : for (int loop2 = 1, loop2_end = state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).NumPossibleObs;
3321 0 : loop2 <= loop2_end;
3322 : ++loop2) {
3323 : int const ObsSurfNum =
3324 0 : state.dataSolarReflectionManager->SolReflRecSurf(ReflSurfRecNum).PossibleObsSurfNums(loop2);
3325 0 : hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
3326 0 : if (hitObs) break;
3327 : }
3328 : }
3329 : } else {
3330 : // Reflecting surface is a building shade
3331 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
3332 0 : if (ObsSurfNum == ReflSurfNum) continue;
3333 0 : hitObs = PierceSurface(state, ObsSurfNum, HitPtRefl, RAYCOS, HitPtObs);
3334 0 : if (hitObs) break;
3335 : }
3336 : } // End of check if reflector is a window or shadowing surface
3337 :
3338 0 : if (hitObs) continue; // Obstruction hit between reflection hit point and sun; go to next obstruction
3339 :
3340 : // No obstructions. Calculate reflected beam illuminance at ref. pt. from this reflecting surface.
3341 0 : SpecReflectance = 0.0;
3342 0 : Real64 CosIncAngRefl = std::abs(dot(RAYCOS, ReflNorm)); // Cos of angle of incidence of beam on reflecting surface
3343 0 : if (s_surf->Surface(ReflSurfNum).Class == SurfaceClass::Window) {
3344 0 : int const ConstrNumRefl = s_surf->SurfActiveConstruction(ReflSurfNum);
3345 : SpecReflectance =
3346 0 : Window::POLYF(std::abs(CosIncAngRefl), state.dataConstruction->Construct(ConstrNumRefl).ReflSolBeamFrontCoef);
3347 : }
3348 0 : if (s_surf->Surface(ReflSurfNum).IsShadowing && s_surf->SurfShadowGlazingConstruct(ReflSurfNum) > 0)
3349 0 : SpecReflectance =
3350 0 : s_surf->SurfShadowGlazingFrac(ReflSurfNum) *
3351 0 : Window::POLYF(
3352 : std::abs(CosIncAngRefl),
3353 0 : state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(ReflSurfNum)).ReflSolBeamFrontCoef);
3354 0 : TVisRefl = Window::POLYF(CosIncAngRec, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac *
3355 0 : surfWin.lightWellEff;
3356 0 : dl->dirIllum(iHour)[iWinCover_Bare].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl; // Bare window
3357 :
3358 0 : Real64 TransBmBmMultRefl = 0.0;
3359 0 : if (ANY_BLIND(ShType)) {
3360 0 : auto const &surfShade = s_surf->surfShades(IWin);
3361 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
3362 :
3363 0 : Real64 ProfAng = ProfileAngle(state, IWin, SunVecMir, matBlind->SlatOrientation);
3364 0 : TransBmBmMultRefl = matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
3365 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
3366 0 : } else if (ShType == WinShadingType::ExtScreen) {
3367 : // pass angle from sun to window normal here using PHSUN and THSUN from above and
3368 : // surface angles SunAltitudeToWindowNormalAngle = PHSUN - SurfaceWindow(IWin)%Phi
3369 : // SunAzimuthToWindowNormalAngle = THSUN - SurfaceWindow(IWin)%Theta
3370 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
3371 0 : assert(screen != nullptr);
3372 :
3373 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
3374 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
3375 : int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
3376 : BilinearInterpCoeffs coeffs;
3377 0 : Material::NormalizePhiTheta(phi, theta);
3378 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
3379 0 : GetBilinearInterpCoeffs(
3380 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
3381 :
3382 0 : TransBmBmMultRefl = BilinearInterp(screen->btars[ip1][it1].BmTrans,
3383 0 : screen->btars[ip1][it2].BmTrans,
3384 0 : screen->btars[ip2][it1].BmTrans,
3385 0 : screen->btars[ip2][it2].BmTrans,
3386 : coeffs);
3387 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sunDisk += SunVecMir.z * SpecReflectance * TVisRefl * TransBmBmMultRefl;
3388 : } // End of check if window has a blind or screen
3389 :
3390 : // Glare from reflected solar disk
3391 :
3392 0 : PHSUNrefl = SunVecMir.z;
3393 0 : THSUNrefl = std::atan2(SunVecMir.y, SunVecMir.x);
3394 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - THSUNrefl) + 0.001);
3395 0 : Real64 YR = std::tan(PHSUNrefl + 0.001);
3396 0 : Real64 POSFAC = DayltgGlarePositionFactor(XR, YR);
3397 0 : if (POSFAC != 0.0 && s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd > 0.000001) {
3398 0 : XAVWL = 14700.0 * std::sqrt(0.000068 * POSFAC) * double(NWX * NWY) /
3399 0 : std::pow(s_surf->SurfaceWindow(IWin).refPts(iRefPoint).solidAngWtd, 0.8);
3400 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk += XAVWL * TVisRefl * SpecReflectance; // Bare window
3401 0 : if (ANY_BLIND(ShType)) {
3402 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
3403 0 : } else if (ShType == WinShadingType::ExtScreen) {
3404 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += XAVWL * TVisRefl * SpecReflectance * TransBmBmMultRefl;
3405 : }
3406 : }
3407 0 : } // End of check that obstruction can specularly reflect
3408 : } // End of loop over obstructions associated with this window
3409 :
3410 0 : } // End of check if this window has associated obstructions
3411 : } // End of check to see if this is exterior type window
3412 : } // End of check if exterior reflection calculation is in effect
3413 :
3414 2066 : } // Last pass
3415 :
3416 453596 : if ((ICtrl > 0 && (ANY_BLIND(ShType) || ANY_SHADE_SCREEN(ShType))) || s_surf->SurfWinSolarDiffusing(IWin)) {
3417 :
3418 : // ----- CASE II -- WINDOW WITH SCREEN, SHADE, BLIND, OR DIFFUSING WINDOW
3419 : // Interior window visible transmittance multiplier for exterior window in adjacent zone
3420 0 : TVisIntWinMult = 1.0;
3421 0 : TVisIntWinDiskMult = 1.0;
3422 0 : if (s_surf->Surface(IWin).SolarEnclIndex != dl->daylightControl(daylightCtrlNum).enclIndex) {
3423 0 : TVisIntWinMult = TVISIntWin;
3424 0 : TVisIntWinDiskMult = TVISIntWinDisk;
3425 : }
3426 :
3427 0 : Real64 const DOMEGA_Ray_3_TVisIntWinMult(DOMEGA_Ray_3 * TVisIntWinMult);
3428 :
3429 0 : auto &wlumsk = dl->winLum(iHour)[iWinCover_Shaded];
3430 0 : auto &edirsk = dl->dirIllum(iHour)[iWinCover_Shaded];
3431 0 : auto &avwlsk = dl->avgWinLum(iHour)[iWinCover_Shaded];
3432 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3433 : // IF (.NOT.SurfaceWindow(IWin)%MovableSlats .AND. JB > 1) EXIT
3434 0 : avwlsk.sky[iSky] += wlumsk.sky[iSky] * TVisIntWinMult;
3435 0 : if (PHRAY > 0.0) edirsk.sky[iSky] += wlumsk.sky[iSky] * DOMEGA_Ray_3_TVisIntWinMult;
3436 : } // for (iSky)
3437 :
3438 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * TVisIntWinMult;
3439 0 : dl->avgWinLum(iHour)[iWinCover_Shaded].sunDisk += dl->winLum(iHour)[iWinCover_Shaded].sunDisk * TVisIntWinDiskMult;
3440 :
3441 0 : if (PHRAY > 0.0) {
3442 0 : dl->dirIllum(iHour)[iWinCover_Shaded].sun += dl->winLum(iHour)[iWinCover_Shaded].sun * DOMEGA_Ray_3_TVisIntWinMult;
3443 : }
3444 : }
3445 453596 : } // FigureDayltgCoeffsAtPointsForSunPosition()
3446 :
3447 1618 : void FigureRefPointDayltgFactorsToAddIllums(EnergyPlusData &state,
3448 : int const daylightCtrlNum, // Current daylighting control number
3449 : int const iRefPoint,
3450 : int const iHour,
3451 : int &ISunPos,
3452 : int const IWin,
3453 : int const loopwin,
3454 : int const NWX, // Number of window elements in x direction for dayltg calc
3455 : int const NWY, // Number of window elements in y direction for dayltg calc
3456 : int const ICtrl // Window control counter
3457 : )
3458 : {
3459 :
3460 : // SUBROUTINE INFORMATION:
3461 : // AUTHOR B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann
3462 : // DATE WRITTEN Oct. 2012
3463 :
3464 : // PURPOSE OF THIS SUBROUTINE:
3465 : // calculation worker routine to fill daylighting coefficients
3466 :
3467 : // METHODOLOGY EMPLOYED:
3468 : // this version is just for reference points.
3469 :
3470 : // SUBROUTINE PARAMETER DEFINITIONS:
3471 1618 : Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating the daylighting and glare factors
3472 :
3473 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3474 1618 : auto &dl = state.dataDayltg;
3475 1618 : auto &s_surf = state.dataSurface;
3476 :
3477 1618 : if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
3478 :
3479 566 : ++ISunPos;
3480 :
3481 : // Altitude of sun (degrees)
3482 566 : dl->sunAngles = dl->sunAnglesHr[iHour];
3483 :
3484 566 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
3485 :
3486 566 : auto &surf = s_surf->Surface(IWin);
3487 :
3488 566 : int const enclNum = surf.SolarEnclIndex;
3489 :
3490 : // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or fixed slat-angle blind;
3491 :
3492 : // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
3493 : // related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
3494 : // and interior surfaces with high visible reflectance.
3495 : // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
3496 : // the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
3497 :
3498 566 : auto &daylFacHr = thisDayltgCtrl.daylFac[iHour];
3499 :
3500 1698 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
3501 :
3502 1132 : auto const &gilsk = dl->horIllum[iHour];
3503 1132 : auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
3504 1132 : auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
3505 1132 : auto const &avwlsk = dl->avgWinLum(iHour)[iWinCover];
3506 :
3507 1132 : auto &daylFac = daylFacHr(loopwin, iRefPoint)[iWinCover];
3508 1132 : auto &illFac = daylFac[iLum_Illum];
3509 1132 : auto &sourceFac = daylFac[iLum_Source];
3510 1132 : auto &backFac = daylFac[iLum_Back];
3511 :
3512 5660 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
3513 :
3514 4528 : if (gilsk.sky[iSky] > tmpDFCalc) {
3515 4528 : illFac.sky[iSky] = (edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky];
3516 4528 : sourceFac.sky[iSky] = avwlsk.sky[iSky] / (NWX * NWY * gilsk.sky[iSky]);
3517 4528 : backFac.sky[iSky] = eintsk.sky[iSky] * dl->enclDaylight(enclNum).aveVisDiffReflect / (Constant::Pi * gilsk.sky[iSky]);
3518 : } else {
3519 0 : illFac.sky[iSky] = 0.0;
3520 0 : sourceFac.sky[iSky] = 0.0;
3521 0 : backFac.sky[iSky] = 0.0;
3522 : }
3523 :
3524 : } // for (iSky)
3525 :
3526 1132 : if (dl->horIllum[iHour].sun > tmpDFCalc) {
3527 1072 : daylFac[iLum_Illum].sun = (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
3528 2144 : daylFac[iLum_Illum].sunDisk =
3529 1072 : (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
3530 1072 : daylFac[iLum_Source].sun = dl->avgWinLum(iHour)[iWinCover].sun / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
3531 1072 : daylFac[iLum_Source].sunDisk = dl->avgWinLum(iHour)[iWinCover].sunDisk / (NWX * NWY * (dl->horIllum[iHour].sun + 0.0001));
3532 1072 : daylFac[iLum_Back].sun = dl->reflIllum(iHour)[iWinCover].sun * dl->enclDaylight(enclNum).aveVisDiffReflect /
3533 1072 : (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
3534 2144 : daylFac[iLum_Back].sunDisk = dl->reflIllum(iHour)[iWinCover].sunDisk * dl->enclDaylight(enclNum).aveVisDiffReflect /
3535 1072 : (Constant::Pi * (dl->horIllum[iHour].sun + 0.0001));
3536 : } else {
3537 60 : daylFac[iLum_Illum].sun = 0.0;
3538 60 : daylFac[iLum_Illum].sunDisk = 0.0;
3539 :
3540 60 : daylFac[iLum_Source].sun = 0.0;
3541 60 : daylFac[iLum_Source].sunDisk = 0.0;
3542 :
3543 60 : daylFac[iLum_Back].sun = 0.0;
3544 60 : daylFac[iLum_Back].sunDisk = 0.0;
3545 : }
3546 : } // for (jSH)
3547 :
3548 : // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
3549 566 : if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
3550 :
3551 0 : Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // Ratio of Tvis of fully-switched state to that of the unswitched state
3552 0 : auto &daylFac2 = daylFacHr(loopwin, iRefPoint)[iWinCover_Shaded];
3553 0 : auto const &daylFac1 = daylFacHr(loopwin, iRefPoint)[iWinCover_Bare];
3554 :
3555 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3556 0 : daylFac2[iLum_Illum].sky[iSky] = daylFac1[iLum_Illum].sky[iSky] * VTR;
3557 0 : daylFac2[iLum_Source].sky[iSky] = daylFac1[iLum_Source].sky[iSky] * VTR;
3558 0 : daylFac2[iLum_Back].sky[iSky] = daylFac1[iLum_Back].sky[iSky] * VTR;
3559 : } // for (iSky)
3560 :
3561 0 : daylFac2[iLum_Illum].sun = daylFac1[iLum_Illum].sun * VTR;
3562 0 : daylFac2[iLum_Source].sun = daylFac1[iLum_Source].sun * VTR;
3563 0 : daylFac2[iLum_Back].sun = daylFac1[iLum_Back].sun * VTR;
3564 0 : daylFac2[iLum_Illum].sunDisk = daylFac1[iLum_Illum].sunDisk * VTR;
3565 0 : daylFac2[iLum_Source].sunDisk = daylFac1[iLum_Source].sunDisk * VTR;
3566 0 : daylFac2[iLum_Back].sunDisk = daylFac1[iLum_Back].sunDisk * VTR;
3567 : } // ICtrl > 0
3568 : } // FigureRefPointDayltgFactorsToAddIllums()
3569 :
3570 2400 : void FigureMapPointDayltgFactorsToAddIllums(EnergyPlusData &state,
3571 : int const MapNum,
3572 : int const iMapPoint,
3573 : int const iHour,
3574 : int const IWin,
3575 : int const loopwin,
3576 : int const ICtrl // Window control counter
3577 : )
3578 : {
3579 :
3580 : // SUBROUTINE INFORMATION:
3581 : // AUTHOR B. Griffith, Oct 2012, derived from legacy code by Fred Winkelmann, Peter Ellis, Linda Lawrie
3582 : // DATE WRITTEN Nov. 2012
3583 :
3584 : // PURPOSE OF THIS SUBROUTINE:
3585 : // calculation worker routine to fill daylighting coefficients
3586 :
3587 : // METHODOLOGY EMPLOYED:
3588 : // this version is just for map points.
3589 :
3590 : // SUBROUTINE PARAMETER DEFINITIONS:
3591 2400 : Real64 constexpr tmpDFCalc(0.05); // cut off illuminance (lux) for exterior horizontal in calculating
3592 : // the daylighting and glare factors
3593 :
3594 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3595 2400 : auto &dl = state.dataDayltg;
3596 2400 : auto &s_surf = state.dataSurface;
3597 :
3598 2400 : if (s_surf->SurfSunCosHourly(iHour).z < DataEnvironment::SunIsUpValue) return;
3599 :
3600 : // Loop over shading index (1=bare window; 2=diffusing glazing, shade, screen or blind;
3601 :
3602 : // TH. 9/22/2009. CR 7625 - daylight illuminance spikes during some sunset hours due to the calculated sky and sun
3603 : // related daylight factors > 1, which theoretically can occur when sun is perpendicular to the window
3604 : // and interior surfaces with high visible reflectance.
3605 : // Added tmpDFCalc (default to 0.05 lux) as the cap for GILSK and GILSU in calculating the daylight factors
3606 : // the assumption behind it is if exterior horizontal surface does not get daylight, spaces do not get daylight.
3607 :
3608 1500 : auto &illumMap = dl->illumMaps(MapNum);
3609 1500 : auto &daylFacHr = illumMap.daylFac[iHour];
3610 4500 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
3611 :
3612 3000 : auto const &gilsk = dl->horIllum[iHour];
3613 3000 : auto const &edirsk = dl->dirIllum(iHour)[iWinCover];
3614 3000 : auto const &eintsk = dl->reflIllum(iHour)[iWinCover];
3615 3000 : auto &illSky = daylFacHr(loopwin, iMapPoint)[iWinCover];
3616 :
3617 15000 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) { // Loop over sky types
3618 12000 : illSky.sky[iSky] = (gilsk.sky[iSky] > tmpDFCalc) ? ((edirsk.sky[iSky] + eintsk.sky[iSky]) / gilsk.sky[iSky]) : 0.0;
3619 : } // for (iSky)
3620 :
3621 3000 : if (dl->horIllum[iHour].sun > tmpDFCalc) {
3622 2800 : daylFacHr(loopwin, iMapPoint)[iWinCover].sun =
3623 2800 : (dl->dirIllum(iHour)[iWinCover].sun + dl->reflIllum(iHour)[iWinCover].sun) / (dl->horIllum[iHour].sun + 0.0001);
3624 2800 : daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk =
3625 2800 : (dl->dirIllum(iHour)[iWinCover].sunDisk + dl->reflIllum(iHour)[iWinCover].sunDisk) / (dl->horIllum[iHour].sun + 0.0001);
3626 : } else {
3627 200 : daylFacHr(loopwin, iMapPoint)[iWinCover].sun = 0.0;
3628 200 : daylFacHr(loopwin, iMapPoint)[iWinCover].sunDisk = 0.0;
3629 : }
3630 : } // for (iWinCover)
3631 :
3632 : // For switchable glazing put daylighting factors for switched (dark) state in IS=2 location
3633 1500 : if (ICtrl > 0 && s_surf->WindowShadingControl(ICtrl).ShadingType == WinShadingType::SwitchableGlazing) {
3634 0 : Real64 VTR = s_surf->SurfWinVisTransRatio(IWin); // ratio of Tvis of switched to unswitched state
3635 0 : auto &illSky2 = daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded];
3636 0 : auto const &illSky1 = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare];
3637 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
3638 0 : illSky2.sky[iSky] = illSky1.sky[iSky] * VTR;
3639 : }
3640 :
3641 0 : daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sun = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sun * VTR;
3642 0 : daylFacHr(loopwin, iMapPoint)[iWinCover_Shaded].sunDisk = daylFacHr(loopwin, iMapPoint)[iWinCover_Bare].sunDisk * VTR;
3643 : } // ICtrl > 0
3644 : }
3645 :
3646 111 : void GetDaylightingParametersInput(EnergyPlusData &state)
3647 : {
3648 :
3649 : // SUBROUTINE INFORMATION:
3650 : // AUTHOR Linda Lawrie
3651 : // DATE WRITTEN Oct 2004
3652 :
3653 : // PURPOSE OF THIS SUBROUTINE:
3654 : // This subroutine provides a simple structure to get all daylighting
3655 : // parameters.
3656 111 : auto &dl = state.dataDayltg;
3657 111 : auto &s_surf = state.dataSurface;
3658 :
3659 111 : if (!dl->getDaylightingParametersInputFlag) return;
3660 110 : dl->getDaylightingParametersInputFlag = false;
3661 :
3662 110 : auto const &s_ipsc = state.dataIPShortCut;
3663 110 : s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
3664 110 : bool ErrorsFound = false;
3665 110 : int TotDaylightingControls = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
3666 110 : if (TotDaylightingControls > 0) {
3667 7 : dl->enclDaylight.allocate(state.dataViewFactor->NumOfSolarEnclosures);
3668 7 : GetInputDayliteRefPt(state, ErrorsFound);
3669 7 : GetDaylightingControls(state, ErrorsFound);
3670 7 : GeometryTransformForDaylighting(state);
3671 7 : GetInputIlluminanceMap(state, ErrorsFound);
3672 7 : GetLightWellData(state, ErrorsFound);
3673 7 : if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons, related to DAYLIGHTING");
3674 7 : DayltgSetupAdjZoneListsAndPointers(state);
3675 : }
3676 :
3677 110 : dl->maxNumRefPtInAnyDaylCtrl = 0;
3678 110 : dl->maxNumRefPtInAnyEncl = 0;
3679 : // Loop through all daylighting controls to find total reference points in each enclosure
3680 119 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
3681 9 : int numRefPoints = dl->daylightControl(daylightCtrlNum).TotalDaylRefPoints;
3682 9 : dl->maxNumRefPtInAnyDaylCtrl = max(numRefPoints, dl->maxNumRefPtInAnyDaylCtrl);
3683 : }
3684 251 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
3685 141 : dl->maxNumRefPtInAnyEncl = max(state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints, dl->maxNumRefPtInAnyEncl);
3686 : }
3687 :
3688 110 : dl->maxEnclSubSurfaces = max(maxval(state.dataHeatBal->Zone, &DataHeatBalance::ZoneData::NumSubSurfaces),
3689 110 : maxval(dl->enclDaylight, &EnclDaylightCalc::NumOfDayltgExtWins));
3690 :
3691 160 : for (int SurfNum : s_surf->AllHTWindowSurfaceList) {
3692 50 : auto const &surf = s_surf->Surface(SurfNum);
3693 50 : int const surfEnclNum = surf.SolarEnclIndex;
3694 50 : int const numEnclRefPoints = state.dataViewFactor->EnclSolInfo(surfEnclNum).TotalEnclosureDaylRefPoints;
3695 50 : auto &surfWin = s_surf->SurfaceWindow(SurfNum);
3696 50 : if (numEnclRefPoints > 0) {
3697 9 : if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
3698 9 : surfWin.refPts.allocate(numEnclRefPoints);
3699 23 : for (auto &refPt : surfWin.refPts) {
3700 14 : new (&refPt) SurfaceWindowRefPt();
3701 : }
3702 :
3703 9 : s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
3704 : }
3705 : } else {
3706 41 : int SurfNumAdj = surf.ExtBoundCond;
3707 41 : if (SurfNumAdj > 0) {
3708 0 : int const adjSurfEnclNum = s_surf->Surface(SurfNumAdj).SolarEnclIndex;
3709 0 : int const numAdjEnclRefPoints = state.dataViewFactor->EnclSolInfo(adjSurfEnclNum).TotalEnclosureDaylRefPoints;
3710 0 : if (numAdjEnclRefPoints > 0) {
3711 0 : if (!s_surf->SurfWinSurfDayLightInit(SurfNum)) {
3712 0 : surfWin.refPts.allocate(numAdjEnclRefPoints);
3713 0 : for (auto &refPt : surfWin.refPts) {
3714 0 : new (&refPt) SurfaceWindowRefPt();
3715 : }
3716 0 : s_surf->SurfWinSurfDayLightInit(SurfNum) = true;
3717 : }
3718 : }
3719 : }
3720 : }
3721 :
3722 50 : if (surf.ExtBoundCond != ExternalEnvironment) continue;
3723 :
3724 50 : if (!surf.HasShadeControl) continue;
3725 :
3726 9 : auto &thisSurfEnclosure(state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex));
3727 9 : if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).GlareControlIsActive) {
3728 : // Error if GlareControlIsActive but window is not in a Daylighting:Detailed zone
3729 0 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
3730 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3731 0 : ShowContinueError(state, "GlareControlIsActive = Yes but it is not in a Daylighting zone or enclosure.");
3732 0 : ShowContinueError(state, format("Zone or enclosure indicated={}", state.dataViewFactor->EnclSolInfo(surf.SolarEnclIndex).Name));
3733 0 : ErrorsFound = true;
3734 : }
3735 : // Error if GlareControlIsActive and window is in a Daylighting:Detailed zone/enclosure with
3736 : // an interior window adjacent to another Daylighting:Detailed zone/enclosure
3737 0 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3738 0 : for (int const intWin : thisSurfEnclosure.SurfacePtr) {
3739 0 : int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
3740 0 : if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
3741 0 : auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
3742 0 : if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3743 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3744 0 : ShowContinueError(state, "GlareControlIsActive = Yes and is in a Daylighting zone or enclosure");
3745 0 : ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
3746 0 : ShowContinueError(state, format("Adjacent Zone or Enclosure indicated={}", adjSurfEnclosure.Name));
3747 0 : ErrorsFound = true;
3748 : }
3749 : }
3750 : }
3751 : }
3752 : }
3753 :
3754 9 : if (s_surf->WindowShadingControl(surf.activeWindowShadingControl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) continue;
3755 :
3756 : // Error if window has shadingControlType = MeetDaylightingIlluminanceSetpoint &
3757 : // but is not in a Daylighting:Detailed zone
3758 0 : if (thisSurfEnclosure.TotalEnclosureDaylRefPoints == 0) {
3759 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3760 0 : ShowContinueError(state, "MeetDaylightingIlluminanceSetpoint but it is not in a Daylighting zone or enclosure.");
3761 0 : ShowContinueError(state, format("Zone or enclosure indicated={}", thisSurfEnclosure.Name));
3762 0 : ErrorsFound = true;
3763 0 : continue;
3764 : }
3765 :
3766 : // Error if window has shadingControlType = MeetDaylightIlluminanceSetpoint and is in a &
3767 : // Daylighting:Detailed zone with an interior window adjacent to another Daylighting:Detailed zone
3768 0 : for (int const intWin : thisSurfEnclosure.SurfacePtr) {
3769 0 : int const SurfNumAdj = s_surf->Surface(intWin).ExtBoundCond;
3770 0 : if (s_surf->Surface(intWin).Class == SurfaceClass::Window && SurfNumAdj > 0) {
3771 0 : auto &adjSurfEnclosure(state.dataViewFactor->EnclSolInfo(s_surf->Surface(SurfNumAdj).SolarEnclIndex));
3772 0 : if (adjSurfEnclosure.TotalEnclosureDaylRefPoints > 0) {
3773 0 : ShowSevereError(state, format("Window={} has Window Shading Control with", surf.Name));
3774 0 : ShowContinueError(state, "MeetDaylightIlluminanceSetpoint and is in a Daylighting zone or enclosure");
3775 0 : ShowContinueError(state, "that shares an interior window with another Daylighting zone or enclosure");
3776 0 : ShowContinueError(state, format("Adjacent Zone or enclosure indicated={}", adjSurfEnclosure.Name));
3777 0 : ErrorsFound = true;
3778 : }
3779 : }
3780 : }
3781 : } // for (SurfNum)
3782 :
3783 110 : if (!state.dataHeatBal->AnyAirBoundary) {
3784 930 : for (int SurfLoop = 1; SurfLoop <= s_surf->TotSurfaces; ++SurfLoop) {
3785 820 : auto const &surf = s_surf->Surface(SurfLoop);
3786 820 : if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
3787 :
3788 48 : int const enclOfSurf = surf.SolarEnclIndex;
3789 48 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclOfSurf);
3790 48 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow || !dl->enclDaylight(enclOfSurf).hasSplitFluxDaylighting)
3791 39 : continue;
3792 :
3793 9 : auto &surfWin = s_surf->SurfaceWindow(SurfLoop);
3794 23 : for (int refPtNum = 1; refPtNum <= enclSol.TotalEnclosureDaylRefPoints; ++refPtNum) {
3795 14 : auto &refPt = surfWin.refPts(refPtNum);
3796 42 : SetupOutputVariable(state,
3797 28 : format("Daylighting Window Reference Point {} Illuminance", refPtNum),
3798 : Constant::Units::lux,
3799 14 : refPt.illumFromWinRep,
3800 : OutputProcessor::TimeStepType::Zone,
3801 : OutputProcessor::StoreType::Average,
3802 14 : surf.Name);
3803 42 : SetupOutputVariable(state,
3804 28 : format("Daylighting Window Reference Point {} View Luminance", refPtNum),
3805 : Constant::Units::cd_m2,
3806 14 : refPt.lumWinRep,
3807 : OutputProcessor::TimeStepType::Zone,
3808 : OutputProcessor::StoreType::Average,
3809 14 : surf.Name);
3810 : }
3811 : }
3812 : } else {
3813 0 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
3814 0 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
3815 0 : for (int const enclSurfNum : enclSol.SurfacePtr) {
3816 0 : auto const &surf = s_surf->Surface(enclSurfNum);
3817 0 : auto &surfWindow = s_surf->SurfaceWindow(enclSurfNum);
3818 :
3819 0 : if (surf.Class != SurfaceClass::Window || !surf.ExtSolar) continue;
3820 :
3821 0 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || enclSol.HasInterZoneWindow) continue;
3822 :
3823 0 : auto const &enclDayltg = dl->enclDaylight(enclNum);
3824 0 : if (!enclDayltg.hasSplitFluxDaylighting) continue;
3825 :
3826 0 : int refPtCount = 0;
3827 0 : for (int controlNum : enclDayltg.daylightControlIndexes) {
3828 0 : auto const &control = dl->daylightControl(controlNum);
3829 0 : for (int refPtNum = 1; refPtNum <= control.TotalDaylRefPoints; ++refPtNum) {
3830 0 : ++refPtCount; // Count reference points across each daylighting control in the same enclosure
3831 0 : auto &refPt = surfWindow.refPts(refPtCount);
3832 0 : std::string varKey = format("{} to {}", surf.Name, state.dataDayltg->DaylRefPt(control.refPts(refPtNum).num).Name);
3833 0 : SetupOutputVariable(state,
3834 : "Daylighting Window Reference Point Illuminance",
3835 : Constant::Units::lux,
3836 0 : refPt.illumFromWinRep,
3837 : OutputProcessor::TimeStepType::Zone,
3838 : OutputProcessor::StoreType::Average,
3839 : varKey);
3840 0 : SetupOutputVariable(state,
3841 : "Daylighting Window Reference Point View Luminance",
3842 : Constant::Units::cd_m2,
3843 0 : refPt.lumWinRep,
3844 : OutputProcessor::TimeStepType::Zone,
3845 : OutputProcessor::StoreType::Average,
3846 : varKey);
3847 0 : }
3848 : } // for (controlNum)
3849 : } // for (enclSurfNum)
3850 : } // for (enclNum)
3851 : }
3852 :
3853 : // RJH DElight Modification Begin - Calls to DElight preprocessing subroutines
3854 110 : if (doesDayLightingUseDElight(state)) {
3855 0 : Real64 dLatitude = state.dataEnvrn->Latitude;
3856 0 : DisplayString(state, "Calculating DElight Daylighting Factors");
3857 0 : DElightManagerF::DElightInputGenerator(state);
3858 : // Init Error Flag to 0 (no Warnings or Errors)
3859 0 : DisplayString(state, "ReturnFrom DElightInputGenerator");
3860 0 : int iErrorFlag = 0;
3861 0 : DisplayString(state, "Calculating DElight DaylightCoefficients");
3862 0 : DElightManagerF::GenerateDElightDaylightCoefficients(dLatitude, iErrorFlag);
3863 : // Check Error Flag for Warnings or Errors returning from DElight
3864 : // RJH 2008-03-07: open file for READWRITE and DELETE file after processing
3865 0 : DisplayString(state, "ReturnFrom DElight DaylightCoefficients Calc");
3866 0 : if (iErrorFlag != 0) {
3867 : // Open DElight Daylight Factors Error File for reading
3868 0 : auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
3869 :
3870 : // Sequentially read lines in DElight Daylight Factors Error File
3871 : // and process them using standard EPlus warning/error handling calls
3872 : // Process all error/warning messages first
3873 : // Then, if any error has occurred, ShowFatalError to terminate processing
3874 0 : bool bEndofErrFile = !iDElightErrorFile.good();
3875 0 : std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
3876 0 : while (!bEndofErrFile) {
3877 0 : auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
3878 0 : if (cErrorLine.eof) {
3879 0 : bEndofErrFile = true;
3880 0 : continue;
3881 : }
3882 : // Is the current line a Warning message?
3883 0 : if (has_prefix(cErrorLine.data, "WARNING: ")) {
3884 0 : cErrorMsg = cErrorLine.data.substr(9);
3885 0 : ShowWarningError(state, cErrorMsg);
3886 : }
3887 : // Is the current line an Error message?
3888 0 : if (has_prefix(cErrorLine.data, "ERROR: ")) {
3889 0 : cErrorMsg = cErrorLine.data.substr(7);
3890 0 : ShowSevereError(state, cErrorMsg);
3891 0 : iErrorFlag = 1;
3892 : }
3893 0 : }
3894 :
3895 : // Close and Delete DElight Error File
3896 0 : if (iDElightErrorFile.is_open()) {
3897 0 : iDElightErrorFile.close();
3898 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
3899 : }
3900 :
3901 : // If any DElight Error occurred then ShowFatalError to terminate
3902 0 : if (iErrorFlag > 0) {
3903 0 : ErrorsFound = true;
3904 : }
3905 0 : } else {
3906 0 : if (FileSystem::fileExists(state.files.outputDelightDfdmpFilePath.filePath)) {
3907 0 : FileSystem::removeFile(state.files.outputDelightDfdmpFilePath.filePath);
3908 : }
3909 : }
3910 : }
3911 : // RJH DElight Modification End - Calls to DElight preprocessing subroutines
3912 :
3913 : // TH 6/3/2010, added to report daylight factors
3914 110 : s_ipsc->cCurrentModuleObject = "Output:DaylightFactors";
3915 110 : int NumReports = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
3916 110 : if (NumReports > 0) {
3917 : int NumNames;
3918 : int NumNumbers;
3919 : int IOStat;
3920 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
3921 1 : s_ipsc->cCurrentModuleObject,
3922 : 1,
3923 1 : s_ipsc->cAlphaArgs,
3924 : NumNames,
3925 1 : s_ipsc->rNumericArgs,
3926 : NumNumbers,
3927 : IOStat,
3928 1 : s_ipsc->lNumericFieldBlanks,
3929 1 : s_ipsc->lAlphaFieldBlanks,
3930 1 : s_ipsc->cAlphaFieldNames,
3931 1 : s_ipsc->cNumericFieldNames);
3932 1 : if (has_prefix(s_ipsc->cAlphaArgs(1), "SIZINGDAYS")) {
3933 1 : dl->DFSReportSizingDays = true;
3934 0 : } else if (has_prefix(s_ipsc->cAlphaArgs(1), "ALLSHADOWCALCULATIONDAYS")) {
3935 0 : dl->DFSReportAllShadowCalculationDays = true;
3936 : }
3937 : }
3938 :
3939 110 : if (ErrorsFound) ShowFatalError(state, "Program terminated for above reasons");
3940 : } // FigureMapPointDayltgFactorsToAddIllums()
3941 :
3942 8 : void GetInputIlluminanceMap(EnergyPlusData &state, bool &ErrorsFound)
3943 : {
3944 : // Perform the GetInput function for the Output:IlluminanceMap
3945 : // Glazer - June 2016 (moved from GetDaylightingControls)
3946 8 : auto &dl = state.dataDayltg;
3947 8 : auto const &s_surf = state.dataSurface;
3948 :
3949 8 : Array1D_bool ZoneMsgDone;
3950 :
3951 8 : Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
3952 8 : Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
3953 : // these are only for Building Rotation for Appendix G when using world coordinate system
3954 8 : Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
3955 8 : Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
3956 :
3957 8 : bool doTransform = false;
3958 8 : Real64 OldAspectRatio = 1.0;
3959 8 : Real64 NewAspectRatio = 1.0;
3960 :
3961 8 : CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
3962 :
3963 8 : auto &ip = state.dataInputProcessing->inputProcessor;
3964 8 : auto const &s_ipsc = state.dataIPShortCut;
3965 8 : s_ipsc->cCurrentModuleObject = "Output:IlluminanceMap";
3966 8 : int TotIllumMaps = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
3967 :
3968 8 : dl->illumMaps.allocate(TotIllumMaps);
3969 :
3970 8 : if (TotIllumMaps > 0) {
3971 : int IOStat;
3972 : int NumAlpha;
3973 : int NumNumber;
3974 2 : auto &ip = state.dataInputProcessing->inputProcessor;
3975 4 : for (int MapNum = 1; MapNum <= TotIllumMaps; ++MapNum) {
3976 4 : ip->getObjectItem(state,
3977 2 : s_ipsc->cCurrentModuleObject,
3978 : MapNum,
3979 2 : s_ipsc->cAlphaArgs,
3980 : NumAlpha,
3981 2 : s_ipsc->rNumericArgs,
3982 : NumNumber,
3983 : IOStat,
3984 2 : s_ipsc->lNumericFieldBlanks,
3985 2 : s_ipsc->lAlphaFieldBlanks,
3986 2 : s_ipsc->cAlphaFieldNames,
3987 2 : s_ipsc->cNumericFieldNames);
3988 :
3989 2 : auto &illumMap = dl->illumMaps(MapNum);
3990 2 : illumMap.Name = s_ipsc->cAlphaArgs(1);
3991 2 : int const zoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
3992 2 : if (zoneNum > 0) {
3993 2 : illumMap.zoneIndex = zoneNum;
3994 : // set enclosure index for first space in zone
3995 2 : int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
3996 2 : illumMap.enclIndex = enclNum;
3997 : // check that all spaces in the zone are in the same enclosure
3998 2 : for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
3999 0 : int spaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
4000 0 : if (enclNum != state.dataHeatBal->space(spaceNum).solarEnclosureNum) {
4001 0 : ShowSevereError(state,
4002 0 : format("{}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting illuminance maps.",
4003 0 : s_ipsc->cCurrentModuleObject,
4004 0 : s_ipsc->cAlphaArgs(1)));
4005 0 : ShowContinueError(
4006 0 : state, format("Zone=\"{}\" spans multiple enclosures. Use a Space Name instead.", state.dataHeatBal->Zone(zoneNum).Name));
4007 0 : ErrorsFound = true;
4008 0 : break;
4009 : }
4010 : }
4011 : } else {
4012 0 : int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
4013 0 : if (spaceNum == 0) {
4014 0 : ShowSevereError(state,
4015 0 : format("{}=\"{}\", invalid {}=\"{}\".",
4016 0 : s_ipsc->cCurrentModuleObject,
4017 0 : s_ipsc->cAlphaArgs(1),
4018 0 : s_ipsc->cAlphaFieldNames(2),
4019 0 : s_ipsc->cAlphaArgs(2)));
4020 0 : ErrorsFound = true;
4021 : } else {
4022 0 : illumMap.spaceIndex = spaceNum;
4023 0 : illumMap.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
4024 0 : illumMap.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
4025 0 : assert(illumMap.enclIndex > 0);
4026 : }
4027 : }
4028 :
4029 2 : illumMap.Z = s_ipsc->rNumericArgs(1);
4030 2 : illumMap.Xmin = s_ipsc->rNumericArgs(2);
4031 2 : illumMap.Xmax = s_ipsc->rNumericArgs(3);
4032 2 : if (s_ipsc->rNumericArgs(2) > s_ipsc->rNumericArgs(3)) {
4033 0 : ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
4034 0 : ShowContinueError(state,
4035 0 : format("...{} {:.2R} must be <= {} {:.2R}.",
4036 0 : s_ipsc->cNumericFieldNames(2),
4037 0 : s_ipsc->rNumericArgs(2),
4038 0 : s_ipsc->cNumericFieldNames(3),
4039 0 : s_ipsc->rNumericArgs(3)));
4040 0 : ErrorsFound = true;
4041 : }
4042 2 : illumMap.Xnum = s_ipsc->rNumericArgs(4);
4043 2 : illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
4044 :
4045 2 : illumMap.Ymin = s_ipsc->rNumericArgs(5);
4046 2 : illumMap.Ymax = s_ipsc->rNumericArgs(6);
4047 2 : if (s_ipsc->rNumericArgs(5) > s_ipsc->rNumericArgs(6)) {
4048 0 : ShowSevereError(state, format("{}=\"{}\", invalid entry.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
4049 0 : ShowContinueError(state,
4050 0 : format("...{} {:.2R} must be <= {} {:.2R}.",
4051 0 : s_ipsc->cNumericFieldNames(5),
4052 0 : s_ipsc->rNumericArgs(5),
4053 0 : s_ipsc->cNumericFieldNames(6),
4054 0 : s_ipsc->rNumericArgs(6)));
4055 0 : ErrorsFound = true;
4056 : }
4057 2 : illumMap.Ynum = s_ipsc->rNumericArgs(7);
4058 2 : illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
4059 :
4060 2 : if (illumMap.Xnum * illumMap.Ynum > MaxMapRefPoints) {
4061 0 : ShowSevereError(state, format("{}=\"{}\", too many map points specified.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
4062 0 : ShowContinueError(state,
4063 0 : format("...{}[{}] * {}[{}].= [{}] must be <= [{}].",
4064 0 : s_ipsc->cNumericFieldNames(4),
4065 0 : illumMap.Xnum,
4066 0 : s_ipsc->cNumericFieldNames(7),
4067 0 : illumMap.Ynum,
4068 0 : illumMap.Xnum * illumMap.Ynum,
4069 : MaxMapRefPoints));
4070 0 : ErrorsFound = true;
4071 : }
4072 : } // MapNum
4073 2 : s_ipsc->cCurrentModuleObject = "OutputControl:IlluminanceMap:Style";
4074 2 : int MapStyleIn = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4075 :
4076 2 : if (MapStyleIn == 0) {
4077 1 : s_ipsc->cAlphaArgs(1) = "COMMA";
4078 1 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4079 1 : } else if (MapStyleIn == 1) {
4080 2 : ip->getObjectItem(state,
4081 1 : s_ipsc->cCurrentModuleObject,
4082 : 1,
4083 1 : s_ipsc->cAlphaArgs,
4084 : NumAlpha,
4085 1 : s_ipsc->rNumericArgs,
4086 : NumNumber,
4087 : IOStat,
4088 1 : s_ipsc->lNumericFieldBlanks,
4089 1 : s_ipsc->lAlphaFieldBlanks,
4090 1 : s_ipsc->cAlphaFieldNames,
4091 1 : s_ipsc->cNumericFieldNames);
4092 1 : if (s_ipsc->cAlphaArgs(1) == "COMMA") {
4093 1 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4094 0 : } else if (s_ipsc->cAlphaArgs(1) == "TAB") {
4095 0 : dl->MapColSep = DataStringGlobals::CharTab; // tab
4096 0 : } else if (s_ipsc->cAlphaArgs(1) == "FIXED" || s_ipsc->cAlphaArgs(1) == "SPACE") {
4097 0 : dl->MapColSep = DataStringGlobals::CharSpace; // space
4098 : } else {
4099 0 : dl->MapColSep = DataStringGlobals::CharComma; // comma
4100 0 : ShowWarningError(state,
4101 0 : format("{}: invalid {}=\"{}\", Commas will be used to separate fields.",
4102 0 : s_ipsc->cCurrentModuleObject,
4103 0 : s_ipsc->cAlphaFieldNames(1),
4104 0 : s_ipsc->cAlphaArgs(1)));
4105 0 : s_ipsc->cAlphaArgs(1) = "COMMA";
4106 : }
4107 : }
4108 2 : print(state.files.eio, "! <Daylighting:Illuminance Maps>,#Maps,Style\n");
4109 2 : ConvertCaseToLower(s_ipsc->cAlphaArgs(1), s_ipsc->cAlphaArgs(2));
4110 2 : s_ipsc->cAlphaArgs(1).erase(1);
4111 2 : s_ipsc->cAlphaArgs(1) += s_ipsc->cAlphaArgs(2).substr(1);
4112 2 : print(state.files.eio, "Daylighting:Illuminance Maps,{},{}\n", TotIllumMaps, s_ipsc->cAlphaArgs(1));
4113 : }
4114 :
4115 : // Check for illuminance maps associated with this zone
4116 10 : for (auto &illumMap : dl->illumMaps) {
4117 :
4118 2 : if (illumMap.zoneIndex == 0) continue;
4119 :
4120 2 : auto &zone = state.dataHeatBal->Zone(illumMap.zoneIndex);
4121 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
4122 2 : Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
4123 2 : Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
4124 :
4125 2 : if (illumMap.Xnum * illumMap.Ynum == 0) continue;
4126 :
4127 : // Add additional daylighting reference points for map
4128 2 : illumMap.TotalMapRefPoints = illumMap.Xnum * illumMap.Ynum;
4129 2 : illumMap.refPts.allocate(illumMap.TotalMapRefPoints);
4130 212 : for (auto &refPt : illumMap.refPts) {
4131 210 : new (&refPt) DaylMapPt();
4132 : }
4133 :
4134 2 : if (illumMap.TotalMapRefPoints > MaxMapRefPoints) {
4135 0 : ShowSevereError(state, "GetDaylighting Parameters: Total Map Reference points entered is greater than maximum allowed.");
4136 0 : ShowContinueError(state, format("Occurs in Zone={}", zone.Name));
4137 0 : ShowContinueError(state,
4138 0 : format("Maximum reference points allowed={}, entered amount ( when error first occurred )={}",
4139 : MaxMapRefPoints,
4140 0 : illumMap.TotalMapRefPoints));
4141 0 : ErrorsFound = true;
4142 0 : break;
4143 : }
4144 :
4145 : // Calc cos and sin of Zone Relative North values for later use in transforming Map Point coordinates
4146 : // CosZoneRelNorth = std::cos( -zone.RelNorth * DegToRadians ); //Tuned These should not be changing
4147 : // SinZoneRelNorth = std::sin( -zone.RelNorth * DegToRadians );
4148 2 : illumMap.Xinc = (illumMap.Xnum != 1) ? ((illumMap.Xmax - illumMap.Xmin) / (illumMap.Xnum - 1)) : 0.0;
4149 2 : illumMap.Yinc = (illumMap.Ynum != 1) ? ((illumMap.Ymax - illumMap.Ymin) / (illumMap.Ynum - 1)) : 0.0;
4150 :
4151 : // Map points and increments are stored in AbsCoord and then that is operated on if relative coords entered.
4152 23 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
4153 231 : for (int X = 1; X <= illumMap.Xnum; ++X) {
4154 210 : int iRefPt = (Y - 1) * illumMap.Xnum + X;
4155 210 : auto &refPt = illumMap.refPts(iRefPt);
4156 210 : refPt.absCoords = {illumMap.Xmin + (X - 1) * illumMap.Xinc, illumMap.Ymin + (Y - 1) * illumMap.Yinc, illumMap.Z};
4157 : }
4158 : }
4159 :
4160 23 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
4161 231 : for (int X = 1; X <= illumMap.Xnum; ++X) {
4162 210 : int iRefPt = (Y - 1) * illumMap.Xnum + X;
4163 210 : auto &refPt = illumMap.refPts(iRefPt);
4164 :
4165 210 : if (!s_surf->DaylRefWorldCoordSystem) {
4166 210 : Real64 Xb = refPt.absCoords.x * CosZoneRelNorth - refPt.absCoords.y * SinZoneRelNorth + zone.OriginX;
4167 210 : Real64 Yb = refPt.absCoords.x * SinZoneRelNorth + refPt.absCoords.y * CosZoneRelNorth + zone.OriginY;
4168 210 : refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
4169 210 : refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
4170 210 : refPt.absCoords.z += zone.OriginZ;
4171 210 : if (doTransform) {
4172 0 : Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
4173 0 : Real64 Yo = refPt.absCoords.y;
4174 : // next derotate the building
4175 0 : Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
4176 0 : Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
4177 : // translate
4178 0 : Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
4179 0 : Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
4180 : // rerotate
4181 0 : refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
4182 :
4183 0 : refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
4184 : }
4185 : } else {
4186 0 : Real64 Xb = refPt.absCoords.x;
4187 0 : Real64 Yb = refPt.absCoords.y;
4188 0 : refPt.absCoords.x = Xb * CosBldgRotAppGonly - Yb * SinBldgRotAppGonly;
4189 0 : refPt.absCoords.y = Xb * SinBldgRotAppGonly + Yb * CosBldgRotAppGonly;
4190 : }
4191 210 : if (iRefPt == 1) {
4192 2 : illumMap.Xmin = refPt.absCoords.x;
4193 2 : illumMap.Ymin = refPt.absCoords.y;
4194 2 : illumMap.Xmax = refPt.absCoords.x;
4195 2 : illumMap.Ymax = refPt.absCoords.y;
4196 2 : illumMap.Z = refPt.absCoords.z;
4197 : }
4198 210 : illumMap.Xmin = min(illumMap.Xmin, refPt.absCoords.x);
4199 210 : illumMap.Ymin = min(illumMap.Ymin, refPt.absCoords.y);
4200 210 : illumMap.Xmax = max(illumMap.Xmax, refPt.absCoords.x);
4201 210 : illumMap.Ymax = max(illumMap.Ymax, refPt.absCoords.y);
4202 210 : if ((refPt.absCoords.x < zone.MinimumX && (zone.MinimumX - refPt.absCoords.x) > 0.001) ||
4203 210 : (refPt.absCoords.x > zone.MaximumX && (refPt.absCoords.x - zone.MaximumX) > 0.001) ||
4204 100 : (refPt.absCoords.y < zone.MinimumY && (zone.MinimumY - refPt.absCoords.y) > 0.001) ||
4205 100 : (refPt.absCoords.y > zone.MaximumY && (refPt.absCoords.y - zone.MaximumY) > 0.001) ||
4206 100 : (refPt.absCoords.z < zone.MinimumZ && (zone.MinimumZ - refPt.absCoords.z) > 0.001) ||
4207 100 : (refPt.absCoords.z > zone.MaximumZ && (refPt.absCoords.z - zone.MaximumZ) > 0.001)) {
4208 110 : refPt.inBounds = false;
4209 : }
4210 :
4211 : // Test extremes of Map Points against Zone Min/Max
4212 212 : if (iRefPt != 1 && iRefPt != illumMap.TotalMapRefPoints) continue;
4213 :
4214 4 : if (refPt.inBounds) continue;
4215 :
4216 2 : if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
4217 4 : ShowWarningError(
4218 : state,
4219 4 : format("GetInputIlluminanceMap: Reference Map point #[{}], X Value outside Zone Min/Max X, Zone={}", iRefPt, zone.Name));
4220 4 : ShowContinueError(state,
4221 4 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
4222 2 : refPt.absCoords.x,
4223 2 : zone.MinimumX,
4224 2 : zone.MaximumX));
4225 4 : ShowContinueError(
4226 : state,
4227 4 : format("...X Reference Distance Outside MinimumX= {:.4R} m.",
4228 4 : (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
4229 : }
4230 2 : if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
4231 4 : ShowWarningError(
4232 : state,
4233 4 : format("GetInputIlluminanceMap: Reference Map point #[{}], Y Value outside Zone Min/Max Y, Zone={}", iRefPt, zone.Name));
4234 4 : ShowContinueError(state,
4235 4 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
4236 2 : refPt.absCoords.y,
4237 2 : zone.MinimumY,
4238 2 : zone.MaximumY));
4239 4 : ShowContinueError(
4240 : state,
4241 4 : format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
4242 4 : (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
4243 : }
4244 2 : if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
4245 0 : ShowWarningError(
4246 : state,
4247 0 : format("GetInputIlluminanceMap: Reference Map point #[{}], Z Value outside Zone Min/Max Z, Zone={}", iRefPt, zone.Name));
4248 0 : ShowContinueError(state,
4249 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
4250 0 : refPt.absCoords.z,
4251 0 : zone.MinimumZ,
4252 0 : zone.MaximumZ));
4253 0 : ShowContinueError(
4254 : state,
4255 0 : format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
4256 0 : (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
4257 : }
4258 : } // for (X)
4259 : } // for (Y)
4260 : } // for (MapNum)
4261 :
4262 8 : ZoneMsgDone.dimension(state.dataGlobal->NumOfZones, false);
4263 10 : for (auto const &illumMap : dl->illumMaps) {
4264 2 : if (illumMap.zoneIndex == 0) continue;
4265 2 : int enclNum = illumMap.enclIndex;
4266 2 : if (!dl->enclDaylight(enclNum).hasSplitFluxDaylighting && !ZoneMsgDone(illumMap.zoneIndex)) {
4267 2 : ShowSevereError(state,
4268 2 : format("Zone Name in Output:IlluminanceMap is not used for Daylighting:Controls={}",
4269 1 : state.dataHeatBal->Zone(illumMap.zoneIndex).Name));
4270 1 : ErrorsFound = true;
4271 : }
4272 : }
4273 8 : ZoneMsgDone.deallocate();
4274 8 : if (ErrorsFound) return;
4275 :
4276 7 : if (TotIllumMaps > 0) {
4277 1 : print(state.files.eio,
4278 : "! <Daylighting:Illuminance Maps:Detail>,Name,Zone,XMin {{m}},XMax {{m}},Xinc {{m}},#X Points,YMin "
4279 : "{{m}},YMax {{m}},Yinc {{m}},#Y Points,Z {{m}}\n");
4280 : }
4281 8 : for (auto const &illumMap : dl->illumMaps) {
4282 1 : print(state.files.eio,
4283 : "Daylighting:Illuminance Maps:Detail,{},{},{:.2R},{:.2R},{:.2R},{},{:.2R},{:.2R},{:.2R},{},{:.2R}\n",
4284 1 : illumMap.Name,
4285 1 : state.dataHeatBal->Zone(illumMap.zoneIndex).Name,
4286 1 : illumMap.Xmin,
4287 1 : illumMap.Xmax,
4288 1 : illumMap.Xinc,
4289 1 : illumMap.Xnum,
4290 1 : illumMap.Ymin,
4291 1 : illumMap.Ymax,
4292 1 : illumMap.Yinc,
4293 1 : illumMap.Ynum,
4294 1 : illumMap.Z);
4295 : }
4296 :
4297 8 : } // GetInputIlluminanceMap()
4298 :
4299 11 : void GetDaylightingControls(EnergyPlusData &state, bool &ErrorsFound)
4300 : {
4301 : // AUTHOR Fred Winkelmann
4302 : // DATE WRITTEN March 2002
4303 : // MODIFIED Glazer - July 2016 - Move geometry transformation portion, rearrange input, allow more than three reference points
4304 : // Obtain the user input data for Daylighting:Controls object in the input file.
4305 :
4306 : static constexpr std::string_view routineName = "GetDaylightingControls";
4307 :
4308 11 : auto &dl = state.dataDayltg;
4309 :
4310 : int IOStat;
4311 : int NumAlpha;
4312 : int NumNumber;
4313 :
4314 : // Smallest deviation from unity for the sum of all fractions
4315 : // Accept approx 4 to 8 ULP error (technically abs(1.0 + sumFracs) should be close to 2)
4316 : // constexpr Real64 FractionTolerance(4 * std::numeric_limits<Real64>::epsilon());
4317 : // Instead, we use a 0.001 = 0.1% tolerance
4318 11 : constexpr Real64 FractionTolerance(0.001);
4319 :
4320 11 : auto &ip = state.dataInputProcessing->inputProcessor;
4321 11 : auto const &s_ipsc = state.dataIPShortCut;
4322 11 : s_ipsc->cCurrentModuleObject = "Daylighting:Controls";
4323 11 : int totDaylightingControls = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4324 11 : dl->daylightControl.allocate(totDaylightingControls);
4325 11 : Array1D<bool> spaceHasDaylightingControl;
4326 11 : spaceHasDaylightingControl.dimension(state.dataGlobal->numSpaces, false);
4327 : // Reset to zero in case this is called more than once in unit tests
4328 24 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
4329 13 : state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints = 0;
4330 : }
4331 24 : for (int controlNum = 1; controlNum <= totDaylightingControls; ++controlNum) {
4332 13 : s_ipsc->cAlphaArgs = "";
4333 13 : s_ipsc->rNumericArgs = 0.0;
4334 26 : ip->getObjectItem(state,
4335 13 : s_ipsc->cCurrentModuleObject,
4336 : controlNum,
4337 13 : s_ipsc->cAlphaArgs,
4338 : NumAlpha,
4339 13 : s_ipsc->rNumericArgs,
4340 : NumNumber,
4341 : IOStat,
4342 13 : s_ipsc->lNumericFieldBlanks,
4343 13 : s_ipsc->lAlphaFieldBlanks,
4344 13 : s_ipsc->cAlphaFieldNames,
4345 13 : s_ipsc->cNumericFieldNames);
4346 :
4347 13 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
4348 :
4349 13 : auto &daylightControl = dl->daylightControl(controlNum);
4350 13 : daylightControl.Name = s_ipsc->cAlphaArgs(1);
4351 :
4352 : // Is it a zone or space name?
4353 13 : int const zoneNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->Zone);
4354 13 : if (zoneNum > 0) {
4355 13 : daylightControl.zoneIndex = zoneNum;
4356 : // set enclosure index for first space in zone
4357 13 : int enclNum = state.dataHeatBal->space(state.dataHeatBal->Zone(zoneNum).spaceIndexes(1)).solarEnclosureNum;
4358 13 : daylightControl.enclIndex = enclNum;
4359 : // check that all spaces in the zone are in the same enclosure
4360 13 : for (int spaceCounter = 2; spaceCounter <= state.dataHeatBal->Zone(zoneNum).numSpaces; ++spaceCounter) {
4361 0 : int zoneSpaceNum = state.dataHeatBal->Zone(zoneNum).spaceIndexes(spaceCounter);
4362 0 : if (daylightControl.enclIndex != state.dataHeatBal->space(zoneSpaceNum).solarEnclosureNum) {
4363 0 : ShowSevereError(state,
4364 0 : format("{}: invalid {}=\"{}\" All spaces in the zone must be in the same enclosure for daylighting.",
4365 0 : s_ipsc->cCurrentModuleObject,
4366 0 : s_ipsc->cAlphaFieldNames(2),
4367 0 : s_ipsc->cAlphaArgs(2)));
4368 0 : ErrorsFound = true;
4369 0 : break;
4370 : }
4371 : }
4372 26 : for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
4373 : // Check if this is a duplicate
4374 13 : if (spaceHasDaylightingControl(zoneSpaceNum)) {
4375 0 : ShowWarningError(state,
4376 0 : format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
4377 0 : s_ipsc->cCurrentModuleObject,
4378 0 : daylightControl.Name,
4379 0 : state.dataHeatBal->space(zoneSpaceNum).Name,
4380 0 : s_ipsc->cCurrentModuleObject));
4381 0 : ShowContinueError(state, "This control will override the lighting power factor for this space.");
4382 : }
4383 13 : spaceHasDaylightingControl(zoneSpaceNum) = true;
4384 : }
4385 : } else {
4386 0 : int const spaceNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataHeatBal->space);
4387 0 : if (spaceNum == 0) {
4388 0 : ShowSevereError(state,
4389 0 : format("{}: invalid {}=\"{}\".", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2)));
4390 0 : ErrorsFound = true;
4391 0 : continue;
4392 : } else {
4393 0 : daylightControl.spaceIndex = spaceNum;
4394 0 : daylightControl.zoneIndex = state.dataHeatBal->space(spaceNum).zoneNum;
4395 0 : daylightControl.enclIndex = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
4396 : // Check if this is a duplicate
4397 0 : if (spaceHasDaylightingControl(spaceNum)) {
4398 0 : ShowWarningError(state,
4399 0 : format("{}=\"{}\" Space=\"{}\" already has a {} object assigned to it.",
4400 0 : s_ipsc->cCurrentModuleObject,
4401 0 : daylightControl.Name,
4402 0 : state.dataHeatBal->space(spaceNum).Name,
4403 0 : s_ipsc->cCurrentModuleObject));
4404 0 : ShowContinueError(state, "This control will override the lighting power factor for this space.");
4405 : }
4406 0 : spaceHasDaylightingControl(spaceNum) = true;
4407 : }
4408 : }
4409 :
4410 13 : dl->enclDaylight(daylightControl.enclIndex).daylightControlIndexes.emplace_back(controlNum);
4411 13 : daylightControl.ZoneName = state.dataHeatBal->Zone(daylightControl.zoneIndex).Name;
4412 :
4413 13 : if (s_ipsc->lAlphaFieldBlanks(3)) {
4414 0 : daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
4415 : } else {
4416 13 : daylightControl.DaylightMethod =
4417 13 : static_cast<DaylightingMethod>(getEnumValue(DaylightingMethodNamesUC, Util::makeUPPER(s_ipsc->cAlphaArgs(3))));
4418 :
4419 13 : if (daylightControl.DaylightMethod == DaylightingMethod::Invalid) {
4420 0 : daylightControl.DaylightMethod = DaylightingMethod::SplitFlux;
4421 0 : ShowWarningError(state,
4422 0 : format("Invalid {} = {}, occurs in {}object for {}=\"{}",
4423 0 : s_ipsc->cAlphaFieldNames(3),
4424 0 : s_ipsc->cAlphaArgs(3),
4425 0 : s_ipsc->cCurrentModuleObject,
4426 0 : s_ipsc->cCurrentModuleObject,
4427 0 : s_ipsc->cAlphaArgs(1)));
4428 0 : ShowContinueError(state, "SplitFlux assumed, and the simulation continues.");
4429 : }
4430 : }
4431 13 : dl->enclDaylight(daylightControl.enclIndex).hasSplitFluxDaylighting |= (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux);
4432 :
4433 13 : if (s_ipsc->lAlphaFieldBlanks(4)) { // Field: Availability Schedule Name
4434 13 : daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
4435 0 : } else if ((daylightControl.availSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
4436 0 : ShowWarningItemNotFound(state,
4437 : eoh,
4438 0 : s_ipsc->cAlphaFieldNames(4),
4439 0 : s_ipsc->cAlphaArgs(4),
4440 : "Schedule was not found so controls will always be available, and the simulation continues.");
4441 0 : daylightControl.availSched = Sched::GetScheduleAlwaysOn(state);
4442 : }
4443 :
4444 13 : daylightControl.LightControlType = static_cast<LtgCtrlType>(getEnumValue(LtgCtrlTypeNamesUC, s_ipsc->cAlphaArgs(5)));
4445 13 : if (daylightControl.LightControlType == LtgCtrlType::Invalid) {
4446 0 : ShowWarningInvalidKey(
4447 0 : state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), "Continuous assumed, and the simulation continues.");
4448 : }
4449 :
4450 13 : daylightControl.MinPowerFraction = s_ipsc->rNumericArgs(1); // Field: Minimum Input Power Fraction for Continuous Dimming Control
4451 13 : daylightControl.MinLightFraction = s_ipsc->rNumericArgs(2); // Field: Minimum Light Output Fraction for Continuous Dimming Control
4452 13 : daylightControl.LightControlSteps = s_ipsc->rNumericArgs(3); // Field: Number of Stepped Control Steps
4453 13 : daylightControl.LightControlProbability =
4454 13 : s_ipsc->rNumericArgs(4); // Field: Probability Lighting will be Reset When Needed in Manual Stepped Control
4455 :
4456 13 : if (!s_ipsc->lAlphaFieldBlanks(6)) { // Field: Glare Calculation Daylighting Reference Point Name
4457 11 : daylightControl.glareRefPtNumber = Util::FindItemInList(s_ipsc->cAlphaArgs(6),
4458 11 : dl->DaylRefPt,
4459 : &RefPointData::Name); // Field: Glare Calculation Daylighting Reference Point Name
4460 11 : if (daylightControl.glareRefPtNumber == 0) {
4461 0 : ShowSevereError(state,
4462 0 : format("{}: invalid {}=\"{}\" for object named: {}",
4463 0 : s_ipsc->cCurrentModuleObject,
4464 0 : s_ipsc->cAlphaFieldNames(6),
4465 0 : s_ipsc->cAlphaArgs(6),
4466 0 : s_ipsc->cAlphaArgs(1)));
4467 0 : ErrorsFound = true;
4468 0 : continue;
4469 : }
4470 2 : } else if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
4471 2 : ShowWarningError(state, format("No {} provided for object named: {}", s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(1)));
4472 6 : ShowContinueError(state, "No glare calculation performed, and the simulation continues.");
4473 : }
4474 :
4475 : // Field: Glare Calculation Azimuth Angle of View Direction Clockwise from Zone y-Axis
4476 13 : daylightControl.ViewAzimuthForGlare = !s_ipsc->lNumericFieldBlanks(5) ? s_ipsc->rNumericArgs(5) : 0.0;
4477 :
4478 13 : daylightControl.MaxGlareallowed = s_ipsc->rNumericArgs(6); // Field: Maximum Allowable Discomfort Glare Index
4479 13 : daylightControl.DElightGriddingResolution = s_ipsc->rNumericArgs(7); // Field: DElight Gridding Resolution
4480 :
4481 13 : int curTotalDaylRefPts = NumAlpha - 6; // first six alpha fields are not part of extensible group
4482 13 : daylightControl.TotalDaylRefPoints = curTotalDaylRefPts;
4483 13 : state.dataViewFactor->EnclSolInfo(daylightControl.enclIndex).TotalEnclosureDaylRefPoints += curTotalDaylRefPts;
4484 13 : dl->ZoneDaylight(daylightControl.zoneIndex).totRefPts += curTotalDaylRefPts;
4485 13 : dl->maxControlRefPoints = max(dl->maxControlRefPoints, curTotalDaylRefPts);
4486 13 : if ((NumNumber - 7) / 2 != daylightControl.TotalDaylRefPoints) {
4487 0 : ShowSevereError(state,
4488 0 : format("{}The number of extensible numeric fields and alpha fields is inconsistent for: {}",
4489 0 : s_ipsc->cCurrentModuleObject,
4490 0 : s_ipsc->cAlphaArgs(1)));
4491 0 : ShowContinueError(state,
4492 0 : format("For each field: {} there needs to be the following fields: Fraction Controlled by Reference Point and "
4493 : "Illuminance Setpoint at Reference Point",
4494 0 : s_ipsc->cAlphaFieldNames(NumAlpha)));
4495 0 : ErrorsFound = true;
4496 : }
4497 :
4498 13 : daylightControl.refPts.allocate(curTotalDaylRefPts);
4499 :
4500 43 : for (auto &refPt : daylightControl.refPts) {
4501 30 : refPt = DaylRefPt();
4502 : }
4503 :
4504 13 : int countRefPts = 0;
4505 43 : for (int refPtNum = 1; refPtNum <= curTotalDaylRefPts; ++refPtNum) {
4506 30 : auto &refPt = daylightControl.refPts(refPtNum);
4507 30 : refPt.num =
4508 30 : Util::FindItemInList(s_ipsc->cAlphaArgs(6 + refPtNum), dl->DaylRefPt, &RefPointData::Name); // Field: Daylighting Reference Point Name
4509 30 : if (refPt.num == 0) {
4510 0 : ShowSevereError(state,
4511 0 : format("{}: invalid {}=\"{}\" for object named: {}",
4512 0 : s_ipsc->cCurrentModuleObject,
4513 0 : s_ipsc->cAlphaFieldNames(6 + refPtNum),
4514 0 : s_ipsc->cAlphaArgs(6 + refPtNum),
4515 0 : s_ipsc->cAlphaArgs(1)));
4516 0 : ErrorsFound = true;
4517 0 : continue;
4518 : } else {
4519 30 : ++countRefPts;
4520 : }
4521 30 : refPt.fracZoneDaylit = s_ipsc->rNumericArgs(6 + refPtNum * 2); // Field: Fraction Controlled by Reference Point
4522 30 : refPt.illumSetPoint = s_ipsc->rNumericArgs(7 + refPtNum * 2); // Field: Illuminance Setpoint at Reference Point
4523 :
4524 30 : if (daylightControl.DaylightMethod == DaylightingMethod::SplitFlux) {
4525 90 : SetupOutputVariable(state,
4526 60 : format("Daylighting Reference Point {} Illuminance", refPtNum),
4527 : Constant::Units::lux,
4528 30 : refPt.lums[iLum_Illum],
4529 : OutputProcessor::TimeStepType::Zone,
4530 : OutputProcessor::StoreType::Average,
4531 30 : daylightControl.Name);
4532 90 : SetupOutputVariable(state,
4533 60 : format("Daylighting Reference Point {} Daylight Illuminance Setpoint Exceeded Time", refPtNum),
4534 : Constant::Units::hr,
4535 30 : refPt.timeExceedingDaylightIlluminanceSetPoint,
4536 : OutputProcessor::TimeStepType::Zone,
4537 : OutputProcessor::StoreType::Sum,
4538 30 : daylightControl.Name);
4539 90 : SetupOutputVariable(state,
4540 60 : format("Daylighting Reference Point {} Glare Index", refPtNum),
4541 : Constant::Units::None,
4542 30 : refPt.glareIndex,
4543 : OutputProcessor::TimeStepType::Zone,
4544 : OutputProcessor::StoreType::Average,
4545 30 : daylightControl.Name);
4546 90 : SetupOutputVariable(state,
4547 60 : format("Daylighting Reference Point {} Glare Index Setpoint Exceeded Time", refPtNum),
4548 : Constant::Units::hr,
4549 30 : refPt.timeExceedingGlareIndexSetPoint,
4550 : OutputProcessor::TimeStepType::Zone,
4551 : OutputProcessor::StoreType::Sum,
4552 30 : daylightControl.Name);
4553 : } // if (DaylightMethod == SplitFlux)
4554 : } // for (RefPtNum)
4555 :
4556 : // Register Error if 0 DElight RefPts have been input for valid DElight object
4557 13 : if (countRefPts < 1) {
4558 0 : ShowSevereError(state, format("No Reference Points input for {} zone ={}", s_ipsc->cCurrentModuleObject, daylightControl.ZoneName));
4559 0 : ErrorsFound = true;
4560 : }
4561 :
4562 13 : Real64 sumFracs = 0.0;
4563 43 : for (auto const &refPt : daylightControl.refPts)
4564 30 : sumFracs += refPt.fracZoneDaylit;
4565 :
4566 13 : daylightControl.sumFracLights = sumFracs;
4567 13 : if ((1.0 - sumFracs) > FractionTolerance) {
4568 2 : ShowWarningError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is < 1.0.");
4569 2 : ShowContinueError(state,
4570 2 : format("..discovered in {}=\"{}\", only {:.3R} of the zone or space is controlled.",
4571 1 : s_ipsc->cCurrentModuleObject,
4572 1 : daylightControl.Name,
4573 : sumFracs));
4574 12 : } else if ((sumFracs - 1.0) > FractionTolerance) {
4575 2 : ShowSevereError(state, "GetDaylightingControls: Fraction of zone or space controlled by the Daylighting reference points is > 1.0.");
4576 2 : ShowContinueError(state,
4577 2 : format("..discovered in {}=\"{}\", trying to control {:.3R} of the zone or space.",
4578 1 : s_ipsc->cCurrentModuleObject,
4579 1 : daylightControl.Name,
4580 : sumFracs));
4581 1 : ErrorsFound = true;
4582 : }
4583 :
4584 13 : if (daylightControl.LightControlType == LtgCtrlType::Stepped && daylightControl.LightControlSteps <= 0) {
4585 0 : ShowWarningError(state, "GetDaylightingControls: For Stepped Control, the number of steps must be > 0");
4586 0 : ShowContinueError(state,
4587 0 : format("..discovered in \"{}\" for Zone=\"{}\", will use 1", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(2)));
4588 0 : daylightControl.LightControlSteps = 1;
4589 : }
4590 26 : SetupOutputVariable(state,
4591 : "Daylighting Lighting Power Multiplier",
4592 : Constant::Units::None,
4593 13 : daylightControl.PowerReductionFactor,
4594 : OutputProcessor::TimeStepType::Zone,
4595 : OutputProcessor::StoreType::Average,
4596 13 : daylightControl.Name);
4597 : } // for (controlNum)
4598 11 : } // GetDaylightingControls()
4599 :
4600 11 : void GeometryTransformForDaylighting(EnergyPlusData &state)
4601 : {
4602 : // AUTHOR Fred Winkelmann
4603 : // DATE WRITTEN March 2002
4604 : // MODIFIED Glazer - July 2016 - separated this from GetInput function
4605 : // For splitflux daylighting, transform the geometry
4606 11 : auto &dl = state.dataDayltg;
4607 11 : auto const &s_surf = state.dataSurface;
4608 :
4609 : // Calc cos and sin of Building Relative North values for later use in transforming Reference Point coordinates
4610 11 : Real64 CosBldgRelNorth = std::cos(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
4611 11 : Real64 SinBldgRelNorth = std::sin(-(state.dataHeatBal->BuildingAzimuth + state.dataHeatBal->BuildingRotationAppendixG) * Constant::DegToRad);
4612 : // these are only for Building Rotation for Appendix G when using world coordinate system
4613 11 : Real64 CosBldgRotAppGonly = std::cos(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
4614 11 : Real64 SinBldgRotAppGonly = std::sin(-state.dataHeatBal->BuildingRotationAppendixG * Constant::DegToRad);
4615 :
4616 11 : bool doTransform = false;
4617 11 : Real64 OldAspectRatio = 1.0;
4618 11 : Real64 NewAspectRatio = 1.0;
4619 :
4620 11 : CheckForGeometricTransform(state, doTransform, OldAspectRatio, NewAspectRatio);
4621 28 : for (auto &daylCntrl : dl->daylightControl) {
4622 17 : auto &zone = state.dataHeatBal->Zone(daylCntrl.zoneIndex);
4623 :
4624 : // Calc cos and sin of Zone Relative North values for later use in transforming Reference Point coordinates
4625 17 : Real64 CosZoneRelNorth = std::cos(-zone.RelNorth * Constant::DegToRad);
4626 17 : Real64 SinZoneRelNorth = std::sin(-zone.RelNorth * Constant::DegToRad);
4627 :
4628 17 : Real64 rLightLevel = InternalHeatGains::GetDesignLightingLevelForZone(state, daylCntrl.zoneIndex);
4629 17 : InternalHeatGains::CheckLightsReplaceableMinMaxForZone(state, daylCntrl.zoneIndex);
4630 :
4631 45 : for (int refPtNum = 1; refPtNum <= daylCntrl.TotalDaylRefPoints; ++refPtNum) {
4632 28 : auto &refPt = daylCntrl.refPts(refPtNum);
4633 28 : auto &curRefPt = dl->DaylRefPt(refPt.num); // get the active daylighting:referencepoint
4634 28 : curRefPt.indexToFracAndIllum = refPtNum; // back reference to the index to the ZoneDaylight structure arrays related to reference points
4635 28 : if (s_surf->DaylRefWorldCoordSystem) {
4636 : // transform only by appendix G rotation
4637 0 : refPt.absCoords.x = curRefPt.coords.x * CosBldgRotAppGonly - curRefPt.coords.y * SinBldgRotAppGonly;
4638 0 : refPt.absCoords.y = curRefPt.coords.x * SinBldgRotAppGonly + curRefPt.coords.y * CosBldgRotAppGonly;
4639 0 : refPt.absCoords.z = curRefPt.coords.z;
4640 : } else {
4641 : // Transform reference point coordinates into building coordinate system
4642 28 : Real64 Xb = curRefPt.coords.x * CosZoneRelNorth - curRefPt.coords.y * SinZoneRelNorth + zone.OriginX;
4643 28 : Real64 Yb = curRefPt.coords.x * SinZoneRelNorth + curRefPt.coords.y * CosZoneRelNorth + zone.OriginY;
4644 : // Transform into World Coordinate System
4645 28 : refPt.absCoords.x = Xb * CosBldgRelNorth - Yb * SinBldgRelNorth;
4646 28 : refPt.absCoords.y = Xb * SinBldgRelNorth + Yb * CosBldgRelNorth;
4647 28 : refPt.absCoords.z = curRefPt.coords.z + zone.OriginZ;
4648 28 : if (doTransform) {
4649 0 : Real64 Xo = refPt.absCoords.x; // world coordinates.... shifted by relative north angle...
4650 0 : Real64 Yo = refPt.absCoords.y;
4651 : // next derotate the building
4652 0 : Real64 XnoRot = Xo * CosBldgRelNorth + Yo * SinBldgRelNorth;
4653 0 : Real64 YnoRot = Yo * CosBldgRelNorth - Xo * SinBldgRelNorth;
4654 : // translate
4655 0 : Real64 Xtrans = XnoRot * std::sqrt(NewAspectRatio / OldAspectRatio);
4656 0 : Real64 Ytrans = YnoRot * std::sqrt(OldAspectRatio / NewAspectRatio);
4657 : // rerotate
4658 0 : refPt.absCoords.x = Xtrans * CosBldgRelNorth - Ytrans * SinBldgRelNorth;
4659 0 : refPt.absCoords.y = Xtrans * SinBldgRelNorth + Ytrans * CosBldgRelNorth;
4660 : }
4661 : }
4662 :
4663 28 : auto &orp = state.dataOutRptPredefined;
4664 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtZone, curRefPt.Name, daylCntrl.ZoneName);
4665 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlName, curRefPt.Name, daylCntrl.Name);
4666 28 : if (daylCntrl.DaylightMethod == DaylightingMethod::SplitFlux) {
4667 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "SplitFlux");
4668 : } else {
4669 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtKind, curRefPt.Name, "DElight");
4670 : }
4671 : // ( 1=continuous, 2=stepped, 3=continuous/off )
4672 28 : if (daylCntrl.LightControlType == LtgCtrlType::Continuous) {
4673 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous");
4674 0 : } else if (daylCntrl.LightControlType == LtgCtrlType::Stepped) {
4675 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Stepped");
4676 0 : } else if (daylCntrl.LightControlType == LtgCtrlType::ContinuousOff) {
4677 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtCtrlType, curRefPt.Name, "Continuous/Off");
4678 : }
4679 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtFrac, curRefPt.Name, refPt.fracZoneDaylit);
4680 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWInst, curRefPt.Name, rLightLevel);
4681 28 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchDyLtWCtrl, curRefPt.Name, rLightLevel * refPt.fracZoneDaylit);
4682 :
4683 28 : if (refPt.absCoords.x < zone.MinimumX || refPt.absCoords.x > zone.MaximumX) {
4684 2 : refPt.inBounds = false;
4685 4 : ShowWarningError(state,
4686 4 : format("GeometryTransformForDaylighting: Reference point X Value outside Zone Min/Max X, Zone={}", zone.Name));
4687 4 : ShowContinueError(state,
4688 4 : format("...X Reference Point= {:.2R}, Zone Minimum X= {:.2R}, Zone Maximum X= {:.2R}",
4689 2 : refPt.absCoords.x,
4690 2 : zone.MinimumX,
4691 2 : zone.MaximumX));
4692 4 : ShowContinueError(
4693 : state,
4694 4 : format("...X Reference Distance Outside MinimumX= {:.4R} m.",
4695 4 : (refPt.absCoords.x < zone.MinimumX) ? (zone.MinimumX - refPt.absCoords.x) : (refPt.absCoords.x - zone.MaximumX)));
4696 : }
4697 28 : if (refPt.absCoords.y < zone.MinimumY || refPt.absCoords.y > zone.MaximumY) {
4698 2 : refPt.inBounds = false;
4699 4 : ShowWarningError(state,
4700 4 : format("GeometryTransformForDaylighting: Reference point Y Value outside Zone Min/Max Y, Zone={}", zone.Name));
4701 4 : ShowContinueError(state,
4702 4 : format("...Y Reference Point= {:.2R}, Zone Minimum Y= {:.2R}, Zone Maximum Y= {:.2R}",
4703 2 : refPt.absCoords.x,
4704 2 : zone.MinimumY,
4705 2 : zone.MaximumY));
4706 4 : ShowContinueError(
4707 : state,
4708 4 : format("...Y Reference Distance Outside MinimumY= {:.4R} m.",
4709 4 : (refPt.absCoords.y < zone.MinimumY) ? (zone.MinimumY - refPt.absCoords.y) : (refPt.absCoords.y - zone.MaximumY)));
4710 : }
4711 28 : if (refPt.absCoords.z < zone.MinimumZ || refPt.absCoords.z > zone.MaximumZ) {
4712 0 : refPt.inBounds = false;
4713 0 : ShowWarningError(state,
4714 0 : format("GeometryTransformForDaylighting: Reference point Z Value outside Zone Min/Max Z, Zone={}", zone.Name));
4715 0 : ShowContinueError(state,
4716 0 : format("...Z Reference Point= {:.2R}, Zone Minimum Z= {:.2R}, Zone Maximum Z= {:.2R}",
4717 0 : refPt.absCoords.z,
4718 0 : zone.MinimumZ,
4719 0 : zone.MaximumZ));
4720 0 : ShowContinueError(
4721 : state,
4722 0 : format("...Z Reference Distance Outside MinimumZ= {:.4R} m.",
4723 0 : (refPt.absCoords.z < zone.MinimumZ) ? (zone.MinimumZ - refPt.absCoords.z) : (refPt.absCoords.z - zone.MaximumZ)));
4724 : }
4725 : } // for (refPt)
4726 : } // for (daylightCtrl)
4727 11 : } // GeometryTransformForDaylighting()
4728 :
4729 14 : void GetInputDayliteRefPt(EnergyPlusData &state, bool &ErrorsFound)
4730 : {
4731 : // Perform GetInput function for the Daylighting:ReferencePoint object
4732 : // Glazer - July 2016
4733 14 : auto const &dl = state.dataDayltg;
4734 14 : auto &ip = state.dataInputProcessing->inputProcessor;
4735 14 : auto const &s_ipsc = state.dataIPShortCut;
4736 14 : s_ipsc->cCurrentModuleObject = "Daylighting:ReferencePoint";
4737 :
4738 14 : int RefPtNum = 0;
4739 : int IOStat;
4740 : int NumAlpha;
4741 : int NumNumber;
4742 :
4743 14 : int TotRefPoints = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4744 :
4745 14 : dl->DaylRefPt.allocate(TotRefPoints);
4746 50 : for (auto &pt : dl->DaylRefPt) {
4747 72 : ip->getObjectItem(state,
4748 36 : s_ipsc->cCurrentModuleObject,
4749 : ++RefPtNum,
4750 36 : s_ipsc->cAlphaArgs,
4751 : NumAlpha,
4752 36 : s_ipsc->rNumericArgs,
4753 : NumNumber,
4754 : IOStat,
4755 36 : s_ipsc->lNumericFieldBlanks,
4756 36 : s_ipsc->lAlphaFieldBlanks,
4757 36 : s_ipsc->cAlphaFieldNames,
4758 36 : s_ipsc->cNumericFieldNames);
4759 36 : pt.Name = s_ipsc->cAlphaArgs(1);
4760 36 : pt.ZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->Zone);
4761 36 : if (pt.ZoneNum == 0) {
4762 0 : int spaceNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataHeatBal->space);
4763 0 : if (spaceNum == 0) {
4764 0 : ShowSevereError(state,
4765 0 : format("{}=\"{}\", invalid {}=\"{}\".",
4766 0 : s_ipsc->cCurrentModuleObject,
4767 0 : s_ipsc->cAlphaArgs(1),
4768 0 : s_ipsc->cAlphaFieldNames(2),
4769 0 : s_ipsc->cAlphaArgs(2)));
4770 0 : ErrorsFound = true;
4771 : } else {
4772 0 : pt.ZoneNum = state.dataHeatBal->space(spaceNum).zoneNum;
4773 : }
4774 : }
4775 36 : pt.coords = {s_ipsc->rNumericArgs(1), s_ipsc->rNumericArgs(2), s_ipsc->rNumericArgs(3)};
4776 : }
4777 14 : }
4778 :
4779 134 : bool doesDayLightingUseDElight(EnergyPlusData const &state)
4780 : {
4781 134 : auto const &dl = state.dataDayltg;
4782 147 : for (auto const &znDayl : dl->daylightControl) {
4783 14 : if (znDayl.DaylightMethod == DaylightingMethod::DElight) {
4784 1 : return true;
4785 : }
4786 : }
4787 133 : return false;
4788 : }
4789 :
4790 107 : void CheckTDDsAndLightShelvesInDaylitZones(EnergyPlusData &state)
4791 : {
4792 : // SUBROUTINE INFORMATION:
4793 : // AUTHOR Brent Griffith
4794 : // DATE WRITTEN Dec 2007
4795 :
4796 : // PURPOSE OF THIS SUBROUTINE:
4797 : // This subroutine checks daylighting input for TDDs and light shelfs
4798 : // which need to be checked after daylighting input has been read in (CR 7145)
4799 : // (eventually this should be changed once/if implementations change to decouple from daylighting calcs so that
4800 : // these devices can be used in models without daylighting controls
4801 : // CR 7145 was for TDDs, but also implenting check for light shelves, the other "daylighting device"
4802 :
4803 : // METHODOLOGY EMPLOYED:
4804 : // loop thru daylighting devices and check that their zones have daylight controls
4805 :
4806 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4807 107 : auto &s_surf = state.dataSurface;
4808 :
4809 107 : bool ErrorsFound = false;
4810 :
4811 109 : for (auto const &pipe : state.dataDaylightingDevicesData->TDDPipe) {
4812 2 : int SurfNum = pipe.Diffuser;
4813 2 : if (SurfNum > 0) {
4814 2 : int const pipeEnclNum = s_surf->Surface(SurfNum).SolarEnclIndex;
4815 2 : if (state.dataViewFactor->EnclSolInfo(pipeEnclNum).TotalEnclosureDaylRefPoints == 0) {
4816 4 : ShowWarningError(state,
4817 4 : format("DaylightingDevice:Tubular = {}: is not connected to a Zone that has Daylighting, no visible transmittance "
4818 : "will be modeled through the daylighting device.",
4819 2 : pipe.Name));
4820 : }
4821 : } else { // SurfNum == 0
4822 : // should not come here (would have already been caught in TDD get input), but is an error
4823 0 : ShowSevereError(state, format("DaylightingDevice:Tubular = {}: Diffuser surface not found ", pipe.Name));
4824 0 : ErrorsFound = true;
4825 : }
4826 : } // for (pipe)
4827 :
4828 107 : for (auto const &shelf : state.dataDaylightingDevicesData->Shelf) {
4829 0 : if (shelf.Window == 0) {
4830 : // should not come here (would have already been caught in shelf get input), but is an error
4831 0 : ShowSevereError(state, format("DaylightingDevice:Shelf = {}: window not found ", shelf.Name));
4832 0 : ErrorsFound = true;
4833 : }
4834 : } // for (shelf)
4835 :
4836 107 : if (ErrorsFound) ShowFatalError(state, "CheckTDDsAndLightShelvesInDaylitZones: Errors in DAYLIGHTING input.");
4837 107 : }
4838 :
4839 108 : void AssociateWindowShadingControlWithDaylighting(EnergyPlusData &state)
4840 : {
4841 108 : auto &dl = state.dataDayltg;
4842 108 : auto &s_surf = state.dataSurface;
4843 :
4844 112 : for (auto &winShadeControl : s_surf->WindowShadingControl) {
4845 4 : if (winShadeControl.DaylightingControlName.empty()) continue;
4846 3 : int found = -1;
4847 9 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
4848 8 : if (Util::SameString(winShadeControl.DaylightingControlName, dl->daylightControl(daylightCtrlNum).Name)) {
4849 2 : found = daylightCtrlNum;
4850 2 : break;
4851 : }
4852 : }
4853 3 : if (found > 0) {
4854 2 : winShadeControl.DaylightControlIndex = found;
4855 : } else {
4856 2 : ShowWarningError(state, "AssociateWindowShadingControlWithDaylighting: Daylighting object name used in WindowShadingControl not found.");
4857 2 : ShowContinueError(state,
4858 2 : format("..The WindowShadingControl object=\"{}\" and referenes an object named: \"{}\"",
4859 1 : winShadeControl.Name,
4860 1 : winShadeControl.DaylightingControlName));
4861 : }
4862 : }
4863 108 : } // AssociateWindowShadingControlWithDaylighting()
4864 :
4865 7 : void GetLightWellData(EnergyPlusData &state, bool &ErrorsFound) // If errors found in input
4866 : {
4867 :
4868 : // SUBROUTINE INFORMATION:
4869 : // AUTHOR Fred Winkelmann
4870 : // DATE WRITTEN Apr 2004
4871 :
4872 : // PURPOSE OF THIS SUBROUTINE:
4873 : // Gets data for a light well associated with a rectangular exterior window.
4874 : // Calculates light well efficiency, defined as the ratio of the amount of visible
4875 : // solar radiation leaving a well to the amount entering the well.
4876 :
4877 : // METHODOLOGY EMPLOYED:
4878 : // Based on fit to Fig. 8-21, "Efficiency factors for various depths of light wells
4879 : // based on well-interreflectance values," Lighting Handbook, 8th Edition, Illuminating
4880 : // Engineering Society of North America, 1993.
4881 :
4882 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4883 :
4884 : int IOStat; // IO Status when calling get input subroutine
4885 : int NumAlpha; // Number of alpha names being passed
4886 : int NumProp; // Number of properties being passed
4887 : int TotLightWells; // Total Light Well objects
4888 :
4889 7 : auto &ip = state.dataInputProcessing->inputProcessor;
4890 7 : auto &s_surf = state.dataSurface;
4891 7 : auto const &s_ipsc = state.dataIPShortCut;
4892 :
4893 : // Get the total number of Light Well objects
4894 7 : s_ipsc->cCurrentModuleObject = "DaylightingDevice:LightWell";
4895 7 : TotLightWells = ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
4896 7 : if (TotLightWells == 0) return;
4897 :
4898 0 : for (int loop = 1; loop <= TotLightWells; ++loop) {
4899 :
4900 0 : ip->getObjectItem(state,
4901 0 : s_ipsc->cCurrentModuleObject,
4902 : loop,
4903 0 : s_ipsc->cAlphaArgs,
4904 : NumAlpha,
4905 0 : s_ipsc->rNumericArgs,
4906 : NumProp,
4907 : IOStat,
4908 0 : s_ipsc->lNumericFieldBlanks,
4909 0 : s_ipsc->lAlphaFieldBlanks,
4910 0 : s_ipsc->cAlphaFieldNames,
4911 0 : s_ipsc->cNumericFieldNames);
4912 :
4913 0 : int SurfNum = Util::FindItemInList(s_ipsc->cAlphaArgs(1), s_surf->Surface);
4914 0 : if (SurfNum == 0) {
4915 0 : ShowSevereError(
4916 0 : state, format("{}: invalid {}=\"{}\" not found.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
4917 0 : ErrorsFound = true;
4918 0 : continue;
4919 : }
4920 :
4921 0 : auto const &surf = s_surf->Surface(SurfNum);
4922 0 : auto &surfWin = s_surf->SurfaceWindow(SurfNum);
4923 : // Check that associated surface is an exterior window
4924 : // True if associated surface is not an exterior window
4925 0 : if (surf.Class != SurfaceClass::Window && surf.ExtBoundCond != ExternalEnvironment) {
4926 0 : ShowSevereError(state,
4927 0 : format("{}: invalid {}=\"{}\" - not an exterior window.",
4928 0 : s_ipsc->cCurrentModuleObject,
4929 0 : s_ipsc->cAlphaFieldNames(1),
4930 0 : s_ipsc->cAlphaArgs(1)));
4931 0 : ErrorsFound = true;
4932 0 : continue;
4933 : }
4934 :
4935 : // Associated surface is an exterior window; calculate light well efficiency.
4936 0 : surfWin.lightWellEff = 1.0;
4937 0 : Real64 HeightWell = s_ipsc->rNumericArgs(1); // Well height (from window to bottom of well) (m)
4938 0 : Real64 PerimWell = s_ipsc->rNumericArgs(2); // Well perimeter (at bottom of well) (m)
4939 0 : Real64 AreaWell = s_ipsc->rNumericArgs(3); // Well area (at bottom of well) (m2)
4940 0 : Real64 VisReflWell = s_ipsc->rNumericArgs(4); // Area-weighted visible reflectance of well walls
4941 :
4942 : // Warning if light well area is less than window area
4943 0 : if (AreaWell < (surf.Area + s_surf->SurfWinDividerArea(SurfNum) - 0.1)) {
4944 0 : ShowSevereError(
4945 0 : state, format("{}: invalid {}=\"{}\" - Areas.", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), s_ipsc->cAlphaArgs(1)));
4946 0 : ShowContinueError(state, format("has Area of Bottom of Well={:.1R} that is less than window area={:.1R}", surf.Area, AreaWell));
4947 : }
4948 :
4949 0 : if (HeightWell >= 0.0 && PerimWell > 0.0 && AreaWell > 0.0) {
4950 0 : Real64 WellCavRatio = 2.5 * HeightWell * PerimWell / AreaWell;
4951 0 : surfWin.lightWellEff = std::exp(-WellCavRatio * (0.16368 - 0.14467 * VisReflWell));
4952 : }
4953 : } // End of loop over light well objects
4954 : } // GetLightWellData()
4955 :
4956 2382 : inline WinCover findWinShadingStatus(EnergyPlusData &state, int const IWin)
4957 : {
4958 : // Return the window shading status, 1=unshaded, 2=shaded
4959 :
4960 2382 : auto &s_surf = state.dataSurface;
4961 2382 : bool WinShadedNoGlareControl = IS_SHADED_NO_GLARE_CTRL(s_surf->SurfWinShadingFlag(IWin));
4962 :
4963 4758 : return ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) && (WinShadedNoGlareControl || s_surf->SurfWinSolarDiffusing(IWin)))
4964 4758 : ? WinCover::Shaded
4965 2382 : : WinCover::Bare;
4966 : }
4967 :
4968 1369 : Real64 DayltgGlare(EnergyPlusData &state,
4969 : int IL, // Reference point index: 1=first ref pt, 2=second ref pt
4970 : Real64 BLUM, // Window background (surround) luminance (cd/m2)
4971 : int const daylightCtrlNum // Current daylighting control number
4972 : )
4973 : {
4974 :
4975 : // SUBROUTINE INFORMATION:
4976 : // AUTHOR Fred Winkelmann
4977 : // DATE WRITTEN July 1997
4978 :
4979 : // PURPOSE OF THIS SUBROUTINE:
4980 : // CALCULATE GLARE INDEX.
4981 :
4982 : // METHODOLOGY EMPLOYED:
4983 : // Called from DayltgInteriorIllum. Finds glare index at reference
4984 : // point no. IL in a space using the Cornell/BRS large source
4985 : // glare formula. BLUM is the background luminance (cd/m**2).
4986 : // TH comment 1/21/2010: The SurfaceWindow(IWin)%ShadingFlag has to be set
4987 : // before calling this subroutine. For switchable glazings this is tricky
4988 : // because the ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop)
4989 : // may change every time step to represent intermediate switched state.
4990 :
4991 : // REFERENCES:
4992 : // Based on DOE-2.1E subroutine DGLARE.
4993 :
4994 1369 : Real64 GTOT = 0.0; // Glare constant
4995 :
4996 1369 : auto &dl = state.dataDayltg;
4997 :
4998 : // Loop over exterior windows associated with zone
4999 1369 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5000 1369 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
5001 2738 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5002 1369 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5003 1369 : WinCover winCover = findWinShadingStatus(state, IWin);
5004 : // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
5005 : // below, which is (0.2936)**0.6
5006 1369 : auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
5007 1369 : Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
5008 1369 : Real64 GTOT2 = BLUM + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
5009 1369 : GTOT += GTOT1 / (GTOT2 + 0.000001);
5010 : }
5011 :
5012 : // Glare index (adding 0.000001 prevents LOG10 (0))
5013 1369 : return max(0.0, 10.0 * std::log10(GTOT + 0.000001));
5014 : }
5015 :
5016 0 : void DayltgGlareWithIntWins(EnergyPlusData &state,
5017 : int const daylightCtrlNum // Current daylighting control number
5018 : )
5019 : {
5020 :
5021 : // SUBROUTINE INFORMATION:
5022 : // AUTHOR Fred Winkelmann
5023 : // DATE WRITTEN March 2004
5024 :
5025 : // PURPOSE OF THIS SUBROUTINE:
5026 : // Calculate daylighting glare index for zones with interior windows.
5027 :
5028 : // METHODOLOGY EMPLOYED:
5029 : // Finds glare index at reference point IL in a daylit zone using the Cornell/BRS large source
5030 : // glare formula. Takes into account inter-reflected illuminance from light entering
5031 : // the zone through interior windows
5032 :
5033 : // REFERENCES:
5034 : // Based on subroutine DayltgGlare.
5035 :
5036 0 : Real64 GTOT = 0.0; // Glare constant(?) // TODO: does this need to be reset for every refPt?
5037 :
5038 : // Calculate background luminance including effect of inter-reflected illuminance from light
5039 : // entering zone through its interior windows
5040 0 : auto &dl = state.dataDayltg;
5041 0 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5042 0 : auto &thisEnclDaylight = dl->enclDaylight(thisDayltgCtrl.enclIndex);
5043 0 : int RefPoints = thisDayltgCtrl.TotalDaylRefPoints; // Number of daylighting reference points in zone
5044 0 : for (int IL = 1; IL <= RefPoints; ++IL) {
5045 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
5046 :
5047 0 : Real64 BackgroundLum = refPt.lums[iLum_Back] + thisEnclDaylight.InterReflIllFrIntWins * thisEnclDaylight.aveVisDiffReflect / Constant::Pi;
5048 0 : BackgroundLum = max(refPt.illumSetPoint * thisEnclDaylight.aveVisDiffReflect / Constant::Pi, BackgroundLum);
5049 :
5050 : // Loop over exterior windows associated with zone
5051 0 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5052 0 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5053 0 : WinCover winCover = findWinShadingStatus(state, IWin);
5054 : // Conversion from ft-L to cd/m2, with cd/m2 = 0.2936 ft-L, gives the 0.4794 factor
5055 : // below, which is (0.2936)**0.6
5056 0 : auto const &extWin = thisDayltgCtrl.refPts(IL).extWins(loop);
5057 0 : Real64 GTOT1 = 0.4794 * (std::pow(extWin.lums[iLum_Source][(int)winCover], 1.6)) * std::pow(extWin.solidAngWtd, 0.8);
5058 0 : Real64 GTOT2 = BackgroundLum + 0.07 * std::sqrt(extWin.solidAng) * extWin.lums[iLum_Source][(int)winCover];
5059 0 : GTOT += GTOT1 / (GTOT2 + 0.000001);
5060 : }
5061 :
5062 : // Glare index
5063 0 : refPt.glareIndex = max(0.0, 10.0 * std::log10(GTOT + 0.000001));
5064 : } // for (IL)
5065 0 : } // DaylGlareWithIntWins()
5066 :
5067 551 : void DayltgExtHorizIllum(EnergyPlusData &state,
5068 : Illums &HI // Horizontal illuminance from sky for different sky types
5069 : )
5070 : {
5071 :
5072 : // SUBROUTINE INFORMATION:
5073 : // AUTHOR Fred Winkelmann
5074 : // DATE WRITTEN July 1997
5075 :
5076 : // PURPOSE OF THIS SUBROUTINE:
5077 : // Calculates exterior daylight illuminance.
5078 :
5079 : // METHODOLOGY EMPLOYED:
5080 : // Called by CalcDayltgCoefficients. Calculates illuminance
5081 : // on unobstructed horizontal surface by integrating
5082 : // over the luminance distribution of standard CIE skies.
5083 : // Calculates horizontal beam illuminance.
5084 : // REFERENCES:
5085 : // Based on DOE-2.1E subroutine DHILL.
5086 :
5087 : // Argument array dimensioning
5088 :
5089 : // SUBROUTINE PARAMETER DEFINITIONS:
5090 551 : Real64 constexpr DTH = (2.0 * Constant::Pi) / double(NTH); // Sky integration azimuth stepsize (radians)
5091 551 : Real64 constexpr DPH = Constant::PiOvr2 / double(NPH); // Sky integration altitude stepsize (radians)
5092 :
5093 : // Integrate to obtain illuminance from sky.
5094 : // The contribution in lumens/m2 from a patch of sky at altitude PH and azimuth TH
5095 : // is L(TH,PH)*SIN(PH)*COS(PH)*DTH*DPH, where L(TH,PH) is the luminance
5096 : // of the patch in cd/m2.
5097 551 : auto &dl = state.dataDayltg;
5098 :
5099 : // Init
5100 551 : if (dl->DayltgExtHorizIllum_firstTime) {
5101 27 : for (int IPH = 1; IPH <= NPH; ++IPH) {
5102 24 : dl->PH[IPH] = (IPH - 0.5) * DPH;
5103 24 : dl->SPHCPH[IPH] = std::sin(dl->PH[IPH]) * std::cos(dl->PH[IPH]); // DA = COS(PH)*DTH*DPH
5104 : }
5105 57 : for (int ITH = 1; ITH <= NTH; ++ITH) {
5106 54 : dl->TH[ITH] = (ITH - 0.5) * DTH;
5107 : }
5108 3 : dl->DayltgExtHorizIllum_firstTime = false;
5109 : }
5110 :
5111 2204 : HI = Illums();
5112 :
5113 : // Sky integration
5114 4959 : for (int IPH = 1; IPH <= NPH; ++IPH) {
5115 4408 : Real64 const PH_IPH = dl->PH[IPH];
5116 4408 : Real64 const SPHCPH_IPH = dl->SPHCPH[IPH];
5117 83752 : for (int ITH = 1; ITH <= NTH; ++ITH) {
5118 79344 : Real64 const TH_ITH = dl->TH[ITH];
5119 396720 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5120 317376 : HI.sky[iSky] += DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH_ITH, PH_IPH) * SPHCPH_IPH;
5121 : }
5122 : }
5123 : }
5124 :
5125 2755 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5126 2204 : HI.sky[iSky] *= DTH * DPH;
5127 : }
5128 :
5129 : // Direct solar horizontal illum (for unit direct normal illuminance)
5130 551 : HI.sun = dl->sunAngles.sinPhi * 1.0;
5131 551 : } // DayltgExtHorizIllum()
5132 :
5133 : // Product of solar transmittances of exterior obstructions
5134 386914 : Real64 DayltgHitObstruction(EnergyPlusData &state,
5135 : int const IHOUR, // Hour number
5136 : int const IWin, // Window index
5137 : Vector3<Real64> const &R1, // Origin of ray (m)
5138 : Vector3<Real64> const &RN // Unit vector along ray
5139 : )
5140 : {
5141 :
5142 : // SUBROUTINE INFORMATION:
5143 : // AUTHOR Fred Winkelmann
5144 : // DATE WRITTEN July 1997
5145 : // MODIFIED FCW, May 2003: update list of surface classes that qualify as obstructions;
5146 : // add interior surfaces as possible obstructors;
5147 : // return from DO loop over surfaces as soon as any obstruction is hit;
5148 : // FCW, July 2003: change from returning whether an obstruction is hit or not
5149 : // to product of solar transmittances of hit obstructions.
5150 : // FCW, Nov 2003: remove interior surfaces as possible obstructors since there
5151 : // is now a separate check for interior obstructions; exclude windows and
5152 : // doors as obstructors since if they are obstructors their base surfaces will
5153 : // also be obstructors
5154 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5155 :
5156 : // PURPOSE OF THIS SUBROUTINE:
5157 : // Determines the product of the solar transmittances of the obstructions hit by a ray
5158 : // from R1 in the direction of vector RN.
5159 :
5160 : // REFERENCES:
5161 : // Based on DOE-2.1E subroutine DHITSH.
5162 :
5163 386914 : auto &s_surf = state.dataSurface;
5164 : // Local declarations
5165 : bool hit; // True iff a particular obstruction is hit
5166 :
5167 386914 : Real64 ObTrans = 1.0;
5168 :
5169 386914 : auto const &window = s_surf->Surface(IWin);
5170 386914 : int const window_iBaseSurf = window.BaseSurf;
5171 :
5172 386914 : Vector3<Real64> DayltgHitObstructionHP;
5173 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5174 : // Building elements are assumed to be opaque
5175 : // A shadowing surface is opaque unless its transmittance schedule value is non-zero
5176 386914 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5177 :
5178 1313432 : for (int ISurf : s_surf->AllShadowPossObstrSurfaceList) {
5179 926518 : auto const &surface = s_surf->Surface(ISurf);
5180 926518 : SurfaceClass IType = surface.Class;
5181 926518 : if ((IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf)) {
5182 539604 : hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
5183 539604 : if (hit) { // Building element is hit (assumed opaque)
5184 0 : ObTrans = 0.0;
5185 0 : break;
5186 : }
5187 386914 : } else if (surface.IsShadowing) {
5188 0 : hit = PierceSurface(state, ISurf, R1, RN, DayltgHitObstructionHP);
5189 0 : if (hit) { // Shading surface is hit
5190 : // Get solar transmittance of the shading surface
5191 0 : Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
5192 0 : if (Trans < 1.e-6) {
5193 0 : ObTrans = 0.0;
5194 0 : break;
5195 : } else {
5196 0 : ObTrans *= Trans;
5197 : }
5198 : }
5199 : }
5200 : }
5201 :
5202 : } else { // Surface octree search
5203 :
5204 0 : auto const &window_base(window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window);
5205 0 : auto const *window_base_p(&window_base);
5206 :
5207 : // Lambda function for the octree to test for surface hit and update transmittance if hit
5208 0 : auto solarTransmittance = [=, &state, &R1, &RN, &hit, &ObTrans](SurfaceData const &surface) -> bool {
5209 0 : if (!surface.IsShadowPossibleObstruction) return false; // Do Consider separate octree without filtered surfaces
5210 0 : DataSurfaces::SurfaceClass const sClass(surface.Class);
5211 0 : Vector3<Real64> HP;
5212 0 : if ((sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && (&surface != window_base_p)) {
5213 0 : hit = PierceSurface(surface, R1, RN, HP);
5214 0 : if (hit) { // Building element is hit (assumed opaque)
5215 0 : ObTrans = 0.0;
5216 0 : return true;
5217 : }
5218 0 : } else if (surface.IsShadowing) {
5219 0 : hit = PierceSurface(surface, R1, RN, HP);
5220 0 : if (hit) { // Shading surface is hit
5221 : // Get solar transmittance of the shading surface
5222 0 : Real64 const Trans = (surface.shadowSurfSched != nullptr) ? surface.shadowSurfSched->getHrTsVal(state, IHOUR, 1) : 0.0;
5223 0 : if (Trans < 1.e-6) {
5224 0 : ObTrans = 0.0;
5225 0 : return true;
5226 : } else {
5227 0 : ObTrans *= Trans;
5228 0 : return ObTrans == 0.0;
5229 : }
5230 : }
5231 : }
5232 0 : return false;
5233 0 : };
5234 :
5235 : // Check octree surface candidates for hits: short circuits if zero transmittance reached
5236 0 : Vector3<Real64> const RN_inv(SurfaceOctreeCube::safe_inverse(RN));
5237 0 : state.dataHeatBalMgr->surfaceOctree.processSomeSurfaceRayIntersectsCube(state, R1, RN, RN_inv, solarTransmittance);
5238 0 : }
5239 :
5240 386914 : return ObTrans;
5241 386914 : } // DayltgHitObstruction()
5242 :
5243 368194 : bool DayltgHitInteriorObstruction(EnergyPlusData &state,
5244 : int const IWin, // Window index
5245 : Vector3<Real64> const &R1, // Origin of ray (m)
5246 : Vector3<Real64> const &R2 // Destination of ray (m)
5247 : )
5248 : {
5249 :
5250 : // SUBROUTINE INFORMATION:
5251 : // AUTHOR Fred Winkelmann
5252 : // DATE WRITTEN July 1997
5253 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5254 :
5255 : // PURPOSE OF THIS SUBROUTINE:
5256 : // This subroutine checks for interior obstructions between reference point and window element.
5257 :
5258 368194 : auto &s_surf = state.dataSurface;
5259 :
5260 : // Preconditions
5261 368194 : assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
5262 :
5263 368194 : bool hit = false;
5264 368194 : Vector3<Real64> RN = (R2 - R1).normalize(); // Make unit vector
5265 368194 : Real64 const d12 = distance(R1, R2); // Distance between R1 and R2
5266 :
5267 368194 : auto const &window = s_surf->Surface(IWin);
5268 368194 : int const window_Enclosure = window.SolarEnclIndex;
5269 368194 : int const window_iBaseSurf = window.BaseSurf;
5270 368194 : auto const &window_base = window_iBaseSurf > 0 ? s_surf->Surface(window_iBaseSurf) : window;
5271 368194 : int const window_base_iExtBoundCond = window_base.ExtBoundCond;
5272 :
5273 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5274 368194 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5275 : // Hit coordinates, if ray hits an obstruction
5276 368194 : Vector3<Real64> DayltgHitInteriorObstructionHP;
5277 :
5278 1560449 : for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
5279 1192255 : auto const &surface = s_surf->Surface(ISurf);
5280 1192255 : SurfaceClass IType = surface.Class;
5281 1192255 : if ((surface.IsShadowing) || // Shadowing surface
5282 1192255 : ((surface.SolarEnclIndex == window_Enclosure) && // Wall/ceiling/floor is in same zone as window
5283 1190414 : (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && (ISurf != window_iBaseSurf) &&
5284 : (ISurf != window_base_iExtBoundCond))) // Exclude window's base or base-adjacent surfaces
5285 : {
5286 454026 : hit = PierceSurface(state, ISurf, R1, RN, d12, DayltgHitInteriorObstructionHP); // Check if R2-R1 segment pierces surface
5287 454026 : if (hit) break; // Segment pierces surface: Don't check the rest
5288 : }
5289 : }
5290 :
5291 368194 : } else { // Surface octree search
5292 :
5293 0 : auto const *window_base_p = &window_base;
5294 0 : auto const &window_base_adjacent = window_base_iExtBoundCond > 0 ? s_surf->Surface(window_base_iExtBoundCond) : window_base;
5295 0 : auto const *window_base_adjacent_p = &window_base_adjacent;
5296 :
5297 : // Lambda function for the octree to test for surface hit
5298 0 : auto surfaceHit = [=, &R1, &hit](SurfaceData const &surface) -> bool {
5299 0 : DataSurfaces::SurfaceClass const sClass = surface.Class;
5300 0 : Vector3<Real64> HP; // Hit point
5301 0 : if ((surface.IsShadowing) || // Shadowing surface
5302 0 : ((surface.SolarEnclIndex == window_Enclosure) && // Surface is in same zone as window
5303 0 : (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5304 0 : (&surface != window_base_p) && (&surface != window_base_adjacent_p))) // Exclude window's base or base-adjacent surfaces
5305 : {
5306 0 : hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5307 0 : return hit;
5308 : } else {
5309 0 : return false;
5310 : }
5311 0 : };
5312 :
5313 : // Check octree surface candidates until a hit is found, if any
5314 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
5315 0 : }
5316 :
5317 368194 : return hit;
5318 368194 : } // DayltgHitInteriorObstruction()
5319 :
5320 0 : bool DayltgHitBetWinObstruction(EnergyPlusData &state,
5321 : int const IWin1, // Surface number of origin window
5322 : int const IWin2, // Surface number of destination window
5323 : Vector3<Real64> const &R1, // Origin of ray (on IWin1) (m)
5324 : Vector3<Real64> const &R2 // Destination of ray (on IWin2) (m)
5325 : )
5326 : {
5327 :
5328 : // SUBROUTINE INFORMATION:
5329 : // AUTHOR Fred Winkelmann
5330 : // DATE WRITTEN Feb 2004
5331 : // RE-ENGINEERED Sept 2015. Stuart Mentzer. Octree for performance.
5332 :
5333 : // PURPOSE OF THIS SUBROUTINE:
5334 : // Determines if a ray from point R1 on window IWin1 to point R2
5335 : // on window IWin2 hits an obstruction
5336 :
5337 0 : auto &s_surf = state.dataSurface;
5338 :
5339 : // Preconditions
5340 0 : assert(magnitude(R2 - R1) > 0.0); // Protect normalize() from divide by zero
5341 :
5342 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5343 : SurfaceClass IType; // Surface type/class
5344 :
5345 0 : bool hit = false;
5346 0 : Vector3<Real64> RN = (R2 - R1).normalize(); // Unit vector
5347 :
5348 0 : Real64 const d12 = distance(R1, R2); // Distance between R1 and R2 (m)
5349 :
5350 0 : auto const &window1 = s_surf->Surface(IWin1);
5351 0 : int const window1_iBaseSurf = window1.BaseSurf;
5352 0 : auto const &window1_base = window1_iBaseSurf > 0 ? s_surf->Surface(window1_iBaseSurf) : window1;
5353 0 : int const window1_base_iExtBoundCond = window1_base.ExtBoundCond;
5354 :
5355 0 : auto const &window2 = s_surf->Surface(IWin2);
5356 0 : int const window2_Enclosure = window2.SolarEnclIndex;
5357 0 : int const window2_iBaseSurf = window2.BaseSurf;
5358 0 : auto const &window2_base = window2_iBaseSurf > 0 ? s_surf->Surface(window2_iBaseSurf) : window2;
5359 0 : int const window2_base_iExtBoundCond = window2_base.ExtBoundCond;
5360 :
5361 : // Preconditions
5362 : // assert( window1.Zone == window2_Zone ); //? This is violated in PurchAirWithDoubleFacadeDaylighting so then why the asymmetry
5363 : // of only checking for wall/roof/floor for window2 zone below?
5364 :
5365 : // Loop over potentially obstructing surfaces, which can be building elements, like walls, or shadowing surfaces, like overhangs
5366 0 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
5367 :
5368 0 : for (int ISurf = 1; ISurf <= s_surf->TotSurfaces; ++ISurf) {
5369 0 : auto const &surface = s_surf->Surface(ISurf);
5370 0 : IType = surface.Class;
5371 0 : if ((surface.IsShadowing) || // Shadowing surface
5372 0 : ((surface.SolarEnclIndex == window2_Enclosure) && // Wall/ceiling/floor is in same zone as windows
5373 0 : (IType == SurfaceClass::Wall || IType == SurfaceClass::Roof || IType == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5374 0 : (ISurf != window1_iBaseSurf) && (ISurf != window2_iBaseSurf) && // Exclude windows' base surfaces
5375 0 : (ISurf != window1_base_iExtBoundCond) && (ISurf != window2_base_iExtBoundCond))) // Exclude windows' base-adjacent surfaces
5376 : {
5377 0 : Vector3<Real64> HP;
5378 0 : hit = PierceSurface(state, ISurf, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5379 0 : if (hit) break; // Segment pierces surface: Don't check the rest
5380 0 : }
5381 : }
5382 :
5383 : } else { // Surface octree search
5384 :
5385 0 : auto const *window1_base_p = &window1_base;
5386 0 : auto const &window1_base_adjacent = window1_base_iExtBoundCond > 0 ? s_surf->Surface(window1_base_iExtBoundCond) : window1_base;
5387 0 : auto const *window1_base_adjacent_p = &window1_base_adjacent;
5388 :
5389 0 : auto const *window2_base_p = &window2_base;
5390 0 : auto const &window2_base_adjacent = (window2_base_iExtBoundCond > 0) ? s_surf->Surface(window2_base_iExtBoundCond) : window2_base;
5391 0 : auto const *window2_base_adjacent_p = &window2_base_adjacent;
5392 :
5393 : // Lambda function for the octree to test for surface hit
5394 0 : auto surfaceHit = [=, &R1, &RN, &hit](SurfaceData const &surface) -> bool {
5395 0 : DataSurfaces::SurfaceClass const sClass = surface.Class;
5396 0 : Vector3<Real64> HP;
5397 0 : if ((surface.IsShadowing) || // Shadowing surface
5398 0 : ((surface.SolarEnclIndex == window2_Enclosure) && // Surface is in same zone as window
5399 0 : (sClass == SurfaceClass::Wall || sClass == SurfaceClass::Roof || sClass == SurfaceClass::Floor) && // Wall, ceiling/roof, or floor
5400 0 : (&surface != window1_base_p) && (&surface != window2_base_p) && // Exclude windows' base surfaces
5401 0 : (&surface != window1_base_adjacent_p) && (&surface != window2_base_adjacent_p))) // Exclude windows' base-adjacent surfaces
5402 : {
5403 0 : hit = PierceSurface(surface, R1, RN, d12, HP); // Check if R2-R1 segment pierces surface
5404 0 : return hit;
5405 : } else {
5406 0 : return false;
5407 : }
5408 0 : };
5409 :
5410 : // Check octree surface candidates until a hit is found, if any
5411 0 : state.dataHeatBalMgr->surfaceOctree.hasSurfaceSegmentIntersectsCube(R1, R2, surfaceHit);
5412 : }
5413 :
5414 0 : return hit;
5415 0 : } // DayltingHitBetWinObstruction()
5416 :
5417 249956 : void initDaylighting(EnergyPlusData &state, bool const initSurfaceHeatBalancefirstTime)
5418 : {
5419 : // For daylit zones, calculate interior daylight illuminance at reference points and
5420 : // simulate lighting control system to get overhead electric lighting reduction
5421 : // factor due to daylighting.
5422 249956 : auto &dl = state.dataDayltg;
5423 249956 : auto &s_surf = state.dataSurface;
5424 :
5425 338520 : for (int SurfNum : s_surf->AllExtSolWindowSurfaceList) {
5426 91270 : for (auto &refPt : s_surf->SurfaceWindow(SurfNum).refPts) {
5427 2706 : refPt.illumFromWinRep = refPt.lumWinRep = 0.0;
5428 : }
5429 : }
5430 :
5431 : // Reset space power reduction factors
5432 622909 : for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
5433 372953 : dl->spacePowerReductionFactor(spaceNum) = 1.0;
5434 : }
5435 251986 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5436 2030 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5437 2030 : thisDayltgCtrl.PowerReductionFactor = 1.0;
5438 2030 : if (state.dataEnvrn->PreviousSolRadPositive) {
5439 : // Reset to zero only if there was solar in the previous timestep, otherwise these are already zero
5440 941 : dl->enclDaylight(thisDayltgCtrl.enclIndex).InterReflIllFrIntWins = 0.0; // inter-reflected illuminance from interior windows
5441 2288 : for (int refPtNum = 1; refPtNum <= thisDayltgCtrl.TotalDaylRefPoints; ++refPtNum) {
5442 1347 : auto &refPt = thisDayltgCtrl.refPts(refPtNum);
5443 1347 : refPt.lums[iLum_Illum] = 0.0;
5444 1347 : refPt.glareIndex = 0.0;
5445 1347 : refPt.timeExceedingGlareIndexSetPoint = 0.0;
5446 1347 : refPt.timeExceedingDaylightIlluminanceSetPoint = 0.0;
5447 : }
5448 : }
5449 :
5450 2030 : if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0) {
5451 943 : if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination");
5452 943 : DayltgInteriorIllum(state, daylightCtrlNum);
5453 : }
5454 : }
5455 :
5456 : // The following report variables are valid only for daylit zones/enclosures without interior windows
5457 249956 : if (state.dataEnvrn->SunIsUp) {
5458 296113 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
5459 175612 : if ((state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) ||
5460 944 : (state.dataViewFactor->EnclSolInfo(enclNum).HasInterZoneWindow))
5461 173724 : continue;
5462 :
5463 944 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
5464 1887 : for (int extWinNum = 1; extWinNum <= thisEnclDaylight.NumOfDayltgExtWins; ++extWinNum) {
5465 943 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(extWinNum);
5466 943 : WinCover winCover = WinCover::Bare;
5467 2829 : if (s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
5468 1886 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
5469 0 : winCover = WinCover::Shaded;
5470 : }
5471 943 : int refPtCount = 0;
5472 1886 : for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
5473 943 : auto &daylCtrl = dl->daylightControl(controlNum);
5474 943 : if (daylCtrl.DaylightMethod != DaylightingMethod::SplitFlux) continue;
5475 :
5476 2293 : for (int refPtNum = 1; refPtNum <= daylCtrl.TotalDaylRefPoints; ++refPtNum) {
5477 1350 : ++refPtCount; // Count reference points across each daylighting control in the same enclosure
5478 1350 : auto &refPt = s_surf->SurfaceWindow(IWin).refPts(refPtCount);
5479 1350 : auto const &daylCtrlRefPt = daylCtrl.refPts(refPtNum);
5480 1350 : refPt.illumFromWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Illum][(int)winCover];
5481 1350 : refPt.lumWinRep = daylCtrlRefPt.extWins(extWinNum).lums[iLum_Source][(int)winCover];
5482 : }
5483 : } // for (controlNum)
5484 : } // for (extWinNum)
5485 : } // for (enclNum)
5486 : } // if (SunIsUp)
5487 :
5488 249956 : if (state.dataEnvrn->SunIsUp && (int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
5489 0 : if (initSurfaceHeatBalancefirstTime) DisplayString(state, "Computing Interior Daylighting Illumination for TDD pipes");
5490 0 : DayltgInteriorTDDIllum(state);
5491 : }
5492 :
5493 251986 : for (int daylightCtrlNum = 1; daylightCtrlNum <= (int)dl->daylightControl.size(); ++daylightCtrlNum) {
5494 2030 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5495 :
5496 : // RJH DElight Modification Begin - Call to DElight electric lighting control subroutine
5497 : // Check if the sun is up and the current Thermal Zone hosts a Daylighting:DElight object
5498 2030 : if (state.dataEnvrn->SunIsUp && thisDayltgCtrl.TotalDaylRefPoints != 0 && (thisDayltgCtrl.DaylightMethod == DaylightingMethod::DElight)) {
5499 0 : int zoneNum = thisDayltgCtrl.zoneIndex;
5500 : // Call DElight interior illuminance and electric lighting control subroutine
5501 0 : Real64 dPowerReducFac = 1.0; // Return value Electric Lighting Power Reduction Factor for current Zone and Timestep
5502 0 : Real64 dHISKFFC = state.dataEnvrn->HISKF * DataDElight::LUX2FC;
5503 0 : Real64 dHISUNFFC = state.dataEnvrn->HISUNF * DataDElight::LUX2FC;
5504 0 : Real64 dSOLCOS1 = state.dataEnvrn->SOLCOS.x;
5505 0 : Real64 dSOLCOS2 = state.dataEnvrn->SOLCOS.y;
5506 0 : Real64 dSOLCOS3 = state.dataEnvrn->SOLCOS.z;
5507 0 : Real64 dLatitude = state.dataEnvrn->Latitude;
5508 0 : Real64 dCloudFraction = state.dataEnvrn->CloudFraction;
5509 : // Init Error Flag to 0 (no Warnings or Errors) (returned from DElight)
5510 0 : int iErrorFlag = 0;
5511 :
5512 0 : DElightManagerF::DElightElecLtgCtrl(len(state.dataHeatBal->Zone(zoneNum).Name),
5513 0 : state.dataHeatBal->Zone(zoneNum).Name,
5514 : dLatitude,
5515 : dHISKFFC,
5516 : dHISUNFFC,
5517 : dCloudFraction,
5518 : dSOLCOS1,
5519 : dSOLCOS2,
5520 : dSOLCOS3,
5521 : dPowerReducFac,
5522 : iErrorFlag);
5523 : // Check Error Flag for Warnings or Errors returning from DElight
5524 : // RJH 2008-03-07: If no warnings/errors then read refpt illuminances for standard output reporting
5525 0 : if (iErrorFlag != 0) {
5526 0 : std::string cErrorMsg; // Each DElight Error Message can be up to 200 characters long
5527 : // Open DElight Electric Lighting Error File for reading
5528 0 : auto iDElightErrorFile = state.files.outputDelightDfdmpFilePath.try_open(state.files.outputControl.delightdfdmp); // (THIS_AUTO_OK)
5529 0 : bool elOpened = iDElightErrorFile.good();
5530 :
5531 : // Sequentially read lines in DElight Electric Lighting Error File
5532 : // and process them using standard EPlus warning/error handling calls
5533 0 : bool bEndofErrFile = false;
5534 0 : while (!bEndofErrFile && elOpened) {
5535 0 : auto cErrorLine = iDElightErrorFile.readLine(); // (THIS_AUTO_OK)
5536 0 : if (cErrorLine.eof) {
5537 0 : bEndofErrFile = true;
5538 0 : continue;
5539 : }
5540 :
5541 : // Is the current line a Warning message?
5542 0 : if (has_prefix(cErrorLine.data, "WARNING: ")) {
5543 0 : cErrorMsg = cErrorLine.data.substr(9);
5544 0 : ShowWarningError(state, cErrorMsg);
5545 : }
5546 : // Is the current line an Error message?
5547 0 : if (has_prefix(cErrorLine.data, "ERROR: ")) {
5548 0 : cErrorMsg = cErrorLine.data.substr(7);
5549 0 : ShowSevereError(state, cErrorMsg);
5550 0 : iErrorFlag = 1;
5551 : }
5552 0 : }
5553 :
5554 : // Close DElight Error File and delete
5555 :
5556 0 : if (elOpened) {
5557 0 : iDElightErrorFile.close();
5558 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
5559 : }
5560 : // If any DElight Error occurred then ShowFatalError to terminate
5561 0 : if (iErrorFlag > 0) {
5562 0 : ShowFatalError(state, "End of DElight Error Messages");
5563 : }
5564 0 : } else { // RJH 2008-03-07: No errors
5565 : // extract reference point illuminance values from DElight Electric Lighting dump file for reporting
5566 : // Open DElight Electric Lighting Dump File for reading
5567 0 : auto iDElightErrorFile = state.files.outputDelightEldmpFilePath.try_open(state.files.outputControl.delighteldmp); // (THIS_AUTO_OK)
5568 0 : bool elOpened = iDElightErrorFile.is_open();
5569 :
5570 : // Sequentially read lines in DElight Electric Lighting Dump File
5571 : // and extract refpt illuminances for standard EPlus output handling
5572 0 : bool bEndofErrFile = false;
5573 0 : int iDElightRefPt = 0; // Reference Point number for reading DElight Dump File (eplusout.delighteldmp)
5574 0 : while (!bEndofErrFile && elOpened) {
5575 0 : auto line = iDElightErrorFile.read<Real64>(); // (THIS_AUTO_OK)
5576 0 : Real64 dRefPtIllum = line.data; // tmp var for reading RefPt illuminance
5577 0 : if (line.eof) {
5578 0 : bEndofErrFile = true;
5579 0 : continue;
5580 : }
5581 : // Increment refpt counter
5582 0 : ++iDElightRefPt;
5583 : // Assure refpt index does not exceed number of refpts in this zone
5584 0 : if (iDElightRefPt <= thisDayltgCtrl.TotalDaylRefPoints) {
5585 0 : thisDayltgCtrl.refPts(iDElightRefPt).lums[iLum_Illum] = dRefPtIllum;
5586 : }
5587 : }
5588 :
5589 : // Close DElight Electric Lighting Dump File and delete
5590 0 : if (elOpened) {
5591 0 : iDElightErrorFile.close();
5592 0 : FileSystem::removeFile(iDElightErrorFile.filePath);
5593 : };
5594 0 : }
5595 : // Store the calculated total zone Power Reduction Factor due to DElight daylighting
5596 : // in the ZoneDaylight structure for later use
5597 0 : thisDayltgCtrl.PowerReductionFactor = dPowerReducFac;
5598 : }
5599 : // RJH DElight Modification End - Call to DElight electric lighting control subroutine
5600 : }
5601 :
5602 249956 : if (state.dataEnvrn->SunIsUp && !state.dataGlobal->DoingSizing) {
5603 88617 : DayltgInteriorMapIllum(state);
5604 : }
5605 586606 : for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
5606 709603 : for (int const spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
5607 372953 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
5608 461517 : for (int SurfNum = thisSpace.WindowSurfaceFirst; SurfNum <= thisSpace.WindowSurfaceLast; ++SurfNum) {
5609 88564 : s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
5610 88564 : if (IS_SHADED(s_surf->SurfWinShadingFlag(SurfNum))) {
5611 0 : s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 1.0;
5612 : } else {
5613 88564 : s_surf->SurfWinFracTimeShadingDeviceOn(SurfNum) = 0.0;
5614 : }
5615 : }
5616 : }
5617 : }
5618 249956 : }
5619 :
5620 249956 : void manageDaylighting(EnergyPlusData &state)
5621 : {
5622 249956 : auto &dl = state.dataDayltg;
5623 :
5624 249956 : if (state.dataEnvrn->SunIsUp && (state.dataEnvrn->BeamSolarRad + state.dataEnvrn->GndSolarRad + state.dataEnvrn->DifSolarRad > 0.0)) {
5625 177258 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
5626 106045 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
5627 106045 : if (enclSol.TotalEnclosureDaylRefPoints == 0 || !enclSol.HasInterZoneWindow) continue;
5628 :
5629 0 : DayltgInterReflIllFrIntWins(state, enclNum);
5630 0 : for (int daylightCtrlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
5631 0 : DayltgGlareWithIntWins(state, daylightCtrlNum);
5632 : }
5633 : }
5634 71213 : DayltgElecLightingControl(state);
5635 178743 : } else if (dl->mapResultsToReport && state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
5636 2 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
5637 1 : ReportIllumMap(state, MapNum);
5638 : }
5639 1 : dl->mapResultsToReport = false;
5640 : }
5641 249956 : } // manageDaylighting()
5642 :
5643 954 : void DayltgInteriorIllum(EnergyPlusData &state,
5644 : int const daylightCtrlNum) // Daylighting:Controls number
5645 : {
5646 :
5647 : // SUBROUTINE INFORMATION:
5648 : // AUTHOR Fred Winkelmann
5649 : // DATE WRITTEN July 1997
5650 : // MODIFIED March 2000, FCW: interpolate clear-sky daylight factors using
5651 : // HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
5652 : // only HourOfDay was used
5653 : // Jan 2001, FCW: interpolate in slat angle for windows with blinds
5654 : // that have movable slats
5655 : // Oct 2002, LKL: changed interpolation steps to HourOfDay/WeightNow
5656 : // LastHour/WeightPreviousHour
5657 : // Aug 2003, FCW: fix bug that prevented shadingControlType =
5658 : // MEETDAYLIGHTILLUMINANCESETPOINT from working
5659 : // Mar 2004, FCW: fix bug in calc of illuminance setpoint contribution
5660 : // to background luminance: now it is divided by pi to give cd/m2
5661 : // Mar 2004, FCW: modify to handle daylighting through interior windows
5662 : // June 2009, TH: modified for thermochromic windows
5663 : // Jan 2010, TH (CR 7984): added iterations for switchable windows with shading
5664 : // control of MeetDaylightIlluminanceSetpoint and glare control is active
5665 : // Also corrected bugs (CR 7988) for switchable glazings not related to CR 7984
5666 :
5667 : // PURPOSE OF THIS SUBROUTINE:
5668 : // Using daylighting factors and exterior illuminance, determine
5669 : // the current-hour interior daylight illuminance and glare index
5670 : // at each reference point in a space. Deploy window shading window by window
5671 : // if glare control is active for window and if the acceptable glare index
5672 : // is exceeded at both reference points.
5673 :
5674 : // Called by InitSurfaceHeatBalance.
5675 :
5676 : // REFERENCES:
5677 : // Based on DOE-2.1E subroutine DINTIL.
5678 954 : auto &dl = state.dataDayltg;
5679 954 : auto &s_surf = state.dataSurface;
5680 :
5681 954 : Real64 constexpr tmpSWIterStep(0.05); // step of switching factor, assuming maximum of 20 switching states
5682 :
5683 : int NREFPT; // Number of daylighting reference points
5684 : int iSky1; // Sky type index values for averaging two sky types
5685 : int iSky2;
5686 954 : Array1D<Real64> SetPnt; // Illuminance setpoint at reference points (lux)
5687 954 : Array1D<Real64> GLRNEW; // New glare index at reference point
5688 :
5689 954 : auto &thisDayltgCtrl = dl->daylightControl(daylightCtrlNum);
5690 954 : int enclNum = thisDayltgCtrl.enclIndex;
5691 954 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
5692 : int ISWFLG; // Switchable glazing flag: =1 if one or more windows in a zone
5693 : // has switchable glazing that adjusts visible transmittance to just meet
5694 : // daylighting setpoint; =0 otherwise.
5695 : Real64 VTRAT; // Ratio between switched and unswitched visible transmittance at normal incidence
5696 : Real64 BACL; // Window background (surround) luminance for glare calc (cd/m2)
5697 : Real64 SkyWeight; // Weighting factor used to average two different sky types
5698 : Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
5699 : // luminous efficacy and horizontal illuminance from averaged sky
5700 : bool GlareFlag; // True if maximum glare is exceeded
5701 :
5702 : Real64 VTRatio; // VT (visible transmittance) ratio = VTNow / VTMaster
5703 : Real64 VTNow; // VT of the time step actual TC window
5704 : Real64 VTMaster; // VT of the base/master TC window
5705 :
5706 954 : Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> tmpDaylFromWinAtRefPt;
5707 :
5708 954 : bool breakOuterLoop(false);
5709 954 : bool continueOuterLoop(false);
5710 :
5711 : struct ShadeGroupLums
5712 : {
5713 : Array1D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, (int)Lum::Num>> WDAYIL; // Illuminance from window at ref-point
5714 : Array1D<std::array<Real64, (int)Lum::Num>> RDAYIL; // Illuminance from window at ref-point after closing shade
5715 : Real64 switchedWinLum;
5716 : Real64 unswitchedWinLum;
5717 : Real64 switchedTvis;
5718 : Real64 unswitchedTvis;
5719 : Real64 lumRatio;
5720 : };
5721 :
5722 954 : Array1D<ShadeGroupLums> shadeGroupsLums;
5723 :
5724 : // Array2D<std::array<std::array<Real64, (int)DataSurfaces::WinCover::Num>, iLum_Num>> WDAYIL; // Illuminance from window at reference point
5725 : // (second index)
5726 : // the number of shade deployment groups (third index)
5727 : // Array2D<std::array<Real64, (int)DataSurfaces::WinCover::Num>> WBACLU; // Background illuminance from window at reference point (second index)
5728 : // the number of shade deployment groups (third index)
5729 : // Array2D<std::array<Real64, iLum_Num>> RDAYIL; // Illuminance from window at reference point after closing shade
5730 : // Array2D<Real64> RBACLU; // Background illuminance from window at reference point after closing shade
5731 : // Array1D<Real64> DILLSW; // Illuminance a ref point from a group of windows that can be switched together,
5732 : // Array1D<Real64> DILLUN; // and from those that aren't (lux)
5733 : // Array1D<Real64> TVIS1; // Visible transmittance at normal incidence of unswitched glazing
5734 : // Array1D<Real64> TVIS2; // Visible transmittance at normal incidence of fully-switched glazing
5735 : // Array1D<Real64> ASETIL; // Illuminance ratio (lux)
5736 :
5737 954 : if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) return;
5738 :
5739 954 : NREFPT = thisDayltgCtrl.TotalDaylRefPoints;
5740 :
5741 954 : if (dl->DayltgInteriorIllum_firstTime) {
5742 7 : dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5743 7 : dl->DayltgInteriorIllum_firstTime = false;
5744 : }
5745 :
5746 : // size these for the maximum of the shade deployment order
5747 954 : shadeGroupsLums.allocate(dl->maxShadeDeployOrderExtWins);
5748 956 : for (auto &shadeGroupLums : shadeGroupsLums) {
5749 2 : shadeGroupLums.WDAYIL.allocate(dl->maxControlRefPoints);
5750 2 : shadeGroupLums.RDAYIL.allocate(dl->maxControlRefPoints);
5751 : }
5752 :
5753 : // Three arrays to save original clear and dark (fully switched) states'
5754 : // zone/window daylighting properties.
5755 954 : tmpDaylFromWinAtRefPt.allocate(dl->maxNumRefPtInAnyDaylCtrl, dl->maxEnclSubSurfaces);
5756 :
5757 954 : SetPnt.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5758 954 : dl->DaylIllum.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5759 954 : GLRNEW.allocate(dl->maxNumRefPtInAnyDaylCtrl);
5760 :
5761 2326 : for (int iRefPt = 1; iRefPt <= (int)dl->maxNumRefPtInAnyDaylCtrl; ++iRefPt) {
5762 2744 : for (int iExtWin = 1; iExtWin <= (int)dl->maxEnclSubSurfaces; ++iExtWin) {
5763 1372 : auto &tmpDayl = tmpDaylFromWinAtRefPt(iRefPt, iExtWin);
5764 1372 : tmpDayl[iLum_Illum] = tmpDayl[iLum_Back] = tmpDayl[iLum_Source] = {0.0, 0.0};
5765 : }
5766 : }
5767 :
5768 : // Initialize reference point illuminance and window background luminance
5769 2323 : for (int IL = 1; IL <= NREFPT; ++IL) {
5770 1369 : auto &refPt = thisDayltgCtrl.refPts(IL);
5771 1369 : SetPnt(IL) = refPt.illumSetPoint;
5772 1369 : dl->DaylIllum(IL) = 0.0;
5773 1369 : refPt.lums[iLum_Back] = 0.0;
5774 : }
5775 :
5776 954 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
5777 887 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
5778 887 : iSky1 = (int)SkyType::Clear;
5779 887 : iSky2 = (int)SkyType::ClearTurbid;
5780 67 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
5781 56 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
5782 56 : iSky1 = (int)SkyType::ClearTurbid;
5783 56 : iSky2 = (int)SkyType::Intermediate;
5784 : } else { // Sky is average of intermediate and overcast
5785 11 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
5786 11 : iSky1 = (int)SkyType::Intermediate;
5787 11 : iSky2 = (int)SkyType::Overcast;
5788 : }
5789 :
5790 : // First loop over exterior windows associated with this zone. The window may be an exterior window in
5791 : // the zone or an exterior window in an adjacent zone that shares an interior window with the zone.
5792 : // Find contribution of each window to the daylight illum and to the glare numerator at each reference point.
5793 : // Use shading flags set in WindowShadingManager.
5794 1908 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5795 954 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5796 :
5797 : // Added TH 6/29/2009 for thermochromic windows
5798 954 : VTRatio = 1.0;
5799 954 : if (NREFPT > 0) {
5800 954 : int const IConst = s_surf->Surface(IWin).Construction;
5801 954 : auto const &construction = state.dataConstruction->Construct(IConst);
5802 954 : if (construction.isTCWindow) {
5803 : // For thermochromic windows, daylight and glare factors are always calculated
5804 : // based on the master construction. They need to be adjusted by the VTRatio, including:
5805 : // ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
5806 : // DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
5807 0 : VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
5808 0 : VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
5809 0 : VTRatio = VTNow / VTMaster;
5810 : }
5811 : }
5812 :
5813 2855 : bool ShadedOrDiffusingGlassWin = s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF &&
5814 1901 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin));
5815 :
5816 954 : Real64 wgtCurrHr = state.dataGlobal->WeightNow;
5817 954 : Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
5818 :
5819 954 : std::array<Illums, (int)DataSurfaces::WinCover::Num> SFHR; // Sky source luminance factor for sky type, bare/shaded window
5820 954 : std::array<Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
5821 954 : std::array<Illums, (int)DataSurfaces::WinCover::Num> BFHR; // Sky background luminance factor for sky type, bare/shaded window
5822 :
5823 : // Loop over reference points
5824 2323 : for (int IL = 1; IL <= NREFPT; ++IL) {
5825 :
5826 1369 : auto const &daylFacCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Bare];
5827 1369 : auto const &daylFacPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Bare];
5828 : // Daylight factors for current sun position
5829 1369 : auto const &illFacCurr = daylFacCurr[iLum_Illum];
5830 1369 : auto const &illFacPrev = daylFacPrev[iLum_Illum];
5831 1369 : auto &dfhr = DFHR[iWinCover_Bare];
5832 1369 : auto const &backFacCurr = daylFacCurr[iLum_Back];
5833 1369 : auto const &backFacPrev = daylFacPrev[iLum_Back];
5834 1369 : auto &bfhr = BFHR[iWinCover_Bare];
5835 1369 : auto const &sourceFacCurr = daylFacCurr[iLum_Source];
5836 1369 : auto const &sourceFacPrev = daylFacPrev[iLum_Source];
5837 1369 : auto &sfhr = SFHR[iWinCover_Bare];
5838 :
5839 1369 : auto const &daylFac2Curr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
5840 1369 : auto const &daylFac2Prev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
5841 :
5842 1369 : auto const &illFac2Curr = daylFac2Curr[iLum_Illum];
5843 1369 : auto const &illFac2Prev = daylFac2Prev[iLum_Illum];
5844 1369 : auto &dfhr2 = DFHR[iWinCover_Shaded];
5845 1369 : auto const &backFac2Curr = daylFac2Curr[iLum_Back];
5846 1369 : auto const &backFac2Prev = daylFac2Prev[iLum_Back];
5847 1369 : auto &bfhr2 = BFHR[iWinCover_Shaded];
5848 1369 : auto const &sourceFac2Curr = daylFac2Curr[iLum_Source];
5849 1369 : auto const &sourceFac2Prev = daylFac2Prev[iLum_Source];
5850 1369 : auto &sfhr2 = SFHR[iWinCover_Shaded];
5851 :
5852 : #ifdef GET_OUT
5853 : auto const &daylFacShCurr = thisDayltgCtrl.daylFac[state.dataGlobal->HourOfDay](loop, IL)[iWinCover_Shaded];
5854 : auto const &daylFacShPrev = thisDayltgCtrl.daylFac[state.dataGlobal->PreviousHour](loop, IL)[iWinCover_Shaded];
5855 :
5856 : auto const &illFacShCurr = daylFacShCurr[iLum_Illum];
5857 : auto const &illFacShPrev = daylFacShPrev[iLum_Illum];
5858 :
5859 : auto const &backFacShCurr = daylFacShCurr[iLum_Back];
5860 : auto const &backFacShPrev = daylFacShPrev[iLum_Back];
5861 :
5862 : auto const &sourceFacShCurr = daylFacShCurr[iLum_Source];
5863 : auto const &sourceFacShPrev = daylFacShPrev[iLum_Source];
5864 : #endif // GET_OUT
5865 6845 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
5866 :
5867 : // ===Bare window===
5868 : // Sky daylight factor for sky type (second index), bare/shaded window (first index)
5869 5476 : dfhr.sky[iSky] = VTRatio * (wgtCurrHr * illFacCurr.sky[iSky] + wgtPrevHr * illFacPrev.sky[iSky]);
5870 5476 : bfhr.sky[iSky] = VTRatio * (wgtCurrHr * backFacCurr.sky[iSky] + wgtPrevHr * backFacPrev.sky[iSky]);
5871 5476 : sfhr.sky[iSky] = VTRatio * (wgtCurrHr * sourceFacCurr.sky[iSky] + wgtPrevHr * sourceFacPrev.sky[iSky]);
5872 :
5873 5476 : if (ShadedOrDiffusingGlassWin) {
5874 :
5875 : // ===Shaded window or window with diffusing glass===
5876 : // Shade, screen, blind with fixed slats, or diffusing glass
5877 16 : dfhr2.sky[iSky] = VTRatio * (wgtCurrHr * illFac2Curr.sky[iSky] + wgtPrevHr * illFac2Prev.sky[iSky]);
5878 16 : bfhr2.sky[iSky] = VTRatio * (wgtCurrHr * backFac2Curr.sky[iSky] + wgtPrevHr * backFac2Prev.sky[iSky]);
5879 16 : sfhr2.sky[iSky] = VTRatio * (wgtCurrHr * sourceFac2Curr.sky[iSky] + wgtPrevHr * sourceFac2Prev.sky[iSky]);
5880 : } // End of check if window is shaded or has diffusing glass
5881 : } // for (iSky)
5882 :
5883 : // Sun daylight factor for bare/shaded window
5884 2738 : DFHR[iWinCover_Bare].sun =
5885 1369 : VTRatio * (wgtCurrHr * (illFacCurr.sun + illFacCurr.sunDisk) + wgtPrevHr * (illFacPrev.sun + illFacPrev.sunDisk));
5886 :
5887 : // Sun background luminance factor for bare/shaded window
5888 2738 : BFHR[iWinCover_Bare].sun =
5889 1369 : VTRatio * (wgtCurrHr * (backFacCurr.sun + backFacCurr.sunDisk) + wgtPrevHr * (backFacPrev.sun + backFacPrev.sunDisk));
5890 :
5891 : // Sun source luminance factor for bare/shaded window
5892 2738 : SFHR[iWinCover_Bare].sun =
5893 1369 : VTRatio * (wgtCurrHr * (sourceFacCurr.sun + sourceFacCurr.sunDisk) + wgtPrevHr * (sourceFacPrev.sun + sourceFacPrev.sunDisk));
5894 :
5895 1369 : if (ShadedOrDiffusingGlassWin) {
5896 :
5897 : // ===Shaded window or window with diffusing glass===
5898 : // Shade, screen, blind with fixed slats, or diffusing glass
5899 4 : DFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * illFac2Curr.sun + wgtPrevHr * illFac2Prev.sun);
5900 4 : BFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * backFac2Curr.sun + wgtPrevHr * backFac2Prev.sun);
5901 4 : SFHR[iWinCover_Shaded].sun = VTRatio * (wgtCurrHr * sourceFac2Curr.sun + wgtPrevHr * sourceFac2Prev.sun);
5902 :
5903 4 : auto const &surfShade = s_surf->surfShades(IWin);
5904 4 : if (!surfShade.blind.slatBlockBeam) {
5905 4 : DFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * illFac2Curr.sunDisk + wgtPrevHr * illFac2Prev.sunDisk);
5906 4 : BFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * backFac2Curr.sunDisk + wgtPrevHr * backFac2Prev.sunDisk);
5907 4 : SFHR[iWinCover_Shaded].sun += VTRatio * (wgtCurrHr * sourceFac2Curr.sunDisk + wgtPrevHr * sourceFac2Prev.sunDisk);
5908 : }
5909 : } // End of check if window is shaded or has diffusing glass
5910 :
5911 : // Get illuminance at ref point from bare and shaded window by
5912 : // multiplying daylight factors by exterior horizontal illuminance
5913 :
5914 : // Adding 0.001 in the following prevents zero HorIllSky in early morning or late evening when sun
5915 : // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
5916 1369 : auto const &gilskCurr = dl->horIllum[state.dataGlobal->HourOfDay];
5917 1369 : auto const &gilskPrev = dl->horIllum[state.dataGlobal->PreviousHour];
5918 :
5919 : // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
5920 : // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
5921 : // also calculated in DayltgLuminousEfficacy.
5922 : Real64 horIllSky1 =
5923 1369 : state.dataGlobal->WeightNow * gilskCurr.sky[iSky1] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky1] + 0.001;
5924 : Real64 horIllSky2 =
5925 1369 : state.dataGlobal->WeightNow * gilskCurr.sky[iSky2] + state.dataGlobal->WeightPreviousHour * gilskPrev.sky[iSky2] + 0.001;
5926 :
5927 1369 : HorIllSkyFac = state.dataEnvrn->HISKF / ((1 - SkyWeight) * horIllSky2 + SkyWeight * horIllSky1);
5928 :
5929 1369 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
5930 1369 : auto &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
5931 2742 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
5932 2738 : auto const &dfhr3 = DFHR[iWinCover];
5933 2738 : auto const &bfhr3 = BFHR[iWinCover];
5934 2738 : auto const &sfhr3 = SFHR[iWinCover];
5935 :
5936 : // What is this?
5937 2738 : if (iWinCover == iWinCover_Shaded && !ShadedOrDiffusingGlassWin) break;
5938 :
5939 1373 : daylFromWinAtRefPt[iLum_Illum][iWinCover] =
5940 1373 : dfhr3.sun * state.dataEnvrn->HISUNF +
5941 1373 : HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * horIllSky1 + dfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
5942 1373 : daylFromWinAtRefPt[iLum_Back][iWinCover] =
5943 1373 : bfhr3.sun * state.dataEnvrn->HISUNF +
5944 1373 : HorIllSkyFac * (bfhr3.sky[iSky1] * SkyWeight * horIllSky1 + bfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
5945 1373 : daylFromWinAtRefPt[iLum_Source][iWinCover] =
5946 1373 : sfhr3.sun * state.dataEnvrn->HISUNF +
5947 1373 : HorIllSkyFac * (sfhr3.sky[iSky1] * SkyWeight * horIllSky1 + sfhr3.sky[iSky2] * (1.0 - SkyWeight) * horIllSky2);
5948 :
5949 1373 : daylFromWinAtRefPt[iLum_Source][iWinCover] = max(daylFromWinAtRefPt[iLum_Source][iWinCover], 0.0);
5950 :
5951 : // Added TH 1/21/2010 - save the original clear and dark (fully switched) states'
5952 : // zone daylighting values, needed for switachable glazings
5953 1373 : tmpDayl[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
5954 1373 : tmpDayl[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
5955 1373 : tmpDayl[iLum_Source][iWinCover] = daylFromWinAtRefPt[iLum_Source][iWinCover];
5956 : } // for for (iWinCover)
5957 :
5958 : } // End of reference point loop, IL
5959 : } // End of first loop over exterior windows associated with this zone
5960 :
5961 : // Initialize flag that one or more windows has switchable glazing
5962 : // control that adjusts visible transmittance to just meet dayltg setpoint
5963 : // (and the window has not already been switched)
5964 954 : ISWFLG = 0;
5965 :
5966 : // Second loop over windows. Find total daylight illuminance and background luminance
5967 : // for each ref pt from all exterior windows associated with the zone. Use shading flags.
5968 : // This illuminance excludes contribution of inter-reflected illuminance produced by solar
5969 : // entering the zone through interior windows (which is calculated in DayltgInterReflIllFrIntWins.
5970 :
5971 1908 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
5972 954 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
5973 954 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
5974 954 : if (s_surf->Surface(IWin).HasShadeControl && ISWFLG == 0) {
5975 2 : if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
5976 0 : s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
5977 0 : ISWFLG = 1;
5978 : }
5979 :
5980 : // Determine if illuminance contribution is from bare or shaded window
5981 : // For switchable glazings with shading control type of WSCT_MeetDaylIlumSetp,
5982 : // the shading flag is initialized at GlassConditionallyLightened (20), and
5983 : // the window is initialized at clear state: IS = 1
5984 : // For other windows with glare control, the shading flag is initialized at >10, to be determined
5985 954 : WinCover winCover = findWinShadingStatus(state, IWin);
5986 :
5987 2323 : for (int IL = 1; IL <= NREFPT; ++IL) {
5988 1369 : auto &refPt = thisDayltgCtrl.refPts(IL);
5989 1369 : dl->DaylIllum(IL) += refPt.extWins(loop).lums[iLum_Illum][(int)winCover];
5990 1369 : refPt.lums[iLum_Back] += refPt.extWins(loop).lums[iLum_Back][(int)winCover];
5991 : }
5992 : } // End of second window loop over exterior windows associated with this zone
5993 :
5994 : // Optical switching control (e.g. electrochromic glass) to adjust
5995 : // window's vis trans downward so daylight level equals or is as
5996 : // close as possible to the illuminance setpoint at first reference point.
5997 : // Assumes vis trans in the fully switched state is less than that in the
5998 : // unswitched state. Assumes some windows in a space may have this control and
5999 : // others not.
6000 :
6001 954 : int count = 0;
6002 :
6003 : // If daylight illuminance is above setpoint, allow switching
6004 954 : if (ISWFLG != 0 && dl->DaylIllum(1) > SetPnt(1)) {
6005 :
6006 : // array of flags to indicate that previously groups would have already shaded this window
6007 0 : Array1D_bool previously_shaded;
6008 0 : previously_shaded.dimension(dl->maxDayltgExtWins, false);
6009 :
6010 : // Third loop over windows. Get illuminance at ref pt 1 from
6011 : // windows that can be switched (DILLSW) with a group and those that can't (DILLUN).
6012 : // Windows that can be switched are initially in the unswitched state. For subsequent
6013 : // groups the windows in previous groups are fully switched.
6014 0 : for (auto &shadeGroupLum : shadeGroupsLums) {
6015 0 : shadeGroupLum.switchedWinLum = shadeGroupLum.unswitchedWinLum = 0.0;
6016 : }
6017 :
6018 0 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6019 0 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6020 0 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6021 0 : for (const int IWin : listOfExtWin) {
6022 0 : ++count;
6023 : // need to map back to the original order of the "loop" to not change all the other data structures
6024 0 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6025 0 : if (loop == 0) continue;
6026 :
6027 0 : if (!s_surf->Surface(IWin).HasShadeControl) continue;
6028 :
6029 0 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6030 0 : WinCover winCover = findWinShadingStatus(state, IWin);
6031 :
6032 0 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Illum];
6033 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened &&
6034 0 : s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
6035 0 : !previously_shaded(loop)) {
6036 0 : shadeGroupLums.switchedWinLum += daylFromWinAtRefPt[(int)winCover];
6037 0 : previously_shaded(loop) = true;
6038 : } else {
6039 0 : shadeGroupLums.unswitchedWinLum +=
6040 0 : !previously_shaded(loop) ? daylFromWinAtRefPt[(int)winCover] : daylFromWinAtRefPt[iWinCover_Shaded];
6041 : }
6042 : } // for (IWin)
6043 : } // for (igroup)
6044 :
6045 : // Transmittance multiplier
6046 0 : for (auto &shadeGroupLums : shadeGroupsLums) {
6047 0 : shadeGroupLums.lumRatio = (SetPnt(1) - shadeGroupLums.unswitchedWinLum) / (shadeGroupLums.switchedWinLum + 0.00001);
6048 : }
6049 :
6050 : // ASETIL < 1 means there's enough light, so check for switching
6051 :
6052 : // Fourth loop over windows to determine which to switch
6053 : // iterate in the order that the shades are specified in WindowShadeControl
6054 0 : count = 0;
6055 0 : breakOuterLoop = false;
6056 0 : continueOuterLoop = false;
6057 0 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6058 0 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6059 0 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6060 :
6061 0 : for (const int IWin : listOfExtWin) {
6062 0 : ++count;
6063 0 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
6064 : // need to map back to the original order of the "loop" to not change all the other data structures
6065 0 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6066 0 : if (loop > 0 && shadeGroupLums.lumRatio < 1.0) {
6067 :
6068 0 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6069 0 : if (!s_surf->Surface(IWin).HasShadeControl) {
6070 0 : continueOuterLoop = true;
6071 0 : continue;
6072 : }
6073 0 : if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::GlassConditionallyLightened ||
6074 0 : s_surf->WindowShadingControl(ICtrl).shadingControlType != WindowShadingControlType::MeetDaylIlumSetp) {
6075 0 : continueOuterLoop = true;
6076 0 : continue;
6077 : }
6078 :
6079 0 : int const IConst = s_surf->SurfActiveConstruction(IWin);
6080 : // Vis trans at normal incidence of unswitched glass
6081 0 : shadeGroupLums.unswitchedTvis =
6082 0 : Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
6083 :
6084 : // Vis trans at normal incidence of fully switched glass
6085 0 : int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
6086 0 : shadeGroupLums.switchedTvis =
6087 0 : Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
6088 :
6089 : // Reset shading flag to indicate that window is shaded by being partially or fully switched
6090 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
6091 :
6092 : // ASETIL < 0 means illuminance from non-daylight-switchable windows exceeds setpoint,
6093 : // so completely switch all daylight-switchable windows to minimize solar gain
6094 0 : if (shadeGroupLums.lumRatio <= 0.0) {
6095 0 : s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
6096 0 : s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
6097 : } else {
6098 : // Case where 0 < ASETIL < 1: darken glass in all
6099 : // daylight-switchable windows to just meet illuminance setpoint
6100 : // From this equation: SETPNT(1) = DILLUN + DILLSW/TVIS1 * VisTransSelected
6101 0 : s_surf->SurfWinVisTransSelected(IWin) =
6102 0 : max(shadeGroupLums.switchedTvis, shadeGroupLums.lumRatio * shadeGroupLums.unswitchedTvis) + 0.000001;
6103 0 : s_surf->SurfWinSwitchingFactor(IWin) = (shadeGroupLums.unswitchedTvis - s_surf->SurfWinVisTransSelected(IWin)) /
6104 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis + 0.000001);
6105 : // bound switching factor between 0 and 1
6106 0 : s_surf->SurfWinSwitchingFactor(IWin) = min(1.0, s_surf->SurfWinSwitchingFactor(IWin));
6107 0 : s_surf->SurfWinSwitchingFactor(IWin) = max(0.0, s_surf->SurfWinSwitchingFactor(IWin));
6108 : }
6109 :
6110 : // Adjust daylight quantities based on ratio between switched and unswitched visible transmittance
6111 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6112 : // DaylIllum(IL) and BacLum(IL) were calculated at the clear state:
6113 : // and need to adjusted for intermediate switched state at VisTransSelected:
6114 0 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6115 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6116 :
6117 0 : VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.unswitchedTvis + 0.000001);
6118 0 : dl->DaylIllum(IL) += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
6119 0 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] += (VTRAT - 1.0) * daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
6120 :
6121 : // Adjust illum, background illum and source luminance for this window in intermediate switched state
6122 : // for later use in the DayltgGlare calc because SurfaceWindow(IWin)%ShadingFlag = WinShadingType::SwitchableGlazing = 2
6123 0 : VTRAT = s_surf->SurfWinVisTransSelected(IWin) / (shadeGroupLums.switchedTvis + 0.000001);
6124 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Illum][iWinCover_Shaded];
6125 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Back][iWinCover_Shaded];
6126 0 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = VTRAT * tmpDayl[iLum_Source][iWinCover_Shaded];
6127 : } // for (IL)
6128 : } // if (loop > 0 && ASETIL < 1)
6129 : // If new daylight does not exceed the illuminance setpoint, done, no more checking other groups of switchable glazings
6130 0 : if (dl->DaylIllum(1) <= SetPnt(1)) {
6131 0 : breakOuterLoop = true;
6132 0 : break;
6133 : }
6134 : } // for (Win)
6135 0 : if (breakOuterLoop) break;
6136 0 : if (continueOuterLoop) continue;
6137 : } // for (igroup)
6138 :
6139 0 : } // ISWFLG /= 0 .AND. DaylIllum(1) > SETPNT(1)
6140 :
6141 : // loop over windows to do luminance based control
6142 954 : count = 0;
6143 956 : for (int igroup = 1; igroup <= (int)thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6144 4 : for (int const IWin : thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1]) {
6145 2 : ++count;
6146 2 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6147 2 : WindowShadingControlType shCtrlType = s_surf->WindowShadingControl(ICtrl).shadingControlType;
6148 2 : if (!((shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffMidNight) ||
6149 : (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffSunset) ||
6150 : (shCtrlType == WindowShadingControlType::HiSolar_HiLumin_OffNextMorning)))
6151 0 : continue;
6152 : // need to map back to the original order of the "loop" to not change all the other data structures
6153 2 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6154 2 : if (loop == 0) continue;
6155 :
6156 2 : WinShadingType currentFlag = s_surf->SurfWinShadingFlag(IWin);
6157 2 : WinShadingType ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
6158 2 : if ((currentFlag != WinShadingType::IntShadeConditionallyOff) && (currentFlag != WinShadingType::GlassConditionallyLightened) &&
6159 0 : (currentFlag != WinShadingType::ExtShadeConditionallyOff) && (currentFlag != WinShadingType::IntBlindConditionallyOff) &&
6160 0 : (currentFlag != WinShadingType::ExtBlindConditionallyOff) && (currentFlag != WinShadingType::BGShadeConditionallyOff) &&
6161 : (currentFlag != WinShadingType::BGBlindConditionallyOff))
6162 0 : continue;
6163 :
6164 2 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(1).extWins(loop).lums;
6165 2 : if (daylFromWinAtRefPt[iLum_Source][iWinCover_Bare] > s_surf->WindowShadingControl(ICtrl).SetPoint2) {
6166 : // shade on if luminance of this window is above setpoint
6167 1 : s_surf->SurfWinShadingFlag(IWin) = ShType;
6168 : // update total illuminance and background luminance
6169 2 : for (int IL = 1; IL <= NREFPT; ++IL) {
6170 1 : dl->DaylIllum(IL) += daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Illum][iWinCover_Bare];
6171 1 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] +=
6172 1 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] - daylFromWinAtRefPt[iLum_Back][iWinCover_Bare];
6173 : }
6174 : } else {
6175 : // shade off if luminance is below setpoint
6176 1 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6177 : }
6178 : } // for (IWin)
6179 : } // for (igroup)
6180 :
6181 : // Calculate glare index at each reference point assuming the daylight illuminance setpoint is
6182 : // met at both reference points, either by daylight or electric lights
6183 2323 : for (int IL = 1; IL <= NREFPT; ++IL) {
6184 1369 : auto &refPt = thisDayltgCtrl.refPts(IL);
6185 1369 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, refPt.lums[iLum_Back]);
6186 : // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,1,loop) for unshaded windows, and
6187 : // ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded windows
6188 1369 : refPt.glareIndex = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6189 : }
6190 :
6191 : // Check if glare level is less than maximum allowed at each ref pt. If maximum
6192 : // is exceeded at either ref pt, attempt to reduce glare to acceptable level by closing
6193 : // shading device on windows that have shades that have not already been closed.
6194 954 : GlareFlag = false;
6195 1579 : for (auto const &refPt : thisDayltgCtrl.refPts) {
6196 997 : if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) {
6197 372 : GlareFlag = true;
6198 372 : break;
6199 : }
6200 : }
6201 :
6202 954 : if (GlareFlag) {
6203 : bool blnCycle;
6204 : bool GlareOK;
6205 : Real64 tmpMult;
6206 : // Glare is too high at a ref pt. Loop through windows.
6207 372 : count = 0;
6208 :
6209 372 : continueOuterLoop = false;
6210 372 : for (std::size_t igroup = 1; igroup <= thisDayltgCtrl.ShadeDeployOrderExtWins.size(); igroup++) {
6211 0 : auto &shadeGroupLums = shadeGroupsLums(igroup);
6212 0 : std::vector<int> const &listOfExtWin = thisDayltgCtrl.ShadeDeployOrderExtWins[igroup - 1];
6213 :
6214 0 : int countBeforeListOfExtWinLoop = count;
6215 0 : bool atLeastOneGlareControlIsActive = false;
6216 :
6217 0 : for (const int IWin : listOfExtWin) {
6218 0 : ++count;
6219 : // need to map back to the original order of the "loop" to not change all the other data structures
6220 0 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6221 0 : if (loop == 0) continue;
6222 :
6223 0 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
6224 : // Check if window is eligible for glare control
6225 : // TH 1/21/2010. Switchable glazings already in partially switched state
6226 : // should be allowed to further dim to control glare
6227 : // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
6228 0 : if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
6229 0 : ANY_BLIND(s_surf->SurfWinShadingFlag(IWin))) {
6230 0 : continueOuterLoop = false;
6231 0 : continue;
6232 : }
6233 0 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6234 0 : if (!s_surf->Surface(IWin).HasShadeControl) {
6235 0 : continueOuterLoop = false;
6236 0 : continue;
6237 : }
6238 0 : if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
6239 0 : atLeastOneGlareControlIsActive = true;
6240 :
6241 : // Illuminance (WDAYIL) and background luminance (WBACLU) contribution from this
6242 : // window without shading (IS=1) and with shading (IS=2) for each ref pt
6243 : // For switchable windows, this may be partially switched rather than fully dark
6244 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6245 0 : auto const &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6246 0 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
6247 0 : shadeGroupLums.WDAYIL(IL)[iLum_Illum][iWinCover] = daylFromWinAtRefPt[iLum_Illum][iWinCover];
6248 0 : shadeGroupLums.WDAYIL(IL)[iLum_Back][iWinCover] = daylFromWinAtRefPt[iLum_Back][iWinCover];
6249 : }
6250 : }
6251 :
6252 : // Recalculate illuminance and glare with shading on this window.
6253 : // For switchable glazings, this is the fully switched (dark) state
6254 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6255 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6256 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6257 0 : auto const &refPt = thisDayltgCtrl.refPts(IL);
6258 :
6259 0 : if (s_surf->SurfWinShadingFlag(IWin) != WinShadingType::SwitchableGlazing) {
6260 : // for non switchable glazings or switchable glazings not switched yet (still in clear state)
6261 : // SurfaceWindow(IWin)%ShadingFlag = WinShadingFlag::GlassConditionallyLightened
6262 0 : rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Bare] + wdayil[iLum_Illum][iWinCover_Shaded];
6263 0 : rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Bare] + wdayil[iLum_Back][iWinCover_Shaded];
6264 : } else {
6265 : // switchable glazings already in partially switched state when calc the RDAYIL(IL) & RBACLU(IL)
6266 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(loop, IL);
6267 0 : rdayil[iLum_Illum] = dl->DaylIllum(IL) - wdayil[iLum_Illum][iWinCover_Shaded] + tmpDayl[iLum_Illum][iWinCover_Shaded];
6268 0 : rdayil[iLum_Back] = refPt.lums[iLum_Back] - wdayil[iLum_Back][iWinCover_Shaded] + tmpDayl[iLum_Back][iWinCover_Shaded];
6269 : }
6270 : } // for (IL)
6271 :
6272 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened)
6273 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::SwitchableGlazing;
6274 0 : else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff)
6275 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntShade;
6276 0 : else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff)
6277 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtShade;
6278 0 : else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff)
6279 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::IntBlind;
6280 0 : else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff)
6281 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ExtBlind;
6282 0 : else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGShadeConditionallyOff)
6283 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGShade;
6284 0 : else if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::BGBlindConditionallyOff)
6285 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::BGBlind;
6286 :
6287 : // For switchable glazings, it is switched to fully dark state,
6288 : // update ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for use in DayltgGlare
6289 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6290 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6291 0 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6292 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6293 :
6294 0 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6295 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6296 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6297 : }
6298 :
6299 0 : int const IConst = s_surf->SurfActiveConstruction(IWin);
6300 : // Vis trans at normal incidence of unswitched glass
6301 0 : shadeGroupLums.unswitchedTvis =
6302 0 : Window::POLYF(1.0, state.dataConstruction->Construct(IConst).TransVisBeamCoef) * surfWin.glazedFrac;
6303 :
6304 : // Vis trans at normal incidence of fully switched glass
6305 0 : int const IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
6306 0 : shadeGroupLums.switchedTvis =
6307 0 : Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
6308 : } // if (switchableGlazing)
6309 : } // if (GlareControlIsActive)
6310 : } // for (IWin)
6311 0 : if (continueOuterLoop) continue;
6312 :
6313 0 : if (atLeastOneGlareControlIsActive) {
6314 :
6315 : // Re-calc daylight and glare at shaded state. For switchable glazings, it is the fully dark state.
6316 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6317 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, shadeGroupLums.RDAYIL(IL)[iLum_Back]);
6318 : // DayltgGlare uses ZoneDaylight(ZoneNum)%SourceLumFromWinAtRefPt(IL,2,loop) for shaded state
6319 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6320 : }
6321 :
6322 : // Check if the shading did not improve the glare conditions
6323 : //
6324 : // blnCycle when true resets the specific window to its non-shaded condition. A later comment says
6325 : // Shading this window has not improved the glare situation.
6326 : // Reset shading flag to no shading condition, go to next window.
6327 : //
6328 : // 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
6329 : // reset it. For each reference point, if the original glare was too high but ok at other reference points and the glare gets
6330 : // lower at the reference and stays ok at the other reference points it is good, don't reset it.
6331 : //
6332 : // The old comments when there were only two reference points were:
6333 : // One ref pt; go to next window if glare has increased.
6334 : // Two ref pts. There are three cases depending on glare values.
6335 : // (1) Initial glare too high at both ref pts. Deploy shading on
6336 : // this window if this decreases glare at both ref pts.
6337 : // (2) Initial glare too high only at first ref pt. Deploy shading
6338 : // on this window if glare at first ref pt decreases and
6339 : // glare at second ref pt stays below max.
6340 : // (3) Initial glare too high at second ref pt. Deploy shading if glare
6341 : // at second ref pt decreases and glare at first ref pt stays below max.
6342 : //
6343 : // The approach taken is just to count the number of reference points that fulfill the individual requirements and see if it
6344 : // covers all the reference points.
6345 0 : int numRefPtOldAboveMaxNewBelowOld = 0;
6346 0 : int numRefPtOldBelowMaxNewBelowMax = 0;
6347 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6348 0 : auto const &refPt = thisDayltgCtrl.refPts(IL);
6349 :
6350 0 : if (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= refPt.glareIndex)
6351 0 : ++numRefPtOldAboveMaxNewBelowOld;
6352 0 : else if (refPt.glareIndex <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(IL) <= thisDayltgCtrl.MaxGlareallowed)
6353 0 : ++numRefPtOldBelowMaxNewBelowMax;
6354 : }
6355 0 : blnCycle = true;
6356 0 : if ((numRefPtOldAboveMaxNewBelowOld + numRefPtOldBelowMaxNewBelowMax) == NREFPT) blnCycle = false;
6357 : }
6358 :
6359 : // restore the count to the value prior to the last loop through the group of exterior windows
6360 0 : count = countBeforeListOfExtWinLoop;
6361 0 : breakOuterLoop = false;
6362 :
6363 0 : for (const int IWin : listOfExtWin) {
6364 0 : ++count;
6365 : // need to map back to the original order of the "loop" to not change all the other data structures
6366 0 : int loop = thisDayltgCtrl.MapShdOrdToLoopNum(count);
6367 0 : if (loop == 0) continue;
6368 :
6369 : // if (SurfWinShadingFlag(IWin) <= BGBlind && SurfWinShadingFlag(IWin) != SwitchableGlazing) {
6370 0 : if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) || ANY_SHADE_SCREEN(s_surf->SurfWinShadingFlag(IWin)) ||
6371 0 : ANY_BLIND(s_surf->SurfWinShadingFlag(IWin)))
6372 0 : continue;
6373 :
6374 0 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
6375 0 : if (!s_surf->Surface(IWin).HasShadeControl) continue;
6376 0 : if (s_surf->WindowShadingControl(ICtrl).GlareControlIsActive) {
6377 :
6378 : // Shading this window has not improved the glare situation.
6379 : // Reset shading flag to no shading condition, go to next window.
6380 0 : if (blnCycle) {
6381 : // for switchable glazings, reset properties to clear state or partial switched state?
6382 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6383 0 : s_surf->SurfWinSwitchingFactor(IWin) = 0.0;
6384 0 : s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.unswitchedTvis;
6385 :
6386 : // RESET properties for fully dark state
6387 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6388 0 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6389 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6390 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6391 0 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6392 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6393 : }
6394 : }
6395 :
6396 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6397 0 : continue;
6398 0 : }
6399 :
6400 : // Shading this window has improved the glare situation.
6401 : // Reset background luminance, glare index, and daylight illuminance at each ref pt.
6402 : // For switchable glazings, this is fully switched, dark state
6403 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6404 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6405 0 : refPt.lums[iLum_Back] = shadeGroupLums.RDAYIL(IL)[iLum_Back];
6406 0 : refPt.glareIndex = GLRNEW(IL);
6407 0 : dl->DaylIllum(IL) = shadeGroupLums.RDAYIL(IL)[iLum_Illum];
6408 : }
6409 :
6410 : // TH comments (5/22/2009): seems for EC windows, if the calculated glare exceeds the max setpoint,
6411 : // the EC windows will be reset to fully dark state which significantly reduces the available daylight.
6412 : // A better way is to dim the EC windows as necessary just to meet the glare index, which will still
6413 : // provide more daylight while not exceeding the max glare! The question is then how to set the
6414 : // SwitchingFactor to just meet the glare index.
6415 : // This was addressed in CR 7984 for E+ 5.0. 1/19/2010
6416 :
6417 : // If switchable glazing, set switching factor to 1: fully switched.
6418 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
6419 : // tmpSWFactor0 = SurfaceWindow( IWin ).SwitchingFactor; // save original
6420 : // switching factor
6421 : ////Unused Set but never used
6422 0 : s_surf->SurfWinSwitchingFactor(IWin) = 1.0;
6423 0 : s_surf->SurfWinVisTransSelected(IWin) = shadeGroupLums.switchedTvis;
6424 :
6425 : // restore fully dark values
6426 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6427 0 : auto &daylFromWinAtRefPt = thisDayltgCtrl.refPts(IL).extWins(loop).lums;
6428 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6429 0 : auto &wdayil = shadeGroupLums.WDAYIL(IL);
6430 0 : wdayil[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6431 0 : wdayil[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6432 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded];
6433 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded];
6434 0 : daylFromWinAtRefPt[iLum_Source][iWinCover_Shaded] = tmpDayl[iLum_Source][iWinCover_Shaded];
6435 : }
6436 : }
6437 :
6438 : // Check if glare now acceptable at each ref pt.
6439 0 : GlareOK = false;
6440 0 : if (NREFPT == 1) {
6441 0 : if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
6442 0 : } else if (NREFPT > 1) {
6443 0 : if (thisDayltgCtrl.refPts(1).glareIndex <= thisDayltgCtrl.MaxGlareallowed &&
6444 0 : thisDayltgCtrl.refPts(2).glareIndex <= thisDayltgCtrl.MaxGlareallowed)
6445 0 : GlareOK = true;
6446 : }
6447 :
6448 0 : if (GlareOK) {
6449 0 : if (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing &&
6450 0 : s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp) {
6451 : // Added TH 1/14/2010
6452 : // Only for switchable glazings with MeetDaylightIlluminanceSetpoint control
6453 : // The glazing is in fully dark state, it might lighten a bit to provide more daylight
6454 : // while meeting maximum discomfort glare index
6455 : // Iteration to find the right switching factor meeting the glare index
6456 :
6457 : // get fully dark state values
6458 0 : Real64 tmpSWSL1 = tmpDaylFromWinAtRefPt(1, loop)[iLum_Source][iWinCover_Shaded];
6459 0 : Real64 tmpSWSL2 = (NREFPT > 1) ? tmpDaylFromWinAtRefPt(2, loop)[iLum_Source][iWinCover_Shaded] : 0.0;
6460 :
6461 : // use simple fixed step search in iteraction, can be improved in future
6462 0 : Real64 tmpSWFactor = 1.0 - tmpSWIterStep;
6463 0 : while (tmpSWFactor > 0) {
6464 : // calc new glare at new switching state
6465 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6466 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6467 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6468 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6469 0 : rdayil[iLum_Illum] =
6470 0 : dl->DaylIllum(IL) +
6471 0 : (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6472 0 : rdayil[iLum_Back] =
6473 0 : refPt.lums[iLum_Back] +
6474 0 : (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6475 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
6476 : // needs to update SourceLumFromWinAtRefPt(IL,2,loop) before re-calc DayltgGlare
6477 0 : tmpMult = (shadeGroupLums.unswitchedTvis -
6478 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6479 0 : shadeGroupLums.switchedTvis;
6480 0 : refPt.extWins(loop).lums[iLum_Source][iWinCover_Shaded] = ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
6481 : // Calc new glare
6482 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6483 : } // for (IL)
6484 :
6485 : // Check whether new glare is OK
6486 0 : GlareOK = false;
6487 0 : if (NREFPT == 1) {
6488 0 : if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
6489 0 : } else if (NREFPT > 1) {
6490 0 : if (GLRNEW(1) <= thisDayltgCtrl.MaxGlareallowed && GLRNEW(2) <= thisDayltgCtrl.MaxGlareallowed) GlareOK = true;
6491 : }
6492 :
6493 0 : if (GlareOK) {
6494 0 : if (tmpSWFactor >= tmpSWIterStep) {
6495 : // Continue to lighten the glazing
6496 0 : tmpSWFactor -= tmpSWIterStep;
6497 0 : continue;
6498 : } else {
6499 : // Glare still OK but glazing already in clear state, no more lighten
6500 0 : breakOuterLoop = true;
6501 0 : break;
6502 : }
6503 : } else {
6504 : // Glare too high, exit and use previous switching state
6505 0 : tmpSWFactor += tmpSWIterStep;
6506 0 : breakOuterLoop = true;
6507 0 : break;
6508 : }
6509 : } // if (tmpSWFactor > 0)
6510 :
6511 : // Final re-calculation if needed
6512 0 : if (!GlareOK) {
6513 : // Glare too high, use previous state and re-calc
6514 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6515 0 : auto &rdayil = shadeGroupLums.RDAYIL(IL);
6516 0 : auto const &wdayil = shadeGroupLums.WDAYIL(IL);
6517 0 : rdayil[iLum_Illum] =
6518 0 : dl->DaylIllum(IL) +
6519 0 : (wdayil[iLum_Illum][iWinCover_Bare] - wdayil[iLum_Illum][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6520 0 : rdayil[iLum_Back] =
6521 0 : thisDayltgCtrl.refPts(IL).lums[iLum_Back] +
6522 0 : (wdayil[iLum_Back][iWinCover_Bare] - wdayil[iLum_Back][iWinCover_Shaded]) * (1.0 - tmpSWFactor);
6523 0 : BACL = max(SetPnt(IL) * dl->enclDaylight(enclNum).aveVisDiffReflect / Constant::Pi, rdayil[iLum_Back]);
6524 :
6525 : // needs to update SourceLumFromWinAtRefPt(IL,2,IWin) before re-calc DayltgGlare
6526 0 : tmpMult = (shadeGroupLums.unswitchedTvis -
6527 0 : (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6528 0 : shadeGroupLums.switchedTvis;
6529 0 : thisDayltgCtrl.refPts(1).extWins(loop).lums[iLum_Source][iWinCover_Shaded] =
6530 0 : ((IL == 1) ? tmpSWSL1 : tmpSWSL2) * tmpMult;
6531 0 : GLRNEW(IL) = DayltgGlare(state, IL, BACL, daylightCtrlNum);
6532 : }
6533 : }
6534 :
6535 : // Update final results
6536 0 : for (int IL = 1; IL <= NREFPT; ++IL) {
6537 0 : auto &refPt = thisDayltgCtrl.refPts(IL);
6538 0 : auto const &rdayil = shadeGroupLums.RDAYIL(IL);
6539 0 : refPt.lums[iLum_Back] = rdayil[iLum_Back];
6540 0 : refPt.glareIndex = GLRNEW(IL);
6541 0 : dl->DaylIllum(IL) = rdayil[iLum_Illum];
6542 :
6543 0 : tmpMult =
6544 0 : (shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor) /
6545 0 : shadeGroupLums.switchedTvis;
6546 : // update report variables
6547 0 : auto &daylFromWinAtRefPt = refPt.extWins(loop).lums;
6548 0 : auto const &tmpDayl = tmpDaylFromWinAtRefPt(IL, loop);
6549 0 : daylFromWinAtRefPt[iLum_Illum][iWinCover_Shaded] = tmpDayl[iLum_Illum][iWinCover_Shaded] * tmpMult;
6550 0 : daylFromWinAtRefPt[iLum_Back][iWinCover_Shaded] = tmpDayl[iLum_Back][iWinCover_Shaded] * tmpMult;
6551 : }
6552 0 : s_surf->SurfWinSwitchingFactor(IWin) = tmpSWFactor;
6553 0 : s_surf->SurfWinVisTransSelected(IWin) =
6554 0 : shadeGroupLums.unswitchedTvis - (shadeGroupLums.unswitchedTvis - shadeGroupLums.switchedTvis) * tmpSWFactor;
6555 :
6556 : } else {
6557 : // For un-switchable glazing or switchable glazing but not MeetDaylightIlluminaceSetpoint control,
6558 : // it is in shaded state and glare is ok - job is done, exit the window loop - IWin
6559 0 : breakOuterLoop = true;
6560 0 : break;
6561 : }
6562 : } // if (glareOK)
6563 : } // if (glareControlIsActive)
6564 : } // for (IWin)
6565 0 : if (breakOuterLoop) break;
6566 : } // for (igroup)
6567 : } // if (GlareFlag)
6568 :
6569 : // Loop again over windows and reset remaining shading flags that
6570 : // are 10 or higher (i.e., conditionally off) to off
6571 1908 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6572 954 : auto const &thisSpace = state.dataHeatBal->space(spaceNum);
6573 1908 : for (int IWin = thisSpace.WindowSurfaceFirst; IWin <= thisSpace.WindowSurfaceLast; ++IWin) {
6574 954 : if (s_surf->Surface(IWin).ExtBoundCond != ExternalEnvironment) continue;
6575 954 : bool anyGlareControl = (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntShadeConditionallyOff) ||
6576 954 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::GlassConditionallyLightened) ||
6577 954 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtShadeConditionallyOff) ||
6578 2862 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::IntBlindConditionallyOff) ||
6579 954 : (s_surf->SurfWinShadingFlag(IWin) == WinShadingType::ExtBlindConditionallyOff);
6580 954 : if (anyGlareControl) {
6581 0 : s_surf->SurfWinShadingFlag(IWin) = WinShadingType::ShadeOff;
6582 : }
6583 : }
6584 : }
6585 :
6586 : // Variables for reporting
6587 2323 : for (int IL = 1; IL <= NREFPT; ++IL) {
6588 1369 : auto &refPt = thisDayltgCtrl.refPts(IL);
6589 1369 : refPt.lums[iLum_Illum] = dl->DaylIllum(IL);
6590 :
6591 : // added TH 12/2/2008
6592 1369 : refPt.timeExceedingGlareIndexSetPoint = (refPt.glareIndex > thisDayltgCtrl.MaxGlareallowed) ? state.dataGlobal->TimeStepZone : 0.0;
6593 : // added TH 7/6/2009
6594 1369 : refPt.timeExceedingDaylightIlluminanceSetPoint = (dl->DaylIllum(IL) > refPt.illumSetPoint) ? state.dataGlobal->TimeStepZone : 0.0;
6595 : }
6596 954 : } // DayltgInteriorIllum()
6597 :
6598 0 : void DayltgInteriorTDDIllum(EnergyPlusData &state)
6599 : {
6600 :
6601 : // SUBROUTINE INFORMATION:
6602 : // AUTHOR Linda Lawrie
6603 : // DATE WRITTEN October 2006
6604 :
6605 : // PURPOSE OF THIS SUBROUTINE:
6606 : // Calculate the TDD Pipe illuminance values
6607 0 : auto &dl = state.dataDayltg;
6608 :
6609 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
6610 : int iSky1; // Sky type index values for averaging two sky types
6611 : int iSky2;
6612 : Real64 SkyWeight; // Weighting factor used to average two different sky types
6613 :
6614 0 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
6615 0 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
6616 0 : iSky1 = (int)SkyType::Clear;
6617 0 : iSky2 = (int)SkyType::ClearTurbid;
6618 0 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
6619 0 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
6620 0 : iSky1 = (int)SkyType::ClearTurbid;
6621 0 : iSky2 = (int)SkyType::Intermediate;
6622 : } else { // Sky is average of intermediate and overcast
6623 0 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
6624 0 : iSky1 = (int)SkyType::Intermediate;
6625 0 : iSky2 = (int)SkyType::Overcast;
6626 : }
6627 :
6628 : // Calculate and report TDD visible transmittances
6629 0 : for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
6630 :
6631 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam =
6632 0 : state.dataGlobal->WeightNow * dl->TDDTransVisBeam(state.dataGlobal->HourOfDay, PipeNum) +
6633 0 : state.dataGlobal->WeightPreviousHour * dl->TDDTransVisBeam(state.dataGlobal->PreviousHour, PipeNum);
6634 :
6635 0 : auto const &tddFluxIncCurr = dl->TDDFluxInc(state.dataGlobal->HourOfDay, PipeNum);
6636 0 : auto const &tddFluxIncPrev = dl->TDDFluxInc(state.dataGlobal->PreviousHour, PipeNum);
6637 :
6638 0 : auto const &tddFluxTransCurr = dl->TDDFluxTrans(state.dataGlobal->HourOfDay, PipeNum);
6639 0 : auto const &tddFluxTransPrev = dl->TDDFluxTrans(state.dataGlobal->PreviousHour, PipeNum);
6640 :
6641 0 : Illums TDDTransVisDiff;
6642 0 : for (int iSky = iSky1; iSky <= iSky2; ++iSky) {
6643 0 : Real64 tddTransVisDiffCurr = (tddFluxIncCurr.sky[iSky] > 0.0) ? (tddFluxTransCurr.sky[iSky] / tddFluxIncCurr.sky[iSky]) : 0.0;
6644 0 : Real64 tddTransVisDiffPrev = (tddFluxIncPrev.sky[iSky] > 0.0) ? (tddFluxTransPrev.sky[iSky] / tddFluxIncPrev.sky[iSky]) : 0.0;
6645 :
6646 0 : TDDTransVisDiff.sky[iSky] =
6647 0 : state.dataGlobal->WeightNow * tddTransVisDiffCurr + state.dataGlobal->WeightPreviousHour * tddTransVisDiffPrev;
6648 : } // for (iSky)
6649 :
6650 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff =
6651 0 : SkyWeight * TDDTransVisDiff.sky[iSky1] + (1.0 - SkyWeight) * TDDTransVisDiff.sky[iSky2];
6652 : } // for (PipeNum)
6653 0 : } // DayltgInteriorTDDIllum()
6654 :
6655 71219 : void DayltgElecLightingControl(EnergyPlusData &state)
6656 : {
6657 :
6658 : // SUBROUTINE INFORMATION:
6659 : // AUTHOR Fred Winkelmann
6660 : // DATE WRITTEN July 1997
6661 : // MODIFIED Mar 2004, FCW: add inter-reflected illuminance from interior windows to DaylIllum
6662 : // Apr 2004, FCW: move CALL ReportIllumMap from DayltgInteriorIllum2 (DayltgInteriorMapIllum)
6663 : // Apr 2010, BG NREL: remove inter-reflected illuminance to stop double counting
6664 : // Aug 2012, BG NREL: added availability schedule logic
6665 :
6666 : // PURPOSE OF THIS SUBROUTINE:
6667 : // For a daylit space, determines lighting power reduction factor due to
6668 : // daylighting for different lighting control systems.
6669 :
6670 : // Called by InitSurfaceHeatBalance.
6671 :
6672 : // REFERENCES:
6673 : // Based on DOE-2.1E subroutine DLTSYS.
6674 71219 : auto &dl = state.dataDayltg;
6675 :
6676 71219 : if (dl->daylightControl.empty()) {
6677 70270 : return;
6678 : }
6679 : // Reset space power reduction factors
6680 1898 : for (int spaceNum = 1; spaceNum <= state.dataGlobal->numSpaces; ++spaceNum) {
6681 949 : dl->spacePowerReductionFactor(spaceNum) = 1.0;
6682 : }
6683 :
6684 1898 : for (auto &thisDayltgCtrl : dl->daylightControl) {
6685 :
6686 949 : if (thisDayltgCtrl.DaylightMethod != DaylightingMethod::SplitFlux) {
6687 : // Set space power reduction factors
6688 0 : if (thisDayltgCtrl.PowerReductionFactor < 1.0) {
6689 0 : if (thisDayltgCtrl.spaceIndex > 0) {
6690 : // This is a space-level daylighting control
6691 0 : dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = thisDayltgCtrl.PowerReductionFactor;
6692 : } else {
6693 : // This is a zone-level daylighting control
6694 0 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6695 0 : dl->spacePowerReductionFactor(spaceNum) = thisDayltgCtrl.PowerReductionFactor;
6696 : }
6697 : }
6698 : }
6699 0 : continue;
6700 0 : }
6701 :
6702 : // Electric lighting power reduction factor for a given daylighting control
6703 949 : Real64 &TotReduction = thisDayltgCtrl.PowerReductionFactor;
6704 949 : TotReduction = 0.0;
6705 949 : Real64 ZFTOT = 0.0;
6706 :
6707 : // check if scheduled to be available
6708 949 : if (thisDayltgCtrl.availSched->getCurrentVal() > 0.0) {
6709 :
6710 : // Loop over reference points
6711 2305 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
6712 1356 : auto &refPt = thisDayltgCtrl.refPts(IL);
6713 : // Total fraction of zone that is daylit
6714 1356 : ZFTOT += refPt.fracZoneDaylit;
6715 :
6716 1356 : dl->DaylIllum(IL) = refPt.lums[iLum_Illum];
6717 1356 : Real64 FL = 0.0;
6718 1356 : if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
6719 276 : FL = (refPt.illumSetPoint - dl->DaylIllum(IL)) / refPt.illumSetPoint;
6720 : }
6721 :
6722 : // BRANCH ON LIGHTING SYSTEM TYPE
6723 1356 : LtgCtrlType LSYSTP = thisDayltgCtrl.LightControlType;
6724 1356 : Real64 FP = 0.0;
6725 1356 : if (LSYSTP != LtgCtrlType::Stepped) {
6726 : // Continuously dimmable system with linear power curve
6727 : // Fractional output power required to meet setpoint
6728 1350 : FP = 1.0;
6729 : // LIGHT-CTRL-TYPE = CONTINUOUS (LSYSTP = 1)
6730 1350 : if (FL <= thisDayltgCtrl.MinLightFraction) {
6731 1138 : FP = thisDayltgCtrl.MinPowerFraction;
6732 : }
6733 : // LIGHT-CTRL-TYPE = CONTINUOUS/OFF (LSYSTP = 3)
6734 1350 : if (FL <= thisDayltgCtrl.MinLightFraction && LSYSTP == LtgCtrlType::ContinuousOff) {
6735 0 : FP = 0.0;
6736 : }
6737 1350 : if (FL > thisDayltgCtrl.MinLightFraction && FL < 1.0) {
6738 212 : FP = (FL + (1.0 - FL) * thisDayltgCtrl.MinPowerFraction - thisDayltgCtrl.MinLightFraction) /
6739 212 : (1.0 - thisDayltgCtrl.MinLightFraction);
6740 : }
6741 :
6742 : } else { // LSYSTP = 2
6743 : // Stepped system
6744 6 : FP = 0.0;
6745 : // #9060: Use a tolerance, otherwise at very low (< 1e-12) daylighting conditions, you can get a multiplier > 1.0
6746 6 : if (dl->DaylIllum(IL) < 0.1) {
6747 2 : FP = 1.0;
6748 4 : } else if (dl->DaylIllum(IL) < refPt.illumSetPoint) {
6749 4 : FP = double(int(thisDayltgCtrl.LightControlSteps * FL) + 1) / double(thisDayltgCtrl.LightControlSteps);
6750 : }
6751 :
6752 6 : if (thisDayltgCtrl.LightControlProbability < 1.0) {
6753 : // Manual operation. Occupant sets lights one level too high a fraction of the time equal to
6754 : // 1. - ZoneDaylight(ZoneNum)%LightControlProbability. RANDOM_NUMBER returns a random number
6755 : // between 0 and 1.
6756 : Real64 XRAN;
6757 2 : RANDOM_NUMBER(XRAN);
6758 2 : if (XRAN >= thisDayltgCtrl.LightControlProbability) {
6759 : // Set level one higher
6760 2 : if (FP < 1.0) {
6761 1 : FP += (1.0 / double(thisDayltgCtrl.LightControlSteps));
6762 : }
6763 : } // XRAN
6764 : } // Light Control Probability < 1
6765 : } // Lighting System Type
6766 :
6767 1356 : refPt.powerReductionFactor = FP;
6768 :
6769 : // Accumulate net ltg power reduction factor for entire zone
6770 1356 : TotReduction += refPt.powerReductionFactor * refPt.fracZoneDaylit;
6771 :
6772 : } // End of loop over reference points, IL
6773 :
6774 : // Correct for fraction of zone (1-ZFTOT) not controlled by
6775 : // the reference points. For this fraction (which is usually zero),
6776 : // the electric lighting is unaffected and the power reduction
6777 : // factor is therefore 1.0.
6778 949 : TotReduction += (1.0 - ZFTOT);
6779 : } else { // controls not currently available
6780 0 : TotReduction = 1.0;
6781 : }
6782 :
6783 : // Set space power reduction factors
6784 949 : if (thisDayltgCtrl.spaceIndex > 0) {
6785 : // This is a space-level daylighting control
6786 0 : dl->spacePowerReductionFactor(thisDayltgCtrl.spaceIndex) = TotReduction;
6787 : } else {
6788 : // This is a zone-level daylighting control
6789 1898 : for (int spaceNum : state.dataHeatBal->Zone(thisDayltgCtrl.zoneIndex).spaceIndexes) {
6790 949 : dl->spacePowerReductionFactor(spaceNum) = TotReduction;
6791 : }
6792 : }
6793 : } // end daylighting control loop
6794 :
6795 : // IF(TotIllumMaps > 0 .and. .not. DoingSizing .and. .not. WarmupFlag .and. .not. KickoffSimulation) THEN
6796 949 : if ((int)dl->illumMaps.size() > 0 && !state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
6797 118 : for (int mapNum = 1; mapNum <= (int)dl->illumMaps.size(); ++mapNum) {
6798 59 : auto &illumMap = dl->illumMaps(mapNum);
6799 59 : if (state.dataGlobal->TimeStep == 1) dl->mapResultsToReport = false;
6800 5959 : for (auto &refPt : illumMap.refPts) {
6801 5900 : refPt.lumsHr[iLum_Illum] += refPt.lums[iLum_Illum] / double(state.dataGlobal->TimeStepsInHour);
6802 5900 : if (refPt.lumsHr[iLum_Illum] > 0.0) {
6803 5900 : dl->mapResultsToReport = true;
6804 5900 : dl->mapResultsReported = true;
6805 : }
6806 : }
6807 59 : ReportIllumMap(state, mapNum);
6808 59 : if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) {
6809 1515 : for (auto &refPt : illumMap.refPts) {
6810 1500 : refPt.lumsHr[iLum_Illum] = refPt.lums[iLum_Illum] = 0.0;
6811 : }
6812 : }
6813 : } // for (mapNum)
6814 : } // if (MapSize > 0)
6815 : } // DayltgElecLightingControl()
6816 :
6817 459 : Real64 DayltgGlarePositionFactor(Real64 X, // Lateral and vertical distance of luminous window element from
6818 : Real64 Y)
6819 : {
6820 :
6821 : // SUBROUTINE INFORMATION:
6822 : // AUTHOR Fred Winkelmann
6823 : // DATE WRITTEN July 1997
6824 :
6825 : // PURPOSE OF THIS SUBROUTINE:
6826 : // by table interpolation, evaluates the
6827 : // Hopkinson position factor used in glare calculation
6828 : // (Hopkinson, Petherbridge, AND Longmore -- Daylighting,
6829 : // London, 1966, PP 307, 323). X (Y) is the lateral
6830 : // (vertical) distance of luminous window element from
6831 : // horizontal line of vision, divided by horizontal distance
6832 : // from eye of observer. The array PF contains values of
6833 : // the position factor for X = 0, 0.5, 1.0, 1.5, 2.0, 2.5,
6834 : // and 3.0 and Y = 0, 0.5, 1.0, 1.5, 2.0. Called by CalcDayltgCoefficients.
6835 :
6836 : // REFERENCES:
6837 : // Based on DOE-2.1E subroutine DPFAC.
6838 :
6839 : // Position factor array
6840 : static constexpr std::array<std::array<Real64, 7>, 5> PF = {{
6841 : {1.00, 0.492, 0.226, 0.128, 0.081, 0.061, 0.057},
6842 : {0.123, 0.119, 0.065, 0.043, 0.029, 0.026, 0.023},
6843 : {0.019, 0.026, 0.019, 0.016, 0.014, 0.011, 0.011},
6844 : {0.008, 0.008, 0.008, 0.008, 0.008, 0.006, 0.006},
6845 : {0.0, 0.0, 0.003, 0.003, 0.003, 0.003, 0.003},
6846 : }};
6847 :
6848 459 : if (X < 0.0 || X >= 3.0) return 0.0;
6849 451 : if (Y < 0.0 || Y >= 2.0) return 0.0;
6850 :
6851 451 : int IX = 1 + int(2.0 * X);
6852 451 : int IY = 1 + int(2.0 * Y);
6853 451 : Real64 X1 = 0.5 * double(IX - 1);
6854 451 : Real64 Y1 = 0.5 * double(IY - 1);
6855 451 : Real64 FA = PF[IY - 1][IX - 1] + 2.0 * (X - X1) * (PF[IY - 1][IX] - PF[IY - 1][IX - 1]);
6856 451 : Real64 FB = PF[IY][IX - 1] + 2.0 * (X - X1) * (PF[IY][IX] - PF[IY][IX - 1]);
6857 451 : return FA + 2.0 * (Y - Y1) * (FB - FA);
6858 : } // DayltgGlarePositionFactor()
6859 :
6860 2066 : void DayltgInterReflectedIllum(EnergyPlusData &state,
6861 : int const ISunPos, // Sun position counter; used to avoid calculating various
6862 : int const IHR, // Hour of day
6863 : int const enclNum, // Daylighting enclosure index
6864 : int const IWin // Window index
6865 : )
6866 : {
6867 :
6868 : // SUBROUTINE INFORMATION:
6869 : // AUTHOR Fred Winkelmann
6870 : // DATE WRITTEN July 1997
6871 : // MODIFIED FCW December 1998
6872 : // FCW June 2001: Add blind calculations
6873 : // FCW Jan 2001: Add blinds with movable slats
6874 : // FCW Jan 2003: Add between-glass blinds
6875 : // FCW Jul 2003: account for transmittance of shading surfaces
6876 : // (previously these were assumed opaque even if transmittance schedule
6877 : // value was non-zero)
6878 : // FCW Aug 2003: modify initialization of WinLum from WinLum = 0. TO
6879 : // WinLum(:,:,IHR) = 0. Otherwise values calculated in previous
6880 : // call are incorrectly zeroed. Result was that window luminance with
6881 : // shade or blind included only contribution from first window element
6882 : // in window element loop in CalcDayltgCoefficients, thus seriously
6883 : // undercalculating window luminance for windows with more than one
6884 : // window element. Similarly, modified initialization of WLUMSU from
6885 : // WLUMSU = 0. to WLUMSU(:,IHR) = 0., and of WLUMSUdisk from
6886 : // WLUMSUdisk = 0. to WLUMSUdisk(:,IHR) = 0.
6887 : // PGE Aug 2003: Add daylighting shelves.
6888 : // FCW Nov 2003: Add beam solar and sky solar reflected from obstructions;
6889 : // add beam solar reflected from ground accounting for obstructions.
6890 : // FCW Nov 2003: increase NPHMAX from 9 to 10 to avoid rays with altitude angle = 0
6891 : // for vertical surfaces.
6892 : // FCW Nov 2003: fix the expression for min and max limits of azimuth; old expression
6893 : // broke down for window normals with negative altitude angle
6894 : // FCW Nov 2003: add specular reflection from exterior obstructions
6895 : // FCW Apr 2004: add light well efficiency multiplying window transmittance
6896 : // FCW Apr 2004: add diffusing glazing
6897 : // RAR (FSEC) May 2006: add exterior window screen
6898 : // B. Griffith NREL April 2010: CR7869 add adjacent zone area if window is not on this zone
6899 : // apply interior window transmission and blocking to beam transmission from ext win
6900 :
6901 : // PURPOSE OF THIS SUBROUTINE:
6902 : // Called from CalcDayltgCoefficients for each window and reference point in a daylit
6903 : // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
6904 : // to internally reflected light by integrating to determine the amount of flux from
6905 : // sky and ground (and beam reflected from obstructions) transmitted through
6906 : // the center of the window and then reflecting this
6907 : // light from the inside surfaces of the space. The "split-flux" method is used
6908 : // (Lynes, Principles of Natural Lighting, 1968). EINT is determined for
6909 : // different sky types and for window with and without shades, screens or blinds.
6910 : // Also finds luminance (WinLum and WLUMSU) of window with shade or blind, &
6911 : // or with diffusing glass, for different sky types.
6912 :
6913 : // REFERENCES:
6914 : // Based on DOE-2.1E subroutine DREFLT.
6915 :
6916 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
6917 : // In the following I,J arrays:
6918 : // I = sky type;
6919 : // J = 1 for bare window, 2 and above for window with shade or blind.
6920 2066 : Illums ZSK; // Sky-related and sun-related illuminance on window from sky/ground
6921 2066 : Vector3<Real64> U; // Unit vector in (PH,TH) direction
6922 2066 : Vector3<Real64> nearestHitPt; // Hit point of ray on nearest obstruction (m)
6923 2066 : Vector3<Real64> obsHitPt; // Coordinates of hit point on an obstruction (m)
6924 2066 : Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
6925 2066 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLCW = {Illums()}; // Sky-related upgoing luminous flux
6926 2066 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> FLFW = {Illums()}; // Sky-related downgoing luminous flux
6927 :
6928 : // 3=intermediate, 4=overcast
6929 : Real64 DPH; // Sky/ground element altitude and azimuth increments (radians)
6930 : Real64 DTH;
6931 : Real64 PH; // Sky/ground element altitude and azimuth (radians)
6932 : Real64 TH;
6933 : Real64 SPH; // Sine and cosine of PH
6934 : Real64 CPH;
6935 : Real64 PHMIN; // Limits of altitude integration (radians)
6936 : Real64 PHMAX;
6937 : Real64 ThMin; // Limits of azimuth integration (radians)
6938 : Real64 ThMax;
6939 : Real64 PhWin; // Altitude, azimuth angle of window normal (radians)
6940 : Real64 ThWin;
6941 : Real64 ACosTanTan; // ACOS(-TAN(Ph)*TAN(PhWin))
6942 : Real64 DA; // CPH*DTH*DPH
6943 : Real64 COSB; // Cosine of angle of incidence of light from sky or ground
6944 : Real64 TVISBR; // Transmittance of window without shading at COSB
6945 : // (times light well efficiency, if appropriate)
6946 : Real64 ZSU;
6947 : // element for clear and overcast sky
6948 : Real64 ObTrans; // Product of solar transmittances of obstructions seen by a light ray
6949 :
6950 : // unused REAL(r64) :: HitPointLumFrClearSky ! Luminance of obstruction from clear sky (cd/m2)
6951 : // unused REAL(r64) :: HitPointLumFrOvercSky ! Luminance of obstruction from overcast sky (cd/m2)
6952 : // unused REAL(r64) :: HitPointLumFrSun ! Luminance of obstruction from sun (cd/m2)
6953 : int ICtrl; // Window control pointer
6954 : Real64 COSBSun; // Cosine of angle of incidence of direct sun on window
6955 : Real64 TVISBSun; // Window's visible transmittance at COSBSun
6956 : // (times light well efficiency, if appropriate)
6957 : Real64 ZSU1; // Transmitted direct normal illuminance (lux)
6958 : // CHARACTER(len=32) :: ShType ! Window shading device type
6959 : bool ShadeOn; // True if exterior or interior window shade present
6960 : bool BlindOn; // True if exterior or interior window blind present
6961 : bool ScreenOn; // True if exterior window screen present
6962 : // int ScNum; // Screen number //Unused Set but never used
6963 : int PipeNum; // TDD pipe object number
6964 : int ShelfNum; // Daylighting shelf object number
6965 : int InShelfSurf; // Inside daylighting shelf surface number
6966 : int OutShelfSurf; // Outside daylighting shelf surface number
6967 : Real64 TransBlBmDiffFront; // Isolated blind vis beam-diffuse front transmittance
6968 : Real64 TransScBmDiffFront; // Isolated screen vis beam-diffuse front transmittance
6969 : Real64 ReflGlDiffDiffBack; // Bare glazing system vis diffuse back reflectance
6970 : Real64 ReflGlDiffDiffFront; // Bare glazing system vis diffuse front reflectance
6971 : Real64 ReflBlBmDiffFront; // Isolated blind vis beam-diffuse front reflectance
6972 : Real64 TransBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front transmittance
6973 : Real64 ReflBlDiffDiffFront; // Isolated blind vis diffuse-diffuse front reflectance
6974 : Real64 ReflBlDiffDiffBack; // Isolated blind vis diffuse-diffuse back reflectance
6975 : Real64 ReflScDiffDiffBack; // Isolated screen vis diffuse-diffuse back reflectance
6976 :
6977 : Real64 td2; // Diffuse-diffuse vis trans of bare glass layers 2 and 3
6978 : Real64 td3;
6979 : Real64 rbd1; // Beam-diffuse back vis reflectance of bare glass layers 1 and 2
6980 : Real64 rbd2;
6981 : Real64 rfd2; // Beam-diffuse front vis reflectance of bare glass layers 2 and 3
6982 : Real64 rfd3;
6983 : Real64 tfshd; // Diffuse-diffuse front vis trans of bare blind
6984 : Real64 rbshd; // Diffuse-diffuse back vis reflectance of bare blind
6985 : Real64 ZSUObsRefl; // Illuminance on window from beam solar reflected by an
6986 : // obstruction (for unit beam normal illuminance)
6987 : int NearestHitSurfNum; // Surface number of nearest obstruction
6988 : int NearestHitSurfNumX; // Surface number to use when obstruction is a shadowing surface
6989 : Real64 LumAtHitPtFrSun; // Luminance at hit point on obstruction from solar reflection
6990 : // for unit beam normal illuminance (cd/m2)
6991 : Real64 SunObstructionMult; // = 1 if sun hits a ground point; otherwise = 0
6992 : bool hitObs; // True iff obstruction is hit
6993 : Real64 ObsVisRefl; // Visible reflectance of obstruction
6994 : Real64 SkyReflVisLum; // Reflected sky luminance at hit point divided by unobstructed sky
6995 : // diffuse horizontal illuminance [(cd/m2)/lux]
6996 : Real64 dReflObsSky; // Contribution to sky-related illuminance on window due to sky diffuse
6997 : // reflection from an obstruction
6998 : Real64 TVisSunRefl; // Diffuse vis trans of bare window for beam reflection calc
6999 : // (times light well efficiency, if appropriate)
7000 : Real64 ZSU1refl; // Beam normal illuminance times ZSU1refl = illuminance on window
7001 : // due to specular reflection from exterior surfaces
7002 :
7003 : ExtWinType extWinType; // Exterior window type (InZoneExtWin, AdjZoneExtWin, NotInOrAdjZoneExtWin)
7004 : Real64 EnclInsideSurfArea; // temporary for calculations, total surface area of enclosure surfaces m2
7005 : int IntWinAdjZoneExtWinNum; // the index of the exterior window in IntWinAdjZoneExtWin nested struct
7006 : int IntWinNum; // window index for interior windows associated with exterior windows
7007 : Real64 COSBintWin;
7008 :
7009 : WinShadingType ShType;
7010 :
7011 2066 : auto &s_mat = state.dataMaterial;
7012 2066 : auto &dl = state.dataDayltg;
7013 2066 : auto &s_surf = state.dataSurface;
7014 :
7015 2066 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
7016 2066 : auto const &surf = s_surf->Surface(IWin);
7017 2066 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
7018 2066 : int const enclNumThisWin = s_surf->Surface(surf.BaseSurf).SolarEnclIndex;
7019 : // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea was calculated in subr DayltgAveInteriorReflectance
7020 :
7021 2066 : if (enclNumThisWin == enclNum) {
7022 2066 : extWinType = ExtWinType::InZone;
7023 2066 : EnclInsideSurfArea = dl->enclDaylight(enclNumThisWin).totInsSurfArea;
7024 2066 : IntWinAdjZoneExtWinNum = 0;
7025 : } else {
7026 0 : extWinType = ExtWinType::AdjZone;
7027 : // If window is exterior window in adjacent zone, then use areas of both enclosures
7028 0 : EnclInsideSurfArea = dl->enclDaylight(enclNum).totInsSurfArea + dl->enclDaylight(enclNumThisWin).totInsSurfArea;
7029 : // find index in IntWinAdjZoneExtWin
7030 0 : for (int AdjExtWinLoop = 1; AdjExtWinLoop <= thisEnclDaylight.NumOfIntWinAdjEnclExtWins; ++AdjExtWinLoop) {
7031 0 : if (IWin == thisEnclDaylight.IntWinAdjEnclExtWin(AdjExtWinLoop).SurfNum) { // found it
7032 0 : IntWinAdjZoneExtWinNum = AdjExtWinLoop;
7033 0 : break; // added TH 4/13/2010
7034 : }
7035 : }
7036 : }
7037 :
7038 : // Initialize window luminance and fluxes for split-flux calculation
7039 8264 : dl->winLum(IHR)[(int)iWinCover_Bare] = dl->winLum(IHR)[(int)iWinCover_Shaded] = Illums();
7040 : // dl->WLUMSU(IHR, _) = 0.0;
7041 : // dl->WLUMSUdisk(IHR, _) = 0.0;
7042 :
7043 2066 : int const IConst = s_surf->SurfActiveConstruction(IWin);
7044 2066 : auto const &construct = state.dataConstruction->Construct(IConst);
7045 :
7046 2066 : BlindOn = false;
7047 2066 : ShadeOn = false;
7048 2066 : ScreenOn = false;
7049 :
7050 2066 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7051 0 : PipeNum = s_surf->SurfWinTDDPipeNum(IWin);
7052 : }
7053 :
7054 2066 : ShelfNum = s_surf->SurfDaylightingShelfInd(IWin);
7055 2066 : if (ShelfNum > 0) {
7056 0 : InShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf; // Inside daylighting shelf present if > 0
7057 0 : OutShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf; // Outside daylighting shelf present if > 0
7058 : } else {
7059 2066 : InShelfSurf = 0;
7060 2066 : OutShelfSurf = 0;
7061 : }
7062 :
7063 : // Divide sky and ground into elements of altitude PH and
7064 : // azimuth TH, and add the contribution of light coming from each
7065 : // element to the transmitted flux at the center of the window
7066 : // Azimuth ranges over a maximum of 2 Pi radians.
7067 : // Altitude ranges over a maximum of Pi/2 radians between -Pi/2 < PH < +Pi/2, so that elements are not counted twice
7068 : // PH = 0 at the horizon; PH = Pi/2 at the zenith
7069 2066 : PHMIN = max(-Constant::PiOvr2, surfWin.phi - Constant::PiOvr2);
7070 2066 : PHMAX = min(Constant::PiOvr2, surfWin.phi + Constant::PiOvr2);
7071 2066 : DPH = (PHMAX - PHMIN) / double(NPHMAX);
7072 :
7073 : // Sky/ground element altitude integration
7074 2066 : Vector3<Real64> const SUNCOS_IHR(s_surf->SurfSunCosHourly(IHR));
7075 22726 : for (int IPH = 1; IPH <= NPHMAX; ++IPH) {
7076 20660 : PH = PHMIN + (double(IPH) - 0.5) * DPH;
7077 :
7078 20660 : SPH = std::sin(PH);
7079 20660 : CPH = std::cos(PH);
7080 : // Third component of unit vector in (TH,PH) direction
7081 20660 : U.z = SPH;
7082 :
7083 : // Limits of azimuth integration
7084 20660 : PhWin = surfWin.phi;
7085 20660 : ThWin = surfWin.theta;
7086 20660 : if (PhWin >= 0.0) {
7087 20660 : if (PH >= Constant::PiOvr2 - PhWin) {
7088 0 : ThMin = -Constant::Pi;
7089 0 : ThMax = Constant::Pi;
7090 : } else {
7091 20660 : ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
7092 20660 : ThMin = ThWin - std::abs(ACosTanTan);
7093 20660 : ThMax = ThWin + std::abs(ACosTanTan);
7094 : }
7095 :
7096 : } else { // PhiSurf < 0.0
7097 0 : if (PH <= -PhWin - Constant::PiOvr2) {
7098 0 : ThMin = -Constant::Pi;
7099 0 : ThMax = Constant::Pi;
7100 : } else {
7101 0 : ACosTanTan = std::acos(-std::tan(PH) * std::tan(PhWin));
7102 0 : ThMin = ThWin - std::abs(ACosTanTan);
7103 0 : ThMax = ThWin + std::abs(ACosTanTan);
7104 : }
7105 : }
7106 :
7107 20660 : DTH = (ThMax - ThMin) / double(NTHMAX);
7108 20660 : DA = CPH * DTH * DPH;
7109 :
7110 : // Sky/ground element azimuth integration
7111 20660 : Real64 const sin_window_phi(std::sin(surfWin.phi));
7112 20660 : Real64 const cos_window_phi(std::cos(surfWin.phi));
7113 351220 : for (int ITH = 1; ITH <= NTHMAX; ++ITH) {
7114 330560 : TH = ThMin + (double(ITH) - 0.5) * DTH;
7115 330560 : U.x = CPH * std::cos(TH);
7116 330560 : U.y = CPH * std::sin(TH);
7117 : // Cosine of angle of incidence of light from sky or ground element
7118 330560 : COSB = SPH * sin_window_phi + CPH * cos_window_phi * std::cos(TH - surfWin.theta);
7119 330560 : if (COSB < 0.0) continue; // Sky/ground elements behind window (although there shouldn't be any)
7120 :
7121 : // Initialize illuminance on window for this sky/ground element
7122 1322240 : ZSK = Illums();
7123 330560 : ZSU = 0.0;
7124 : // Initialize illuminance on window from beam solar reflection if ray hits an obstruction
7125 330560 : ZSUObsRefl = 0.0;
7126 :
7127 330560 : if (ISunPos == 1) { // Intersection calculation has to be done only for first sun position
7128 : // Determine net transmittance of obstructions that the ray hits. ObTrans will be 1.0
7129 : // if no obstructions are hit.
7130 18720 : ObTrans = DayltgHitObstruction(state, IHR, IWin, s_surf->SurfaceWindow(IWin).WinCenter, U);
7131 18720 : dl->ObTransM[IPH][ITH] = ObTrans;
7132 18720 : dl->SkyObstructionMult[IPH][ITH] = 1.0;
7133 : }
7134 :
7135 : // SKY AND GROUND RADIATION ON WINDOW
7136 :
7137 : // Contribution is from sky if PH > 0 (ray goes upward), and from ground if PH < 0 (ray goes downward)
7138 : // (There may also be contributions from reflection from obstructions; see 'BEAM SOLAR AND SKY SOLAR
7139 : // REFLECTED FROM NEAREST OBSTRUCTION,' below.)
7140 :
7141 330560 : if (PH > 0.0) { // Contribution is from sky
7142 826400 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7143 661120 : ZSK.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), TH, PH) * COSB * DA * dl->ObTransM[IPH][ITH];
7144 : }
7145 : } else { // PH <= 0.0; contribution is from ground
7146 165280 : if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
7147 : // Calculate effect of obstructions on shading of sky diffuse reaching the ground point hit
7148 : // by the ray. This effect is given by the ratio SkyObstructionMult =
7149 : // (obstructed sky diffuse at ground point)/(unobstructed sky diffuse at ground point).
7150 : // This ratio is calculated for an isotropic sky.
7151 : // Ground point hit by the ray:
7152 0 : Real64 Alfa = std::acos(-U.z);
7153 0 : Real64 Beta = std::atan2(U.y, U.x);
7154 0 : Real64 HorDis = (s_surf->SurfaceWindow(IWin).WinCenter.z - s_surf->GroundLevelZ) * std::tan(Alfa);
7155 0 : groundHitPt.z = s_surf->GroundLevelZ;
7156 0 : groundHitPt.x = s_surf->SurfaceWindow(IWin).WinCenter.x + HorDis * std::cos(Beta);
7157 0 : groundHitPt.y = s_surf->SurfaceWindow(IWin).WinCenter.y + HorDis * std::sin(Beta);
7158 :
7159 0 : dl->SkyObstructionMult[IPH][ITH] =
7160 0 : CalcObstrMultiplier(state, groundHitPt, AltAngStepsForSolReflCalc, DataSurfaces::AzimAngStepsForSolReflCalc);
7161 : } // End of check if solar reflection calc is in effect
7162 :
7163 165280 : auto const &gilsk = dl->horIllum[IHR];
7164 826400 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7165 : // Below, luminance of ground in cd/m2 is illuminance on ground in lumens/m2
7166 : // times ground reflectance, divided by pi, times obstruction multiplier.
7167 1322240 : ZSK.sky[iSky] = (gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
7168 661120 : dl->SkyObstructionMult[IPH][ITH];
7169 : }
7170 : // Determine if sun illuminates the point that ray hits the ground. If the solar reflection
7171 : // calculation has been requested (CalcSolRefl = .TRUE.) shading by obstructions, including
7172 : // the building itself, is considered in determining whether sun hits the ground point.
7173 : // Otherwise this shading is ignored and the sun always hits the ground point.
7174 165280 : SunObstructionMult = 1.0;
7175 165280 : if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] > 1.e-6 && ISunPos == 1) {
7176 : // Sun reaches ground point if vector from this point to the sun is unobstructed
7177 0 : hitObs = false;
7178 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
7179 0 : hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
7180 0 : if (hitObs) break;
7181 : }
7182 0 : if (hitObs) SunObstructionMult = 0.0;
7183 : }
7184 165280 : ZSU = (dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi) * COSB * DA * dl->ObTransM[IPH][ITH] *
7185 : SunObstructionMult;
7186 : }
7187 : // BEAM SOLAR AND SKY SOLAR REFLECTED FROM NEAREST OBSTRUCTION
7188 :
7189 330560 : if (s_surf->CalcSolRefl && dl->ObTransM[IPH][ITH] < 1.0) {
7190 : // Find obstruction whose hit point is closest to the center of the window
7191 0 : DayltgClosestObstruction(state, s_surf->SurfaceWindow(IWin).WinCenter, U, NearestHitSurfNum, nearestHitPt);
7192 0 : if (NearestHitSurfNum > 0) {
7193 :
7194 : // Beam solar reflected from nearest obstruction.
7195 0 : LumAtHitPtFrSun = DayltgSurfaceLumFromSun(state, IHR, U, NearestHitSurfNum, nearestHitPt);
7196 0 : ZSUObsRefl = LumAtHitPtFrSun * COSB * DA;
7197 0 : ZSU += ZSUObsRefl;
7198 :
7199 : // Sky solar reflected from nearest obstruction.
7200 0 : int const ObsConstrNum = s_surf->Surface(NearestHitSurfNum).Construction;
7201 0 : if (ObsConstrNum > 0) {
7202 : // Exterior building surface is nearest hit
7203 0 : if (!state.dataConstruction->Construct(ObsConstrNum).TypeIsWindow) {
7204 : // Obstruction is not a window, i.e., is an opaque surface
7205 0 : ObsVisRefl = 1.0 - s_mat->materials(state.dataConstruction->Construct(ObsConstrNum).LayerPoint(1))->AbsorpVisible;
7206 : } else {
7207 : // Obstruction is a window; assume it is bare
7208 0 : ObsVisRefl = state.dataConstruction->Construct(ObsConstrNum).ReflectVisDiffFront;
7209 : }
7210 : } else {
7211 : // Shadowing surface is nearest hit
7212 0 : if (s_surf->SurfDaylightingShelfInd(NearestHitSurfNum) > 0) {
7213 : // Skip daylighting shelves, whose reflection is separately calculated
7214 0 : ObsVisRefl = 0.0;
7215 : } else {
7216 0 : ObsVisRefl = s_surf->SurfShadowDiffuseVisRefl(NearestHitSurfNum);
7217 0 : if (s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum) > 0)
7218 0 : ObsVisRefl +=
7219 0 : s_surf->SurfShadowGlazingFrac(NearestHitSurfNum) *
7220 0 : state.dataConstruction->Construct(s_surf->SurfShadowGlazingConstruct(NearestHitSurfNum)).ReflectVisDiffFront;
7221 : // Note in the above that ShadowSurfDiffuseVisRefl is the reflectance of opaque part of
7222 : // shadowing surface times (1 - ShadowSurfGlazingFrac)
7223 : }
7224 : }
7225 0 : NearestHitSurfNumX = NearestHitSurfNum;
7226 : // Each shadowing surface has a "mirror" duplicate surface facing in the opposite direction.
7227 : // The following gets the correct side of a shadowing surface for reflection.
7228 0 : if (s_surf->Surface(NearestHitSurfNum).IsShadowing) {
7229 0 : if (dot(U, s_surf->Surface(NearestHitSurfNum).OutNormVec) > 0.0) NearestHitSurfNumX = NearestHitSurfNum + 1;
7230 : }
7231 0 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !s_surf->ShadingTransmittanceVaries ||
7232 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
7233 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
7234 0 : state.dataSolarShading->SurfDifShdgRatioIsoSky(NearestHitSurfNumX) / Constant::Pi;
7235 : } else {
7236 0 : SkyReflVisLum = ObsVisRefl * s_surf->Surface(NearestHitSurfNumX).ViewFactorSky *
7237 0 : state.dataSolarShading->SurfDifShdgRatioIsoSkyHRTS(1, IHR, NearestHitSurfNumX) / Constant::Pi;
7238 : }
7239 0 : dReflObsSky = SkyReflVisLum * COSB * DA;
7240 :
7241 0 : auto const &gilsk = dl->horIllum[IHR];
7242 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7243 0 : ZSK.sky[iSky] += gilsk.sky[iSky] * dReflObsSky;
7244 : }
7245 : }
7246 : } // End of check if exterior solar reflection calculation is active
7247 :
7248 : // ===Bare window (no shade or blind; non-diffusing glass)===
7249 :
7250 : // Increment flux entering space and window luminance (cd/m2).
7251 : // FLCW--(I,J) = part of incoming flux (in lumens) that goes up to ceiling and upper part of walls.
7252 : // FLFW--(I,J) = part that goes down to floor and lower part of walls
7253 :
7254 330560 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7255 : // Unshaded visible transmittance of TDD for a single ray from sky/ground element
7256 0 : TVISBR = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
7257 :
7258 : // Make all transmitted light diffuse for a TDD with a bare diffuser
7259 0 : auto &wlumsk = dl->winLum(IHR)[iWinCover_Bare];
7260 0 : auto &flfwsk = FLFW[iWinCover_Bare];
7261 0 : auto &flcwsk = FLCW[iWinCover_Bare];
7262 :
7263 0 : auto &tddFluxInc = dl->TDDFluxInc(IHR, PipeNum);
7264 0 : auto &tddFluxTrans = dl->TDDFluxTrans(IHR, PipeNum);
7265 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7266 0 : wlumsk.sky[iSky] += ZSK.sky[iSky] * TVISBR / Constant::Pi;
7267 0 : flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * (1.0 - surfWin.fractionUpgoing);
7268 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7269 :
7270 : // For later calculation of diffuse visible transmittance
7271 0 : tddFluxInc.sky[iSky] += ZSK.sky[iSky];
7272 0 : tddFluxTrans.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7273 :
7274 : } // for (iSky)
7275 :
7276 0 : tddFluxInc.sky[(int)SkyType::Clear] += ZSU;
7277 0 : tddFluxTrans.sky[(int)SkyType::Clear] += ZSU * TVISBR;
7278 :
7279 0 : dl->winLum(IHR)[iWinCover_Bare].sun += ZSU * TVISBR / Constant::Pi;
7280 0 : flfwsk.sun += ZSU * TVISBR * (1.0 - surfWin.fractionUpgoing);
7281 0 : flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7282 :
7283 : } else { // Bare window
7284 : // Transmittance of bare window for this sky/ground element
7285 330560 : TVISBR = Window::POLYF(COSB, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
7286 :
7287 330560 : if (InShelfSurf > 0) { // Inside daylighting shelf
7288 : // Daylighting shelf simplification: All light is diffuse
7289 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7290 0 : auto &flcwsk = FLCW[iWinCover_Bare];
7291 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7292 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7293 : }
7294 0 : flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7295 :
7296 : } else { // Normal window
7297 :
7298 : // CR 7869 correct TVISBR if disk beam passes thru interior window
7299 330560 : if (extWinType == ExtWinType::AdjZone) {
7300 : // modify TVISBR by second window transmission
7301 : // first determine if ray from point passes thru any interior window
7302 0 : hitObs = false;
7303 0 : for (int IntWinLoop = 1; IntWinLoop <= thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).NumOfIntWindows;
7304 : ++IntWinLoop) {
7305 0 : IntWinNum = thisEnclDaylight.IntWinAdjEnclExtWin(IntWinAdjZoneExtWinNum).IntWinNum(IntWinLoop);
7306 0 : auto const &surfIntWin = s_surf->SurfaceWindow(IntWinNum);
7307 0 : hitObs = PierceSurface(state, IntWinNum, surfIntWin.WinCenter, SUNCOS_IHR, obsHitPt);
7308 0 : if (hitObs) { // disk passes thru
7309 : // cosine of incidence angle of light from sky or ground element for
7310 0 : COSBintWin = SPH * std::sin(surfIntWin.phi) + CPH * std::cos(surfIntWin.phi) * std::cos(TH - surfIntWin.theta);
7311 0 : TVISBR *= Window::POLYF(COSBintWin,
7312 0 : state.dataConstruction->Construct(s_surf->Surface(IntWinNum).Construction).TransVisBeamCoef);
7313 0 : break;
7314 : }
7315 : }
7316 0 : if (!hitObs) { // blocked by opaque parts, beam does not actually pass thru interior window to reach zone
7317 0 : TVISBR = 0.0;
7318 : }
7319 : }
7320 :
7321 330560 : auto &flfwsk = FLFW[iWinCover_Bare];
7322 330560 : auto &flcwsk = FLCW[iWinCover_Bare];
7323 1652800 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7324 : // IF (PH < 0.0d0) THEN
7325 : // Fixed by FCW, Nov. 2003:
7326 1322240 : if (PH > 0.0) {
7327 661120 : flfwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7328 : } else {
7329 661120 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR;
7330 : }
7331 : } // for (iSky)
7332 :
7333 330560 : if (PH > 0.0) {
7334 165280 : flfwsk.sun += ZSU * TVISBR;
7335 : } else {
7336 165280 : flcwsk.sun += ZSU * TVISBR;
7337 : }
7338 :
7339 : } // End of check if window with daylighting shelf or normal window
7340 : } // End of check if TDD:DOME or bare window
7341 :
7342 : // Check if window has shade or blind
7343 330560 : ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
7344 330560 : if (s_surf->Surface(IWin).HasShadeControl) {
7345 0 : ShType = s_surf->WindowShadingControl(ICtrl).ShadingType;
7346 0 : ShadeOn = ANY_SHADE(ShType);
7347 0 : BlindOn = ANY_BLIND(ShType);
7348 0 : ScreenOn = (ShType == WinShadingType::ExtScreen);
7349 : }
7350 :
7351 330560 : if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
7352 :
7353 : // ===Window with interior or exterior shade or blind, exterior screen, or with diffusing glass===
7354 :
7355 : // Increment flux entering space and window luminance. Shades and diffusing glass are
7356 : // assumed to be perfect diffusers, i.e., the transmittance is independent of angle of
7357 : // incidence and the transmitted light is isotropic. The transmittance of a blind is
7358 : // assumed to depend on profile angle and slat angle; the diffuse light entering the room from
7359 : // the slats of the blind is assumed to be isotropic. With blinds, light can also enter
7360 : // the room by passing between the slats without reflection. The beam transmittance of a screen
7361 : // is assumed to depend on sun azimuth and azimuth angle.
7362 :
7363 : // For light from a shade, or from diffusing glass, or from the slats of a blind, a flux fraction,
7364 : // SurfaceWindow(IWin)%FractionUpgoing (determined by window tilt), goes up toward
7365 : // ceiling and upper part of walls, and 1-Surfacewindow(iwin)%FractionUpgoing
7366 : // goes down toward floor and lower part of walls. For a blind, the light passing
7367 : // between the slats goes either up or down depending on the altitude angle of the
7368 : // element from which the light came. For a screen, the light passing
7369 : // between the screen's cylinders goes either up or down depending on the altitude angle of the
7370 : // element from which the light came.
7371 :
7372 0 : int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
7373 0 : if (s_surf->SurfWinSolarDiffusing(IWin)) IConstShaded = s_surf->Surface(IWin).Construction;
7374 :
7375 : // Transmittance of window including shade, screen or blind
7376 0 : Real64 transBmBmMult = 0.0;
7377 0 : Real64 transMult = 0.0;
7378 :
7379 0 : if (ShadeOn) { // Shade
7380 0 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7381 : // Shaded visible transmittance of TDD for a single ray from sky/ground element
7382 0 : transMult = TransTDD(state, PipeNum, COSB, RadType::VisibleBeam) * surfWin.glazedFrac;
7383 : } else { // Shade only, no TDD
7384 : // Calculate transmittance of the combined window and shading device for this sky/ground element
7385 0 : transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7386 0 : surfWin.lightWellEff;
7387 : }
7388 :
7389 0 : } else if (ScreenOn) { // Screen: get beam-beam, beam-diffuse and diffuse-diffuse vis trans/ref of screen and glazing system
7390 0 : auto const *screen = dynamic_cast<Material::MaterialScreen *>(s_mat->materials(surfWin.screenNum));
7391 0 : assert(screen != nullptr);
7392 :
7393 0 : Real64 phi = std::abs(PH - surfWin.phi);
7394 0 : Real64 theta = std::abs(TH - surfWin.theta);
7395 : int ip1, ip2, it1, it2; // lo/hi phi/theta interpolation map indices
7396 : BilinearInterpCoeffs coeffs;
7397 :
7398 0 : Material::NormalizePhiTheta(phi, theta);
7399 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
7400 0 : GetBilinearInterpCoeffs(phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
7401 :
7402 0 : ReflGlDiffDiffFront = state.dataConstruction->Construct(IConst).ReflectVisDiffFront;
7403 0 : ReflScDiffDiffBack = screen->DfRefVis;
7404 :
7405 0 : auto const &b11 = screen->btars[ip1][it1];
7406 0 : auto const &b12 = screen->btars[ip1][it2];
7407 0 : auto const &b21 = screen->btars[ip2][it1];
7408 0 : auto const &b22 = screen->btars[ip2][it2];
7409 :
7410 0 : TransScBmDiffFront = BilinearInterp(b11.DfTransVis, b12.DfTransVis, b21.DfTransVis, b22.DfTransVis, coeffs);
7411 :
7412 0 : transMult = TransScBmDiffFront * surfWin.glazedFrac * state.dataConstruction->Construct(IConst).TransDiffVis /
7413 0 : (1 - ReflGlDiffDiffFront * ReflScDiffDiffBack) * surfWin.lightWellEff;
7414 :
7415 0 : transBmBmMult = BilinearInterp(b11.BmTransVis, b12.BmTransVis, b21.BmTransVis, b22.BmTransVis, coeffs);
7416 :
7417 0 : } else if (BlindOn) { // Blind: get beam-diffuse and beam-beam vis trans of blind+glazing system
7418 : // PETER: As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
7419 : // for TDDs because it is based on TVISBR which is correctly calculated for TDDs above.
7420 0 : auto const &surfShade = s_surf->surfShades(IWin);
7421 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
7422 0 : assert(matBlind != nullptr);
7423 0 : Real64 ProfAng = ProfileAngle(state, IWin, U, matBlind->SlatOrientation);
7424 :
7425 0 : auto &btar = surfShade.blind.TAR;
7426 0 : int idxLo = surfShade.blind.profAngIdxLo;
7427 0 : int idxHi = std::min(Material::MaxProfAngs, idxLo + 1);
7428 0 : Real64 interpFac = surfShade.blind.profAngInterpFac;
7429 0 : TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7430 :
7431 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7432 0 : ReflGlDiffDiffBack = construct.ReflectVisDiffBack;
7433 0 : ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7434 0 : ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
7435 0 : TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
7436 0 : transMult = TVISBR * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7437 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7438 :
7439 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7440 0 : ReflGlDiffDiffFront = construct.ReflectVisDiffFront;
7441 0 : ReflBlDiffDiffBack = btar.Vis.Bk.Df.Ref;
7442 0 : transMult = TransBlBmDiffFront * surfWin.glazedFrac * construct.TransDiffVis /
7443 0 : (1.0 - ReflGlDiffDiffFront * ReflBlDiffDiffBack) * surfWin.lightWellEff;
7444 :
7445 : } else { // Between-glass blind
7446 0 : Real64 t1 = Window::POLYF(COSB, construct.tBareVisCoef(1));
7447 0 : td2 = construct.tBareVisDiff(2);
7448 0 : rbd1 = construct.rbBareVisDiff(1);
7449 0 : rfd2 = construct.rfBareVisDiff(2);
7450 0 : Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7451 0 : tfshd = btar.Vis.Ft.Df.Tra;
7452 0 : Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7453 0 : rbshd = btar.Vis.Ft.Df.Ref;
7454 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7455 0 : transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7456 : } else { // 3 glass layers; blind between layers 2 and 3
7457 0 : Real64 t2 = Window::POLYF(COSB, construct.tBareVisCoef(2));
7458 0 : td3 = construct.tBareVisDiff(3);
7459 0 : rfd3 = construct.rfBareVisDiff(3);
7460 0 : rbd2 = construct.rbBareVisDiff(2);
7461 0 : transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7462 0 : surfWin.lightWellEff;
7463 : }
7464 : }
7465 :
7466 0 : transBmBmMult = TVISBR * matBlind->BeamBeamTrans(ProfAng, surfShade.blind.slatAng);
7467 : } else { // Diffusing glass
7468 0 : transMult = Window::POLYF(COSB, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7469 0 : surfWin.lightWellEff;
7470 : } // End of check if shade, blind or diffusing glass
7471 :
7472 0 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7473 : // No beam is transmitted. This takes care of all types of screens and blinds.
7474 0 : transBmBmMult = 0.0;
7475 : }
7476 :
7477 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7478 0 : if (InShelfSurf > 0) { // Inside daylighting shelf
7479 0 : transBmBmMult = 0.0;
7480 : }
7481 :
7482 : // DayltgInterReflectedIllumTransBmBmMult is used in the following for windows with blinds or screens to get contribution from light
7483 : // passing directly between slats or between screen material without reflection.
7484 :
7485 0 : auto &wlumsk = dl->winLum(IHR)[iWinCover_Shaded];
7486 0 : auto &flfwsk = FLFW[iWinCover_Shaded];
7487 0 : auto &flcwsk = FLCW[iWinCover_Shaded];
7488 :
7489 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7490 : // Should these be bare or shaded?
7491 0 : wlumsk.sky[iSky] += ZSK.sky[iSky] * transMult / Constant::Pi;
7492 0 : flfwsk.sky[iSky] += ZSK.sky[iSky] * transMult * (1.0 - surfWin.fractionUpgoing);
7493 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * transMult * surfWin.fractionUpgoing;
7494 :
7495 0 : if (BlindOn || ScreenOn) {
7496 0 : if (PH > 0.0) {
7497 0 : flfwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
7498 : } else {
7499 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * transBmBmMult;
7500 : }
7501 : }
7502 : }
7503 :
7504 0 : dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU * transMult / Constant::Pi;
7505 0 : flfwsk.sun += ZSU * transMult * (1.0 - surfWin.fractionUpgoing);
7506 0 : flcwsk.sun += ZSU * transMult * surfWin.fractionUpgoing;
7507 0 : if (BlindOn || ScreenOn) {
7508 0 : if (PH > 0.0) {
7509 0 : flfwsk.sun += ZSU * transBmBmMult;
7510 : } else {
7511 0 : flcwsk.sun += ZSU * transBmBmMult;
7512 : }
7513 : }
7514 : } // End of window with shade, screen, blind or diffusing glass
7515 :
7516 : } // End of azimuth integration loop, ITH
7517 : } // End of altitude integration loop, IPH
7518 :
7519 2066 : if (OutShelfSurf > 0) { // Outside daylighting shelf
7520 : // Add exterior diffuse illuminance due to outside shelf
7521 : // Since all of the illuminance is added to the zone as upgoing diffuse, it can be added as a lump sum here
7522 :
7523 0 : TVISBR = construct.TransDiffVis; // Assume diffuse transmittance for shelf illuminance
7524 :
7525 0 : auto const &gilsk = dl->horIllum[IHR];
7526 0 : auto &flcwsk = FLCW[iWinCover_Bare];
7527 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7528 : // This is only an estimate because the anisotropic sky view of the shelf is not yet taken into account.
7529 : // SurfAnisoSkyMult would be great to use but it is not available until the heat balance starts up.
7530 0 : ZSK.sky[iSky] = gilsk.sky[iSky] * 1.0 * state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis *
7531 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
7532 :
7533 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7534 0 : flcwsk.sky[iSky] += ZSK.sky[iSky] * TVISBR * surfWin.fractionUpgoing;
7535 : } // ISKY
7536 :
7537 0 : ZSU = dl->horIllum[IHR].sun * state.dataHeatBal->SurfSunlitFracHR(IHR, OutShelfSurf) *
7538 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis * state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor;
7539 0 : flcwsk.sun += ZSU * TVISBR * surfWin.fractionUpgoing;
7540 : }
7541 :
7542 : // Sky-related portion of internally reflected illuminance.
7543 : // The inside surface area, ZoneDaylight(ZoneNum)%totInsSurfArea, and ZoneDaylight(ZoneNum)%aveVisDiffReflect,
7544 : // were calculated in subr DayltgAveInteriorReflectance.
7545 :
7546 6198 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
7547 4132 : auto &eintsk = dl->reflIllum(IHR)[iWinCover];
7548 4132 : auto const &flfwsk = FLFW[iWinCover];
7549 4132 : auto const &flcwsk = FLCW[iWinCover];
7550 :
7551 20660 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7552 : // Full area of window is used in following since effect of dividers on reducing
7553 : // effective window transmittance has already been accounted for in calc of FLFWSK and FLCWSK.
7554 33056 : eintsk.sky[iSky] = (flfwsk.sky[iSky] * surfWin.rhoFloorWall + flcwsk.sky[iSky] * surfWin.rhoCeilingWall) *
7555 16528 : (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - dl->enclDaylight(enclNum).aveVisDiffReflect));
7556 : } // for (iSky)
7557 : } // for (iWinCover)
7558 :
7559 : // BEAM SOLAR RADIATION ON WINDOW
7560 :
7561 : // Beam reaching window directly (without specular reflection from exterior obstructions)
7562 :
7563 2066 : if (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0) {
7564 : // Cos of angle of incidence
7565 1454 : COSBSun = dl->sunAngles.sinPhi * std::sin(surfWin.phi) +
7566 1454 : dl->sunAngles.cosPhi * std::cos(surfWin.phi) * std::cos(dl->sunAngles.theta - surfWin.theta);
7567 :
7568 1454 : if (COSBSun > 0.0) {
7569 : // Multiply direct normal illuminance (normalized to 1.0 lux)
7570 : // by incident angle factor and by fraction of window that is sunlit.
7571 : // Note that in the following SurfSunlitFracHR accounts for possibly non-zero transmittance of
7572 : // shading surfaces.
7573 :
7574 1454 : ZSU1 = COSBSun * state.dataHeatBal->SurfSunlitFracHR(IHR, IWin);
7575 :
7576 : // Contribution to window luminance and downgoing flux
7577 :
7578 : // -- Bare window
7579 :
7580 1454 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7581 : // Unshaded visible transmittance of TDD for collimated beam from the sun
7582 0 : TVISBSun = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
7583 0 : dl->TDDTransVisBeam(IHR, PipeNum) = TVISBSun;
7584 :
7585 0 : FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
7586 :
7587 0 : dl->winLum(IHR)[iWinCover_Bare].sun += ZSU1 * TVISBSun / Constant::Pi;
7588 0 : FLFW[iWinCover_Bare].sun += ZSU1 * TVISBSun * (1.0 - surfWin.fractionUpgoing);
7589 0 : FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
7590 :
7591 : } else { // Bare window
7592 1454 : TVISBSun = Window::POLYF(COSBSun, construct.TransVisBeamCoef) * surfWin.glazedFrac * surfWin.lightWellEff;
7593 :
7594 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7595 1454 : if (InShelfSurf > 0) { // Inside daylighting shelf
7596 0 : FLFW[iWinCover_Bare].sunDisk = 0.0; // Diffuse light only
7597 :
7598 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7599 : // WLUMSU(1,IHR) = WLUMSU(1,IHR) + ZSU1 * TVISBSun / PI
7600 : // FLFWSU(1) = FLFWSU(1) + ZSU1 * TVISBSun * (1.0 - SurfaceWindow(IWin)%FractionUpgoing)
7601 0 : FLCW[iWinCover_Bare].sun += ZSU1 * TVISBSun * surfWin.fractionUpgoing;
7602 : } else { // Normal window
7603 1454 : FLFW[iWinCover_Bare].sunDisk = ZSU1 * TVISBSun;
7604 : }
7605 : }
7606 :
7607 : // -- Window with shade, screen, blind or diffusing glass
7608 1454 : if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
7609 0 : Real64 transBmBmMult = 0.0;
7610 0 : Real64 transMult = 0.0;
7611 :
7612 0 : if (ShadeOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade or screen on or diffusing glass
7613 0 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7614 : // Shaded visible transmittance of TDD for collimated beam from the sun
7615 0 : transMult = TransTDD(state, PipeNum, COSBSun, RadType::VisibleBeam) * surfWin.glazedFrac;
7616 :
7617 0 : } else if (ScreenOn) {
7618 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
7619 0 : assert(screen != nullptr);
7620 0 : Real64 phi = std::abs(dl->sunAngles.phi - surfWin.phi);
7621 0 : Real64 theta = std::abs(dl->sunAngles.theta - surfWin.theta);
7622 : int ip1, ip2, it1, it2;
7623 : BilinearInterpCoeffs coeffs;
7624 0 : Material::NormalizePhiTheta(phi, theta);
7625 0 : Material::GetPhiThetaIndices(phi, theta, screen->dPhi, screen->dTheta, ip1, ip2, it1, it2);
7626 0 : GetBilinearInterpCoeffs(
7627 0 : phi, theta, ip1 * screen->dPhi, ip2 * screen->dPhi, it1 * screen->dTheta, it2 * screen->dTheta, coeffs);
7628 0 : Real64 BmBmTransVis = BilinearInterp(screen->btars[ip1][it1].BmTransVis,
7629 0 : screen->btars[ip1][it2].BmTransVis,
7630 0 : screen->btars[ip2][it1].BmTransVis,
7631 0 : screen->btars[ip2][it2].BmTransVis,
7632 : coeffs);
7633 :
7634 0 : transMult = BmBmTransVis * surfWin.glazedFrac * surfWin.lightWellEff;
7635 : } else {
7636 0 : int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
7637 0 : if (s_surf->SurfWinSolarDiffusing(IWin)) IConstShaded = surf.Construction;
7638 0 : transMult = Window::POLYF(COSBSun, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac *
7639 0 : surfWin.lightWellEff;
7640 : }
7641 :
7642 : } else { // Blind on
7643 0 : auto const &surfShade = s_surf->surfShades(IWin);
7644 : // As long as only interior blinds are allowed for TDDs, no need to change TransMult calculation
7645 : // for TDDs because it is based on TVISBSun which is correctly calculated for TDDs above.
7646 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
7647 0 : assert(matBlind != nullptr);
7648 :
7649 : // These are "cached" in the surfShade struct
7650 0 : auto &btar = surfShade.blind.TAR;
7651 0 : int idxLo = surfShade.blind.profAngIdxLo;
7652 0 : int idxHi = surfShade.blind.profAngIdxHi;
7653 0 : int interpFac = surfShade.blind.profAngInterpFac;
7654 0 : TransBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7655 :
7656 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7657 : // TH CR 8121, 7/7/2010
7658 : // ReflBlBmDiffFront = WindowManager::InterpProfAng(ProfAng,Blind(BlNum)%VisFrontBeamDiffRefl)
7659 0 : ReflBlBmDiffFront = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7660 :
7661 : // TH added 7/12/2010 for CR 8121
7662 0 : ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
7663 0 : TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
7664 :
7665 0 : transMult = TVISBSun * (TransBlBmDiffFront + ReflBlBmDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7666 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7667 :
7668 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7669 0 : transMult = TransBlBmDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
7670 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7671 :
7672 : } else { // Between-glass blind
7673 0 : Real64 t1 = Window::POLYF(COSBSun, construct.tBareVisCoef(1));
7674 0 : Real64 tfshBd = Interp(btar.Vis.Ft.Bm[idxLo].DfTra, btar.Vis.Ft.Bm[idxHi].DfTra, interpFac);
7675 0 : Real64 rfshB = Interp(btar.Vis.Ft.Bm[idxLo].DfRef, btar.Vis.Ft.Bm[idxHi].DfRef, interpFac);
7676 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7677 0 : transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7678 : } else { // 3 glass layers; blind between layers 2 and 3
7679 0 : Real64 t2 = Window::POLYF(COSBSun, construct.tBareVisCoef(2));
7680 0 : transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7681 0 : surfWin.lightWellEff;
7682 : }
7683 : }
7684 :
7685 0 : transBmBmMult = TVISBSun * matBlind->BeamBeamTrans(surfShade.blind.profAng, surfShade.blind.slatAng);
7686 : } // ShadeOn/ScreenOn/BlindOn/Diffusing glass
7687 :
7688 0 : if (s_surf->Surface(IWin).OriginalClass == SurfaceClass::TDD_Dome) {
7689 0 : transBmBmMult = 0.0; // No beam, diffuse only
7690 : }
7691 :
7692 : // Daylighting shelf simplification: No beam makes it past end of shelf, all light is diffuse
7693 0 : if (InShelfSurf > 0) { // Inside daylighting shelf
7694 0 : transBmBmMult = 0.0; // No beam, diffuse only
7695 : // SurfaceWindow(IWin)%FractionUpgoing is already set to 1.0 earlier
7696 : }
7697 :
7698 0 : dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1 * transMult / Constant::Pi;
7699 0 : dl->winLum(IHR)[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult / Constant::Pi;
7700 0 : FLFW[iWinCover_Shaded].sun += ZSU1 * transMult * (1.0 - surfWin.fractionUpgoing);
7701 0 : FLFW[iWinCover_Shaded].sunDisk = ZSU1 * transBmBmMult;
7702 0 : FLCW[iWinCover_Shaded].sun += ZSU1 * transMult * surfWin.fractionUpgoing;
7703 : } // if (BlindOn || ShadeOn)
7704 : } // if (COSBSun > 0)
7705 : } // if (SurfSunlitFracHR > 0)
7706 :
7707 : // Beam reaching window after specular reflection from exterior obstruction
7708 :
7709 : // In the following, Beam normal illuminance times ZSU1refl = illuminance on window due to
7710 : // specular reflection from exterior surfaces
7711 :
7712 2066 : if (s_surf->CalcSolRefl && s_surf->Surface(IWin).OriginalClass != SurfaceClass::TDD_Dome) {
7713 :
7714 0 : ZSU1refl = s_surf->SurfReflFacBmToBmSolObs(IHR, IWin);
7715 :
7716 0 : if (ZSU1refl > 0.0) {
7717 : // Contribution to window luminance and downgoing flux
7718 :
7719 : // -- Bare window. We use diffuse-diffuse transmittance here rather than beam-beam to avoid
7720 : // complications due to specular reflection from multiple exterior surfaces
7721 :
7722 0 : TVisSunRefl = construct.TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
7723 : // In the following it is assumed that all reflected beam is going downward, as it would be in the
7724 : // important case of reflection from a highly glazed facade of a neighboring building. However, in
7725 : // rare cases (such as upward specular reflection from a flat horizontal skylight) it may
7726 : // actually be going upward.
7727 0 : FLFW[iWinCover_Bare].sunDisk += ZSU1refl * TVisSunRefl;
7728 :
7729 : // -- Window with shade, blind or diffusing glass
7730 :
7731 0 : if (ShadeOn || BlindOn || ScreenOn || s_surf->SurfWinSolarDiffusing(IWin)) {
7732 0 : Real64 transMult = 0.0;
7733 :
7734 0 : if (ShadeOn || s_surf->SurfWinSolarDiffusing(IWin)) { // Shade on or diffusing glass
7735 0 : int IConstShaded = s_surf->SurfWinActiveShadedConstruction(IWin);
7736 0 : if (s_surf->SurfWinSolarDiffusing(IWin)) IConstShaded = s_surf->Surface(IWin).Construction;
7737 0 : transMult = state.dataConstruction->Construct(IConstShaded).TransDiffVis * surfWin.glazedFrac * surfWin.lightWellEff;
7738 :
7739 0 : } else if (ScreenOn) { // Exterior screen on
7740 0 : auto const *screen = dynamic_cast<Material::MaterialScreen const *>(s_mat->materials(surfWin.screenNum));
7741 0 : assert(screen != nullptr);
7742 0 : Real64 TransScDiffDiffFront = screen->DfTransVis;
7743 :
7744 0 : transMult = TransScDiffDiffFront *
7745 0 : (state.dataConstruction->Construct(IConst).TransDiffVis / (1.0 - ReflGlDiffDiffFront * ReflScDiffDiffBack)) *
7746 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7747 :
7748 : } else { // Blind on
7749 :
7750 0 : auto const &surfShade = s_surf->surfShades(IWin);
7751 0 : auto const &btar = surfShade.blind.TAR;
7752 0 : auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(s_mat->materials(surfShade.blind.matNum));
7753 :
7754 0 : assert(matBlind != nullptr);
7755 :
7756 0 : TransBlDiffDiffFront = btar.Vis.Ft.Df.Tra;
7757 0 : if (ShType == WinShadingType::IntBlind) { // Interior blind
7758 0 : ReflBlDiffDiffFront = btar.Vis.Ft.Df.Ref;
7759 0 : transMult = TVisSunRefl * (TransBlDiffDiffFront + ReflBlDiffDiffFront * ReflGlDiffDiffBack * TransBlDiffDiffFront /
7760 0 : (1.0 - ReflBlDiffDiffFront * ReflGlDiffDiffBack));
7761 :
7762 0 : } else if (ShType == WinShadingType::ExtBlind) { // Exterior blind
7763 0 : transMult = TransBlDiffDiffFront * (construct.TransDiffVis / (1.0 - ReflGlDiffDiffFront * btar.Vis.Bk.Df.Ref)) *
7764 0 : surfWin.glazedFrac * surfWin.lightWellEff;
7765 :
7766 : } else { // Between-glass blind
7767 0 : Real64 t1 = construct.tBareVisDiff(1);
7768 0 : Real64 tfshBd = btar.Vis.Ft.Df.Tra;
7769 0 : Real64 rfshB = btar.Vis.Ft.Df.Ref;
7770 0 : if (construct.TotGlassLayers == 2) { // 2 glass layers
7771 0 : transMult = t1 * (tfshBd * (1.0 + rfd2 * rbshd) + rfshB * rbd1 * tfshd) * td2 * surfWin.lightWellEff;
7772 : } else { // 3 glass layers; blind between layers 2 and 3
7773 0 : Real64 t2 = construct.tBareVisDiff(2);
7774 0 : transMult = t1 * t2 * (tfshBd * (1.0 + rfd3 * rbshd) + rfshB * (rbd2 * tfshd + td2 * rbd1 * td2 * tfshd)) * td3 *
7775 0 : surfWin.lightWellEff;
7776 : }
7777 : } // End of check of interior/exterior/between-glass blind
7778 : } // if (Blind)
7779 :
7780 0 : dl->winLum(IHR)[iWinCover_Shaded].sun += ZSU1refl * transMult / Constant::Pi;
7781 0 : FLFW[iWinCover_Shaded].sun += ZSU1refl * transMult * (1.0 - surfWin.fractionUpgoing);
7782 0 : FLCW[iWinCover_Shaded].sun += ZSU1refl * transMult * surfWin.fractionUpgoing;
7783 : } // End of check if window has shade, blind or diffusing glass
7784 : } // End of check if ZSU1refl > 0.0
7785 : } // End of check if solar reflections are in effect
7786 :
7787 : // Sun-related portion of internally reflected illuminance
7788 :
7789 : // Full area of window is used in following since effect of dividers on reducing
7790 : // effective window transmittance already accounted for in calc of FLFWSU and FLCWSU
7791 : // CR 7869 added effect of intervening interior windows on transmittance and
7792 : // added inside surface area of adjacent zone
7793 6198 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
7794 8264 : dl->reflIllum(IHR)[iWinCover].sun = (FLFW[iWinCover].sun * surfWin.rhoFloorWall + FLCW[iWinCover].sun * surfWin.rhoCeilingWall) *
7795 4132 : (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
7796 :
7797 8264 : dl->reflIllum(IHR)[iWinCover].sunDisk = FLFW[iWinCover].sunDisk * surfWin.rhoFloorWall * (surf.Area / surfWin.glazedFrac) /
7798 4132 : (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
7799 : }
7800 2066 : } // DayltgInterReflectedIllum()
7801 :
7802 0 : void ComplexFenestrationLuminances(EnergyPlusData &state,
7803 : int const IWin,
7804 : int const WinEl,
7805 : int const NBasis,
7806 : int const IHR,
7807 : int const iRefPoint,
7808 : Array1D<Illums> &ElementLuminance, // luminance at window element (exterior side)
7809 : CalledFor const CalledFrom,
7810 : int const MapNum)
7811 : {
7812 :
7813 : // SUBROUTINE INFORMATION:
7814 : // AUTHOR Simon Vidanovic
7815 : // DATE WRITTEN June 2013
7816 :
7817 0 : Vector3<Real64> obsHitPt; // Coordinates of hit point on an obstruction (m)
7818 0 : Vector3<Real64> groundHitPt; // Coordinates of point that ray from window center hits the ground (m)
7819 :
7820 0 : auto &dl = state.dataDayltg;
7821 0 : auto &s_surf = state.dataSurface;
7822 :
7823 0 : int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
7824 0 : auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
7825 : // Calculate luminance from sky and sun excluding exterior obstruction transmittances and obstruction multipliers
7826 0 : int SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
7827 0 : for (int iIncElem = 1; iIncElem <= NBasis; ++iIncElem) {
7828 0 : Real64 LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
7829 : // COSB = ComplexWind(IWin)%Geom(CurCplxFenState)%CosInc(iIncElem)
7830 : // DA = ComplexWind(IWin)%Geom(CurCplxFenState)%DAInc(iIncElem)
7831 0 : Real64 Altitude = complexWinGeom.pInc(iIncElem).Altitude;
7832 0 : Real64 Azimuth = complexWinGeom.pInc(iIncElem).Azimuth;
7833 0 : auto &elemLum = ElementLuminance(iIncElem);
7834 0 : auto const &gilsk = dl->horIllum[IHR];
7835 :
7836 0 : if (Altitude > 0.0) {
7837 : // Ray from sky element
7838 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7839 0 : elemLum.sky[iSky] = DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc;
7840 : }
7841 0 : } else if (Altitude < 0.0) {
7842 : // Ray from ground element
7843 : // BeamObstrMultiplier = ComplexWind(IWin)%DaylghtGeom(CurCplxFenState)%GndObstrMultiplier(WinEl, iIncElem)
7844 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7845 0 : elemLum.sky[iSky] = gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7846 : }
7847 0 : elemLum.sun = dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7848 : } else {
7849 : // Ray from the element which is half sky and half ground
7850 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7851 : // in this case half of the pach is coming from the sky and half from the ground
7852 0 : elemLum.sky[iSky] = 0.5 * DayltgSkyLuminance(state, static_cast<SkyType>(iSky), Azimuth, Altitude) * LambdaInc +
7853 0 : 0.5 * gilsk.sky[iSky] * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7854 : }
7855 0 : elemLum.sun = 0.5 * dl->horIllum[IHR].sun * state.dataEnvrn->GndReflectanceForDayltg / Constant::Pi * LambdaInc;
7856 : }
7857 : // Sun beam calculations
7858 0 : if ((SolBmIndex == iIncElem) && (state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) > 0.0)) {
7859 0 : elemLum.sunDisk = 1.0;
7860 : }
7861 : }
7862 :
7863 0 : auto const &complexWinDaylightGeom = state.dataBSDFWindow->ComplexWind(IWin).DaylghtGeom(CurCplxFenState);
7864 :
7865 0 : if (CalledFrom == CalledFor::RefPoint) {
7866 0 : auto const &complexWinRefPoint = complexWinDaylightGeom.RefPoint(iRefPoint);
7867 : // add exterior obstructions transmittances to calculated luminances
7868 0 : for (int iReflElem = 1; iReflElem <= complexWinRefPoint.NReflSurf(WinEl); ++iReflElem) {
7869 0 : Real64 ObstrTrans = complexWinRefPoint.TransOutSurf(iReflElem, WinEl);
7870 0 : int iReflElemIndex = complexWinRefPoint.RefSurfIndex(iReflElem, WinEl);
7871 :
7872 0 : auto &elemLum = ElementLuminance(iReflElemIndex);
7873 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7874 0 : elemLum.sky[iSky] *= ObstrTrans;
7875 : }
7876 0 : elemLum.sun *= ObstrTrans;
7877 0 : elemLum.sunDisk *= ObstrTrans;
7878 : }
7879 :
7880 : // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
7881 : // sun reaches the ground for that point
7882 0 : Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
7883 0 : for (int iGndElem = 1; iGndElem <= complexWinRefPoint.NGnd(WinEl); ++iGndElem) {
7884 : // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
7885 : // were hit in the process
7886 :
7887 0 : Real64 BeamObstrMultiplier = complexWinRefPoint.GndObstrMultiplier(iGndElem, WinEl);
7888 0 : int iGndElemIndex = complexWinRefPoint.GndIndex(iGndElem, WinEl);
7889 :
7890 0 : auto &elemLum = ElementLuminance(iGndElemIndex);
7891 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7892 0 : elemLum.sky[iSky] *= BeamObstrMultiplier;
7893 : }
7894 :
7895 : // direct sun disk reflect off the ground
7896 0 : Real64 SunObstrMultiplier = 1.0;
7897 0 : if (s_surf->CalcSolRefl) {
7898 : // Sun reaches ground point if vector from this point to the sun is unobstructed
7899 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
7900 0 : groundHitPt = complexWinRefPoint.GndPt(iGndElem, WinEl);
7901 0 : bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
7902 0 : if (hitObs) {
7903 0 : SunObstrMultiplier = 0.0;
7904 0 : break;
7905 : }
7906 : }
7907 : }
7908 0 : elemLum.sun *= SunObstrMultiplier;
7909 : }
7910 :
7911 0 : } else { // if (CalledFrom != RefPoint)
7912 :
7913 0 : auto const &complexWinIllumMap = complexWinDaylightGeom.IlluminanceMap(iRefPoint, MapNum);
7914 : // add exterior obstructions transmittances to calculated luminances
7915 0 : for (int iReflElem = 1; iReflElem <= complexWinIllumMap.NReflSurf(WinEl); ++iReflElem) {
7916 0 : Real64 ObstrTrans = complexWinIllumMap.TransOutSurf(iReflElem, WinEl);
7917 0 : int iReflElemIndex = complexWinIllumMap.RefSurfIndex(iReflElem, WinEl);
7918 0 : auto &elemLum = ElementLuminance(iReflElemIndex);
7919 :
7920 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7921 0 : elemLum.sky[iSky] *= ObstrTrans;
7922 : }
7923 0 : elemLum.sun *= ObstrTrans;
7924 0 : elemLum.sunDisk *= ObstrTrans;
7925 : }
7926 :
7927 : // add exterior ground element obstruction multipliers to calculated luminances. For sun reflection, calculate if
7928 : // sun reaches the ground for that point
7929 0 : Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
7930 0 : for (int iGndElem = 1; iGndElem <= complexWinIllumMap.NGnd(WinEl); ++iGndElem) {
7931 : // case for sky elements. Integration is done over upper ground hemisphere to determine how many obstructions
7932 : // were hit in the process
7933 0 : Real64 BeamObstrMultiplier = complexWinIllumMap.GndObstrMultiplier(iGndElem, WinEl);
7934 0 : int iGndElemIndex = complexWinIllumMap.GndIndex(iGndElem, WinEl);
7935 :
7936 0 : auto &elemLum = ElementLuminance(iGndElemIndex);
7937 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
7938 0 : elemLum.sky[iSky] *= BeamObstrMultiplier;
7939 : }
7940 :
7941 : // direct sun disk reflect off the ground
7942 0 : Real64 SunObstrMultiplier = 1.0;
7943 0 : if (s_surf->CalcSolRefl) {
7944 : // Sun reaches ground point if vector from this point to the sun is unobstructed
7945 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
7946 0 : groundHitPt = complexWinIllumMap.GndPt(iGndElem, WinEl);
7947 :
7948 0 : bool hitObs = PierceSurface(state, ObsSurfNum, groundHitPt, SUNCOS_IHR, obsHitPt);
7949 0 : if (hitObs) {
7950 0 : SunObstrMultiplier = 0.0;
7951 0 : break;
7952 : }
7953 : }
7954 : }
7955 0 : elemLum.sun *= SunObstrMultiplier;
7956 : }
7957 0 : } // if (CalledFrom == RefPoint)
7958 0 : } // ComplexFenestrationLuminances()
7959 :
7960 0 : void DayltgInterReflectedIllumComplexFenestration(EnergyPlusData &state,
7961 : int const IWin, // Window index
7962 : int const WinEl, // Current window element counter
7963 : int const IHR, // Hour of day
7964 : int const daylightCtrlNum, // Daylighting control number
7965 : int const iRefPoint, // reference point counter
7966 : CalledFor const CalledFrom,
7967 : int const MapNum)
7968 : {
7969 :
7970 : // SUBROUTINE INFORMATION:
7971 : // AUTHOR Simon Vidanovic
7972 : // DATE WRITTEN April 2013
7973 :
7974 : // PURPOSE OF THIS SUBROUTINE:
7975 : // Called from CalcDayltgCoefficients for each complex (bsdf) fenestration and reference point in a daylit
7976 : // space, for each sun position. Calculates illuminance (EINTSK and EINTSU) at reference point due
7977 : // to internally reflected light by integrating to determine the amount of flux from
7978 : // sky and ground (and beam reflected from obstructions) transmitted through
7979 : // the center of the window and then reflecting this
7980 : // light from the inside surfaces of the space.
7981 :
7982 0 : auto &dl = state.dataDayltg;
7983 0 : auto &s_surf = state.dataSurface;
7984 :
7985 0 : Array1D<Illums> FL; // Sky related luminous flux
7986 : // Array1D<Real64> FLSU; // Sun related luminous flux, excluding entering beam
7987 : // Array1D<Real64> FLSUdisk; // Sun related luminous flux, due to entering beam
7988 :
7989 0 : Array1D<Illums> FirstFlux; // Sky related first reflected flux
7990 : // Array1D<Real64> FirstFluxSU; // Sun related first reflected flux, excluding entering beam
7991 : // Array1D<Real64> FirstFluxSUdisk; // Sun related first reflected flux, due to entering beam
7992 :
7993 0 : Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
7994 : // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side), exluding beam
7995 : // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side), due to sun beam
7996 0 : Illums FLTot;
7997 : // Real64 FLSUTot;
7998 : // Real64 FLSUdiskTot;
7999 :
8000 : // Total for first relflected fluxes
8001 0 : Illums FFTot = Illums();
8002 : // Real64 FFSUTot;
8003 : // Real64 FFSUdiskTot;
8004 :
8005 : int NIncBasis;
8006 : int SolBmIndex; // index of current sun position
8007 :
8008 : Real64 LambdaInc; // current lambda value for incoming direction
8009 : // REAL(r64) :: LambdaTrn ! current lambda value for incoming direction
8010 : Real64 dirTrans; // directional bsdf transmittance
8011 :
8012 0 : auto const &surf = s_surf->Surface(IWin);
8013 0 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
8014 :
8015 0 : int CurCplxFenState = surfWin.ComplexFen.CurrentState;
8016 0 : auto &complexWinGeom = state.dataBSDFWindow->ComplexWind(IWin).Geom(CurCplxFenState);
8017 0 : int iConst = surfWin.ComplexFen.State(CurCplxFenState).Konst;
8018 0 : int NTrnBasis = complexWinGeom.Trn.NBasis;
8019 :
8020 0 : if (!allocated(FL)) FL.allocate(NTrnBasis);
8021 0 : FL = Illums();
8022 : // if (!allocated(FLSU)) FLSU.dimension(NTrnBasis, 0.0);
8023 : // if (!allocated(FLSUdisk)) FLSUdisk.dimension(NTrnBasis, 0.0);
8024 :
8025 0 : if (!allocated(FirstFlux)) FirstFlux.allocate(NTrnBasis);
8026 0 : FirstFlux = Illums();
8027 : // if (!allocated(FirstFluxSU)) FirstFluxSU.dimension(NTrnBasis, 0.0);
8028 : // if (!allocated(FirstFluxSUdisk)) FirstFluxSUdisk.dimension(NTrnBasis, 0.0);
8029 :
8030 0 : NIncBasis = complexWinGeom.Inc.NBasis;
8031 0 : if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
8032 0 : ElementLuminance = Illums();
8033 : // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
8034 : // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
8035 :
8036 : // Integration over sky/ground/sun elements is done over window incoming basis element and flux is calculated for each
8037 : // outgoing direction. This is used to calculate first reflected flux
8038 :
8039 0 : ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
8040 :
8041 : // luminance from sun disk needs to include fraction of sunlit area
8042 0 : SolBmIndex = complexWinGeom.SolBmIndex(IHR, state.dataGlobal->TimeStep);
8043 0 : Real64 COSIncSun = (SolBmIndex > 0) ? complexWinGeom.CosInc(SolBmIndex) : 0.0;
8044 :
8045 0 : for (int i = 1; i <= (int)ElementLuminance.size(); ++i)
8046 0 : ElementLuminance(i).sunDisk *= state.dataHeatBal->SurfSunlitFracHR(IHR, IWin) * COSIncSun;
8047 :
8048 : // FLSKTot = 0.0;
8049 0 : FLTot.sun = 0.0;
8050 0 : FLTot.sunDisk = 0.0;
8051 0 : FFTot.sun = 0.0;
8052 0 : FFTot.sunDisk = 0.0;
8053 : // now calculate flux into each outgoing direction by integrating over all incoming directions
8054 0 : for (int iBackElem = 1; iBackElem <= NTrnBasis; ++iBackElem) {
8055 0 : for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
8056 0 : LambdaInc = complexWinGeom.Inc.Lamda(iIncElem);
8057 0 : dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iBackElem, iIncElem);
8058 :
8059 0 : auto &fl = FL(iBackElem);
8060 0 : auto const &elemLum = ElementLuminance(iIncElem);
8061 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8062 0 : fl.sky[iSky] += dirTrans * LambdaInc * elemLum.sky[iSky];
8063 : }
8064 :
8065 0 : fl.sun += dirTrans * LambdaInc * elemLum.sun;
8066 0 : fl.sunDisk += dirTrans * LambdaInc * elemLum.sunDisk;
8067 : }
8068 :
8069 0 : auto &firstFlux = FirstFlux(iBackElem);
8070 0 : auto const &fl = FL(iBackElem);
8071 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8072 0 : firstFlux.sky[iSky] = fl.sky[iSky] * complexWinGeom.AveRhoVisOverlap(iBackElem);
8073 0 : FFTot.sky[iSky] += firstFlux.sky[iSky];
8074 : // FLSKTot( iSky ) += FLSK( iSky, iBackElem );
8075 : }
8076 0 : firstFlux.sun = fl.sun * complexWinGeom.AveRhoVisOverlap(iBackElem);
8077 0 : FFTot.sun += firstFlux.sun;
8078 0 : FLTot.sun += fl.sun;
8079 :
8080 0 : firstFlux.sunDisk = fl.sunDisk * complexWinGeom.AveRhoVisOverlap(iBackElem);
8081 0 : FFTot.sunDisk += firstFlux.sunDisk;
8082 0 : FLTot.sunDisk += fl.sunDisk;
8083 : }
8084 :
8085 0 : auto const &thisEnclDaylight = dl->enclDaylight(dl->daylightControl(daylightCtrlNum).enclIndex);
8086 0 : Real64 EnclInsideSurfArea = thisEnclDaylight.totInsSurfArea;
8087 :
8088 0 : auto &eintsk = dl->reflIllum(IHR)[iWinCover_Bare];
8089 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8090 0 : eintsk.sky[iSky] = FFTot.sky[iSky] * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8091 : } // for (iSky)
8092 :
8093 0 : dl->reflIllum(IHR)[iWinCover_Bare].sun =
8094 0 : FFTot.sun * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8095 0 : dl->reflIllum(IHR)[iWinCover_Bare].sunDisk =
8096 0 : FFTot.sunDisk * (surf.Area / surfWin.glazedFrac) / (EnclInsideSurfArea * (1.0 - thisEnclDaylight.aveVisDiffReflect));
8097 :
8098 0 : if (allocated(FL)) FL.deallocate();
8099 : // if (allocated(FLSU)) FLSU.deallocate();
8100 : // if (allocated(FLSUdisk)) FLSUdisk.deallocate();
8101 :
8102 0 : if (allocated(FirstFlux)) FirstFlux.deallocate();
8103 : // if (allocated(FirstFluxSU)) FirstFluxSU.deallocate();
8104 : // if (allocated(FirstFluxSUdisk)) FirstFluxSUdisk.deallocate();
8105 :
8106 0 : if (allocated(ElementLuminance)) ElementLuminance.deallocate();
8107 : // if (allocated(ElementLuminanceSun)) ElementLuminanceSun.deallocate();
8108 : // if (allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.deallocate();
8109 0 : }
8110 :
8111 0 : void DayltgDirectIllumComplexFenestration(EnergyPlusData &state,
8112 : int const IWin, // Window index
8113 : int const WinEl, // Current window element counter
8114 : int const IHR, // Hour of day
8115 : int const iRefPoint, // reference point index
8116 : CalledFor const CalledFrom,
8117 : int const MapNum)
8118 : {
8119 :
8120 : // SUBROUTINE INFORMATION:
8121 : // AUTHOR Simon Vidanovic
8122 : // DATE WRITTEN June 2013
8123 :
8124 0 : auto &dl = state.dataDayltg;
8125 0 : auto &s_surf = state.dataSurface;
8126 :
8127 : // Luminances from different sources to the window
8128 0 : Array1D<Illums> ElementLuminance; // sky related luminance at window element (exterior side)
8129 : // Array1D<Real64> ElementLuminanceSun; // sun related luminance at window element (exterior side),
8130 : // exluding beam
8131 : // Array1D<Real64> ElementLuminanceSunDisk; // sun related luminance at window element (exterior side),
8132 : // due to sun beam
8133 :
8134 : int RefPointIndex; // reference point patch number
8135 :
8136 : Real64 dirTrans; // directional BSDF transmittance
8137 : Real64 dOmega; // solid view angle of current element
8138 : Real64 zProjection; // z-axe projection of solid view angle (used to calculate amount of light at horizontal surface
8139 : // laying at reference point)
8140 :
8141 0 : int CurCplxFenState = s_surf->SurfaceWindow(IWin).ComplexFen.CurrentState;
8142 0 : auto &complexWin = state.dataBSDFWindow->ComplexWind(IWin);
8143 0 : int iConst = s_surf->SurfaceWindow(IWin).ComplexFen.State(CurCplxFenState).Konst;
8144 0 : int NIncBasis = complexWin.Geom(CurCplxFenState).Inc.NBasis;
8145 :
8146 0 : if (!allocated(ElementLuminance)) ElementLuminance.allocate(NIncBasis);
8147 0 : ElementLuminance = Illums();
8148 : // if (!allocated(ElementLuminanceSun)) ElementLuminanceSun.dimension(NIncBasis, 0.0);
8149 : // if (!allocated(ElementLuminanceSunDisk)) ElementLuminanceSunDisk.dimension(NIncBasis, 0.0);
8150 :
8151 0 : ComplexFenestrationLuminances(state, IWin, WinEl, NIncBasis, IHR, iRefPoint, ElementLuminance, CalledFrom, MapNum);
8152 :
8153 : // find number of outgoing basis towards current reference point
8154 0 : if (CalledFrom == CalledFor::RefPoint) {
8155 0 : RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).RefPoint(iRefPoint).RefPointIndex(WinEl);
8156 0 : dOmega = complexWin.RefPoint(iRefPoint).SolidAngle(WinEl);
8157 0 : zProjection = complexWin.RefPoint(iRefPoint).SolidAngleVec(WinEl).z;
8158 0 : } else if (CalledFrom == CalledFor::MapPoint) {
8159 0 : assert(MapNum > 0);
8160 0 : RefPointIndex = complexWin.DaylghtGeom(CurCplxFenState).IlluminanceMap(iRefPoint, MapNum).RefPointIndex(WinEl);
8161 0 : dOmega = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngle(WinEl);
8162 0 : zProjection = complexWin.IlluminanceMap(iRefPoint, MapNum).SolidAngleVec(WinEl).z;
8163 : }
8164 :
8165 0 : Illums WinLum = Illums();
8166 0 : Illums EDir = Illums();
8167 :
8168 0 : for (int iIncElem = 1; iIncElem <= NIncBasis; ++iIncElem) {
8169 : // LambdaInc = ComplexWind(IWin)%Geom(CurCplxFenState)%Inc%Lamda(iIncElem)
8170 0 : dirTrans = state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(RefPointIndex, iIncElem);
8171 :
8172 0 : auto const &elemLum = ElementLuminance(iIncElem);
8173 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8174 0 : WinLum.sky[iSky] += dirTrans * elemLum.sky[iSky];
8175 : }
8176 :
8177 0 : WinLum.sun += dirTrans * elemLum.sun;
8178 :
8179 : // For sun disk need to go throug outgoing directions and see which directions actually contain reference point
8180 : }
8181 :
8182 0 : if (zProjection > 0.0) {
8183 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8184 0 : EDir.sky[iSky] = WinLum.sky[iSky] * dOmega * zProjection;
8185 : }
8186 0 : EDir.sun = WinLum.sun * dOmega * zProjection;
8187 : }
8188 :
8189 : // Store solution in global variables
8190 0 : auto &avwlsk = dl->avgWinLum(IHR)[iWinCover_Bare];
8191 0 : auto &edirsk = dl->dirIllum(IHR)[iWinCover_Bare];
8192 :
8193 0 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8194 0 : avwlsk.sky[iSky] += WinLum.sky[iSky];
8195 0 : edirsk.sky[iSky] += EDir.sky[iSky];
8196 : }
8197 :
8198 0 : dl->avgWinLum(IHR)[iWinCover_Bare].sun += WinLum.sun;
8199 0 : dl->dirIllum(IHR)[iWinCover_Bare].sun += EDir.sun;
8200 : // AVWLSUdisk(1,IHR) = AVWLSUdisk(1,IHR) + WinLumSUdisk
8201 0 : } // DayltgDirectIllumComplexFenestration()
8202 :
8203 0 : void DayltgDirectSunDiskComplexFenestration(EnergyPlusData &state,
8204 : int const iWin, // Window index
8205 : int const iHour, // Hour of day
8206 : int const iRefPoint,
8207 : int const NumEl, // Total number of window elements
8208 : Real64 const AZVIEW, // Azimuth of view vector in absolute coord system for
8209 : CalledFor const CalledFrom, // indicate which type of routine called this routine
8210 : int const MapNum)
8211 : {
8212 :
8213 : // SUBROUTINE INFORMATION:
8214 : // AUTHOR Simon Vidanovic
8215 : // DATE WRITTEN June 2013
8216 :
8217 : // PURPOSE OF THIS SUBROUTINE:
8218 : // Calculate illuminance from sun disk for complex fenestration systems
8219 :
8220 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8221 0 : auto &dl = state.dataDayltg;
8222 0 : auto &s_surf = state.dataSurface;
8223 :
8224 0 : assert(CalledFrom != CalledFor::MapPoint || MapNum > 0);
8225 :
8226 0 : auto const &window = s_surf->SurfaceWindow(iWin);
8227 0 : int CurCplxFenState = window.ComplexFen.CurrentState;
8228 0 : int iConst = window.ComplexFen.State(CurCplxFenState).Konst;
8229 :
8230 0 : auto const &complexWindow = state.dataBSDFWindow->ComplexWind(iWin);
8231 0 : auto const &complexWindowGeom = complexWindow.Geom(CurCplxFenState);
8232 0 : auto const &complexWindowDayltgGeom = complexWindow.DaylghtGeom(CurCplxFenState);
8233 0 : int SolBmIndex = complexWindowGeom.SolBmIndex(iHour, state.dataGlobal->TimeStep);
8234 :
8235 0 : Real64 WindowSolidAngleDaylightPoint = (CalledFrom == CalledFor::RefPoint) ? window.refPts(iRefPoint).solidAngWtd : 0.0;
8236 0 : if (WindowSolidAngleDaylightPoint < 1e-6) return;
8237 :
8238 0 : Illums WinLum;
8239 0 : Illums ElemLum;
8240 :
8241 0 : int NTrnBasis = complexWindowGeom.Trn.NBasis;
8242 0 : for (int iTrnElem = 1; iTrnElem <= NTrnBasis; ++iTrnElem) {
8243 : // if ray from any part of the window can reach reference point
8244 : int refPointIntersect = (CalledFrom == CalledFor::RefPoint)
8245 0 : ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPointIntersection(iTrnElem)
8246 0 : : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPointIntersection(iTrnElem);
8247 :
8248 0 : if (refPointIntersect == 0) continue;
8249 :
8250 0 : Real64 PosFac = (CalledFrom == CalledFor::RefPoint) ? complexWindowDayltgGeom.RefPoint(iRefPoint).RefPtIntPosFac(iTrnElem)
8251 0 : : complexWindowDayltgGeom.IlluminanceMap(iRefPoint, MapNum).RefPtIntPosFac(iTrnElem);
8252 :
8253 0 : Real64 RayZ = -complexWindowGeom.sTrn(iTrnElem).z;
8254 :
8255 : // Need to recalculate position factor for dominant direction in case of specular bsdf. Otherwise this will produce
8256 : // very inaccurate results because of position factor of the sun and bsdf pach can vary by lot
8257 0 : if (iTrnElem == SolBmIndex) {
8258 0 : Real64 XR = std::tan(std::abs(Constant::PiOvr2 - AZVIEW - dl->sunAngles.theta) + 0.001);
8259 0 : Real64 YR = std::tan(dl->sunAngles.phi + 0.001);
8260 0 : PosFac = DayltgGlarePositionFactor(XR, YR);
8261 0 : RayZ = dl->sunAngles.sinPhi;
8262 : }
8263 :
8264 0 : if (PosFac == 0.0) continue;
8265 :
8266 0 : Real64 dirTrans = (SolBmIndex > 0) ? state.dataConstruction->Construct(iConst).BSDFInput.VisFrtTrans(iTrnElem, SolBmIndex) : 0.0;
8267 0 : Real64 LambdaTrn = complexWindowGeom.Trn.Lamda(iTrnElem);
8268 0 : Vector3<Real64> V = -complexWindowGeom.sTrn(iTrnElem);
8269 0 : Vector3<Real64> RWin = s_surf->Surface(iWin).Centroid;
8270 0 : Real64 TransBeam = DayltgHitObstruction(state, iHour, iWin, RWin, V);
8271 :
8272 0 : WinLum.sunDisk += (14700.0 * std::sqrt(0.000068 * PosFac) * double(NumEl) / std::pow(WindowSolidAngleDaylightPoint, 0.8)) * dirTrans *
8273 0 : LambdaTrn * TransBeam;
8274 :
8275 0 : ElemLum.sunDisk += RayZ * dirTrans * LambdaTrn * TransBeam;
8276 0 : } // for (iTrnElem)
8277 :
8278 0 : dl->avgWinLum(iHour)[iWinCover_Bare].sunDisk = WinLum.sunDisk;
8279 0 : dl->dirIllum(iHour)[iWinCover_Bare].sunDisk = ElemLum.sunDisk;
8280 : }
8281 :
8282 2509936 : Real64 DayltgSkyLuminance(EnergyPlusData const &state,
8283 : SkyType sky, // Sky type: 1=clear, 2=clear turbid, 3=intermediate, 4=overcast
8284 : Real64 const THSKY, // Azimuth and altitude of sky element (radians)
8285 : Real64 const PHSKY)
8286 : {
8287 :
8288 : // SUBROUTINE INFORMATION:
8289 : // AUTHOR Fred Winkelmann
8290 : // DATE WRITTEN July 1997
8291 :
8292 : // PURPOSE OF THIS SUBROUTINE:
8293 : // Called by CalcDayltgCoefficients, DayltgExtHorizIllum AND DayltgInterReflectedIllum. gives
8294 : // luminance in cd/m2 for four different sky types, as described in R.Perez, P.Ineichen,
8295 : // R.Seals, J.Michalsky and R.Stewart, "Modeling daylight availability and irradiance
8296 : // components from direct and global irradiance," Solar Energy 44, 1990, 271-289.
8297 : // The luminance distributions in this routine are normalized such that
8298 : // the zenith luminance is 1.0, i.e., DayltgSkyLuminance =
8299 : // (sky luminance at THSKY, PHSKY)/(zenith luminance), which is dimensionless.
8300 : // The sky types are:
8301 : // 1. Standard CIE clear sky
8302 : // 2. Standard CIE high-turbidity clear sky
8303 : // 3. CIE intermediate sky
8304 : // 4. CIE overcast sky
8305 :
8306 : // METHODOLOGY EMPLOYED:
8307 :
8308 : // REFERENCES:
8309 : // Based on DOE-2.1E subroutine DSKYLU, which did only clear and overcast skies.
8310 :
8311 : // OTHER NOTES:
8312 : // THSKY ranges from 0 to 2Pi starting with 0 directly East and rotating clockwise.
8313 : // PHSKY ranges from 0 to Pi starting with 0 at the horizon and Pi/2 at the zenith.
8314 :
8315 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
8316 2509936 : auto const &dl = state.dataDayltg;
8317 :
8318 2509936 : Real64 G = 0.0; // Angle between sun and element of sky (radians)
8319 2509936 : Real64 COSG = 0.0; // Cosine of G
8320 :
8321 2509936 : Real64 SPHSKY = max(std::sin(PHSKY), 0.01); // Prevent floating point underflows
8322 2509936 : Real64 Z = Constant::PiOvr2 - dl->sunAngles.phi;
8323 2509936 : if (sky != SkyType::Overcast) { // Following not needed for overcast sky
8324 1882452 : COSG = SPHSKY * dl->sunAngles.sinPhi + std::cos(PHSKY) * dl->sunAngles.cosPhi * std::cos(THSKY - dl->sunAngles.theta);
8325 1882452 : COSG = max(DataPrecisionGlobals::constant_minusone, min(COSG, 1.0)); // Prevent out of range due to roundoff
8326 1882452 : G = std::acos(COSG);
8327 : }
8328 :
8329 2509936 : switch (sky) {
8330 627484 : case SkyType::Clear: {
8331 627484 : Real64 Z1 = 0.910 + 10.0 * std::exp(-3.0 * G) + 0.45 * COSG * COSG;
8332 627484 : Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
8333 627484 : Real64 Z3 = 0.27385 * (0.91 + 10.0 * std::exp(-3.0 * Z) + 0.45 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
8334 627484 : return Z1 * Z2 / Z3;
8335 :
8336 : } break;
8337 627484 : case SkyType::ClearTurbid: {
8338 627484 : Real64 Z1 = 0.856 + 16.0 * std::exp(-3.0 * G) + 0.3 * COSG * COSG;
8339 627484 : Real64 Z2 = 1.0 - std::exp(-0.32 / SPHSKY);
8340 627484 : Real64 Z3 = 0.27385 * (0.856 + 16.0 * std::exp(-3.0 * Z) + 0.3 * dl->sunAngles.sinPhi * dl->sunAngles.sinPhi);
8341 627484 : return Z1 * Z2 / Z3;
8342 :
8343 : } break;
8344 :
8345 627484 : case SkyType::Intermediate: {
8346 627484 : 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;
8347 627484 : Real64 Z2 = std::exp(-G * 0.563 * ((dl->sunAngles.phi - 0.008) * (PHSKY + 1.059) + 0.812));
8348 627484 : Real64 Z3 = 0.99224 * std::sin(2.6 * dl->sunAngles.phi + 0.316) + 2.73852;
8349 627484 : Real64 Z4 = std::exp(-Z * 0.563 * ((dl->sunAngles.phi - 0.008) * 2.6298 + 0.812));
8350 627484 : return Z1 * Z2 / (Z3 * Z4);
8351 : } break;
8352 627484 : case SkyType::Overcast: {
8353 627484 : return (1.0 + 2.0 * SPHSKY) / 3.0;
8354 : } break;
8355 0 : default:
8356 0 : assert(false);
8357 : return 0.0;
8358 : }
8359 : }
8360 :
8361 816 : Real64 ProfileAngle(EnergyPlusData &state,
8362 : int const SurfNum, // Surface number
8363 : Vector3<Real64> const &CosDirSun, // Solar direction cosines
8364 : DataWindowEquivalentLayer::Orientation const HorOrVert // If HORIZONTAL, calculates ProfileAngHor
8365 : )
8366 : {
8367 :
8368 : // SUBROUTINE INFORMATION:
8369 : // AUTHOR Fred Winkelmann
8370 : // DATE WRITTEN May 2001
8371 :
8372 : // PURPOSE OF THIS SUBROUTINE:
8373 : // Calculates profile angle for a surface.
8374 :
8375 : // Locals
8376 : // SUBROUTINE ARGUMENT DEFINITIONS:
8377 : // For HorOrVert = HORIZONTAL,
8378 : // this is the incidence angle in a plane that is normal to the window
8379 : // and parallel to the Y-axis of the window (the axis along
8380 : // which the height of the window is measured).
8381 : // For HorOrVert = VERTICAL,
8382 : // this is the incidence angle in a plane that is normal to the window
8383 : // and parallel to the X-axis of the window (the axis along
8384 : // which the width of the window is measured).
8385 : // If VERTICAL, calculates ProfileAngVert
8386 816 : auto &s_surf = state.dataSurface;
8387 :
8388 816 : auto const &surf = s_surf->Surface(SurfNum);
8389 816 : if (HorOrVert == DataWindowEquivalentLayer::Orientation::Horizontal) { // Profile angle for horizontal structures
8390 646 : Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad; // Window elevation: angle between outward normal and horizontal (radians)
8391 646 : Real64 AzimWin = (90.0 - surf.Azimuth) * Constant::DegToRad; // Window azimuth (radians)
8392 646 : Real64 ElevSun = std::asin(CosDirSun.z); // Sun elevation; angle between sun and horizontal (radians)
8393 646 : Real64 AzimSun = std::atan2(CosDirSun.y, CosDirSun.x); // Sun azimuth (radians)
8394 646 : return std::atan(std::sin(ElevSun) / std::abs(std::cos(ElevSun) * std::cos(AzimWin - AzimSun))) - ElevWin;
8395 : } else { // Profile angle for vertical structures
8396 170 : Real64 ElevWin = Constant::PiOvr2 - surf.Tilt * Constant::DegToRad;
8397 170 : Real64 AzimWin = surf.Azimuth * Constant::DegToRad; // 7952
8398 170 : Real64 AzimSun = std::atan2(CosDirSun.x, CosDirSun.y); // 7952
8399 :
8400 : Real64 ProfileAng;
8401 170 : if (std::abs(ElevWin) < 0.1) { // Near-vertical window
8402 170 : ProfileAng = AzimWin - AzimSun; // CR7952 allow sign changes.
8403 : } else {
8404 0 : Vector3<Real64> WinNorm = surf.OutNormVec; // Window outward normal unit vector
8405 0 : Real64 ThWin = AzimWin - Constant::PiOvr2;
8406 0 : Real64 const sin_ElevWin = std::sin(ElevWin);
8407 : // Cross product of WinNorm and vector along window baseline
8408 0 : Vector3<Real64> WinNormCrossBase = {-sin_ElevWin * std::cos(ThWin), sin_ElevWin * std::sin(ThWin), std::cos(ElevWin)};
8409 : // Projection of sun vector onto plane (perpendicular to window plane) determined
8410 : // by WinNorm and vector along baseline of window
8411 0 : Vector3<Real64> SunPrime = CosDirSun - WinNormCrossBase * dot(CosDirSun, WinNormCrossBase);
8412 0 : ProfileAng = std::abs(std::acos(dot(WinNorm, SunPrime) / SunPrime.magnitude()));
8413 : // CR7952 correct sign of result for vertical slats
8414 0 : if ((AzimWin - AzimSun) < 0.0) ProfileAng = -1.0 * ProfileAng;
8415 0 : }
8416 : // Constrain to 0 to pi
8417 170 : if (ProfileAng > Constant::Pi) ProfileAng = 2.0 * Constant::Pi - ProfileAng;
8418 170 : return ProfileAng;
8419 : }
8420 : }
8421 :
8422 0 : void DayltgClosestObstruction(EnergyPlusData &state,
8423 : Vector3<Real64> const &RecPt, // Point on window from which ray emanates (m)
8424 : Vector3<Real64> const &RayVec, // Unit vector along ray pointing away from window (m)
8425 : int &NearestHitSurfNum, // Surface number of nearest obstruction that is hit by ray;
8426 : Vector3<Real64> &NearestHitPt // Ray's hit point on nearest obstruction (m)
8427 : )
8428 : {
8429 :
8430 : // SUBROUTINE INFORMATION:
8431 : // AUTHOR Fred Winkelmann
8432 : // DATE WRITTEN November 2003
8433 :
8434 : // PURPOSE OF THIS SUBROUTINE:
8435 : // Determines surface number and hit point of closest exterior obstruction hit
8436 : // by a ray from a window. If no obstruction is hit, NearestHitSurfNum = 0.
8437 :
8438 : // Locals
8439 : // SUBROUTINE ARGUMENT DEFINITIONS:
8440 : // = 0 if no obstruction is hit.
8441 0 : auto &s_surf = state.dataSurface;
8442 :
8443 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8444 0 : Vector3<Real64> HitPt; // Hit point on an obstruction (m)
8445 : bool hit; // True iff obstruction is hit
8446 :
8447 0 : NearestHitSurfNum = 0;
8448 0 : Real64 NearestHitDistance_sq(std::numeric_limits<Real64>::max()); // Distance squared from receiving point to nearest hit point for a ray (m^2)
8449 0 : NearestHitPt = 0.0;
8450 0 : if (s_surf->TotSurfaces < octreeCrossover) { // Linear search through surfaces
8451 :
8452 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
8453 : // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
8454 0 : hit = PierceSurface(state, ObsSurfNum, RecPt, RayVec, HitPt);
8455 0 : if (!hit) // Ray pierces surface
8456 0 : continue;
8457 :
8458 : // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
8459 : // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
8460 0 : if ((s_surf->Surface(ObsSurfNum).Class == SurfaceClass::Window) && (s_surf->Surface(ObsSurfNum).BaseSurf == NearestHitSurfNum)) {
8461 0 : NearestHitSurfNum = ObsSurfNum;
8462 : } else {
8463 : // Distance squared from receiving point to hit point
8464 0 : Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
8465 : // Reset NearestHitSurfNum and NearestHitDistance_sq if this hit point is closer than previous closest
8466 0 : if (HitDistance_sq < NearestHitDistance_sq) {
8467 0 : NearestHitDistance_sq = HitDistance_sq;
8468 0 : NearestHitSurfNum = ObsSurfNum;
8469 0 : NearestHitPt = HitPt;
8470 : }
8471 : } // End of check if obstruction was hit
8472 : } // for (ObsSurfNum)
8473 :
8474 : } else { // Surface octree search
8475 :
8476 0 : SurfaceData const *nearestHitSurface(nullptr);
8477 :
8478 : // Lambda function for the octree to test for surface hit
8479 0 : auto surfaceHit = [&s_surf, &RecPt, &RayVec, &hit, &NearestHitDistance_sq, &nearestHitSurface, &NearestHitPt](SurfaceData const &surface) {
8480 0 : if (surface.IsShadowPossibleObstruction) {
8481 0 : Vector3<Real64> HitPt;
8482 : // Determine if this ray hits the surface and, if so, get the distance from the receiving point to the hit
8483 0 : hit = PierceSurface(surface, RecPt, RayVec, HitPt); // Check if ray pierces surface
8484 0 : if (!hit) return;
8485 :
8486 : // If obstruction is a window and its base surface is the nearest obstruction hit so far set nearestHitSurface to this window
8487 : // Note that in this case NearestHitDistance_sq has already been calculated, so does not have to be recalculated
8488 0 : if ((surface.Class == SurfaceClass::Window) && (surface.BaseSurf > 0) && (&s_surf->Surface(surface.BaseSurf) == nearestHitSurface)) {
8489 0 : nearestHitSurface = &surface;
8490 : } else {
8491 : // Distance squared from receiving point to hit point
8492 0 : Real64 const HitDistance_sq(distance_squared(HitPt, RecPt));
8493 : // Reset nearestHitSurface and NearestHitDistance_sq if this hit point is closer than previous closest
8494 0 : if (HitDistance_sq < NearestHitDistance_sq) {
8495 0 : NearestHitDistance_sq = HitDistance_sq;
8496 0 : nearestHitSurface = &surface;
8497 0 : NearestHitPt = HitPt;
8498 : }
8499 : } // End of check if obstruction was hit
8500 0 : }
8501 0 : };
8502 :
8503 : // Process octree surface candidates
8504 0 : Vector3<Real64> const RayVec_inv(SurfaceOctreeCube::safe_inverse(RayVec));
8505 0 : state.dataHeatBalMgr->surfaceOctree.processSurfaceRayIntersectsCube(RecPt, RayVec, RayVec_inv, surfaceHit);
8506 0 : if (nearestHitSurface != nullptr) { // Find surface number: This is inefficient: Improve when surfaces know their own number
8507 0 : for (int i = 1; i <= s_surf->TotSurfaces; ++i) {
8508 0 : if (&s_surf->Surface(i) == nearestHitSurface) {
8509 0 : NearestHitSurfNum = i;
8510 0 : break;
8511 : }
8512 : }
8513 0 : assert(NearestHitSurfNum != 0);
8514 : }
8515 0 : }
8516 0 : } // DayltgClosestObstruction()
8517 :
8518 0 : Real64 DayltgSurfaceLumFromSun(EnergyPlusData &state,
8519 : int const IHR, // Hour number
8520 : Vector3<Real64> const &Ray, // Ray from window to reflecting surface (m)
8521 : int const ReflSurfNum, // Number of surface for which luminance is being calculated
8522 : Vector3<Real64> const &ReflHitPt // Point on ReflSurfNum for luminance calculation (m)
8523 : )
8524 : {
8525 :
8526 : // SUBROUTINE INFORMATION:
8527 : // AUTHOR Fred Winkelmann
8528 : // DATE WRITTEN November 2003
8529 :
8530 : // PURPOSE OF THIS SUBROUTINE:
8531 : // Calculates exterior surface luminance due to beam solar diffuse reflection.
8532 :
8533 : // Locals
8534 : // SUBROUTINE ARGUMENT DEFINITIONS:
8535 : // beam normal illuminance (cd/m2)
8536 :
8537 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8538 0 : Vector3<Real64> SurfaceLumFromSunReflNorm; // Unit normal to reflecting surface (m)
8539 0 : Vector3<Real64> SurfaceLumFromSunObsHitPt; // Hit point on obstruction (m)
8540 : bool hitObs; // True iff obstruction is hit
8541 : Real64 DiffVisRefl; // Diffuse visible reflectance of ReflSurfNum
8542 :
8543 0 : auto &s_surf = state.dataSurface;
8544 : // Skip daylighting shelves since reflection from these is separately calculated
8545 0 : if (s_surf->SurfDaylightingShelfInd(ReflSurfNum) > 0) return 0.0;
8546 :
8547 0 : auto const &reflSurf = s_surf->Surface(ReflSurfNum);
8548 :
8549 : // Normal to reflecting surface in hemisphere containing window element
8550 0 : SurfaceLumFromSunReflNorm = reflSurf.OutNormVec;
8551 0 : if (reflSurf.IsShadowing) {
8552 0 : if (dot(SurfaceLumFromSunReflNorm, Ray) > 0.0) {
8553 0 : SurfaceLumFromSunReflNorm *= -1.0;
8554 : }
8555 : }
8556 : // Cosine of angle of incidence of sun at HitPt if sun were to reach HitPt
8557 0 : Vector3<Real64> const SUNCOS_IHR = s_surf->SurfSunCosHourly(IHR);
8558 0 : Real64 CosIncAngAtHitPt = dot(SurfaceLumFromSunReflNorm, SUNCOS_IHR);
8559 : // Require that the sun be in front of this surface relative to window element
8560 0 : if (CosIncAngAtHitPt <= 0.0) return 0.0; // Sun is in back of reflecting surface
8561 : // Sun reaches ReflHitPt if vector from ReflHitPt to sun is unobstructed
8562 0 : hitObs = false;
8563 0 : for (int ObsSurfNum : s_surf->AllShadowPossObstrSurfaceList) {
8564 : // Exclude as a possible obstructor ReflSurfNum and its base surface (if it has one)
8565 0 : if (ObsSurfNum == ReflSurfNum || ObsSurfNum == reflSurf.BaseSurf) continue;
8566 0 : hitObs = PierceSurface(state, ObsSurfNum, ReflHitPt, SUNCOS_IHR, SurfaceLumFromSunObsHitPt);
8567 0 : if (hitObs) break;
8568 : }
8569 :
8570 0 : if (hitObs) return 0.0; // Obstruction was hit, blocking s auto surfaceHit = [&state, &GroundHitPtun
8571 : // Obstruction was not hit; sun reaches ReflHitPt.
8572 : // Calculate luminance at ReflHitPt due to beam solar reflection (for unit beam normal illuminance)
8573 0 : if (reflSurf.IsShadowing) {
8574 0 : DiffVisRefl = s_surf->SurfShadowDiffuseVisRefl(ReflSurfNum);
8575 : // Note that if the shadowing surface has a non-zero glazing fraction (e.g., neighboring bldg) that the above is
8576 : // (1 - glazing fraction) * (vis refl of opaque part of shadowing surface); specular reflection is
8577 : // excluded in this value of DiffVisRefl.
8578 : } else { // Exterior building surface
8579 0 : if (!state.dataConstruction->Construct(reflSurf.Construction).TypeIsWindow) {
8580 0 : DiffVisRefl = 1.0 - state.dataConstruction->Construct(reflSurf.Construction).OutsideAbsorpSolar;
8581 : } else {
8582 : // Window; assume bare so no beam-to-diffuse reflection
8583 0 : DiffVisRefl = 0.0;
8584 : }
8585 : }
8586 0 : return CosIncAngAtHitPt * DiffVisRefl / Constant::Pi;
8587 0 : }
8588 :
8589 88617 : void DayltgInteriorMapIllum(EnergyPlusData &state)
8590 : {
8591 :
8592 : // *****super modified version of DayltgInteriorIllum by Peter Graham Ellis
8593 : // *****removes all control code, just calculates illum with previously determined control settings
8594 : // *****this should be packaged into a subroutine called from 2 places
8595 :
8596 : // SUBROUTINE INFORMATION:
8597 : // AUTHOR Fred Winkelmann
8598 : // DATE WRITTEN July 1997
8599 : // MODIFIED March 2000, FW: interpolate clear-sky daylight factors using
8600 : // HourOfDay/WeightNow and NextHour/WeightNextHour. Previously
8601 : // only HourOfDay was used
8602 : // Jan 2001, FW: interpolate in slat angle for windows with blinds
8603 : // that have movable slats
8604 : // Dec 2003, FW: fix bug--even though between-glass shade/blind is on
8605 : // daylight illum at ref pt was calculated as though it was off
8606 : // June 2009, TH: modified for thermochromic windows
8607 : // March 2010, TH: fix bug (CR 8057) for electrochromic windows
8608 : // RE-ENGINEERED na
8609 :
8610 : // PURPOSE OF THIS SUBROUTINE:
8611 : // Using daylighting factors and exterior illuminance, determine
8612 : // the current-hour interior daylight illuminance and glare index
8613 : // at each reference point in a space.
8614 :
8615 : // Called by InitSurfaceHeatBalance.
8616 :
8617 : // REFERENCES:
8618 : // Based on DOE-2.1E subroutine DINTIL.
8619 88617 : auto &dl = state.dataDayltg;
8620 :
8621 : // Locals
8622 88617 : Array1D<Real64> daylight_illum;
8623 :
8624 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8625 : int iSky1; // Sky type index values for averaging two sky types
8626 : int iSky2;
8627 : Real64 SkyWeight; // Weighting factor used to average two different sky types
8628 : Real64 HorIllSkyFac; // Ratio between horizontal illuminance from sky horizontal irradiance and
8629 : // luminous efficacy and horizontal illuminance from averaged sky
8630 :
8631 88617 : if (state.dataGlobal->WarmupFlag) return;
8632 :
8633 9388 : auto &s_surf = state.dataSurface;
8634 :
8635 9388 : daylight_illum.allocate(MaxMapRefPoints);
8636 :
8637 : // Initialize reference point illuminance and window background luminance
8638 :
8639 9447 : for (auto &thisMap : dl->illumMaps) {
8640 59 : int enclNum = thisMap.enclIndex;
8641 59 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
8642 :
8643 59 : int NREFPT = thisMap.TotalMapRefPoints; // Number of daylighting map reference points
8644 :
8645 59 : daylight_illum = 0.0;
8646 :
8647 59 : if (state.dataEnvrn->SkyClearness > 3.0) { // Sky is average of clear and clear turbid
8648 50 : SkyWeight = min(1.0, (state.dataEnvrn->SkyClearness - 3.0) / 3.0);
8649 50 : iSky1 = (int)SkyType::Clear;
8650 50 : iSky2 = (int)SkyType::ClearTurbid;
8651 9 : } else if (state.dataEnvrn->SkyClearness > 1.2) { // Sky is average of clear turbid and intermediate
8652 8 : SkyWeight = (state.dataEnvrn->SkyClearness - 1.2) / 1.8;
8653 8 : iSky1 = (int)SkyType::ClearTurbid;
8654 8 : iSky2 = (int)SkyType::Intermediate;
8655 : } else { // Sky is average of intermediate and overcast
8656 1 : SkyWeight = min(1.0, max(0.0, (state.dataEnvrn->SkyClearness - 1.0) / 0.2, (state.dataEnvrn->SkyBrightness - 0.05) / 0.4));
8657 1 : iSky1 = (int)SkyType::Intermediate;
8658 1 : iSky2 = (int)SkyType::Overcast;
8659 : }
8660 :
8661 : // First loop over windows in this space.
8662 : // Find contribution of each window to the daylight illum
8663 : // and to the glare numerator at each reference point.
8664 : // Use shading flags set in WindowShadingManager.
8665 :
8666 59 : auto &daylFacHrCurr = thisMap.daylFac[state.dataGlobal->HourOfDay];
8667 59 : auto &daylFacHrPrev = thisMap.daylFac[state.dataGlobal->PreviousHour];
8668 :
8669 118 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
8670 59 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
8671 :
8672 : // Added TH 6/29/2009 for thermochromic windows
8673 59 : Real64 VTRatio = 1.0;
8674 59 : if (NREFPT > 0) {
8675 59 : int IConst = s_surf->Surface(IWin).Construction;
8676 59 : auto const &construction = state.dataConstruction->Construct(IConst);
8677 59 : if (construction.isTCWindow) {
8678 : // For thermochromic windows, daylight and glare factors are always calculated
8679 : // based on the master construction. They need to be adjusted by the VTRatio, including:
8680 : // ZoneDaylight()%DaylIllFacSky, DaylIllFacSun, DaylIllFacSunDisk; DaylBackFacSky,
8681 : // DaylBackFacSun, DaylBackFacSunDisk, DaylSourceFacSky, DaylSourceFacSun, DaylSourceFacSunDisk
8682 0 : Real64 VTNow = Window::POLYF(1.0, construction.TransVisBeamCoef);
8683 0 : Real64 VTMaster = Window::POLYF(1.0, state.dataConstruction->Construct(construction.TCMasterConstrNum).TransVisBeamCoef);
8684 0 : VTRatio = VTNow / VTMaster;
8685 : }
8686 : }
8687 :
8688 59 : Real64 wgtThisHr = state.dataGlobal->WeightNow;
8689 59 : Real64 wgtPrevHr = state.dataGlobal->WeightPreviousHour;
8690 :
8691 59 : std::array<Dayltg::Illums, (int)DataSurfaces::WinCover::Num> DFHR; // Sky daylight factor for sky type, bare/shaded window
8692 :
8693 59 : auto &dfhr = DFHR[iWinCover_Bare];
8694 59 : auto &dfhrSh = DFHR[iWinCover_Shaded];
8695 :
8696 59 : auto &surfShade = s_surf->surfShades(IWin);
8697 : // Loop over reference points
8698 5959 : for (int ILB = 1; ILB <= NREFPT; ++ILB) {
8699 : // if (ILB != 5) continue;
8700 5900 : auto const &illSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Bare];
8701 5900 : auto const &illSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Bare];
8702 5900 : auto const &illShSkyCurr = daylFacHrCurr(loop, ILB)[iWinCover_Shaded];
8703 5900 : auto const &illShSkyPrev = daylFacHrPrev(loop, ILB)[iWinCover_Shaded];
8704 :
8705 : // Daylight factors for current sun position
8706 29500 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8707 : // ===Bare window===
8708 23600 : dfhr.sky[iSky] = VTRatio * (wgtThisHr * illSkyCurr.sky[iSky] + wgtPrevHr * illSkyPrev.sky[iSky]);
8709 :
8710 70800 : if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
8711 47200 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
8712 :
8713 : // ===Shaded window===
8714 : // Shade, screen, blind with fixed slats, or diffusing glass
8715 0 : dfhrSh.sky[iSky] = VTRatio * (wgtThisHr * illShSkyCurr.sky[iSky] + wgtPrevHr * illShSkyPrev.sky[iSky]);
8716 : } // End of check if window is shaded or has diffusing glass
8717 : } // for (iSky)
8718 :
8719 : // Sun daylight factor for bare/shaded window
8720 5900 : std::array<Illums, (int)DataSurfaces::WinCover::Num> tmpDFHR;
8721 11800 : tmpDFHR[iWinCover_Bare].sun =
8722 5900 : VTRatio * (wgtThisHr * (illSkyCurr.sun + illSkyCurr.sunDisk) + wgtPrevHr * (illSkyPrev.sun + illSkyPrev.sunDisk));
8723 :
8724 17700 : if ((s_surf->SurfWinWindowModelType(IWin) != WindowModel::BSDF) &&
8725 11800 : (IS_SHADED(s_surf->SurfWinShadingFlag(IWin)) || s_surf->SurfWinSolarDiffusing(IWin))) {
8726 :
8727 : // ===Shaded window===
8728 : // Shade, screen, blind with fixed slats, or diffusing glass
8729 0 : tmpDFHR[iWinCover_Shaded].sun = VTRatio * (wgtThisHr * illShSkyCurr.sun + wgtPrevHr * illShSkyPrev.sun);
8730 :
8731 0 : if (!surfShade.blind.slatBlockBeam) {
8732 0 : tmpDFHR[iWinCover_Shaded].sun += VTRatio * (wgtThisHr * illShSkyCurr.sunDisk + wgtPrevHr * illShSkyPrev.sunDisk);
8733 : }
8734 : } // End of check if window is shaded or has diffusing glass
8735 :
8736 : // Get illuminance at ref point from bare and shaded window by
8737 : // multiplying daylight factors by exterior horizontal illuminance
8738 :
8739 : // Adding 0.001 in the following prevents zero DayltgInteriorMapIllumHorIllSky in early morning or late evening when sun
8740 : // is up in the present time step but GILSK(ISky,HourOfDay) and GILSK(ISky,NextHour) are both zero.
8741 5900 : Illums tmpHorIll; // Horizontal illuminance for different sky types
8742 5900 : auto const &gilCurr = dl->horIllum[state.dataGlobal->HourOfDay];
8743 5900 : auto const &gilPrev = dl->horIllum[state.dataGlobal->PreviousHour];
8744 29500 : for (int iSky = (int)SkyType::Clear; iSky < (int)SkyType::Num; ++iSky) {
8745 23600 : tmpHorIll.sky[iSky] = wgtThisHr * gilCurr.sky[iSky] + wgtPrevHr * gilPrev.sky[iSky] + 0.001;
8746 : }
8747 :
8748 : // HISKF is current time step horizontal illuminance from sky, calculated in DayltgLuminousEfficacy,
8749 : // which is called in WeatherManager. HISUNF is current time step horizontal illuminance from sun,
8750 : // also calculated in DayltgLuminousEfficacy.
8751 5900 : HorIllSkyFac = state.dataEnvrn->HISKF / ((1.0 - SkyWeight) * tmpHorIll.sky[iSky2] + SkyWeight * tmpHorIll.sky[iSky1]);
8752 :
8753 11800 : for (int iWinCover = 0; iWinCover < (int)WinCover::Num; ++iWinCover) {
8754 11800 : if (iWinCover == iWinCover_Shaded) {
8755 5900 : if (s_surf->SurfWinWindowModelType(IWin) == WindowModel::BSDF) break;
8756 5900 : if (NOT_SHADED(s_surf->SurfWinShadingFlag(IWin)) && !s_surf->SurfWinSolarDiffusing(IWin)) break;
8757 : }
8758 5900 : auto const &dfhr3 = DFHR[iWinCover];
8759 :
8760 11800 : thisMap.refPts(ILB).winLums(loop)[iWinCover] = tmpDFHR[iWinCover].sun * state.dataEnvrn->HISUNF +
8761 5900 : HorIllSkyFac * (dfhr3.sky[iSky1] * SkyWeight * tmpHorIll.sky[iSky1] +
8762 5900 : dfhr3.sky[iSky2] * (1.0 - SkyWeight) * tmpHorIll.sky[iSky2]);
8763 : }
8764 :
8765 : } // End of reference point loop
8766 : } // End of first loop over windows
8767 :
8768 : // Second loop over windows. Find total daylight illuminance
8769 : // and background luminance for each ref pt from all windows in
8770 : // the space. Use shading flags.
8771 :
8772 118 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
8773 59 : int IWin = thisEnclDaylight.DayltgExtWinSurfNums(loop);
8774 59 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
8775 :
8776 59 : WinCover winCover = findWinShadingStatus(state, IWin);
8777 :
8778 : // CR 8057. 3/17/2010.
8779 : // Switchable windows may be in partially switched state rather than fully dark state
8780 59 : Real64 VTMULT = 1.0;
8781 :
8782 59 : int ICtrl = s_surf->Surface(IWin).activeWindowShadingControl;
8783 59 : if (s_surf->Surface(IWin).HasShadeControl) {
8784 0 : if (s_surf->WindowShadingControl(ICtrl).shadingControlType == WindowShadingControlType::MeetDaylIlumSetp &&
8785 0 : s_surf->SurfWinShadingFlag(IWin) == WinShadingType::SwitchableGlazing) {
8786 : // switchable windows in partial or fully switched state,
8787 : // get its intermediate VT calculated in DayltgInteriorIllum
8788 0 : int IConstShaded = s_surf->Surface(IWin).activeShadedConstruction;
8789 0 : if (IConstShaded > 0) {
8790 : // Visible transmittance (VT) of electrochromic (EC) windows in fully dark state
8791 0 : Real64 VTDark = Window::POLYF(1.0, state.dataConstruction->Construct(IConstShaded).TransVisBeamCoef) * surfWin.glazedFrac;
8792 0 : if (VTDark > 0) VTMULT = s_surf->SurfWinVisTransSelected(IWin) / VTDark;
8793 : }
8794 : }
8795 : }
8796 :
8797 5959 : for (int IL = 1; IL <= NREFPT; ++IL) {
8798 : // Determine if illuminance contribution is from bare or shaded window
8799 5900 : daylight_illum(IL) += VTMULT * thisMap.refPts(IL).winLums(loop)[(int)winCover];
8800 : }
8801 : } // End of second window loop
8802 :
8803 : // Variables for reporting
8804 5959 : for (int IL = 1; IL <= NREFPT; ++IL) {
8805 5900 : thisMap.refPts(IL).lums[iLum_Illum] = max(daylight_illum(IL), 0.0);
8806 : }
8807 : } // End loop over maps
8808 88617 : } // DayltgInteriorMapIllum()
8809 :
8810 61 : void ReportIllumMap(EnergyPlusData &state, int const MapNum)
8811 : {
8812 :
8813 : // SUBROUTINE INFORMATION:
8814 : // AUTHOR Peter Ellis
8815 : // DATE WRITTEN May 2003
8816 :
8817 : // PURPOSE OF THIS SUBROUTINE:
8818 : // This subroutine produces the Daylighting Illuminance Map output. Each separate map (by zone)
8819 : // is placed on a temporary file and later (see CloseReportIllumMaps) coallesced into a single
8820 : // output file.
8821 :
8822 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8823 :
8824 61 : std::string MapNoString;
8825 61 : auto &dl = state.dataDayltg;
8826 :
8827 61 : if (dl->ReportIllumMap_firstTime) {
8828 1 : dl->ReportIllumMap_firstTime = false;
8829 1 : dl->FirstTimeMaps.dimension((int)dl->illumMaps.size(), true);
8830 1 : dl->EnvrnPrint.dimension((int)dl->illumMaps.size(), true);
8831 1 : dl->SavedMnDy.allocate((int)dl->illumMaps.size());
8832 : }
8833 :
8834 61 : auto &illumMap = dl->illumMaps(MapNum);
8835 :
8836 61 : if (dl->FirstTimeMaps(MapNum)) {
8837 :
8838 2 : dl->FirstTimeMaps(MapNum) = false;
8839 :
8840 2 : auto openMapFile = [&](const fs::path &filePath) -> InputOutputFile & {
8841 2 : auto &outputFile = *illumMap.mapFile;
8842 2 : outputFile.filePath = FileSystem::appendSuffixToPath(filePath, fmt::to_string(MapNum));
8843 2 : outputFile.ensure_open(state, "ReportIllumMap");
8844 2 : return outputFile;
8845 2 : };
8846 2 : if (dl->MapColSep == DataStringGlobals::CharTab) {
8847 0 : if (!openMapFile(state.files.outputMapTabFilePath).good()) return;
8848 : // CommaDelimited = false; //Unused Set but never used
8849 2 : } else if (dl->MapColSep == DataStringGlobals::CharComma) {
8850 1 : if (!openMapFile(state.files.outputMapCsvFilePath).good()) return;
8851 : // CommaDelimited = true; //Unused Set but never used
8852 : } else {
8853 1 : if (!openMapFile(state.files.outputMapTxtFilePath).good()) return;
8854 : // CommaDelimited = false; //Unused Set but never used
8855 : }
8856 :
8857 2 : dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
8858 :
8859 2 : illumMap.Name = format("{} at {:.2R}m", illumMap.Name, illumMap.Z);
8860 : }
8861 61 : if (dl->SavedMnDy(MapNum) != state.dataEnvrn->CurMnDyHr.substr(0, 5)) {
8862 0 : dl->EnvrnPrint(MapNum) = true;
8863 0 : dl->SavedMnDy(MapNum) = state.dataEnvrn->CurMnDyHr.substr(0, 5);
8864 : }
8865 :
8866 61 : illumMap.pointsHeader = "";
8867 61 : int rCount = 0;
8868 122 : for (auto &thisDayltgCtrl : dl->daylightControl) {
8869 61 : if (thisDayltgCtrl.zoneIndex != illumMap.zoneIndex) continue;
8870 :
8871 184 : for (int R = 1; R <= thisDayltgCtrl.TotalDaylRefPoints; ++R) {
8872 123 : ++rCount;
8873 123 : auto const &refPt = thisDayltgCtrl.refPts(R);
8874 123 : illumMap.pointsHeader += format(" RefPt{}=({:.2R}:{:.2R}:{:.2R}),", rCount, refPt.absCoords.x, refPt.absCoords.y, refPt.absCoords.z);
8875 : }
8876 : }
8877 :
8878 61 : if (rCount > 0) {
8879 : // Remove trailing comma
8880 61 : illumMap.pointsHeader.pop_back();
8881 : }
8882 61 : if (dl->EnvrnPrint(MapNum)) {
8883 1 : WriteDaylightMapTitle(
8884 1 : state, MapNum, *illumMap.mapFile, illumMap.Name, state.dataEnvrn->EnvironmentName, illumMap.zoneIndex, illumMap.pointsHeader, illumMap.Z);
8885 1 : dl->EnvrnPrint(MapNum) = false;
8886 : }
8887 :
8888 61 : if (!state.dataGlobal->WarmupFlag) {
8889 60 : if (state.dataGlobal->TimeStep == state.dataGlobal->TimeStepsInHour) { // Report only hourly
8890 :
8891 16 : int linelen = 0;
8892 : // Write X scale column header
8893 16 : std::string mapLine = format(" {} {:02}:00", dl->SavedMnDy(MapNum), state.dataGlobal->HourOfDay);
8894 16 : if (illumMap.HeaderXLineLengthNeeded) linelen = int(len(mapLine));
8895 16 : int RefPt = 1;
8896 176 : for (int X = 1; X <= illumMap.Xnum; ++X) {
8897 : const std::string AddXorYString =
8898 160 : format("{}({:.2R};{:.2R})=", dl->MapColSep, illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
8899 160 : if (illumMap.HeaderXLineLengthNeeded) linelen += int(len(AddXorYString));
8900 160 : mapLine += AddXorYString;
8901 160 : ++RefPt;
8902 160 : } // X
8903 :
8904 16 : if (illumMap.HeaderXLineLengthNeeded) {
8905 1 : illumMap.HeaderXLineLength = linelen;
8906 1 : if (static_cast<std::string::size_type>(illumMap.HeaderXLineLength) > len(mapLine)) {
8907 0 : ShowWarningError(state,
8908 0 : format("ReportIllumMap: Map=\"{}\" -- the X Header overflows buffer -- will be truncated at {} characters.",
8909 0 : illumMap.Name,
8910 0 : int(len(mapLine))));
8911 0 : ShowContinueError(state, format("...needed {} characters. Please contact EnergyPlus support.", illumMap.HeaderXLineLength));
8912 : }
8913 1 : illumMap.HeaderXLineLengthNeeded = false;
8914 : }
8915 :
8916 16 : print(*illumMap.mapFile, "{}\n", mapLine);
8917 :
8918 : // Write Y scale prefix and illuminance values
8919 16 : RefPt = 1;
8920 176 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
8921 160 : mapLine = format("({:.2R};{:.2R})=", illumMap.refPts(RefPt).absCoords.x, illumMap.refPts(RefPt).absCoords.y);
8922 1760 : for (int R = RefPt; R <= RefPt + illumMap.Xnum - 1; ++R) {
8923 1600 : int IllumOut = nint(illumMap.refPts(R).lumsHr[iLum_Illum]);
8924 1600 : std::string String = fmt::to_string(IllumOut);
8925 : ;
8926 1600 : if (!illumMap.refPts(R).inBounds) {
8927 0 : String = "*" + String;
8928 : }
8929 1600 : mapLine += dl->MapColSep + String;
8930 1600 : }
8931 :
8932 160 : print(*illumMap.mapFile, "{}\n", mapLine);
8933 :
8934 160 : RefPt += illumMap.Xnum;
8935 : } // X
8936 :
8937 16 : if (state.dataSQLiteProcedures->sqlite) {
8938 0 : if (dl->SQFirstTime) {
8939 0 : int const nX(maxval(dl->illumMaps, &IllumMap::Xnum));
8940 0 : int const nY(maxval(dl->illumMaps, &IllumMap::Ynum));
8941 0 : dl->XValue.allocate(nX);
8942 0 : dl->YValue.allocate(nY);
8943 0 : dl->IllumValue.allocate(nX, nY);
8944 0 : dl->SQFirstTime = false;
8945 : }
8946 :
8947 0 : for (int Y = 1; Y <= illumMap.Ynum; ++Y) {
8948 0 : dl->YValue(Y) = illumMap.Ymin + (Y - 1) * illumMap.Yinc;
8949 0 : for (int X = 1; X <= illumMap.Xnum; ++X) {
8950 0 : dl->XValue(X) = illumMap.Xmin + (X - 1) * illumMap.Xinc;
8951 0 : int IllumIndex = X + (Y - 1) * illumMap.Xnum;
8952 0 : dl->IllumValue(X, Y) = nint(illumMap.refPts(IllumIndex).lumsHr[iLum_Illum]);
8953 0 : if (!illumMap.refPts(IllumIndex).inBounds) {
8954 0 : dl->IllumValue(X, Y) = -dl->IllumValue(X, Y);
8955 : }
8956 : } // X Loop
8957 : } // Y Loop
8958 :
8959 : // We need DataGlobals::CalendarYear, and not DataEnvironment::Year because
8960 : // otherwise if you run a TMY file, you'll get for eg 1977, 1981, etc
8961 0 : state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMap(MapNum,
8962 0 : state.dataGlobal->CalendarYear,
8963 0 : state.dataEnvrn->Month,
8964 0 : state.dataEnvrn->DayOfMonth,
8965 0 : state.dataGlobal->HourOfDay,
8966 : illumMap.Xnum,
8967 0 : dl->XValue,
8968 : illumMap.Ynum,
8969 0 : dl->YValue,
8970 0 : dl->IllumValue);
8971 :
8972 : } // WriteOutputToSQLite
8973 16 : } // end time step
8974 : } // not Warmup
8975 61 : }
8976 :
8977 26 : void CloseReportIllumMaps(EnergyPlusData &state)
8978 : {
8979 :
8980 : // SUBROUTINE INFORMATION:
8981 : // AUTHOR Linda K. Lawrie
8982 : // DATE WRITTEN June 2003
8983 :
8984 : // PURPOSE OF THIS SUBROUTINE:
8985 : // This subroutine "closes" out the created daylight illuminance maps by merging them
8986 : // into the "eplusout.map" file.
8987 :
8988 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
8989 26 : auto &dl = state.dataDayltg;
8990 :
8991 26 : if ((int)dl->illumMaps.size() > 0) {
8992 : // Write map header
8993 0 : if (dl->MapColSep == DataStringGlobals::CharTab) {
8994 0 : state.files.map.filePath = state.files.outputMapTabFilePath;
8995 0 : } else if (dl->MapColSep == DataStringGlobals::CharComma) {
8996 0 : state.files.map.filePath = state.files.outputMapCsvFilePath;
8997 : } else {
8998 0 : state.files.map.filePath = state.files.outputMapTxtFilePath;
8999 : }
9000 :
9001 0 : state.files.map.ensure_open(state, "CloseReportIllumMaps");
9002 :
9003 0 : for (int MapNum = 1; MapNum <= (int)dl->illumMaps.size(); ++MapNum) {
9004 0 : auto &illumMap = dl->illumMaps(MapNum);
9005 0 : if (!illumMap.mapFile->good()) continue; // fatal error processing
9006 :
9007 0 : const std::vector<std::string> mapLines = illumMap.mapFile->getLines();
9008 0 : if (mapLines.empty()) {
9009 0 : ShowSevereError(state, format("CloseReportIllumMaps: IllumMap=\"{}\" is empty.", illumMap.Name));
9010 0 : break;
9011 : }
9012 0 : for (const std::string &mapLine : mapLines) {
9013 0 : print(state.files.map, "{}\n", mapLine);
9014 : }
9015 0 : illumMap.mapFile->del();
9016 0 : }
9017 :
9018 0 : if (!dl->mapResultsReported && !state.dataErrTracking->AbortProcessing) {
9019 0 : const std::string message = "CloseReportIllumMaps: Illuminance maps requested but no data ever reported. Likely cause is no solar.";
9020 0 : ShowSevereError(state, message);
9021 0 : print(state.files.map, "{}\n", message);
9022 0 : }
9023 : }
9024 26 : }
9025 :
9026 26 : void CloseDFSFile(EnergyPlusData &state)
9027 : {
9028 :
9029 : // SUBROUTINE INFORMATION:
9030 : // AUTHOR Linda Lawrie
9031 : // DATE WRITTEN August 2010
9032 :
9033 : // PURPOSE OF THIS SUBROUTINE:
9034 : // Make sure DFSFile is closed at exit time. Do not rely on operating system to
9035 : // take care of it.
9036 :
9037 26 : state.files.dfs.close();
9038 26 : }
9039 :
9040 7 : void DayltgSetupAdjZoneListsAndPointers(EnergyPlusData &state)
9041 : {
9042 :
9043 : // SUBROUTINE INFORMATION:
9044 : // AUTHOR Fred Winkelmann
9045 : // DATE WRITTEN Feb. 2004
9046 : // MODIFIED: June 2010;LKL - Merged two routines.
9047 :
9048 : // PURPOSE OF THIS SUBROUTINE:
9049 : // For each Daylighting:Detailed enclosure, creates a list of adjacent enclosures,
9050 : // that have one or more exterior windows and that share one or more interior
9051 : // windows with Z. Used in calculation of daylighting through interior windows.
9052 :
9053 : // Sets the daylighting factor pointers for each Daylighting:Detailed control. The pointer
9054 : // may be associated with an exterior window in a daylit target zone's enclosure or an exterior window in
9055 : // an adjacent enclosure, daylit or not, that shares interior windows with the target zone's enclosure.
9056 :
9057 : // Count number of exterior Windows (use to allocate arrays)
9058 7 : auto &dl = state.dataDayltg;
9059 7 : auto &s_surf = state.dataSurface;
9060 :
9061 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9062 9 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9063 9 : thisEnclDaylight.TotalExtWindows = 0;
9064 :
9065 : // Count exterior windows in this solar enclosure
9066 64 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9067 55 : auto const &surf = s_surf->Surface(surfNum);
9068 55 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9069 46 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9070 9 : ++thisEnclDaylight.TotalExtWindows;
9071 : }
9072 : }
9073 : } // End of primary enclosure loop
9074 :
9075 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9076 9 : int NumList = 0;
9077 9 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9078 9 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9079 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9080 : // This is a Daylighting:Detailed enclosure
9081 : // Find adjacent zones/enclosures
9082 22 : for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
9083 13 : if (adjEnclNum == enclNum) continue;
9084 : // Require that adjEnclNum have a least one exterior window
9085 4 : bool AdjEnclHasExtWins = false;
9086 28 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9087 32 : if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
9088 4 : (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
9089 4 : AdjEnclHasExtWins = true;
9090 4 : break;
9091 : }
9092 : }
9093 4 : if (!AdjEnclHasExtWins) continue;
9094 : // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to ZoneNum
9095 32 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9096 28 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9097 28 : if ((surfAdj.Class == SurfaceClass::Window) && (surfAdj.ExtBoundCond >= 1)) {
9098 : // This is an interior window in ZoneNumAdj
9099 0 : if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex == enclNum) {
9100 : // This interior window is adjacent to ZoneNum
9101 0 : ++NumList;
9102 0 : break;
9103 : }
9104 : }
9105 : }
9106 : }
9107 9 : thisEnclDaylight.AdjIntWinEnclNums.allocate(NumList);
9108 9 : thisEnclDaylight.AdjIntWinEnclNums = 0;
9109 : } // End of primary enclosure loop
9110 :
9111 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9112 9 : int NumList = 0;
9113 9 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9114 9 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9115 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9116 : // This is a Daylighting:Detailed enclosure
9117 : // Find adjacent zones/enclosures
9118 22 : for (int adjEnclNum = 1; adjEnclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++adjEnclNum) {
9119 13 : if (adjEnclNum == enclNum) continue;
9120 : // Require that adjEnclNum have a least one exterior window
9121 4 : bool AdjEnclHasExtWins = false;
9122 28 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9123 28 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9124 28 : if (surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) {
9125 4 : AdjEnclHasExtWins = true;
9126 4 : break;
9127 : }
9128 : }
9129 4 : if (!AdjEnclHasExtWins) continue;
9130 : // Loop again through surfaces in ZoneNumAdj and see if any are interior windows adjacent to enclNum
9131 32 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9132 28 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9133 28 : if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond < 1) continue;
9134 :
9135 : // This is an interior window in adjEnclNum
9136 0 : if (s_surf->Surface(surfAdj.ExtBoundCond).SolarEnclIndex != enclNum) continue;
9137 :
9138 : // This interior window is adjacent to ZoneNum
9139 0 : ++NumList;
9140 0 : int enclNumAdj = surfAdj.SolarEnclIndex;
9141 0 : thisEnclDaylight.AdjIntWinEnclNums(NumList) = enclNumAdj;
9142 0 : dl->enclDaylight(enclNumAdj).adjEnclHasDayltgCtrl = true;
9143 0 : break;
9144 : }
9145 : }
9146 9 : thisEnclDaylight.NumOfIntWinAdjEncls = NumList;
9147 : } // End of primary enclosure loop
9148 :
9149 : // now fill out information on relationship between adjacent exterior windows and associated interior windows
9150 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9151 9 : auto &enclDayl = dl->enclDaylight(enclNum);
9152 : // first find count of exterior windows
9153 9 : if (enclDayl.NumOfIntWinAdjEncls <= 0) {
9154 9 : enclDayl.NumOfIntWinAdjEnclExtWins = 0;
9155 9 : continue;
9156 : }
9157 0 : for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
9158 0 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9159 0 : if ((s_surf->Surface(SurfNumAdj).Class == SurfaceClass::Window) &&
9160 0 : (s_surf->Surface(SurfNumAdj).ExtBoundCond == ExternalEnvironment)) {
9161 0 : ++enclDayl.NumOfIntWinAdjEnclExtWins;
9162 : }
9163 : }
9164 : }
9165 : // now allocate nested struct based on exterior window count
9166 0 : enclDayl.IntWinAdjEnclExtWin.allocate(enclDayl.NumOfIntWinAdjEnclExtWins);
9167 :
9168 : // now fill nested structure
9169 0 : int ExtWinIndex = 0;
9170 0 : for (int adjEnclNum : enclDayl.AdjIntWinEnclNums) {
9171 0 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9172 0 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9173 0 : if (surfAdj.Class != SurfaceClass::Window || surfAdj.ExtBoundCond != ExternalEnvironment) continue;
9174 :
9175 0 : ++ExtWinIndex;
9176 0 : auto &intWinAdjEnclExtWin = enclDayl.IntWinAdjEnclExtWin(ExtWinIndex);
9177 0 : intWinAdjEnclExtWin.SurfNum = SurfNumAdj;
9178 :
9179 : // now count interior windows shared by both zones
9180 0 : int NumOfIntWindowsCount = 0;
9181 0 : for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9182 0 : auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
9183 0 : if ((surfAdj2.Class == SurfaceClass::Window) && (surfAdj2.ExtBoundCond >= 1)) {
9184 : // This is an interior window in ZoneNumAdj
9185 0 : if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
9186 : // This interior window is adjacent to ZoneNum and associated with this
9187 0 : ++NumOfIntWindowsCount;
9188 : }
9189 : }
9190 : } // for (SurfNumAdj2)
9191 :
9192 : // allocate nested array
9193 0 : intWinAdjEnclExtWin.IntWinNum.allocate(NumOfIntWindowsCount);
9194 0 : intWinAdjEnclExtWin.IntWinNum = 0;
9195 0 : int IntWinIndex = 0;
9196 0 : for (int SurfNumAdj2 : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9197 0 : auto const &surfAdj2 = s_surf->Surface(SurfNumAdj2);
9198 0 : if (surfAdj2.Class != SurfaceClass::Window || surfAdj2.ExtBoundCond < 1) continue;
9199 :
9200 : // This is an interior window in ZoneNumAdj
9201 0 : if (s_surf->Surface(surfAdj2.ExtBoundCond).SolarEnclIndex == enclNum) {
9202 : // This interior window is adjacent to ZoneNum and associated with this
9203 0 : intWinAdjEnclExtWin.IntWinNum(++IntWinIndex) = SurfNumAdj2;
9204 : }
9205 : } // for (SurfNumAdj2)
9206 : } // for (SurfNumAdj)
9207 : } // for (adjEnclNum)
9208 : } // End of primary enclosure loop
9209 :
9210 7 : Array1D_int enclExtWin;
9211 7 : enclExtWin.dimension(state.dataViewFactor->NumOfSolarEnclosures, 0);
9212 :
9213 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9214 9 : enclExtWin(enclNum) = 0;
9215 9 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9216 9 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9217 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9218 : // This is a Daylighting:Detailed zone
9219 :
9220 : // Get exterior windows in this solar enclosure
9221 64 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9222 55 : auto const &surf = s_surf->Surface(surfNum);
9223 55 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9224 46 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9225 9 : ++enclExtWin(enclNum);
9226 : }
9227 : }
9228 :
9229 : // Get exterior windows in adjacent enclosures that share interior windows with enclNum
9230 9 : if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
9231 :
9232 0 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9233 : // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
9234 : // it would not be an "AdjIntWinEncl"
9235 0 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9236 0 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9237 0 : if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
9238 0 : surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
9239 0 : ++enclExtWin(enclNum);
9240 : }
9241 : }
9242 : } // for (adjEnclNum)
9243 : } // for (enclNum)
9244 :
9245 7 : dl->maxShadeDeployOrderExtWins = 0;
9246 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9247 9 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9248 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9249 9 : thisEnclDaylight.NumOfDayltgExtWins = 0;
9250 9 : int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
9251 9 : if (thisEnclNumRefPoints > 0) {
9252 : // This is a Daylighting:Detailed enclosure
9253 :
9254 : // Get exterior windows in this enclosure
9255 9 : if (enclExtWin(enclNum) == 0) continue;
9256 9 : thisEnclDaylight.DayltgExtWinSurfNums.allocate(enclExtWin(enclNum));
9257 9 : thisEnclDaylight.DayltgExtWinSurfNums = 0;
9258 18 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9259 9 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9260 9 : thisDayltgCtrl.MapShdOrdToLoopNum.allocate(enclExtWin(enclNum));
9261 9 : thisDayltgCtrl.MapShdOrdToLoopNum = 0;
9262 :
9263 9 : assert((int)thisDayltgCtrl.refPts.size() == thisDayltgCtrl.TotalDaylRefPoints);
9264 23 : for (auto &refPt : thisDayltgCtrl.refPts) {
9265 14 : refPt.extWins.allocate(enclExtWin(enclNum));
9266 28 : for (auto &extWin : refPt.extWins) {
9267 14 : new (&extWin) DaylRefPtExtWin();
9268 : }
9269 : }
9270 : }
9271 :
9272 9 : int enclExtWinCtr = 0;
9273 :
9274 64 : for (int const surfNum : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9275 55 : auto const &surf = s_surf->Surface(surfNum);
9276 55 : if ((surf.Class == SurfaceClass::Window && surf.ExtBoundCond == ExternalEnvironment) ||
9277 46 : surf.OriginalClass == SurfaceClass::TDD_Diffuser) {
9278 9 : ++enclExtWinCtr;
9279 9 : thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = surfNum;
9280 : }
9281 : }
9282 :
9283 : // Get exterior windows in adjacent enclosures that share interior windows with enclNum
9284 9 : if (thisEnclDaylight.NumOfIntWinAdjEncls > 0) {
9285 0 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9286 : // Get exterior windows in EnclNumAdj -- there must be at least one, otherwise
9287 : // it would not be an "AdjIntWinEncl"
9288 0 : for (int SurfNumAdj : state.dataViewFactor->EnclSolInfo(adjEnclNum).SurfacePtr) {
9289 0 : auto const &surfAdj = s_surf->Surface(SurfNumAdj);
9290 0 : if ((surfAdj.Class == SurfaceClass::Window && surfAdj.ExtBoundCond == ExternalEnvironment) ||
9291 0 : surfAdj.OriginalClass == SurfaceClass::TDD_Diffuser) {
9292 0 : ++enclExtWinCtr;
9293 0 : thisEnclDaylight.DayltgExtWinSurfNums(enclExtWinCtr) = SurfNumAdj;
9294 :
9295 0 : auto &surfWinAdj = s_surf->SurfaceWindow(SurfNumAdj);
9296 : // If no daylighting in the adjacent enclosure, set up variables anyway:
9297 0 : if (state.dataViewFactor->EnclSolInfo(adjEnclNum).TotalEnclosureDaylRefPoints == 0 &&
9298 0 : !s_surf->SurfWinSurfDayLightInit(SurfNumAdj)) {
9299 0 : surfWinAdj.refPts.allocate(thisEnclNumRefPoints);
9300 0 : for (auto &refPt : surfWinAdj.refPts) {
9301 0 : new (&refPt) SurfaceWindowRefPt();
9302 : }
9303 0 : s_surf->SurfWinSurfDayLightInit(SurfNumAdj) = true;
9304 : }
9305 : }
9306 : } // for (SurfNumAdj)
9307 : } // for (adjEnclNum)
9308 : } // if (thisEnclDaylight.NumOfIntWinAdjEncls > 0)
9309 :
9310 9 : thisEnclDaylight.NumOfDayltgExtWins = enclExtWin(enclNum);
9311 9 : int winSize = enclExtWin(enclNum);
9312 18 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9313 9 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9314 9 : int refSize = thisDayltgCtrl.TotalDaylRefPoints;
9315 225 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
9316 216 : thisDayltgCtrl.daylFac[iHr].allocate(winSize, refSize);
9317 : }
9318 : }
9319 : } // if (thisEncl.NumOfRefPoints > 0)
9320 :
9321 9 : if (s_surf->TotWinShadingControl > 0) {
9322 1 : CreateShadeDeploymentOrder(state, enclNum);
9323 : }
9324 : } // for (enclNum)
9325 :
9326 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9327 9 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9328 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9329 9 : int thisEnclNumRefPoints = state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints;
9330 9 : if (thisEnclNumRefPoints > 0) {
9331 9 : if (s_surf->TotWinShadingControl > 0) {
9332 1 : MapShadeDeploymentOrderToLoopNumber(state, enclNum);
9333 : }
9334 : }
9335 : }
9336 :
9337 8 : for (auto &illumMap : dl->illumMaps) {
9338 1 : assert((int)illumMap.refPts.size() == illumMap.TotalMapRefPoints);
9339 1 : if (illumMap.TotalMapRefPoints == 0) continue;
9340 :
9341 1 : int numExtWin = enclExtWin(illumMap.enclIndex);
9342 1 : if (numExtWin == 0) continue;
9343 :
9344 101 : for (auto &refPt : illumMap.refPts) {
9345 100 : refPt.winLums.allocate(numExtWin);
9346 200 : for (auto &winLums : refPt.winLums) {
9347 100 : winLums = {0.0, 0.0};
9348 : }
9349 : }
9350 :
9351 25 : for (int iHr = 1; iHr <= Constant::iHoursInDay; ++iHr) {
9352 24 : illumMap.daylFac[iHr].allocate(numExtWin, illumMap.TotalMapRefPoints);
9353 : }
9354 :
9355 : } // End of map loop
9356 :
9357 7 : dl->dirIllum.allocate(Constant::iHoursInDay);
9358 7 : dl->reflIllum.allocate(Constant::iHoursInDay);
9359 7 : dl->winLum.allocate(Constant::iHoursInDay);
9360 7 : dl->avgWinLum.allocate(Constant::iHoursInDay);
9361 :
9362 : static constexpr std::string_view Format_700("! <Enclosure/Window Adjacency Daylighting Counts>, Enclosure Name, Number of Exterior Windows, "
9363 : "Number of Exterior Windows in Adjacent Enclosures\n");
9364 7 : print(state.files.eio, Format_700);
9365 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9366 9 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9367 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9368 9 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9369 : static constexpr std::string_view Format_701("Enclosure/Window Adjacency Daylighting Counts, {},{},{}\n");
9370 9 : print(state.files.eio,
9371 : Format_701,
9372 9 : state.dataViewFactor->EnclSolInfo(enclNum).Name,
9373 9 : thisEnclDaylight.TotalExtWindows,
9374 18 : (thisEnclDaylight.NumOfDayltgExtWins - thisEnclDaylight.TotalExtWindows));
9375 : }
9376 : static constexpr std::string_view Format_702(
9377 : "! <Enclosure/Window Adjacency Daylighting Matrix>, Enclosure Name, Number of Adjacent Enclosures with Windows,Adjacent "
9378 : "Enclosure Names - 1st 100 (max)\n");
9379 7 : print(state.files.eio, Format_702);
9380 16 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9381 9 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9382 9 : if (!thisEnclDaylight.hasSplitFluxDaylighting) continue;
9383 9 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9384 : static constexpr std::string_view Format_703("Enclosure/Window Adjacency Daylighting Matrix, {},{}");
9385 9 : print(state.files.eio, Format_703, state.dataViewFactor->EnclSolInfo(enclNum).Name, thisEnclDaylight.NumOfIntWinAdjEncls);
9386 9 : for (int loop = 1, loop_end = min(thisEnclDaylight.NumOfIntWinAdjEncls, 100); loop <= loop_end; ++loop) {
9387 0 : print(state.files.eio, ",{}", state.dataViewFactor->EnclSolInfo(thisEnclDaylight.AdjIntWinEnclNums(loop)).Name);
9388 : }
9389 9 : print(state.files.eio, "\n");
9390 : }
9391 :
9392 7 : enclExtWin.deallocate();
9393 7 : }
9394 :
9395 3 : void CreateShadeDeploymentOrder(EnergyPlusData &state, int const enclNum)
9396 : {
9397 : // J. Glazer - 2018
9398 : // create sorted list for shade deployment order
9399 : // first step is to create a sortable list of WindowShadingControl objects by sequence
9400 3 : auto &dl = state.dataDayltg;
9401 3 : auto &s_surf = state.dataSurface;
9402 :
9403 3 : std::vector<std::pair<int, int>> shadeControlSequence; // sequence, WindowShadingControl
9404 10 : for (int iShadeCtrl = 1; iShadeCtrl <= s_surf->TotWinShadingControl; ++iShadeCtrl) {
9405 7 : auto &winShadeControl = s_surf->WindowShadingControl(iShadeCtrl);
9406 7 : for (int spaceNum : state.dataHeatBal->Zone(winShadeControl.ZoneIndex).spaceIndexes) {
9407 7 : int shadeCtrlEnclNum = state.dataHeatBal->space(spaceNum).solarEnclosureNum;
9408 7 : if (shadeCtrlEnclNum == enclNum) {
9409 7 : shadeControlSequence.push_back(std::make_pair(winShadeControl.SequenceNumber, iShadeCtrl));
9410 7 : break;
9411 : }
9412 : }
9413 : }
9414 : // sort the WindowShadingControl objects based on sequence number
9415 3 : sort(shadeControlSequence.begin(), shadeControlSequence.end());
9416 : // now make the deployment list of lists.
9417 : // each sublist is a group of surfaces that should be deployed together
9418 : // often the sublist is just a single item.
9419 3 : dl->maxShadeDeployOrderExtWins = 0;
9420 6 : for (int controlNum : dl->enclDaylight(enclNum).daylightControlIndexes) {
9421 3 : auto &thisDaylightCtrl = dl->daylightControl(controlNum);
9422 10 : for (auto sequence : shadeControlSequence) { // This is an iterator (THIS_AUTO_OK)
9423 7 : int curShadeControlNum = sequence.second;
9424 7 : auto const &winShadeControl = s_surf->WindowShadingControl(curShadeControlNum);
9425 7 : if (winShadeControl.multiSurfaceControl == MultiSurfaceControl::Group) {
9426 : // add a group of surfaces since they should be deployed as a group
9427 5 : std::vector<int> group;
9428 16 : for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
9429 11 : group.push_back(winShadeControl.FenestrationIndex(i));
9430 : }
9431 5 : thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(group);
9432 5 : } else {
9433 : // add each individual surface as a separate list so they are deployed individually
9434 10 : for (int i = 1; i <= winShadeControl.FenestrationCount; i++) {
9435 8 : std::vector<int> singleMemberVector;
9436 8 : singleMemberVector.push_back(winShadeControl.FenestrationIndex(i));
9437 8 : thisDaylightCtrl.ShadeDeployOrderExtWins.push_back(singleMemberVector);
9438 8 : }
9439 : }
9440 : }
9441 3 : dl->maxShadeDeployOrderExtWins = max(dl->maxShadeDeployOrderExtWins, (int)thisDaylightCtrl.ShadeDeployOrderExtWins.size());
9442 : }
9443 :
9444 3 : dl->maxDayltgExtWins = 0;
9445 6 : for (auto const &enclDayl : dl->enclDaylight) {
9446 3 : dl->maxDayltgExtWins = max(dl->maxDayltgExtWins, enclDayl.NumOfDayltgExtWins);
9447 : }
9448 :
9449 3 : } // CreateShadeDeploymentOrder()
9450 :
9451 2 : void MapShadeDeploymentOrderToLoopNumber(EnergyPlusData &state, int const enclNum)
9452 : {
9453 : // J. Glazer - 2018
9454 : // Allow a way to map back to the original "loop" index that is used in many other places in the
9455 : // ZoneDayLight data structure when traversing the list in the order of the window shaded deployment
9456 2 : auto &dl = state.dataDayltg;
9457 2 : auto &s_surf = state.dataSurface;
9458 :
9459 2 : auto const &thisEnclDaylight = dl->enclDaylight(enclNum);
9460 2 : auto const &thisEnclSol = state.dataViewFactor->EnclSolInfo(enclNum);
9461 :
9462 2 : if (thisEnclSol.TotalEnclosureDaylRefPoints == 0 || thisEnclDaylight.NumOfDayltgExtWins == 0) return;
9463 :
9464 4 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9465 2 : auto &thisDaylightCtrl = dl->daylightControl(controlNum);
9466 2 : if (thisDaylightCtrl.ShadeDeployOrderExtWins.size() == 0) continue;
9467 :
9468 2 : int count = 0;
9469 2 : bool showOnce = true;
9470 9 : for (auto const &listOfExtWin : thisDaylightCtrl.ShadeDeployOrderExtWins) {
9471 17 : for (int IWinShdOrd : listOfExtWin) {
9472 10 : ++count;
9473 10 : if (count > thisEnclDaylight.NumOfDayltgExtWins) {
9474 0 : if (showOnce) {
9475 0 : ShowWarningError(
9476 : state,
9477 0 : format("MapShadeDeploymentOrderToLoopNumber: too many controlled shaded windows in enclosure {}", thisEnclSol.Name));
9478 0 : ShowContinueError(state,
9479 : "Check the Zone Name in the WindowShadingControl that references the following fenestration surfaces:");
9480 0 : showOnce = false;
9481 : }
9482 0 : ShowContinueError(state, format(" - {}", s_surf->Surface(IWinShdOrd).Name));
9483 : }
9484 46 : for (int loop = 1; loop <= thisEnclDaylight.NumOfDayltgExtWins; ++loop) {
9485 46 : int IWinLoop = thisEnclDaylight.DayltgExtWinSurfNums(loop);
9486 46 : if (IWinShdOrd == IWinLoop) {
9487 10 : thisDaylightCtrl.MapShdOrdToLoopNum(count) = loop;
9488 10 : break;
9489 : }
9490 : }
9491 : }
9492 : } // for (listOfExtWin)
9493 : } // for (controlNum)
9494 : } // MapShadeDeploymentOrderToLoopNumber()
9495 :
9496 0 : void DayltgInterReflIllFrIntWins(EnergyPlusData &state, int const enclNum)
9497 : {
9498 :
9499 : // SUBROUTINE INFORMATION:
9500 : // AUTHOR Fred Winkelmann
9501 : // DATE WRITTEN Mar. 2004
9502 :
9503 : // PURPOSE OF THIS SUBROUTINE:
9504 : // Calculates the inter-reflected illuminance in a daylit zone from beam
9505 : // and diffuse daylight entering the zone through interior windows. This illuminance
9506 : // is determined by the split-flux method and is assumed to be uniform, i.e., the same
9507 : // at all reference points.
9508 :
9509 0 : auto &dl = state.dataDayltg;
9510 0 : auto &s_surf = state.dataSurface;
9511 :
9512 0 : auto &enclDayl = dl->enclDaylight(enclNum);
9513 0 : auto const &enclSol = state.dataViewFactor->EnclSolInfo(enclNum);
9514 :
9515 0 : enclDayl.InterReflIllFrIntWins = 0.0;
9516 :
9517 0 : for (int const IWin : enclSol.SurfacePtr) {
9518 0 : auto &surf = s_surf->Surface(IWin);
9519 0 : if (surf.Class != SurfaceClass::Window || surf.ExtBoundCond < 1) continue;
9520 0 : auto const &surfWin = s_surf->SurfaceWindow(IWin);
9521 : // This is an interior window in ZoneNum
9522 0 : int const ConstrNum = surf.Construction;
9523 0 : int const adjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
9524 : // Luminous flux transmitted through an int win from adjacent zone's enclosure (lumens)
9525 0 : Real64 QDifTrans = state.dataHeatBal->EnclSolQSDifSol(adjEnclNum) * state.dataConstruction->Construct(ConstrNum).TransDiffVis * surf.Area *
9526 0 : state.dataEnvrn->PDIFLW;
9527 0 : Real64 QDifTransUp = QDifTrans * surfWin.fractionUpgoing; // Upgoing part of QDifTrans (lumens)
9528 0 : Real64 QDifTransDn = QDifTrans * (1.0 - surfWin.fractionUpgoing); // Downgoing part of QDifTrans (lumens)
9529 0 : if (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect) != 0.0) {
9530 0 : enclDayl.InterReflIllFrIntWins += (QDifTransDn * surfWin.rhoFloorWall + QDifTransUp * surfWin.rhoCeilingWall) /
9531 0 : (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
9532 : }
9533 : } // for (iWin)
9534 :
9535 : // Add inter-reflected illuminance from beam solar entering enclosure through interior windows
9536 : // TH, CR 7873, 9/17/2009
9537 0 : if (dl->enclDaylight(enclNum).totInsSurfArea > 0) {
9538 0 : enclDayl.InterReflIllFrIntWins +=
9539 0 : (state.dataHeatBal->EnclSolDBIntWin(enclNum) * state.dataEnvrn->BeamSolarRad * state.dataEnvrn->PDIRLW * enclDayl.floorVisRefl) /
9540 0 : (enclDayl.totInsSurfArea * (1.0 - enclDayl.aveVisDiffReflect));
9541 : }
9542 0 : } // DayltgInterReflIllFrIntWins()
9543 :
9544 5 : void CalcMinIntWinSolidAngs(EnergyPlusData &state)
9545 : {
9546 :
9547 : // SUBROUTINE INFORMATION:
9548 : // AUTHOR Fred Winkelmann
9549 : // DATE WRITTEN Feb. 2004
9550 :
9551 : // PURPOSE OF THIS SUBROUTINE:
9552 : // For each Daylighting:Detailed zone finds the minimum solid angle subtended
9553 : // by interior windows through which daylight can pass from adjacent zones with
9554 : // exterior windows.
9555 :
9556 5 : auto &dl = state.dataDayltg;
9557 5 : auto &s_surf = state.dataSurface;
9558 :
9559 12 : for (int enclNum = 1; enclNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclNum) {
9560 7 : auto &thisEnclDaylight = dl->enclDaylight(enclNum);
9561 7 : thisEnclDaylight.MinIntWinSolidAng = 2.0 * Constant::Pi;
9562 7 : if (state.dataViewFactor->EnclSolInfo(enclNum).TotalEnclosureDaylRefPoints == 0) continue;
9563 7 : if (thisEnclDaylight.NumOfIntWinAdjEncls == 0) continue;
9564 :
9565 0 : for (int IWin : state.dataViewFactor->EnclSolInfo(enclNum).SurfacePtr) {
9566 0 : auto const &surf = s_surf->Surface(IWin);
9567 :
9568 0 : if ((surf.Class != SurfaceClass::Window) || (surf.ExtBoundCond < 1)) continue;
9569 :
9570 : // This is an interior window in enclNum
9571 0 : int const winAdjEnclNum = s_surf->Surface(surf.ExtBoundCond).SolarEnclIndex;
9572 0 : bool IntWinNextToIntWinAdjZone = false; // True if an interior window is next to a zone with one or more exterior windows
9573 0 : for (int adjEnclNum : thisEnclDaylight.AdjIntWinEnclNums) {
9574 0 : if (winAdjEnclNum == adjEnclNum) {
9575 0 : IntWinNextToIntWinAdjZone = true;
9576 0 : break;
9577 : }
9578 : }
9579 :
9580 0 : if (!IntWinNextToIntWinAdjZone) continue;
9581 :
9582 0 : for (int controlNum : thisEnclDaylight.daylightControlIndexes) {
9583 0 : auto &thisDayltgCtrl = dl->daylightControl(controlNum);
9584 0 : for (int IL = 1; IL <= thisDayltgCtrl.TotalDaylRefPoints; ++IL) {
9585 : // Reference point in absolute coordinate system
9586 0 : Vector3<Real64> RREF = thisDayltgCtrl.refPts(IL).absCoords;
9587 0 : bool is_Triangle = (surf.Sides == 3);
9588 0 : bool is_Rectangle = (surf.Sides == 4);
9589 :
9590 0 : Vector3<Real64> W1, W2, W3;
9591 0 : if (is_Rectangle) {
9592 : // Vertices of window numbered counter-clockwise starting at upper left as viewed
9593 : // from inside of room. Assumes original vertices are numbered counter-clockwise from
9594 : // upper left as viewed from outside.
9595 0 : W3 = surf.Vertex(2);
9596 0 : W2 = surf.Vertex(3);
9597 0 : W1 = surf.Vertex(4);
9598 0 : } else if (is_Triangle) {
9599 0 : W3 = surf.Vertex(2);
9600 0 : W2 = surf.Vertex(3);
9601 0 : W1 = surf.Vertex(1);
9602 : }
9603 : // Unit vectors from window vertex 2 to 1 and 2 to 3, center point of window,
9604 : // and vector from ref pt to center of window
9605 0 : Vector3<Real64> W21 = W1 - W2;
9606 0 : Vector3<Real64> W23 = W3 - W2;
9607 0 : Real64 HW = W21.magnitude();
9608 0 : Real64 WW = W23.magnitude();
9609 0 : Vector3<Real64> WC = (is_Rectangle) ? (W2 + (W23 + W21) / 2.0) : (W2 + (W23 + W21) / 3.0);
9610 :
9611 : // Vector from ref point to center of window
9612 0 : Vector3<Real64> REFWC = WC - RREF;
9613 0 : W21 /= HW;
9614 0 : W23 /= WW;
9615 : // Unit vector normal to window (pointing away from room)
9616 0 : Vector3<Real64> WNORM = surf.OutNormVec;
9617 : // Distance from ref point to center of window
9618 0 : Real64 DIS = REFWC.magnitude();
9619 : // Unit vector from ref point to center of window
9620 0 : Vector3<Real64> Ray = REFWC / DIS;
9621 : // Cosine of angle between ray from ref pt to center of window and window outward normal
9622 0 : Real64 COSB = dot(WNORM, Ray);
9623 0 : if (COSB > 0.01765) { // 0 <= B < 89 deg
9624 : // Above test avoids case where ref point cannot receive daylight directly from the
9625 : // interior window
9626 0 : Real64 IntWinSolidAng = COSB * surf.Area / (pow_2(DIS) + 0.001);
9627 0 : thisEnclDaylight.MinIntWinSolidAng = min(thisEnclDaylight.MinIntWinSolidAng, IntWinSolidAng);
9628 : }
9629 0 : } // for (IL)
9630 : } // for (controlNum)
9631 : } // for (IWin)
9632 : } // for (enclNum)
9633 5 : }
9634 :
9635 19 : void CheckForGeometricTransform(EnergyPlusData &state, bool &doTransform, Real64 &OldAspectRatio, Real64 &NewAspectRatio)
9636 : {
9637 :
9638 : // SUBROUTINE INFORMATION:
9639 : // AUTHOR Linda Lawrie
9640 : // DATE WRITTEN February 2009
9641 :
9642 : // PURPOSE OF THIS SUBROUTINE:
9643 : // check for geometrytransform in the daylighting access for reference and map points
9644 :
9645 : // METHODOLOGY EMPLOYED:
9646 : // once reference points have been converted to WCS,
9647 : // change them to reflect a different aspect
9648 : // ratio for the entire building based on user input.
9649 :
9650 : // SUBROUTINE PARAMETER DEFINITIONS:
9651 : static constexpr std::string_view CurrentModuleObject = "GeometryTransform";
9652 :
9653 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
9654 19 : Array1D_string cAlphas(1);
9655 19 : Array1D<Real64> rNumerics;
9656 :
9657 : // begin execution
9658 : // get user input...
9659 19 : doTransform = false;
9660 19 : OldAspectRatio = 1.0;
9661 19 : NewAspectRatio = 1.0;
9662 :
9663 19 : auto &ip = state.dataInputProcessing->inputProcessor;
9664 19 : auto const &s_ipsc = state.dataIPShortCut;
9665 19 : auto &s_surf = state.dataSurface;
9666 :
9667 19 : if (ip->getNumObjectsFound(state, CurrentModuleObject) == 1) {
9668 : int NAlphas;
9669 : int NNum;
9670 : int IOStat;
9671 0 : ip->getObjectItem(state,
9672 : CurrentModuleObject,
9673 : 1,
9674 : cAlphas,
9675 : NAlphas,
9676 : rNumerics,
9677 : NNum,
9678 : IOStat,
9679 0 : s_ipsc->lNumericFieldBlanks,
9680 0 : s_ipsc->lAlphaFieldBlanks,
9681 0 : s_ipsc->cAlphaFieldNames,
9682 0 : s_ipsc->cNumericFieldNames);
9683 0 : OldAspectRatio = rNumerics(1);
9684 0 : NewAspectRatio = rNumerics(2);
9685 0 : std::string transformPlane = cAlphas(1);
9686 0 : if (transformPlane != "XY") {
9687 0 : ShowWarningError(state, format("{}: invalid {}=\"{}...ignored.", CurrentModuleObject, s_ipsc->cAlphaFieldNames(1), cAlphas(1)));
9688 : }
9689 0 : doTransform = true;
9690 0 : s_surf->AspectTransform = true;
9691 0 : }
9692 19 : if (s_surf->WorldCoordSystem) {
9693 0 : doTransform = false;
9694 0 : s_surf->AspectTransform = false;
9695 : }
9696 19 : }
9697 :
9698 1 : void WriteDaylightMapTitle(EnergyPlusData &state,
9699 : int const mapNum,
9700 : InputOutputFile &mapFile,
9701 : std::string const &mapName,
9702 : std::string const &environmentName,
9703 : int const ZoneNum,
9704 : std::string const &refPts,
9705 : Real64 const zcoord)
9706 : {
9707 : // SUBROUTINE INFORMATION:
9708 : // AUTHOR Greg Stark
9709 : // DATE WRITTEN Sept 2008
9710 :
9711 : // PURPOSE OF THIS SUBROUTINE:
9712 : // The purpose of the routine is to allow the daylighting map data to be written in various formats
9713 :
9714 : // must add correct number of commas at end
9715 1 : auto &dl = state.dataDayltg;
9716 :
9717 1 : std::string fullmapName = fmt::format("{}:{}:{} Illuminance [lux] (Hourly)", state.dataHeatBal->Zone(ZoneNum).Name, environmentName, mapName);
9718 1 : print(mapFile, "Date/Time{}{}{}{}{}{}\n", dl->MapColSep, fullmapName, dl->MapColSep, refPts, dl->MapColSep, dl->MapColSep);
9719 :
9720 1 : if (state.dataSQLiteProcedures->sqlite) {
9721 0 : state.dataSQLiteProcedures->sqlite->createSQLiteDaylightMapTitle(mapNum, fullmapName, environmentName, ZoneNum, refPts, zcoord);
9722 : }
9723 1 : } // WritDaylightMapTitle()
9724 :
9725 : } // namespace EnergyPlus::Dayltg
|