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