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 <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Fmath.hh>
53 : #include <ObjexxFCL/numeric.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Construction.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataDaylighting.hh>
59 : #include <EnergyPlus/DataDaylightingDevices.hh>
60 : #include <EnergyPlus/DataHeatBalance.hh>
61 : #include <EnergyPlus/DataIPShortCuts.hh>
62 : #include <EnergyPlus/DataSurfaces.hh>
63 : #include <EnergyPlus/DataSystemVariables.hh>
64 : #include <EnergyPlus/DaylightingDevices.hh>
65 : #include <EnergyPlus/DaylightingManager.hh>
66 : #include <EnergyPlus/DisplayRoutines.hh>
67 : #include <EnergyPlus/FluidProperties.hh>
68 : #include <EnergyPlus/General.hh>
69 : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
70 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
71 : #include <EnergyPlus/OutputProcessor.hh>
72 : #include <EnergyPlus/SolarShading.hh>
73 : #include <EnergyPlus/UtilityRoutines.hh>
74 :
75 : namespace EnergyPlus {
76 :
77 : namespace Dayltg {
78 :
79 : // MODULE INFORMATION:
80 : // AUTHOR Peter Graham Ellis
81 : // DATE WRITTEN May 2003
82 : // MODIFIED PGE, Aug 2003: Added daylighting shelves.
83 : // RE-ENGINEERED na
84 :
85 : // PURPOSE OF THIS MODULE:
86 : // Simulates daylighting devices, namely tubular daylighting devices (a.k.a. light pipes, sun pipes, or
87 : // tubular skylights) and daylighting shelves (a.k.a. light shelves).
88 :
89 : // METHODOLOGY EMPLOYED:
90 : // TUBULAR DAYLIGHTING DEVICE
91 : // A tubular daylighting device (TDD) is constructed of three components: a dome, a pipe, and a diffuser.
92 : // The dome and diffuser are treated as special window surfaces to take advantage of many of the already
93 : // existing daylighting and heat transfer routines. Together the dome and diffuser become "receiver"
94 : // and "transmitter", i.e. radiation entering the dome ends up exiting the diffuser. The geometry and
95 : // construction of the pipe and the constructions of the window surfaces determine the transmittance of
96 : // the TDD.
97 : // The main task of the module is to determine the total transmittance of the TDD for several
98 : // types of radiation, including visible beam, solar beam, solar isotropic, and solar anisotropic sky.
99 : // The fundamental building block for each type of radiation is the transmittance of a beam or ray of
100 : // radiation (visible or solar) at a given incident angle. This transmittance is calculated and
101 : // tabulated for each TDD during initialization using a numerical integral based on the analytical
102 : // solution derived by Swift and Smith. Diffuse transmittances are subsequently calculated by integrating
103 : // discrete rays over the viewable area.
104 : // There are three parts to the TDD model:
105 : // 1. Daylighting
106 : // 2. Solar gain
107 : // 3. Thermal conductive/convective gain
108 : // The daylighting simulation uses the visible beam transmittance to find the amount of direct beam
109 : // solar illuminance that enters the zone. The visible beam transmittance is also used for calculating
110 : // the contribution of each discrete ray from a differential area during a comprehensive sky/ground
111 : // integration.
112 : // The heat balance simulation handles both the solar gain and thermal conductive/convective gain.
113 : // Although visible and solar radiation are similar, solar gain is simulated very differently from the
114 : // daylighting illuminance calculations. The gain from direct beam solar is found using the
115 : // solar beam transmittance. The diffuse solar, however, is more complicated. A sky/ground integration
116 : // is NOT performed. Instead anisotropic sky view factor multipliers (SurfAnisoSkyMult) are calculated for
117 : // each surface. The diffuse sky/ground transmittance of the TDD is solved using a modification of the
118 : // SurfAnisoSkyMult. The ground radiation transmittance and anisotropic sky transmittance are found separately.
119 : // See CalcTDDTransSolIso, CalcTDDTransSolHorizon, CalcTDDTransSolAniso below.
120 : // For thermal conductive/convective gain, TDDs are treated as one big object with an effective R value.
121 : // The outside face temperature of the dome and the inside face temperature of the diffuser are calculated
122 : // with the outside and inside heat balances respectively. The temperatures are then copied to the inside
123 : // face of the dome and the outside face of the diffuser. Normal exterior and interior convection and IR
124 : // radiation exchange occurs for both surfaces.
125 : // Solar radiation that is not transmitted through the pipe is absorbed and distributed among the transition
126 : // zones that the pipe passes through between dome and diffuser. The heat is distributed proportionate to
127 : // the length of the zone. Any exterior length of pipe also receives a proportionate amount of heat, but
128 : // this is lost to the outside.
129 : // REFERENCES:
130 : // Ellis, P. G., and Strand, R. K. Paper to be published.
131 : // Swift, P. D., and Smith, G. B. "Cylindrical Mirror Light Pipes",
132 : // Solar Energy Materials and Solar Cells 36 (1995), pp. 159-168.
133 : // DAYLIGHTING SHELVES
134 : // A daylighting shelf is constructed of up to three components: a window, an inside shelf, and an outside
135 : // shelf. Both inside shelf and outside shelf are optional, but if neither is specified, nothing happens.
136 : // The window must be divided into two window surfaces: an upper window and a lower window. The upper
137 : // window interacts with the daylighting shelf but the lower window does not, except to receive shading from
138 : // the outside shelf. The inside shelf, if specified, acts to reflect all transmitted light from the
139 : // upper window onto the ceiling of the zone as diffuse light. The outside shelf, if specified, changes
140 : // the total amount of light incident on the window. All light reflected from the outside shelf also goes
141 : // onto the zone ceiling.
142 : // Most of the work for daylighting shelves is actually done in DaylightingManager.cc, SolarShading.cc,
143 : // and HeatBalanceSurfaceManager.cc. The main task of the module is to get the input and initialize the
144 : // shelf. The biggest part of initialization is calculating the window view factor to the outside shelf.
145 : // It is up to the user to reduce the window view factor to ground accordingly.
146 : // The inside shelf is modeled in both daylighting and heat balance simulations by converting all light
147 : // transmitted by the upper window into diffuse upgoing flux. No beam or downgoing flux can pass the end
148 : // of the shelf regardless of the shelf's position or orientation. Since it is defined as a partition,
149 : // the inside shelf is essentially the same as an internal mass surface. The initialization doubles the
150 : // surface area so that both sides are exposed to the zone air. All beam solar transmitted by the window
151 : // is absorbed in one side of the shelf, i.e. half of the doubled area.
152 : // The outside shelf is modeled in the daylighting simulation after the detailed sky/ground integration has
153 : // been completed. Since exterior surfaces currently do not reflect or have a luminance in the Daylighting
154 : // Manager, the shelf just serves to block part of the ground luminance. The luminance of the shelf itself
155 : // is added as a lump sum based on the view factor to the shelf, the sunlit fraction, the reflectance of the
156 : // shelf construction, and the sun and sky illuminance on the shelf. All the luminance is added to the
157 : // diffuse upgoing flux. The shelf view factor to sky is assumed to be 1.0 for lack of better information.
158 : // The outside shelf is treated similarly in the heat balance simulation, but here the shelf view factor to
159 : // sky is conveniently given by SurfAnisoSkyMult. NOTE: The solar shading code was modified to allow sunlit
160 : // fraction, sunlit area, SurfAnisoSkyMult, etc. to be calculated for attached shading surfaces.
161 : // Future shelf model improvements:
162 : // 1. Allow beam and downgoing flux to pass the end of the inside shelf depending on actual shelf geometry.
163 : // 2. Reduce outside shelf view factor to sky (for daylighting) by taking into account anisotropic sky
164 : // distribution and shading, i.e. the daylighting equivalent of SurfAnisoSkyMult.
165 : // 3. Expand view factor to shelf calculation to handle more complicated geometry.
166 : // REFERENCES:
167 : // Mills, A. F. Heat and Mass Transfer, 1995, p. 499. (Shape factor for adjacent rectangles.)
168 :
169 : // Using/Aliasing
170 : using DataSurfaces::ExternalEnvironment;
171 : using DataSurfaces::SurfaceClass;
172 :
173 108 : void InitDaylightingDevices(EnergyPlusData &state)
174 : {
175 :
176 : // SUBROUTINE INFORMATION:
177 : // AUTHOR Peter Graham Ellis
178 : // DATE WRITTEN May 2003
179 : // MODIFIED PGE, Aug 2003: Added daylighting shelves.
180 :
181 : // PURPOSE OF THIS SUBROUTINE:
182 : // This subroutine initializes all daylighting device: TDD pipes and daylighting shelves.
183 : // This is only called once at the beginning of the simulation under the BeginSimFlag.
184 :
185 : // METHODOLOGY EMPLOYED:
186 : // Daylighting and thermal variables are calculated. BeamTrans/COSAngle table is calculated.
187 :
188 : struct TDDPipeStoredData
189 : {
190 : // Members
191 : Real64 AspectRatio; // Aspect ratio, length / diameter
192 : Real64 Reflectance; // Reflectance of surface
193 : Array1D<Real64> TransBeam; // Table of beam transmittance vs. cosine angle
194 :
195 : // Default Constructor
196 0 : TDDPipeStoredData() : AspectRatio(0.0), Reflectance(0.0), TransBeam(NumOfAngles, 0.0)
197 : {
198 0 : }
199 : };
200 :
201 : // Object Data
202 108 : Array1D<TDDPipeStoredData> TDDPipeStored;
203 :
204 : // Initialize tubular daylighting devices (TDDs)
205 108 : GetTDDInput(state);
206 :
207 108 : if ((int)state.dataDaylightingDevicesData->TDDPipe.size() > 0) {
208 0 : DisplayString(state, "Initializing Tubular Daylighting Devices");
209 : // Setup COSAngle list for all TDDs
210 0 : state.dataDaylightingDevices->COSAngle(1) = 0.0;
211 0 : state.dataDaylightingDevices->COSAngle(NumOfAngles) = 1.0;
212 :
213 0 : Real64 dTheta = 90.0 * Constant::DegToRad / (NumOfAngles - 1.0);
214 0 : Real64 Theta = 90.0 * Constant::DegToRad;
215 0 : for (int AngleNum = 2; AngleNum <= NumOfAngles - 1; ++AngleNum) {
216 0 : Theta -= dTheta;
217 0 : state.dataDaylightingDevices->COSAngle(AngleNum) = std::cos(Theta);
218 : } // AngleNum
219 :
220 0 : TDDPipeStored.allocate((int)state.dataDaylightingDevicesData->TDDPipe.size() * 2);
221 :
222 0 : for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
223 : // Initialize optical properties
224 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio =
225 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength / state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter;
226 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectVis =
227 0 : 1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).InsideAbsorpVis;
228 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectSol =
229 0 : 1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).InsideAbsorpSolar;
230 :
231 : // Calculate the beam transmittance table for visible and solar spectrum
232 : // First time thru use the visible reflectance
233 0 : Real64 Reflectance = state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectVis;
234 0 : int NumStored = 0; // Counter for number of pipes stored as they are calculated
235 : int StoredNum; // Stored TDD pipe object number
236 0 : for (int Loop = 1; Loop <= 2; ++Loop) {
237 : // For computational efficiency, search stored pipes to see if an identical pipe has already been calculated
238 0 : bool Found = false;
239 0 : for (StoredNum = 1; StoredNum <= NumStored; ++StoredNum) {
240 0 : if (TDDPipeStored(StoredNum).AspectRatio != state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio) continue;
241 0 : if (TDDPipeStored(StoredNum).Reflectance == Reflectance) {
242 0 : Found = true; // StoredNum points to the matching TDDPipeStored
243 0 : break;
244 : }
245 : } // StoredNum
246 :
247 0 : if (!Found) { // Not yet calculated
248 :
249 : // Add a new pipe to TDDPipeStored
250 0 : ++NumStored;
251 0 : TDDPipeStored(NumStored).AspectRatio = state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio;
252 0 : TDDPipeStored(NumStored).Reflectance = Reflectance;
253 :
254 : // Set beam transmittances for 0 and 90 degrees
255 0 : TDDPipeStored(NumStored).TransBeam(1) = 0.0;
256 0 : TDDPipeStored(NumStored).TransBeam(NumOfAngles) = 1.0;
257 :
258 : // Calculate intermediate beam transmittances between 0 and 90 degrees
259 0 : Theta = 90.0 * Constant::DegToRad;
260 0 : for (int AngleNum = 2; AngleNum <= NumOfAngles - 1; ++AngleNum) {
261 0 : Theta -= dTheta;
262 0 : TDDPipeStored(NumStored).TransBeam(AngleNum) =
263 0 : CalcPipeTransBeam(Reflectance, state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio, Theta);
264 : } // AngleNum
265 :
266 0 : StoredNum = NumStored;
267 : }
268 :
269 : // Assign stored values to TDDPipe
270 0 : if (Loop == 1) { // Visible
271 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransVisBeam = TDDPipeStored(StoredNum).TransBeam;
272 : } else { // Solar
273 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransSolBeam = TDDPipeStored(StoredNum).TransBeam;
274 : }
275 :
276 : // Second time thru use the solar reflectance
277 0 : Reflectance = state.dataDaylightingDevicesData->TDDPipe(PipeNum).ReflectSol;
278 : } // Loop
279 :
280 : // Calculate the solar isotropic diffuse and horizon transmittances. These values are constant for a given TDD.
281 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso = CalcTDDTransSolIso(state, PipeNum);
282 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolHorizon = CalcTDDTransSolHorizon(state, PipeNum);
283 :
284 : // Initialize thermal properties
285 0 : Real64 SumTZoneLengths = 0.0;
286 0 : for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
287 0 : SumTZoneLengths += state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum);
288 :
289 0 : SetupZoneInternalGain(state,
290 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum),
291 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name,
292 : DataHeatBalance::IntGainType::DaylightingDeviceTubular,
293 0 : &state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain(TZoneNum));
294 :
295 : } // TZoneNum
296 :
297 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).ExtLength =
298 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength - SumTZoneLengths;
299 :
300 : // Setup report variables: CurrentModuleObject='DaylightingDevice:Tubular'
301 0 : SetupOutputVariable(state,
302 : "Tubular Daylighting Device Transmitted Solar Radiation Rate",
303 : Constant::Units::W,
304 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransmittedSolar,
305 : OutputProcessor::TimeStepType::Zone,
306 : OutputProcessor::StoreType::Average,
307 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
308 0 : SetupOutputVariable(state,
309 : "Tubular Daylighting Device Pipe Absorbed Solar Radiation Rate",
310 : Constant::Units::W,
311 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeAbsorbedSolar,
312 : OutputProcessor::TimeStepType::Zone,
313 : OutputProcessor::StoreType::Average,
314 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
315 0 : SetupOutputVariable(state,
316 : "Tubular Daylighting Device Heat Gain Rate",
317 : Constant::Units::W,
318 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).HeatGain,
319 : OutputProcessor::TimeStepType::Zone,
320 : OutputProcessor::StoreType::Average,
321 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
322 0 : SetupOutputVariable(state,
323 : "Tubular Daylighting Device Heat Loss Rate",
324 : Constant::Units::W,
325 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).HeatLoss,
326 : OutputProcessor::TimeStepType::Zone,
327 : OutputProcessor::StoreType::Average,
328 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
329 :
330 0 : SetupOutputVariable(state,
331 : "Tubular Daylighting Device Beam Solar Transmittance",
332 : Constant::Units::None,
333 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolBeam,
334 : OutputProcessor::TimeStepType::Zone,
335 : OutputProcessor::StoreType::Average,
336 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
337 0 : SetupOutputVariable(state,
338 : "Tubular Daylighting Device Beam Visible Transmittance",
339 : Constant::Units::None,
340 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam,
341 : OutputProcessor::TimeStepType::Zone,
342 : OutputProcessor::StoreType::Average,
343 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
344 0 : SetupOutputVariable(state,
345 : "Tubular Daylighting Device Diffuse Solar Transmittance",
346 : Constant::Units::None,
347 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolDiff,
348 : OutputProcessor::TimeStepType::Zone,
349 : OutputProcessor::StoreType::Average,
350 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
351 0 : SetupOutputVariable(state,
352 : "Tubular Daylighting Device Diffuse Visible Transmittance",
353 : Constant::Units::None,
354 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff,
355 : OutputProcessor::TimeStepType::Zone,
356 : OutputProcessor::StoreType::Average,
357 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
358 :
359 : } // PipeNum
360 :
361 0 : TDDPipeStored.deallocate();
362 : }
363 :
364 : // Initialize daylighting shelves
365 108 : GetShelfInput(state);
366 :
367 108 : if ((int)state.dataDaylightingDevicesData->Shelf.size() > 0) DisplayString(state, "Initializing Light Shelf Daylighting Devices");
368 :
369 108 : for (int ShelfNum = 1; ShelfNum <= (int)state.dataDaylightingDevicesData->Shelf.size(); ++ShelfNum) {
370 0 : int WinSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).Window;
371 :
372 0 : int ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf;
373 0 : if (ShelfSurf > 0) {
374 : // Double surface area so that both sides of the shelf are treated as internal mass
375 0 : state.dataSurface->Surface(ShelfSurf).Area *= 2.0;
376 : }
377 :
378 0 : ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf;
379 0 : if (ShelfSurf > 0) {
380 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis =
381 0 : 1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction).OutsideAbsorpVis;
382 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectSol =
383 0 : 1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction).OutsideAbsorpSolar;
384 :
385 0 : if (state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor < 0) CalcViewFactorToShelf(state, ShelfNum);
386 :
387 0 : adjustViewFactorsWithShelf(state,
388 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor,
389 0 : state.dataSurface->Surface(WinSurf).ViewFactorSky,
390 0 : state.dataSurface->Surface(WinSurf).ViewFactorGround,
391 : WinSurf,
392 : ShelfNum);
393 :
394 : // Report calculated view factor so that user knows what to make the view factor to ground
395 0 : if (!state.dataDaylightingDevices->ShelfReported) {
396 0 : print(state.files.eio,
397 : "! <Shelf Details>,Name,View Factor to Outside Shelf,Window Name,Window View Factor to Sky,Window View Factor to Ground\n");
398 0 : state.dataDaylightingDevices->ShelfReported = true;
399 : }
400 0 : print(state.files.eio,
401 : "{},{:.2R},{},{:.2R},{:.2R}\n",
402 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name,
403 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor,
404 0 : state.dataSurface->Surface(WinSurf).Name,
405 0 : state.dataSurface->Surface(WinSurf).ViewFactorSky,
406 0 : state.dataSurface->Surface(WinSurf).ViewFactorGround);
407 : // CALL SetupOutputVariable(state, 'View Factor To Outside Shelf []', &
408 : // Shelf(ShelfNum)%ViewFactor,'Zone','Average',Shelf(ShelfNum)%Name)
409 : }
410 : }
411 :
412 : // Warning that if Calculate Solar Reflection From Exterior Surfaces = Yes in Building input, then
413 : // solar reflection calculated from obstructions will not be used in daylighting shelf or tubular device
414 : // calculation
415 :
416 108 : if (state.dataSurface->CalcSolRefl &&
417 0 : ((int)state.dataDaylightingDevicesData->TDDPipe.size() > 0 || (int)state.dataDaylightingDevicesData->Shelf.size() > 0)) {
418 0 : ShowWarningError(state, "InitDaylightingDevices: Solar Distribution Model includes Solar Reflection calculations;");
419 0 : ShowContinueError(state, "the resulting reflected solar values will not be used in the");
420 0 : ShowContinueError(state, "DaylightingDevice:Shelf or DaylightingDevice:Tubular calculations.");
421 : }
422 108 : }
423 :
424 110 : void GetTDDInput(EnergyPlusData &state)
425 : {
426 :
427 : // SUBROUTINE INFORMATION:
428 : // AUTHOR Peter Graham Ellis
429 : // DATE WRITTEN May 2003
430 :
431 : // PURPOSE OF THIS SUBROUTINE:
432 : // Gets the input for TDD pipes and does some error checking.
433 :
434 : // METHODOLOGY EMPLOYED:
435 : // Standard EnergyPlus methodology.
436 :
437 110 : auto &ipsc = state.dataIPShortCut;
438 110 : auto &cCurrentModuleObject = ipsc->cCurrentModuleObject;
439 :
440 110 : cCurrentModuleObject = "DaylightingDevice:Tubular";
441 110 : int NumOfTDDPipes = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
442 :
443 110 : if (NumOfTDDPipes > 0) {
444 2 : state.dataDaylightingDevicesData->TDDPipe.allocate(NumOfTDDPipes);
445 : int IOStatus; // Used in GetObjectItem
446 : int NumAlphas; // Number of Alphas for each GetObjectItem call
447 : int NumNumbers; // Number of Numbers for each GetObjectItem call
448 :
449 6 : for (int PipeNum = 1; PipeNum <= NumOfTDDPipes; ++PipeNum) {
450 8 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
451 : cCurrentModuleObject,
452 : PipeNum,
453 4 : ipsc->cAlphaArgs,
454 : NumAlphas,
455 4 : ipsc->rNumericArgs,
456 : NumNumbers,
457 : IOStatus,
458 4 : ipsc->lNumericFieldBlanks,
459 4 : ipsc->lAlphaFieldBlanks,
460 4 : ipsc->cAlphaFieldNames,
461 4 : ipsc->cNumericFieldNames);
462 : // Pipe name
463 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name = ipsc->cAlphaArgs(1);
464 :
465 : // Get TDD:DOME object
466 4 : int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataSurface->Surface);
467 :
468 4 : if (SurfNum == 0) {
469 0 : ShowSevereError(state, format("{} = {}: Dome {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
470 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
471 : } else {
472 4 : if (FindTDDPipe(state, SurfNum) > 0) {
473 0 : ShowSevereError(state,
474 0 : format("{} = {}: Dome {} is referenced by more than one TDD.",
475 : cCurrentModuleObject,
476 0 : ipsc->cAlphaArgs(1),
477 0 : ipsc->cAlphaArgs(2)));
478 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
479 : }
480 :
481 4 : if (state.dataSurface->Surface(SurfNum).Class != SurfaceClass::TDD_Dome) {
482 0 : ShowSevereError(state,
483 0 : format("{} = {}: Dome {} is not of surface type TubularDaylightDome.",
484 : cCurrentModuleObject,
485 0 : ipsc->cAlphaArgs(1),
486 0 : ipsc->cAlphaArgs(2)));
487 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
488 : }
489 :
490 4 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TotGlassLayers > 1) {
491 0 : ShowSevereError(state,
492 0 : format("{} = {}: Dome {} construction ({}) must have only 1 glass layer.",
493 : cCurrentModuleObject,
494 0 : ipsc->cAlphaArgs(1),
495 0 : ipsc->cAlphaArgs(2),
496 0 : state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).Name));
497 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
498 : }
499 :
500 4 : if (state.dataSurface->Surface(SurfNum).HasShadeControl) {
501 0 : ShowSevereError(state,
502 0 : format("{} = {}: Dome {} must not have a shading control.",
503 : cCurrentModuleObject,
504 0 : ipsc->cAlphaArgs(1),
505 0 : ipsc->cAlphaArgs(2)));
506 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
507 : }
508 :
509 4 : if (state.dataSurface->Surface(SurfNum).FrameDivider > 0) {
510 0 : ShowSevereError(
511 : state,
512 0 : format(
513 0 : "{} = {}: Dome {} must not have a frame/divider.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
514 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
515 : }
516 :
517 4 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
518 0 : ShowSevereError(state,
519 0 : format("{} = {}: Dome {} Equivalent Layer Window is not supported.",
520 : cCurrentModuleObject,
521 0 : ipsc->cAlphaArgs(1),
522 0 : ipsc->cAlphaArgs(2)));
523 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
524 : }
525 : // Window multiplier is already handled in SurfaceGeometry.cc
526 :
527 4 : if (!state.dataSurface->Surface(SurfNum).ExtSolar) {
528 0 : ShowWarningError(state,
529 0 : format("{} = {}: Dome {} is not exposed to exterior radiation.",
530 : cCurrentModuleObject,
531 0 : ipsc->cAlphaArgs(1),
532 0 : ipsc->cAlphaArgs(2)));
533 : }
534 :
535 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome = SurfNum;
536 4 : state.dataSurface->SurfWinTDDPipeNum(SurfNum) = PipeNum;
537 : }
538 :
539 : // Get TDD:DIFFUSER object
540 4 : SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(3), state.dataSurface->Surface);
541 :
542 4 : if (SurfNum == 0) {
543 0 : ShowSevereError(state,
544 0 : format("{} = {}: Diffuser {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(3)));
545 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
546 : } else {
547 4 : if (FindTDDPipe(state, SurfNum) > 0) {
548 0 : ShowSevereError(state,
549 0 : format("{} = {}: Diffuser {} is referenced by more than one TDD.",
550 : cCurrentModuleObject,
551 0 : ipsc->cAlphaArgs(1),
552 0 : ipsc->cAlphaArgs(3)));
553 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
554 : }
555 :
556 4 : if (state.dataSurface->Surface(SurfNum).OriginalClass != SurfaceClass::TDD_Diffuser) {
557 0 : ShowSevereError(state,
558 0 : format("{} = {}: Diffuser {} is not of surface type TubularDaylightDiffuser.",
559 : cCurrentModuleObject,
560 0 : ipsc->cAlphaArgs(1),
561 0 : ipsc->cAlphaArgs(3)));
562 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
563 : }
564 :
565 4 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TotGlassLayers > 1) {
566 0 : ShowSevereError(state,
567 0 : format("{} = {}: Diffuser {} construction ({}) must have only 1 glass layer.",
568 : cCurrentModuleObject,
569 0 : ipsc->cAlphaArgs(1),
570 0 : ipsc->cAlphaArgs(3),
571 0 : state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).Name));
572 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
573 : }
574 :
575 4 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TransDiff <= 1.0e-10) {
576 0 : ShowSevereError(state,
577 0 : format("{} = {}: Diffuser {} construction ({}) invalid value.",
578 : cCurrentModuleObject,
579 0 : ipsc->cAlphaArgs(1),
580 0 : ipsc->cAlphaArgs(3),
581 0 : state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).Name));
582 0 : ShowContinueError(state,
583 0 : format("Diffuse solar transmittance of construction [{:.4R}] too small for calculations.",
584 0 : state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TransDiff));
585 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
586 : }
587 :
588 8 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome > 0 &&
589 4 : std::abs(state.dataSurface->Surface(SurfNum).Area -
590 4 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) > 0.1) {
591 0 : if (General::SafeDivide(std::abs(state.dataSurface->Surface(SurfNum).Area -
592 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area),
593 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) >
594 : 0.1) { // greater than 10%
595 0 : ShowSevereError(state,
596 0 : format("{} = {}: Dome and diffuser areas are significantly different (>10%).",
597 : cCurrentModuleObject,
598 0 : ipsc->cAlphaArgs(1)));
599 0 : ShowContinueError(state,
600 0 : format("...Diffuser Area=[{:.4R}]; Dome Area=[{:.4R}].",
601 0 : state.dataSurface->Surface(SurfNum).Area,
602 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
603 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
604 : } else {
605 0 : ShowWarningError(
606 0 : state, format("{} = {}: Dome and diffuser areas differ by > .1 m2.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
607 0 : ShowContinueError(state,
608 0 : format("...Diffuser Area=[{:.4R}]; Dome Area=[{:.4R}].",
609 0 : state.dataSurface->Surface(SurfNum).Area,
610 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
611 : }
612 : }
613 :
614 4 : if (state.dataSurface->Surface(SurfNum).HasShadeControl) {
615 0 : ShowSevereError(state,
616 0 : format("{} = {}: Diffuser {} must not have a shading control.",
617 : cCurrentModuleObject,
618 0 : ipsc->cAlphaArgs(1),
619 0 : ipsc->cAlphaArgs(3)));
620 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
621 : }
622 :
623 4 : if (state.dataSurface->Surface(SurfNum).FrameDivider > 0) {
624 0 : ShowSevereError(state,
625 0 : format("{} = {}: Diffuser {} must not have a frame/divider.",
626 : cCurrentModuleObject,
627 0 : ipsc->cAlphaArgs(1),
628 0 : ipsc->cAlphaArgs(3)));
629 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
630 : }
631 :
632 4 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
633 0 : ShowSevereError(state,
634 0 : format("{} = {}: Diffuser {} Equivalent Layer Window is not supported.",
635 : cCurrentModuleObject,
636 0 : ipsc->cAlphaArgs(1),
637 0 : ipsc->cAlphaArgs(2)));
638 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
639 : }
640 :
641 : // Window multiplier is already handled in SurfaceGeometry.cc
642 :
643 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser = SurfNum;
644 4 : state.dataSurface->SurfWinTDDPipeNum(SurfNum) = PipeNum;
645 : }
646 :
647 : // Construction
648 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction =
649 4 : Util::FindItemInList(ipsc->cAlphaArgs(4), state.dataConstruction->Construct);
650 :
651 4 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction == 0) {
652 0 : ShowSevereError(
653 0 : state, format("{} = {}: Pipe construction {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(4)));
654 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
655 : } else {
656 4 : state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).IsUsed = true;
657 : }
658 :
659 4 : if (ipsc->rNumericArgs(1) > 0) {
660 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter = ipsc->rNumericArgs(1);
661 : } else {
662 0 : ShowSevereError(state, format("{} = {}: Pipe diameter must be greater than zero.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
663 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
664 : }
665 :
666 4 : Real64 PipeArea = 0.25 * Constant::Pi * pow_2(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter);
667 8 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome > 0 &&
668 4 : std::abs(PipeArea - state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) > 0.1) {
669 0 : if (General::SafeDivide(
670 0 : std::abs(PipeArea - state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area),
671 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area) > 0.1) { // greater than 10%
672 0 : ShowSevereError(state,
673 0 : format("{} = {}: Pipe and dome/diffuser areas are significantly different (>10%).",
674 : cCurrentModuleObject,
675 0 : ipsc->cAlphaArgs(1)));
676 0 : ShowContinueError(state,
677 0 : format("...Pipe Area=[{:.4R}]; Dome/Diffuser Area=[{:.4R}].",
678 : PipeArea,
679 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
680 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
681 : } else {
682 0 : ShowWarningError(
683 0 : state, format("{} = {}: Pipe and dome/diffuser areas differ by > .1 m2.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
684 0 : ShowContinueError(state,
685 0 : format("...Pipe Area=[{:.4R}]; Dome/Diffuser Area=[{:.4R}].",
686 : PipeArea,
687 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Area));
688 : }
689 : }
690 :
691 4 : if (ipsc->rNumericArgs(2) > 0) {
692 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength = ipsc->rNumericArgs(2);
693 : } else {
694 0 : ShowSevereError(state, format("{} = {}: Pipe length must be greater than zero.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
695 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
696 : }
697 :
698 4 : if (ipsc->rNumericArgs(3) > 0) {
699 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Reff = ipsc->rNumericArgs(3);
700 : } else {
701 0 : ShowSevereError(state,
702 0 : format("{} = {}: Effective thermal resistance (R value) must be greater than zero.",
703 : cCurrentModuleObject,
704 0 : ipsc->cAlphaArgs(1)));
705 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
706 : }
707 :
708 : // Transition zones
709 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones = NumAlphas - 4;
710 :
711 4 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones < 1) {
712 0 : ShowWarningError(state,
713 0 : format("{} = {}: No transition zones specified. All pipe absorbed solar goes to exterior.",
714 : cCurrentModuleObject,
715 0 : ipsc->cAlphaArgs(1)));
716 4 : } else if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones > MaxTZones) {
717 0 : ShowSevereError(state,
718 0 : format("{} = {}: Maximum number of transition zones exceeded.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
719 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
720 : } else {
721 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone.allocate(state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
722 8 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength.allocate(
723 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
724 8 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain.allocate(
725 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
726 :
727 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone = 0;
728 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength = 0.0;
729 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain = 0.0;
730 :
731 8 : for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
732 4 : std::string const TZoneName = ipsc->cAlphaArgs(TZoneNum + 4);
733 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum) = Util::FindItemInList(TZoneName, state.dataHeatBal->Zone);
734 4 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum) == 0) {
735 0 : ShowSevereError(state,
736 0 : format("{} = {}: Transition zone {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), TZoneName));
737 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
738 : }
739 :
740 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) = ipsc->rNumericArgs(TZoneNum + 3);
741 4 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) < 0) {
742 0 : ShowSevereError(state,
743 0 : format("{} = {}: Transition zone length for {} must be zero or greater.",
744 : cCurrentModuleObject,
745 0 : ipsc->cAlphaArgs(1),
746 : TZoneName));
747 0 : state.dataDaylightingDevices->GetTDDInputErrorsFound = true;
748 : }
749 4 : } // TZoneNum
750 : }
751 :
752 : } // PipeNum
753 :
754 2 : if (state.dataDaylightingDevices->GetTDDInputErrorsFound) ShowFatalError(state, "Errors in DaylightingDevice:Tubular input.");
755 2 : state.dataDayltg->TDDTransVisBeam.allocate(Constant::iHoursInDay, NumOfTDDPipes);
756 2 : state.dataDayltg->TDDFluxInc.allocate(Constant::iHoursInDay, NumOfTDDPipes);
757 2 : state.dataDayltg->TDDFluxTrans.allocate(Constant::iHoursInDay, NumOfTDDPipes);
758 50 : for (int hr = 1; hr <= Constant::iHoursInDay; ++hr) {
759 144 : for (int tddNum = 1; tddNum <= NumOfTDDPipes; ++tddNum) {
760 96 : state.dataDayltg->TDDTransVisBeam(hr, tddNum) = 0.0;
761 384 : state.dataDayltg->TDDFluxInc(hr, tddNum) = Illums();
762 384 : state.dataDayltg->TDDFluxTrans(hr, tddNum) = Illums();
763 : } // for (tddNum)
764 : } // for (hr)
765 : }
766 110 : }
767 :
768 108 : void GetShelfInput(EnergyPlusData &state)
769 : {
770 :
771 : // SUBROUTINE INFORMATION:
772 : // AUTHOR Peter Graham Ellis
773 : // DATE WRITTEN August 2003
774 :
775 : // PURPOSE OF THIS SUBROUTINE:
776 : // Gets the input for light shelves and does some error checking.
777 :
778 : // METHODOLOGY EMPLOYED:
779 : // Standard EnergyPlus methodology.
780 :
781 108 : auto &ipsc = state.dataIPShortCut;
782 108 : auto &cCurrentModuleObject = ipsc->cCurrentModuleObject;
783 :
784 108 : cCurrentModuleObject = "DaylightingDevice:Shelf";
785 108 : int NumOfShelf = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
786 :
787 108 : if (NumOfShelf > 0) {
788 0 : state.dataDaylightingDevicesData->Shelf.allocate(NumOfShelf);
789 : int NumAlphas; // Number of Alphas for each GetObjectItem call
790 : int NumNumbers; // Number of Numbers for each GetObjectItem call
791 : int IOStatus; // Used in GetObjectItem
792 :
793 0 : for (int ShelfNum = 1; ShelfNum <= NumOfShelf; ++ShelfNum) {
794 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
795 : cCurrentModuleObject,
796 : ShelfNum,
797 0 : ipsc->cAlphaArgs,
798 : NumAlphas,
799 0 : ipsc->rNumericArgs,
800 : NumNumbers,
801 : IOStatus,
802 0 : ipsc->lNumericFieldBlanks,
803 0 : ipsc->lAlphaFieldBlanks,
804 0 : ipsc->cAlphaFieldNames,
805 0 : ipsc->cNumericFieldNames);
806 : // Shelf name
807 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name = ipsc->cAlphaArgs(1);
808 :
809 : // Get window object
810 0 : int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataSurface->Surface);
811 :
812 0 : if (SurfNum == 0) {
813 0 : ShowSevereError(state, format("{} = {}: Window {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
814 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
815 : } else {
816 0 : if (state.dataSurface->Surface(SurfNum).Class != SurfaceClass::Window) {
817 0 : ShowSevereError(state,
818 0 : format("{} = {}: Window {} is not of surface type WINDOW.",
819 : cCurrentModuleObject,
820 0 : ipsc->cAlphaArgs(1),
821 0 : ipsc->cAlphaArgs(2)));
822 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
823 : }
824 :
825 0 : if (state.dataSurface->SurfDaylightingShelfInd(SurfNum) > 0) {
826 0 : ShowSevereError(state,
827 0 : format("{} = {}: Window {} is referenced by more than one shelf.",
828 : cCurrentModuleObject,
829 0 : ipsc->cAlphaArgs(1),
830 0 : ipsc->cAlphaArgs(2)));
831 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
832 : }
833 :
834 0 : if (state.dataSurface->Surface(SurfNum).HasShadeControl) {
835 0 : ShowSevereError(state,
836 0 : format("{} = {}: Window {} must not have a shading control.",
837 : cCurrentModuleObject,
838 0 : ipsc->cAlphaArgs(1),
839 0 : ipsc->cAlphaArgs(2)));
840 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
841 : }
842 :
843 0 : if (state.dataSurface->Surface(SurfNum).FrameDivider > 0) {
844 0 : ShowSevereError(state,
845 0 : format("{} = {}: Window {} must not have a frame/divider.",
846 : cCurrentModuleObject,
847 0 : ipsc->cAlphaArgs(1),
848 0 : ipsc->cAlphaArgs(2)));
849 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
850 : }
851 :
852 0 : if (state.dataSurface->Surface(SurfNum).Sides != 4) {
853 0 : ShowSevereError(
854 0 : state, format("{} = {}: Window {} must have 4 sides.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(2)));
855 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
856 : }
857 0 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
858 0 : ShowSevereError(state,
859 0 : format("{} = {}: Window {} Equivalent Layer Window is not supported.",
860 : cCurrentModuleObject,
861 0 : ipsc->cAlphaArgs(1),
862 0 : ipsc->cAlphaArgs(2)));
863 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
864 : }
865 :
866 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Window = SurfNum;
867 0 : state.dataSurface->SurfDaylightingShelfInd(SurfNum) = ShelfNum;
868 : }
869 :
870 : // Get inside shelf heat transfer surface (optional)
871 0 : if (ipsc->cAlphaArgs(3) != "") {
872 0 : SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(3), state.dataSurface->Surface);
873 :
874 0 : if (SurfNum == 0) {
875 0 : ShowSevereError(
876 0 : state, format("{} = {}: Inside shelf {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(3)));
877 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
878 : } else {
879 : // No error if shelf belongs to more than one window, e.g. concave corners
880 :
881 0 : if (state.dataSurface->Surface(SurfNum).ExtBoundCond != SurfNum) {
882 0 : ShowSevereError(state,
883 0 : format("{} = {}: Inside shelf {} must be its own Outside Boundary Condition Object.",
884 : cCurrentModuleObject,
885 0 : ipsc->cAlphaArgs(1),
886 0 : ipsc->cAlphaArgs(3)));
887 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
888 : }
889 :
890 0 : if (state.dataSurface->Surface(SurfNum).Sides != 4) {
891 0 : ShowSevereError(
892 : state,
893 0 : format(
894 0 : "{} = {}: Inside shelf {} must have 4 sides.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(3)));
895 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
896 : }
897 :
898 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf = SurfNum;
899 : }
900 : }
901 :
902 : // Get outside shelf attached shading surface (optional)
903 0 : if (ipsc->cAlphaArgs(4) != "") {
904 0 : SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(4), state.dataSurface->Surface);
905 :
906 0 : if (SurfNum == 0) {
907 0 : ShowSevereError(
908 0 : state, format("{} = {}: Outside shelf {} not found.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(4)));
909 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
910 : } else {
911 : // No error if shelf belongs to more than one window, e.g. concave corners
912 :
913 0 : if (state.dataSurface->Surface(SurfNum).Class != SurfaceClass::Shading) {
914 0 : ShowSevereError(state,
915 0 : format("{} = {}: Outside shelf {} is not a Shading:Zone:Detailed object.",
916 : cCurrentModuleObject,
917 0 : ipsc->cAlphaArgs(1),
918 0 : ipsc->cAlphaArgs(4)));
919 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
920 : }
921 :
922 0 : if (state.dataSurface->Surface(SurfNum).shadowSurfSched != nullptr) {
923 0 : ShowSevereError(state,
924 0 : format("{} = {}: Outside shelf {} must not have a transmittance schedule.",
925 : cCurrentModuleObject,
926 0 : ipsc->cAlphaArgs(1),
927 0 : ipsc->cAlphaArgs(4)));
928 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
929 : }
930 :
931 0 : if (state.dataSurface->Surface(SurfNum).Sides != 4) {
932 0 : ShowSevereError(
933 : state,
934 0 : format(
935 0 : "{} = {}: Outside shelf {} must have 4 sides.", cCurrentModuleObject, ipsc->cAlphaArgs(1), ipsc->cAlphaArgs(4)));
936 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
937 : }
938 :
939 0 : int ConstrNum = 0;
940 : // Get outside shelf construction (required if outside shelf is specified)
941 0 : if (ipsc->cAlphaArgs(5) != "") {
942 0 : ConstrNum = Util::FindItemInList(ipsc->cAlphaArgs(5), state.dataConstruction->Construct);
943 :
944 0 : if (ConstrNum == 0) {
945 0 : ShowSevereError(state,
946 0 : format("{} = {}: Outside shelf construction {} not found.",
947 : cCurrentModuleObject,
948 0 : ipsc->cAlphaArgs(1),
949 0 : ipsc->cAlphaArgs(5)));
950 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
951 0 : } else if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) {
952 0 : ShowSevereError(state,
953 0 : format("{} = {}: Outside shelf construction {} must not have WindowMaterial:Glazing.",
954 : cCurrentModuleObject,
955 0 : ipsc->cAlphaArgs(1),
956 0 : ipsc->cAlphaArgs(5)));
957 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
958 : } else {
959 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction = ConstrNum;
960 0 : state.dataConstruction->Construct(ConstrNum).IsUsed = true;
961 : }
962 : } else {
963 0 : ShowSevereError(state,
964 0 : format("{} = {}: Outside shelf requires an outside shelf construction to be specified.",
965 : cCurrentModuleObject,
966 0 : ipsc->cAlphaArgs(1)));
967 0 : state.dataDaylightingDevices->GetShelfInputErrorsFound = true;
968 : }
969 :
970 : // Get view factor to outside shelf (optional)
971 0 : if (NumNumbers > 0) {
972 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor = ipsc->rNumericArgs(1);
973 :
974 0 : if (ipsc->rNumericArgs(1) == 0.0) {
975 0 : ShowWarningError(state,
976 0 : format("{} = {}: View factor to outside shelf is zero. Shelf does not reflect on window.",
977 : cCurrentModuleObject,
978 0 : ipsc->cAlphaArgs(1)));
979 : }
980 : } else {
981 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor =
982 : -1.0; // Flag to have the view factor calculated during initialization
983 : }
984 :
985 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf = SurfNum;
986 :
987 : // Reset some properties of the SURFACE:SHADING:ATTACHED object in order to receive radiation and shading
988 : // Normally this would be done during initialization, but that's not early enough for some shading calculations
989 0 : state.dataSurface->Surface(SurfNum).BaseSurf = SurfNum;
990 0 : state.dataSurface->Surface(SurfNum).HeatTransSurf = true;
991 0 : state.dataSurface->AllHTSurfaceList.push_back(SurfNum);
992 : // Is this needed? surfZone.ZoneHTNonWindowSurfaceList.push_back(SurfNum);
993 0 : state.dataSurface->Surface(SurfNum).Construction = ConstrNum; // Kludge to allow shading surface to be a heat transfer surface
994 0 : state.dataSurface->SurfActiveConstruction(SurfNum) = ConstrNum;
995 0 : state.dataConstruction->Construct(ConstrNum).IsUsed = true;
996 : }
997 : }
998 :
999 0 : if (state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf == 0 && state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf == 0)
1000 0 : ShowWarningError(state,
1001 0 : format("{} = {}: No inside shelf or outside shelf was specified.", cCurrentModuleObject, ipsc->cAlphaArgs(1)));
1002 :
1003 : } // ShelfNum
1004 :
1005 0 : if (state.dataDaylightingDevices->GetShelfInputErrorsFound) ShowFatalError(state, "Errors in DaylightingDevice:Shelf input.");
1006 : }
1007 108 : }
1008 :
1009 0 : Real64 CalcPipeTransBeam(Real64 const R, // Reflectance of surface, constant (can be made R = f(theta) later)
1010 : Real64 const A, // Aspect ratio, L / d
1011 : Real64 const Theta // Angle of entry in radians
1012 : )
1013 : {
1014 :
1015 : // SUBROUTINE INFORMATION:
1016 : // AUTHOR Peter Graham Ellis
1017 : // DATE WRITTEN May 2003
1018 : // MODIFIED na
1019 : // RE-ENGINEERED na
1020 :
1021 : // PURPOSE OF THIS SUBROUTINE:
1022 : // Calculates the numerical integral for the transmittance of a reflective cylinder with
1023 : // incident collimated beam radiation as described in Swift and Smith.
1024 :
1025 : // METHODOLOGY EMPLOYED:
1026 : // Since this integral can be slow, a table of values is calculated and stored during
1027 : // initialization of the TDD. Intermediate values are calculated by interpolation.
1028 : // Transmittance of sky and ground diffuse radiation is done by other functions.
1029 :
1030 : // REFERENCES:
1031 : // Swift, P. D., and Smith, G. B. "Cylindrical Mirror Light Pipes",
1032 : // Solar Energy Materials and Solar Cells 36 (1995), pp. 159-168.
1033 :
1034 : // OTHER NOTES:
1035 : // The execution time of this function can be reduced by adjusting parameters N and xTol below.
1036 : // However, there is some penalty in accuracy for N < 100,000 and xTol > 150.
1037 :
1038 : // USE STATEMENTS: na
1039 :
1040 : // Return value
1041 : Real64 CalcPipeTransBeam;
1042 :
1043 : // Locals
1044 : // FUNCTION ARGUMENT DEFINITIONS:
1045 :
1046 : // FUNCTION PARAMETER DEFINITIONS:
1047 0 : Real64 constexpr N(100000.0); // Number of integration points
1048 0 : Real64 constexpr xTol(150.0); // Tolerance factor to skip iterations where dT is approximately 0
1049 : // Must be >= 1.0, increase this number to decrease the execution time
1050 0 : Real64 const myLocalTiny(TINY(1.0));
1051 :
1052 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1053 : Real64 i; // Integration interval between points
1054 : Real64 s; // Entry point
1055 : Real64 dT;
1056 : Real64 T; // Beam transmittance for collimated solar real
1057 : Real64 x; // Intermediate variables for speed optimization
1058 : Real64 c1;
1059 : Real64 c2;
1060 : Real64 xLimit; // Limiting x value to prevent floating point underflow
1061 :
1062 0 : CalcPipeTransBeam = 0.0;
1063 :
1064 0 : T = 0.0;
1065 0 : i = 1.0 / N;
1066 :
1067 0 : xLimit = (std::log(pow_2(N) * myLocalTiny) / std::log(R)) / xTol;
1068 :
1069 0 : c1 = A * std::tan(Theta);
1070 0 : c2 = 4.0 / Constant::Pi;
1071 :
1072 0 : s = i;
1073 0 : while (s < (1.0 - i)) {
1074 0 : x = c1 / s;
1075 :
1076 0 : if (x < xLimit) {
1077 0 : dT = c2 * std::pow(R, int(x)) * (1.0 - (1.0 - R) * (x - int(x))) * pow_2(s) / std::sqrt(1.0 - pow_2(s));
1078 0 : T += dT;
1079 : }
1080 :
1081 0 : s += i;
1082 : }
1083 :
1084 0 : T /= (N - 1.0); // - 1.0, because started on i, not 0
1085 :
1086 0 : CalcPipeTransBeam = T;
1087 :
1088 0 : return CalcPipeTransBeam;
1089 : }
1090 :
1091 0 : Real64 CalcTDDTransSolIso(EnergyPlusData &state, int const PipeNum) // TDD pipe object number
1092 : {
1093 :
1094 : // SUBROUTINE INFORMATION:
1095 : // AUTHOR Peter Graham Ellis
1096 : // DATE WRITTEN July 2003
1097 : // MODIFIED na
1098 : // RE-ENGINEERED na
1099 :
1100 : // PURPOSE OF THIS SUBROUTINE:
1101 : // Calculates the transmittance of sky isotropic radiation for use with the anisotropic sky transmittance.
1102 : // This value is also used for all ground reflected solar radiation (which is isotropic).
1103 :
1104 : // METHODOLOGY EMPLOYED:
1105 : // The transmittance is calculated and stored once at initialization because the value is a constant.
1106 : // The routine numerically integrates over the entire sky. All radiation is isotropic, but incident
1107 : // angle varies over the hemisphere.
1108 : // Trans = Flux Transmitted / Flux Incident
1109 : // Not sure if shading and tilt is adequately accounted for by DifShdgRatioIsoSky later on or not...
1110 :
1111 : // REFERENCES:
1112 : // See AnisoSkyViewFactors in SolarShading.cc.
1113 :
1114 : // USE STATEMENTS: na
1115 :
1116 : // Return value
1117 : Real64 CalcTDDTransSolIso;
1118 :
1119 : // Locals
1120 : // FUNCTION ARGUMENT DEFINITIONS:
1121 :
1122 : // FUNCTION PARAMETER DEFINITIONS:
1123 0 : int constexpr NPH(1000); // Number of altitude integration points
1124 :
1125 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1126 0 : Real64 FluxInc = 0.0; // Incident solar flux
1127 0 : Real64 FluxTrans = 0.0; // Transmitted solar flux
1128 : Real64 trans; // Total beam solar transmittance of TDD
1129 : Real64 COSI; // Cosine of incident angle
1130 : Real64 SINI; // Sine of incident angle
1131 :
1132 0 : Real64 const dPH = 90.0 * Constant::DegToRad / NPH; // Altitude angle of sky element
1133 0 : Real64 PH = 0.5 * dPH; // Altitude angle increment
1134 :
1135 : // Integrate from 0 to Pi/2 altitude
1136 0 : for (int N = 1; N <= NPH; ++N) {
1137 0 : COSI = std::cos(Constant::PiOvr2 - PH);
1138 0 : SINI = std::sin(Constant::PiOvr2 - PH);
1139 :
1140 0 : Real64 P = COSI; // Angular distribution function: P = COS(Incident Angle) for diffuse isotropic
1141 :
1142 : // Calculate total TDD transmittance for given angle
1143 0 : trans = TransTDD(state, PipeNum, COSI, RadType::SolarBeam);
1144 :
1145 0 : FluxInc += P * SINI * dPH;
1146 0 : FluxTrans += trans * P * SINI * dPH;
1147 :
1148 0 : PH += dPH; // Increment the altitude angle
1149 : } // N
1150 :
1151 0 : CalcTDDTransSolIso = FluxTrans / FluxInc;
1152 :
1153 0 : return CalcTDDTransSolIso;
1154 : }
1155 :
1156 0 : Real64 CalcTDDTransSolHorizon(EnergyPlusData &state, int const PipeNum) // TDD pipe object number
1157 : {
1158 :
1159 : // SUBROUTINE INFORMATION:
1160 : // AUTHOR Peter Graham Ellis
1161 : // DATE WRITTEN July 2003
1162 : // MODIFIED na
1163 : // RE-ENGINEERED na
1164 :
1165 : // PURPOSE OF THIS SUBROUTINE:
1166 : // Calculates the transmittance of sky horizon radiation for use with the anisotropic sky transmittance.
1167 :
1168 : // METHODOLOGY EMPLOYED:
1169 : // The transmittance is calculated and stored once at initialization because the value is a constant.
1170 : // The routine numerically integrates over the horizon as an infinitesimally narrow strip of the sky.
1171 : // Horizon radiation is isotropic, but incident angle varies over the semicircle.
1172 : // Trans = Flux Transmitted / Flux Incident
1173 : // Not sure if shading is adequately accounted for by DifShdgRatioHoriz later on or not...
1174 :
1175 : // REFERENCES:
1176 : // See AnisoSkyViewFactors in SolarShading.cc.
1177 :
1178 : // Using/Aliasing
1179 : using namespace DataSurfaces;
1180 :
1181 : // Return value
1182 : Real64 CalcTDDTransSolHorizon;
1183 :
1184 : // Locals
1185 : // FUNCTION ARGUMENT DEFINITIONS:
1186 :
1187 : // FUNCTION PARAMETER DEFINITIONS:
1188 0 : int constexpr NTH(18); // Number of azimuth integration points
1189 :
1190 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1191 0 : Real64 FluxInc = 0.0; // Incident solar flux
1192 0 : Real64 FluxTrans = 0.0; // Transmitted solar flux
1193 : Real64 CosPhi; // Cosine of TDD:DOME altitude angle
1194 : Real64 Theta; // TDD:DOME azimuth angle
1195 :
1196 0 : CosPhi = std::cos(Constant::PiOvr2 -
1197 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Tilt * Constant::DegToRad);
1198 0 : Theta = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Azimuth * Constant::DegToRad;
1199 :
1200 0 : if (CosPhi > 0.01) { // Dome has a view of the horizon
1201 : // Integrate over the semicircle
1202 0 : Real64 const THMIN = Theta - Constant::PiOvr2; // Minimum azimuth integration limit
1203 : // Real64 const THMAX = Theta + PiOvr2; // Maximum azimuth integration limit
1204 0 : Real64 const dTH = 180.0 * Constant::DegToRad / NTH; // Azimuth angle increment
1205 0 : Real64 TH = THMIN + 0.5 * dTH; // Azimuth angle of sky horizon element
1206 :
1207 0 : for (int N = 1; N <= NTH; ++N) {
1208 : // Calculate incident angle between dome outward normal and horizon element
1209 0 : Real64 COSI = CosPhi * std::cos(TH - Theta); // Cosine of the incident angle
1210 :
1211 : // Calculate total TDD transmittance for given angle
1212 0 : Real64 trans = TransTDD(state, PipeNum, COSI, RadType::SolarBeam); // Total beam solar transmittance of TDD
1213 :
1214 0 : FluxInc += COSI * dTH;
1215 0 : FluxTrans += trans * COSI * dTH;
1216 :
1217 0 : TH += dTH; // Increment the azimuth angle
1218 : } // N
1219 :
1220 0 : CalcTDDTransSolHorizon = FluxTrans / FluxInc;
1221 :
1222 : } else { // Dome is nearly horizontal and has almost no view of the horizon
1223 0 : CalcTDDTransSolHorizon = 0.0; // = TransTDD(state, PipeNum, ???, SolarBeam) ! Could change to an angle near the horizon
1224 : }
1225 :
1226 0 : return CalcTDDTransSolHorizon;
1227 : }
1228 :
1229 0 : Real64 CalcTDDTransSolAniso(EnergyPlusData &state,
1230 : int const PipeNum, // TDD pipe object number
1231 : Real64 const COSI // Cosine of the incident angle
1232 : )
1233 : {
1234 :
1235 : // SUBROUTINE INFORMATION:
1236 : // AUTHOR Peter Graham Ellis
1237 : // DATE WRITTEN July 2003
1238 : // MODIFIED na
1239 : // RE-ENGINEERED na
1240 :
1241 : // PURPOSE OF THIS SUBROUTINE:
1242 : // Calculates the transmittance of the anisotropic sky.
1243 :
1244 : // METHODOLOGY EMPLOYED:
1245 : // Similar to the Trans = FluxTrans/FluxInc integrations above, the anisotropic sky can be decomposed
1246 : // and have a different transmittance applied to each component.
1247 : // FluxInc = IsoSkyRad + CircumSolarRad + HorizonRad
1248 : // FluxTrans = T1*IsoSkyRad + T2*CircumSolarRad + T3*HorizonRad
1249 : // It turns out that FluxTrans/FluxInc is equivalent to AnisoSkyTDDMult/SurfAnisoSkyMult.
1250 : // SurfAnisoSkyMult has been conveniently calculated already in AnisoSkyViewFactors in SolarShading.cc.
1251 : // SurfAnisoSkyMult = MultIsoSky*DifShdgRatioIsoSky + MultCircumSolar*SunlitFrac + MultHorizonZenith*DifShdgRatioHoriz
1252 : // In this routine a similar AnisoSkyTDDMult is calculated that applies the appropriate transmittance to each
1253 : // of the components above. The result is Trans = AnisoSkyTDDMult/SurfAnisoSkyMult.
1254 : // Shading and orientation are already taken care of by DifShdgRatioIsoSky and DifShdgRatioHoriz.
1255 :
1256 : // REFERENCES:
1257 : // See AnisoSkyViewFactors in SolarShading.cc.
1258 :
1259 : // Return value
1260 : Real64 CalcTDDTransSolAniso;
1261 :
1262 : // Locals
1263 : // FUNCTION ARGUMENT DEFINITIONS:
1264 :
1265 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1266 : int DomeSurf; // TDD:DOME surface number
1267 : Real64 IsoSkyRad; // Isotropic sky radiation component
1268 : Real64 CircumSolarRad; // Circumsolar sky radiation component
1269 : Real64 HorizonRad; // Horizon sky radiation component
1270 : Real64 AnisoSkyTDDMult; // Anisotropic sky multiplier for TDD
1271 :
1272 0 : DomeSurf = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
1273 :
1274 0 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
1275 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
1276 0 : IsoSkyRad = state.dataSolarShading->SurfMultIsoSky(DomeSurf) * state.dataSolarShading->SurfDifShdgRatioIsoSky(DomeSurf);
1277 0 : HorizonRad = state.dataSolarShading->SurfMultHorizonZenith(DomeSurf) * state.dataSolarShading->SurfDifShdgRatioHoriz(DomeSurf);
1278 : } else {
1279 0 : IsoSkyRad = state.dataSolarShading->SurfMultIsoSky(DomeSurf) * state.dataSolarShading->SurfCurDifShdgRatioIsoSky(DomeSurf);
1280 0 : HorizonRad = state.dataSolarShading->SurfMultHorizonZenith(DomeSurf) *
1281 0 : state.dataSolarShading->SurfDifShdgRatioHorizHRTS(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay, DomeSurf);
1282 : }
1283 0 : CircumSolarRad = state.dataSolarShading->SurfMultCircumSolar(DomeSurf) *
1284 0 : state.dataHeatBal->SurfSunlitFrac(state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep, DomeSurf);
1285 :
1286 0 : AnisoSkyTDDMult = state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso * IsoSkyRad +
1287 0 : TransTDD(state, PipeNum, COSI, RadType::SolarBeam) * CircumSolarRad +
1288 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolHorizon * HorizonRad;
1289 :
1290 0 : if (state.dataSolarShading->SurfAnisoSkyMult(DomeSurf) > 0.0) {
1291 0 : CalcTDDTransSolAniso = AnisoSkyTDDMult / state.dataSolarShading->SurfAnisoSkyMult(DomeSurf);
1292 : } else {
1293 0 : CalcTDDTransSolAniso = 0.0;
1294 : }
1295 :
1296 0 : return CalcTDDTransSolAniso;
1297 : }
1298 :
1299 0 : Real64 TransTDD(EnergyPlusData &state,
1300 : int const PipeNum, // TDD pipe object number
1301 : Real64 const COSI, // Cosine of the incident angle
1302 : RadType const RadiationType // Radiation type flag
1303 : )
1304 : {
1305 :
1306 : // SUBROUTINE INFORMATION:
1307 : // AUTHOR Peter Graham Ellis
1308 : // DATE WRITTEN May 2003
1309 : // MODIFIED na
1310 : // RE-ENGINEERED na
1311 :
1312 : // PURPOSE OF THIS SUBROUTINE:
1313 : // Calculates the total transmittance of the TDD for specified radiation type.
1314 :
1315 : // METHODOLOGY EMPLOYED:
1316 : // The transmittances for each component (i.e. TDD:DIFFUSER, TDD:DOME, and pipe) are calculated.
1317 : // All transmittances are multiplied to get the total for the TDD:
1318 : // TransTDD = transDome * transPipe * transDiff
1319 : // Transmittance of beam radiation is calculated by interpolating the values in a
1320 : // table created during initialization. The table values are from Swift and Smith's
1321 : // numerical integral for collimated beam radiation.
1322 : // Transmittances of isotropic and anisotropic diffuse radiation are more complicated and call
1323 : // other subroutines in this module.
1324 : // All light reaching the TDD:DIFFUSER is assumed to be diffuse.
1325 : // NOTE: Dome transmittance could be improved by taking into account curvature of the dome.
1326 :
1327 : // REFERENCES:
1328 : // Swift, P. D., and Smith, G. B. "Cylindrical Mirror Light Pipes",
1329 : // Solar Energy Materials and Solar Cells 36 (1995), pp. 159-168.
1330 :
1331 : // Return value
1332 : Real64 TransTDD;
1333 :
1334 : // Locals
1335 : // FUNCTION ARGUMENT DEFINITIONS:
1336 :
1337 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1338 : int constDome; // Construction object number for TDD:DOME
1339 : int constDiff; // Construction object number for TDD:DIFFUSER
1340 : Real64 transDome;
1341 : Real64 transPipe;
1342 : Real64 transDiff;
1343 :
1344 0 : TransTDD = 0.0;
1345 :
1346 : // Get constructions of each TDD component
1347 0 : constDome = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Construction;
1348 0 : constDiff = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser).Construction;
1349 :
1350 : // Get the transmittance of each component and of total TDD
1351 0 : switch (RadiationType) {
1352 0 : case RadType::VisibleBeam: {
1353 0 : transDome = Window::POLYF(COSI, state.dataConstruction->Construct(constDome).TransVisBeamCoef);
1354 0 : transPipe = InterpolatePipeTransBeam(state, COSI, state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransVisBeam);
1355 0 : transDiff = state.dataConstruction->Construct(constDiff).TransDiffVis; // May want to change to POLYF also!
1356 :
1357 0 : TransTDD = transDome * transPipe * transDiff;
1358 :
1359 0 : } break;
1360 0 : case RadType::SolarBeam: {
1361 0 : transDome = Window::POLYF(COSI, state.dataConstruction->Construct(constDome).TransSolBeamCoef);
1362 0 : transPipe = InterpolatePipeTransBeam(state, COSI, state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransSolBeam);
1363 0 : transDiff = state.dataConstruction->Construct(constDiff).TransDiff; // May want to change to POLYF also!
1364 :
1365 0 : TransTDD = transDome * transPipe * transDiff;
1366 :
1367 0 : } break;
1368 0 : case RadType::SolarAniso: {
1369 0 : TransTDD = CalcTDDTransSolAniso(state, PipeNum, COSI);
1370 0 : } break;
1371 0 : case RadType::SolarIso: {
1372 0 : TransTDD = state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso;
1373 0 : } break;
1374 0 : default:
1375 0 : break;
1376 : }
1377 :
1378 0 : return TransTDD;
1379 : }
1380 :
1381 0 : Real64 InterpolatePipeTransBeam(EnergyPlusData &state,
1382 : Real64 const COSI, // Cosine of the incident angle
1383 : const Array1D<Real64> &transBeam // Table of beam transmittance vs. cosine angle
1384 : )
1385 : {
1386 :
1387 : // SUBROUTINE INFORMATION:
1388 : // AUTHOR Peter Graham Ellis
1389 : // DATE WRITTEN July 2003
1390 : // MODIFIED na
1391 : // RE-ENGINEERED na
1392 :
1393 : // PURPOSE OF THIS SUBROUTINE:
1394 : // Interpolates the beam transmittance vs. cosine angle table.
1395 :
1396 : // METHODOLOGY EMPLOYED: na
1397 : // REFERENCES: na
1398 :
1399 : // Using/Aliasing
1400 : using Fluid::FindArrayIndex; // USEd code could be copied here to eliminate dependence on FluidProperties
1401 :
1402 : // Return value
1403 : Real64 InterpolatePipeTransBeam;
1404 :
1405 : // Argument array dimensioning
1406 0 : EP_SIZE_CHECK(transBeam, NumOfAngles);
1407 :
1408 : // Locals
1409 : // FUNCTION ARGUMENT DEFINITIONS:
1410 :
1411 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1412 : int Lo;
1413 : int Hi;
1414 : Real64 m;
1415 : Real64 b;
1416 :
1417 0 : InterpolatePipeTransBeam = 0.0;
1418 :
1419 : // Linearly interpolate transBeam/COSAngle table to get value at current cosine of the angle
1420 0 : Lo = FindArrayIndex(COSI, state.dataDaylightingDevices->COSAngle);
1421 0 : Hi = Lo + 1;
1422 :
1423 0 : if (Lo > 0 && Hi <= NumOfAngles) {
1424 0 : m = (transBeam(Hi) - transBeam(Lo)) / (state.dataDaylightingDevices->COSAngle(Hi) - state.dataDaylightingDevices->COSAngle(Lo));
1425 0 : b = transBeam(Lo) - m * state.dataDaylightingDevices->COSAngle(Lo);
1426 :
1427 0 : InterpolatePipeTransBeam = m * COSI + b;
1428 : } else {
1429 0 : InterpolatePipeTransBeam = 0.0;
1430 : }
1431 :
1432 0 : return InterpolatePipeTransBeam;
1433 : }
1434 :
1435 8 : int FindTDDPipe(EnergyPlusData &state, int const WinNum)
1436 : {
1437 :
1438 : // SUBROUTINE INFORMATION:
1439 : // AUTHOR Peter Graham Ellis
1440 : // DATE WRITTEN May 2003
1441 : // MODIFIED na
1442 : // RE-ENGINEERED na
1443 :
1444 : // PURPOSE OF THIS SUBROUTINE:
1445 : // Given the TDD:DOME or TDD:DIFFUSER object number, returns TDD pipe number.
1446 :
1447 : // Return value
1448 : int FindTDDPipe;
1449 :
1450 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1451 : int PipeNum; // TDD pipe object number
1452 :
1453 8 : FindTDDPipe = 0;
1454 :
1455 8 : if ((int)state.dataDaylightingDevicesData->TDDPipe.size() <= 0) {
1456 0 : ShowFatalError(state,
1457 0 : format("FindTDDPipe: Surface={}, TDD:Dome object does not reference a valid Diffuser object....needs "
1458 : "DaylightingDevice:Tubular of same name as Surface.",
1459 0 : state.dataSurface->Surface(WinNum).Name));
1460 : }
1461 :
1462 24 : for (PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
1463 32 : if ((WinNum == state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome) ||
1464 16 : (WinNum == state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser)) {
1465 0 : FindTDDPipe = PipeNum;
1466 0 : break;
1467 : }
1468 : } // PipeNum
1469 :
1470 8 : return FindTDDPipe;
1471 : }
1472 :
1473 249958 : void DistributeTDDAbsorbedSolar(EnergyPlusData &state)
1474 : {
1475 :
1476 : // SUBROUTINE INFORMATION:
1477 : // AUTHOR Peter Graham Ellis
1478 : // DATE WRITTEN July 2003
1479 : // MODIFIED na
1480 : // RE-ENGINEERED na
1481 :
1482 : // PURPOSE OF THIS SUBROUTINE:
1483 : // Sums the absorbed solar gains from TDD pipes that pass through transition zones.
1484 :
1485 : // METHODOLOGY EMPLOYED:
1486 : // The total absorbed solar gain is a sum of the following gains:
1487 : // 1. Inward bound solar absorbed by multiple pipe reflections (solar entering pipe - solar exiting pipe)
1488 : // 2. Outward bound solar absorbed by multiple pipe reflections due to:
1489 : // a. Reflection off of diffuser surface (inside of TDD)
1490 : // b. Zone diffuse interior shortwave incident on the diffuser from windows, lights, etc.
1491 : // 3. Inward absorbed solar in dome and diffuser glass
1492 : // This subroutine is called by InitIntSolarDistribution in HeatBalanceSurfaceManager.cc.
1493 :
1494 249958 : for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
1495 0 : int DiffSurf = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser;
1496 0 : Real64 transDiff = state.dataConstruction->Construct(state.dataSurface->Surface(DiffSurf).Construction).TransDiff;
1497 :
1498 : // Calculate diffuse solar reflected back up the pipe by the inside surface of the TDD:DIFFUSER
1499 : // All solar arriving at the diffuser is assumed to be isotropically diffuse by this point
1500 0 : Real64 QRefl = (state.dataHeatBal->SurfQRadSWOutIncident(DiffSurf) - state.dataHeatBal->SurfWinQRadSWwinAbsTot(DiffSurf)) *
1501 0 : state.dataSurface->Surface(DiffSurf).Area -
1502 0 : state.dataSurface->SurfWinTransSolar(DiffSurf);
1503 :
1504 : // Add diffuse interior shortwave reflected from zone surfaces and from zone sources, lights, etc.
1505 0 : QRefl += state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(DiffSurf).SolarEnclIndex) *
1506 0 : state.dataSurface->Surface(DiffSurf).Area * transDiff;
1507 :
1508 0 : Real64 TotTDDPipeGain = state.dataSurface->SurfWinTransSolar(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome) -
1509 0 : state.dataHeatBal->SurfQRadSWOutIncident(DiffSurf) * state.dataSurface->Surface(DiffSurf).Area +
1510 0 : QRefl * (1.0 - state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso / transDiff) +
1511 0 : state.dataHeatBal->SurfWinQRadSWwinAbs(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome, 1) *
1512 0 : state.dataSurface->Surface(DiffSurf).Area / 2.0 +
1513 0 : state.dataHeatBal->SurfWinQRadSWwinAbs(DiffSurf, 1) * state.dataSurface->Surface(DiffSurf).Area /
1514 0 : 2.0; // Solar entering pipe | Solar exiting pipe | Absorbed due to
1515 : // reflections on the way out | Inward absorbed solar from dome
1516 : // glass | Inward absorbed solar from diffuser glass
1517 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeAbsorbedSolar = max(0.0, TotTDDPipeGain); // Report variable [W]
1518 :
1519 0 : for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
1520 : // Distribute absorbed solar gain in proportion to transition zone length
1521 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain(TZoneNum) =
1522 0 : TotTDDPipeGain * (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) /
1523 0 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength);
1524 : } // TZoneNum
1525 : }
1526 249958 : }
1527 :
1528 0 : void CalcViewFactorToShelf(EnergyPlusData &state, int const ShelfNum) // Daylighting shelf object number
1529 : {
1530 :
1531 : // SUBROUTINE INFORMATION:
1532 : // AUTHOR Peter Graham Ellis
1533 : // DATE WRITTEN August 2003
1534 : // MODIFIED na
1535 : // RE-ENGINEERED na
1536 :
1537 : // PURPOSE OF THIS SUBROUTINE:
1538 : // Attempts to calculate exact analytical view factor from window to outside shelf.
1539 :
1540 : // METHODOLOGY EMPLOYED:
1541 : // Uses a standard analytical solution. It is required that window and shelf have the same width, i.e.
1542 : // one edge (or two vertices) shared in common. An error or warning is issued if not true.
1543 : // A more general routine should be implemented at some point to solve for more complicated geometries.
1544 : // Until then, the user has the option to specify their own solution for the view factor in the input object.
1545 :
1546 : // REFERENCES:
1547 : // Mills, A. F. Heat and Mass Transfer, 1995, p. 499. (Shape factor for adjacent rectangles.)
1548 :
1549 : // USE STATEMENTS:
1550 :
1551 : // Locals
1552 : // SUBROUTINE ARGUMENT DEFINITIONS:
1553 :
1554 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1555 : Real64 W; // Width, height, and length of window/shelf geometry
1556 : Real64 H;
1557 : Real64 L;
1558 : Real64 M; // Intermediate variables
1559 : Real64 N;
1560 : Real64 E1; // Intermediate equations
1561 : Real64 E2;
1562 : Real64 E3;
1563 : Real64 E4;
1564 : int VWin; // Vertex indices
1565 : int VShelf;
1566 : int NumMatch; // Number of vertices matched
1567 :
1568 0 : W = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Width;
1569 0 : H = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Height;
1570 :
1571 : // Find length, i.e. projection, of outside shelf
1572 0 : if (state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Width == W) {
1573 0 : L = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Height;
1574 0 : } else if (state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Height == W) {
1575 0 : L = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Width;
1576 : } else {
1577 0 : ShowFatalError(state,
1578 0 : format("DaylightingDevice:Shelf = {}: Width of window and outside shelf do not match.",
1579 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1580 : }
1581 :
1582 : // Error if more or less than two vertices match
1583 0 : NumMatch = 0;
1584 0 : for (VWin = 1; VWin <= 4; ++VWin) {
1585 0 : for (VShelf = 1; VShelf <= 4; ++VShelf) {
1586 0 : if (distance(state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Vertex(VWin),
1587 0 : state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Vertex(VShelf)) == 0.0)
1588 0 : ++NumMatch;
1589 : }
1590 : }
1591 :
1592 0 : if (NumMatch < 2) {
1593 0 : ShowWarningError(
1594 : state,
1595 0 : format("DaylightingDevice:Shelf = {}: Window and outside shelf must share two vertices. View factor calculation may be inaccurate.",
1596 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1597 0 : } else if (NumMatch > 2) {
1598 0 : ShowFatalError(state,
1599 0 : format("DaylightingDevice:Shelf = {}: Window and outside shelf share too many vertices.",
1600 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1601 : }
1602 :
1603 : // Calculate exact analytical view factor from window to outside shelf
1604 0 : M = H / W;
1605 0 : N = L / W;
1606 :
1607 0 : E1 = M * std::atan(1.0 / M) + N * std::atan(1.0 / N) - std::sqrt(pow_2(N) + pow_2(M)) * std::atan(std::pow(pow_2(N) + pow_2(M), -0.5));
1608 0 : E2 = ((1.0 + pow_2(M)) * (1.0 + pow_2(N))) / (1.0 + pow_2(M) + pow_2(N));
1609 0 : E3 = std::pow(pow_2(M) * (1.0 + pow_2(M) + pow_2(N)) / ((1.0 + pow_2(M)) * (pow_2(M) + pow_2(N))), pow_2(M));
1610 0 : E4 = std::pow(pow_2(N) * (1.0 + pow_2(M) + pow_2(N)) / ((1.0 + pow_2(N)) * (pow_2(M) + pow_2(N))), pow_2(N));
1611 :
1612 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor = (1.0 / (Constant::Pi * M)) * (E1 + 0.25 * std::log(E2 * E3 * E4));
1613 0 : }
1614 :
1615 9 : void adjustViewFactorsWithShelf(
1616 : EnergyPlusData &state, Real64 &viewFactorToShelf, Real64 &viewFactorToSky, Real64 &viewFactorToGround, int WinSurf, int ShelfNum)
1617 : {
1618 : // First, make sure none of the view factors are less than zero and return if there isn't a problem or if
1619 : // view factor to shelf greater than one. Both cases together would also eliminate if other views are zero
1620 : // which means nothing would need to be done.
1621 9 : if (viewFactorToSky <= 0.0) viewFactorToSky = 0.0;
1622 9 : if (viewFactorToGround <= 0.0) viewFactorToGround = 0.0;
1623 9 : if (viewFactorToShelf <= 0.0) { // No shelf impact for which to account
1624 4 : ShowWarningError(state,
1625 4 : format("DaylightingDevice:Shelf = {}: Window view factor to shelf was less than 0. This should not happen.",
1626 2 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1627 4 : ShowContinueError(state, "The view factor has been reset to zero.");
1628 2 : viewFactorToShelf = 0.0;
1629 2 : if ((viewFactorToGround + viewFactorToSky) > 1.0) { // This data came in incorrect, fix by proportional reduction
1630 1 : viewFactorToGround = viewFactorToGround / (viewFactorToGround + viewFactorToSky);
1631 1 : viewFactorToSky = 1.0 - viewFactorToGround;
1632 2 : ShowWarningError(state,
1633 2 : format("DaylightingDevice:Shelf = {}: The sum of the window view factors to ground and sky were greater than 1. "
1634 : "This should not happen.",
1635 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1636 3 : ShowContinueError(
1637 : state, "The view factors have been reset to so that they do not exceed 1. Check/fix your input file data to avoid this issue.");
1638 : }
1639 2 : return;
1640 : }
1641 7 : if (viewFactorToShelf + viewFactorToSky + viewFactorToGround <= 1.0) return; // nothing wrong here
1642 5 : if (viewFactorToShelf >= 1.0) { // Don't allow shelf view of greater than 1 (zero out other views)
1643 2 : ShowWarningError(state,
1644 2 : format("DaylightingDevice:Shelf = {}: Window view factor to shelf was greater than 1. This should not happen.",
1645 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1646 2 : ShowContinueError(state, "The view factor has been reset to 1 and the other view factors to sky and ground have been set to 0.");
1647 1 : viewFactorToShelf = 1.0;
1648 1 : viewFactorToGround = 0.0;
1649 1 : viewFactorToSky = 0.0;
1650 1 : return;
1651 : }
1652 :
1653 : // If the flow is still here, there is something that needs to be adjusted so set the maximum shelf height and the minimum window height
1654 4 : int ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf;
1655 4 : Real64 zShelfMax = state.dataSurface->Surface(ShelfSurf).Vertex(1).z;
1656 4 : Real64 zShelfMin = zShelfMax;
1657 16 : for (int vertex = 2; vertex <= state.dataSurface->Surface(ShelfSurf).Sides; ++vertex) {
1658 12 : if (state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z > zShelfMax)
1659 0 : zShelfMax = state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z;
1660 12 : if (state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z < zShelfMin)
1661 0 : zShelfMin = state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z;
1662 : }
1663 4 : Real64 zWinMax = state.dataSurface->Surface(WinSurf).Vertex(1).z;
1664 4 : Real64 zWinMin = zWinMax;
1665 16 : for (int vertex = 2; vertex <= state.dataSurface->Surface(WinSurf).Sides; ++vertex) {
1666 12 : if (state.dataSurface->Surface(WinSurf).Vertex(vertex).z > zWinMax) zWinMax = state.dataSurface->Surface(WinSurf).Vertex(vertex).z;
1667 12 : if (state.dataSurface->Surface(WinSurf).Vertex(vertex).z < zWinMin) zWinMin = state.dataSurface->Surface(WinSurf).Vertex(vertex).z;
1668 : }
1669 :
1670 : Real64 leftoverViewFactor;
1671 : // Now correct the view factors based on the location of the shelf with respect to the window
1672 8 : ShowWarningError(
1673 : state,
1674 8 : format("DaylightingDevice:Shelf = {}: Window view factor to shelf [{:.2R}] results in a sum of view factors greater than 1.",
1675 4 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name,
1676 4 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor));
1677 4 : if (zWinMin >= zShelfMax) { // Shelf is fully below window, reduce view to ground first based on view to shelf
1678 2 : ShowContinueError(
1679 : state,
1680 : "Since the light shelf is below the window to which it is associated, the view factor of the window to the ground was reduced");
1681 2 : ShowContinueError(
1682 : state, "and possibly also the view factor to the sky. Check you input and/or consider turning off autosizing of the view factors.");
1683 1 : leftoverViewFactor = 1.0 - viewFactorToShelf - viewFactorToSky;
1684 1 : if (leftoverViewFactor >= 0.0) {
1685 1 : viewFactorToGround = leftoverViewFactor; // Other view factors okay
1686 : } else {
1687 0 : viewFactorToGround = 0.0;
1688 0 : viewFactorToSky = 1.0 - viewFactorToShelf;
1689 0 : if (viewFactorToSky < 0.0) {
1690 0 : viewFactorToSky = 0.0;
1691 0 : viewFactorToShelf = 1.0;
1692 : }
1693 : }
1694 :
1695 3 : } else if (zShelfMin >= zWinMax) { // Shelf is fully above window, reduce view to sky first based on view to shelf
1696 2 : ShowContinueError(
1697 : state, "Since the light shelf is above the window to which it is associated, the view factor of the window to the sky was reduced");
1698 2 : ShowContinueError(
1699 : state,
1700 : "and possibly also the view factor to the ground. Check you input and/or consider turning off autosizing of the view factors.");
1701 1 : leftoverViewFactor = 1.0 - viewFactorToShelf - viewFactorToGround;
1702 1 : if (leftoverViewFactor >= 0.0) {
1703 1 : viewFactorToSky = leftoverViewFactor;
1704 : } else {
1705 0 : viewFactorToSky = 0.0;
1706 0 : viewFactorToGround = 1.0 - viewFactorToShelf;
1707 0 : if (viewFactorToGround < 0.0) {
1708 0 : viewFactorToGround = 0.0;
1709 0 : viewFactorToShelf = 1.0;
1710 : }
1711 : }
1712 : } else { // At least part of the shelf is somewhere in the middle of the window so we need to split out the view factors
1713 4 : ShowContinueError(
1714 : state,
1715 : "Since the light shelf is neither fully above or fully below the window to which it is associated, the view factor of the window");
1716 4 : ShowContinueError(
1717 : state,
1718 : "to the ground and sky were both potentially reduced. Check you input and/or consider turning off autosizing of the view factors.");
1719 : Real64 zShelfAvg;
1720 2 : if (((zShelfMin >= zWinMin) && (zShelfMax <= zWinMax)) || // Shelf does not go above or below the window
1721 0 : ((zShelfMin < zWinMin) && (zShelfMax > zWinMax))) { // Shelf goes both above AND below the window
1722 2 : zShelfAvg = 0.5 * (zShelfMin + zShelfMax);
1723 0 : } else if (zShelfMin < zWinMin) { // Shelf goes partially below the window only
1724 0 : Real64 fracAbove = 0.0;
1725 0 : if (zShelfMax > zShelfMin) {
1726 0 : fracAbove = (zShelfMax - zWinMin) / (zShelfMax - zShelfMin);
1727 0 : if (fracAbove > 1.0) fracAbove = 1.0;
1728 : }
1729 0 : zShelfAvg = zWinMin + fracAbove * (zShelfMax - zWinMin);
1730 : } else { // (zShelfMax > zWinMax): Shelf goes partially above window
1731 0 : Real64 fracBelow = 0.0;
1732 0 : if (zShelfMax > zShelfMin) {
1733 0 : fracBelow = (zWinMax - zShelfMin) / (zShelfMax - zShelfMin);
1734 : }
1735 0 : zShelfAvg = zWinMax - fracBelow * (zWinMax - zShelfMin);
1736 : }
1737 :
1738 : // Find height ratio based on shelf average height
1739 : Real64 heightRatio;
1740 2 : if (zWinMax > zWinMin) { // Window has a positive height
1741 2 : heightRatio = (zShelfAvg - zWinMin) / (zWinMax - zWinMin);
1742 2 : heightRatio = min(heightRatio, 1.0);
1743 2 : heightRatio = max(heightRatio, 0.0);
1744 : } else { // Window does not have a positive height (not realistic) so set height ratio based on shelf location
1745 0 : if (zShelfAvg > zWinMax) {
1746 0 : heightRatio = 1.0;
1747 : } else {
1748 0 : heightRatio = 0.0;
1749 : }
1750 : }
1751 :
1752 : // Take what is left over after the view to shelf is subtracted and then distribute/adjust that proportionally
1753 : // for the views to ground and sky based on their original weights. Finally, account for the location of the shelf
1754 : // with respect to the shelf and reset the values of the actual variables used in the rest of the simulation.
1755 2 : leftoverViewFactor = 1.0 - viewFactorToShelf; // By previous logic above, leftover is greater than zero and less than one
1756 : Real64 vfGroundAdjustMax;
1757 : Real64 vfGroundAdjustMin;
1758 2 : if (viewFactorToGround > viewFactorToShelf) { // How much view to ground could be reduced potentially if shelf at bottom
1759 1 : vfGroundAdjustMin = viewFactorToGround - viewFactorToShelf;
1760 : } else {
1761 1 : vfGroundAdjustMin = 0.0;
1762 : }
1763 2 : if (viewFactorToGround > leftoverViewFactor) { // How much view to ground could be reduced potentially if shelf at top
1764 0 : vfGroundAdjustMax = leftoverViewFactor;
1765 : } else {
1766 2 : vfGroundAdjustMax = viewFactorToGround;
1767 : }
1768 2 : viewFactorToGround = vfGroundAdjustMin + heightRatio * (vfGroundAdjustMax - vfGroundAdjustMin);
1769 2 : viewFactorToSky = leftoverViewFactor - viewFactorToGround;
1770 : }
1771 8 : ShowWarningError(state,
1772 8 : format("DaylightingDevice:Shelf = {}: As a result of user input (see previous messages), at least one view factor but "
1773 : "possibly more than one was reduced.",
1774 4 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1775 8 : ShowContinueError(state,
1776 : "These include the view factors to the ground, the sky, and the exterior light shelf. Note that views to other exterior "
1777 : "surfaces could further complicated this.");
1778 12 : ShowContinueError(state, "Please consider manually calculating or adjusting view factors to avoid this problem.");
1779 : }
1780 :
1781 249958 : void FigureTDDZoneGains(EnergyPlusData &state)
1782 : {
1783 :
1784 : // SUBROUTINE INFORMATION:
1785 : // AUTHOR B. Griffith
1786 : // DATE WRITTEN Dec 2011
1787 :
1788 : // PURPOSE OF THIS SUBROUTINE:
1789 : // initialize zone gains at begin new environment
1790 :
1791 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1792 :
1793 249958 : if ((int)state.dataDaylightingDevicesData->TDDPipe.size() == 0) return;
1794 :
1795 0 : if (state.dataGlobal->BeginEnvrnFlag && state.dataDaylightingDevices->MyEnvrnFlag) {
1796 0 : for (int Loop = 1; Loop <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++Loop) {
1797 0 : state.dataDaylightingDevicesData->TDDPipe(Loop).TZoneHeatGain = 0.0;
1798 : }
1799 0 : state.dataDaylightingDevices->MyEnvrnFlag = false;
1800 : }
1801 0 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataDaylightingDevices->MyEnvrnFlag = true;
1802 : }
1803 :
1804 : } // namespace Dayltg
1805 :
1806 : } // namespace EnergyPlus
|