Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <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 goemetry.
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 796 : 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 1 : TDDPipeStoredData() : AspectRatio(0.0), Reflectance(0.0), TransBeam(NumOfAngles, 0.0)
197 : {
198 1 : }
199 : };
200 :
201 : // Object Data
202 796 : Array1D<TDDPipeStoredData> TDDPipeStored;
203 :
204 : // Initialize tubular daylighting devices (TDDs)
205 796 : GetTDDInput(state);
206 :
207 796 : 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::DegToRadians / (NumOfAngles - 1.0);
214 1 : Real64 Theta = 90.0 * Constant::DegToRadians;
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) continue;
241 2 : if (TDDPipeStored(StoredNum).Reflectance == Reflectance) {
242 2 : Found = true; // StoredNum points to the matching TDDPipeStored
243 2 : break;
244 : }
245 : } // StoredNum
246 :
247 4 : if (!Found) { // Not yet calculated
248 :
249 : // Add a new pipe to TDDPipeStored
250 2 : ++NumStored;
251 2 : TDDPipeStored(NumStored).AspectRatio = state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio;
252 2 : TDDPipeStored(NumStored).Reflectance = Reflectance;
253 :
254 : // Set beam transmittances for 0 and 90 degrees
255 2 : TDDPipeStored(NumStored).TransBeam(1) = 0.0;
256 2 : TDDPipeStored(NumStored).TransBeam(NumOfAngles) = 1.0;
257 :
258 : // Calculate intermediate beam transmittances between 0 and 90 degrees
259 2 : Theta = 90.0 * Constant::DegToRadians;
260 36 : for (int AngleNum = 2; AngleNum <= NumOfAngles - 1; ++AngleNum) {
261 34 : Theta -= dTheta;
262 34 : TDDPipeStored(NumStored).TransBeam(AngleNum) =
263 34 : CalcPipeTransBeam(Reflectance, state.dataDaylightingDevicesData->TDDPipe(PipeNum).AspectRatio, Theta);
264 : } // AngleNum
265 :
266 2 : StoredNum = NumStored;
267 : }
268 :
269 : // Assign stored values to TDDPipe
270 4 : if (Loop == 1) { // Visible
271 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransVisBeam = TDDPipeStored(StoredNum).TransBeam;
272 : } else { // Solar
273 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransSolBeam = TDDPipeStored(StoredNum).TransBeam;
274 : }
275 :
276 : // Second time thru use the solar reflectance
277 4 : 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 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso = CalcTDDTransSolIso(state, PipeNum);
282 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolHorizon = CalcTDDTransSolHorizon(state, PipeNum);
283 :
284 : // Initialize thermal properties
285 2 : Real64 SumTZoneLengths = 0.0;
286 4 : for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
287 2 : SumTZoneLengths += state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum);
288 :
289 4 : SetupZoneInternalGain(state,
290 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum),
291 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name,
292 : DataHeatBalance::IntGainType::DaylightingDeviceTubular,
293 2 : &state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain(TZoneNum));
294 :
295 : } // TZoneNum
296 :
297 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).ExtLength =
298 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength - SumTZoneLengths;
299 :
300 : // Setup report variables: CurrentModuleObject='DaylightingDevice:Tubular'
301 4 : SetupOutputVariable(state,
302 : "Tubular Daylighting Device Transmitted Solar Radiation Rate",
303 : Constant::Units::W,
304 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransmittedSolar,
305 : OutputProcessor::TimeStepType::Zone,
306 : OutputProcessor::StoreType::Average,
307 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
308 4 : SetupOutputVariable(state,
309 : "Tubular Daylighting Device Pipe Absorbed Solar Radiation Rate",
310 : Constant::Units::W,
311 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeAbsorbedSolar,
312 : OutputProcessor::TimeStepType::Zone,
313 : OutputProcessor::StoreType::Average,
314 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
315 4 : SetupOutputVariable(state,
316 : "Tubular Daylighting Device Heat Gain Rate",
317 : Constant::Units::W,
318 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).HeatGain,
319 : OutputProcessor::TimeStepType::Zone,
320 : OutputProcessor::StoreType::Average,
321 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
322 4 : SetupOutputVariable(state,
323 : "Tubular Daylighting Device Heat Loss Rate",
324 : Constant::Units::W,
325 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).HeatLoss,
326 : OutputProcessor::TimeStepType::Zone,
327 : OutputProcessor::StoreType::Average,
328 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
329 :
330 4 : SetupOutputVariable(state,
331 : "Tubular Daylighting Device Beam Solar Transmittance",
332 : Constant::Units::None,
333 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolBeam,
334 : OutputProcessor::TimeStepType::Zone,
335 : OutputProcessor::StoreType::Average,
336 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
337 4 : SetupOutputVariable(state,
338 : "Tubular Daylighting Device Beam Visible Transmittance",
339 : Constant::Units::None,
340 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisBeam,
341 : OutputProcessor::TimeStepType::Zone,
342 : OutputProcessor::StoreType::Average,
343 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
344 4 : SetupOutputVariable(state,
345 : "Tubular Daylighting Device Diffuse Solar Transmittance",
346 : Constant::Units::None,
347 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolDiff,
348 : OutputProcessor::TimeStepType::Zone,
349 : OutputProcessor::StoreType::Average,
350 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
351 4 : SetupOutputVariable(state,
352 : "Tubular Daylighting Device Diffuse Visible Transmittance",
353 : Constant::Units::None,
354 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransVisDiff,
355 : OutputProcessor::TimeStepType::Zone,
356 : OutputProcessor::StoreType::Average,
357 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name);
358 :
359 : } // PipeNum
360 :
361 1 : TDDPipeStored.deallocate();
362 : }
363 :
364 : // Initialize daylighting shelves
365 796 : GetShelfInput(state);
366 :
367 796 : if ((int)state.dataDaylightingDevicesData->Shelf.size() > 0) DisplayString(state, "Initializing Light Shelf Daylighting Devices");
368 :
369 797 : for (int ShelfNum = 1; ShelfNum <= (int)state.dataDaylightingDevicesData->Shelf.size(); ++ShelfNum) {
370 1 : int WinSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).Window;
371 :
372 1 : int ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf;
373 1 : if (ShelfSurf > 0) {
374 : // Double surface area so that both sides of the shelf are treated as internal mass
375 1 : state.dataSurface->Surface(ShelfSurf).Area *= 2.0;
376 : }
377 :
378 1 : ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf;
379 1 : if (ShelfSurf > 0) {
380 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectVis =
381 1 : 1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction).OutsideAbsorpVis;
382 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).OutReflectSol =
383 1 : 1.0 - state.dataConstruction->Construct(state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction).OutsideAbsorpSolar;
384 :
385 1 : if (state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor < 0) CalcViewFactorToShelf(state, ShelfNum);
386 :
387 1 : adjustViewFactorsWithShelf(state,
388 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor,
389 1 : state.dataSurface->Surface(WinSurf).ViewFactorSky,
390 1 : 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 1 : if (!state.dataDaylightingDevices->ShelfReported) {
396 1 : 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 1 : state.dataDaylightingDevices->ShelfReported = true;
399 : }
400 1 : print(state.files.eio,
401 : "{},{:.2R},{},{:.2R},{:.2R}\n",
402 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name,
403 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor,
404 1 : state.dataSurface->Surface(WinSurf).Name,
405 1 : state.dataSurface->Surface(WinSurf).ViewFactorSky,
406 1 : 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 805 : if (state.dataSurface->CalcSolRefl &&
417 9 : ((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 796 : }
423 :
424 796 : 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 796 : auto &ipsc = state.dataIPShortCut;
438 796 : auto &cCurrentModuleObject = ipsc->cCurrentModuleObject;
439 :
440 796 : cCurrentModuleObject = "DaylightingDevice:Tubular";
441 796 : int NumOfTDDPipes = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
442 :
443 796 : if (NumOfTDDPipes > 0) {
444 1 : 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 3 : for (int PipeNum = 1; PipeNum <= NumOfTDDPipes; ++PipeNum) {
450 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
451 : cCurrentModuleObject,
452 : PipeNum,
453 2 : ipsc->cAlphaArgs,
454 : NumAlphas,
455 2 : ipsc->rNumericArgs,
456 : NumNumbers,
457 : IOStatus,
458 2 : ipsc->lNumericFieldBlanks,
459 2 : ipsc->lAlphaFieldBlanks,
460 2 : ipsc->cAlphaFieldNames,
461 2 : ipsc->cNumericFieldNames);
462 : // Pipe name
463 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Name = ipsc->cAlphaArgs(1);
464 :
465 : // Get TDD:DOME object
466 2 : int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataSurface->Surface);
467 :
468 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome = SurfNum;
536 2 : state.dataSurface->SurfWinTDDPipeNum(SurfNum) = PipeNum;
537 : }
538 :
539 : // Get TDD:DIFFUSER object
540 2 : SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(3), state.dataSurface->Surface);
541 :
542 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 4 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome > 0 &&
589 2 : std::abs(state.dataSurface->Surface(SurfNum).Area -
590 2 : 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 2 : 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 2 : 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 2 : 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 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser = SurfNum;
644 2 : state.dataSurface->SurfWinTDDPipeNum(SurfNum) = PipeNum;
645 : }
646 :
647 : // Construction
648 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction =
649 2 : Util::FindItemInList(ipsc->cAlphaArgs(4), state.dataConstruction->Construct);
650 :
651 2 : 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 2 : state.dataConstruction->Construct(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Construction).IsUsed = true;
657 : }
658 :
659 2 : if (ipsc->rNumericArgs(1) > 0) {
660 2 : 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 2 : Real64 PipeArea = 0.25 * Constant::Pi * pow_2(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diameter);
667 4 : if (state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome > 0 &&
668 2 : 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 2 : if (ipsc->rNumericArgs(2) > 0) {
692 2 : 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 2 : if (ipsc->rNumericArgs(3) > 0) {
699 2 : 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 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones = NumAlphas - 4;
710 :
711 2 : 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 2 : } 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 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone.allocate(state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
722 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength.allocate(
723 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
724 4 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain.allocate(
725 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones);
726 :
727 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone = 0;
728 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength = 0.0;
729 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain = 0.0;
730 :
731 4 : for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
732 2 : std::string const TZoneName = ipsc->cAlphaArgs(TZoneNum + 4);
733 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZone(TZoneNum) = Util::FindItemInList(TZoneName, state.dataHeatBal->Zone);
734 2 : 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 2 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) = ipsc->rNumericArgs(TZoneNum + 3);
741 2 : 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 2 : } // TZoneNum
750 : }
751 :
752 : } // PipeNum
753 :
754 1 : if (state.dataDaylightingDevices->GetTDDInputErrorsFound) ShowFatalError(state, "Errors in DaylightingDevice:Tubular input.");
755 1 : state.dataDayltg->TDDTransVisBeam.allocate(Constant::HoursInDay, NumOfTDDPipes);
756 1 : state.dataDayltg->TDDFluxInc.allocate(Constant::HoursInDay, NumOfTDDPipes);
757 1 : state.dataDayltg->TDDFluxTrans.allocate(Constant::HoursInDay, NumOfTDDPipes);
758 25 : for (int hr = 1; hr <= Constant::HoursInDay; ++hr) {
759 72 : for (int tddNum = 1; tddNum <= NumOfTDDPipes; ++tddNum) {
760 48 : state.dataDayltg->TDDTransVisBeam(hr, tddNum) = 0.0;
761 192 : state.dataDayltg->TDDFluxInc(hr, tddNum) = Illums();
762 192 : state.dataDayltg->TDDFluxTrans(hr, tddNum) = Illums();
763 : } // for (tddNum)
764 : } // for (hr)
765 : }
766 796 : }
767 :
768 796 : 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 796 : auto &ipsc = state.dataIPShortCut;
782 796 : auto &cCurrentModuleObject = ipsc->cCurrentModuleObject;
783 :
784 796 : cCurrentModuleObject = "DaylightingDevice:Shelf";
785 796 : int NumOfShelf = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
786 :
787 796 : if (NumOfShelf > 0) {
788 1 : 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 2 : for (int ShelfNum = 1; ShelfNum <= NumOfShelf; ++ShelfNum) {
794 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
795 : cCurrentModuleObject,
796 : ShelfNum,
797 1 : ipsc->cAlphaArgs,
798 : NumAlphas,
799 1 : ipsc->rNumericArgs,
800 : NumNumbers,
801 : IOStatus,
802 1 : ipsc->lNumericFieldBlanks,
803 1 : ipsc->lAlphaFieldBlanks,
804 1 : ipsc->cAlphaFieldNames,
805 1 : ipsc->cNumericFieldNames);
806 : // Shelf name
807 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name = ipsc->cAlphaArgs(1);
808 :
809 : // Get window object
810 1 : int SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(2), state.dataSurface->Surface);
811 :
812 1 : 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 1 : 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 1 : 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 1 : 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 1 : 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 1 : 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 1 : 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 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Window = SurfNum;
867 1 : state.dataSurface->SurfDaylightingShelfInd(SurfNum) = ShelfNum;
868 : }
869 :
870 : // Get inside shelf heat transfer surface (optional)
871 1 : if (ipsc->cAlphaArgs(3) != "") {
872 1 : SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(3), state.dataSurface->Surface);
873 :
874 1 : 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 1 : 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 1 : 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 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).InSurf = SurfNum;
899 : }
900 : }
901 :
902 : // Get outside shelf attached shading surface (optional)
903 1 : if (ipsc->cAlphaArgs(4) != "") {
904 1 : SurfNum = Util::FindItemInList(ipsc->cAlphaArgs(4), state.dataSurface->Surface);
905 :
906 1 : 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 1 : 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 1 : if (state.dataSurface->Surface(SurfNum).SchedShadowSurfIndex > 0) {
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 1 : 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 1 : int ConstrNum = 0;
940 : // Get outside shelf construction (required if outside shelf is specified)
941 1 : if (ipsc->cAlphaArgs(5) != "") {
942 1 : ConstrNum = Util::FindItemInList(ipsc->cAlphaArgs(5), state.dataConstruction->Construct);
943 :
944 1 : 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 1 : } 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 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Construction = ConstrNum;
960 1 : 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 1 : 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 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor =
982 : -1.0; // Flag to have the view factor calculated during initialization
983 : }
984 :
985 1 : 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 1 : state.dataSurface->Surface(SurfNum).BaseSurf = SurfNum;
990 1 : state.dataSurface->Surface(SurfNum).HeatTransSurf = true;
991 1 : state.dataSurface->AllHTSurfaceList.push_back(SurfNum);
992 : // Is this needed? surfZone.ZoneHTNonWindowSurfaceList.push_back(SurfNum);
993 1 : state.dataSurface->Surface(SurfNum).Construction = ConstrNum; // Kludge to allow shading surface to be a heat transfer surface
994 1 : state.dataSurface->SurfActiveConstruction(SurfNum) = ConstrNum;
995 1 : state.dataConstruction->Construct(ConstrNum).IsUsed = true;
996 : }
997 : }
998 :
999 1 : 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 1 : if (state.dataDaylightingDevices->GetShelfInputErrorsFound) ShowFatalError(state, "Errors in DaylightingDevice:Shelf input.");
1006 : }
1007 796 : }
1008 :
1009 34 : 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 34 : Real64 constexpr N(100000.0); // Number of integration points
1048 34 : 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 34 : 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 34 : CalcPipeTransBeam = 0.0;
1063 :
1064 34 : T = 0.0;
1065 34 : i = 1.0 / N;
1066 :
1067 34 : xLimit = (std::log(pow_2(N) * myLocalTiny) / std::log(R)) / xTol;
1068 :
1069 34 : c1 = A * std::tan(Theta);
1070 34 : c2 = 4.0 / Constant::Pi;
1071 :
1072 34 : s = i;
1073 3400000 : while (s < (1.0 - i)) {
1074 3399966 : x = c1 / s;
1075 :
1076 3399966 : if (x < xLimit) {
1077 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));
1078 3007257 : T += dT;
1079 : }
1080 :
1081 3399966 : s += i;
1082 : }
1083 :
1084 34 : T /= (N - 1.0); // - 1.0, because started on i, not 0
1085 :
1086 34 : CalcPipeTransBeam = T;
1087 :
1088 34 : return CalcPipeTransBeam;
1089 : }
1090 :
1091 2 : 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 2 : int constexpr NPH(1000); // Number of altitude integration points
1124 :
1125 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1126 2 : Real64 FluxInc = 0.0; // Incident solar flux
1127 2 : 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 2 : Real64 const dPH = 90.0 * Constant::DegToRadians / NPH; // Altitude angle of sky element
1133 2 : Real64 PH = 0.5 * dPH; // Altitude angle increment
1134 :
1135 : // Integrate from 0 to Pi/2 altitude
1136 2002 : for (int N = 1; N <= NPH; ++N) {
1137 2000 : COSI = std::cos(Constant::PiOvr2 - PH);
1138 2000 : SINI = std::sin(Constant::PiOvr2 - PH);
1139 :
1140 2000 : Real64 P = COSI; // Angular distribution function: P = COS(Incident Angle) for diffuse isotropic
1141 :
1142 : // Calculate total TDD transmittance for given angle
1143 2000 : trans = TransTDD(state, PipeNum, COSI, RadType::SolarBeam);
1144 :
1145 2000 : FluxInc += P * SINI * dPH;
1146 2000 : FluxTrans += trans * P * SINI * dPH;
1147 :
1148 2000 : PH += dPH; // Increment the altitude angle
1149 : } // N
1150 :
1151 2 : CalcTDDTransSolIso = FluxTrans / FluxInc;
1152 :
1153 2 : return CalcTDDTransSolIso;
1154 : }
1155 :
1156 2 : 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 2 : int constexpr NTH(18); // Number of azimuth integration points
1189 :
1190 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1191 2 : Real64 FluxInc = 0.0; // Incident solar flux
1192 2 : 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 2 : CosPhi = std::cos(Constant::PiOvr2 -
1197 2 : state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Tilt * Constant::DegToRadians);
1198 2 : Theta = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Azimuth * Constant::DegToRadians;
1199 :
1200 2 : if (CosPhi > 0.01) { // Dome has a view of the horizon
1201 : // Integrate over the semicircle
1202 2 : Real64 const THMIN = Theta - Constant::PiOvr2; // Minimum azimuth integration limit
1203 : // Real64 const THMAX = Theta + PiOvr2; // Maximum azimuth integration limit
1204 2 : Real64 const dTH = 180.0 * Constant::DegToRadians / NTH; // Azimuth angle increment
1205 2 : Real64 TH = THMIN + 0.5 * dTH; // Azimuth angle of sky horizon element
1206 :
1207 38 : for (int N = 1; N <= NTH; ++N) {
1208 : // Calculate incident angle between dome outward normal and horizon element
1209 36 : Real64 COSI = CosPhi * std::cos(TH - Theta); // Cosine of the incident angle
1210 :
1211 : // Calculate total TDD transmittance for given angle
1212 36 : Real64 trans = TransTDD(state, PipeNum, COSI, RadType::SolarBeam); // Total beam solar transmittance of TDD
1213 :
1214 36 : FluxInc += COSI * dTH;
1215 36 : FluxTrans += trans * COSI * dTH;
1216 :
1217 36 : TH += dTH; // Increment the azimuth angle
1218 : } // N
1219 :
1220 2 : 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 2 : return CalcTDDTransSolHorizon;
1227 : }
1228 :
1229 4928 : 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 4928 : DomeSurf = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome;
1273 :
1274 4928 : if (!state.dataSysVars->DetailedSkyDiffuseAlgorithm || !state.dataSurface->ShadingTransmittanceVaries ||
1275 0 : state.dataHeatBal->SolarDistribution == DataHeatBalance::Shadowing::Minimal) {
1276 4928 : IsoSkyRad = state.dataSolarShading->SurfMultIsoSky(DomeSurf) * state.dataSolarShading->SurfDifShdgRatioIsoSky(DomeSurf);
1277 4928 : 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 4928 : CircumSolarRad = state.dataSolarShading->SurfMultCircumSolar(DomeSurf) *
1284 4928 : state.dataHeatBal->SurfSunlitFrac(state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep, DomeSurf);
1285 :
1286 4928 : AnisoSkyTDDMult = state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso * IsoSkyRad +
1287 4928 : TransTDD(state, PipeNum, COSI, RadType::SolarBeam) * CircumSolarRad +
1288 4928 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolHorizon * HorizonRad;
1289 :
1290 4928 : if (state.dataSolarShading->SurfAnisoSkyMult(DomeSurf) > 0.0) {
1291 4928 : CalcTDDTransSolAniso = AnisoSkyTDDMult / state.dataSolarShading->SurfAnisoSkyMult(DomeSurf);
1292 : } else {
1293 0 : CalcTDDTransSolAniso = 0.0;
1294 : }
1295 :
1296 4928 : return CalcTDDTransSolAniso;
1297 : }
1298 :
1299 802540 : 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 : // Using/Aliasing
1332 : using General::POLYF;
1333 :
1334 : // Return value
1335 : Real64 TransTDD;
1336 :
1337 : // Locals
1338 : // FUNCTION ARGUMENT DEFINITIONS:
1339 :
1340 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1341 : int constDome; // Construction object number for TDD:DOME
1342 : int constDiff; // Construction object number for TDD:DIFFUSER
1343 : Real64 transDome;
1344 : Real64 transPipe;
1345 : Real64 transDiff;
1346 :
1347 802540 : TransTDD = 0.0;
1348 :
1349 : // Get constructions of each TDD component
1350 802540 : constDome = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome).Construction;
1351 802540 : constDiff = state.dataSurface->Surface(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser).Construction;
1352 :
1353 : // Get the transmittance of each component and of total TDD
1354 802540 : switch (RadiationType) {
1355 788268 : case RadType::VisibleBeam: {
1356 788268 : transDome = POLYF(COSI, state.dataConstruction->Construct(constDome).TransVisBeamCoef);
1357 788268 : transPipe = InterpolatePipeTransBeam(state, COSI, state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransVisBeam);
1358 788268 : transDiff = state.dataConstruction->Construct(constDiff).TransDiffVis; // May want to change to POLYF also!
1359 :
1360 788268 : TransTDD = transDome * transPipe * transDiff;
1361 :
1362 788268 : } break;
1363 9344 : case RadType::SolarBeam: {
1364 9344 : transDome = POLYF(COSI, state.dataConstruction->Construct(constDome).TransSolBeamCoef);
1365 9344 : transPipe = InterpolatePipeTransBeam(state, COSI, state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeTransSolBeam);
1366 9344 : transDiff = state.dataConstruction->Construct(constDiff).TransDiff; // May want to change to POLYF also!
1367 :
1368 9344 : TransTDD = transDome * transPipe * transDiff;
1369 :
1370 9344 : } break;
1371 4928 : case RadType::SolarAniso: {
1372 4928 : TransTDD = CalcTDDTransSolAniso(state, PipeNum, COSI);
1373 4928 : } break;
1374 0 : case RadType::SolarIso: {
1375 0 : TransTDD = state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso;
1376 0 : } break;
1377 0 : default:
1378 0 : break;
1379 : }
1380 :
1381 802540 : return TransTDD;
1382 : }
1383 :
1384 797612 : Real64 InterpolatePipeTransBeam(EnergyPlusData &state,
1385 : Real64 const COSI, // Cosine of the incident angle
1386 : const Array1D<Real64> &transBeam // Table of beam transmittance vs. cosine angle
1387 : )
1388 : {
1389 :
1390 : // SUBROUTINE INFORMATION:
1391 : // AUTHOR Peter Graham Ellis
1392 : // DATE WRITTEN July 2003
1393 : // MODIFIED na
1394 : // RE-ENGINEERED na
1395 :
1396 : // PURPOSE OF THIS SUBROUTINE:
1397 : // Interpolates the beam transmittance vs. cosine angle table.
1398 :
1399 : // METHODOLOGY EMPLOYED: na
1400 : // REFERENCES: na
1401 :
1402 : // Using/Aliasing
1403 : using FluidProperties::FindArrayIndex; // USEd code could be copied here to eliminate dependence on FluidProperties
1404 :
1405 : // Return value
1406 : Real64 InterpolatePipeTransBeam;
1407 :
1408 : // Argument array dimensioning
1409 797612 : EP_SIZE_CHECK(transBeam, NumOfAngles);
1410 :
1411 : // Locals
1412 : // FUNCTION ARGUMENT DEFINITIONS:
1413 :
1414 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1415 : int Lo;
1416 : int Hi;
1417 : Real64 m;
1418 : Real64 b;
1419 :
1420 797612 : InterpolatePipeTransBeam = 0.0;
1421 :
1422 : // Linearly interpolate transBeam/COSAngle table to get value at current cosine of the angle
1423 797612 : Lo = FindArrayIndex(COSI, state.dataDaylightingDevices->COSAngle);
1424 797612 : Hi = Lo + 1;
1425 :
1426 797612 : if (Lo > 0 && Hi <= NumOfAngles) {
1427 797192 : m = (transBeam(Hi) - transBeam(Lo)) / (state.dataDaylightingDevices->COSAngle(Hi) - state.dataDaylightingDevices->COSAngle(Lo));
1428 797192 : b = transBeam(Lo) - m * state.dataDaylightingDevices->COSAngle(Lo);
1429 :
1430 797192 : InterpolatePipeTransBeam = m * COSI + b;
1431 : } else {
1432 420 : InterpolatePipeTransBeam = 0.0;
1433 : }
1434 :
1435 797612 : return InterpolatePipeTransBeam;
1436 : }
1437 :
1438 4 : int FindTDDPipe(EnergyPlusData &state, int const WinNum)
1439 : {
1440 :
1441 : // SUBROUTINE INFORMATION:
1442 : // AUTHOR Peter Graham Ellis
1443 : // DATE WRITTEN May 2003
1444 : // MODIFIED na
1445 : // RE-ENGINEERED na
1446 :
1447 : // PURPOSE OF THIS SUBROUTINE:
1448 : // Given the TDD:DOME or TDD:DIFFUSER object number, returns TDD pipe number.
1449 :
1450 : // Return value
1451 : int FindTDDPipe;
1452 :
1453 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1454 : int PipeNum; // TDD pipe object number
1455 :
1456 4 : FindTDDPipe = 0;
1457 :
1458 4 : if ((int)state.dataDaylightingDevicesData->TDDPipe.size() <= 0) {
1459 0 : ShowFatalError(state,
1460 0 : format("FindTDDPipe: Surface={}, TDD:Dome object does not reference a valid Diffuser object....needs "
1461 : "DaylightingDevice:Tubular of same name as Surface.",
1462 0 : state.dataSurface->Surface(WinNum).Name));
1463 : }
1464 :
1465 12 : for (PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
1466 16 : if ((WinNum == state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome) ||
1467 8 : (WinNum == state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser)) {
1468 0 : FindTDDPipe = PipeNum;
1469 0 : break;
1470 : }
1471 : } // PipeNum
1472 :
1473 4 : return FindTDDPipe;
1474 : }
1475 :
1476 2804678 : void DistributeTDDAbsorbedSolar(EnergyPlusData &state)
1477 : {
1478 :
1479 : // SUBROUTINE INFORMATION:
1480 : // AUTHOR Peter Graham Ellis
1481 : // DATE WRITTEN July 2003
1482 : // MODIFIED na
1483 : // RE-ENGINEERED na
1484 :
1485 : // PURPOSE OF THIS SUBROUTINE:
1486 : // Sums the absorbed solar gains from TDD pipes that pass through transition zones.
1487 :
1488 : // METHODOLOGY EMPLOYED:
1489 : // The total absorbed solar gain is a sum of the following gains:
1490 : // 1. Inward bound solar absorbed by multiple pipe reflections (solar entering pipe - solar exiting pipe)
1491 : // 2. Outward bound solar absorbed by multiple pipe reflections due to:
1492 : // a. Reflection off of diffuser surface (inside of TDD)
1493 : // b. Zone diffuse interior shortwave incident on the diffuser from windows, lights, etc.
1494 : // 3. Inward absorbed solar in dome and diffuser glass
1495 : // This subroutine is called by InitIntSolarDistribution in HeatBalanceSurfaceManager.cc.
1496 :
1497 2808728 : for (int PipeNum = 1; PipeNum <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++PipeNum) {
1498 4050 : int DiffSurf = state.dataDaylightingDevicesData->TDDPipe(PipeNum).Diffuser;
1499 4050 : Real64 transDiff = state.dataConstruction->Construct(state.dataSurface->Surface(DiffSurf).Construction).TransDiff;
1500 :
1501 : // Calculate diffuse solar reflected back up the pipe by the inside surface of the TDD:DIFFUSER
1502 : // All solar arriving at the diffuser is assumed to be isotropically diffuse by this point
1503 4050 : Real64 QRefl = (state.dataHeatBal->SurfQRadSWOutIncident(DiffSurf) - state.dataHeatBal->SurfWinQRadSWwinAbsTot(DiffSurf)) *
1504 4050 : state.dataSurface->Surface(DiffSurf).Area -
1505 4050 : state.dataSurface->SurfWinTransSolar(DiffSurf);
1506 :
1507 : // Add diffuse interior shortwave reflected from zone surfaces and from zone sources, lights, etc.
1508 4050 : QRefl += state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(DiffSurf).SolarEnclIndex) *
1509 4050 : state.dataSurface->Surface(DiffSurf).Area * transDiff;
1510 :
1511 4050 : Real64 TotTDDPipeGain = state.dataSurface->SurfWinTransSolar(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome) -
1512 4050 : state.dataHeatBal->SurfQRadSWOutIncident(DiffSurf) * state.dataSurface->Surface(DiffSurf).Area +
1513 4050 : QRefl * (1.0 - state.dataDaylightingDevicesData->TDDPipe(PipeNum).TransSolIso / transDiff) +
1514 4050 : state.dataHeatBal->SurfWinQRadSWwinAbs(state.dataDaylightingDevicesData->TDDPipe(PipeNum).Dome, 1) *
1515 4050 : state.dataSurface->Surface(DiffSurf).Area / 2.0 +
1516 4050 : state.dataHeatBal->SurfWinQRadSWwinAbs(DiffSurf, 1) * state.dataSurface->Surface(DiffSurf).Area /
1517 4050 : 2.0; // Solar entering pipe | Solar exiting pipe | Absorbed due to
1518 : // reflections on the way out | Inward absorbed solar from dome
1519 : // glass | Inward absorbed solar from diffuser glass
1520 4050 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).PipeAbsorbedSolar = max(0.0, TotTDDPipeGain); // Report variable [W]
1521 :
1522 8100 : for (int TZoneNum = 1; TZoneNum <= state.dataDaylightingDevicesData->TDDPipe(PipeNum).NumOfTZones; ++TZoneNum) {
1523 : // Distribute absorbed solar gain in proportion to transition zone length
1524 4050 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneHeatGain(TZoneNum) =
1525 4050 : TotTDDPipeGain * (state.dataDaylightingDevicesData->TDDPipe(PipeNum).TZoneLength(TZoneNum) /
1526 4050 : state.dataDaylightingDevicesData->TDDPipe(PipeNum).TotLength);
1527 : } // TZoneNum
1528 : }
1529 2804678 : }
1530 :
1531 1 : void CalcViewFactorToShelf(EnergyPlusData &state, int const ShelfNum) // Daylighting shelf object number
1532 : {
1533 :
1534 : // SUBROUTINE INFORMATION:
1535 : // AUTHOR Peter Graham Ellis
1536 : // DATE WRITTEN August 2003
1537 : // MODIFIED na
1538 : // RE-ENGINEERED na
1539 :
1540 : // PURPOSE OF THIS SUBROUTINE:
1541 : // Attempts to calculate exact analytical view factor from window to outside shelf.
1542 :
1543 : // METHODOLOGY EMPLOYED:
1544 : // Uses a standard analytical solution. It is required that window and shelf have the same width, i.e.
1545 : // one edge (or two vertices) shared in common. An error or warning is issued if not true.
1546 : // A more general routine should be implemented at some point to solve for more complicated geometries.
1547 : // Until then, the user has the option to specify their own solution for the view factor in the input object.
1548 :
1549 : // REFERENCES:
1550 : // Mills, A. F. Heat and Mass Transfer, 1995, p. 499. (Shape factor for adjacent rectangles.)
1551 :
1552 : // USE STATEMENTS:
1553 :
1554 : // Locals
1555 : // SUBROUTINE ARGUMENT DEFINITIONS:
1556 :
1557 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1558 : Real64 W; // Width, height, and length of window/shelf geometry
1559 : Real64 H;
1560 : Real64 L;
1561 : Real64 M; // Intermediate variables
1562 : Real64 N;
1563 : Real64 E1; // Intermediate equations
1564 : Real64 E2;
1565 : Real64 E3;
1566 : Real64 E4;
1567 : int VWin; // Vertex indices
1568 : int VShelf;
1569 : int NumMatch; // Number of vertices matched
1570 :
1571 1 : W = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Width;
1572 1 : H = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Height;
1573 :
1574 : // Find length, i.e. projection, of outside shelf
1575 1 : if (state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Width == W) {
1576 1 : L = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Height;
1577 0 : } else if (state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Height == W) {
1578 0 : L = state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Width;
1579 : } else {
1580 0 : ShowFatalError(state,
1581 0 : format("DaylightingDevice:Shelf = {}: Width of window and outside shelf do not match.",
1582 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1583 : }
1584 :
1585 : // Error if more or less than two vertices match
1586 1 : NumMatch = 0;
1587 5 : for (VWin = 1; VWin <= 4; ++VWin) {
1588 20 : for (VShelf = 1; VShelf <= 4; ++VShelf) {
1589 16 : if (distance(state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).Window).Vertex(VWin),
1590 32 : state.dataSurface->Surface(state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf).Vertex(VShelf)) == 0.0)
1591 2 : ++NumMatch;
1592 : }
1593 : }
1594 :
1595 1 : if (NumMatch < 2) {
1596 0 : ShowWarningError(
1597 : state,
1598 0 : format("DaylightingDevice:Shelf = {}: Window and outside shelf must share two vertices. View factor calculation may be inaccurate.",
1599 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1600 1 : } else if (NumMatch > 2) {
1601 0 : ShowFatalError(state,
1602 0 : format("DaylightingDevice:Shelf = {}: Window and outside shelf share too many vertices.",
1603 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1604 : }
1605 :
1606 : // Calculate exact analytical view factor from window to outside shelf
1607 1 : M = H / W;
1608 1 : N = L / W;
1609 :
1610 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));
1611 1 : E2 = ((1.0 + pow_2(M)) * (1.0 + pow_2(N))) / (1.0 + pow_2(M) + pow_2(N));
1612 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));
1613 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));
1614 :
1615 1 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor = (1.0 / (Constant::Pi * M)) * (E1 + 0.25 * std::log(E2 * E3 * E4));
1616 1 : }
1617 :
1618 1 : void adjustViewFactorsWithShelf(
1619 : EnergyPlusData &state, Real64 &viewFactorToShelf, Real64 &viewFactorToSky, Real64 &viewFactorToGround, int WinSurf, int ShelfNum)
1620 : {
1621 : // First, make sure none of the view factors are less than zero and return if there isn't a problem or if
1622 : // view factor to shelf greater than one. Both cases together would also eliminate if other views are zero
1623 : // which means nothing would need to be done.
1624 1 : if (viewFactorToSky <= 0.0) viewFactorToSky = 0.0;
1625 1 : if (viewFactorToGround <= 0.0) viewFactorToGround = 0.0;
1626 1 : if (viewFactorToShelf <= 0.0) { // No shelf impact for which to account
1627 0 : ShowWarningError(state,
1628 0 : format("DaylightingDevice:Shelf = {}: Window view factor to shelf was less than 0. This should not happen.",
1629 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1630 0 : ShowContinueError(state, "The view factor has been reset to zero.");
1631 0 : viewFactorToShelf = 0.0;
1632 0 : if ((viewFactorToGround + viewFactorToSky) > 1.0) { // This data came in incorrect, fix by proportional reduction
1633 0 : viewFactorToGround = viewFactorToGround / (viewFactorToGround + viewFactorToSky);
1634 0 : viewFactorToSky = 1.0 - viewFactorToGround;
1635 0 : ShowWarningError(state,
1636 0 : format("DaylightingDevice:Shelf = {}: The sum of the window view factors to ground and sky were greater than 1. "
1637 : "This should not happen.",
1638 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1639 0 : ShowContinueError(
1640 : 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.");
1641 : }
1642 0 : return;
1643 : }
1644 1 : if (viewFactorToShelf + viewFactorToSky + viewFactorToGround <= 1.0) return; // nothing wrong here
1645 0 : if (viewFactorToShelf >= 1.0) { // Don't allow shelf view of greater than 1 (zero out other views)
1646 0 : ShowWarningError(state,
1647 0 : format("DaylightingDevice:Shelf = {}: Window view factor to shelf was greater than 1. This should not happen.",
1648 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1649 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.");
1650 0 : viewFactorToShelf = 1.0;
1651 0 : viewFactorToGround = 0.0;
1652 0 : viewFactorToSky = 0.0;
1653 0 : return;
1654 : }
1655 :
1656 : // 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
1657 0 : int ShelfSurf = state.dataDaylightingDevicesData->Shelf(ShelfNum).OutSurf;
1658 0 : Real64 zShelfMax = state.dataSurface->Surface(ShelfSurf).Vertex(1).z;
1659 0 : Real64 zShelfMin = zShelfMax;
1660 0 : for (int vertex = 2; vertex <= state.dataSurface->Surface(ShelfSurf).Sides; ++vertex) {
1661 0 : if (state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z > zShelfMax)
1662 0 : zShelfMax = state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z;
1663 0 : if (state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z < zShelfMin)
1664 0 : zShelfMin = state.dataSurface->Surface(ShelfSurf).Vertex(vertex).z;
1665 : }
1666 0 : Real64 zWinMax = state.dataSurface->Surface(WinSurf).Vertex(1).z;
1667 0 : Real64 zWinMin = zWinMax;
1668 0 : for (int vertex = 2; vertex <= state.dataSurface->Surface(WinSurf).Sides; ++vertex) {
1669 0 : if (state.dataSurface->Surface(WinSurf).Vertex(vertex).z > zWinMax) zWinMax = state.dataSurface->Surface(WinSurf).Vertex(vertex).z;
1670 0 : if (state.dataSurface->Surface(WinSurf).Vertex(vertex).z < zWinMin) zWinMin = state.dataSurface->Surface(WinSurf).Vertex(vertex).z;
1671 : }
1672 :
1673 : Real64 leftoverViewFactor;
1674 : // Now correct the view factors based on the location of the shelf with respect to the window
1675 0 : ShowWarningError(
1676 : state,
1677 0 : format("DaylightingDevice:Shelf = {}: Window view factor to shelf [{:.2R}] results in a sum of view factors greater than 1.",
1678 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name,
1679 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).ViewFactor));
1680 0 : if (zWinMin >= zShelfMax) { // Shelf is fully below window, reduce view to ground first based on view to shelf
1681 0 : ShowContinueError(
1682 : state,
1683 : "Since the light shelf is below the window to which it is associated, the view factor of the window to the ground was reduced");
1684 0 : ShowContinueError(
1685 : state, "and possibly also the view factor to the sky. Check you input and/or consider turning off autosizing of the view factors.");
1686 0 : leftoverViewFactor = 1.0 - viewFactorToShelf - viewFactorToSky;
1687 0 : if (leftoverViewFactor >= 0.0) {
1688 0 : viewFactorToGround = leftoverViewFactor; // Other view factors okay
1689 : } else {
1690 0 : viewFactorToGround = 0.0;
1691 0 : viewFactorToSky = 1.0 - viewFactorToShelf;
1692 0 : if (viewFactorToSky < 0.0) {
1693 0 : viewFactorToSky = 0.0;
1694 0 : viewFactorToShelf = 1.0;
1695 : }
1696 : }
1697 :
1698 0 : } else if (zShelfMin >= zWinMax) { // Shelf is fully above window, reduce view to sky first based on view to shelf
1699 0 : ShowContinueError(
1700 : 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");
1701 0 : ShowContinueError(
1702 : state,
1703 : "and possibly also the view factor to the ground. Check you input and/or consider turning off autosizing of the view factors.");
1704 0 : leftoverViewFactor = 1.0 - viewFactorToShelf - viewFactorToGround;
1705 0 : if (leftoverViewFactor >= 0.0) {
1706 0 : viewFactorToSky = leftoverViewFactor;
1707 : } else {
1708 0 : viewFactorToSky = 0.0;
1709 0 : viewFactorToGround = 1.0 - viewFactorToShelf;
1710 0 : if (viewFactorToGround < 0.0) {
1711 0 : viewFactorToGround = 0.0;
1712 0 : viewFactorToShelf = 1.0;
1713 : }
1714 : }
1715 : } else { // At least part of the shelf is somewhere in the middle of the window so we need to split out the view factors
1716 0 : ShowContinueError(
1717 : state,
1718 : "Since the light shelf is neither fully above or fully below the window to which it is associated, the view factor of the window");
1719 0 : ShowContinueError(
1720 : state,
1721 : "to the ground and sky were both potentially reduced. Check you input and/or consider turning off autosizing of the view factors.");
1722 : Real64 zShelfAvg;
1723 0 : if (((zShelfMin >= zWinMin) && (zShelfMax <= zWinMax)) || // Shelf does not go above or below the window
1724 0 : ((zShelfMin < zWinMin) && (zShelfMax > zWinMax))) { // Shelf goes both above AND below the window
1725 0 : zShelfAvg = 0.5 * (zShelfMin + zShelfMax);
1726 0 : } else if (zShelfMin < zWinMin) { // Shelf goes partially below the window only
1727 0 : Real64 fracAbove = 0.0;
1728 0 : if (zShelfMax > zShelfMin) {
1729 0 : fracAbove = (zShelfMax - zWinMin) / (zShelfMax - zShelfMin);
1730 0 : if (fracAbove > 1.0) fracAbove = 1.0;
1731 : }
1732 0 : zShelfAvg = zWinMin + fracAbove * (zShelfMax - zWinMin);
1733 : } else { // (zShelfMax > zWinMax): Shelf goes partially above window
1734 0 : Real64 fracBelow = 0.0;
1735 0 : if (zShelfMax > zShelfMin) {
1736 0 : fracBelow = (zWinMax - zShelfMin) / (zShelfMax - zShelfMin);
1737 : }
1738 0 : zShelfAvg = zWinMax - fracBelow * (zWinMax - zShelfMin);
1739 : }
1740 :
1741 : // Find height ratio based on shelf average height
1742 : Real64 heightRatio;
1743 0 : if (zWinMax > zWinMin) { // Window has a positive height
1744 0 : heightRatio = (zShelfAvg - zWinMin) / (zWinMax - zWinMin);
1745 0 : heightRatio = min(heightRatio, 1.0);
1746 0 : heightRatio = max(heightRatio, 0.0);
1747 : } else { // Window does not have a positive height (not realistic) so set height ratio based on shelf location
1748 0 : if (zShelfAvg > zWinMax) {
1749 0 : heightRatio = 1.0;
1750 : } else {
1751 0 : heightRatio = 0.0;
1752 : }
1753 : }
1754 :
1755 : // Take what is left over after the view to shelf is subtracted and then distribute/adjust that proportionally
1756 : // for the views to ground and sky based on their original weights. Finally, account for the location of the shelf
1757 : // with respect to the shelf and reset the values of the actual variables used in the rest of the simulation.
1758 0 : leftoverViewFactor = 1.0 - viewFactorToShelf; // By previous logic above, leftover is greater than zero and less than one
1759 : Real64 vfGroundAdjustMax;
1760 : Real64 vfGroundAdjustMin;
1761 0 : if (viewFactorToGround > viewFactorToShelf) { // How much view to ground could be reduced potentially if shelf at bottom
1762 0 : vfGroundAdjustMin = viewFactorToGround - viewFactorToShelf;
1763 : } else {
1764 0 : vfGroundAdjustMin = 0.0;
1765 : }
1766 0 : if (viewFactorToGround > leftoverViewFactor) { // How much view to ground could be reduced potentially if shelf at top
1767 0 : vfGroundAdjustMax = leftoverViewFactor;
1768 : } else {
1769 0 : vfGroundAdjustMax = viewFactorToGround;
1770 : }
1771 0 : viewFactorToGround = vfGroundAdjustMin + heightRatio * (vfGroundAdjustMax - vfGroundAdjustMin);
1772 0 : viewFactorToSky = leftoverViewFactor - viewFactorToGround;
1773 : }
1774 0 : ShowWarningError(state,
1775 0 : format("DaylightingDevice:Shelf = {}: As a result of user input (see previous messages), at least one view factor but "
1776 : "possibly more than one was reduced.",
1777 0 : state.dataDaylightingDevicesData->Shelf(ShelfNum).Name));
1778 0 : ShowContinueError(state,
1779 : "These include the view factors to the ground, the sky, and the exterior light shelf. Note that views to other exterior "
1780 : "surfaces could further complicated this.");
1781 0 : ShowContinueError(state, "Please consider manually calculating or adjusting view factors to avoid this problem.");
1782 : }
1783 :
1784 2804678 : void FigureTDDZoneGains(EnergyPlusData &state)
1785 : {
1786 :
1787 : // SUBROUTINE INFORMATION:
1788 : // AUTHOR B. Griffith
1789 : // DATE WRITTEN Dec 2011
1790 :
1791 : // PURPOSE OF THIS SUBROUTINE:
1792 : // intialize zone gains at begin new environment
1793 :
1794 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1795 :
1796 2804678 : if ((int)state.dataDaylightingDevicesData->TDDPipe.size() == 0) return;
1797 :
1798 2025 : if (state.dataGlobal->BeginEnvrnFlag && state.dataDaylightingDevices->MyEnvrnFlag) {
1799 15 : for (int Loop = 1; Loop <= (int)state.dataDaylightingDevicesData->TDDPipe.size(); ++Loop) {
1800 10 : state.dataDaylightingDevicesData->TDDPipe(Loop).TZoneHeatGain = 0.0;
1801 : }
1802 5 : state.dataDaylightingDevices->MyEnvrnFlag = false;
1803 : }
1804 2025 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataDaylightingDevices->MyEnvrnFlag = true;
1805 : }
1806 :
1807 : } // namespace Dayltg
1808 :
1809 : } // namespace EnergyPlus
|