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 <cassert>
50 : #include <cmath>
51 : #include <cstdint>
52 :
53 : // ObjexxFCL Headers
54 : #include <ObjexxFCL/Array.functions.hh>
55 : #include <ObjexxFCL/ArrayS.functions.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/Construction.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataEnvironment.hh>
61 : #include <EnergyPlus/DataHeatBalSurface.hh>
62 : #include <EnergyPlus/DataHeatBalance.hh>
63 : #include <EnergyPlus/DataLoopNode.hh>
64 : #include <EnergyPlus/DataShadowingCombinations.hh>
65 : #include <EnergyPlus/DataSurfaces.hh>
66 : #include <EnergyPlus/DataSystemVariables.hh>
67 : #include <EnergyPlus/DataViewFactorInformation.hh>
68 : #include <EnergyPlus/DataZoneEquipment.hh>
69 : #include <EnergyPlus/HeatBalanceSurfaceManager.hh>
70 : #include <EnergyPlus/Material.hh>
71 : #include <EnergyPlus/PierceSurface.hh>
72 : #include <EnergyPlus/Psychrometrics.hh>
73 : #include <EnergyPlus/ScheduleManager.hh>
74 : #include <EnergyPlus/TARCOGGassesParams.hh>
75 : #include <EnergyPlus/TARCOGMain.hh>
76 : #include <EnergyPlus/TARCOGParams.hh>
77 : #include <EnergyPlus/UtilityRoutines.hh>
78 : #include <EnergyPlus/Vectors.hh>
79 : #include <EnergyPlus/WindowComplexManager.hh>
80 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
81 :
82 : namespace EnergyPlus {
83 :
84 : namespace WindowComplexManager {
85 :
86 : // Module containing the routines dealing with complex fenestration
87 :
88 : // MODULE INFORMATION:
89 : // AUTHOR Joe Klems
90 : // DATE WRITTEN ???
91 : // MODIFIED November 2011, Simon Vidanovic
92 : // RE-ENGINEERED na
93 :
94 : // PURPOSE OF THIS MODULE:
95 : // Initialize data for solar and thermal calculations and also performs thermal calculations for BSDF window
96 :
97 : using namespace DataVectorTypes;
98 : using namespace DataBSDFWindow;
99 : using namespace DataSurfaces; // , ONLY: TotSurfaces,TotWindows,Surface,SurfaceWindow !update this later
100 : using namespace DataHeatBalance;
101 : using namespace DataShadowingCombinations;
102 : using namespace Vectors;
103 :
104 801 : void InitBSDFWindows(EnergyPlusData &state)
105 : {
106 :
107 : // SUBROUTINE INFORMATION:
108 : // AUTHOR Joe Klems
109 : // DATE WRITTEN August 2011
110 : // MODIFIED na
111 : // RE-ENGINEERED na
112 :
113 : // PURPOSE OF THIS SUBROUTINE:
114 : // Set up the overall optical geometry for a BSDF window
115 :
116 : using namespace Vectors;
117 :
118 : int BaseSurf; // base surface number (used in finding back surface)
119 : int NumStates; // Local variable for the number of states
120 801 : Array1D<Real64> Thetas; // temp array holding theta values
121 801 : Array1D_int NPhis; // temp array holding number of phis for a given theta
122 801 : Array1D<Real64> V(3); // vector array
123 : Real64 VLen; // Length of vector array
124 : int NHold; // No. values in the Temporary array
125 :
126 : struct TempBasisIdx
127 : {
128 : // Members
129 : int Basis; // Basis no in basis table
130 : int State; // State in which basis first occurs
131 :
132 : // Default Constructor
133 0 : TempBasisIdx() : Basis(0), State(0)
134 : {
135 0 : }
136 : };
137 :
138 : // Object Data
139 801 : Array1D<TempBasisIdx> IHold; // Temporary array
140 :
141 801 : if (state.dataBSDFWindow->TotComplexFenStates <= 0) {
142 791 : return; // Nothing to do if no complex fenestration states
143 : }
144 : // Construct Basis List
145 10 : state.dataWindowComplexManager->BasisList.allocate(state.dataBSDFWindow->TotComplexFenStates);
146 :
147 : // Note: Construction of the basis list contains the assumption of identical incoming and outgoing bases in
148 : // that the complex fenestration state definition contains only one basis description, hence
149 : // assumes square property matrices. If this assumption were relaxed through change of the
150 : // definition or additional definition of a state type with non-square matrices, then the loop
151 : // below should be modified to enter both of the bases into the basis list.
152 :
153 24 : for (int IConst = state.dataBSDFWindow->FirstBSDF; IConst <= state.dataBSDFWindow->FirstBSDF + state.dataBSDFWindow->TotComplexFenStates - 1;
154 : ++IConst) {
155 14 : state.dataWindowComplexManager->MatrixNo = state.dataConstruction->Construct(IConst).BSDFInput.BasisMatIndex;
156 14 : if (state.dataWindowComplexManager->NumBasis == 0) {
157 10 : state.dataWindowComplexManager->NumBasis = 1;
158 10 : ConstructBasis(state, IConst, state.dataWindowComplexManager->BasisList(1));
159 : } else {
160 11 : for (int IBasis = 1; IBasis <= state.dataWindowComplexManager->NumBasis; ++IBasis) {
161 7 : if (state.dataWindowComplexManager->MatrixNo == state.dataWindowComplexManager->BasisList(IBasis).BasisMatIndex) {
162 0 : goto BsLoop_loop;
163 : }
164 : }
165 4 : ++state.dataWindowComplexManager->NumBasis;
166 4 : ConstructBasis(state, IConst, state.dataWindowComplexManager->BasisList(state.dataWindowComplexManager->NumBasis));
167 : }
168 14 : BsLoop_loop:;
169 : }
170 10 : state.dataWindowComplexManager->BasisList.redimension(state.dataWindowComplexManager->NumBasis);
171 : // Proceed to set up geometry for complex fenestration states
172 10 : state.dataBSDFWindow->ComplexWind.allocate(state.dataSurface->TotSurfaces); // Set up companion array to SurfaceWindow to hold window
173 : // geometry for each state. This is an allocatable array of
174 : // geometries for the window states but only the complex
175 : // fenestration surfaces will have the arrays allocated
176 : // Search Thru Surfaces for Complex Fenestration State references
177 : // This will define the first complex fenestration state for that window, others will follow if there are
178 : // control specifications
179 10 : state.dataWindowComplexManager->WindowList.allocate(state.dataSurface->TotSurfaces); // Temporary allocation
180 20 : state.dataWindowComplexManager->WindowStateList.allocate(state.dataBSDFWindow->TotComplexFenStates,
181 10 : state.dataSurface->TotSurfaces); // Temporary allocation
182 125 : for (int ISurf = 1; ISurf <= state.dataSurface->TotSurfaces; ++ISurf) {
183 115 : int IConst = state.dataSurface->Surface(ISurf).Construction;
184 115 : if (IConst == 0) {
185 0 : continue; // This is true for overhangs (Shading:Zone:Detailed)
186 : }
187 115 : if (!(state.dataConstruction->Construct(IConst).TypeIsWindow && (state.dataConstruction->Construct(IConst).WindowTypeBSDF))) {
188 91 : continue; // Only BSDF windows
189 : }
190 : // Simon Check: Thermal construction removed
191 : // ThConst = Construct(IConst)%BSDFInput%ThermalConstruction
192 24 : state.dataSurface->SurfWinWindowModelType(ISurf) = WindowModel::BSDF;
193 24 : state.dataHeatBal->AnyBSDF = true;
194 24 : ++state.dataWindowComplexManager->NumComplexWind;
195 24 : NumStates = 1;
196 24 : state.dataWindowComplexManager->WindowList(state.dataWindowComplexManager->NumComplexWind).NumStates =
197 : 1; // Having found the construction reference in
198 : // the Surface array defines the first state for this window
199 24 : state.dataWindowComplexManager->WindowList(state.dataWindowComplexManager->NumComplexWind).SurfNo = ISurf;
200 : // WindowList( NumComplexWind ).Azimuth = DegToRadians * Surface( ISurf ).Azimuth;
201 : // WindowList( NumComplexWind ).Tilt = DegToRadians * Surface( ISurf ).Tilt;
202 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).InitInc =
203 24 : state.dataWindowComplexManager->Calculate_Geometry;
204 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).InitTrn =
205 24 : state.dataWindowComplexManager->Calculate_Geometry;
206 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).CopyIncState = 0;
207 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).CopyTrnState = 0;
208 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).Konst = IConst;
209 : // Simon Check: ThermalConstruction assigned to current construction
210 : // WindowStateList(NumComplexWind, NumStates)%ThermConst = ThConst
211 72 : for (int I = 1; I <= state.dataWindowComplexManager->NumBasis; ++I) { // Find basis in Basis List
212 48 : if (state.dataConstruction->Construct(IConst).BSDFInput.BasisMatIndex == state.dataWindowComplexManager->BasisList(I).BasisMatIndex) {
213 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).IncBasisIndx =
214 : I; // Note: square property matrices
215 24 : state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).TrnBasisIndx =
216 : I; // assumption
217 : }
218 : }
219 24 : if (state.dataWindowComplexManager->WindowStateList(NumStates, state.dataWindowComplexManager->NumComplexWind).IncBasisIndx <= 0) {
220 0 : ShowFatalError(state, "Complex Window Init: Window Basis not in BasisList.");
221 : }
222 : }
223 : // Should now have a WindowList with dataWindowComplexManager. NumComplexWind entries containing all the complex fenestrations
224 : // with a first state defined for each.
225 : // * * *
226 : // Here a search should be made for control specifications, which will give additional states for
227 : // controlled complex fenestrations. These should be added to the dataWindowComplexManager.WindowStateList, and
228 : // WindowList( )%NumStates incremented for each window for which states are added.
229 : // Added states should have WindowStateList ( , )%InitInc set to Calculate_Geometry
230 : // * * *
231 :
232 : // At this point, we have a complete WindowList and WindowStateList, with dataWindowComplexManager. NumComplexWind
233 : // defined, and NumStates for each complex window defined
234 : // Now sort through the window list to see that geometry will only be done once for each
235 : // window, basis combination
236 : // Note: code below assumes identical incoming and outgoing bases; following code will
237 : // need revision if this assumption relaxed
238 :
239 34 : for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) { // Search window list for repeated bases
240 24 : if (state.dataWindowComplexManager->WindowList(IWind).NumStates > 1) {
241 0 : IHold.allocate(state.dataWindowComplexManager->WindowList(IWind).NumStates);
242 0 : NHold = 1;
243 0 : IHold(1).State = 1;
244 0 : IHold(1).Basis = state.dataWindowComplexManager->WindowStateList(1, IWind).IncBasisIndx;
245 : // If the Mth new basis found is basis B in the basis list, and it
246 : // first occurs in the WindowStateList in state N, then IHold(M)%Basis=B
247 : // and IHold(M)%State=N
248 0 : for (int K = 1; K <= state.dataWindowComplexManager->NumBasis; ++K) {
249 0 : if (K > NHold) {
250 0 : break;
251 : }
252 0 : int KBasis = IHold(K).Basis;
253 0 : int J = IHold(K).State;
254 0 : state.dataWindowComplexManager->InitBSDFWindowsOnce = true;
255 0 : for (int I = J + 1; I <= state.dataWindowComplexManager->WindowList(IWind).NumStates;
256 : ++I) { // See if subsequent states have the same basis
257 0 : if ((state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitInc ==
258 0 : state.dataWindowComplexManager->Calculate_Geometry) &&
259 0 : (state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).IncBasisIndx ==
260 : KBasis)) {
261 : // Note: square property matrices (same inc & trn bases) assumption
262 : // If same incident and outgoing basis assumption removed, following code will need to
263 : // be extended to treat the two bases separately
264 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitInc =
265 0 : state.dataWindowComplexManager->Copy_Geometry;
266 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitTrn =
267 0 : state.dataWindowComplexManager->Copy_Geometry;
268 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyIncState = J;
269 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyTrnState = J;
270 0 : } else if (state.dataWindowComplexManager->InitBSDFWindowsOnce) {
271 0 : state.dataWindowComplexManager->InitBSDFWindowsOnce = false; // First occurrence of a different basis
272 0 : ++NHold;
273 0 : IHold(NHold).State = I;
274 0 : IHold(NHold).Basis = state.dataWindowComplexManager->WindowStateList(I, IWind).IncBasisIndx;
275 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).InitTrn =
276 0 : state.dataWindowComplexManager->Calculate_Geometry;
277 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyIncState = 0;
278 0 : state.dataWindowComplexManager->WindowStateList(I, state.dataWindowComplexManager->NumComplexWind).CopyTrnState = 0;
279 : }
280 : }
281 : }
282 0 : IHold.deallocate();
283 : }
284 : }
285 :
286 : // Now go through window list and window state list and calculate or copy the
287 : // geometry information for each window, state
288 34 : for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
289 24 : int ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
290 24 : NumStates = state.dataWindowComplexManager->WindowList(IWind).NumStates;
291 : // ALLOCATE(SurfaceWindow( ISurf )%ComplexFen) !activate the BSDF window description
292 : // for this surface
293 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.NumStates = NumStates;
294 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State.allocate(NumStates); // Allocate space for the states
295 24 : state.dataBSDFWindow->ComplexWind(ISurf).NumStates = NumStates;
296 24 : state.dataBSDFWindow->ComplexWind(ISurf).Geom.allocate(NumStates); // Allocate space for the geometries
297 : // Azimuth = WindowList( IWind ).Azimuth;
298 : // Tilt = WindowList( IWind ).Tilt;
299 : // Get the number of back surfaces for this window
300 24 : BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
301 24 : int NBkSurf = state.dataShadowComb->ShadowComb(BaseSurf).NumBackSurf;
302 24 : state.dataBSDFWindow->ComplexWind(ISurf).NBkSurf = NBkSurf;
303 : // Define the back surface directions
304 24 : state.dataBSDFWindow->ComplexWind(ISurf).sWinSurf.allocate(NBkSurf);
305 24 : state.dataBSDFWindow->ComplexWind(ISurf).sdotN.allocate(NBkSurf);
306 : // Define the unit vectors pointing from the window center to the back surface centers
307 180 : for (int KBkSurf = 1; KBkSurf <= NBkSurf; ++KBkSurf) {
308 156 : BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
309 156 : int JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf); // these are all proper back surfaces
310 156 : V = state.dataSurface->Surface(JSurf).Centroid - state.dataSurface->Surface(ISurf).Centroid;
311 156 : VLen = magnitude(V);
312 : // Define the unit vector from the window center to the back
313 156 : state.dataBSDFWindow->ComplexWind(ISurf).sWinSurf(KBkSurf) = V / VLen;
314 : // surface center
315 : // Define the back surface cosine(incident angle)
316 156 : state.dataBSDFWindow->ComplexWind(ISurf).sdotN(KBkSurf) = dot(V, state.dataSurface->Surface(JSurf).OutNormVec) / VLen;
317 : }
318 48 : for (int IState = 1; IState <= NumStates; ++IState) {
319 : // The following assumes identical incoming and outgoing bases. The logic will need to be
320 : // redesigned if this assumption is relaxed
321 24 : int IConst = state.dataWindowComplexManager->WindowStateList(IState, IWind).Konst;
322 : // ThConst = WindowStateList ( IWind , IState )%ThermConst
323 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst = IConst;
324 : // SurfaceWindow(ISurf)%ComplexFen%State(IState)%ThermConst = ThConst
325 24 : if (state.dataWindowComplexManager->WindowStateList(IState, IWind).InitInc == state.dataWindowComplexManager->Calculate_Geometry) {
326 48 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState).Inc = state.dataWindowComplexManager->BasisList(
327 48 : state.dataWindowComplexManager->WindowStateList(IState, IWind).IncBasisIndx); // Put in the basis structure from the BasisList
328 24 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState).Trn =
329 48 : state.dataWindowComplexManager->BasisList(state.dataWindowComplexManager->WindowStateList(IState, IWind).TrnBasisIndx);
330 :
331 24 : SetupComplexWindowStateGeometry(state,
332 : ISurf,
333 : IState,
334 : IConst,
335 24 : state.dataBSDFWindow->ComplexWind(ISurf),
336 24 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState),
337 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState));
338 : // Note--setting up the state geometry will include constructing outgoing basis/surface
339 : // maps and those incoming maps that will not depend on shading.
340 : } else {
341 0 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState) = state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(
342 0 : state.dataWindowComplexManager->WindowStateList(IState, IWind).CopyIncState); // Note this overwrites Konst
343 0 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst = IConst; // so it has to be put back
344 : // SurfaceWindow (ISurf )%ComplexFen%State(IState)%ThermConst = ThConst !same for ThermConst
345 0 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState) =
346 0 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(state.dataWindowComplexManager->WindowStateList(IState, IWind).CopyIncState);
347 : }
348 :
349 : } // State loop
350 : } // Complex Window loop
351 : // Allocate all beam-dependent complex fenestration quantities
352 34 : for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
353 24 : int ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
354 24 : NumStates = state.dataWindowComplexManager->WindowList(IWind).NumStates;
355 48 : for (int IState = 1; IState <= NumStates; ++IState) {
356 24 : AllocateCFSStateHourlyData(state, ISurf, IState);
357 : } // State loop
358 : } // Complex Window loop
359 3174 : }
360 :
361 24 : void AllocateCFSStateHourlyData(EnergyPlusData &state,
362 : int const iSurf, // Surface number
363 : int const iState // Complex fenestration state number
364 : )
365 : {
366 :
367 : // SUBROUTINE INFORMATION:
368 : // AUTHOR Simon Vidanovic
369 : // DATE WRITTEN May 2013
370 : // MODIFIED na
371 : // RE-ENGINEERED na
372 :
373 : // PURPOSE OF THIS SUBROUTINE:
374 : // Allocate hourly data arrays for complex fenestration state
375 :
376 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
377 : int NLayers; // Number of complex fenestration layers
378 : int NBkSurf; // Number of back surfaces
379 : int KBkSurf; // Back surfaces counter
380 :
381 24 : NLayers = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).NLayers;
382 24 : NBkSurf = state.dataBSDFWindow->ComplexWind(iSurf).NBkSurf;
383 :
384 48 : state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).SolBmGndWt.allocate(
385 24 : 24, state.dataGlobal->TimeStepsInHour, state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).NGnd);
386 24 : state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).SolBmIndex.allocate(24, state.dataGlobal->TimeStepsInHour);
387 24 : state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).ThetaBm.allocate(24, state.dataGlobal->TimeStepsInHour);
388 24 : state.dataBSDFWindow->ComplexWind(iSurf).Geom(iState).PhiBm.allocate(24, state.dataGlobal->TimeStepsInHour);
389 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinDirHemiTrans.allocate(24, state.dataGlobal->TimeStepsInHour);
390 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinDirSpecTrans.allocate(24, state.dataGlobal->TimeStepsInHour);
391 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinBmGndTrans.allocate(24, state.dataGlobal->TimeStepsInHour);
392 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinBmFtAbs.allocate(24, state.dataGlobal->TimeStepsInHour, NLayers);
393 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinBmGndAbs.allocate(24, state.dataGlobal->TimeStepsInHour, NLayers);
394 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).WinToSurfBmTrans.allocate(24, state.dataGlobal->TimeStepsInHour, NBkSurf);
395 24 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).BkSurf.allocate(NBkSurf);
396 180 : for (KBkSurf = 1; KBkSurf <= NBkSurf; ++KBkSurf) {
397 312 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).BkSurf(KBkSurf).WinDHBkRefl.allocate(24,
398 156 : state.dataGlobal->TimeStepsInHour);
399 312 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState).BkSurf(KBkSurf).WinDirBkAbs.allocate(
400 156 : 24, state.dataGlobal->TimeStepsInHour, NLayers);
401 : }
402 24 : }
403 :
404 0 : void ExpandComplexState(EnergyPlusData &state,
405 : int const iSurf, // Surface number
406 : int const iConst // Construction number
407 : )
408 : {
409 :
410 : // SUBROUTINE INFORMATION:
411 : // AUTHOR Simon Vidanovic
412 : // DATE WRITTEN May 2013
413 : // MODIFIED Simon Vidanovic (July 2013)
414 : // RE-ENGINEERED na
415 :
416 : // PURPOSE OF THIS SUBROUTINE:
417 : // When complex fenestration is controlled by EMS, program does not know in advance how many states are assigned to
418 : // ceratin surface. This information can be obtain only at runtime. Purpose of this routine is to extend number of states
419 : // used by complex fenestration in case that is necessary.
420 :
421 : // Expands states by one
422 0 : int NumOfStates = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates;
423 :
424 0 : state.dataBSDFWindow->ComplexWind(iSurf).Geom.redimension(NumOfStates + 1);
425 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State.redimension(NumOfStates + 1);
426 :
427 : // Do daylighting geometry only in case it is initialized. If daylighting is not used then no need to expand state for that
428 0 : if (state.dataBSDFWindow->ComplexWind(iSurf).DaylightingInitialized) {
429 0 : state.dataBSDFWindow->ComplexWind(iSurf).DaylghtGeom.redimension(NumOfStates + 1);
430 0 : state.dataBSDFWindow->ComplexWind(iSurf).DaylightingInitialized = false;
431 : } else {
432 0 : state.dataBSDFWindow->ComplexWind(iSurf).DaylghtGeom.allocate(NumOfStates + 1);
433 : }
434 :
435 : // Increase number of states and insert new state
436 0 : ++NumOfStates;
437 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates = NumOfStates;
438 0 : state.dataBSDFWindow->ComplexWind(iSurf).NumStates = NumOfStates;
439 :
440 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(NumOfStates).Konst = iConst;
441 :
442 : // load basis and setup window state geometry
443 0 : ConstructBasis(state, iConst, state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates).Inc);
444 0 : ConstructBasis(state, iConst, state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates).Trn);
445 :
446 0 : SetupComplexWindowStateGeometry(state,
447 : iSurf,
448 : NumOfStates,
449 : iConst,
450 0 : state.dataBSDFWindow->ComplexWind(iSurf),
451 0 : state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates),
452 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(NumOfStates));
453 :
454 : // allocation of memory for hourly data can be performed only after window state geometry has been setup
455 0 : AllocateCFSStateHourlyData(state, iSurf, NumOfStates);
456 :
457 : // calculate static properties for complex fenestration
458 0 : CalcWindowStaticProperties(state,
459 : iSurf,
460 : NumOfStates,
461 0 : state.dataBSDFWindow->ComplexWind(iSurf),
462 0 : state.dataBSDFWindow->ComplexWind(iSurf).Geom(NumOfStates),
463 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(NumOfStates));
464 :
465 : // calculate hourly data from complex fenestration
466 0 : CFSShadeAndBeamInitialization(state, iSurf, NumOfStates);
467 0 : }
468 :
469 39540 : void CheckCFSStates(EnergyPlusData &state, int const iSurf) // Surface number
470 : {
471 :
472 : // SUBROUTINE INFORMATION:
473 : // AUTHOR Simon Vidanovic
474 : // DATE WRITTEN May 2013
475 : // MODIFIED na
476 : // RE-ENGINEERED na
477 :
478 : // PURPOSE OF THIS SUBROUTINE:
479 : // Check if there are new states available for complex fenestration and performs proper initialization
480 :
481 : int NumOfStates; // number of states for current surface
482 : bool StateFound; // variable to indicate if state has been found
483 : int i; // Local counter
484 : int CurrentCFSState;
485 :
486 39540 : StateFound = false;
487 39540 : CurrentCFSState = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.CurrentState;
488 :
489 : // Check if EMS changed construction number
490 39540 : if (state.dataSurface->Surface(iSurf).Construction != state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(CurrentCFSState).Konst) {
491 :
492 : // If construction number changed then take new state
493 : // First search for existing states. Maybe state is already added in previous timestep
494 0 : NumOfStates = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates;
495 0 : for (i = 1; i <= NumOfStates; ++i) {
496 0 : if (state.dataSurface->Surface(iSurf).Construction == state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(i).Konst) {
497 0 : StateFound = true;
498 0 : CurrentCFSState = i;
499 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.CurrentState = i;
500 : }
501 : }
502 : } else {
503 39540 : StateFound = true;
504 : }
505 :
506 : // If new state is not found in the list of current states, then create new one, initialize and make it active
507 39540 : if (!StateFound) {
508 0 : ExpandComplexState(state, iSurf, state.dataSurface->Surface(iSurf).Construction);
509 0 : CurrentCFSState = state.dataSurface->SurfaceWindow(iSurf).ComplexFen.NumStates;
510 0 : state.dataSurface->SurfaceWindow(iSurf).ComplexFen.CurrentState = CurrentCFSState;
511 : }
512 39540 : }
513 :
514 801 : void InitComplexWindows(EnergyPlusData &state)
515 : {
516 :
517 : // SUBROUTINE INFORMATION:
518 : // AUTHOR Linda Lawrie
519 : // DATE WRITTEN November 2012
520 : // MODIFIED na
521 : // RE-ENGINEERED na
522 :
523 : // PURPOSE OF THIS SUBROUTINE:
524 : // Extract simple init for Complex Windows
525 :
526 : // One-time initialization
527 801 : if (state.dataWindowComplexManager->InitComplexWindowsOnce) {
528 801 : state.dataWindowComplexManager->InitComplexWindowsOnce = false;
529 801 : InitBSDFWindows(state);
530 801 : CalcStaticProperties(state);
531 : }
532 801 : }
533 :
534 10829 : void UpdateComplexWindows(EnergyPlusData &state)
535 : {
536 :
537 : // SUBROUTINE INFORMATION:
538 : // AUTHOR Joe Klems
539 : // DATE WRITTEN August 2011
540 : // MODIFIED B. Griffith, Nov. 2012 revised for detailed timestep integration mode
541 : // RE-ENGINEERED na
542 :
543 : // PURPOSE OF THIS SUBROUTINE:
544 : // Performs the shading-dependent initialization of the Complex Fenestration data;
545 : // On first call, calls the one-time initializition
546 :
547 : // LOGICAL,SAVE :: Once =.TRUE. !Flag for insuring things happen once
548 : int NumStates; // Number of states for a given complex fen
549 : int ISurf; // Index for sorting thru Surface array
550 : int IState; // Index identifying the window state for a particular window
551 : int IWind; // Index identifying a window in the WindowList
552 :
553 10829 : if (state.dataWindowComplexManager->NumComplexWind == 0) {
554 10805 : return;
555 : }
556 :
557 24 : if (state.dataGlobal->KickOffSizing || state.dataGlobal->KickOffSimulation) {
558 0 : return;
559 : }
560 :
561 : // Shading-dependent initialization; performed once for each shading period
562 :
563 : // Initialize the geometric quantities
564 :
565 76 : for (IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
566 52 : ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
567 52 : NumStates = state.dataBSDFWindow->ComplexWind(ISurf).NumStates;
568 104 : for (IState = 1; IState <= NumStates; ++IState) {
569 52 : CFSShadeAndBeamInitialization(state, ISurf, IState);
570 : } // State loop
571 : } // window loop
572 : }
573 :
574 52 : void CFSShadeAndBeamInitialization(EnergyPlusData &state,
575 : int const iSurf, // Window surface number
576 : int const iState // Window state number
577 : )
578 : {
579 :
580 : // SUBROUTINE INFORMATION:
581 : // AUTHOR Simon Vidanovic
582 : // DATE WRITTEN May 2013
583 : // MODIFIED na
584 : // RE-ENGINEERED na
585 :
586 : // PURPOSE OF THIS SUBROUTINE:
587 : // Calculates shading properties of complex fenestration
588 : // Refactoring from Klems code
589 :
590 : using namespace Vectors;
591 :
592 52 : Vector SunDir(0.0, 0.0, 1.0); // unit vector pointing toward sun (world CS)
593 52 : Vector HitPt(0.0, 0.0, 1.0); // vector location of ray intersection with a surface
594 :
595 52 : if (state.dataGlobal->KickOffSizing || state.dataGlobal->KickOffSimulation) {
596 0 : return;
597 : }
598 :
599 : int IncRay; // Index of incident ray corresponding to beam direction
600 : Real64 Theta; // Theta angle of incident ray corresponding to beam direction
601 : Real64 Phi; // Phi angle of incident ray corresponding to beam direction
602 : bool hit; // hit flag
603 : int TotHits; // hit counter
604 52 : auto &complexWindow(state.dataBSDFWindow->ComplexWind(iSurf));
605 52 : auto &complexWindowGeom(complexWindow.Geom(iState));
606 52 : auto &surfaceWindowState(state.dataSurface->SurfaceWindow(iSurf).ComplexFen.State(iState));
607 :
608 52 : if (!state.dataSysVars->DetailedSolarTimestepIntegration) {
609 52 : std::size_t lHT(0); // Linear index for ( Hour, TS )
610 52 : std::size_t lHTI(0); // Linear index for ( Hour, TS, I )
611 1300 : for (int Hour = 1; Hour <= 24; ++Hour) {
612 6864 : for (int TS = 1; TS <= state.dataGlobal->TimeStepsInHour; ++TS, ++lHT) { // [ lHT ] == ( Hour, TS )
613 5616 : SunDir = state.dataBSDFWindow->SUNCOSTS(TS, Hour);
614 5616 : Theta = 0.0;
615 5616 : Phi = 0.0;
616 5616 : if (state.dataBSDFWindow->SUNCOSTS(TS, Hour)(3) > DataEnvironment::SunIsUpValue) {
617 2833 : IncRay = FindInBasis(state, SunDir, RayIdentificationType::Front_Incident, iSurf, iState, complexWindowGeom.Inc, Theta, Phi);
618 2833 : complexWindowGeom.ThetaBm[lHT] = Theta;
619 2833 : complexWindowGeom.PhiBm[lHT] = Phi;
620 : } else {
621 2783 : complexWindowGeom.ThetaBm[lHT] = 0.0;
622 2783 : complexWindowGeom.PhiBm[lHT] = 0.0;
623 2783 : IncRay = 0; // sundown can't have ray incident on window
624 : }
625 5616 : if (IncRay > 0) { // Sun may be incident on the window
626 1675 : complexWindowGeom.SolBmIndex[lHT] = IncRay;
627 : } else { // Window can't be sunlit, set front incidence ray index to zero
628 3941 : complexWindowGeom.SolBmIndex[lHT] = 0;
629 : }
630 212976 : for (int I = 1, nGnd = complexWindowGeom.NGnd; I <= nGnd; ++I, ++lHTI) { // Gnd pt loop
631 207360 : TotHits = 0;
632 207360 : Vector const gndPt(complexWindowGeom.GndPt(I));
633 3184002 : for (int JSurf = 1, eSurf = state.dataSurface->TotSurfaces; JSurf <= eSurf; ++JSurf) {
634 : // the following test will cycle on anything except exterior surfaces and shading surfaces
635 5977366 : if (state.dataSurface->Surface(JSurf).HeatTransSurf &&
636 2988683 : state.dataSurface->Surface(JSurf).ExtBoundCond != ExternalEnvironment) {
637 1036924 : continue;
638 : }
639 : // skip surfaces that face away from the ground point
640 1951759 : if (dot(SunDir, state.dataSurface->Surface(JSurf).NewellSurfaceNormalVector) >= 0.0) {
641 1430842 : continue;
642 : }
643 : // Looking for surfaces between GndPt and sun
644 520917 : hit = PierceSurface(state, JSurf, gndPt, SunDir, HitPt);
645 520917 : if (hit) {
646 : // Are not going into the details of whether a hit surface is transparent
647 : // Since this is ultimately simply weighting the transmittance, so great
648 : // detail is not warranted
649 12041 : ++TotHits;
650 12041 : break;
651 : }
652 : }
653 207360 : if (TotHits > 0) {
654 12041 : complexWindowGeom.SolBmGndWt[lHTI] = 0.0; // [ lHTI ] == ( Hour, TS, I )
655 : } else {
656 195319 : complexWindowGeom.SolBmGndWt[lHTI] = 1.0; // [ lHTI ] == ( Hour, TS, I )
657 : }
658 207360 : } // Gnd pt loop
659 :
660 : // update window beam properties
661 5616 : CalculateWindowBeamProperties(state, iSurf, iState, complexWindow, complexWindowGeom, surfaceWindowState, Hour, TS);
662 : } // Timestep loop
663 : } // Hour loop
664 : } else { // detailed timestep integration
665 : std::size_t const lHT(
666 0 : complexWindowGeom.ThetaBm.index(state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep)); // [ lHT ] == ( HourOfDay, TimeStep )
667 0 : SunDir = state.dataBSDFWindow->SUNCOSTS(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay);
668 0 : Theta = 0.0;
669 0 : Phi = 0.0;
670 0 : if (state.dataBSDFWindow->SUNCOSTS(state.dataGlobal->TimeStep, state.dataGlobal->HourOfDay)(3) > DataEnvironment::SunIsUpValue) {
671 0 : IncRay = FindInBasis(state, SunDir, RayIdentificationType::Front_Incident, iSurf, iState, complexWindowGeom.Inc, Theta, Phi);
672 0 : complexWindowGeom.ThetaBm[lHT] = Theta;
673 0 : complexWindowGeom.PhiBm[lHT] = Phi;
674 : } else {
675 0 : complexWindowGeom.ThetaBm[lHT] = 0.0;
676 0 : complexWindowGeom.PhiBm[lHT] = 0.0;
677 0 : IncRay = 0; // sundown can't have ray incident on window
678 : }
679 :
680 0 : if (IncRay > 0) { // Sun may be incident on the window
681 0 : complexWindowGeom.SolBmIndex[lHT] = IncRay;
682 : } else { // Window can't be sunlit, set front incidence ray index to zero
683 0 : complexWindowGeom.SolBmIndex[lHT] = 0.0;
684 : }
685 0 : std::size_t lHTI(complexWindowGeom.SolBmGndWt.index(
686 0 : state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep, 1)); // Linear index for ( HourOfDay, TimeStep, I )
687 0 : for (int I = 1, nGnd = complexWindowGeom.NGnd; I <= nGnd; ++I, ++lHTI) { // Gnd pt loop
688 0 : TotHits = 0;
689 0 : Vector const gndPt(complexWindowGeom.GndPt(I));
690 0 : for (int JSurf = 1; JSurf <= state.dataSurface->TotSurfaces; ++JSurf) {
691 : // the following test will cycle on anything except exterior surfaces and shading surfaces
692 0 : if (state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).ExtBoundCond != ExternalEnvironment) {
693 0 : continue;
694 : }
695 : // skip surfaces that face away from the ground point
696 0 : if (dot(SunDir, state.dataSurface->Surface(JSurf).NewellSurfaceNormalVector) >= 0.0) {
697 0 : continue;
698 : }
699 : // Looking for surfaces between GndPt and sun
700 0 : hit = PierceSurface(state, JSurf, gndPt, SunDir, HitPt);
701 0 : if (hit) {
702 : // Are not going into the details of whether a hit surface is transparent
703 : // Since this is ultimately simply weighting the transmittance, so great
704 : // detail is not warranted
705 0 : ++TotHits;
706 0 : break;
707 : }
708 : }
709 0 : if (TotHits > 0) {
710 0 : complexWindowGeom.SolBmGndWt[lHTI] = 0.0; // [ lHTI ] == ( HourOfDay, TimeStep, I )
711 : } else {
712 0 : complexWindowGeom.SolBmGndWt[lHTI] = 1.0; // [ lHTI ] == ( HourOfDay, TimeStep, I )
713 : }
714 0 : } // Gnd pt loop
715 :
716 : // Update window beam properties
717 0 : CalculateWindowBeamProperties(
718 0 : state, iSurf, iState, complexWindow, complexWindowGeom, surfaceWindowState, state.dataGlobal->HourOfDay, state.dataGlobal->TimeStep);
719 : } // solar calculation mode, average over days or detailed
720 52 : }
721 :
722 5616 : void CalculateWindowBeamProperties(EnergyPlusData &state,
723 : int const ISurf, // Window surface number
724 : int const IState, // Window state number
725 : BSDFWindowGeomDescr const &Window, // Window Geometry
726 : BSDFGeomDescr const &Geom, // State Geometry
727 : BSDFStateDescr &State, // State Description
728 : int const Hour, // Hour number
729 : int const TS // Timestep number
730 : )
731 : {
732 :
733 : // SUBROUTINE INFORMATION:
734 : // AUTHOR Joe Klems
735 : // DATE WRITTEN August 2011
736 : // MODIFIED na
737 : // RE-ENGINEERED na
738 :
739 : // PURPOSE OF THIS SUBROUTINE:
740 : // Calculates those optical properties of all the Complex Fenestrations that
741 : // depend on the beam direction (hence, on hour and time step)
742 :
743 : // METHODOLOGY EMPLOYED:
744 : // Locate the bidirectional property matrices in the BSDFInput structure
745 : // and use them to calculate the desired average properties.
746 :
747 : using namespace Vectors;
748 :
749 : int IConst; // State construction number
750 : int I; // general purpose index--Back surface
751 : int J; // general purpose index--ray
752 : int JRay; // ray index number
753 : Real64 Theta;
754 : Real64 Phi;
755 : int JSurf; // gen purpose surface no
756 : int BaseSurf; // base surface no
757 : int M; // general purpose index--ray
758 : int L; // general purpose index--layer
759 : int KBkSurf; // general purpose index--back surface
760 : Real64 Sum1; // general purpose sum
761 : Real64 Sum2; // general purpose sum
762 : int IBm; // index of beam ray in incoming basis
763 : int BkIncRay; // index of sun dir in back incidence basis
764 : bool RegWindFnd; // flag for regular exterior back surf window
765 5616 : Array1D_int RegWinIndex; // bk surf nos of reg windows
766 5616 : int NRegWin(0); // no reg windows found as back surfaces
767 5616 : int KRegWin(0); // index of reg window as back surface
768 : Real64 Refl; // temporary reflectance
769 5616 : Array1D<Real64> Absorb; // temporary layer absorptance
770 :
771 : // Object Data
772 5616 : Vector SunDir; // current sun direction
773 :
774 5616 : IConst = state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst;
775 :
776 : // Begin calculation
777 : // Calculate the Transmittance from a given beam direction to a given zone surface
778 :
779 5616 : IBm = Geom.SolBmIndex(Hour, TS);
780 5616 : if (IBm <= 0.0) { // Beam cannot be incident on window for this Hour, TS
781 3941 : State.WinToSurfBmTrans(Hour, TS, {1, Window.NBkSurf}) = 0.0;
782 3941 : State.WinDirHemiTrans(Hour, TS) = 0.0;
783 3941 : State.WinDirSpecTrans(Hour, TS) = 0.0;
784 3941 : State.WinBmFtAbs(Hour, TS, {1, State.NLayers}) = 0.0;
785 : } else {
786 12084 : for (I = 1; I <= Window.NBkSurf; ++I) { // Back surface loop
787 10409 : Sum1 = 0.0;
788 159996 : for (J = 1; J <= Geom.NSurfInt(I); ++J) { // Ray loop
789 149587 : Sum1 +=
790 149587 : Geom.Trn.Lamda(Geom.SurfInt(J, I)) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(IBm, Geom.SurfInt(J, I));
791 : } // Ray loop
792 10409 : State.WinToSurfBmTrans(Hour, TS, I) = Sum1;
793 : } // Back surface loop
794 : // Calculate the directional-hemispherical transmittance
795 1675 : Sum1 = 0.0;
796 151262 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
797 149587 : Sum1 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(IBm, J);
798 : }
799 1675 : State.WinDirHemiTrans(Hour, TS) = Sum1;
800 : // Calculate the directional specular transmittance
801 : // Note: again using assumption that Inc and Trn basis have same structure
802 1675 : State.WinDirSpecTrans(Hour, TS) = Geom.Trn.Lamda(IBm) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(IBm, IBm);
803 : // Calculate the layer front absorptance for beam radiation
804 6230 : for (L = 1; L <= State.NLayers; ++L) {
805 4555 : State.WinBmFtAbs(Hour, TS, L) = state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(IBm, 1);
806 : }
807 : }
808 : // Calculate, for a given beam direction, the transmittance into the zone
809 : // for ground-reflected radiation (transmitted radiation assumed uniformly diffuse)
810 :
811 5616 : Sum1 = 0.0;
812 5616 : Sum2 = 0.0;
813 212976 : for (J = 1; J <= Geom.NGnd; ++J) { // Incident ray loop
814 207360 : JRay = Geom.GndIndex(J);
815 207360 : if (Geom.SolBmGndWt(Hour, TS, J) > 0.0) {
816 195319 : Sum2 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay);
817 23724254 : for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
818 23528935 : Sum1 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay) * Geom.Trn.Lamda(M) *
819 23528935 : state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(JRay, M);
820 : } // Outgoing ray loop
821 : }
822 : } // Indcident ray loop
823 5616 : if (Sum2 > 0.0) {
824 5561 : State.WinBmGndTrans(Hour, TS) = Sum1 / Sum2;
825 : } else {
826 55 : State.WinBmGndTrans(Hour, TS) = 0.0; // No unshaded ground => no transmittance
827 : }
828 :
829 : // Calculate, for a given beam direction, the layer front absorptance
830 : // for ground-reflected radiation
831 :
832 21216 : for (L = 1; L <= State.NLayers; ++L) { // layer loop
833 15600 : Sum1 = 0.0;
834 15600 : Sum2 = 0.0;
835 599280 : for (J = 1; J <= Geom.NGnd; ++J) { // Incident ray loop
836 583680 : JRay = Geom.GndIndex(J);
837 583680 : if (Geom.SolBmGndWt(Hour, TS, J) > 0.0) {
838 548444 : Sum2 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay);
839 548444 : Sum1 += Geom.SolBmGndWt(Hour, TS, J) * Geom.Inc.Lamda(JRay) *
840 548444 : state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(JRay, 1);
841 : }
842 : } // Incident ray loop
843 15600 : if (Sum2 > 0.0) {
844 15435 : State.WinBmGndAbs(Hour, TS, L) = Sum1 / Sum2;
845 : } else {
846 165 : State.WinBmGndAbs(Hour, TS, L) = 0.0; // No unshaded ground => no absorptance
847 : }
848 : } // layer loop
849 :
850 : // Check the back surfaces for exterior windows
851 5616 : RegWindFnd = false;
852 5616 : NRegWin = 0.0;
853 5616 : RegWinIndex.allocate(Window.NBkSurf);
854 41760 : for (KBkSurf = 1; KBkSurf <= Window.NBkSurf; ++KBkSurf) {
855 36144 : BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
856 36144 : JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf);
857 36144 : if (state.dataSurface->SurfWinWindowModelType(JSurf) == WindowModel::BSDF) {
858 8064 : continue;
859 : }
860 56160 : if (!(state.dataSurface->Surface(JSurf).Class == SurfaceClass::Window ||
861 28080 : state.dataSurface->Surface(JSurf).Class == SurfaceClass::GlassDoor)) {
862 28080 : continue;
863 : }
864 0 : if (!(state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).ExtBoundCond == ExternalEnvironment &&
865 0 : state.dataSurface->Surface(JSurf).ExtSolar)) {
866 0 : continue;
867 : }
868 : // Back surface is an exterior window or door
869 0 : RegWindFnd = true;
870 0 : ++NRegWin;
871 0 : RegWinIndex(NRegWin) = KBkSurf;
872 : }
873 5616 : if (RegWindFnd) {
874 0 : Absorb.allocate(State.NLayers);
875 0 : SunDir = state.dataBSDFWindow->SUNCOSTS(TS, Hour);
876 0 : BkIncRay = FindInBasis(state,
877 : SunDir,
878 : RayIdentificationType::Back_Incident,
879 : ISurf,
880 : IState,
881 0 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState).Trn,
882 : Theta,
883 : Phi);
884 0 : if (BkIncRay > 0) {
885 : // Here calculate the back incidence properties for the solar ray
886 : // this does not say whether or not the ray can pass through the
887 : // back surface window and hit this one!
888 0 : Sum1 = 0.0;
889 0 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
890 0 : Sum1 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(BkIncRay, J);
891 : }
892 0 : Refl = Sum1;
893 0 : for (L = 1; L <= State.NLayers; ++L) {
894 0 : Absorb(L) = state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(BkIncRay, 1);
895 : }
896 : } else {
897 : // solar ray can't be incident on back, so set properties equal to zero
898 0 : Refl = 0.0;
899 0 : for (L = 1; L <= State.NLayers; ++L) {
900 0 : Absorb(L) = 0.0;
901 : }
902 : }
903 0 : for (KRegWin = 1; KRegWin <= NRegWin; ++KRegWin) {
904 0 : KBkSurf = RegWinIndex(KRegWin);
905 0 : State.BkSurf(KBkSurf).WinDHBkRefl(Hour, TS) = Refl;
906 0 : for (L = 1; L <= State.NLayers; ++L) {
907 0 : State.BkSurf(KBkSurf).WinDirBkAbs(Hour, TS, L) = Absorb(L);
908 : }
909 : }
910 : }
911 5616 : if (allocated(Absorb)) {
912 0 : Absorb.deallocate();
913 : }
914 5616 : RegWinIndex.deallocate();
915 5616 : }
916 :
917 801 : void CalcStaticProperties(EnergyPlusData &state)
918 : {
919 :
920 : // SUBROUTINE INFORMATION:
921 : // AUTHOR Joe Klems
922 : // DATE WRITTEN <date_written>
923 : // MODIFIED na
924 : // RE-ENGINEERED na
925 :
926 : // PURPOSE OF THIS SUBROUTINE:
927 : // Calculates those optical properties of all the Complex Fenestrations that
928 : // do not depend on the beam direction (hence, on hour and time step)
929 :
930 : using namespace Vectors;
931 :
932 : // ISurf(0); // Index for sorting thru Surface array
933 : // // static int IConst( 0 ); // Index for accessing Construct array
934 : // IState(0); // Index identifying the window state for a particular window
935 : // IWind(0); // Index identifying a window in the WindowList
936 : // NumStates(0); // local copy of no of states
937 :
938 825 : for (int IWind = 1; IWind <= state.dataWindowComplexManager->NumComplexWind; ++IWind) {
939 24 : int ISurf = state.dataWindowComplexManager->WindowList(IWind).SurfNo;
940 24 : int NumStates = state.dataWindowComplexManager->WindowList(IWind).NumStates;
941 48 : for (int IState = 1; IState <= NumStates; ++IState) {
942 : // IConst = WindowStateList ( IWind , IState )%Konst
943 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst =
944 24 : state.dataWindowComplexManager->WindowStateList(IState, IWind).Konst;
945 24 : CalcWindowStaticProperties(state,
946 : ISurf,
947 : IState,
948 24 : state.dataBSDFWindow->ComplexWind(ISurf),
949 24 : state.dataBSDFWindow->ComplexWind(ISurf).Geom(IState),
950 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState));
951 : }
952 : }
953 801 : }
954 :
955 14 : void CalculateBasisLength(EnergyPlusData &state,
956 : BSDFWindowInputStruct const &Input, // BSDF data input struct for this construction
957 : int const IConst, // Construction number of input
958 : int &NBasis // Calculated Basis length
959 : )
960 : {
961 :
962 : // SUBROUTINE INFORMATION:
963 : // AUTHOR Joe Klems
964 : // DATE WRITTEN August 2011
965 : // MODIFIED na
966 : // RE-ENGINEERED na
967 :
968 : // PURPOSE OF THIS SUBROUTINE:
969 : // Calculates the basis length for a Window6 Non-Symmetric or Axisymmetric basis
970 : // from the input basis matrix
971 :
972 14 : if (Input.BasisMatNcols == 1) {
973 : // Axisymmetric basis, No. rows is no. of thetas = basis length
974 0 : NBasis = Input.BasisMatNrows;
975 0 : return;
976 : }
977 14 : NBasis = 1;
978 106 : for (int I = 2; I <= Input.BasisMatNrows; ++I) {
979 92 : NBasis += std::floor(state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(2, I) + 0.001);
980 : }
981 : }
982 :
983 14 : void ConstructBasis(EnergyPlusData &state,
984 : int const IConst, // Index for accessing Construct array
985 : BasisStruct &Basis)
986 : {
987 : // SUBROUTINE INFORMATION:
988 : // AUTHOR Joe Klems
989 : // DATE WRITTEN June 2011
990 : // MODIFIED na
991 : // RE-ENGINEERED na
992 :
993 : // PURPOSE OF THIS SUBROUTINE:
994 : // Set up a basis from the matrix information pointed to in Construction by ICons
995 :
996 14 : int I(0); // general purpose index
997 14 : int J(0); // general purpose index
998 14 : int NThetas(0); // Current number of theta values
999 14 : int NumElem(0); // Number of elements in current basis
1000 14 : int ElemNo(0); // Current basis element number
1001 : int MaxNPhis; // Max no of NPhis for any theta
1002 14 : Real64 Theta(0.0); // Current theta value
1003 14 : Real64 Phi(0.0); // Current phi value
1004 14 : Real64 DTheta(0.0); // Increment for theta value (Window6 type input)
1005 14 : Real64 DPhi(0.0); // Increment for phi value (Window6 type input)
1006 14 : Real64 HalfDTheta(0.0); // Half-width of all theta bins except first and last (W6 input)
1007 14 : Real64 Lamda(0.0); // Current 'Lamda' value (element weight)
1008 14 : Real64 SolAng(0.0); // Current element solid angle
1009 14 : Real64 NextTheta(0.0); // Next theta in the W6 basis after current
1010 14 : Real64 LastTheta(0.0); // Previous theta in the W6 basis before current
1011 14 : Real64 LowerTheta(0.0); // Lower theta boundary of the element
1012 14 : Real64 UpperTheta(0.0); // Upper theta boundary of the element
1013 14 : Array1D<Real64> Thetas; // temp array holding theta values
1014 14 : Array1D_int NPhis; // temp array holding number of phis for a given theta
1015 :
1016 14 : NThetas = state.dataConstruction->Construct(IConst).BSDFInput.BasisMatNrows; // Note here assuming row by row input
1017 14 : Basis.NThetas = NThetas;
1018 14 : Basis.BasisMatIndex = state.dataConstruction->Construct(IConst).BSDFInput.BasisMatIndex;
1019 14 : Basis.NBasis = state.dataConstruction->Construct(IConst).BSDFInput.NBasis;
1020 14 : Basis.Grid.allocate(Basis.NBasis);
1021 14 : Thetas.allocate(NThetas + 1); // Temp array
1022 : // By convention the Thetas array contains a final point at Pi/2 which is not a basis element
1023 14 : NPhis.allocate(NThetas + 1); // Temp array
1024 14 : Basis.Thetas.allocate(NThetas + 1);
1025 14 : Basis.NPhis.allocate(NThetas + 1);
1026 :
1027 14 : Basis.Lamda.allocate(state.dataConstruction->Construct(IConst).BSDFInput.NBasis);
1028 14 : Basis.SolAng.allocate(state.dataConstruction->Construct(IConst).BSDFInput.NBasis);
1029 14 : if (state.dataConstruction->Construct(IConst).BSDFInput.BasisType == DataBSDFWindow::Basis::WINDOW) {
1030 : // WINDOW6 Basis
1031 14 : Basis.BasisType = DataBSDFWindow::Basis::WINDOW;
1032 14 : if (state.dataConstruction->Construct(IConst).BSDFInput.BasisSymmetryType == DataBSDFWindow::BasisSymmetry::None) {
1033 : // No basis symmetry
1034 14 : Basis.BasisSymmetryType = DataBSDFWindow::BasisSymmetry::None;
1035 14 : Thetas(1) = 0.0; // By convention, the first basis point is at the center (theta=0,phi=0)
1036 14 : Thetas(NThetas + 1) = 0.5 * Constant::Pi; // and there is an N+1st point (not a basis element) at Pi/2
1037 14 : NPhis(1) = 1;
1038 14 : NumElem = 1;
1039 106 : for (I = 2; I <= NThetas; ++I) {
1040 92 : Thetas(I) = state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(1, I) * Constant::DegToRad;
1041 92 : NPhis(I) = std::floor(state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(2, I) + 0.001);
1042 92 : if (NPhis(I) <= 0) {
1043 0 : ShowFatalError(state, "WindowComplexManager: incorrect input, no. phis must be positive.");
1044 : }
1045 92 : NumElem += NPhis(I);
1046 : }
1047 14 : MaxNPhis = maxval(NPhis({1, NThetas}));
1048 14 : Basis.Phis.allocate(NThetas + 1, MaxNPhis + 1); // N+1st Phi point (not basis element) at 2Pi
1049 14 : Basis.BasisIndex.allocate(MaxNPhis, NThetas + 1);
1050 14 : Basis.Phis = 0.0; // Initialize so undefined elements will contain zero
1051 14 : Basis.BasisIndex = 0; // Initialize so undefined elements will contain zero
1052 14 : if (NumElem != state.dataConstruction->Construct(IConst).BSDFInput.NBasis) { // Constructed Basis must match property matrices
1053 0 : ShowFatalError(state, "WindowComplexManager: Constructed basis length does not match property matrices.");
1054 : }
1055 14 : Basis.Thetas = Thetas;
1056 14 : Basis.NPhis = NPhis;
1057 14 : ElemNo = 0;
1058 120 : for (I = 1; I <= NThetas; ++I) {
1059 106 : Theta = Thetas(I);
1060 106 : if (I == 1) { // First theta value must always be zero
1061 14 : HalfDTheta = 0.5 * Thetas(I + 1);
1062 14 : LastTheta = 0.0;
1063 14 : NextTheta = Thetas(I + 1);
1064 14 : LowerTheta = 0.0;
1065 14 : UpperTheta = HalfDTheta;
1066 92 : } else if (I > 1 && I < NThetas) {
1067 78 : LastTheta = Thetas(I - 1);
1068 78 : NextTheta = Thetas(I + 1);
1069 78 : LowerTheta = UpperTheta;
1070 78 : HalfDTheta = Theta - LowerTheta;
1071 78 : UpperTheta = Theta + HalfDTheta;
1072 14 : } else if (I == NThetas) {
1073 14 : LastTheta = Thetas(I - 1);
1074 14 : NextTheta = 0.5 * Constant::Pi;
1075 14 : LowerTheta = UpperTheta; // It is assumed that Thetas(N) is the mean between the previous
1076 : // UpperTheta and pi/2.
1077 14 : UpperTheta = 0.5 * Constant::Pi;
1078 : }
1079 106 : DPhi = 2.0 * Constant::Pi / NPhis(I);
1080 106 : if (I == 1) {
1081 14 : Lamda = Constant::Pi * pow_2(std::sin(UpperTheta));
1082 14 : SolAng = 2.0 * Constant::Pi * (1.0 - std::cos(UpperTheta));
1083 : } else {
1084 92 : Lamda = 0.5 * DPhi * (pow_2(std::sin(UpperTheta)) - pow_2(std::sin(LowerTheta))); // For W6 basis, lamda is funct of Theta and
1085 : // NPhis, not individual Phi
1086 92 : SolAng = DPhi * (std::cos(LowerTheta) - std::cos(UpperTheta));
1087 : }
1088 106 : DTheta = UpperTheta - LowerTheta;
1089 106 : Basis.Phis(I, NPhis(I) + 1) = 2.0 * Constant::Pi; // Non-basis-element Phi point for table searching in Phi
1090 1616 : for (J = 1; J <= NPhis(I); ++J) {
1091 1510 : ++ElemNo;
1092 1510 : Basis.BasisIndex(J, I) = ElemNo;
1093 1510 : Phi = (J - 1) * DPhi;
1094 1510 : Basis.Phis(I, J) = Phi; // Note: this ordering of I & J are necessary to allow Phis(Theta) to
1095 : // be searched as a one-dimensional table
1096 1510 : FillBasisElement(state,
1097 : Theta,
1098 : Phi,
1099 : ElemNo,
1100 : Basis.Grid(ElemNo),
1101 : LowerTheta,
1102 : UpperTheta,
1103 : DPhi,
1104 : DataBSDFWindow::Basis::WINDOW); // This gets all the simple grid characteristics
1105 1510 : Basis.Lamda(ElemNo) = Lamda;
1106 1510 : Basis.SolAng(ElemNo) = SolAng;
1107 : }
1108 : }
1109 : } else { // BST
1110 : // Axisymmetric basis symmetry (Note this only useful specular systems, where it allows shorter data input)
1111 0 : Basis.BasisSymmetryType = DataBSDFWindow::BasisSymmetry::Axisymmetric;
1112 0 : Thetas(1) = 0.0; // By convention, the first basis point is at the center (theta=0,phi=0)
1113 0 : Thetas(NThetas + 1) = 0.5 * Constant::Pi; // and there is an N+1st point (not a basis element) at Pi/2
1114 0 : NPhis = 1; // As insurance, define one phi for each theta
1115 0 : NumElem = 1;
1116 0 : for (I = 2; I <= NThetas; ++I) {
1117 0 : Thetas(I) = state.dataConstruction->Construct(IConst).BSDFInput.BasisMat(1, I) * Constant::DegToRad;
1118 0 : ++NumElem;
1119 : }
1120 0 : Basis.Phis.allocate(1, NThetas);
1121 0 : Basis.BasisIndex.allocate(1, NThetas);
1122 0 : Basis.Phis = 0.0; // Initialize so undefined elements will contain zero
1123 0 : Basis.BasisIndex = 0; // Initialize so undefined elements will contain zero
1124 0 : if (NumElem != state.dataConstruction->Construct(IConst).BSDFInput.NBasis) { // Constructed Basis must match property matrices
1125 0 : ShowFatalError(state, "WindowComplexManager: Constructed basis length does not match property matrices.");
1126 : }
1127 0 : Basis.Thetas = Thetas;
1128 0 : Basis.NPhis = NPhis;
1129 0 : ElemNo = 0;
1130 0 : DPhi = 2.0 * Constant::Pi;
1131 0 : for (I = 1; I <= NThetas; ++I) {
1132 0 : Theta = Thetas(I);
1133 0 : if (I == 1) { // First theta value must always be zero
1134 0 : HalfDTheta = 0.5 * Thetas(I + 1);
1135 0 : LastTheta = 0.0;
1136 0 : NextTheta = Thetas(I + 1);
1137 0 : LowerTheta = 0.0;
1138 0 : UpperTheta = HalfDTheta;
1139 0 : } else if (I > 1 && I < NThetas) {
1140 0 : LastTheta = Thetas(I - 1);
1141 0 : NextTheta = Thetas(I + 1);
1142 0 : LowerTheta = UpperTheta;
1143 0 : HalfDTheta = Theta - LowerTheta;
1144 0 : UpperTheta = Theta + HalfDTheta;
1145 0 : } else if (I == NThetas) {
1146 0 : LastTheta = Thetas(I - 1);
1147 0 : NextTheta = 0.5 * Constant::Pi;
1148 0 : LowerTheta = UpperTheta; // It is assumed that Thetas(N) is the mean between the previous
1149 : // UpperTheta and pi/2.
1150 0 : UpperTheta = 0.5 * Constant::Pi;
1151 : }
1152 0 : if (I == 1) {
1153 0 : Lamda = Constant::Pi * pow_2(std::sin(UpperTheta));
1154 0 : SolAng = 2.0 * Constant::Pi * (1.0 - std::cos(UpperTheta));
1155 : } else {
1156 0 : Lamda = 0.5 * DPhi * (pow_2(std::sin(UpperTheta)) - pow_2(std::sin(LowerTheta))); // For W6 basis, lamda is funct of Theta and
1157 : // NPhis, not individual Phi
1158 0 : SolAng = DPhi * (std::cos(LowerTheta) - std::cos(UpperTheta));
1159 : }
1160 0 : DTheta = UpperTheta - LowerTheta;
1161 0 : ++ElemNo;
1162 0 : Basis.BasisIndex(1, I) = ElemNo;
1163 0 : Phi = 0.0;
1164 0 : Basis.Phis(I, 1) = Phi; // Note: this ordering of I & J are necessary to allow Phis(Theta) to
1165 : // be searched as a one-dimensional table
1166 0 : FillBasisElement(state,
1167 : Theta,
1168 : Phi,
1169 : ElemNo,
1170 : Basis.Grid(ElemNo),
1171 : LowerTheta,
1172 : UpperTheta,
1173 : DPhi,
1174 : DataBSDFWindow::Basis::WINDOW); // This gets all the simple grid characteristics
1175 0 : Basis.Lamda(ElemNo) = Lamda;
1176 0 : Basis.SolAng(ElemNo) = SolAng;
1177 : }
1178 : } // BST
1179 : } else { // BTW
1180 0 : ShowFatalError(state, "WindowComplexManager: Non-Window6 basis type not yet implemented.");
1181 : } // BTW
1182 14 : Thetas.deallocate();
1183 14 : NPhis.deallocate();
1184 14 : }
1185 :
1186 1510 : void FillBasisElement(EnergyPlusData &state,
1187 : Real64 const Theta, // Central polar angle of element
1188 : Real64 const Phi, // Central azimuthal angle of element
1189 : int const Elem, // Index number of element in basis
1190 : BasisElemDescr &BasisElem,
1191 : Real64 const LowerTheta, // Lower edge of element (polar angle)
1192 : Real64 const UpperTheta, // Upper edge of element (polar angle)
1193 : Real64 const DPhi, // Width of element (azimuthal angle)
1194 : DataBSDFWindow::Basis const InputType // Basis type
1195 : )
1196 : {
1197 :
1198 : // SUBROUTINE INFORMATION:
1199 : // AUTHOR Joe Klems
1200 : // DATE WRITTEN August 2010
1201 : // MODIFIED na
1202 : // RE-ENGINEERED na
1203 :
1204 : // PURPOSE OF THIS SUBROUTINE:
1205 : // fill in values for all the components of a basis element
1206 :
1207 1510 : if (InputType == DataBSDFWindow::Basis::WINDOW) {
1208 : // WINDOW6 Type BASIS
1209 1510 : if (Elem == 1) {
1210 : // first element, theta=0, is special case
1211 14 : BasisElem.Theta = Theta;
1212 14 : BasisElem.Phi = 0.0;
1213 14 : BasisElem.dPhi = 2.0 * Constant::Pi;
1214 14 : BasisElem.UpprTheta = UpperTheta;
1215 14 : BasisElem.dTheta = BasisElem.UpprTheta - Theta;
1216 14 : BasisElem.LwrTheta = Theta;
1217 14 : BasisElem.LwrPhi = 0.0;
1218 14 : BasisElem.UpprPhi = 2.0 * Constant::Pi;
1219 : } else {
1220 1496 : BasisElem.Theta = Theta;
1221 1496 : BasisElem.Phi = Phi;
1222 1496 : BasisElem.dPhi = DPhi;
1223 1496 : BasisElem.LwrPhi = Phi - DPhi / 2.0;
1224 1496 : BasisElem.UpprPhi = Phi + DPhi / 2.0;
1225 1496 : BasisElem.LwrTheta = LowerTheta;
1226 1496 : BasisElem.UpprTheta = UpperTheta;
1227 1496 : BasisElem.dTheta = BasisElem.UpprTheta - BasisElem.LwrTheta;
1228 : }
1229 : } else {
1230 : // Non-WINDOW6 Type Basis
1231 : // Currently not implemented
1232 0 : ShowFatalError(state, "WindowComplexManager: Custom basis type not yet implemented.");
1233 : }
1234 1510 : }
1235 :
1236 24 : void SetupComplexWindowStateGeometry(EnergyPlusData &state,
1237 : int const ISurf, // Surface number of the complex fenestration
1238 : int const IState, // State number of the complex fenestration state
1239 : int const IConst, // Pointer to construction for this state
1240 : BSDFWindowGeomDescr &Window, // Window Geometry
1241 : BSDFGeomDescr &Geom, // State Geometry
1242 : [[maybe_unused]] BSDFStateDescr &State // State Description
1243 : )
1244 : {
1245 :
1246 : // SUBROUTINE INFORMATION:
1247 : // AUTHOR J. Klems
1248 : // DATE WRITTEN June 2011
1249 : // MODIFIED na
1250 : // RE-ENGINEERED na
1251 :
1252 : // PURPOSE OF THIS SUBROUTINE:
1253 : // Define all the geometric quantites for a complex fenestration state
1254 :
1255 : using namespace Vectors;
1256 :
1257 : // Locals
1258 : // SUBROUTINE ARGUMENT DEFINITIONS:
1259 : // INTEGER, INTENT(IN) :: IWind !Complex fenestration number (in window list)
1260 :
1261 : Real64 Azimuth; // Complex fenestration azimuth
1262 : Real64 Tilt; // Complex fenestration tilt
1263 : int ElemNo; // Grid index variable
1264 : bool hit; // Surface intersection flag
1265 : int I; // Temp Indices
1266 : int J;
1267 : int IRay; // Ray index variable
1268 : int IZone; // Zone containing the complex window
1269 : int JSurf; // Secondary Surface index
1270 : int BaseSurf; // base surface index
1271 : int KBkSurf; // Back surface index
1272 : int MaxHits; // Max no of hits found
1273 : int MaxInt; // Max no of intersections found
1274 : int NSky; // No of sky rays
1275 : int NGnd; // No of gnd rays
1276 : int NReflSurf; // No of rays striking ext surfaces
1277 : int NBkSurf; // No of back surfaces
1278 : int TotHits; // Current number of surface intersections
1279 : Real64 Theta; // Basis theta angle
1280 : Real64 Phi; // Basis phi angle
1281 : Real64 HitDsq; // Squared distance to current hit pt
1282 : Real64 LeastHitDsq; // Squared distance to closest hit pt
1283 24 : Array1D<Real64> V(3); // vector array
1284 24 : Array1D_int TmpRfSfInd; // Temporary RefSurfIndex
1285 24 : Array1D_int TmpRfRyNH; // Temporary RefRayNHits
1286 24 : Array2D_int TmpHSurfNo; // Temporary HitSurfNo
1287 24 : Array2D<Real64> TmpHSurfDSq; // Temporary HitSurfDSq
1288 24 : Array1D_int TmpSkyInd; // Temporary sky index list
1289 24 : Array1D_int TmpGndInd; // Temporary gnd index list
1290 24 : Array2D_int TmpSurfInt; // Temporary index of ray intersecing back surf
1291 24 : Array2D<Real64> TmpSjdotN; // Temporary dot prod of ray angle w bk surf norm
1292 24 : Array1D_int ITemp1D; // Temporary INT 1D array
1293 24 : Array2D<Real64> Temp2D; // Temporary real 2D array
1294 : Real64 TransRSurf; // Norminal transmittance of shading surface
1295 : Real64 WtSum; // Sum for normalizing various weights
1296 : Real64 DotProd; // Temporary variable for manipulating dot product .dot.
1297 :
1298 : struct BackHitList
1299 : {
1300 : // Members
1301 : int KBkSurf; // Back surface index of the hit surface
1302 : int HitSurf; // Surface number of the hit surface
1303 : Vector HitPt; // coords of hit pt (world syst)
1304 : Real64 HitDsq; // Squared distance to the current hit pt
1305 :
1306 : // Default Constructor
1307 24 : BackHitList() : KBkSurf(0), HitSurf(0), HitDsq(0.0)
1308 : {
1309 24 : }
1310 : };
1311 :
1312 : // Object Data
1313 24 : Vector HitPt; // coords of hit pt (world syst)
1314 24 : Vector X; // position vector
1315 24 : Vector VecNorm; // outer normal vector
1316 24 : Array1D<Vector> TmpGndPt; // Temporary ground intersection list
1317 24 : Array2D<Vector> TempV2D; // Temporary vector 2D array
1318 24 : Array2D<Vector> TmpHitPt; // Temporary HitPt
1319 24 : BackHitList BSHit; // Temp list of back surface hit quantities for a ray
1320 :
1321 : // This routine primarily fills in the BSDFGeomDescr type for a given window and state
1322 : // Note that on call the incoming and outgoing basis structure types have already been filled in
1323 : // Define the central ray directions (in world coordinate system)
1324 :
1325 24 : state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).NLayers = state.dataConstruction->Construct(IConst).BSDFInput.NumLayers;
1326 24 : Azimuth = Constant::DegToRad * state.dataSurface->Surface(ISurf).Azimuth;
1327 24 : Tilt = Constant::DegToRad * state.dataSurface->Surface(ISurf).Tilt;
1328 :
1329 : // For incoming grid
1330 :
1331 24 : Geom.sInc.allocate(Geom.Inc.NBasis);
1332 24 : Geom.sInc = Vector(0.0, 0.0, 0.0);
1333 24 : Geom.pInc.allocate(Geom.Inc.NBasis);
1334 24 : Geom.CosInc.allocate(Geom.Inc.NBasis);
1335 24 : Geom.DAInc.allocate(Geom.Inc.NBasis);
1336 24 : Geom.pInc = BSDFDaylghtPosition(0.0, 0.0);
1337 1944 : for (ElemNo = 1; ElemNo <= Geom.Inc.NBasis; ++ElemNo) {
1338 1920 : Theta = Geom.Inc.Grid(ElemNo).Theta;
1339 1920 : Phi = Geom.Inc.Grid(ElemNo).Phi;
1340 : // The following puts in the vectors depending on
1341 : // window orientation
1342 1920 : Geom.sInc(ElemNo) = WorldVectFromW6(state, Theta, Phi, RayIdentificationType::Front_Incident, Tilt, Azimuth);
1343 1920 : Geom.pInc(ElemNo) = DaylghtAltAndAzimuth(Geom.sInc(ElemNo));
1344 :
1345 1920 : Geom.CosInc(ElemNo) = std::cos(Geom.Inc.Grid(ElemNo).Theta);
1346 : // Geom%DAInc(ElemNo) = COS(Geom%pInc(ElemNo)%Altitude) * Geom%Inc%Grid(ElemNo)%dTheta * Geom%Inc%Grid(ElemNo)%dPhi
1347 : // Geom%DAInc(ElemNo) = Geom%Inc%Grid(ElemNo)%dTheta * Geom%Inc%Grid(ElemNo)%dPhi
1348 1920 : Geom.DAInc(ElemNo) = std::cos(Geom.Inc.Grid(ElemNo).Theta) * Geom.Inc.Grid(ElemNo).dTheta * Geom.Inc.Grid(ElemNo).dPhi;
1349 : }
1350 : // For outgoing grid
1351 24 : Geom.sTrn.allocate(Geom.Trn.NBasis);
1352 24 : Geom.sTrn = Vector(0.0, 0.0, 0.0);
1353 24 : Geom.pTrn.allocate(Geom.Trn.NBasis);
1354 24 : Geom.pTrn = BSDFDaylghtPosition(0.0, 0.0);
1355 1944 : for (ElemNo = 1; ElemNo <= Geom.Trn.NBasis; ++ElemNo) {
1356 1920 : Theta = Geom.Trn.Grid(ElemNo).Theta;
1357 1920 : Phi = Geom.Trn.Grid(ElemNo).Phi;
1358 : // The following puts in the vectors depending on
1359 : // window orientation
1360 1920 : Geom.sTrn(ElemNo) = WorldVectFromW6(state, Theta, Phi, RayIdentificationType::Front_Transmitted, Tilt, Azimuth);
1361 1920 : Geom.pTrn(ElemNo) = DaylghtAltAndAzimuth(Geom.sTrn(ElemNo));
1362 : }
1363 : // Incident Basis:
1364 : // Construct sky and ground ray index maps, and list of rays intersecting exterior surfaces
1365 : // Sky, and ground ray index maps, and rays that are potentially beam radiation reflected from exterior surfaces
1366 24 : TmpRfSfInd.allocate(Geom.Inc.NBasis);
1367 24 : TmpRfRyNH.allocate(Geom.Inc.NBasis);
1368 24 : TmpHSurfNo.allocate(state.dataSurface->TotSurfaces, Geom.Inc.NBasis);
1369 24 : TmpHSurfDSq.allocate(state.dataSurface->TotSurfaces, Geom.Inc.NBasis);
1370 24 : TmpHitPt.allocate(state.dataSurface->TotSurfaces, Geom.Inc.NBasis);
1371 24 : TmpSkyInd.allocate(Geom.Inc.NBasis);
1372 24 : TmpGndInd.allocate(Geom.Inc.NBasis);
1373 24 : TmpGndPt.allocate(Geom.Inc.NBasis);
1374 24 : NSky = 0;
1375 24 : NGnd = 0;
1376 24 : NReflSurf = 0;
1377 24 : TmpRfRyNH = 0;
1378 24 : Geom.NSkyUnobs = 0;
1379 24 : Geom.NGndUnobs = 0;
1380 : // Note--this loop could be repeated for different positions in the window plane (as for detailed reflection
1381 : // calculations, varying the origin in the call to PierceSurface. Essentially, have set NsubV =1.
1382 1944 : for (IRay = 1; IRay <= Geom.Inc.NBasis; ++IRay) {
1383 1920 : if (Geom.sInc(IRay).z < 0.0) {
1384 : // A ground ray
1385 816 : ++Geom.NGndUnobs;
1386 : } else {
1387 : // A sky ray
1388 1104 : ++Geom.NSkyUnobs;
1389 : }
1390 : // Exterior reveal shadowing/reflection treatment should be inserted here
1391 1920 : TotHits = 0;
1392 37070 : for (JSurf = 1; JSurf <= state.dataSurface->TotSurfaces; ++JSurf) {
1393 : // the following test will cycle on anything except exterior surfaces and shading surfaces
1394 35150 : if (state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).ExtBoundCond != ExternalEnvironment) {
1395 12652 : continue;
1396 : }
1397 : // skip the base surface containing the window and any other subsurfaces of that surface
1398 43076 : if (JSurf == state.dataSurface->Surface(ISurf).BaseSurf ||
1399 20578 : state.dataSurface->Surface(JSurf).BaseSurf == state.dataSurface->Surface(ISurf).BaseSurf) {
1400 3840 : continue;
1401 : }
1402 : // skip surfaces that face away from the window
1403 18658 : DotProd = dot(Geom.sInc(IRay), state.dataSurface->Surface(JSurf).NewellSurfaceNormalVector);
1404 18658 : if (DotProd >= 0.0) {
1405 8862 : continue;
1406 : }
1407 9796 : hit = PierceSurface(state, JSurf, state.dataSurface->Surface(ISurf).Centroid, Geom.sInc(IRay), HitPt);
1408 9796 : if (!hit) {
1409 9796 : continue; // Miss: Try next surface
1410 : }
1411 0 : if (TotHits == 0) {
1412 : // First hit for this ray
1413 0 : TotHits = 1;
1414 0 : ++NReflSurf;
1415 0 : TmpRfSfInd(NReflSurf) = IRay;
1416 0 : TmpRfRyNH(NReflSurf) = 1;
1417 0 : TmpHSurfNo(1, NReflSurf) = JSurf;
1418 0 : TmpHitPt(1, NReflSurf) = HitPt;
1419 0 : V = HitPt - state.dataSurface->Surface(ISurf).Centroid; // vector array from window ctr to hit pt
1420 0 : LeastHitDsq = magnitude_squared(V); // dist^2 window ctr to hit pt
1421 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
1422 0 : if (!state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).shadowSurfSched != nullptr) {
1423 0 : TransRSurf = 1.0; // If a shadowing surface may have a scheduled transmittance,
1424 : // treat it here as completely transparent
1425 : } else {
1426 0 : TransRSurf = 0.0;
1427 : }
1428 : } else {
1429 0 : V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
1430 0 : HitDsq = magnitude_squared(V);
1431 0 : if (HitDsq >= LeastHitDsq) {
1432 0 : if (TransRSurf > 0.0) { // forget the new hit if the closer hit is opaque
1433 0 : J = TotHits + 1;
1434 0 : if (TotHits > 1) {
1435 0 : for (I = 2; I <= TotHits; ++I) {
1436 0 : if (HitDsq < TmpHSurfDSq(I, NReflSurf)) {
1437 0 : J = I;
1438 0 : break;
1439 : }
1440 : }
1441 0 : if (!state.dataSurface->Surface(JSurf).HeatTransSurf &&
1442 0 : state.dataSurface->Surface(JSurf).shadowSurfSched == nullptr) {
1443 : // The new hit is opaque, so we can drop all the hits further away
1444 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
1445 0 : TmpHitPt(J, NReflSurf) = HitPt;
1446 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
1447 0 : TotHits = J;
1448 : } else {
1449 : // The new hit is scheduled (presumed transparent), so keep the more distant hits
1450 : // Note that all the hists in the list will be transparent except the last,
1451 : // which may be either transparent or opaque
1452 0 : if (TotHits >= J) {
1453 0 : for (I = TotHits; I >= J; --I) {
1454 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
1455 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
1456 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
1457 : }
1458 0 : TmpHSurfNo(J, NReflSurf) = JSurf;
1459 0 : TmpHitPt(J, NReflSurf) = HitPt;
1460 0 : TmpHSurfDSq(J, NReflSurf) = HitDsq;
1461 0 : ++TotHits;
1462 : }
1463 : }
1464 : }
1465 : }
1466 : } else {
1467 : // A new closest hit. If it is opaque, drop the current hit list,
1468 : // otherwise add it at the front
1469 0 : LeastHitDsq = HitDsq;
1470 0 : if (!state.dataSurface->Surface(JSurf).HeatTransSurf && state.dataSurface->Surface(JSurf).shadowSurfSched != nullptr) {
1471 0 : TransRSurf = 1.0; // New closest hit is transparent, keep the existing hit list
1472 0 : for (I = TotHits; I >= 1; --I) {
1473 0 : TmpHSurfNo(I + 1, NReflSurf) = TmpHSurfNo(I, NReflSurf);
1474 0 : TmpHitPt(I + 1, NReflSurf) = TmpHitPt(I, NReflSurf);
1475 0 : TmpHSurfDSq(I + 1, NReflSurf) = TmpHSurfDSq(I, NReflSurf);
1476 0 : ++TotHits;
1477 : }
1478 : } else {
1479 0 : TransRSurf = 0.0; // New closest hit is opaque, drop the existing hit list
1480 0 : TotHits = 1;
1481 : }
1482 0 : TmpHSurfNo(1, NReflSurf) = JSurf; // In either case the new hit is put in position 1
1483 0 : TmpHitPt(1, NReflSurf) = HitPt;
1484 0 : TmpHSurfDSq(1, NReflSurf) = LeastHitDsq;
1485 : }
1486 : }
1487 : } // End of loop over surfaces
1488 1920 : if (TotHits <= 0) {
1489 : // This ray reached the sky or ground unobstructed
1490 1920 : if (Geom.sInc(IRay).z < 0.0) {
1491 : // A ground ray
1492 816 : ++NGnd;
1493 816 : TmpGndInd(NGnd) = IRay;
1494 816 : TmpGndPt(NGnd).x = state.dataSurface->Surface(ISurf).Centroid.x -
1495 816 : (Geom.sInc(IRay).x / Geom.sInc(IRay).z) * state.dataSurface->Surface(ISurf).Centroid.z;
1496 816 : TmpGndPt(NGnd).y = state.dataSurface->Surface(ISurf).Centroid.y -
1497 816 : (Geom.sInc(IRay).y / Geom.sInc(IRay).z) * state.dataSurface->Surface(ISurf).Centroid.z;
1498 816 : TmpGndPt(NGnd).z = 0.0;
1499 : } else {
1500 : // A sky ray
1501 1104 : ++NSky;
1502 1104 : TmpSkyInd(NSky) = IRay;
1503 : }
1504 : } else {
1505 : // Save the number of hits for this ray
1506 0 : TmpRfRyNH(NReflSurf) = TotHits;
1507 : }
1508 : } // End of loop over basis rays
1509 : // Store results of indexing the incident basis for this window
1510 24 : Geom.NSky = NSky;
1511 24 : Geom.NGnd = NGnd;
1512 24 : Geom.NReflSurf = NReflSurf;
1513 24 : Geom.SkyIndex.allocate(NSky);
1514 24 : Geom.SkyIndex = TmpSkyInd({1, NSky});
1515 24 : TmpSkyInd.deallocate();
1516 24 : Geom.GndIndex.allocate(NGnd);
1517 24 : Geom.GndPt.allocate(NGnd);
1518 24 : Geom.GndIndex = TmpGndInd({1, NGnd});
1519 24 : Geom.GndPt = TmpGndPt({1, NGnd});
1520 24 : TmpGndInd.deallocate();
1521 24 : TmpGndPt.deallocate();
1522 24 : MaxHits = maxval(TmpRfRyNH);
1523 24 : Geom.RefSurfIndex.allocate(NReflSurf);
1524 24 : Geom.RefRayNHits.allocate(NReflSurf);
1525 24 : Geom.HitSurfNo.allocate(MaxHits, NReflSurf);
1526 24 : Geom.HitSurfDSq.allocate(MaxHits, NReflSurf);
1527 24 : Geom.HitPt.allocate(MaxHits, NReflSurf);
1528 24 : Geom.RefSurfIndex = TmpRfSfInd({1, NReflSurf});
1529 24 : Geom.RefRayNHits = TmpRfRyNH({1, NReflSurf});
1530 24 : Geom.HitSurfNo = 0;
1531 24 : Geom.HitSurfDSq = 0.0;
1532 24 : Geom.HitPt = Vector(0.0, 0.0, 0.0);
1533 24 : for (I = 1; I <= NReflSurf; ++I) {
1534 0 : TotHits = TmpRfRyNH(I);
1535 0 : Geom.HitSurfNo({1, TotHits}, I) = TmpHSurfNo({1, TotHits}, I);
1536 0 : Geom.HitSurfDSq({1, TotHits}, I) = TmpHSurfDSq({1, TotHits}, I);
1537 0 : Geom.HitPt({1, TotHits}, I) = TmpHitPt({1, TotHits}, I);
1538 : }
1539 24 : TmpRfRyNH.deallocate();
1540 24 : TmpRfSfInd.deallocate();
1541 24 : TmpHSurfNo.deallocate();
1542 24 : TmpHSurfDSq.deallocate();
1543 24 : TmpHitPt.deallocate();
1544 : // In above scheme sky and ground rays are those that intesect no exterior surfaces.
1545 : // The list of hit points is compiled for later (future?) calculation
1546 : // of reflections from these surfaces. The hit list for each ray includes all
1547 : // surfaces with schedulable transmittance intersected by the ray,
1548 : // in order of increasing distance, up to the first opaque surface.
1549 : // Rays that intesect one or more schedulable transmittance but no opaque
1550 : // surfaces (therefore may reach the sky or ground) are left out of the sky/ground
1551 : // calcuation. A correction for these rays could/should be made after the
1552 : // shading calculation.
1553 : // Now calculate weights for averaging the transmittance matrix
1554 : // Sky Weights
1555 24 : Geom.SolSkyWt.allocate(NSky);
1556 1128 : for (I = 1; I <= NSky; ++I) {
1557 1104 : J = Geom.SkyIndex(I);
1558 1104 : Geom.SolSkyWt(I) = SkyWeight(Geom.sInc(J));
1559 : }
1560 24 : WtSum = sum(Geom.SolSkyWt({1, NSky}));
1561 24 : if (WtSum > Constant::rTinyValue) {
1562 24 : Geom.SolSkyWt({1, NSky}) /= WtSum;
1563 : } else {
1564 0 : Geom.SolSkyWt({1, NSky}) = 0.0;
1565 : }
1566 : // SkyGround Weights
1567 24 : Geom.SolSkyGndWt.allocate(NGnd);
1568 840 : for (I = 1; I <= NGnd; ++I) {
1569 816 : Geom.SolSkyGndWt(I) = SkyGndWeight(Geom.GndPt(I));
1570 : }
1571 24 : WtSum = sum(Geom.SolSkyGndWt({1, NGnd}));
1572 24 : if (WtSum > Constant::rTinyValue) {
1573 24 : Geom.SolSkyGndWt({1, NGnd}) /= WtSum;
1574 : } else {
1575 0 : Geom.SolSkyGndWt({1, NGnd}) = 0.0;
1576 : }
1577 : // Weights for beam reflected from ground are calculated after shading
1578 : // interval is determined
1579 : // Transmitted Basis:
1580 : // Construct back surface intersection maps
1581 24 : IZone = state.dataSurface->Surface(ISurf).Zone;
1582 24 : NBkSurf = Window.NBkSurf;
1583 24 : Geom.NSurfInt.allocate(NBkSurf);
1584 24 : Geom.NSurfInt = 0; // Initialize the number of intersections to zero
1585 24 : TmpSurfInt.allocate(Geom.Trn.NBasis, NBkSurf);
1586 24 : TmpSjdotN.allocate(Geom.Trn.NBasis, NBkSurf);
1587 : // Find the intersections of the basis rays with the back surfaces
1588 1944 : for (IRay = 1; IRay <= Geom.Trn.NBasis; ++IRay) { // ray loop
1589 1920 : TotHits = 0;
1590 : // Insert treatment of intersection & reflection from interior reveals here
1591 14244 : for (KBkSurf = 1; KBkSurf <= NBkSurf; ++KBkSurf) { // back surf loop
1592 12324 : BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
1593 12324 : JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf); // these are all proper back surfaces
1594 12324 : hit = PierceSurface(state, JSurf, state.dataSurface->Surface(ISurf).Centroid, Geom.sTrn(IRay), HitPt);
1595 12324 : if (!hit) {
1596 10132 : continue; // Miss: Try next surface
1597 : }
1598 2192 : if (TotHits == 0) {
1599 : // First hit for this ray
1600 1920 : TotHits = 1;
1601 1920 : BSHit.KBkSurf = KBkSurf;
1602 1920 : BSHit.HitSurf = JSurf;
1603 1920 : BSHit.HitPt = HitPt;
1604 1920 : V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
1605 1920 : BSHit.HitDsq = magnitude_squared(V);
1606 272 : } else if (BSHit.HitSurf == state.dataSurface->Surface(JSurf).BaseSurf) {
1607 : // another hit, check whether this is a subsurface of a previously hit base surface
1608 : // (which would be listed first in the Surface array)
1609 : // if so, replace the previous hit with this one
1610 272 : ++TotHits;
1611 272 : BSHit.KBkSurf = KBkSurf;
1612 272 : BSHit.HitSurf = JSurf;
1613 272 : BSHit.HitPt = HitPt;
1614 272 : V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
1615 272 : BSHit.HitDsq = magnitude_squared(V);
1616 : } else {
1617 0 : ++TotHits;
1618 : // is the new hit closer than the previous one (i.e., zone not strictly convex)?
1619 : // if so, take the closer hit
1620 0 : V = HitPt - state.dataSurface->Surface(ISurf).Centroid;
1621 0 : HitDsq = magnitude_squared(V);
1622 0 : if (HitDsq < BSHit.HitDsq) {
1623 0 : BSHit.KBkSurf = KBkSurf;
1624 0 : BSHit.HitSurf = JSurf;
1625 0 : BSHit.HitPt = HitPt;
1626 0 : BSHit.HitDsq = HitDsq;
1627 : }
1628 : }
1629 : } // back surf loop
1630 1920 : if (TotHits == 0) {
1631 : // this should not happen--means a ray has gotten lost
1632 : // ShowWarningError(state, "BSDF--Zone surfaces do not completely enclose zone--transmitted ray lost");
1633 : } else {
1634 1920 : KBkSurf = BSHit.KBkSurf;
1635 1920 : JSurf = BSHit.HitSurf;
1636 1920 : ++Geom.NSurfInt(KBkSurf);
1637 1920 : TmpSurfInt(Geom.NSurfInt(KBkSurf), KBkSurf) = IRay;
1638 1920 : VecNorm = state.dataSurface->Surface(JSurf).OutNormVec;
1639 1920 : TmpSjdotN(Geom.NSurfInt(KBkSurf), KBkSurf) = dot(Geom.sTrn(IRay), VecNorm);
1640 : }
1641 : } // ray loop
1642 : // All rays traced, now put away the results in the temporary arrays
1643 24 : MaxInt = maxval(Geom.NSurfInt);
1644 24 : Geom.SurfInt.allocate(MaxInt, Window.NBkSurf);
1645 24 : Geom.SjdotN.allocate(MaxInt, Window.NBkSurf);
1646 24 : Geom.SurfInt = 0;
1647 180 : for (I = 1; I <= Window.NBkSurf; ++I) {
1648 156 : Geom.SurfInt({1, Geom.NSurfInt(I)}, I) = TmpSurfInt({1, Geom.NSurfInt(I)}, I);
1649 156 : Geom.SjdotN({1, Geom.NSurfInt(I)}, I) = TmpSjdotN({1, Geom.NSurfInt(I)}, I);
1650 : }
1651 :
1652 24 : TmpSurfInt.deallocate();
1653 24 : TmpSjdotN.deallocate();
1654 24 : }
1655 :
1656 24 : void CalcWindowStaticProperties(EnergyPlusData &state,
1657 : int const ISurf, // Surface number of the complex fenestration
1658 : int const IState, // State number of the complex fenestration state
1659 : BSDFWindowGeomDescr &Window, // Window Geometry
1660 : BSDFGeomDescr &Geom, // State Geometry
1661 : BSDFStateDescr &State // State Description
1662 : )
1663 : {
1664 :
1665 : // SUBROUTINE INFORMATION:
1666 : // AUTHOR Joe Klems
1667 : // DATE WRITTEN <date_written>
1668 : // MODIFIED na
1669 : // RE-ENGINEERED na
1670 :
1671 : // PURPOSE OF THIS SUBROUTINE:
1672 : // Calculates those optical properties of all the Complex Fenestrations that
1673 : // do not depend on the beam direction (hence, on hour and time step)
1674 :
1675 : using namespace Vectors;
1676 :
1677 : int IConst; // Pointer to construction for this fenestration
1678 24 : int I(0); // general purpose index
1679 24 : int J(0); // general purpose index
1680 24 : int JJ(0); // general purpose index--ray
1681 24 : int L(0); // general purpose index--layer
1682 24 : int M(0); // general purpose index--ray
1683 : int KBkSurf; // back surface index
1684 : int JSurf; // surface number (used for back surface)
1685 : int BaseSurf; // base surface number (used for finding back surface)
1686 : Real64 Sum1; // general purpose temporary sum
1687 : Real64 Sum2; // general purpose temporary sum
1688 : Real64 Sum3; // general purpose temporary sum
1689 : Real64 Hold; // temp variable
1690 :
1691 24 : IConst = state.dataSurface->SurfaceWindow(ISurf).ComplexFen.State(IState).Konst;
1692 :
1693 : // Calculate the hemispherical-hemispherical transmittance
1694 :
1695 24 : Sum1 = 0.0;
1696 24 : Sum2 = 0.0;
1697 1944 : for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incident ray loop
1698 1920 : Sum2 += Geom.Inc.Lamda(J);
1699 216360 : for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
1700 214440 : Sum1 += Geom.Inc.Lamda(J) * Geom.Trn.Lamda(M) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(M, J);
1701 : } // Outgoing ray loop
1702 : } // Incident ray loop
1703 24 : if (Sum2 > 0) {
1704 24 : State.WinDiffTrans = Sum1 / Sum2;
1705 : } else {
1706 0 : State.WinDiffTrans = 0.0;
1707 0 : ShowWarningError(state, "BSDF--Inc basis has zero projected solid angle");
1708 : }
1709 :
1710 : // Calculate the hemispherical-hemispherical transmittance for visible spetrum
1711 :
1712 24 : Sum1 = 0.0;
1713 24 : Sum2 = 0.0;
1714 1944 : for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incident ray loop
1715 1920 : Sum2 += Geom.Inc.Lamda(J);
1716 216360 : for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
1717 214440 : Sum1 += Geom.Inc.Lamda(J) * Geom.Trn.Lamda(M) * state.dataConstruction->Construct(IConst).BSDFInput.VisFrtTrans(M, J);
1718 : } // Outgoing ray loop
1719 : } // Incident ray loop
1720 24 : if (Sum2 > 0.0) {
1721 24 : State.WinDiffVisTrans = Sum1 / Sum2;
1722 : } else {
1723 0 : State.WinDiffVisTrans = 0.0;
1724 0 : ShowWarningError(state, "BSDF--Inc basis has zero projected solid angle");
1725 : }
1726 :
1727 : // Set the nominal diffuse transmittance so the surface isn't mistaken as opaque
1728 : // Simon: Commented this out. We are not using TransDiff and it is already set to 0.1 in input routines.
1729 : // Construct( IConst ).TransDiff = SurfaceWindow( ISurf ).ComplexFen.State( IState ).WinDiffTrans;
1730 : // Calculate Window Sky Transmittance (transmitted radiation assumed diffuse)
1731 : // and Sky Absorptance (by layer)
1732 24 : Sum1 = 0.0;
1733 24 : Sum2 = 0.0;
1734 24 : Sum3 = 0.0;
1735 1128 : for (JJ = 1; JJ <= Geom.NSky; ++JJ) {
1736 122184 : for (M = 1; M <= Geom.Trn.NBasis; ++M) {
1737 121080 : J = Geom.SkyIndex(JJ);
1738 121080 : Sum1 +=
1739 121080 : Geom.SolSkyWt(JJ) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(M, J) * Geom.Inc.Lamda(J) * Geom.Trn.Lamda(M);
1740 : }
1741 : }
1742 1128 : for (JJ = 1; JJ <= Geom.NSky; ++JJ) {
1743 1104 : J = Geom.SkyIndex(JJ);
1744 1104 : Sum2 += Geom.SolSkyWt(JJ) * Geom.Inc.Lamda(J);
1745 : }
1746 :
1747 24 : if (Sum2 != 0.0) {
1748 24 : State.WinSkyTrans = Sum1 / Sum2;
1749 : } else {
1750 0 : State.WinSkyTrans = 0.0;
1751 : }
1752 :
1753 24 : State.WinSkyFtAbs.allocate(State.NLayers);
1754 : // Also allocate the beam quantities for this state
1755 92 : for (L = 1; L <= State.NLayers; ++L) {
1756 68 : Sum3 = 0.0;
1757 3224 : for (JJ = 1; JJ <= Geom.NSky; ++JJ) {
1758 3156 : J = Geom.SkyIndex(JJ);
1759 3156 : Sum3 += Geom.SolSkyWt(JJ) * Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
1760 : }
1761 :
1762 68 : if (Sum2 != 0.0) {
1763 68 : State.WinSkyFtAbs(L) = Sum3 / Sum2;
1764 : } else {
1765 0 : State.WinSkyFtAbs(L) = 0.0;
1766 : }
1767 : }
1768 :
1769 : // Calculate Window Sky/Ground Transmittance
1770 : //(applies to ground-reflected sky radiation, transmitted radiation assumed diffuse)
1771 : // This is the same calculation as the sky transmittance, except that the set of incident
1772 : // rays and the ray weights are different
1773 : // Also calculate Window Sky/Ground Absorptance (by layer)
1774 24 : Sum1 = 0.0;
1775 24 : Sum2 = 0.0;
1776 24 : Sum3 = 0.0;
1777 :
1778 840 : for (JJ = 1; JJ <= Geom.NGnd; ++JJ) {
1779 94176 : for (M = 1; M <= Geom.Trn.NBasis; ++M) {
1780 93360 : J = Geom.GndIndex(JJ);
1781 93360 : Sum1 += Geom.SolSkyGndWt(JJ) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(M, J) * Geom.Inc.Lamda(J) *
1782 93360 : Geom.Trn.Lamda(M);
1783 : }
1784 : }
1785 :
1786 840 : for (JJ = 1; JJ <= Geom.NGnd; ++JJ) {
1787 816 : J = Geom.GndIndex(JJ);
1788 816 : Sum2 += Geom.SolSkyGndWt(JJ) * Geom.Inc.Lamda(J);
1789 : }
1790 :
1791 24 : if (Sum2 != 0.0) {
1792 24 : State.WinSkyGndTrans = Sum1 / Sum2;
1793 : } else {
1794 0 : State.WinSkyGndTrans = 0.0;
1795 : }
1796 :
1797 24 : State.WinSkyGndAbs.allocate(State.NLayers);
1798 92 : for (L = 1; L <= State.NLayers; ++L) {
1799 68 : Sum3 = 0.0;
1800 2404 : for (JJ = 1; JJ <= Geom.NGnd; ++JJ) {
1801 2336 : J = Geom.GndIndex(JJ);
1802 2336 : Sum3 += Geom.SolSkyGndWt(JJ) * Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
1803 : }
1804 :
1805 68 : if (Sum2 != 0.0) {
1806 68 : State.WinSkyGndAbs(L) = Sum3 / Sum2;
1807 : } else {
1808 0 : State.WinSkyGndAbs(L) = 0.0;
1809 : }
1810 : }
1811 :
1812 : // Calculate Window Back Hemispherical Reflectance and Layer Back Hemispherical Absorptance
1813 24 : Sum1 = 0.0;
1814 24 : Sum2 = 0.0;
1815 24 : Sum3 = 0.0;
1816 : // Note this again assumes the equivalence Inc basis = transmission basis for back incidence and
1817 : // Trn basis = incident basis for back incidence
1818 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
1819 216360 : for (M = 1; M <= Geom.Inc.NBasis; ++M) {
1820 214440 : Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(M, J) * Geom.Trn.Lamda(J) * Geom.Inc.Lamda(M);
1821 : }
1822 : }
1823 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
1824 1920 : Sum2 += Geom.Trn.Lamda(J);
1825 : }
1826 :
1827 24 : if (Sum2 != 0.0) {
1828 24 : State.WinBkHemRefl = Sum1 / Sum2;
1829 : } else {
1830 0 : State.WinBkHemRefl = 0.0;
1831 : }
1832 :
1833 24 : state.dataConstruction->Construct(IConst).ReflectSolDiffBack = State.WinBkHemRefl;
1834 :
1835 24 : State.WinBkHemAbs.allocate(State.NLayers);
1836 92 : for (L = 1; L <= State.NLayers; ++L) {
1837 5560 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
1838 5492 : Sum3 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(J, 1);
1839 : }
1840 :
1841 68 : if (Sum2 != 0.0) {
1842 68 : State.WinBkHemAbs(L) = Sum3 / Sum2;
1843 : } else {
1844 0 : State.WinBkHemAbs(L) = 0.0;
1845 : }
1846 :
1847 : // Put this into the construction for use in non-detailed optical calculations
1848 68 : state.dataConstruction->Construct(IConst).AbsDiffBack(L) = State.WinBkHemAbs(L);
1849 : }
1850 :
1851 : // Calculate Window Layer Front Hemispherical Absorptance
1852 24 : Sum1 = 0.0;
1853 24 : Sum2 = 0.0;
1854 1944 : for (J = 1; J <= Geom.Inc.NBasis; ++J) {
1855 1920 : Sum2 += Geom.Inc.Lamda(J);
1856 : }
1857 24 : State.WinFtHemAbs.allocate(State.NLayers);
1858 92 : for (L = 1; L <= State.NLayers; ++L) {
1859 68 : Sum1 = 0.0;
1860 5560 : for (J = 1; J <= Geom.Inc.NBasis; ++J) {
1861 5492 : Sum1 += Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
1862 : }
1863 :
1864 68 : if (Sum2 != 0.0) {
1865 68 : State.WinFtHemAbs(L) = Sum1 / Sum2;
1866 : } else {
1867 0 : State.WinFtHemAbs(L) = 0.0;
1868 : }
1869 :
1870 : // Put this into the construction for use in non-detailed optical calculations
1871 68 : state.dataConstruction->Construct(IConst).AbsDiff(L) = State.WinFtHemAbs(L);
1872 : }
1873 :
1874 : // Calculate Window Back Hemispherical Visible Reflectance
1875 24 : Sum1 = 0.0;
1876 24 : Sum2 = 0.0;
1877 : // Note this again assumes the equivalence Inc basis = transmission basis for back incidence and
1878 : // Trn basis = incident basis for back incidence
1879 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
1880 216360 : for (M = 1; M <= Geom.Inc.NBasis; ++M) {
1881 214440 : Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.VisBkRefl(M, J) * Geom.Trn.Lamda(J) * Geom.Inc.Lamda(M);
1882 : }
1883 : }
1884 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
1885 1920 : Sum2 += Geom.Trn.Lamda(J);
1886 : }
1887 :
1888 24 : if (Sum2 != 0.0) {
1889 24 : State.WinBkHemVisRefl = Sum1 / Sum2;
1890 : } else {
1891 0 : State.WinBkHemVisRefl = 0.0;
1892 : }
1893 :
1894 24 : state.dataConstruction->Construct(IConst).ReflectVisDiffBack = State.WinBkHemVisRefl;
1895 :
1896 : // * * * *
1897 : // Note potential problem if one relaxes the assumption that Inc and Trn basis have same structure:
1898 : // The following calculations are made for the set of ray numbers defined in the Trn basis that
1899 : // were determined to connect the center of the window to a particular back surface.
1900 : // Here it is assumed that one can reverse these rays and get an equivalent set in the Trn
1901 : // basis for back-incidence quantities: back transmittance and back layer absorptance
1902 : // This assumption may fail if the Inc and Trn bases are allowed to have different structure.
1903 : // Note also that in this case one would need to rethink the relationship of the basis
1904 : // definitions to back-incidence quantities: possibly this would
1905 : // also require that the basis for back incident quantities be
1906 : // different from the Trn basis, and similarly the basis for backward outgoing rays
1907 : // be different from the Inc basis.
1908 :
1909 : // * * * *
1910 : // Note that we are assuming that for back incidence the layer numberings are the same
1911 : // as for front incidence, i.e., from outside to inside when incidence is from inside
1912 : // * * * *
1913 : // For back surfaces that are complex fenestrations, calculate the directional-hemispherical back
1914 : // reflectance and the directional back absorptance by layer for this fenestration receiving
1915 : // radiation via the back surface
1916 : // Make this calculation only for cases where the back surface is a Complex Fenestration
1917 : // First allocate the back surface section of the state properties
1918 24 : if (!allocated(State.BkSurf)) {
1919 0 : State.BkSurf.allocate(Window.NBkSurf);
1920 : }
1921 180 : for (KBkSurf = 1; KBkSurf <= Window.NBkSurf; ++KBkSurf) { // back surface loop
1922 156 : BaseSurf = state.dataSurface->Surface(ISurf).BaseSurf; // ShadowComb is organized by base surface
1923 156 : JSurf = state.dataShadowComb->ShadowComb(BaseSurf).BackSurf(KBkSurf);
1924 156 : if (state.dataSurface->SurfWinWindowModelType(JSurf) != WindowModel::BSDF) {
1925 120 : continue;
1926 : }
1927 :
1928 : // Directional-hemispherical back reflectance
1929 36 : Sum1 = 0.0;
1930 36 : Sum2 = 0.0;
1931 308 : for (J = 1; J <= Geom.NSurfInt(KBkSurf); ++J) { // Inc Ray loop
1932 272 : Sum2 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf));
1933 33888 : for (M = 1; M <= Geom.Inc.NBasis; ++M) { // Outgoing Ray loop
1934 33616 : Sum1 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf)) * Geom.Inc.Lamda(M) *
1935 33616 : state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(Geom.SurfInt(J, KBkSurf), M);
1936 : } // Outgoing Ray loop
1937 : } // Inc Ray loop
1938 36 : if (Sum2 > 0.0) {
1939 36 : Hold = Sum1 / Sum2;
1940 900 : for (I = 1; I <= 24; ++I) {
1941 4896 : for (J = 1; J <= state.dataGlobal->TimeStepsInHour; ++J) {
1942 4032 : State.BkSurf(KBkSurf).WinDHBkRefl(I, J) = Hold;
1943 : }
1944 : }
1945 : } else {
1946 0 : for (I = 1; I <= 24; ++I) {
1947 0 : for (J = 1; J <= state.dataGlobal->TimeStepsInHour; ++J) {
1948 0 : State.BkSurf(KBkSurf).WinDHBkRefl(I, J) = 0.0;
1949 : }
1950 : }
1951 : }
1952 :
1953 : // Directional layer back absorption
1954 147 : for (L = 1; L <= State.NLayers; ++L) { // layer loop
1955 111 : Sum1 = 0.0;
1956 111 : Sum2 = 0.0;
1957 962 : for (J = 1; J <= Geom.NSurfInt(KBkSurf); ++J) { // Inc Ray loop
1958 851 : Sum2 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf));
1959 851 : Sum1 += Geom.Trn.Lamda(Geom.SurfInt(J, KBkSurf)) *
1960 851 : state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(Geom.SurfInt(J, KBkSurf), 1);
1961 : } // Inc Ray loop
1962 111 : if (Sum2 > 0.0) {
1963 111 : Hold = Sum1 / Sum2;
1964 2775 : for (I = 1; I <= 24; ++I) {
1965 15192 : for (J = 1; J <= state.dataGlobal->TimeStepsInHour; ++J) {
1966 12528 : State.BkSurf(KBkSurf).WinDirBkAbs(I, J, L) = Hold;
1967 : }
1968 : }
1969 : } else {
1970 0 : for (I = 1; I <= 24; ++I) {
1971 0 : for (J = 1; J <= state.dataGlobal->TimeStepsInHour; ++J) {
1972 0 : State.BkSurf(KBkSurf).WinDirBkAbs(I, J, L) = 0.0;
1973 : }
1974 : }
1975 : }
1976 :
1977 : } // layer loop
1978 : } // back surface loop
1979 :
1980 : // ********************************************************************************
1981 : // Allocation and calculation of integrated values for front of window surface
1982 : // ********************************************************************************
1983 :
1984 : // Sum of front absorptances for each incident direction (integration of absorptances)
1985 24 : if (!allocated(State.IntegratedFtAbs)) {
1986 24 : State.IntegratedFtAbs.allocate(Geom.Inc.NBasis);
1987 : }
1988 1944 : for (J = 1; J <= Geom.Inc.NBasis; ++J) {
1989 1920 : Sum1 = 0.0;
1990 7412 : for (L = 1; L <= State.NLayers; ++L) { // layer loop
1991 5492 : Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).FrtAbs(J, 1);
1992 : }
1993 1920 : State.IntegratedFtAbs(J) = Sum1;
1994 : }
1995 :
1996 : // Integrating front transmittance
1997 24 : if (!allocated(State.IntegratedFtTrans)) {
1998 24 : State.IntegratedFtTrans.allocate(Geom.Inc.NBasis);
1999 : }
2000 1944 : for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incident ray loop
2001 1920 : Sum1 = 0.0;
2002 216360 : for (M = 1; M <= Geom.Trn.NBasis; ++M) { // Outgoing ray loop
2003 214440 : Sum1 += Geom.Trn.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolFrtTrans(J, M);
2004 : } // Outgoing ray loop
2005 1920 : State.IntegratedFtTrans(J) = Sum1;
2006 : } // Incident ray loop
2007 :
2008 24 : if (!allocated(State.IntegratedFtRefl)) {
2009 24 : State.IntegratedFtRefl.allocate(Geom.Inc.NBasis);
2010 : }
2011 : // Integrating front reflectance
2012 1944 : for (J = 1; J <= Geom.Inc.NBasis; ++J) { // Incoming ray loop
2013 1920 : State.IntegratedFtRefl(J) = 1 - State.IntegratedFtTrans(J) - State.IntegratedFtAbs(J);
2014 : } // Incoming ray loop
2015 :
2016 : // ********************************************************************************
2017 : // Allocation and calculation of integrated values for back of window surface
2018 : // ********************************************************************************
2019 :
2020 : // Sum of back absorptances for each incident direction (integration of absorptances)
2021 24 : if (!allocated(State.IntegratedBkAbs)) {
2022 24 : State.IntegratedBkAbs.allocate(Geom.Trn.NBasis);
2023 : }
2024 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) {
2025 1920 : Sum1 = 0.0;
2026 7412 : for (L = 1; L <= State.NLayers; ++L) { // layer loop
2027 5492 : Sum1 += state.dataConstruction->Construct(IConst).BSDFInput.Layer(L).BkAbs(J, 1);
2028 : }
2029 1920 : State.IntegratedBkAbs(J) = Sum1;
2030 : }
2031 :
2032 : // Integrating back reflectance
2033 24 : if (!allocated(State.IntegratedBkRefl)) {
2034 24 : State.IntegratedBkRefl.allocate(Geom.Trn.NBasis);
2035 : }
2036 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) { // Outgoing ray loop
2037 1920 : Sum1 = 0.0;
2038 216360 : for (M = 1; M <= Geom.Inc.NBasis; ++M) { // Incident ray loop
2039 214440 : Sum1 += Geom.Inc.Lamda(J) * state.dataConstruction->Construct(IConst).BSDFInput.SolBkRefl(J, M);
2040 : } // Incident ray loop
2041 1920 : State.IntegratedBkRefl(J) = Sum1;
2042 : } // Outgoing ray loop
2043 :
2044 24 : if (!allocated(State.IntegratedBkTrans)) {
2045 24 : State.IntegratedBkTrans.allocate(Geom.Trn.NBasis);
2046 : }
2047 : // Integrating back transmittance
2048 1944 : for (J = 1; J <= Geom.Trn.NBasis; ++J) { // Outgoing ray loop
2049 1920 : State.IntegratedBkTrans(J) = 1 - State.IntegratedBkRefl(J) - State.IntegratedBkAbs(J);
2050 : } // Outgoing ray loop
2051 24 : }
2052 :
2053 1104 : Real64 SkyWeight([[maybe_unused]] Vector const &DirVec) // Direction of the element to be weighted
2054 : {
2055 :
2056 : // FUNCTION INFORMATION:
2057 : // AUTHOR Joe Klems
2058 : // DATE WRITTEN June 2011
2059 : // MODIFIED na
2060 : // RE-ENGINEERED na
2061 :
2062 : // PURPOSE OF THIS FUNCTION:
2063 : // Search a one-dimensional array for a given value, returning the index of the element equal to the value, if
2064 : // found, or zero
2065 :
2066 : using namespace Vectors;
2067 :
2068 : // Return value
2069 : Real64 Wt; // Weight
2070 :
2071 1104 : Wt = 1.0;
2072 :
2073 : // To do: figure out how to weight sky elements to reproduce the current E+ assumptions
2074 : // Possibly one will need to calculated average DH transmittance for isotropic sky and
2075 : // horizon separately and then later average according to sky conditions. Then a completely
2076 : // different scheme for daylight. For now: rays that reach sky equally weighted in calculating
2077 : // transmittance, rays passing through surfaces with scheduled transmittance are neglected.
2078 :
2079 1104 : return Wt;
2080 : }
2081 :
2082 816 : Real64 SkyGndWeight([[maybe_unused]] Vector const &PosVec) // x,y,z(=0) of ground intersection pt
2083 : {
2084 :
2085 : // FUNCTION INFORMATION:
2086 : // AUTHOR Joe Klems
2087 : // DATE WRITTEN June 2011
2088 : // MODIFIED na
2089 : // RE-ENGINEERED na
2090 :
2091 : // PURPOSE OF THIS FUNCTION:
2092 : // Search a one-dimensional array for a given value, returning the index of the element equal to the value, if
2093 : // found, or zero
2094 :
2095 : using namespace Vectors;
2096 :
2097 : // Return value
2098 : Real64 Wt; // Weight
2099 :
2100 816 : Wt = 1.0;
2101 :
2102 : // At present, equally weights all ground rays for calculation of the complex window transmittance for
2103 : // sky radiation reflected from ground. This does not take into account shading of the ground.
2104 : // The correct procedure would be to generate a set of rays to the sky and see which do not intersect
2105 : // surfaces, as is done in the reflection manager. However, this would increase computational load.
2106 : // Given that equal weighting, by averaging the transmittance only over rays that come from the ground,
2107 : // already produces a more accurate ground transmittance than the existing method, it is at least questionable
2108 : // whether the more detailed procedure would produce enough improvement in accuracy to make up for
2109 : // the additional calculation time. Therefore a more detailed treatment is deferred until there is some
2110 : // experience with the new method to determine whether further detail is warranted.
2111 :
2112 816 : return Wt;
2113 : }
2114 :
2115 4074 : BSDFDaylghtPosition DaylghtAltAndAzimuth(Vector const &UnitVect) // vector which needs to be converted
2116 : {
2117 :
2118 : // SUBROUTINE INFORMATION:
2119 : // AUTHOR Simon Vidanovic
2120 : // DATE WRITTEN April 2013
2121 : // MODIFIED na
2122 : // RE-ENGINEERED na
2123 :
2124 : // PURPOSE OF THIS FUNCTION:
2125 : // Transform unit vector (given in world coordinates) into altitude and azimuth. Azimuth is measured from positive x-axe.
2126 : // Altitude range is from -pi/2 to pi/2. Vector laying in horizontal plane will have altitude equal to zero and vector
2127 : // pointing upward will have altitude equal to pi/2. Range for azimuth is calculated from -pi to +pi.
2128 :
2129 : using namespace DataBSDFWindow;
2130 : using namespace Vectors;
2131 : // Return value
2132 4074 : BSDFDaylghtPosition DayPos; // altitude and azimuth in world coordinates
2133 :
2134 4074 : if (UnitVect.x != 0.0) {
2135 3620 : if (UnitVect.x >= 0.0) {
2136 1828 : DayPos.Azimuth = std::atan(UnitVect.y / UnitVect.x);
2137 : } else {
2138 1792 : if (UnitVect.y >= 0.0) {
2139 922 : DayPos.Azimuth = Constant::Pi + std::atan(UnitVect.y / UnitVect.x);
2140 : } else {
2141 870 : DayPos.Azimuth = -Constant::Pi + std::atan(UnitVect.y / UnitVect.x);
2142 : }
2143 : }
2144 : } else {
2145 454 : if (UnitVect.y >= 0.0) {
2146 182 : DayPos.Azimuth = Constant::PiOvr2;
2147 : } else {
2148 272 : DayPos.Azimuth = -Constant::PiOvr2;
2149 : }
2150 : }
2151 :
2152 4074 : DayPos.Altitude = std::asin(UnitVect.z);
2153 :
2154 4074 : return DayPos;
2155 : }
2156 :
2157 3840 : Vector WorldVectFromW6([[maybe_unused]] EnergyPlusData &state,
2158 : Real64 const Theta, // Polar angle in W6 Coords
2159 : Real64 const Phi, // Azimuthal angle in W6 Coords
2160 : const RayIdentificationType RadType, // Type of radiation: Front_Incident, etc.
2161 : Real64 const Gamma, // Surface tilt angle, radians, world coordinate system
2162 : Real64 const Alpha // Surface azimuth, radians, world coordinate system
2163 : )
2164 : {
2165 :
2166 : // SUBROUTINE INFORMATION:
2167 : // AUTHOR Joe Klems
2168 : // DATE WRITTEN Aug 2010
2169 : // MODIFIED na
2170 : // RE-ENGINEERED na
2171 :
2172 : // PURPOSE OF THIS FUNCTION:
2173 : // Transform angular coordinates in the WINDOW6 coordinate system for
2174 : // a given surface into a unit vector in the world coordinate system,
2175 : // pointing to the radiation source (for incident radiation) or in
2176 : // the direction of propagation (for outgoing radiation)
2177 :
2178 : using namespace Vectors;
2179 :
2180 : // Return value
2181 3840 : Vector UnitVect; // unit vector direction in world CS
2182 :
2183 : // Error tolerance is used to make small numbers equal to zero. Due to precision of pi constant used in E+, performing
2184 : // trigonometric operations on those constant will not cause absolutely accurate results
2185 3840 : Real64 constexpr ErrorTolerance(1.e-10);
2186 :
2187 3840 : UnitVect = Vector(0.0, 0.0, 0.0);
2188 :
2189 3840 : Real64 const sin_Phi = std::sin(Phi);
2190 3840 : Real64 const cos_Phi = std::cos(Phi);
2191 :
2192 3840 : Real64 const sin_Gamma = std::sin(Gamma);
2193 3840 : Real64 const cos_Gamma = std::cos(Gamma);
2194 :
2195 3840 : Real64 const sin_Alpha = std::sin(Alpha);
2196 3840 : Real64 const cos_Alpha = std::cos(Alpha);
2197 :
2198 3840 : Real64 const sin_Theta = std::sin(Theta);
2199 3840 : Real64 const cos_Theta = std::cos(Theta);
2200 :
2201 3840 : switch (RadType) {
2202 1920 : case RayIdentificationType::Front_Incident: { // W6 vector will point in direction of propagation, must reverse to get world vector
2203 : // after the W6 vector has been rotated into the world CS
2204 1920 : UnitVect.x = sin_Theta * sin_Phi * cos_Gamma * sin_Alpha - sin_Theta * cos_Phi * cos_Alpha + cos_Theta * sin_Gamma * sin_Alpha;
2205 1920 : UnitVect.y = sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha + cos_Theta * sin_Gamma * cos_Alpha;
2206 1920 : UnitVect.z = -(sin_Theta * sin_Phi * sin_Gamma - cos_Theta * cos_Gamma);
2207 1920 : break;
2208 : }
2209 1920 : case RayIdentificationType::Front_Transmitted: {
2210 1920 : UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * sin_Alpha - cos_Theta * sin_Gamma * sin_Alpha;
2211 1920 : UnitVect.y = -(sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha + cos_Theta * sin_Gamma * cos_Alpha);
2212 1920 : UnitVect.z = sin_Theta * sin_Phi * sin_Gamma - cos_Theta * cos_Gamma;
2213 1920 : break;
2214 : }
2215 0 : case RayIdentificationType::Front_Reflected: {
2216 0 : UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * sin_Alpha + cos_Theta * sin_Gamma * sin_Alpha;
2217 0 : UnitVect.y = cos_Theta * sin_Gamma * cos_Alpha - sin_Theta * cos_Phi * sin_Alpha - sin_Theta * sin_Phi * cos_Gamma * cos_Alpha;
2218 0 : UnitVect.z = sin_Theta * sin_Phi * sin_Gamma + cos_Theta * cos_Gamma;
2219 0 : break;
2220 : }
2221 0 : case RayIdentificationType::Back_Incident: {
2222 0 : UnitVect.x = sin_Theta * sin_Phi * cos_Gamma * sin_Alpha - sin_Theta * cos_Phi * cos_Alpha - cos_Theta * sin_Gamma * sin_Alpha;
2223 0 : UnitVect.y = sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha - cos_Theta * sin_Gamma * cos_Alpha;
2224 0 : UnitVect.z = -cos_Theta * cos_Gamma - sin_Theta * sin_Phi * sin_Gamma;
2225 0 : break;
2226 : }
2227 0 : case RayIdentificationType::Back_Transmitted: { // This is same as front reflected
2228 0 : UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * sin_Alpha + cos_Theta * sin_Gamma * sin_Alpha;
2229 0 : UnitVect.y = cos_Theta * sin_Gamma * cos_Alpha - sin_Theta * cos_Phi * sin_Alpha - sin_Theta * sin_Phi * cos_Gamma * cos_Alpha;
2230 0 : UnitVect.z = sin_Theta * sin_Phi * sin_Gamma + cos_Theta * cos_Gamma;
2231 0 : break;
2232 : }
2233 0 : case RayIdentificationType::Back_Reflected: { // This is same as front transmitted
2234 0 : UnitVect.x = sin_Theta * cos_Phi * cos_Alpha - sin_Theta * sin_Phi * cos_Gamma * cos_Alpha - cos_Theta * sin_Gamma * sin_Alpha;
2235 0 : UnitVect.y = -(sin_Theta * cos_Phi * sin_Alpha + sin_Theta * sin_Phi * cos_Gamma * cos_Alpha + cos_Theta * sin_Gamma * cos_Alpha);
2236 0 : UnitVect.z = sin_Theta * sin_Phi * sin_Gamma - cos_Theta * cos_Gamma;
2237 0 : break;
2238 : }
2239 0 : default:
2240 0 : break;
2241 : }
2242 :
2243 : // Remove small numbers from evaluation (due to limited decimal points for pi)
2244 3840 : if (std::abs(UnitVect.x) <= ErrorTolerance) {
2245 364 : UnitVect.x = 0.0;
2246 : }
2247 3840 : if (std::abs(UnitVect.y) <= ErrorTolerance) {
2248 212 : UnitVect.y = 0.0;
2249 : }
2250 3840 : if (std::abs(UnitVect.z) <= ErrorTolerance) {
2251 576 : UnitVect.z = 0.0;
2252 : }
2253 :
2254 3840 : return UnitVect;
2255 : }
2256 :
2257 2833 : int FindInBasis(EnergyPlusData &state,
2258 : Vector const &RayToFind, // Ray vector direction in world CS
2259 : const RayIdentificationType RadType, // Type of radiation: Front_Incident, etc.
2260 : int const ISurf, // Window Surface number
2261 : [[maybe_unused]] int const IState, // Complex Fenestration state number
2262 : BasisStruct const &Basis, // Complex Fenestration basis root
2263 : Real64 &Theta, // Theta value for ray
2264 : Real64 &Phi // Phi value for ray
2265 : )
2266 : {
2267 :
2268 : // SUBROUTINE INFORMATION:
2269 : // AUTHOR Joe Klems
2270 : // DATE WRITTEN August 2011
2271 : // MODIFIED na
2272 : // RE-ENGINEERED na
2273 :
2274 : using namespace Vectors;
2275 :
2276 : // Return value
2277 : int RayIndex; // Index of ray in basis, zero if ray not in hemisphere
2278 :
2279 : // INTEGER, INTENT(IN) :: IWind !window index in window list
2280 :
2281 : int ITheta; // Table index of Theta
2282 : int IPhi; // Table index of Phi, given ITheta
2283 : int IThDn; // Theta lower table index
2284 : int IThUp; // Theta upper table index
2285 : int IPhDn; // Phi lower table index
2286 : int IPhUp; // Phi upper table index
2287 : Real64 Gamma; // Gamma (tilt) angle of window
2288 : Real64 Alpha; // Alpha (azimuth) angle of window
2289 : Real64 DotProd;
2290 :
2291 2833 : Theta = 0.0;
2292 2833 : Phi = 0.0;
2293 :
2294 : // Check if surface and vector are pointing in different directions
2295 2833 : DotProd = dot(RayToFind, state.dataSurface->Surface(ISurf).NewellSurfaceNormalVector);
2296 2833 : if (DotProd <= 0.0) {
2297 1158 : RayIndex = 0;
2298 1158 : return RayIndex;
2299 : }
2300 :
2301 : // get window tilt and azimuth
2302 1675 : Gamma = Constant::DegToRad * state.dataSurface->Surface(ISurf).Tilt;
2303 1675 : Alpha = Constant::DegToRad * state.dataSurface->Surface(ISurf).Azimuth;
2304 : // get the corresponding local Theta, Phi for ray
2305 1675 : W6CoordsFromWorldVect(state, RayToFind, RadType, Gamma, Alpha, Theta, Phi);
2306 :
2307 1675 : if (Theta >= 0.5 * Constant::Pi) { // Ray was in not in correct hemisphere
2308 0 : RayIndex = 0;
2309 0 : return RayIndex;
2310 : }
2311 1675 : if (Basis.BasisSymmetryType == DataBSDFWindow::BasisSymmetry::None) {
2312 : // Search the basis thetas
2313 1675 : if (Theta <= 0.0) {
2314 : // Special case, Theta = 0.; this is always the first basis element
2315 0 : RayIndex = 1;
2316 0 : return RayIndex;
2317 : }
2318 : // So here Theta > 0
2319 : // Note the table searches always go to the limit point, which is not itself a basis element
2320 1675 : IThUp = SearchAscTable(Theta, Basis.NThetas + 1, Basis.Thetas);
2321 1675 : IThDn = IThUp - 1;
2322 : // Determine which of the theta basis points is closer to the Theta value
2323 1675 : if (Theta <= Basis.Grid(Basis.BasisIndex(1, IThDn)).UpprTheta) {
2324 : // Note this will take care of both the special cases IThUp=2 and IThUp=NThetas +1
2325 872 : ITheta = IThDn;
2326 : } else {
2327 803 : ITheta = IThUp;
2328 : }
2329 : // Now determine the Phi index
2330 1675 : if (Basis.NPhis(ITheta) == 1) {
2331 : // Note that for W6 basis this can only happen for the first basis element
2332 : // If later bases are introduced this logic may have to be redesigned
2333 0 : RayIndex = Basis.BasisIndex(1, ITheta);
2334 0 : return RayIndex;
2335 : }
2336 1675 : IPhUp = SearchAscTable(Phi, Basis.NPhis(ITheta) + 1, Basis.Phis(ITheta, _));
2337 1675 : IPhDn = IPhUp - 1;
2338 1675 : if (Phi <= Basis.Grid(Basis.BasisIndex(IPhDn, ITheta)).UpprPhi) {
2339 862 : IPhi = IPhDn;
2340 : } else {
2341 813 : if (IPhUp == Basis.NPhis(ITheta) + 1) {
2342 : // Phi is above upper limit for highest Phi basis element, meaning it is closer to 2Pi,
2343 : // i.e., the first element
2344 114 : IPhi = 1;
2345 : } else {
2346 699 : IPhi = IPhUp;
2347 : }
2348 : }
2349 1675 : RayIndex = Basis.BasisIndex(IPhi, ITheta);
2350 1675 : return RayIndex;
2351 0 : } else if (Basis.BasisSymmetryType == DataBSDFWindow::BasisSymmetry::Axisymmetric) {
2352 : // Search the basis thetas
2353 0 : if (Theta <= 0.0) {
2354 : // Special case, Theta = 0.; this is always the first basis element
2355 0 : RayIndex = 1;
2356 0 : return RayIndex;
2357 : }
2358 : // So here Theta > 0
2359 : // Note the table searches always go to the limit point, which is not itself a basis element
2360 0 : IThUp = SearchAscTable(Theta, Basis.NThetas + 1, Basis.Thetas);
2361 0 : IThDn = IThUp - 1;
2362 : // Determine which of the theta basis points is closer to the Theta value
2363 0 : if (Theta <= Basis.Grid(Basis.BasisIndex(1, IThDn)).UpprTheta) {
2364 : // Note this will take care of both the special cases IThUp=2 and IThUp=NThetas +1
2365 0 : ITheta = IThDn;
2366 : } else {
2367 0 : ITheta = IThUp;
2368 : }
2369 0 : RayIndex = Basis.BasisIndex(1, ITheta);
2370 0 : return RayIndex;
2371 : }
2372 : // No other type is implemented
2373 0 : RayIndex = 0;
2374 :
2375 0 : return RayIndex;
2376 : }
2377 :
2378 3327 : void W6CoordsFromWorldVect([[maybe_unused]] EnergyPlusData &state,
2379 : Vector const &RayVect, // Ray vector direction in world CS
2380 : const RayIdentificationType RadType, // Type of radiation: Front_Incident, etc.
2381 : Real64 const Gamma, // Surface tilt angle, world coordinate system
2382 : Real64 const Alpha, // Surface azimuth, world coordinate system
2383 : Real64 &Theta, // Polar angle in W6 Coords
2384 : Real64 &Phi // Azimuthal angle in W6 Coords
2385 : )
2386 : {
2387 :
2388 : // SUBROUTINE INFORMATION:
2389 : // AUTHOR Joe Klems
2390 : // DATE WRITTEN August 2011
2391 : // MODIFIED na
2392 : // RE-ENGINEERED na
2393 :
2394 : // PURPOSE OF THIS FUNCTION:
2395 : // Invert the transformation from W6 to world coordinates to
2396 : // calculate the theta, phi corresponding to a given ray direction
2397 : // in the world coordinate system, for a window with a
2398 : // given rotation and tilt (Gamma and Alpha)
2399 : // (needed for locating the sun direction in the local coordinate system)
2400 :
2401 : using namespace Vectors;
2402 :
2403 3327 : Real64 Cost(0.0); // Temp for cos theta
2404 : Real64 Sint; // Temp for sin theta
2405 : Real64 Psi; // Temp for phi before rotation adjustment
2406 : Real64 RdotX; // Temp variable for manipulating .dot. produt
2407 : Real64 RdotY; // Temp variable for manipulating .dot. produt
2408 : Real64 RdotZ; // Temp variable for manipulating .dot. produt
2409 :
2410 : // Object Data
2411 3327 : Vector W6x; // W6 x coordinate unit vector
2412 3327 : Vector W6y; // W6 y coordinate unit vector
2413 3327 : Vector W6z; // W6 z coordinate unit vector
2414 :
2415 : // define the local W6 coordinate vectors
2416 3327 : W6x.x = std::cos(Alpha);
2417 3327 : W6x.y = -std::sin(Alpha);
2418 3327 : W6x.z = 0.0;
2419 3327 : W6y.x = -std::cos(Gamma) * std::sin(Alpha);
2420 3327 : W6y.y = -std::cos(Gamma) * std::cos(Alpha);
2421 3327 : W6y.z = std::sin(Gamma);
2422 3327 : W6z.x = -std::sin(Gamma) * std::sin(Alpha);
2423 3327 : W6z.y = -std::sin(Gamma) * std::cos(Alpha);
2424 3327 : W6z.z = -std::cos(Gamma);
2425 :
2426 3327 : switch (RadType) {
2427 3327 : case RayIdentificationType::Front_Incident: {
2428 3327 : RdotZ = dot(W6z, RayVect);
2429 3327 : Cost = -RdotZ;
2430 3327 : Sint = std::sqrt(1.0 - pow_2(Cost));
2431 3327 : Theta = std::acos(Cost);
2432 3327 : RdotY = dot(W6y, RayVect);
2433 3327 : RdotX = dot(W6x, RayVect);
2434 3327 : Psi = std::atan2(-RdotY / Sint, -RdotX / Sint);
2435 3327 : if (Psi < 0.0) {
2436 3327 : Phi = 2.0 * Constant::Pi + Psi;
2437 : } else {
2438 0 : Phi = Psi;
2439 : }
2440 3327 : break;
2441 : }
2442 0 : case RayIdentificationType::Front_Transmitted: {
2443 0 : Cost = dot(W6z, RayVect);
2444 0 : Sint = std::sqrt(1.0 - pow_2(Cost));
2445 0 : Theta = std::acos(Cost);
2446 0 : RdotY = dot(W6y, RayVect);
2447 0 : RdotX = dot(W6x, RayVect);
2448 0 : Psi = std::atan2(RdotY / Sint, RdotX / Sint);
2449 0 : if (Psi < 0.0) {
2450 0 : Phi = 2.0 * Constant::Pi + Psi;
2451 : } else {
2452 0 : Phi = Psi;
2453 : }
2454 0 : break;
2455 : }
2456 0 : case RayIdentificationType::Front_Reflected: {
2457 0 : RdotZ = dot(W6z, RayVect);
2458 0 : Cost = -RdotZ;
2459 0 : Sint = std::sqrt(1.0 - pow_2(Cost));
2460 0 : Theta = std::acos(Cost);
2461 0 : RdotY = dot(W6y, RayVect);
2462 0 : RdotX = dot(W6x, RayVect);
2463 0 : Psi = std::atan2(RdotY / Sint, RdotX / Sint);
2464 0 : if (Psi < 0.0) {
2465 0 : Phi = 2.0 * Constant::Pi + Psi;
2466 : } else {
2467 0 : Phi = Psi;
2468 : }
2469 0 : break;
2470 : }
2471 0 : case RayIdentificationType::Back_Incident: {
2472 0 : Cost = dot(W6z, RayVect);
2473 0 : Sint = std::sqrt(1.0 - pow_2(Cost));
2474 0 : Theta = std::acos(Cost);
2475 0 : RdotY = dot(W6y, RayVect);
2476 0 : RdotX = dot(W6x, RayVect);
2477 0 : Psi = std::atan2(-RdotY / Sint, -RdotX / Sint);
2478 0 : if (Psi < 0.0) {
2479 0 : Phi = 2 * Constant::Pi + Psi;
2480 : } else {
2481 0 : Phi = Psi;
2482 : }
2483 0 : break;
2484 : }
2485 0 : case RayIdentificationType::Back_Transmitted: { // This is same as front reflected
2486 0 : RdotZ = dot(W6z, RayVect);
2487 0 : Cost = -RdotZ;
2488 0 : Sint = std::sqrt(1.0 - pow_2(Cost));
2489 0 : Theta = std::acos(Cost);
2490 0 : RdotY = dot(W6y, RayVect);
2491 0 : RdotX = dot(W6x, RayVect);
2492 0 : Psi = std::atan2(RdotY / Sint, RdotX / Sint);
2493 0 : if (Psi < 0.0) {
2494 0 : Phi = 2.0 * Constant::Pi + Psi;
2495 : } else {
2496 0 : Phi = Psi;
2497 : }
2498 0 : break;
2499 : }
2500 0 : case RayIdentificationType::Back_Reflected: { // This is same as front transmitted
2501 0 : Cost = dot(W6z, RayVect);
2502 0 : Sint = std::sqrt(1.0 - pow_2(Cost));
2503 0 : Theta = std::acos(Cost);
2504 0 : RdotY = dot(W6y, RayVect);
2505 0 : RdotX = dot(W6x, RayVect);
2506 0 : Psi = std::atan2(RdotY / Sint, RdotX / Sint);
2507 0 : if (Psi < 0.0) {
2508 0 : Phi = 2.0 * Constant::Pi + Psi;
2509 : } else {
2510 0 : Phi = Psi;
2511 : }
2512 0 : break;
2513 : }
2514 0 : default:
2515 0 : assert(false);
2516 : break;
2517 : }
2518 3327 : if (std::abs(Cost) < Constant::rTinyValue) {
2519 0 : Cost = 0.0;
2520 : }
2521 3327 : if (Cost < 0.0) {
2522 672 : Theta = Constant::Pi - Theta; // This signals ray out of hemisphere
2523 : }
2524 3327 : }
2525 :
2526 37990 : void CalcComplexWindowThermal(EnergyPlusData &state,
2527 : int const SurfNum, // Surface number
2528 : int &ConstrNum, // Construction number
2529 : Real64 const HextConvCoeff, // Outside air film conductance coefficient
2530 : Real64 &SurfInsideTemp, // Inside window surface temperature
2531 : Real64 &SurfOutsideTemp, // Outside surface temperature (C)
2532 : Real64 &SurfOutsideEmiss,
2533 : DataBSDFWindow::Condition const CalcCondition // Calucation condition (summer, winter or no condition)
2534 : )
2535 : {
2536 :
2537 : // SUBROUTINE INFORMATION:
2538 : // AUTHOR B. Griffith
2539 : // DATE WRITTEN October 2009
2540 : // MODIFIED Simon Vidanovic
2541 : // RE-ENGINEERED September 2011
2542 :
2543 : // PURPOSE OF THIS SUBROUTINE:
2544 : // wrapper between E+ and TARCOG
2545 :
2546 : // METHODOLOGY EMPLOYED:
2547 : // draft out an attempt for proof-of-concept, to reuse native TARCOG implementation
2548 : // based off of 1-26-2009 version of WinCOG/TARCOG solution from Carli, Inc.
2549 :
2550 : using namespace DataBSDFWindow;
2551 : using Psychrometrics::PsyCpAirFnW;
2552 : using Psychrometrics::PsyTdpFnWPb;
2553 : using TARCOGGassesParams::maxgas;
2554 : using TARCOGMain::TARCOG90;
2555 : using TARCOGParams::maxlay;
2556 : using TARCOGParams::maxlay1;
2557 :
2558 : // Locals
2559 : // SUBROUTINE ARGUMENT DEFINITIONS:
2560 : // (temperature of innermost face) [C]
2561 : // INTEGER, INTENT(IN) :: CurrentThermalModelNumber
2562 :
2563 : // SUBROUTINE PARAMETER DEFINITIONS:
2564 : // INTEGER, PARAMETER :: maxlay = 100 ! maximum number of layers (including laminates)
2565 : // INTEGER, PARAMETER :: maxgas = 10 ! maximum number of individual gasses
2566 : // INTEGER, PARAMETER :: maxlay1 = maxlay+1 ! maximum number of 'gaps', including in and out (maxlay+1)
2567 : // REAL(r64), PARAMETER :: StefanBoltzmannConst = 5.6697d-8 ! Stefan-Boltzmann constant in W/(m2*K4)
2568 :
2569 : // TARCOG Inputs:
2570 37990 : int nlayer(0); // Number of glazing layers
2571 37990 : int iwd(0); // Wind direction: 0 - windward, 1 - leeward
2572 37990 : Real64 tout(0.0); // Outdoor temperature [K]
2573 37990 : Real64 tind(0.0); // Indoor temperature [K]
2574 37990 : Real64 trmin(0.0); // Indoor mean radiant temperature [K]
2575 37990 : Real64 wso(0.0); // Outdoor wind speed [m/s]
2576 37990 : Real64 wsi(0.0); // Inside forced air speed [m/s]
2577 37990 : Real64 dir(0.0); // Direct solar radiation [W/m^2]
2578 37990 : int isky(0); // Flag for sky temperature (Tsky) and sky emittance (esky)
2579 : // 0 - both tsky and esky are specified
2580 : // 1 - tsky specified, esky = 1
2581 : // 2 - Swinbank model for effective sky emittance
2582 37990 : Real64 tsky(0.0); // Night sky temperature [K]
2583 37990 : Real64 esky(0.0); // Effective night sky emittance
2584 37990 : Real64 fclr(0.0); // Fraction of sky that is clear
2585 : Real64 VacuumPressure; // maximal pressure for gas to be considered as vacuum [Pa]
2586 : Real64 VacuumMaxGapThickness; // maximal gap thickness for which vacuum calculation will work without issuing
2587 : // warning message
2588 :
2589 37990 : auto &gap = state.dataWindowComplexManager->gap;
2590 37990 : auto &thick = state.dataWindowComplexManager->thick;
2591 37990 : auto &scon = state.dataWindowComplexManager->scon;
2592 37990 : auto &tir = state.dataWindowComplexManager->tir;
2593 37990 : auto &emis = state.dataWindowComplexManager->emis;
2594 37990 : auto &SupportPlr = state.dataWindowComplexManager->SupportPlr;
2595 37990 : auto &PillarSpacing = state.dataWindowComplexManager->PillarSpacing;
2596 37990 : auto &PillarRadius = state.dataWindowComplexManager->PillarRadius;
2597 37990 : auto &asol = state.dataWindowComplexManager->asol;
2598 37990 : auto &presure = state.dataWindowComplexManager->presure;
2599 37990 : auto &GapDefMax = state.dataWindowComplexManager->GapDefMax;
2600 37990 : auto &YoungsMod = state.dataWindowComplexManager->YoungsMod;
2601 37990 : auto &PoissonsRat = state.dataWindowComplexManager->PoissonsRat;
2602 37990 : auto &LayerDef = state.dataWindowComplexManager->LayerDef;
2603 37990 : auto &iprop = state.dataWindowComplexManager->iprop;
2604 37990 : auto &frct = state.dataWindowComplexManager->frct;
2605 37990 : auto &gcon = state.dataWindowComplexManager->gcon;
2606 37990 : auto &gvis = state.dataWindowComplexManager->gvis;
2607 37990 : auto &gcp = state.dataWindowComplexManager->gcp;
2608 37990 : auto &wght = state.dataWindowComplexManager->wght;
2609 37990 : auto &gama = state.dataWindowComplexManager->gama;
2610 37990 : auto &nmix = state.dataWindowComplexManager->nmix;
2611 37990 : auto &ibc = state.dataWindowComplexManager->ibc;
2612 37990 : auto &Atop = state.dataWindowComplexManager->Atop;
2613 37990 : auto &Abot = state.dataWindowComplexManager->Abot;
2614 37990 : auto &Al = state.dataWindowComplexManager->Al;
2615 37990 : auto &Ar = state.dataWindowComplexManager->Ar;
2616 37990 : auto &Ah = state.dataWindowComplexManager->Ah;
2617 37990 : auto &SlatThick = state.dataWindowComplexManager->SlatThick;
2618 37990 : auto &SlatWidth = state.dataWindowComplexManager->SlatWidth;
2619 37990 : auto &SlatAngle = state.dataWindowComplexManager->SlatAngle;
2620 37990 : auto &SlatCond = state.dataWindowComplexManager->SlatCond;
2621 37990 : auto &SlatSpacing = state.dataWindowComplexManager->SlatSpacing;
2622 37990 : auto &SlatCurve = state.dataWindowComplexManager->SlatCurve;
2623 37990 : auto &vvent = state.dataWindowComplexManager->vvent;
2624 37990 : auto &tvent = state.dataWindowComplexManager->tvent;
2625 37990 : auto &LayerType = state.dataWindowComplexManager->LayerType;
2626 37990 : auto &nslice = state.dataWindowComplexManager->nslice;
2627 37990 : auto &LaminateA = state.dataWindowComplexManager->LaminateA;
2628 37990 : auto &LaminateB = state.dataWindowComplexManager->LaminateB;
2629 37990 : auto &sumsol = state.dataWindowComplexManager->sumsol;
2630 37990 : auto &theta = state.dataWindowComplexManager->theta;
2631 37990 : auto &q = state.dataWindowComplexManager->q;
2632 37990 : auto &qv = state.dataWindowComplexManager->qv;
2633 37990 : auto &hcgap = state.dataWindowComplexManager->hcgap;
2634 37990 : auto &hrgap = state.dataWindowComplexManager->hrgap;
2635 37990 : auto &hg = state.dataWindowComplexManager->hg;
2636 37990 : auto &hr = state.dataWindowComplexManager->hr;
2637 37990 : auto &hs = state.dataWindowComplexManager->hs;
2638 37990 : auto &Ra = state.dataWindowComplexManager->Ra;
2639 37990 : auto &Nu = state.dataWindowComplexManager->Nu;
2640 37990 : auto &Keff = state.dataWindowComplexManager->Keff;
2641 37990 : auto &ShadeGapKeffConv = state.dataWindowComplexManager->ShadeGapKeffConv;
2642 :
2643 37990 : Real64 totsol(0.0); // Total solar transmittance of the IGU
2644 37990 : Real64 tilt(0.0); // Window tilt [degrees]
2645 37990 : Real64 height(0.0); // IGU cavity height [m]
2646 37990 : Real64 heightt(0.0); // Total window height [m]
2647 37990 : Real64 width(0.0); // Window width [m]
2648 :
2649 : // Deflection
2650 : // Tarcog requires deflection as input parameters. Deflection is NOT used in EnergyPlus simulations
2651 : TARCOGParams::DeflectionCalculation CalcDeflection; // Deflection calculation flag:
2652 : // 0 - no deflection calculations
2653 : // 1 - perform deflection calculation (input is Pressure/Temp)
2654 : // 2 - perform deflection calculation (input is measured deflection)
2655 : Real64 Pa; // Atmospheric (outside/inside) pressure (used onlu if CalcDeflection = 1)
2656 : Real64 Pini; // Initial presssure at time of fabrication (used only if CalcDeflection = 1)
2657 : Real64 Tini; // Initial temperature at time of fabrication (used only if CalcDeflection = 1)
2658 37990 : Real64 hin(0.0); // Indoor combined film coefficient (if non-zero) [W/m^2.K]
2659 37990 : Real64 hout(0.0); // Outdoor combined film coefficient (if non-zero) [W/m^2.K]
2660 37990 : TARCOGGassesParams::Stdrd standard(TARCOGGassesParams::Stdrd::ISO15099); // Calculation standard switch:
2661 : // 1 - ISO 15099,
2662 : // 2 - EN673 / ISO 10292 Declared,
2663 : // 3 - EN673 / ISO 10292 Design.
2664 37990 : TARCOGParams::TARCOGThermalModel ThermalMod = TARCOGParams::TARCOGThermalModel::ISO15099; // Thermal model:
2665 : // 0 - ISO15099
2666 : // 1 - Scaled Cavity Width (SCW)
2667 : // 2 - Convective Scalar Model (CSM)
2668 37990 : std::string Debug_dir; // Target directory for debug files (pointer to a character array)
2669 37990 : std::string Debug_file("Test"); // Template file name used to create debug output files
2670 37990 : std::int32_t Window_ID(-1); // ID of the window (long integer value, passed by W6)
2671 37990 : std::int32_t IGU_ID(-1); // ID of the IGU (long integer value, passed by W6)
2672 37990 : Real64 SDScalar(0.0); // SD convection factor (value between 0 and 1)
2673 : // 0.0 - No SD layer
2674 : // 1.0 - Closed SD
2675 : // Notes: * vvent, tvent, Atop, Abot, Al, Ar and Ah are considered for SD layers only.
2676 : // ** SlatThick, SlatWidth, SlatAngle, SlatCond, SlatSpacing, SlatCurve
2677 : // are used for Venetian blind layers only.
2678 : // *** For vvent & tvent: vvent(1) - exterior, vvent(nlayer+1) - interior.
2679 : // **** Forced ventilation calculation is not active at this time.
2680 : // TARCOG Output:
2681 :
2682 37990 : Real64 ufactor(0.0); // Center of glass U-value [W/m^2.K]
2683 37990 : Real64 sc(0.0); // Shading Coefficient
2684 37990 : Real64 hflux(0.0); // Net heat flux between room and window [W/m^2]
2685 37990 : Real64 hcin(0.0); // Indoor convective surface heat transfer coefficient [W/m^2.K]
2686 37990 : Real64 hcout(0.0); // Outdoor convective surface heat transfer coefficient [W/m^2.K]
2687 37990 : Real64 hrin(0.0); // Indoor radiative surface heat transfer coefficient [W/m^2.K]
2688 37990 : Real64 hrout(0.0); // Outdoor radiative surface heat transfer coefficient [W/m^2.K]
2689 37990 : Real64 shgc(0.0); // Solar heat gain coefficient - per ISO 15099
2690 37990 : Real64 shgct(0.0); // Solar heat gain coefficient - per old procedure
2691 37990 : Real64 tamb(0.0); // Outdoor environmental temperature [K]
2692 37990 : Real64 troom(0.0); // Indoor environmental temperature [K]
2693 37990 : Real64 he(0.0); // External heat transfer coefficient [W/m^2.K] - EN673 and ISO 10292 procedure
2694 37990 : Real64 hi(0.0); // Internal heat transfer coefficient [W/m^2.K] - EN673 and ISO 10292 procedure
2695 37990 : int nperr(0); // Error code
2696 37990 : Real64 ShadeEmisRatioOut(0.0); // Ratio of modified to glass emissivity at the outermost glazing surface
2697 37990 : Real64 ShadeEmisRatioIn(0.0); // Ratio of modified to glass emissivity at the innermost glazing surface
2698 37990 : Real64 ShadeHcRatioOut(0.0); // Ratio of modified to unshaded Hc at the outermost glazing surface
2699 37990 : Real64 ShadeHcRatioIn(0.0); // Ratio of modified to unshaded Hc at the innermost glazing surface
2700 37990 : Real64 HcUnshadedOut(0.0); // Hc value at outdoor surface of an unshaded subsystem [W/m^2.K]
2701 37990 : Real64 HcUnshadedIn(0.0); // Hc value at indoor surface of an unshaded subsystem [W/m^2.K]
2702 :
2703 : int ZoneNum; // Zone number corresponding to SurfNum
2704 :
2705 : int TotLay; // Total number of layers in a construction
2706 : // (sum of solid layers and gap layers)
2707 : int Lay; // Layer number
2708 : int LayPtr; // Material number for a layer
2709 : int IGlass; // glass layer number (1,2,3,...)
2710 : int IGap; // Gap layer number (1,2,...)
2711 : int TotGlassLay; // Total number of glass layers in a construction
2712 : int k; // Layer counter
2713 : int SurfNumAdj; // An interzone surface's number in the adjacent zone
2714 : WinShadingType ShadeFlag; // Flag indicating whether shade or blind is on, and shade/blind position
2715 : int IMix;
2716 :
2717 : Real64 IncidentSolar; // Solar incident on outside of window (W)
2718 : Real64 ConvHeatFlowNatural; // Convective heat flow from gap between glass and interior shade or blind (W)
2719 : Real64 ShadeArea; // shade/blind area (m2)
2720 : Real64 sconsh; // shade/blind conductance (W/m2-K)
2721 : Real64 CondHeatGainShade; // Conduction through shade/blind, outside to inside (W)
2722 :
2723 : Real64 ShGlReflFacIR; // Factor for long-wave inter-reflection between shade/blind and adjacent glass
2724 : // Real64 RhoGlIR1; // Long-wave reflectance of glass surface facing shade/blind; 1=exterior shade/blind,
2725 : Real64 RhoGlIR2;
2726 : // 2=interior shade/blind
2727 : Real64 RhoShIR1; // Long-wave reflectance of shade/blind surface facing glass; 1=interior shade/blind,
2728 : Real64 RhoShIR2;
2729 : // 2=exterior shade/blind
2730 : Real64 EpsShIR1; // Long-wave emissivity of shade/blind surface facing glass; 1=interior shade/blind,
2731 : Real64 EpsShIR2;
2732 : // 2=exterior shade/blind
2733 : Real64 TauShIR; // Long-wave transmittance of isolated shade/blind
2734 : Real64 NetIRHeatGainShade; // Net IR heat gain to zone from interior shade/blind (W)
2735 : Real64 NetIRHeatGainGlass; // Net IR heat gain to zone from shade/blind side of glass when interior
2736 : // shade/blind is present. Zero if shade/blind has zero IR transmittance (W)
2737 : Real64 ConvHeatGainFrZoneSideOfShade; // Convective heat gain to zone from side of interior shade facing zone (W)
2738 : Real64 ConvHeatGainFrZoneSideOfGlass; // Convective heat gain to zone from side of glass facing zone when
2739 : // no interior shade/blind is present (W)
2740 : Real64 CondHeatGainGlass; // Conduction through inner glass layer, outside to inside (W)
2741 : Real64 TotAirflowGap; // Total volumetric airflow through window gap (m3/s)
2742 : Real64 TAirflowGapOutlet; // Temperature of air leaving airflow gap between glass panes (K)
2743 : Real64 TAirflowGapOutletC; // Temperature of air leaving airflow gap between glass panes (C)
2744 : Real64 ConvHeatFlowForced; // Convective heat flow from forced airflow gap (W)
2745 : Real64 InletAirHumRat; // Humidity ratio of air from window gap entering fan
2746 : Real64 ZoneTemp; // Zone air temperature (C)
2747 : Real64 CpAirOutlet; // Heat capacity of air from window gap (J/kg-K)
2748 : Real64 CpAirZone; // Heat capacity of zone air (J/kg-K)
2749 : Real64 ConvHeatGainToZoneAir; // Convective heat gain to zone air from window gap airflow (W)
2750 : // int ConstrNumSh; // Construction number with shading device
2751 37990 : int CalcSHGC(0); // SHGC calculations are not necessary for E+ run
2752 37990 : int NumOfIterations(0);
2753 :
2754 37990 : std::string tarcogErrorMessage; // store error text from tarcog
2755 :
2756 : // Simon: locally used variables
2757 : int ngllayer;
2758 : int nglface;
2759 : int nglfacep;
2760 : int ThermalModelNum;
2761 : Real64 rmir; // IR radiance of window's interior surround (W/m2)
2762 : Real64 outir;
2763 : Real64 Ebout;
2764 : Real64 dominantGapWidth; // store value for dominant gap width. Used for airflow calculations
2765 : Real64 edgeGlCorrFac;
2766 :
2767 : Real64 SrdSurfTempAbs; // Absolute temperature of a surrounding surface
2768 : Real64 OutSrdIR;
2769 :
2770 37990 : auto &s_mat = state.dataMaterial;
2771 : // fill local vars
2772 :
2773 37990 : CalcDeflection = TARCOGParams::DeflectionCalculation::NONE;
2774 37990 : CalcSHGC = 0;
2775 :
2776 37990 : if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
2777 37968 : ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
2778 37968 : SurfNumAdj = state.dataSurface->Surface(SurfNum).ExtBoundCond;
2779 37968 : ShadeFlag = state.dataSurface->SurfWinShadingFlag(SurfNum);
2780 : }
2781 :
2782 37990 : TotGlassLay = state.dataConstruction->Construct(ConstrNum).TotGlassLayers;
2783 37990 : ngllayer = state.dataConstruction->Construct(ConstrNum).TotGlassLayers;
2784 37990 : nglface = 2 * ngllayer;
2785 37990 : nglfacep = nglface;
2786 37990 : hrin = 0.0;
2787 37990 : hcin = 0.0;
2788 37990 : hrout = 0.0;
2789 37990 : hcout = 0.0;
2790 :
2791 37990 : Pa = state.dataEnvrn->OutBaroPress;
2792 :
2793 37990 : ThermalModelNum = state.dataConstruction->Construct(ConstrNum).BSDFInput.ThermalModel;
2794 37990 : standard = state.dataMaterial->WindowThermalModel(ThermalModelNum).CalculationStandard;
2795 37990 : ThermalMod = state.dataMaterial->WindowThermalModel(ThermalModelNum).ThermalModel;
2796 37990 : CalcDeflection = state.dataMaterial->WindowThermalModel(ThermalModelNum).DeflectionModel;
2797 37990 : SDScalar = state.dataMaterial->WindowThermalModel(ThermalModelNum).SDScalar;
2798 37990 : VacuumPressure = state.dataMaterial->WindowThermalModel(ThermalModelNum).VacuumPressureLimit;
2799 37990 : Tini = state.dataMaterial->WindowThermalModel(ThermalModelNum).InitialTemperature - Constant::Kelvin;
2800 37990 : Pini = state.dataMaterial->WindowThermalModel(ThermalModelNum).InitialPressure;
2801 :
2802 37990 : nlayer = state.dataConstruction->Construct(ConstrNum).TotSolidLayers;
2803 37990 : isky = 3; // IR radiation is provided from external source
2804 37990 : iwd = 0; // assume windward for now. TODO compare surface normal with wind direction
2805 :
2806 37990 : if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
2807 37968 : ZoneNum = state.dataSurface->Surface(SurfNum).Zone;
2808 37968 : Real64 RefAirTemp = state.dataSurface->Surface(SurfNum).getInsideAirTemperature(state, SurfNum);
2809 37968 : tind = RefAirTemp + Constant::Kelvin; // Inside air temperature
2810 :
2811 : // now get "outside" air temperature
2812 37968 : if (SurfNumAdj > 0) { // Interzone window
2813 :
2814 0 : int enclNumAdj = state.dataSurface->Surface(SurfNumAdj).RadEnclIndex;
2815 0 : RefAirTemp = state.dataSurface->Surface(SurfNumAdj).getInsideAirTemperature(state, SurfNumAdj);
2816 0 : tout = RefAirTemp + Constant::Kelvin; // outside air temperature
2817 :
2818 0 : tsky = state.dataViewFactor->EnclRadInfo(enclNumAdj).MRT +
2819 : Constant::Kelvin; // TODO this misses IR from sources such as high temp radiant and baseboards
2820 :
2821 : // ! Add long-wave radiation from adjacent zone absorbed by glass layer closest to the adjacent zone.
2822 : // AbsRadGlassFace(1) = AbsRadGlassFace(1) + SurfQRadThermInAbs(SurfNumAdj)
2823 : // ! The IR radiance of this window's "exterior" surround is the IR radiance
2824 : // ! from surfaces and high-temp radiant sources in the adjacent zone
2825 0 : outir = state.dataSurface->SurfWinIRfromParentZone(SurfNumAdj) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(SurfNumAdj);
2826 :
2827 : } else { // Exterior window (ExtBoundCond = 0)
2828 : // Calculate LWR from surrounding surfaces if defined for an exterior window
2829 37968 : OutSrdIR = 0;
2830 37968 : if (state.dataGlobal->AnyLocalEnvironmentsInModel) {
2831 0 : if (state.dataSurface->Surface(SurfNum).SurfHasSurroundingSurfProperty) {
2832 0 : SrdSurfTempAbs = state.dataSurface->Surface(SurfNum).SrdSurfTemp + Constant::Kelvin;
2833 0 : OutSrdIR = Constant::StefanBoltzmann * state.dataSurface->Surface(SurfNum).ViewFactorSrdSurfs * pow_4(SrdSurfTempAbs);
2834 : }
2835 : }
2836 37968 : if (state.dataSurface->Surface(SurfNum).ExtWind) { // Window is exposed to wind (and possibly rain)
2837 37968 : if (state.dataEnvrn->IsRain) { // Raining: since wind exposed, outside window surface gets wet
2838 0 : tout = state.dataSurface->SurfOutWetBulbTemp(SurfNum) + Constant::Kelvin;
2839 : } else { // Dry
2840 37968 : tout = state.dataSurface->SurfOutDryBulbTemp(SurfNum) + Constant::Kelvin;
2841 : }
2842 : } else { // Window not exposed to wind
2843 0 : tout = state.dataSurface->SurfOutDryBulbTemp(SurfNum) + Constant::Kelvin;
2844 : }
2845 : // tsky = SkyTemp + TKelvin
2846 37968 : tsky = state.dataEnvrn->SkyTempKelvin;
2847 37968 : Ebout = state.dataWindowComplexManager->sigma * pow_4(tout);
2848 37968 : outir = state.dataSurface->Surface(SurfNum).ViewFactorSkyIR *
2849 37968 : (state.dataSurface->SurfAirSkyRadSplit(SurfNum) * state.dataWindowComplexManager->sigma * pow_4(tsky) +
2850 37968 : (1.0 - state.dataSurface->SurfAirSkyRadSplit(SurfNum)) * Ebout) +
2851 37968 : state.dataSurface->Surface(SurfNum).ViewFactorGroundIR * Ebout + OutSrdIR;
2852 : }
2853 :
2854 37968 : hin = state.dataHeatBalSurf->SurfHConvInt(SurfNum); // Room-side surface convective film conductance
2855 37968 : ibc(2) = 0; // convective coefficient on indoor side will be recalculated (like in Winkelmann routines)
2856 :
2857 : // hcout=HextConvCoeff ! Exterior convection coefficient is passed in from outer routine
2858 37968 : hout = HextConvCoeff; // Exterior convection coefficient is passed in from outer routine
2859 37968 : ibc(1) = 2; // prescribed convective film coeff on outdoor side
2860 37968 : tilt = state.dataSurface->Surface(SurfNum).Tilt;
2861 37968 : height = state.dataSurface->Surface(SurfNum).Height;
2862 37968 : heightt = height; // for now put same window and glazing pocket hights
2863 37968 : width = state.dataSurface->Surface(SurfNum).Width;
2864 :
2865 : // indoor mean radiant temperature.
2866 : // IR incident on window from zone surfaces and high-temp radiant sources
2867 37968 : rmir = state.dataSurface->SurfWinIRfromParentZone(SurfNum) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(SurfNum);
2868 37968 : trmin = root_4(rmir / Constant::StefanBoltzmann); // TODO check model equation.
2869 :
2870 : // outdoor wind speed
2871 37968 : if (!state.dataSurface->Surface(SurfNum).ExtWind) {
2872 0 : wso = 0.0; // No wind exposure
2873 : // ELSE IF (Surface(SurfNum)%Class == SurfaceClass::Window .AND. SurfaceWindow(SurfNum)%ShadingFlag ==
2874 : // WinShadingType::ExtShade) THEN
2875 : // wso = 0.0 ! Assume zero wind speed at outside glass surface of window with exterior shade
2876 : } else {
2877 37968 : wso = state.dataSurface->SurfOutWindSpeed(SurfNum);
2878 : }
2879 :
2880 : // indoor wind speed
2881 37968 : wsi = 0.0; // assumuption (TODO, what to use for inside air velocity?)
2882 :
2883 37968 : fclr = 1.0 - state.dataEnvrn->CloudFraction;
2884 : }
2885 :
2886 37990 : TotLay = state.dataConstruction->Construct(ConstrNum).TotLayers;
2887 37990 : IGap = 0;
2888 :
2889 : //****************************************************************************************************
2890 : // Inside and outside gas coefficients
2891 : //****************************************************************************************************
2892 37990 : iprop(1, 1) = 1; // air on outdoor side
2893 37990 : frct(1, 1) = 1.0; // pure air on outdoor side
2894 37990 : nmix(1) = 1; // pure air on outdoor side
2895 :
2896 37990 : iprop(1, nlayer + 1) = 1; // air on indoor side
2897 37990 : frct(1, nlayer + 1) = 1.0; // pure air on indoor side
2898 37990 : nmix(nlayer + 1) = 1; // pure air on indoor side
2899 :
2900 : // Simon: feed gas coefficients with air. This is necessary for tarcog because it is used on indoor and outdoor sides
2901 37990 : auto const &gasAir = Material::gases[(int)Material::GasType::Air];
2902 37990 : wght(iprop(1, 1)) = gasAir.wght;
2903 37990 : gama(iprop(1, 1)) = gasAir.specHeatRatio;
2904 :
2905 37990 : gcon(1, iprop(1, 1)) = gasAir.con.c0;
2906 37990 : gcon(2, iprop(1, 1)) = gasAir.con.c1;
2907 37990 : gcon(3, iprop(1, 1)) = gasAir.con.c2;
2908 37990 : gvis(1, iprop(1, 1)) = gasAir.vis.c0;
2909 37990 : gvis(2, iprop(1, 1)) = gasAir.vis.c1;
2910 37990 : gvis(3, iprop(1, 1)) = gasAir.vis.c2;
2911 37990 : gcp(1, iprop(1, 1)) = gasAir.cp.c0;
2912 37990 : gcp(2, iprop(1, 1)) = gasAir.cp.c1;
2913 37990 : gcp(3, iprop(1, 1)) = gasAir.cp.c2;
2914 :
2915 : // Fill window layer properties needed for window layer heat balance calculation
2916 37990 : IGlass = 0;
2917 37990 : IGap = 0;
2918 213144 : for (Lay = 1; Lay <= TotLay; ++Lay) {
2919 175154 : LayPtr = state.dataConstruction->Construct(ConstrNum).LayerPoint(Lay);
2920 175154 : auto const *mat = s_mat->materials(LayPtr);
2921 :
2922 175154 : if ((mat->group == Material::Group::Glass) || (mat->group == Material::Group::GlassSimple)) {
2923 76654 : auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
2924 76654 : assert(matGlass != nullptr);
2925 :
2926 76654 : ++IGlass;
2927 76654 : LayerType(IGlass) = TARCOGParams::TARCOGLayerType::SPECULAR; // this marks specular layer type
2928 76654 : thick(IGlass) = matGlass->Thickness;
2929 76654 : scon(IGlass) = matGlass->Conductivity;
2930 76654 : emis(2 * IGlass - 1) = matGlass->AbsorpThermalFront;
2931 76654 : emis(2 * IGlass) = matGlass->AbsorpThermalBack;
2932 76654 : tir(2 * IGlass - 1) = matGlass->TransThermal;
2933 76654 : tir(2 * IGlass) = matGlass->TransThermal;
2934 76654 : YoungsMod(IGlass) = matGlass->YoungModulus;
2935 76654 : PoissonsRat(IGlass) = matGlass->PoissonsRatio;
2936 :
2937 175154 : } else if (mat->group == Material::Group::ComplexShade) {
2938 29918 : auto const *matShade = dynamic_cast<Material::MaterialComplexShade const *>(mat);
2939 29918 : ++IGlass;
2940 29918 : LayerType(IGlass) = matShade->LayerType;
2941 :
2942 29918 : thick(IGlass) = matShade->Thickness;
2943 29918 : scon(IGlass) = matShade->Conductivity;
2944 29918 : emis(2 * IGlass - 1) = matShade->FrontEmissivity;
2945 29918 : emis(2 * IGlass) = matShade->BackEmissivity;
2946 29918 : tir(2 * IGlass - 1) = matShade->TransThermal;
2947 29918 : tir(2 * IGlass) = matShade->TransThermal;
2948 :
2949 : // This needs to be converted into correct areas. That can be done only after loading complete window data
2950 29918 : Atop(IGlass) = matShade->topOpeningMult;
2951 29918 : Abot(IGlass) = matShade->bottomOpeningMult;
2952 29918 : Al(IGlass) = matShade->leftOpeningMult;
2953 29918 : Ar(IGlass) = matShade->rightOpeningMult;
2954 29918 : Ah(IGlass) = matShade->frontOpeningMult;
2955 :
2956 29918 : SlatThick(IGlass) = matShade->SlatThickness;
2957 29918 : SlatWidth(IGlass) = matShade->SlatWidth;
2958 29918 : SlatAngle(IGlass) = matShade->SlatAngle;
2959 29918 : SlatCond(IGlass) = matShade->SlatConductivity;
2960 29918 : SlatSpacing(IGlass) = matShade->SlatSpacing;
2961 29918 : SlatCurve(IGlass) = matShade->SlatCurve;
2962 :
2963 68582 : } else if (mat->group == Material::Group::ComplexWindowGap) {
2964 68582 : auto const *matGap = dynamic_cast<Material::MaterialComplexWindowGap const *>(mat);
2965 68582 : ++IGap;
2966 68582 : gap(IGap) = matGap->Thickness;
2967 68582 : presure(IGap) = matGap->Pressure;
2968 :
2969 68582 : GapDefMax(IGap) = matGap->deflectedThickness;
2970 :
2971 68582 : PillarSpacing(IGap) = matGap->pillarSpacing;
2972 68582 : PillarRadius(IGap) = matGap->pillarRadius;
2973 68582 : SupportPlr(IGap) = matGap->pillarSpacing != 0.0 && matGap->pillarRadius != 0.0;
2974 :
2975 68582 : nmix(IGap + 1) = matGap->numGases;
2976 149272 : for (IMix = 1; IMix <= nmix(IGap + 1); ++IMix) {
2977 80690 : auto const &gas = matGap->gases[IMix - 1];
2978 80690 : frct(IMix, IGap + 1) = matGap->gasFracts[IMix - 1];
2979 :
2980 : // Now has to build-up gas coefficients arrays. All used gasses should be stored into these arrays and
2981 : // to be correctly referenced by gap arrays
2982 :
2983 : // First check if gas coefficients are already part of array. Duplicates are not necessary
2984 80690 : bool feedData(false);
2985 80690 : CheckGasCoefs(gas.wght, iprop(IMix, IGap + 1), wght, feedData);
2986 80690 : if (feedData) {
2987 2 : wght(iprop(IMix, IGap + 1)) = gas.wght;
2988 2 : gama(iprop(IMix, IGap + 1)) = gas.specHeatRatio;
2989 :
2990 2 : gcon(1, iprop(IMix, IGap + 1)) = gas.con.c0;
2991 2 : gcon(2, iprop(IMix, IGap + 1)) = gas.con.c1;
2992 2 : gcon(3, iprop(IMix, IGap + 1)) = gas.con.c2;
2993 :
2994 2 : gvis(1, iprop(IMix, IGap + 1)) = gas.vis.c0;
2995 2 : gvis(2, iprop(IMix, IGap + 1)) = gas.vis.c1;
2996 2 : gvis(3, iprop(IMix, IGap + 1)) = gas.vis.c2;
2997 :
2998 2 : gcp(1, iprop(IMix, IGap + 1)) = gas.cp.c0;
2999 2 : gcp(2, iprop(IMix, IGap + 1)) = gas.cp.c1;
3000 2 : gcp(3, iprop(IMix, IGap + 1)) = gas.cp.c2;
3001 : } // IF feedData THEN
3002 : }
3003 : } else {
3004 0 : ShowContinueError(state, "Illegal layer type in Construction:ComplexFenestrationState.");
3005 0 : ShowContinueError(state, "Allowed object are:");
3006 0 : ShowContinueError(state, " - WindowMaterial:Glazing");
3007 0 : ShowContinueError(state, " - WindowMaterial:ComplexShade");
3008 0 : ShowContinueError(state, " - WindowMaterial:Gap");
3009 0 : ShowFatalError(state, "halting because of error in layer definition for Construction:ComplexFenestrationState");
3010 : }
3011 :
3012 : } // End of loop over glass, gap and blind/shade layers in a window construction
3013 :
3014 37990 : if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
3015 : // now calculate correct areas for multipliers
3016 144480 : for (Lay = 1; Lay <= nlayer; ++Lay) {
3017 106512 : if (LayerType(Lay) != TARCOGParams::TARCOGLayerType::SPECULAR) { // Layer is shading
3018 : // before changing multipliers, need to determine which one is dominant gap width
3019 29904 : if (Lay == 1) { // Exterior shading device
3020 10080 : dominantGapWidth = gap(Lay);
3021 19824 : } else if (Lay == nlayer) { // Interior shading device
3022 15792 : dominantGapWidth = gap(Lay - 1);
3023 : } else { // In-between shading device
3024 4032 : dominantGapWidth = min(gap(Lay - 1), gap(Lay));
3025 : }
3026 29904 : Atop(Lay) *= dominantGapWidth * width;
3027 29904 : Abot(Lay) *= dominantGapWidth * width;
3028 29904 : Al(Lay) *= dominantGapWidth * height;
3029 29904 : Ar(Lay) *= dominantGapWidth * height;
3030 29904 : Ah(Lay) *= width * height;
3031 : }
3032 : }
3033 : }
3034 :
3035 : // ThermalMod = 0
3036 37990 : CalcSHGC = 0;
3037 :
3038 37990 : Window_ID = ConstrNum;
3039 :
3040 : // vector of absorbed solar energy fractions for each layer.
3041 37990 : asol = 0.0;
3042 : // direct solar radiation
3043 37990 : if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
3044 37968 : ShadeFlag = state.dataSurface->SurfWinShadingFlag(SurfNum);
3045 37968 : dir = state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) +
3046 37968 : state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(SurfNum).SolarEnclIndex); // TODO, check , !
3047 : // currently using Exterior beam plus diffuse solar incident on surface
3048 : // plus zone short wave. CHECK
3049 : // if (dir.ne.0.0d0) then
3050 144480 : for (IGlass = 1; IGlass <= nlayer; ++IGlass) {
3051 : // IF (dir > 0.0D0 ) THEN
3052 106512 : asol(IGlass) = state.dataHeatBal->SurfWinQRadSWwinAbs(SurfNum, IGlass);
3053 : // ELSE
3054 : // asol(IGLASS) = 0.0D0
3055 : // ENDIF
3056 : }
3057 : // end if
3058 :
3059 : // Add contribution of IR from zone internal gains (lights, equipment and people). This is absorbed in zone-side layer and it
3060 : // is assumed that nothing is transmitted through
3061 37968 : asol(nlayer) += state.dataHeatBal->SurfQdotRadIntGainsInPerArea(SurfNum);
3062 :
3063 37968 : presure = state.dataEnvrn->OutBaroPress;
3064 :
3065 : // Instead of doing temperature guess get solution from previous iteration. That should be much better than guess
3066 250992 : for (k = 1; k <= 2 * nlayer; ++k) {
3067 213024 : theta(k) = state.dataSurface->SurfaceWindow(SurfNum).thetaFace[k];
3068 : }
3069 : }
3070 :
3071 : // Standard conditions run (winter and summer)
3072 37990 : if (CalcCondition == DataBSDFWindow::Condition::Winter) {
3073 11 : tind = 294.15;
3074 11 : tout = 255.15;
3075 11 : hcout = 26.0;
3076 11 : wso = 5.5;
3077 11 : dir = 0.0;
3078 37979 : } else if (CalcCondition == DataBSDFWindow::Condition::Summer) {
3079 11 : tind = 297.15;
3080 11 : tout = 305.15;
3081 11 : hcout = 15.0;
3082 11 : wso = 2.75;
3083 11 : dir = 783.0;
3084 11 : CalcSHGC = 1;
3085 : }
3086 :
3087 : // Common condition data
3088 37990 : if (CalcCondition != DataBSDFWindow::Condition::Invalid) {
3089 22 : trmin = tind;
3090 22 : outir = 0.0;
3091 22 : tsky = tout;
3092 22 : wsi = 0.0;
3093 22 : fclr = 1.0;
3094 22 : ibc(1) = 0;
3095 22 : ibc(2) = 0;
3096 22 : presure = 101325.0;
3097 22 : iwd = 0; // Windward wind direction
3098 22 : isky = 0;
3099 22 : esky = 1.0;
3100 22 : height = 1.0;
3101 22 : heightt = 1.0;
3102 22 : width = 1.0;
3103 22 : tilt = 90.0;
3104 : // Just to make initial quess different from absolute zero
3105 22 : theta = 273.15;
3106 : }
3107 :
3108 37990 : edgeGlCorrFac = (SurfNum != 0) ? state.dataSurface->SurfaceWindow(SurfNum).edgeGlassCorrFac : 1;
3109 :
3110 : // call TARCOG
3111 37990 : int constexpr Debug_mode = 0;
3112 37990 : TARCOG90(state,
3113 : nlayer,
3114 : iwd,
3115 : tout,
3116 : tind,
3117 : trmin,
3118 : wso,
3119 : wsi,
3120 : dir,
3121 : outir,
3122 : isky,
3123 : tsky,
3124 : esky,
3125 : fclr,
3126 : VacuumPressure,
3127 : VacuumMaxGapThickness,
3128 : CalcDeflection,
3129 : Pa,
3130 : Pini,
3131 : Tini,
3132 : gap,
3133 : GapDefMax,
3134 : thick,
3135 : scon,
3136 : YoungsMod,
3137 : PoissonsRat,
3138 : tir,
3139 : emis,
3140 : totsol,
3141 : tilt,
3142 : asol,
3143 : height,
3144 : heightt,
3145 : width,
3146 : presure,
3147 : iprop,
3148 : frct,
3149 : gcon,
3150 : gvis,
3151 : gcp,
3152 : wght,
3153 : gama,
3154 : nmix,
3155 : SupportPlr,
3156 : PillarSpacing,
3157 : PillarRadius,
3158 : theta,
3159 : LayerDef,
3160 : q,
3161 : qv,
3162 : ufactor,
3163 : sc,
3164 : hflux,
3165 : hcin,
3166 : hcout,
3167 : hrin,
3168 : hrout,
3169 : hin,
3170 : hout,
3171 : hcgap,
3172 : hrgap,
3173 : shgc,
3174 : nperr,
3175 : tarcogErrorMessage,
3176 : shgct,
3177 : tamb,
3178 : troom,
3179 : ibc,
3180 : Atop,
3181 : Abot,
3182 : Al,
3183 : Ar,
3184 : Ah,
3185 : SlatThick,
3186 : SlatWidth,
3187 : SlatAngle,
3188 : SlatCond,
3189 : SlatSpacing,
3190 : SlatCurve,
3191 : vvent,
3192 : tvent,
3193 : LayerType,
3194 : nslice,
3195 : LaminateA,
3196 : LaminateB,
3197 : sumsol,
3198 : hg,
3199 : hr,
3200 : hs,
3201 : he,
3202 : hi,
3203 : Ra,
3204 : Nu,
3205 : standard,
3206 : ThermalMod,
3207 : Debug_mode,
3208 : Debug_dir,
3209 : Debug_file,
3210 : Window_ID,
3211 : IGU_ID,
3212 : ShadeEmisRatioOut,
3213 : ShadeEmisRatioIn,
3214 : ShadeHcRatioOut,
3215 : ShadeHcRatioIn,
3216 : HcUnshadedOut,
3217 : HcUnshadedIn,
3218 : Keff,
3219 : ShadeGapKeffConv,
3220 : SDScalar,
3221 : CalcSHGC,
3222 : NumOfIterations,
3223 : edgeGlCorrFac);
3224 :
3225 : // process results from TARCOG
3226 37990 : if ((nperr > 0) && (nperr < 1000)) { // process error signal from tarcog
3227 :
3228 0 : ShowSevereError(state, "Window tarcog returned an error");
3229 0 : tarcogErrorMessage = "message = \"" + tarcogErrorMessage + "\"";
3230 0 : ShowContinueErrorTimeStamp(state, tarcogErrorMessage);
3231 0 : if (CalcCondition == DataBSDFWindow::Condition::Invalid) {
3232 0 : ShowContinueError(state, format("surface name = {}", state.dataSurface->Surface(SurfNum).Name));
3233 : }
3234 0 : ShowContinueError(state, format("construction name = {}", state.dataConstruction->Construct(ConstrNum).Name));
3235 0 : ShowFatalError(state, "halting because of error in tarcog");
3236 37990 : } else if (CalcCondition == DataBSDFWindow::Condition::Winter) {
3237 11 : state.dataHeatBal->NominalU(ConstrNum) = ufactor;
3238 37979 : } else if (CalcCondition == DataBSDFWindow::Condition::Summer) {
3239 : // tempInt = SurfaceWindow(SurfNum)%ComplexFen%CurrentState
3240 : // tempReal = SurfaceWindow(SurfNum)%ComplexFen%State(tempInt)%WinDiffTrans
3241 :
3242 : // Sum1 = 0.0d0
3243 : // Sum2 = 0.0d0
3244 : // do j = 1 , ComplexWind(SurfNum)%Geom%Inc%NBasis !Incident ray loop
3245 : // Sum2 = Sum2 + ComplexWind(SurfNum)%Geom%Inc%Lamda (j)
3246 : // do m = 1 , ComplexWind(SurfNum)%Geom%Trn%NBasis !Outgoing ray loop
3247 : // Sum1 =Sum1 + ComplexWind(SurfNum)%Geom%Inc%Lamda(j) * ComplexWind(SurfNum)%Geom%Trn%Lamda(m) * &
3248 : // & Construct(ConstrNum)%BSDFInput%SolFrtTrans ( j , m )
3249 : // end do !Outgoing ray loop
3250 : // end do !Incident ray loop
3251 : // if (Sum2 > 0.0d0) THEN
3252 : // tempReal = Sum1/Sum2
3253 : // else
3254 : // tempReal = 0.0d0
3255 : // CALL ShowWarningError(state, 'BSDF--Inc basis has zero projected solid angle')
3256 : // endif
3257 :
3258 11 : state.dataConstruction->Construct(ConstrNum).SummerSHGC = shgc;
3259 :
3260 : // Construct(SurfNum)%VisTransNorm = SurfaceWindow(SurfNum)%ComplexFen%State(tempInt)%WinDiffVisTrans
3261 37968 : } else if (CalcCondition == DataBSDFWindow::Condition::Invalid) { // expect converged results...
3262 : // Window heat balance solution has converged.
3263 :
3264 37968 : state.dataSurface->SurfWinWindowCalcIterationsRep(SurfNum) = NumOfIterations;
3265 37968 : state.dataHeatBalSurf->SurfHConvInt(SurfNum) = hcin;
3266 :
3267 : // For interior shade, add convective gain from glass/shade gap air flow to zone convective gain;
3268 : // For all cases, get total window heat gain for reporting. See CalcWinFrameAndDividerTemps for
3269 : // contribution of frame and divider.
3270 :
3271 37968 : SurfInsideTemp = theta(2 * nlayer) - Constant::Kelvin;
3272 37968 : state.dataSurface->SurfWinEffInsSurfTemp(SurfNum) = SurfInsideTemp;
3273 37968 : SurfOutsideTemp = theta(1) - Constant::Kelvin;
3274 37968 : SurfOutsideEmiss = emis(1);
3275 :
3276 37968 : IncidentSolar = state.dataSurface->Surface(SurfNum).Area * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
3277 37968 : if (ANY_INTERIOR_SHADE_BLIND(ShadeFlag)) {
3278 : // Interior shade or blind
3279 15792 : ConvHeatFlowNatural = -qv(nlayer) * height * width;
3280 :
3281 15792 : state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum) = ConvHeatFlowNatural;
3282 15792 : state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) = ConvHeatFlowNatural;
3283 15792 : state.dataSurface->SurfWinGapConvHtFlowRepEnergy(SurfNum) =
3284 15792 : state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
3285 : // Window heat gain from glazing and shade/blind to zone. Consists of transmitted solar, convection
3286 : // from air exiting gap, convection from zone-side of shade/blind, net IR to zone from shade and net IR to
3287 : // zone from the glass adjacent to the shade/blind (zero if shade/blind IR transmittance is zero).
3288 : // Following assumes glazed area = window area (i.e., dividers ignored) in calculating
3289 : // IR to zone from glass when interior shade/blind is present.
3290 15792 : ShadeArea = state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum);
3291 15792 : sconsh = scon(ngllayer + 1) / thick(ngllayer + 1);
3292 15792 : nglfacep = nglface + 2;
3293 15792 : CondHeatGainShade = ShadeArea * sconsh * (theta(nglfacep - 1) - theta(nglfacep));
3294 15792 : EpsShIR1 = emis(nglface + 1);
3295 15792 : EpsShIR2 = emis(nglface + 2);
3296 15792 : TauShIR = tir(nglface + 1);
3297 15792 : RhoShIR1 = max(0.0, 1.0 - TauShIR - EpsShIR1);
3298 15792 : RhoShIR2 = max(0.0, 1.0 - TauShIR - EpsShIR2);
3299 15792 : RhoGlIR2 = 1.0 - emis(2 * ngllayer);
3300 15792 : ShGlReflFacIR = 1.0 - RhoGlIR2 * RhoShIR1;
3301 15792 : NetIRHeatGainShade =
3302 15792 : ShadeArea * EpsShIR2 * (state.dataWindowComplexManager->sigma * pow_4(theta(nglfacep)) - rmir) +
3303 15792 : EpsShIR1 * (state.dataWindowComplexManager->sigma * pow_4(theta(nglfacep - 1)) - rmir) * RhoGlIR2 * TauShIR / ShGlReflFacIR;
3304 15792 : NetIRHeatGainGlass = ShadeArea * (emis(2 * ngllayer) * TauShIR / ShGlReflFacIR) *
3305 15792 : (state.dataWindowComplexManager->sigma * pow_4(theta(2 * ngllayer)) - rmir);
3306 15792 : ConvHeatGainFrZoneSideOfShade = ShadeArea * hcin * (theta(nglfacep) - tind);
3307 31584 : state.dataSurface->SurfWinHeatGain(SurfNum) = state.dataSurface->SurfWinTransSolar(SurfNum) + ConvHeatFlowNatural +
3308 15792 : ConvHeatGainFrZoneSideOfShade + NetIRHeatGainGlass + NetIRHeatGainShade;
3309 : // store components for reporting
3310 15792 : state.dataSurface->SurfWinGainConvShadeToZoneRep(SurfNum) = ConvHeatGainFrZoneSideOfShade;
3311 15792 : state.dataSurface->SurfWinGainIRGlazToZoneRep(SurfNum) = NetIRHeatGainGlass;
3312 15792 : state.dataSurface->SurfWinGainIRShadeToZoneRep(SurfNum) = NetIRHeatGainShade;
3313 : } else {
3314 : // Interior shade or blind not present; innermost layer is glass
3315 22176 : CondHeatGainGlass =
3316 22176 : state.dataSurface->Surface(SurfNum).Area * scon(nlayer) / thick(nlayer) * (theta(2 * nlayer - 1) - theta(2 * nlayer));
3317 22176 : NetIRHeatGainGlass = state.dataSurface->Surface(SurfNum).Area * emis(2 * nlayer) *
3318 22176 : (state.dataWindowComplexManager->sigma * pow_4(theta(2 * nlayer)) - rmir);
3319 22176 : ConvHeatGainFrZoneSideOfGlass = state.dataSurface->Surface(SurfNum).Area * hcin * (theta(2 * nlayer) - tind);
3320 22176 : state.dataSurface->SurfWinHeatGain(SurfNum) =
3321 22176 : state.dataSurface->SurfWinTransSolar(SurfNum) + ConvHeatGainFrZoneSideOfGlass + NetIRHeatGainGlass;
3322 : // store components for reporting
3323 22176 : state.dataSurface->SurfWinGainConvGlazToZoneRep(SurfNum) = ConvHeatGainFrZoneSideOfGlass;
3324 22176 : state.dataSurface->SurfWinGainIRGlazToZoneRep(SurfNum) = NetIRHeatGainGlass;
3325 : // need to report convective heat flow from the gap in case of exterior shade
3326 22176 : if (ShadeFlag == WinShadingType::ExtShade) {
3327 10080 : ConvHeatFlowNatural = -qv(2) * height * width; // qv(1) is exterior environment
3328 :
3329 10080 : state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) = ConvHeatFlowNatural;
3330 10080 : state.dataSurface->SurfWinGapConvHtFlowRepEnergy(SurfNum) =
3331 10080 : state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
3332 : }
3333 : }
3334 :
3335 : // Add convective heat gain from airflow window
3336 : // Note: effect of fan heat on gap outlet temperature is neglected since fan power (based
3337 : // on pressure drop through the gap) is extremely small
3338 :
3339 : // WinGapConvHtFlowRep(SurfNum) = 0.0d0
3340 : // WinGapConvHtFlowRepEnergy(SurfNum) = 0.0d0
3341 37968 : TotAirflowGap = state.dataSurface->SurfWinAirflowThisTS(SurfNum) * state.dataSurface->Surface(SurfNum).Width;
3342 37968 : TAirflowGapOutlet = Constant::Kelvin; // TODO Need to calculate this
3343 37968 : TAirflowGapOutletC = TAirflowGapOutlet - Constant::Kelvin;
3344 37968 : state.dataSurface->SurfWinTAirflowGapOutlet(SurfNum) = TAirflowGapOutletC;
3345 37968 : if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
3346 0 : ConvHeatFlowForced = sum(qv); // TODO. figure forced ventilation heat flow in Watts
3347 :
3348 0 : state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) = ConvHeatFlowForced;
3349 0 : state.dataSurface->SurfWinGapConvHtFlowRepEnergy(SurfNum) =
3350 0 : state.dataSurface->SurfWinGapConvHtFlowRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
3351 : // Add heat from gap airflow to zone air if destination is inside air; save the heat gain to return
3352 : // air in case it needs to be sent to the zone (due to no return air determined in HVAC simulation)
3353 0 : if (state.dataSurface->SurfWinAirflowDestination(SurfNum) == WindowAirFlowDestination::Indoor ||
3354 0 : state.dataSurface->SurfWinAirflowDestination(SurfNum) == WindowAirFlowDestination::Return) {
3355 0 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum);
3356 0 : if (state.dataSurface->SurfWinAirflowSource(SurfNum) == WindowAirFlowSource::Indoor) {
3357 0 : InletAirHumRat = thisZoneHB.airHumRat;
3358 : } else { // AirflowSource = outside air
3359 0 : InletAirHumRat = state.dataEnvrn->OutHumRat;
3360 : }
3361 0 : ZoneTemp = thisZoneHB.MAT; // this should be Tin (account for different reference temps)
3362 0 : CpAirOutlet = PsyCpAirFnW(InletAirHumRat);
3363 0 : CpAirZone = PsyCpAirFnW(thisZoneHB.airHumRat);
3364 0 : ConvHeatGainToZoneAir = TotAirflowGap * (CpAirOutlet * (TAirflowGapOutletC)-CpAirZone * ZoneTemp);
3365 0 : if (state.dataSurface->SurfWinAirflowDestination(SurfNum) == WindowAirFlowDestination::Indoor) {
3366 0 : state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum) = ConvHeatGainToZoneAir;
3367 0 : state.dataSurface->SurfWinHeatGain(SurfNum) += ConvHeatGainToZoneAir;
3368 : } else {
3369 0 : state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum) = ConvHeatGainToZoneAir;
3370 : }
3371 : }
3372 : // For AirflowDestination = ReturnAir in a controlled (i.e., conditioned) zone with return air, see CalcZoneLeavingConditions
3373 : // for calculation of modification of return-air temperature due to airflow from window gaps into return air.
3374 : }
3375 :
3376 : // Correct WinHeatGain for interior diffuse shortwave (solar and shortwave from lights) transmitted
3377 : // back out window
3378 37968 : ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
3379 : // ConstrNumSh = Surface(SurfNum)%ShadedConstruction
3380 : // IF(SurfaceWindow(SurfNum)%StormWinFlag==1) THEN
3381 : // ConstrNum = Surface(SurfNum)%StormWinConstruction
3382 : // ConstrNumSh = Surface(SurfNum)%StormWinShadedConstruction
3383 : // END IF
3384 : // IF(ShadeFlag <= 0) THEN
3385 : // TransDiff = Construct(ConstrNum).TransDiff;
3386 37968 : int IState = state.dataSurface->SurfaceWindow(SurfNum).ComplexFen.NumStates;
3387 37968 : Real64 ReflDiff = state.dataSurface->SurfaceWindow(SurfNum).ComplexFen.State(IState).WinBkHemRefl;
3388 : // ELSE IF(ShadeFlag==WinShadingType::IntShade .OR. ShadeFlag==WinShadingType::ExtShade) THEN
3389 : // TransDiff = Construct(ConstrNum)%TransDiff
3390 : // ELSE IF(ShadeFlag==WinShadingType::IntBlind .OR. ShadeFlag==WinShadingType::ExtBlind .OR.ShadeFlag==WinShadingType::BGBlind) THEN
3391 : // TransDiff = InterpSlatAng(SurfaceWindow(SurfNum)%SlatAngThisTS,SurfaceWindow(SurfNum)%MovableSlats, &
3392 : // Construct(ConstrNumSh)%BlTransDiff)
3393 : // ELSE IF(ShadeFlag == SwitchableGlazing) THEN
3394 : // TransDiff = InterpSW(SurfaceWindow(SurfNum)%SwitchingFactor,Construct(ConstrNum)%TransDiff, &
3395 : // Construct(ConstrNumSh)%TransDiff)
3396 : // END IF
3397 37968 : state.dataSurface->SurfWinLossSWZoneToOutWinRep(SurfNum) =
3398 37968 : state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(SurfNum).SolarEnclIndex) * state.dataSurface->Surface(SurfNum).Area *
3399 75936 : (1 - ReflDiff) +
3400 37968 : state.dataHeatBalSurf->SurfWinInitialBeamSolInTrans(SurfNum);
3401 37968 : state.dataSurface->SurfWinHeatGain(SurfNum) -=
3402 37968 : (state.dataSurface->SurfWinLossSWZoneToOutWinRep(SurfNum) +
3403 37968 : state.dataHeatBalSurf->SurfWinInitialDifSolInTrans(SurfNum) * state.dataSurface->Surface(SurfNum).Area);
3404 :
3405 37968 : if (ShadeFlag == WinShadingType::IntShade || ShadeFlag == WinShadingType::ExtShade) {
3406 25872 : state.dataSurface->SurfWinShadingAbsorbedSolar(SurfNum) =
3407 25872 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
3408 25872 : (state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum));
3409 25872 : state.dataSurface->SurfWinShadingAbsorbedSolarEnergy(SurfNum) =
3410 25872 : state.dataSurface->SurfWinShadingAbsorbedSolar(SurfNum) * state.dataGlobal->TimeStepZoneSec;
3411 : }
3412 37968 : if (state.dataEnvrn->SunIsUp) {
3413 19152 : state.dataSurface->SurfWinSysSolTransmittance(SurfNum) =
3414 19152 : state.dataSurface->SurfWinTransSolar(SurfNum) /
3415 19152 : (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) *
3416 19152 : (state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum)) +
3417 : 0.0001);
3418 19152 : state.dataSurface->SurfWinSysSolAbsorptance(SurfNum) =
3419 19152 : (state.dataHeatBal->SurfWinQRadSWwinAbsTot(SurfNum) + state.dataSurface->SurfWinShadingAbsorbedSolar(SurfNum)) /
3420 19152 : (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) *
3421 19152 : (state.dataSurface->Surface(SurfNum).Area + state.dataSurface->SurfWinDividerArea(SurfNum)) +
3422 : 0.0001);
3423 19152 : state.dataSurface->SurfWinSysSolReflectance(SurfNum) =
3424 19152 : 1.0 - state.dataSurface->SurfWinSysSolTransmittance(SurfNum) - state.dataSurface->SurfWinSysSolAbsorptance(SurfNum);
3425 : } else {
3426 18816 : state.dataSurface->SurfWinSysSolTransmittance(SurfNum) = 0.0;
3427 18816 : state.dataSurface->SurfWinSysSolAbsorptance(SurfNum) = 0.0;
3428 18816 : state.dataSurface->SurfWinSysSolReflectance(SurfNum) = 0.0;
3429 : }
3430 :
3431 : // Save hcv for use in divider calc with interior or exterior shade (see CalcWinFrameAndDividerTemps)
3432 37968 : if (ShadeFlag == WinShadingType::IntShade) {
3433 15792 : state.dataSurface->SurfWinConvCoeffWithShade(SurfNum) = 0.0;
3434 : }
3435 :
3436 37968 : if (ShadeFlag == WinShadingType::IntShade) {
3437 15792 : auto const &surfShade = state.dataSurface->surfShades(SurfNum);
3438 15792 : SurfInsideTemp = theta(2 * ngllayer + 2) - Constant::Kelvin;
3439 :
3440 : // // Get properties of inside shading layer
3441 :
3442 15792 : Real64 EffShEmiss = surfShade.effShadeEmi;
3443 15792 : Real64 EffGlEmiss = surfShade.effGlassEmi;
3444 15792 : state.dataSurface->SurfWinEffInsSurfTemp(SurfNum) =
3445 15792 : (EffShEmiss * SurfInsideTemp + EffGlEmiss * (theta(2 * ngllayer) - Constant::Kelvin)) / (EffShEmiss + EffGlEmiss);
3446 :
3447 : } else {
3448 22176 : SurfOutsideTemp = theta(1) - Constant::Kelvin;
3449 : }
3450 :
3451 144480 : for (k = 1; k <= nlayer; ++k) {
3452 106512 : state.dataSurface->SurfaceWindow(SurfNum).thetaFace[2 * k - 1] = theta(2 * k - 1);
3453 106512 : state.dataSurface->SurfaceWindow(SurfNum).thetaFace[2 * k] = theta(2 * k);
3454 :
3455 : // temperatures for reporting
3456 106512 : state.dataHeatBal->SurfWinFenLaySurfTempFront(SurfNum, k) = theta(2 * k - 1) - Constant::Kelvin;
3457 106512 : state.dataHeatBal->SurfWinFenLaySurfTempBack(SurfNum, k) = theta(2 * k) - Constant::Kelvin;
3458 : // thetas(k) = theta(k)
3459 : }
3460 : }
3461 37990 : }
3462 :
3463 : // This function check if gas with molecular weight has already been feed into coefficients and
3464 : // feed arrays
3465 :
3466 80690 : void CheckGasCoefs(Real64 const currentWeight, int &indexNumber, Array1D<Real64> &wght, bool &feedData)
3467 : {
3468 :
3469 : // Using/Aliasing
3470 : using TARCOGGassesParams::maxgas;
3471 :
3472 : // Argument array dimensioning
3473 80690 : EP_SIZE_CHECK(wght, maxgas);
3474 :
3475 80690 : feedData = false;
3476 80690 : bool coeffFound = false;
3477 80690 : int counter = 1;
3478 173486 : while ((counter <= maxgas) && (wght(counter) != 0) && (!coeffFound)) {
3479 92796 : if (std::abs(currentWeight - wght(counter)) < 1.0e-5) {
3480 80688 : coeffFound = true;
3481 : } else {
3482 12108 : ++counter;
3483 : }
3484 : } // DO WHILE((counter.LE.maxgas).AND.(wght(couner).NE.0).AND.(.NOT.coeffFound))
3485 :
3486 : // In case coefficient is not found data needs to be stored in gas coefficients arrays
3487 80690 : if ((!coeffFound) && (counter < maxgas)) {
3488 2 : feedData = true;
3489 : }
3490 :
3491 80690 : indexNumber = counter;
3492 80690 : }
3493 :
3494 3350 : int SearchAscTable(Real64 const y, // Value to be found in the table
3495 : int const n, // Number of values in the table
3496 : Array1S<Real64> const ytab // Table of values, monotonic, ascending order
3497 : )
3498 : {
3499 :
3500 : // FUNCTION INFORMATION:
3501 : // AUTHOR Joe Klems
3502 : // DATE WRITTEN Feb 2011
3503 : // MODIFIED na
3504 : // RE-ENGINEERED na
3505 :
3506 : // PURPOSE OF THIS FUNCTION:
3507 : // Given an ascending monotonic table with n entries, find an index i
3508 : // such that ytab(i-1) < y <= ytab(i)
3509 :
3510 : // METHODOLOGY EMPLOYED:
3511 : // binary search
3512 :
3513 : // Return value
3514 : int SearchAscTable;
3515 :
3516 : int Ih; // Intex for upper end of interval
3517 : int Il; // Index for lower end of interval
3518 : int Im; // Index for midpoint of interval
3519 : Real64 Yh; // Table value for upper end of interval
3520 : Real64 Yl; // Table value for lower end of interval
3521 : Real64 Ym; // Table value for midpoint of interval
3522 :
3523 3350 : Yh = ytab(n);
3524 3350 : Yl = ytab(1);
3525 3350 : Ih = n;
3526 3350 : Il = 1;
3527 3350 : if (y < Yl) {
3528 0 : SearchAscTable = 1;
3529 0 : return SearchAscTable;
3530 3350 : } else if (y > Yh) {
3531 0 : SearchAscTable = n;
3532 0 : return SearchAscTable;
3533 : }
3534 : while (true) {
3535 14487 : if (Ih - Il <= 1) {
3536 3350 : break;
3537 : }
3538 11137 : Im = (Ih + Il) / 2;
3539 11137 : Ym = ytab(Im);
3540 11137 : if (y <= Ym) {
3541 3709 : Yh = Ym;
3542 3709 : Ih = Im;
3543 : } else {
3544 7428 : Yl = Ym;
3545 7428 : Il = Im;
3546 : }
3547 : }
3548 :
3549 3350 : SearchAscTable = Ih;
3550 :
3551 3350 : return SearchAscTable;
3552 : }
3553 :
3554 : } // namespace WindowComplexManager
3555 :
3556 : } // namespace EnergyPlus
|