Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cassert>
50 : #include <cmath>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/ArrayS.functions.hh>
55 : #include <ObjexxFCL/Fmath.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/Construction.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataHeatBalSurface.hh>
61 : #include <EnergyPlus/DataHeatBalance.hh>
62 : #include <EnergyPlus/DataIPShortCuts.hh>
63 : #include <EnergyPlus/DataSurfaces.hh>
64 : #include <EnergyPlus/DataSystemVariables.hh>
65 : #include <EnergyPlus/DataViewFactorInformation.hh>
66 : #include <EnergyPlus/DisplayRoutines.hh>
67 : #include <EnergyPlus/General.hh>
68 : #include <EnergyPlus/HeatBalanceIntRadExchange.hh>
69 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
70 : #include <EnergyPlus/Material.hh>
71 : #include <EnergyPlus/UtilityRoutines.hh>
72 : #include <EnergyPlus/WindowEquivalentLayer.hh>
73 :
74 : namespace EnergyPlus {
75 :
76 : namespace HeatBalanceIntRadExchange {
77 : // Module containing the routines dealing with the interior radiant exchange
78 : // between surfaces.
79 :
80 : // MODULE INFORMATION:
81 : // AUTHOR Rick Strand
82 : // DATE WRITTEN September 2000
83 : // MODIFIED Aug 2001, FW: recalculate ScriptF for a zone if window interior
84 : // shade/blind status is different from previous time step. This is
85 : // because ScriptF, which is used to calculate interior LW
86 : // exchange between surfaces, depends on inside surface emissivities,
87 : // which, for a window, depends on whether or not an interior
88 : // shade or blind is in place.
89 :
90 : // PURPOSE OF THIS MODULE:
91 : // Part of the heat balance modularization/re-engineering. Purpose of this
92 : // module is to replace the MRT with RBAL method of modeling radiant exchange
93 : // between interior surfaces.
94 :
95 : // METHODOLOGY EMPLOYED:
96 : // Standard EnergyPlus methodology
97 :
98 : // REFERENCES:
99 : // ASHRAE Loads Toolkit "Script F" routines by Curt Pedersen
100 : // Hottel, H.C., and A.F. Sarofim. "Radiative Transfer" (mainly chapter 3),
101 : // McGraw-Hill, Inc., New York, 1967.
102 :
103 20521676 : void CalcInteriorRadExchange(EnergyPlusData &state,
104 : Array1S<Real64> const SurfaceTemp, // Current surface temperatures
105 : int const SurfIterations, // Number of iterations in calling subroutine
106 : Array1D<Real64> &NetLWRadToSurf, // Net long wavelength radiant exchange from other surfaces
107 : ObjexxFCL::Optional_int_const ZoneToResimulate, // if passed in, then only calculate for this zone
108 : #ifdef EP_Count_Calls
109 : std::string_view const CalledFrom)
110 : #else
111 : [[maybe_unused]] std::string_view const CalledFrom)
112 : #endif
113 : {
114 :
115 : // SUBROUTINE INFORMATION:
116 : // AUTHOR Rick Strand
117 : // DATE WRITTEN September 2000
118 : // MODIFIED 6/18/01, FCW: calculate IR on windows
119 : // Jan 2002, FCW: add blinds with movable slats
120 : // Sep 2011 LKL/BG - resimulate only zones needing it for Radiant systems
121 :
122 : // PURPOSE OF THIS SUBROUTINE:
123 : // Determines the interior radiant exchange between surfaces using
124 : // Hottel's ScriptF method for the grey interchange between surfaces
125 : // in an enclosure.
126 :
127 : // REFERENCES:
128 : // Hottel, H. C. and A. F. Sarofim, Radiative Transfer, Ch 3, McGraw Hill, 1967.
129 :
130 : // Types
131 : typedef Array1D<Real64>::size_type size_type;
132 :
133 : bool IntShadeOrBlindStatusChanged; // True if status of interior shade or blind on at least
134 : // one window in a zone has changed from previous time step
135 :
136 20521676 : auto &SurfaceTempRad = state.dataHeatBalIntRadExchg->SurfaceTempRad;
137 20521676 : auto &SurfaceTempInKto4th = state.dataHeatBalIntRadExchg->SurfaceTempInKto4th;
138 20521676 : auto &SurfaceEmiss = state.dataHeatBalIntRadExchg->SurfaceEmiss;
139 :
140 20521676 : if (state.dataHeatBalIntRadExchg->CalcInteriorRadExchangefirstTime) {
141 796 : SurfaceTempRad.allocate(state.dataHeatBalIntRadExchg->MaxNumOfRadEnclosureSurfs);
142 796 : SurfaceTempInKto4th.allocate(state.dataHeatBalIntRadExchg->MaxNumOfRadEnclosureSurfs);
143 796 : SurfaceEmiss.allocate(state.dataHeatBalIntRadExchg->MaxNumOfRadEnclosureSurfs);
144 796 : state.dataHeatBalIntRadExchg->CalcInteriorRadExchangefirstTime = false;
145 796 : if (state.dataSysVars->DeveloperFlag) {
146 0 : DisplayString(state, " OMP turned off, HBIRE loop executed in serial");
147 : }
148 : }
149 :
150 20521676 : if (state.dataGlobal->KickOffSimulation || state.dataGlobal->KickOffSizing) return;
151 :
152 20346965 : bool const PartialResimulate(present(ZoneToResimulate));
153 :
154 : #ifdef EP_Count_Calls
155 : if (!PartialResimulate) {
156 : ++state.dataTimingsData->NumIntRadExchange_Calls;
157 : } else {
158 : ++state.dataTimingsData->NumIntRadExchangeZ_Calls;
159 : }
160 : if (CalledFrom.empty()) {
161 : // do nothing
162 : } else if (CalledFrom == "Main") {
163 : ++state.dataTimingsData->NumIntRadExchangeMain_Calls;
164 : } else if (CalledFrom == "Outside") {
165 : ++state.dataTimingsData->NumIntRadExchangeOSurf_Calls;
166 : } else if (CalledFrom == "Inside") {
167 : ++state.dataTimingsData->NumIntRadExchangeISurf_Calls;
168 : }
169 : #endif
170 :
171 20346965 : int startEnclosure = 1;
172 20346965 : int endEnclosure = state.dataViewFactor->NumOfRadiantEnclosures;
173 20346965 : if (PartialResimulate) {
174 : // ToDo: For now, use min and max enclosure numbers associated with this zone, this could include unrelated enclosures
175 3178218 : startEnclosure = state.dataHeatBal->Zone(ZoneToResimulate).zoneRadEnclosureFirst;
176 3178218 : endEnclosure = state.dataHeatBal->Zone(ZoneToResimulate).zoneRadEnclosureLast;
177 6356436 : for (int enclosureNum = startEnclosure; enclosureNum <= endEnclosure; ++enclosureNum) {
178 3178218 : auto const &enclosure = state.dataViewFactor->EnclRadInfo(enclosureNum);
179 26479332 : for (int i : enclosure.SurfacePtr) {
180 23301114 : NetLWRadToSurf(i) = 0.0;
181 23301114 : state.dataSurface->SurfWinIRfromParentZone(i) = 0.0;
182 : }
183 : }
184 : } else {
185 17168747 : NetLWRadToSurf = 0.0;
186 1203572772 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++)
187 1186404025 : state.dataSurface->SurfWinIRfromParentZone(SurfNum) = 0.0;
188 : }
189 :
190 156828756 : for (int enclosureNum = startEnclosure; enclosureNum <= endEnclosure; ++enclosureNum) {
191 :
192 136481791 : auto &zone_info = state.dataViewFactor->EnclRadInfo(enclosureNum);
193 136481791 : auto &zone_ScriptF = zone_info.ScriptF; // Tuned Transposed
194 136481791 : int const n_zone_Surfaces = zone_info.NumOfSurfaces;
195 136481791 : size_type const s_zone_Surfaces = n_zone_Surfaces;
196 :
197 : // Calculate ScriptF if first time step in environment and surface heat-balance iterations not yet started;
198 : // recalculate ScriptF if status of window interior shades or blinds has changed from
199 : // previous time step. This recalculation is required since ScriptF depends on the inside
200 : // emissivity of the inside surfaces, which, for windows, is (1) the emissivity of the
201 : // inside face of the inside glass layer if there is no interior shade/blind, or (2) the effective
202 : // emissivity of the shade/blind if the shade/blind is in place. (The "effective emissivity"
203 : // in this case is (1) the shade/blind emissivity if the shade/blind IR transmittance is zero,
204 : // or (2) a weighted average of the shade/blind emissivity and inside glass emissivity if the
205 : // shade/blind IR transmittance is not zero (which is sometimes the case for a "shade" and
206 : // usually the case for a blind). It assumed for switchable glazing that the inside surface
207 : // emissivity does not change if the glazing is switched on or off.
208 :
209 : // Determine if status of interior shade/blind on one or more windows in the zone has changed
210 : // from previous time step. Also make a check for any changes in interior movable insulation.
211 :
212 136481791 : if (SurfIterations == 0) {
213 :
214 : bool IntMovInsulChanged; // True if the status of interior movable insulation has changed
215 :
216 61925872 : IntShadeOrBlindStatusChanged = false;
217 61925872 : IntMovInsulChanged = false;
218 :
219 61925872 : if (!state.dataGlobal->BeginEnvrnFlag) { // Check for change in shade/blind status
220 592812908 : for (int const SurfNum : zone_info.SurfacePtr) {
221 530952668 : if (IntShadeOrBlindStatusChanged || IntMovInsulChanged)
222 : break; // Need only check if one window's status or one movable insulation status has changed
223 530951519 : if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).TypeIsWindow) {
224 70815602 : DataSurfaces::WinShadingType ShadeFlag = state.dataSurface->SurfWinShadingFlag(SurfNum);
225 70815602 : DataSurfaces::WinShadingType ShadeFlagPrev = state.dataSurface->SurfWinExtIntShadePrevTS(SurfNum);
226 70815602 : if (ShadeFlagPrev != ShadeFlag && (ANY_INTERIOR_SHADE_BLIND(ShadeFlagPrev) || ANY_INTERIOR_SHADE_BLIND(ShadeFlag)))
227 25584 : IntShadeOrBlindStatusChanged = true;
228 70839758 : if (state.dataSurface->SurfWinWindowModelType(SurfNum) == DataSurfaces::WindowModel::EQL &&
229 : state.dataWindowEquivLayer
230 24156 : ->CFS(state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).EQLConsPtr)
231 24156 : .ISControlled) {
232 0 : IntShadeOrBlindStatusChanged = true;
233 : }
234 : } else {
235 460135917 : if (state.dataSurface->AnyMovableInsulation) UpdateMovableInsulationFlag(state, IntMovInsulChanged, SurfNum);
236 : }
237 : }
238 : }
239 :
240 123826160 : if (IntShadeOrBlindStatusChanged || IntMovInsulChanged ||
241 61900288 : state.dataGlobal->BeginEnvrnFlag) { // Calc inside surface emissivities for this time step
242 833637 : for (int ZoneSurfNum = 1; ZoneSurfNum <= n_zone_Surfaces; ++ZoneSurfNum) {
243 743570 : int const SurfNum = zone_info.SurfacePtr(ZoneSurfNum);
244 743570 : int const ConstrNum = state.dataSurface->Surface(SurfNum).Construction;
245 743570 : zone_info.Emissivity(ZoneSurfNum) = state.dataHeatBalSurf->SurfAbsThermalInt(SurfNum);
246 849857 : if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow &&
247 106287 : ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
248 14373 : zone_info.Emissivity(ZoneSurfNum) = state.dataHeatBalSurf->SurfAbsThermalInt(SurfNum);
249 : }
250 743606 : if (state.dataSurface->SurfWinWindowModelType(SurfNum) == DataSurfaces::WindowModel::EQL &&
251 36 : state.dataWindowEquivLayer->CFS(state.dataConstruction->Construct(ConstrNum).EQLConsPtr).ISControlled) {
252 0 : zone_info.Emissivity(ZoneSurfNum) = WindowEquivalentLayer::EQLWindowInsideEffectiveEmiss(state, ConstrNum);
253 : }
254 : }
255 :
256 90067 : if (state.dataHeatBalIntRadExchg->CarrollMethod) {
257 456 : CalcFp(n_zone_Surfaces, zone_info.Emissivity, zone_info.FMRT, zone_info.Fp);
258 : } else {
259 89611 : CalcScriptF(state, n_zone_Surfaces, zone_info.Area, zone_info.F, zone_info.Emissivity, zone_ScriptF);
260 : // precalc - multiply by StefanBoltzmannConstant
261 89611 : zone_ScriptF *= Constant::StefanBoltzmann;
262 : }
263 : }
264 :
265 : } // End of check if SurfIterations = 0
266 :
267 : // Set surface emissivities and temperatures
268 : // Also, for Carroll method, calculate numerators and denominators of radiant temperature
269 136481791 : Real64 CarrollMRTNumerator(0.0);
270 136481791 : Real64 CarrollMRTDenominator(0.0);
271 : Real64 CarrollMRTInKTo4th; // Carroll MRT
272 1312610530 : for (size_type ZoneSurfNum = 0; ZoneSurfNum < s_zone_Surfaces; ++ZoneSurfNum) {
273 1176128739 : int const SurfNum = zone_info.SurfacePtr[ZoneSurfNum];
274 1176128739 : auto const &surf = state.dataSurface->Surface(SurfNum);
275 1176128739 : auto const &surfWindow = state.dataSurface->SurfaceWindow(SurfNum);
276 1176128739 : int const constrNum = surf.Construction;
277 1176128739 : auto const &construct = state.dataConstruction->Construct(constrNum);
278 1176128739 : if (construct.WindowTypeEQL) {
279 37770 : SurfaceTempRad[ZoneSurfNum] = state.dataSurface->SurfWinEffInsSurfTemp(SurfNum);
280 37770 : SurfaceEmiss[ZoneSurfNum] = WindowEquivalentLayer::EQLWindowInsideEffectiveEmiss(state, constrNum);
281 1176090969 : } else if (construct.WindowTypeBSDF && state.dataSurface->SurfWinShadingFlag(SurfNum) == DataSurfaces::WinShadingType::IntShade) {
282 100716 : SurfaceTempRad[ZoneSurfNum] = state.dataSurface->SurfWinEffInsSurfTemp(SurfNum);
283 100716 : SurfaceEmiss[ZoneSurfNum] = surfWindow.EffShBlindEmiss[1] + surfWindow.EffGlassEmiss[1];
284 1175990253 : } else if (construct.WindowTypeBSDF) {
285 146658 : SurfaceTempRad[ZoneSurfNum] = state.dataSurface->SurfWinEffInsSurfTemp(SurfNum);
286 146658 : SurfaceEmiss[ZoneSurfNum] = construct.InsideAbsorpThermal;
287 1175843595 : } else if (construct.TypeIsWindow && surf.OriginalClass != DataSurfaces::SurfaceClass::TDD_Diffuser) {
288 154818215 : if (SurfIterations == 0 && NOT_SHADED(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
289 : // If the window is bare this TS and it is the first time through we use the previous TS glass
290 : // temperature whether or not the window was shaded in the previous TS. If the window was shaded
291 : // the previous time step this temperature is a better starting value than the shade temperature.
292 69959336 : SurfaceTempRad[ZoneSurfNum] = surfWindow.thetaFace[2 * construct.TotGlassLayers] - Constant::Kelvin;
293 69959336 : SurfaceEmiss[ZoneSurfNum] = construct.InsideAbsorpThermal;
294 : // For windows with an interior shade or blind an effective inside surface temp
295 : // and emiss is used here that is a weighted combination of shade/blind and glass temp and emiss.
296 84858879 : } else if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
297 789117 : SurfaceTempRad[ZoneSurfNum] = state.dataSurface->SurfWinEffInsSurfTemp(SurfNum);
298 789117 : SurfaceEmiss[ZoneSurfNum] = state.dataHeatBalSurf->SurfAbsThermalInt(SurfNum);
299 : } else {
300 84069762 : SurfaceTempRad[ZoneSurfNum] = SurfaceTemp(SurfNum);
301 84069762 : SurfaceEmiss[ZoneSurfNum] = construct.InsideAbsorpThermal;
302 : }
303 : } else {
304 1021025380 : SurfaceTempRad[ZoneSurfNum] = SurfaceTemp(SurfNum);
305 1021025380 : SurfaceEmiss[ZoneSurfNum] = construct.InsideAbsorpThermal;
306 : }
307 1176128739 : if (state.dataHeatBalIntRadExchg->CarrollMethod) {
308 9384094 : CarrollMRTNumerator += SurfaceTempRad[ZoneSurfNum] * zone_info.Fp[ZoneSurfNum] * zone_info.Area[ZoneSurfNum];
309 9384094 : CarrollMRTDenominator += zone_info.Fp[ZoneSurfNum] * zone_info.Area[ZoneSurfNum];
310 : }
311 1176128739 : SurfaceTempInKto4th[ZoneSurfNum] = pow_4(SurfaceTempRad[ZoneSurfNum] + Constant::Kelvin);
312 : }
313 :
314 136481791 : if (state.dataHeatBalIntRadExchg->CarrollMethod) {
315 1128467 : if (CarrollMRTDenominator > 0.0) {
316 1128467 : CarrollMRTInKTo4th = pow_4(CarrollMRTNumerator / CarrollMRTDenominator + Constant::Kelvin);
317 : } else {
318 : // Likely only one surface in this enclosure
319 0 : CarrollMRTInKTo4th = 293.15; // arbitrary value, IR will be zero
320 : }
321 : // These are the money loops
322 10512561 : for (size_type RecZoneSurfNum = 0; RecZoneSurfNum < s_zone_Surfaces; ++RecZoneSurfNum) {
323 9384094 : int const RecSurfNum = zone_info.SurfacePtr[RecZoneSurfNum];
324 9384094 : int const ConstrNumRec = state.dataSurface->Surface(RecSurfNum).Construction;
325 9384094 : auto const &rec_construct = state.dataConstruction->Construct(ConstrNumRec);
326 9384094 : auto &netLWRadToRecSurf = NetLWRadToSurf(RecSurfNum);
327 9384094 : if (rec_construct.TypeIsWindow) {
328 : // auto& rec_surface_window(SurfaceWindow(RecSurfNum));
329 712716 : Real64 CarrollMRTInKTo4thWin = CarrollMRTInKTo4th; // arbitrary value, IR will be zero
330 712716 : Real64 CarrollMRTNumeratorWin(0.0);
331 712716 : Real64 CarrollMRTDenominatorWin(0.0);
332 6414444 : for (size_type SendZoneSurfNum = 0; SendZoneSurfNum < s_zone_Surfaces; ++SendZoneSurfNum) {
333 5701728 : if (SendZoneSurfNum != RecZoneSurfNum) {
334 4989012 : CarrollMRTNumeratorWin +=
335 4989012 : SurfaceTempRad[SendZoneSurfNum] * zone_info.Fp[SendZoneSurfNum] * zone_info.Area[SendZoneSurfNum];
336 4989012 : CarrollMRTDenominatorWin += zone_info.Fp[SendZoneSurfNum] * zone_info.Area[SendZoneSurfNum];
337 : }
338 : }
339 712716 : if (CarrollMRTDenominatorWin > 0.0) {
340 712716 : CarrollMRTInKTo4thWin = pow_4(CarrollMRTNumeratorWin / CarrollMRTDenominatorWin + Constant::Kelvin);
341 : }
342 712716 : state.dataSurface->SurfWinIRfromParentZone(RecSurfNum) +=
343 712716 : (zone_info.Fp[RecZoneSurfNum] * CarrollMRTInKTo4thWin) / SurfaceEmiss[RecZoneSurfNum];
344 : }
345 9384094 : netLWRadToRecSurf += zone_info.Fp[RecZoneSurfNum] * (CarrollMRTInKTo4th - SurfaceTempInKto4th[RecZoneSurfNum]);
346 : }
347 : } else {
348 1302097969 : for (size_type RecZoneSurfNum = 0; RecZoneSurfNum < s_zone_Surfaces; ++RecZoneSurfNum) {
349 1166744645 : int const RecSurfNum = zone_info.SurfacePtr[RecZoneSurfNum];
350 1166744645 : int const ConstrNumRec = state.dataSurface->Surface(RecSurfNum).Construction;
351 1166744645 : auto const &rec_construct = state.dataConstruction->Construct(ConstrNumRec);
352 1166744645 : auto &netLWRadToRecSurf = NetLWRadToSurf(RecSurfNum);
353 :
354 : // Calculate net long-wave radiation for opaque surfaces and incident
355 : // long-wave radiation for windows.
356 1166744645 : if (rec_construct.TypeIsWindow) { // Window
357 : // auto& rec_surface_window(SurfaceWindow(RecSurfNum));
358 154419399 : Real64 scriptF_acc(0.0); // Local accumulator
359 154419399 : Real64 netLWRadToRecSurf_cor(0.0); // Correction
360 154419399 : Real64 IRfromParentZone_acc(0.0); // Local accumulator
361 2180534464 : for (size_type SendZoneSurfNum = 0; SendZoneSurfNum < s_zone_Surfaces; ++SendZoneSurfNum) {
362 2026115065 : size_type lSR = RecZoneSurfNum * s_zone_Surfaces + SendZoneSurfNum;
363 2026115065 : Real64 const scriptF(zone_ScriptF[lSR]); // [ lSR ] == ( SendZoneSurfNum+1, RecZoneSurfNum+1 )
364 2026115065 : Real64 const scriptF_temp_ink_4th(scriptF * SurfaceTempInKto4th[SendZoneSurfNum]);
365 : // Calculate interior LW incident on window rather than net LW for use in window layer heat balance calculation.
366 2026115065 : IRfromParentZone_acc += scriptF_temp_ink_4th;
367 :
368 2026115065 : if (RecZoneSurfNum != SendZoneSurfNum) {
369 1871695666 : scriptF_acc += scriptF;
370 : } else {
371 154419399 : netLWRadToRecSurf_cor = scriptF_temp_ink_4th;
372 : }
373 :
374 : // Per BG -- this should never happened. (CR6346,CR6550 caused this to be put in. Now removed. LKL 1/2013)
375 : // IF (SurfaceWindow(RecSurfNum)%IRfromParentZone < 0.0) THEN
376 : // CALL ShowRecurringWarningErrorAtEnd(state, 'CalcInteriorRadExchange: Window_IRFromParentZone negative,
377 : // Window="'// &
378 : // TRIM(Surface(RecSurfNum)%Name)//'"', &
379 : // SurfaceWindow(RecSurfNum)%IRErrCount)
380 : // CALL ShowRecurringContinueErrorAtEnd(state, '..occurs in Zone="'//TRIM(Surface(RecSurfNum)%ZoneName)// &
381 : // '", reset to 0.0 for remaining calculations.',SurfaceWindow(RecSurfNum)%IRErrCountC)
382 : // SurfaceWindow(RecSurfNum)%IRfromParentZone=0.0
383 : // ENDIF
384 : }
385 154419399 : netLWRadToRecSurf += IRfromParentZone_acc - netLWRadToRecSurf_cor - (scriptF_acc * SurfaceTempInKto4th[RecZoneSurfNum]);
386 154419399 : state.dataSurface->SurfWinIRfromParentZone(RecSurfNum) += IRfromParentZone_acc / SurfaceEmiss[RecZoneSurfNum];
387 : } else {
388 1012325246 : Real64 netLWRadToRecSurf_acc(0.0); // Local accumulator
389 1012325246 : zone_ScriptF[RecZoneSurfNum * s_zone_Surfaces + RecZoneSurfNum] = 0;
390 10968748908 : for (size_type SendZoneSurfNum = 0; SendZoneSurfNum < s_zone_Surfaces; ++SendZoneSurfNum) {
391 9956423662 : size_type lSR = RecZoneSurfNum * s_zone_Surfaces + SendZoneSurfNum;
392 9956423662 : netLWRadToRecSurf_acc +=
393 19912847324 : zone_ScriptF[lSR] * (SurfaceTempInKto4th[SendZoneSurfNum] -
394 9956423662 : SurfaceTempInKto4th[RecZoneSurfNum]); // [ lSR ] == ( SendZoneSurfNum+1, RecZoneSurfNum+1 )
395 : }
396 1012325246 : netLWRadToRecSurf += netLWRadToRecSurf_acc;
397 : }
398 : }
399 : }
400 : }
401 :
402 : // Automatic Surface Multipliers: Update values of surfaces not simulated
403 20346965 : if (state.dataSurface->UseRepresentativeSurfaceCalculations) {
404 2007960 : for (int SurfNum : state.dataSurface->AllHTSurfaceList) {
405 2004498 : int RepSurfNum = state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum;
406 2004498 : if (SurfNum != RepSurfNum) {
407 453522 : state.dataSurface->SurfWinIRfromParentZone(SurfNum) = state.dataSurface->SurfWinIRfromParentZone(RepSurfNum);
408 453522 : NetLWRadToSurf(SurfNum) = NetLWRadToSurf(RepSurfNum);
409 : }
410 3462 : }
411 : }
412 : }
413 :
414 181260 : void UpdateMovableInsulationFlag(EnergyPlusData &state, bool &MovableInsulationChange, int const SurfNum)
415 : {
416 :
417 : // SUBROUTINE INFORMATION:
418 : // AUTHOR Rick Strand
419 : // DATE WRITTEN July 2016
420 :
421 : // PURPOSE OF THIS SUBROUTINE:
422 : // To determine if any changes in interior movable insulation have happened.
423 : // If there have been changes due to a schedule change AND a change in properties,
424 : // then the matrices which are used to calculate interior radiation must be recalculated.
425 :
426 181260 : MovableInsulationChange = false;
427 :
428 181260 : if (state.dataHeatBalSurf->SurfMovInsulIntPresent(SurfNum) != state.dataHeatBalSurf->SurfMovInsulIntPresentPrevTS(SurfNum)) {
429 0 : auto const &thissurf = state.dataSurface->Surface(SurfNum);
430 : Real64 AbsorpDiff =
431 0 : std::abs(state.dataConstruction->Construct(thissurf.Construction).InsideAbsorpThermal -
432 0 : dynamic_cast<Material::MaterialChild *>(state.dataMaterial->Material(state.dataSurface->SurfMaterialMovInsulInt(SurfNum)))
433 0 : ->AbsorpThermal);
434 0 : if (AbsorpDiff > 0.01) {
435 0 : MovableInsulationChange = true;
436 : }
437 : }
438 181260 : }
439 :
440 796 : void InitInteriorRadExchange(EnergyPlusData &state)
441 : {
442 :
443 : // SUBROUTINE INFORMATION:
444 : // AUTHOR Rick Strand
445 : // DATE WRITTEN September 2000
446 :
447 : // PURPOSE OF THIS SUBROUTINE:
448 : // Initializes the various parameters for Hottel's ScriptF method for
449 : // the grey interchange between surfaces in an enclosure.
450 :
451 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
452 : static constexpr std::string_view RoutineName("InitInteriorRadExchange: ");
453 : bool NoUserInputF; // Logical flag signifying no input F's for zone
454 796 : bool ErrorsFound(false);
455 : Real64 CheckValue1;
456 : Real64 CheckValue2;
457 : Real64 FinalCheckValue;
458 796 : Array2D<Real64> SaveApproximateViewFactors; // Save for View Factor reporting
459 : Real64 RowSum;
460 : Real64 FixedRowSum;
461 : int NumIterations;
462 796 : std::string Option1; // view factor report option
463 :
464 796 : auto &ViewFactorReport = state.dataHeatBalIntRadExchg->ViewFactorReport;
465 :
466 796 : General::ScanForReports(state, "ViewFactorInfo", ViewFactorReport, _, Option1);
467 :
468 796 : if (ViewFactorReport) { // Print heading
469 8 : print(state.files.eio, "{}\n", "! <Surface View Factor and Grey Interchange Information>");
470 8 : print(state.files.eio, "{}\n", "! <View Factor - Zone/Enclosure Information>,Zone/Enclosure Name,Number of Surfaces");
471 8 : print(state.files.eio,
472 : "{}\n",
473 : "! <View Factor - Surface Information>,Surface Name,Surface Class,Area {m2},Azimuth,Tilt,Thermal Emissivity,#Sides,Vertices");
474 8 : print(state.files.eio, "{}\n", "! <View Factor / Grey Interchange Type>,Surface Name(s)");
475 8 : print(state.files.eio, "{}\n", "! <View Factor>,Surface Name,Surface Class,Row Sum,View Factors for each Surface");
476 : }
477 :
478 796 : state.dataHeatBalIntRadExchg->MaxNumOfRadEnclosureSurfs = 0;
479 5851 : for (int enclosureNum = 1; enclosureNum <= state.dataViewFactor->NumOfRadiantEnclosures; ++enclosureNum) {
480 5055 : auto &thisEnclosure(state.dataViewFactor->EnclRadInfo(enclosureNum));
481 5055 : if (enclosureNum == 1) {
482 764 : if (state.dataGlobal->DisplayAdvancedReportVariables) {
483 21 : print(state.files.eio,
484 : "{}\n",
485 : "! <Surface View Factor Check Values>,Zone/Enclosure Name,Original Check Value,Calculated Fixed Check Value,Final Check "
486 : "Value,Number of Iterations,Fixed RowSum Convergence,Used RowSum Convergence");
487 : }
488 : }
489 5055 : int numEnclosureSurfaces = 0;
490 10123 : for (int spaceNum : thisEnclosure.spaceNums) {
491 : // Note that Space.Surfaces only includes HT surfs, see SurfaceGeometry::CreateMissingSpaces
492 : // But it also includes air boundary surfaces which need to be excluded here
493 49476 : for (int surfNum : state.dataHeatBal->space(spaceNum).surfaces) {
494 44408 : if (state.dataSurface->Surface(surfNum).IsAirBoundarySurf) continue;
495 : // Automatic Surface Multipliers: Only include representative surfaces
496 44390 : if (surfNum == state.dataSurface->Surface(surfNum).RepresentativeCalcSurfNum) {
497 44259 : ++numEnclosureSurfaces;
498 : }
499 5068 : }
500 5055 : }
501 5055 : thisEnclosure.NumOfSurfaces = numEnclosureSurfaces;
502 5055 : state.dataHeatBalIntRadExchg->MaxNumOfRadEnclosureSurfs =
503 5055 : max(state.dataHeatBalIntRadExchg->MaxNumOfRadEnclosureSurfs, numEnclosureSurfaces);
504 5055 : if (numEnclosureSurfaces < 1) {
505 0 : ShowSevereError(state, format("{}No surfaces in enclosure={}.", RoutineName, thisEnclosure.Name));
506 0 : ErrorsFound = true;
507 : }
508 :
509 : // Allocate the parts of the derived type
510 5055 : thisEnclosure.F.dimension(numEnclosureSurfaces, numEnclosureSurfaces, 0.0);
511 5055 : thisEnclosure.ScriptF.dimension(numEnclosureSurfaces, numEnclosureSurfaces, 0.0);
512 5055 : thisEnclosure.Area.dimension(numEnclosureSurfaces, 0.0);
513 5055 : thisEnclosure.Emissivity.dimension(numEnclosureSurfaces, 0.0);
514 5055 : thisEnclosure.Azimuth.dimension(numEnclosureSurfaces, 0.0);
515 5055 : thisEnclosure.Tilt.dimension(numEnclosureSurfaces, 0.0);
516 5055 : thisEnclosure.Fp.dimension(numEnclosureSurfaces, 1.0);
517 5055 : thisEnclosure.FMRT.dimension(numEnclosureSurfaces, 0.0);
518 5055 : thisEnclosure.SurfacePtr.dimension(numEnclosureSurfaces, 0);
519 :
520 : // Initialize the enclosure surface arrays
521 5055 : int enclosureSurfNum = 0;
522 10123 : for (int const spaceNum : thisEnclosure.spaceNums) {
523 5068 : int priorZoneTotEnclSurfs = enclosureSurfNum;
524 49476 : for (int surfNum : state.dataHeatBal->space(spaceNum).surfaces) {
525 44408 : if (state.dataSurface->Surface(surfNum).IsAirBoundarySurf) continue;
526 : // Automatic Surface Multipliers: Only include representative surfaces
527 44390 : if (surfNum == state.dataSurface->Surface(surfNum).RepresentativeCalcSurfNum) {
528 44259 : ++enclosureSurfNum;
529 44259 : thisEnclosure.SurfacePtr(enclosureSurfNum) = surfNum;
530 44259 : thisEnclosure.Area(enclosureSurfNum) = state.dataSurface->Surface(surfNum).Area;
531 88518 : thisEnclosure.Emissivity(enclosureSurfNum) =
532 44259 : state.dataConstruction->Construct(state.dataSurface->Surface(surfNum).Construction).InsideAbsorpThermal;
533 44259 : thisEnclosure.Azimuth(enclosureSurfNum) = state.dataSurface->Surface(surfNum).Azimuth;
534 44259 : thisEnclosure.Tilt(enclosureSurfNum) = state.dataSurface->Surface(surfNum).Tilt;
535 : }
536 5068 : }
537 :
538 49476 : for (int surfNum : state.dataHeatBal->space(spaceNum).surfaces) {
539 44408 : if (state.dataSurface->Surface(surfNum).IsAirBoundarySurf) continue;
540 : // Map non-representative surfaces
541 44390 : if (surfNum != state.dataSurface->Surface(surfNum).RepresentativeCalcSurfNum) {
542 : // Automatic Surface Multipliers: search for corresponding enclosure surface number
543 1642 : for (int enclSNum = priorZoneTotEnclSurfs + 1; enclSNum <= enclosureSurfNum; ++enclSNum) {
544 1511 : if (thisEnclosure.SurfacePtr(enclSNum) == state.dataSurface->Surface(surfNum).RepresentativeCalcSurfNum) {
545 : // enclosureSurfMap[allSurfNum] = enclSNum;
546 : // Increase the area for the combined enclosure surface
547 131 : thisEnclosure.Area(enclSNum) += state.dataSurface->Surface(surfNum).Area;
548 : }
549 : }
550 : }
551 : // Store SurfaceReportNums to maintain original reporting order
552 256321 : for (int enclSNum = priorZoneTotEnclSurfs + 1; enclSNum <= enclosureSurfNum; ++enclSNum) {
553 256190 : if (thisEnclosure.SurfacePtr(enclSNum) == state.dataSurface->AllSurfaceListReportOrder[surfNum - 1]) {
554 44259 : thisEnclosure.SurfaceReportNums.push_back(enclSNum);
555 44259 : break;
556 : }
557 : }
558 5068 : }
559 5055 : }
560 :
561 5055 : if (thisEnclosure.NumOfSurfaces == 1) {
562 : // If there is only one surface in a zone, then there is no radiant exchange
563 2 : thisEnclosure.F = 0.0;
564 2 : thisEnclosure.ScriptF = 0.0;
565 2 : thisEnclosure.Fp = 0.0;
566 2 : thisEnclosure.FMRT = 0.0;
567 2 : if (state.dataGlobal->DisplayAdvancedReportVariables)
568 1 : print(state.files.eio, "Surface View Factor Check Values,{},0,0,0,-1,0,0\n", thisEnclosure.Name);
569 2 : continue; // Go to the next enclosure in the loop
570 : }
571 :
572 : // Process View Factors
573 5053 : if (state.dataHeatBalIntRadExchg->CarrollMethod) {
574 :
575 : // User View Factors cannot be used with Carroll method.
576 19 : if (state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ZoneProperty:UserViewFactors:BySurfaceName")) {
577 0 : ShowWarningError(state, "ZoneProperty:UserViewFactors:BySurfaceName objects have been defined, however View");
578 0 : ShowContinueError(state, " Factors are not used when Zone Radiant Exchange Algorithm is set to CarrollMRT.");
579 : }
580 19 : CalcFMRT(state, thisEnclosure.NumOfSurfaces, thisEnclosure.Area, thisEnclosure.FMRT);
581 19 : CalcFp(thisEnclosure.NumOfSurfaces, thisEnclosure.Emissivity, thisEnclosure.FMRT, thisEnclosure.Fp);
582 : } else {
583 : // Get user supplied view factors if available in idf.
584 :
585 5034 : NoUserInputF = true;
586 :
587 5034 : constexpr std::string_view cCurrentModuleObject = "ZoneProperty:UserViewFactors:BySurfaceName";
588 5034 : int NumZonesWithUserFbyS = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
589 5034 : if (NumZonesWithUserFbyS > 0) {
590 :
591 2 : GetInputViewFactorsbyName(state,
592 2 : thisEnclosure.Name,
593 : thisEnclosure.NumOfSurfaces,
594 : thisEnclosure.F,
595 2 : thisEnclosure.SurfacePtr,
596 : NoUserInputF,
597 : ErrorsFound); // Obtains user input view factors from input file
598 : }
599 :
600 5034 : if (NoUserInputF) {
601 :
602 : // Calculate the view factors and make sure they satisfy reciprocity
603 5033 : CalcApproximateViewFactors(state,
604 : thisEnclosure.NumOfSurfaces,
605 5033 : thisEnclosure.Area,
606 5033 : thisEnclosure.Azimuth,
607 5033 : thisEnclosure.Tilt,
608 : thisEnclosure.F,
609 5033 : thisEnclosure.SurfacePtr);
610 : }
611 :
612 5034 : if (ViewFactorReport) { // Allocate and save user or approximate view factors for reporting.
613 40 : SaveApproximateViewFactors.allocate(thisEnclosure.NumOfSurfaces, thisEnclosure.NumOfSurfaces);
614 40 : SaveApproximateViewFactors = thisEnclosure.F;
615 : }
616 :
617 5034 : bool anyIntMassInZone = DoesZoneHaveInternalMass(state, thisEnclosure.NumOfSurfaces, thisEnclosure.SurfacePtr);
618 5034 : FixViewFactors(state,
619 : thisEnclosure.NumOfSurfaces,
620 5034 : thisEnclosure.Area,
621 : thisEnclosure.F,
622 5034 : thisEnclosure.Name,
623 5034 : thisEnclosure.spaceNums,
624 : CheckValue1,
625 : CheckValue2,
626 : FinalCheckValue,
627 : NumIterations,
628 : FixedRowSum,
629 : anyIntMassInZone);
630 :
631 : // Calculate the script F factors
632 5034 : CalcScriptF(state, thisEnclosure.NumOfSurfaces, thisEnclosure.Area, thisEnclosure.F, thisEnclosure.Emissivity, thisEnclosure.ScriptF);
633 5034 : if (ViewFactorReport) { // Write to SurfInfo File
634 : // Zone Surface Information Output
635 0 : print(
636 40 : state.files.eio, "Surface View Factor - Zone/Enclosure Information,{},{}\n", thisEnclosure.Name, thisEnclosure.NumOfSurfaces);
637 :
638 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
639 662 : print(state.files.eio,
640 : "Surface View Factor - Surface Information,{},{},{:.4R},{:.4R},{:.4R},{:.4R},{}",
641 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name,
642 662 : cSurfaceClass(state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Class),
643 : thisEnclosure.Area(SurfNum),
644 : thisEnclosure.Azimuth(SurfNum),
645 : thisEnclosure.Tilt(SurfNum),
646 : thisEnclosure.Emissivity(SurfNum),
647 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Sides);
648 1655 : for (int Vindex = 1; Vindex <= state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Sides; ++Vindex) {
649 1324 : auto const &Vertex = state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Vertex(Vindex);
650 1324 : print(state.files.eio, ",{:.4R},{:.4R},{:.4R}", Vertex.x, Vertex.y, Vertex.z);
651 : }
652 331 : print(state.files.eio, "\n");
653 40 : }
654 :
655 40 : print(state.files.eio, "Approximate or User Input ViewFactors,To Surface,Surface Class,RowSum");
656 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
657 331 : print(state.files.eio, ",{}", state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name);
658 40 : }
659 40 : print(state.files.eio, "\n");
660 :
661 371 : for (int Findex : thisEnclosure.SurfaceReportNums) {
662 331 : RowSum = sum(SaveApproximateViewFactors(_, Findex));
663 331 : print(state.files.eio,
664 : "{},{},{},{:.4R}",
665 : "View Factor",
666 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
667 662 : cSurfaceClass(state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Class),
668 : RowSum);
669 3234 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
670 2903 : print(state.files.eio, ",{:.4R}", SaveApproximateViewFactors(SurfNum, Findex));
671 331 : }
672 331 : print(state.files.eio, "\n");
673 40 : }
674 :
675 40 : print(state.files.eio, "Final ViewFactors,To Surface,Surface Class,RowSum");
676 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
677 331 : print(state.files.eio, ",{}", state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name);
678 40 : }
679 40 : print(state.files.eio, "\n");
680 :
681 371 : for (int Findex : thisEnclosure.SurfaceReportNums) {
682 331 : RowSum = sum(thisEnclosure.F(_, Findex));
683 331 : print(state.files.eio,
684 : "{},{},{},{:.4R}",
685 : "View Factor",
686 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
687 662 : cSurfaceClass(state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Class),
688 : RowSum);
689 3234 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
690 2903 : print(state.files.eio, ",{:.4R}", thisEnclosure.F(SurfNum, Findex));
691 331 : }
692 331 : print(state.files.eio, "\n");
693 40 : }
694 :
695 40 : if (Option1 == "IDF") {
696 : // TODO Both "original" and "final" print the same output. This is likely a bug
697 : // (discovered while updating output to {fmt}
698 : // see:
699 : // https://github.com/NREL/EnergyPlusArchive/commit/1c08247853c297dce59f3f53cde47ccfa67720c0#diff-124964a7e9b73ce494c1952ab1acdeeb
700 8 : print(state.files.debug, "{}\n", "!======== original input factors ===========================");
701 8 : print(state.files.debug, "ZoneProperty:UserViewFactors:BySurfaceName,{},\n", thisEnclosure.Name);
702 88 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
703 956 : for (int Findex : thisEnclosure.SurfaceReportNums) {
704 1752 : print(state.files.debug,
705 : " {},{},{:.6R}",
706 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name,
707 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
708 : thisEnclosure.F(Findex, SurfNum));
709 876 : if (!(SurfNum == thisEnclosure.NumOfSurfaces && Findex == thisEnclosure.NumOfSurfaces)) {
710 868 : print(state.files.debug, ",\n");
711 : } else {
712 8 : print(state.files.debug, ";\n");
713 : }
714 80 : }
715 8 : }
716 8 : print(state.files.debug, "{}\n", "!============= end of data ======================");
717 :
718 8 : print(state.files.debug, "{}\n", "!============ final view factors =======================");
719 8 : print(state.files.debug, "ZoneProperty:UserViewFactors:BySurfaceName,{},\n", thisEnclosure.Name);
720 88 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
721 956 : for (int Findex : thisEnclosure.SurfaceReportNums) {
722 1752 : print(state.files.debug,
723 : " {},{},{:.6R}",
724 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name,
725 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
726 : thisEnclosure.F(Findex, SurfNum));
727 876 : if (!(SurfNum == thisEnclosure.SurfaceReportNums.back() && Findex == thisEnclosure.SurfaceReportNums.back())) {
728 868 : print(state.files.debug, ",\n");
729 : } else {
730 8 : print(state.files.debug, ";\n");
731 : }
732 80 : }
733 8 : }
734 8 : print(state.files.debug, "{}\n", "!============= end of data ======================");
735 : }
736 :
737 40 : print(state.files.eio, "Script F Factors,X Surface");
738 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
739 331 : print(state.files.eio, ",{}", state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name);
740 40 : }
741 40 : print(state.files.eio, "\n");
742 371 : for (int Findex : thisEnclosure.SurfaceReportNums) {
743 331 : print(state.files.eio, "{},{}", "Script F Factor", state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name);
744 3234 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
745 2903 : print(state.files.eio, ",{:.4R}", thisEnclosure.ScriptF(Findex, SurfNum));
746 331 : }
747 331 : print(state.files.eio, "\n");
748 40 : }
749 :
750 : // Deallocate saved approximate/user view factors
751 40 : SaveApproximateViewFactors.deallocate();
752 : }
753 :
754 5034 : RowSum = 0.0;
755 49133 : for (int Findex : thisEnclosure.SurfaceReportNums) {
756 44099 : RowSum += sum(thisEnclosure.F(_, Findex));
757 5034 : }
758 5034 : RowSum = std::abs(RowSum - thisEnclosure.NumOfSurfaces);
759 5034 : FixedRowSum = std::abs(FixedRowSum - thisEnclosure.NumOfSurfaces);
760 5034 : if (state.dataGlobal->DisplayAdvancedReportVariables) {
761 59 : print(state.files.eio,
762 : "Surface View Factor Check Values,{},{:.6R},{:.6R},{:.6R},{},{:.6R},{:.6R}\n",
763 59 : thisEnclosure.Name,
764 : CheckValue1,
765 : CheckValue2,
766 : FinalCheckValue,
767 : NumIterations,
768 : FixedRowSum,
769 : RowSum);
770 : }
771 : }
772 : }
773 :
774 796 : if (ErrorsFound) {
775 0 : ShowFatalError(state, format("{}Errors found during initialization of radiant exchange. Program terminated.", RoutineName));
776 : }
777 796 : }
778 :
779 796 : void InitSolarViewFactors(EnergyPlusData &state)
780 : {
781 :
782 : // Initializes view factors for diffuse solar distribution between surfaces in an enclosure.
783 :
784 796 : Array2D<Real64> SaveApproximateViewFactors; // Save for View Factor reporting
785 796 : std::string Option1; // view factor report option
786 : static constexpr std::string_view RoutineName("InitSolarViewFactors: ");
787 :
788 796 : bool ErrorsFound = false;
789 796 : bool ViewFactorReport = false;
790 796 : General::ScanForReports(state, "ViewFactorInfo", ViewFactorReport, _, Option1);
791 :
792 796 : if (ViewFactorReport) { // Print heading
793 8 : print(state.files.eio, "{}\n", "! <Solar View Factor Information>");
794 8 : print(state.files.eio, "{}\n", "! <Solar View Factor - Zone/Enclosure Information>,Zone/Enclosure Name,Number of Surfaces");
795 8 : print(state.files.eio,
796 : "{}\n",
797 : "! <Solar View Factor - Surface Information>,Surface Name,Surface Class,Area {m2},Azimuth,Tilt,Solar Absorbtance,#Sides,Vertices");
798 8 : print(state.files.eio, "{}\n", "! <Solar View Factor / Interchange Type>,Surface Name(s)");
799 8 : print(state.files.eio, "{}\n", "! <Solar View Factor>,Surface Name,Surface Class,Row Sum,View Factors for each Surface");
800 : }
801 :
802 796 : const std::string cCurrentModuleObject = "ZoneProperty:UserViewFactors:BySurfaceName";
803 796 : int NumZonesWithUserFbyS = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
804 796 : if (NumZonesWithUserFbyS > 0) AlignInputViewFactors(state, cCurrentModuleObject, ErrorsFound);
805 :
806 5851 : for (int enclosureNum = 1; enclosureNum <= state.dataViewFactor->NumOfSolarEnclosures; ++enclosureNum) {
807 5055 : auto &thisEnclosure = state.dataViewFactor->EnclSolInfo(enclosureNum);
808 5055 : if (enclosureNum == 1) {
809 764 : if (state.dataGlobal->DisplayAdvancedReportVariables)
810 21 : print(state.files.eio,
811 : "{}\n",
812 : "! <Solar View Factor Check Values>,Zone/Enclosure Name,Original Check Value,Calculated Fixed Check "
813 : "Value,Final Check Value,Number of Iterations,Fixed RowSum Convergence,Used RowSum "
814 : "Convergence");
815 : }
816 5055 : int numEnclosureSurfaces = 0;
817 10123 : for (int spaceNum : thisEnclosure.spaceNums) {
818 : // Note that Space.Surfaces only includes HT surfs, see SurfaceGeometry::CreateMissingSpaces
819 : // But it also includes air boundary surfaces which need to be excluded here
820 49476 : for (int surfNum : state.dataHeatBal->space(spaceNum).surfaces) {
821 44408 : if (state.dataSurface->Surface(surfNum).IsAirBoundarySurf) continue;
822 44390 : ++numEnclosureSurfaces;
823 5068 : }
824 5055 : }
825 5055 : thisEnclosure.NumOfSurfaces = numEnclosureSurfaces;
826 5055 : if (numEnclosureSurfaces < 1) {
827 0 : ShowSevereError(state, format("{}No surfaces in enclosure={}.", RoutineName, thisEnclosure.Name));
828 0 : ErrorsFound = true;
829 : }
830 :
831 : // Allocate the parts of the derived type
832 5055 : thisEnclosure.F.dimension(numEnclosureSurfaces, numEnclosureSurfaces, 0.0);
833 5055 : thisEnclosure.Area.dimension(numEnclosureSurfaces, 0.0);
834 5055 : thisEnclosure.SolAbsorptance.dimension(numEnclosureSurfaces, 0.0);
835 5055 : thisEnclosure.Azimuth.dimension(numEnclosureSurfaces, 0.0);
836 5055 : thisEnclosure.Tilt.dimension(numEnclosureSurfaces, 0.0);
837 5055 : thisEnclosure.SurfacePtr.dimension(numEnclosureSurfaces, 0);
838 :
839 : // Initialize the surface pointer array
840 5055 : int enclosureSurfNum = 0;
841 10123 : for (int const spaceNum : thisEnclosure.spaceNums) {
842 5068 : int priorZoneTotEnclSurfs = enclosureSurfNum;
843 49476 : for (int surfNum : state.dataHeatBal->space(spaceNum).surfaces) {
844 44408 : if (state.dataSurface->Surface(surfNum).IsAirBoundarySurf) continue;
845 44390 : ++enclosureSurfNum;
846 44390 : thisEnclosure.SurfacePtr(enclosureSurfNum) = surfNum;
847 : // Store pointers back to here
848 44390 : state.dataSurface->Surface(surfNum).SolarEnclSurfIndex = enclosureSurfNum;
849 44390 : state.dataSurface->Surface(surfNum).SolarEnclIndex = enclosureNum;
850 44390 : state.dataSurface->Surface(surfNum).RadEnclIndex = enclosureNum; // Radiant and Solar enclosures are parallel for now
851 44390 : if ((state.dataConstruction->Construct(state.dataSurface->Surface(surfNum).Construction).TypeIsWindow) &&
852 44404 : (state.dataSurface->Surface(surfNum).ExtBoundCond > 0) &&
853 14 : (state.dataSurface->Surface(surfNum).BaseSurf != surfNum)) { // Interzone window present
854 14 : thisEnclosure.HasInterZoneWindow = true;
855 : }
856 5068 : }
857 : // Store SurfaceReportNums to maintain original reporting order
858 49476 : for (int surfNum : state.dataHeatBal->space(spaceNum).surfaces) {
859 258642 : for (int enclSNum = priorZoneTotEnclSurfs + 1; enclSNum <= enclosureSurfNum; ++enclSNum) {
860 258624 : if (thisEnclosure.SurfacePtr(enclSNum) == state.dataSurface->AllSurfaceListReportOrder[surfNum - 1]) {
861 44390 : thisEnclosure.SurfaceReportNums.push_back(enclSNum);
862 44390 : break;
863 : }
864 : }
865 5068 : }
866 5055 : }
867 : // Initialize the area and related arrays
868 49445 : for (int enclSurfNum = 1; enclSurfNum <= thisEnclosure.NumOfSurfaces; ++enclSurfNum) {
869 44390 : int const SurfNum = thisEnclosure.SurfacePtr(enclSurfNum);
870 44390 : thisEnclosure.Area(enclSurfNum) = state.dataSurface->Surface(SurfNum).Area;
871 88780 : thisEnclosure.SolAbsorptance(enclSurfNum) =
872 44390 : state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).InsideAbsorpSolar;
873 44390 : thisEnclosure.Azimuth(enclSurfNum) = state.dataSurface->Surface(SurfNum).Azimuth;
874 44390 : thisEnclosure.Tilt(enclSurfNum) = state.dataSurface->Surface(SurfNum).Tilt;
875 : }
876 :
877 5055 : if (thisEnclosure.NumOfSurfaces == 1) {
878 : // If there is only one surface in a zone, then there is no solar distribution
879 2 : if (state.dataGlobal->DisplayAdvancedReportVariables)
880 1 : print(state.files.eio, "Solar View Factor Check Values,{},0,0,0,-1,0,0\n", thisEnclosure.Name);
881 2 : continue; // Go to the next enclosure in the loop
882 : }
883 :
884 : // Get user supplied view factors if available in idf.
885 :
886 5053 : bool NoUserInputF = true;
887 :
888 5053 : if (NumZonesWithUserFbyS > 0) {
889 :
890 2 : GetInputViewFactorsbyName(state,
891 2 : thisEnclosure.Name,
892 : thisEnclosure.NumOfSurfaces,
893 : thisEnclosure.F,
894 2 : thisEnclosure.SurfacePtr,
895 : NoUserInputF,
896 : ErrorsFound); // Obtains user input view factors from input file
897 : }
898 :
899 5053 : if (NoUserInputF) {
900 :
901 : // Calculate the view factors and make sure they satisfy reciprocity
902 5052 : CalcApproximateViewFactors(state,
903 : thisEnclosure.NumOfSurfaces,
904 5052 : thisEnclosure.Area,
905 5052 : thisEnclosure.Azimuth,
906 5052 : thisEnclosure.Tilt,
907 : thisEnclosure.F,
908 5052 : thisEnclosure.SurfacePtr);
909 : }
910 :
911 5053 : if (ViewFactorReport) { // Allocate and save user or approximate view factors for reporting.
912 40 : SaveApproximateViewFactors.allocate(thisEnclosure.NumOfSurfaces, thisEnclosure.NumOfSurfaces);
913 40 : SaveApproximateViewFactors = thisEnclosure.F;
914 : }
915 :
916 5053 : Real64 CheckValue1 = 0.0;
917 5053 : Real64 CheckValue2 = 0.0;
918 5053 : Real64 FinalCheckValue = 0.0;
919 5053 : Real64 FixedRowSum = 0.0;
920 5053 : int NumIterations = 0;
921 :
922 5053 : bool anyIntMassInZone = DoesZoneHaveInternalMass(state, thisEnclosure.NumOfSurfaces, thisEnclosure.SurfacePtr);
923 5053 : FixViewFactors(state,
924 : thisEnclosure.NumOfSurfaces,
925 5053 : thisEnclosure.Area,
926 : thisEnclosure.F,
927 5053 : thisEnclosure.Name,
928 5053 : thisEnclosure.spaceNums,
929 : CheckValue1,
930 : CheckValue2,
931 : FinalCheckValue,
932 : NumIterations,
933 : FixedRowSum,
934 : anyIntMassInZone);
935 :
936 5053 : if (ViewFactorReport) { // Write to SurfInfo File
937 : // Zone Surface Information Output
938 40 : print(state.files.eio, "Solar View Factor - Zone/Enclosure Information,{},{}\n", thisEnclosure.Name, thisEnclosure.NumOfSurfaces);
939 :
940 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
941 662 : print(state.files.eio,
942 : "Solar View Factor - Surface Information,{},{},{:.4R},{:.4R},{:.4R},{:.4R},{}",
943 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name,
944 662 : cSurfaceClass(state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Class),
945 : thisEnclosure.Area(SurfNum),
946 : thisEnclosure.Azimuth(SurfNum),
947 : thisEnclosure.Tilt(SurfNum),
948 : thisEnclosure.SolAbsorptance(SurfNum),
949 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Sides);
950 :
951 1655 : for (int Vindex = 1; Vindex <= state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Sides; ++Vindex) {
952 1324 : auto const &Vertex = state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Vertex(Vindex);
953 1324 : print(state.files.eio, ",{:.4R},{:.4R},{:.4R}", Vertex.x, Vertex.y, Vertex.z);
954 : }
955 331 : print(state.files.eio, "\n");
956 40 : }
957 :
958 40 : print(state.files.eio, "Approximate or User Input Solar ViewFactors,To Surface,Surface Class,RowSum");
959 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
960 331 : print(state.files.eio, ",{}", state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name);
961 40 : }
962 40 : print(state.files.eio, "\n");
963 :
964 371 : for (int Findex : thisEnclosure.SurfaceReportNums) {
965 331 : Real64 RowSum = sum(SaveApproximateViewFactors(_, Findex));
966 331 : print(state.files.eio,
967 : "Solar View Factor,{},{},{:.4R}",
968 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
969 662 : cSurfaceClass(state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Class),
970 : RowSum);
971 3234 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
972 2903 : print(state.files.eio, ",{:.4R}", SaveApproximateViewFactors(SurfNum, Findex));
973 331 : }
974 331 : print(state.files.eio, "\n");
975 40 : }
976 :
977 40 : print(state.files.eio, "Final Solar ViewFactors,To Surface,Surface Class,RowSum");
978 371 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
979 331 : print(state.files.eio, ",{}", state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name);
980 40 : }
981 40 : print(state.files.eio, "\n");
982 :
983 371 : for (int Findex : thisEnclosure.SurfaceReportNums) {
984 331 : Real64 RowSum = sum(thisEnclosure.F(_, Findex));
985 331 : print(state.files.eio,
986 : "{},{},{},{:.4R}",
987 : "Solar View Factor",
988 331 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
989 662 : cSurfaceClass(state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Class),
990 : RowSum);
991 3234 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
992 2903 : print(state.files.eio, ",{:.4R}", thisEnclosure.F(SurfNum, Findex));
993 331 : }
994 331 : print(state.files.eio, "\n");
995 40 : }
996 :
997 40 : if (Option1 == "IDF") {
998 : // TODO Both "original" and "final" print the same output. This is likely a bug
999 : // see:
1000 : // https://github.com/NREL/EnergyPlusArchive/commit/1c08247853c297dce59f3f53cde47ccfa67720c0#diff-124964a7e9b73ce494c1952ab1acdeeb
1001 8 : print(state.files.debug, "{}\n", "!======== original input factors ===========================");
1002 8 : print(state.files.debug, "ZoneProperty:UserViewFactors:BySurfaceName,{},\n", thisEnclosure.Name);
1003 88 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
1004 956 : for (int Findex : thisEnclosure.SurfaceReportNums) {
1005 1752 : print(state.files.debug,
1006 : " {},{},{:.6R}",
1007 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name,
1008 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
1009 : thisEnclosure.F(Findex, SurfNum));
1010 876 : if (!(SurfNum == thisEnclosure.NumOfSurfaces && Findex == thisEnclosure.NumOfSurfaces)) {
1011 868 : print(state.files.debug, ",\n");
1012 : } else {
1013 8 : print(state.files.debug, ";\n");
1014 : }
1015 80 : }
1016 8 : }
1017 8 : print(state.files.debug, "{}\n", "!============= end of data ======================");
1018 :
1019 8 : print(state.files.debug, "{}\n", "!============ final view factors =======================");
1020 8 : print(state.files.debug, "ZoneProperty:UserViewFactors:BySurfaceName,{},\n", thisEnclosure.Name);
1021 88 : for (int SurfNum : thisEnclosure.SurfaceReportNums) {
1022 956 : for (int Findex : thisEnclosure.SurfaceReportNums) {
1023 1752 : print(state.files.debug,
1024 : " {},{},{:.6R}",
1025 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(SurfNum)).Name,
1026 876 : state.dataSurface->Surface(thisEnclosure.SurfacePtr(Findex)).Name,
1027 : thisEnclosure.F(Findex, SurfNum));
1028 876 : if (!(SurfNum == thisEnclosure.NumOfSurfaces && Findex == thisEnclosure.NumOfSurfaces)) {
1029 868 : print(state.files.debug, ",\n");
1030 : } else {
1031 8 : print(state.files.debug, ";\n");
1032 : }
1033 80 : }
1034 8 : }
1035 8 : print(state.files.debug, "{}\n", "!============= end of data ======================");
1036 : }
1037 40 : SaveApproximateViewFactors.deallocate();
1038 : }
1039 :
1040 5053 : Real64 RowSum = 0.0;
1041 49441 : for (int Findex : thisEnclosure.SurfaceReportNums) {
1042 44388 : RowSum += sum(thisEnclosure.F(_, Findex));
1043 5053 : }
1044 5053 : RowSum = std::abs(RowSum - thisEnclosure.NumOfSurfaces);
1045 5053 : FixedRowSum = std::abs(FixedRowSum - thisEnclosure.NumOfSurfaces);
1046 5053 : if (state.dataGlobal->DisplayAdvancedReportVariables) {
1047 59 : print(state.files.eio,
1048 : "Solar View Factor Check Values,{},{:.6R},{:.6R},{:.6R},{},{:.6R},{:.6R}\n",
1049 59 : thisEnclosure.Name,
1050 : CheckValue1,
1051 : CheckValue2,
1052 : FinalCheckValue,
1053 : NumIterations,
1054 : FixedRowSum,
1055 : RowSum);
1056 : }
1057 : }
1058 :
1059 796 : if (ErrorsFound) {
1060 0 : ShowFatalError(state, format("{}Errors found during initialization of diffuse solar distribution. Program terminated.", RoutineName));
1061 : }
1062 796 : }
1063 :
1064 0 : void GetInputViewFactors(EnergyPlusData &state,
1065 : std::string const &ZoneName, // Needed to check for user input view factors.
1066 : int const N, // NUMBER OF SURFACES
1067 : Array2A<Real64> F, // USER INPUT DIRECT VIEW FACTOR MATRIX (N X N)
1068 : [[maybe_unused]] const Array1D_int &SPtr, // pointer to actual surface number
1069 : bool &NoUserInputF, // Flag signifying no input F's for this
1070 : bool &ErrorsFound // True when errors are found in number of fields vs max args
1071 : )
1072 : {
1073 :
1074 : // SUBROUTINE INFORMATION:
1075 : // AUTHOR Curt Pedersen
1076 : // DATE WRITTEN September 2005
1077 : // MODIFIED Linda Lawrie;September 2010
1078 :
1079 : // PURPOSE OF THIS SUBROUTINE:
1080 : // This routine gets the user view factor info.
1081 :
1082 : // Argument array dimensioning
1083 0 : F.dim(N, N);
1084 : // EP_SIZE_CHECK(SPtr, N);
1085 :
1086 0 : NoUserInputF = true;
1087 0 : int UserFZoneIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, "ZoneProperty:UserViewFactors", ZoneName);
1088 0 : if (UserFZoneIndex > 0) {
1089 : int NumAlphas;
1090 : int NumNums;
1091 : int IOStat;
1092 0 : NoUserInputF = false;
1093 :
1094 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1095 : "ZoneProperty:UserViewFactors",
1096 : UserFZoneIndex,
1097 0 : state.dataIPShortCut->cAlphaArgs,
1098 : NumAlphas,
1099 0 : state.dataIPShortCut->rNumericArgs,
1100 : NumNums,
1101 : IOStat,
1102 0 : state.dataIPShortCut->lNumericFieldBlanks,
1103 0 : state.dataIPShortCut->lAlphaFieldBlanks,
1104 0 : state.dataIPShortCut->cAlphaFieldNames,
1105 0 : state.dataIPShortCut->cNumericFieldNames);
1106 :
1107 0 : if (NumNums < 3 * pow_2(N)) {
1108 0 : std::string_view cCurrentModuleObject = "ZoneProperty:UserViewFactors";
1109 0 : ShowSevereError(state, format("GetInputViewFactors: {}=\"{}\", not enough values.", cCurrentModuleObject, ZoneName));
1110 0 : ShowContinueError(state, format("...Number of input values [{}] is less than the required number=[{}].", NumNums, 3 * pow_2(N)));
1111 0 : ErrorsFound = true;
1112 0 : NumNums = 0;
1113 : }
1114 0 : F = 0.0;
1115 0 : for (int index = 1; index <= NumNums; index += 3) {
1116 0 : int inx1 = state.dataIPShortCut->rNumericArgs(index);
1117 0 : int inx2 = state.dataIPShortCut->rNumericArgs(index + 1);
1118 0 : F(inx2, inx1) = state.dataIPShortCut->rNumericArgs(index + 2);
1119 : }
1120 : }
1121 0 : }
1122 :
1123 1 : void AlignInputViewFactors(EnergyPlusData &state,
1124 : std::string const &cCurrentModuleObject, // Object type
1125 : bool &ErrorsFound // True when errors are found
1126 : )
1127 : {
1128 1 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
1129 1 : auto &instancesValue = instances.value();
1130 2 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
1131 1 : auto const &fields = instance.value();
1132 2 : std::string const thisSpaceOrSpaceListName = Util::makeUPPER(fields.at("zone_or_zonelist_or_space_or_spacelist_name").get<std::string>());
1133 : // do not mark object as used here - let GetInputViewFactorsbyName do that
1134 :
1135 : // Look for matching radiant enclosure name (solar enclosures have the same names)
1136 1 : bool enclMatchFound = false;
1137 1 : for (int enclosureNum = 1; enclosureNum <= state.dataViewFactor->NumOfRadiantEnclosures; ++enclosureNum) {
1138 1 : auto &thisEnclosure = state.dataViewFactor->EnclRadInfo(enclosureNum);
1139 1 : if (Util::SameString(thisSpaceOrSpaceListName, thisEnclosure.Name)) {
1140 : // View factor space name matches enclosure name
1141 1 : enclMatchFound = true;
1142 1 : break;
1143 : }
1144 : }
1145 1 : if (enclMatchFound) continue; // We're done with this instance
1146 : // Find matching SpaceList name
1147 0 : int spaceListNum = Util::FindItemInList(thisSpaceOrSpaceListName, state.dataHeatBal->spaceList);
1148 0 : int zoneListNum = 0;
1149 0 : int inputZoneNum = 0;
1150 0 : size_t listSize = 0;
1151 0 : if (spaceListNum > 0) {
1152 0 : listSize = int(state.dataHeatBal->spaceList(spaceListNum).spaces.size());
1153 : } else {
1154 : // Check Zone and ZoneList for backwards compatibility
1155 0 : zoneListNum = Util::FindItemInList(thisSpaceOrSpaceListName, state.dataHeatBal->ZoneList);
1156 0 : if (zoneListNum > 0) {
1157 0 : for (int const zoneNum : state.dataHeatBal->ZoneList(zoneListNum).Zone) {
1158 0 : listSize += state.dataHeatBal->Zone(zoneNum).spaceIndexes.size();
1159 : }
1160 : } else {
1161 0 : inputZoneNum = Util::FindItemInList(thisSpaceOrSpaceListName, state.dataHeatBal->Zone);
1162 0 : if (inputZoneNum > 0) {
1163 0 : listSize = state.dataHeatBal->Zone(inputZoneNum).spaceIndexes.size();
1164 : }
1165 : }
1166 : }
1167 :
1168 0 : if (listSize > 0) {
1169 : // Look for radiant enclosure with same list of spaces (solar enclosures are the same, so this is sufficient)
1170 0 : for (int enclosureNum = 1; enclosureNum <= state.dataViewFactor->NumOfRadiantEnclosures; ++enclosureNum) {
1171 0 : auto &thisEnclosure = state.dataViewFactor->EnclRadInfo(enclosureNum);
1172 0 : bool anySpaceNotFound = false;
1173 : // If the number of enclosure spaces is not the same as the number of spacelist space, go to the next enclosure
1174 0 : if (listSize != thisEnclosure.spaceNums.size()) continue;
1175 0 : if (spaceListNum > 0) {
1176 0 : for (int sListSpaceNum : state.dataHeatBal->spaceList(spaceListNum).spaces) {
1177 : // Search for matching spaces
1178 0 : bool thisSpaceFound = false;
1179 0 : for (int enclSpaceNum : thisEnclosure.spaceNums) {
1180 0 : if (enclSpaceNum == sListSpaceNum) {
1181 0 : thisSpaceFound = true;
1182 0 : break;
1183 : }
1184 0 : }
1185 0 : if (!thisSpaceFound) {
1186 0 : anySpaceNotFound = true;
1187 0 : break;
1188 : }
1189 0 : }
1190 0 : } else if (zoneListNum > 0) {
1191 0 : for (int zListZoneNum : state.dataHeatBal->ZoneList(zoneListNum).Zone) {
1192 0 : for (int spaceNum : state.dataHeatBal->Zone(zListZoneNum).spaceIndexes) {
1193 : // Search for matching spaces
1194 0 : bool thisSpaceFound = false;
1195 0 : for (int enclSpaceNum : thisEnclosure.spaceNums) {
1196 0 : if (enclSpaceNum == spaceNum) {
1197 0 : thisSpaceFound = true;
1198 0 : break;
1199 : }
1200 0 : }
1201 0 : if (!thisSpaceFound) {
1202 0 : anySpaceNotFound = true;
1203 0 : break;
1204 : }
1205 0 : }
1206 0 : if (anySpaceNotFound) {
1207 0 : break;
1208 : }
1209 : }
1210 0 : } else if (inputZoneNum > 0) {
1211 0 : for (int spaceNum : state.dataHeatBal->Zone(inputZoneNum).spaceIndexes) {
1212 : // Search for matching spaces
1213 0 : bool thisSpaceFound = false;
1214 0 : for (int enclSpaceNum : thisEnclosure.spaceNums) {
1215 0 : if (enclSpaceNum == spaceNum) {
1216 0 : thisSpaceFound = true;
1217 0 : break;
1218 : }
1219 0 : }
1220 0 : if (!thisSpaceFound) {
1221 0 : anySpaceNotFound = true;
1222 0 : break;
1223 : }
1224 0 : }
1225 : }
1226 0 : if (anySpaceNotFound) {
1227 0 : continue; // On to the next enclosure
1228 : } else {
1229 0 : enclMatchFound = true;
1230 : // If matching SpaceList or ZoneList or Zone found, set the radiant and solar enclosure names to match
1231 0 : thisEnclosure.Name = thisSpaceOrSpaceListName;
1232 0 : state.dataViewFactor->EnclSolInfo(enclosureNum).Name = thisSpaceOrSpaceListName;
1233 0 : break; // We're done with radiant enclosures
1234 : }
1235 : }
1236 : }
1237 0 : if (!enclMatchFound) {
1238 0 : if (spaceListNum > 0) {
1239 0 : ShowSevereError(state,
1240 0 : format("AlignInputViewFactors: {}=\"{}\" found a matching SpaceList, but did not find a matching radiant or "
1241 : "solar enclosure with the same spaces.",
1242 : cCurrentModuleObject,
1243 : thisSpaceOrSpaceListName));
1244 0 : ErrorsFound = true;
1245 :
1246 0 : } else if (zoneListNum > 0) {
1247 0 : ShowSevereError(state,
1248 0 : format("AlignInputViewFactors: {}=\"{}\" found a matching ZoneList, but did not find a matching radiant or solar "
1249 : "enclosure with the same spaces.",
1250 : cCurrentModuleObject,
1251 : thisSpaceOrSpaceListName));
1252 0 : ErrorsFound = true;
1253 :
1254 : } else {
1255 0 : ShowSevereError(state,
1256 0 : format("AlignInputViewFactors: {}=\"{}\" did not find a matching radiant or solar enclosure name.",
1257 : cCurrentModuleObject,
1258 : thisSpaceOrSpaceListName));
1259 0 : ErrorsFound = true;
1260 : }
1261 : }
1262 2 : }
1263 1 : }
1264 :
1265 4 : void GetInputViewFactorsbyName(EnergyPlusData &state,
1266 : std::string const &EnclosureName, // Needed to check for user input view factors.
1267 : int const N, // NUMBER OF SURFACES
1268 : Array2A<Real64> F, // USER INPUT DIRECT VIEW FACTOR MATRIX (N X N)
1269 : const Array1D_int &SPtr, // pointer to actual surface number
1270 : bool &NoUserInputF, // Flag signifying no input F's for this
1271 : bool &ErrorsFound // True when errors are found in number of fields vs max args
1272 : )
1273 : {
1274 :
1275 : // SUBROUTINE INFORMATION:
1276 : // AUTHOR Curt Pedersen
1277 : // DATE WRITTEN September 2005
1278 : // MODIFIED Linda Lawrie;September 2010
1279 :
1280 : // PURPOSE OF THIS SUBROUTINE:
1281 : // This routine gets the user view factor info for an enclosure which could be a zone or a group of zones
1282 :
1283 : // Argument array dimensioning
1284 4 : F.dim(N, N);
1285 4 : EP_SIZE_CHECK(SPtr, N);
1286 :
1287 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1288 : int UserFZoneIndex;
1289 4 : Array1D_string enclosureSurfaceNames;
1290 4 : NoUserInputF = true;
1291 4 : UserFZoneIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(
1292 : state, "ZoneProperty:UserViewFactors:BySurfaceName", "zone_or_zonelist_or_space_or_spacelist_name", EnclosureName);
1293 :
1294 4 : if (UserFZoneIndex > 0) {
1295 : int NumAlphas;
1296 : int NumNums;
1297 : int IOStat;
1298 2 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
1299 2 : enclosureSurfaceNames.allocate(N);
1300 22 : for (int index = 1; index <= N; ++index) {
1301 20 : enclosureSurfaceNames(index) = state.dataSurface->Surface(SPtr(index)).Name;
1302 : }
1303 2 : NoUserInputF = false;
1304 :
1305 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1306 : "ZoneProperty:UserViewFactors:BySurfaceName",
1307 : UserFZoneIndex,
1308 2 : state.dataIPShortCut->cAlphaArgs,
1309 : NumAlphas,
1310 2 : state.dataIPShortCut->rNumericArgs,
1311 : NumNums,
1312 : IOStat,
1313 2 : state.dataIPShortCut->lNumericFieldBlanks,
1314 2 : state.dataIPShortCut->lAlphaFieldBlanks,
1315 2 : state.dataIPShortCut->cAlphaFieldNames,
1316 2 : state.dataIPShortCut->cNumericFieldNames);
1317 :
1318 2 : F = 0.0;
1319 2 : int numinx1 = 0;
1320 2 : if (NumNums < pow_2(N)) {
1321 0 : ShowWarningError(state, format("GetInputViewFactors: {}=\"{}\", not enough values.", cCurrentModuleObject, EnclosureName));
1322 0 : ShowContinueError(state,
1323 0 : format("...Number of input values [{}] is less than the required number=[{}] Missing surface pairs will have a "
1324 : "zero view factor.",
1325 : NumNums,
1326 0 : pow_2(N)));
1327 : }
1328 :
1329 202 : for (int index = 2; index <= NumAlphas; index += 2) {
1330 200 : int inx1 = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(index), enclosureSurfaceNames, N);
1331 200 : int inx2 = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(index + 1), enclosureSurfaceNames, N);
1332 200 : if (inx1 == 0) {
1333 0 : ShowSevereError(state, format("GetInputViewFactors: {}=\"{}\", invalid surface name.", cCurrentModuleObject, EnclosureName));
1334 0 : ShowContinueError(state,
1335 0 : format("...Surface name=\"{}\", not in this zone or enclosure.", state.dataIPShortCut->cAlphaArgs(index)));
1336 0 : ErrorsFound = true;
1337 : }
1338 200 : if (inx2 == 0) {
1339 0 : ShowSevereError(state, format("GetInputViewFactors: {}=\"{}\", invalid surface name.", cCurrentModuleObject, EnclosureName));
1340 0 : ShowContinueError(state,
1341 0 : format("...Surface name=\"{}\", not in this zone or enclosure.", state.dataIPShortCut->cAlphaArgs(index + 2)));
1342 0 : ErrorsFound = true;
1343 : }
1344 200 : ++numinx1;
1345 200 : if (inx1 > 0 && inx2 > 0) F(inx2, inx1) = state.dataIPShortCut->rNumericArgs(numinx1);
1346 : }
1347 :
1348 2 : enclosureSurfaceNames.deallocate();
1349 : }
1350 4 : }
1351 :
1352 10085 : void CalcApproximateViewFactors(EnergyPlusData &state,
1353 : int const N, // NUMBER OF SURFACES
1354 : const Array1D<Real64> &A, // AREA VECTOR- ASSUMED,BE N ELEMENTS LONG
1355 : const Array1D<Real64> &Azimuth, // Facing angle of the surface (in degrees)
1356 : const Array1D<Real64> &Tilt, // Tilt angle of the surface (in degrees)
1357 : Array2A<Real64> F, // APPROXIMATE DIRECT VIEW FACTOR MATRIX (N X N)
1358 : const Array1D_int &SPtr // pointer to REAL(r64) surface number (for error message)
1359 : )
1360 : {
1361 :
1362 : // SUBROUTINE INFORMATION:
1363 : // AUTHOR Curt Pedersen
1364 : // DATE WRITTEN July 2000
1365 : // MODIFIED March 2001 (RKS) to disallow surfaces facing the same direction to interact radiatively
1366 : // May 2002 (COP) to include INTMASS, FLOOR, ROOF and CEILING.
1367 : // RE-ENGINEERED September 2000 (RKS for EnergyPlus)
1368 :
1369 : // PURPOSE OF THIS SUBROUTINE:
1370 : // This subroutine approximates view factors using an area weighting.
1371 : // This is improved by one degree by not allowing surfaces facing the same
1372 : // direction to "see" each other.
1373 :
1374 : // METHODOLOGY EMPLOYED:
1375 : // Each surface sees some area of other surfaces within the zone. The view
1376 : // factors from the surface to the other seen surfaces are defined by their
1377 : // area over the summed area of seen surfaces. Surfaces facing the same angle
1378 : // are assumed to not be able to see each other.
1379 : // Modified May 2002 to cover poorly defined surface orientation. Now all thermal masses, roofs and
1380 : // ceilings are "seen" by other surfaces. Floors are seen by all other surfaces, but
1381 : // not by other floors.
1382 :
1383 : // Argument array dimensioning
1384 10085 : EP_SIZE_CHECK(A, N);
1385 10085 : EP_SIZE_CHECK(Azimuth, N);
1386 10085 : EP_SIZE_CHECK(Tilt, N);
1387 10085 : F.dim(N, N);
1388 10085 : EP_SIZE_CHECK(SPtr, N);
1389 :
1390 : // SUBROUTINE PARAMETER DEFINITIONS:
1391 10085 : Real64 constexpr SameAngleLimit(10.0); // If the difference in the azimuth angles are above this value (degrees),
1392 : // then the surfaces are assumed to be facing different directions.
1393 :
1394 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1395 : int i; // DO loop counters for surfaces in the zone
1396 : int j;
1397 10085 : Array1D<Real64> ZoneArea; // Sum of the area of all zone surfaces seen
1398 :
1399 : // Calculate the sum of the areas seen by all zone surfaces
1400 10085 : ZoneArea.dimension(N, 0.0);
1401 98552 : for (i = 1; i <= N; ++i) {
1402 1026072 : for (j = 1; j <= N; ++j) {
1403 : // No surface sees itself and no floor sees another floor
1404 1062189 : if (i == j || (state.dataSurface->Surface(SPtr(j)).Class == DataSurfaces::SurfaceClass::Floor &&
1405 124584 : state.dataSurface->Surface(SPtr(i)).Class == DataSurfaces::SurfaceClass::Floor))
1406 110715 : continue;
1407 : // All surface types see internal mass
1408 : // All surface types see floors
1409 : // Floors always see ceilings/roofs
1410 : // All other surfaces whose tilt or facing angle differences are greater than 10 degrees see each other
1411 826890 : if ((state.dataSurface->Surface(SPtr(j)).Class == DataSurfaces::SurfaceClass::IntMass) ||
1412 785289 : (state.dataSurface->Surface(SPtr(i)).Class == DataSurfaces::SurfaceClass::IntMass) ||
1413 744172 : (state.dataSurface->Surface(SPtr(j)).Class == DataSurfaces::SurfaceClass::Floor) ||
1414 1196828 : (state.dataSurface->Surface(SPtr(i)).Class == DataSurfaces::SurfaceClass::Floor) ||
1415 2162007 : (std::abs(Azimuth(i) - Azimuth(j)) > SameAngleLimit && std::abs(Azimuth(i) - Azimuth(j)) < 360.0 - SameAngleLimit) ||
1416 145494 : (std::abs(Tilt(i) - Tilt(j)) > SameAngleLimit)) {
1417 721986 : ZoneArea(i) += A(j);
1418 : }
1419 : }
1420 88467 : if (ZoneArea(i) <= 0.0) {
1421 0 : ShowWarningError(state, "CalcApproximateViewFactors: Zero area for all other zone surfaces.");
1422 0 : ShowContinueError(state,
1423 0 : format("Happens for Surface=\"{}\" in Zone={}",
1424 0 : state.dataSurface->Surface(SPtr(i)).Name,
1425 0 : state.dataHeatBal->Zone(state.dataSurface->Surface(SPtr(i)).Zone).Name));
1426 : }
1427 : }
1428 :
1429 : // Set up the approximate view factors. First these are initialized to all zero.
1430 : // This will clear out any junk leftover from whenever. Then, for each zone
1431 : // surface, set the view factor from that surface to other surfaces as the
1432 : // area of the other surface divided by the sum of the area of all zone surfaces
1433 : // that the original surface can actually see (calculated above). This will
1434 : // allow that the sum of all view factors from the original surface to all other
1435 : // surfaces will equal unity. F(I,J)=0 if I=J or if the surfaces face the same
1436 : // direction.
1437 10085 : F = 0.0;
1438 98552 : for (i = 1; i <= N; ++i) {
1439 1026072 : for (j = 1; j <= N; ++j) {
1440 : // No surface sees itself and no floor sees another floor
1441 1062189 : if (i == j || (state.dataSurface->Surface(SPtr(j)).Class == DataSurfaces::SurfaceClass::Floor &&
1442 124584 : state.dataSurface->Surface(SPtr(i)).Class == DataSurfaces::SurfaceClass::Floor))
1443 110715 : continue;
1444 : // All surface types see internal mass
1445 : // All surface types see floors
1446 : // Floors always see ceilings/roofs
1447 : // All other surfaces whose tilt or facing angle differences are greater than 10 degrees see each other
1448 826890 : if ((state.dataSurface->Surface(SPtr(j)).Class == DataSurfaces::SurfaceClass::IntMass) ||
1449 785289 : (state.dataSurface->Surface(SPtr(i)).Class == DataSurfaces::SurfaceClass::IntMass) ||
1450 744172 : (state.dataSurface->Surface(SPtr(j)).Class == DataSurfaces::SurfaceClass::Floor) ||
1451 1196828 : (state.dataSurface->Surface(SPtr(i)).Class == DataSurfaces::SurfaceClass::Floor) ||
1452 2162007 : (std::abs(Azimuth(i) - Azimuth(j)) > SameAngleLimit && std::abs(Azimuth(i) - Azimuth(j)) < 360.0 - SameAngleLimit) ||
1453 145494 : (std::abs(Tilt(i) - Tilt(j)) > SameAngleLimit)) {
1454 : // avoid a divide by zero if there are no other surfaces in the zone that can be seen
1455 721986 : if (ZoneArea(i) > 0.0) F(j, i) = A(j) / (ZoneArea(i));
1456 : }
1457 : }
1458 : }
1459 :
1460 10085 : ZoneArea.deallocate();
1461 10085 : }
1462 :
1463 10087 : void FixViewFactors(EnergyPlusData &state,
1464 : int const N, // NUMBER OF SURFACES
1465 : const Array1D<Real64> &A, // AREA VECTOR- ASSUMED,BE N ELEMENTS LONG
1466 : Array2A<Real64> F, // APPROXIMATE DIRECT VIEW FACTOR MATRIX (N X N)
1467 : std::string &enclName, // Name of Enclosure being fixed
1468 : std::vector<int> const &spaceNums, // Zones which are part of this enclosure
1469 : Real64 &OriginalCheckValue, // check of SUM(F) - N
1470 : Real64 &FixedCheckValue, // check after fixed of SUM(F) - N
1471 : Real64 &FinalCheckValue, // the one to go with
1472 : int &NumIterations, // number of iterations to fixed
1473 : Real64 &RowSum, // RowSum of Fixed
1474 : bool const anyIntMassInZone // are there any internal mass surfaces in the zone
1475 : )
1476 : {
1477 :
1478 : // SUBROUTINE INFORMATION:
1479 : // AUTHOR Curt Pedersen
1480 : // DATE WRITTEN July 2000
1481 : // MODIFIED September 2000 (RKS for EnergyPlus)
1482 : // April 2005,COP added capability to handle a
1483 : // surface larger than sum of all others (nonenclosure)
1484 : // by using a Fii view factor for that surface. Process is
1485 : // now much more robust and stable.
1486 :
1487 : // PURPOSE OF THIS SUBROUTINE:
1488 : // This subroutine fixes approximate view factors and enforces reciprocity
1489 : // and completeness.
1490 :
1491 : // METHODOLOGY EMPLOYED:
1492 : // A(i)*F(i,j)=A(j)*F(j,i); F(i,i)=0.; SUM(F(i,j)=1.0, j=1,N)
1493 : // Subroutine takes approximate view factors and enforces reciprocity by
1494 : // averaging AiFij and AjFji. Then it determines a set of row coefficients
1495 : // which can be multipled by each AF product to force the sum of AiFij for
1496 : // each row to equal Ai, and applies them. Completeness is checked, and if
1497 : // not satisfied, the AF averaging and row modifications are repeated until
1498 : // completeness is within a preselected small deviation from 1.0
1499 : // The routine also checks the number of surfaces and if N<=3, just enforces reciprocity.
1500 :
1501 : // Argument array dimensioning
1502 10087 : EP_SIZE_CHECK(A, N);
1503 10087 : F.dim(N, N);
1504 :
1505 : // SUBROUTINE PARAMETER DEFINITIONS:
1506 10087 : Real64 constexpr PrimaryConvergence(0.001);
1507 10087 : Real64 constexpr DifferenceConvergence(0.00001);
1508 :
1509 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1510 : Real64 ConvrgNew;
1511 : Real64 CheckConvergeTolerance; // check value for actual warning
1512 :
1513 : bool Converged;
1514 : // OriginalCheckValue is the first pass at a completeness check. Even if this is zero,
1515 : // there is no guarantee that reciprocity is satisfied. As a result, the rest of this
1516 : // routine is needed to correct any issues even if the user defined view factors
1517 : // satisfy completeness (OriginalCheckValue = 0).
1518 10087 : OriginalCheckValue = std::abs(sum(F) - N);
1519 :
1520 : // Allocate and zero arrays
1521 10087 : Array2D<Real64> FixedAF(F); // store for largest area check
1522 :
1523 10087 : Real64 ConvrgOld = 10.0;
1524 10087 : Real64 LargestArea = maxval(A);
1525 10087 : bool severeErrorPresent = false;
1526 : // Check for Strange Geometry
1527 : // When one surface has an area that exceeds the sum of all other surface areas in a zone,
1528 : // essentially the situation is a non-complete enclosure. As a result, the view factors
1529 : // calculated using the standard procedure below will not converge and may result in invalid
1530 : // view factors where either reciprocity or completeness is not satisfied. However, when
1531 : // the largest surface is just slightly smaller than the rest of the surface areas in the
1532 : // zone, it has been shown that there can still be problems. The correction below can
1533 : // be helpful in avoiding these problems. So, the criteria below (with the 0.99 term added
1534 : // into the comparison in the next line of code) intends to capture more cases that are
1535 : // "unbalanced" in surface area distribution so other strange cases can take advantage of
1536 : // this correction. The use of 0.99 is simply to provide some reasonable boundary numerically
1537 : // and does not have some derived theoretical basis.
1538 10087 : if (LargestArea > 0.99 * (sum(A) - LargestArea) && (N > 3)) {
1539 226 : for (int i = 1; i <= N; ++i) {
1540 226 : if (LargestArea != A(i)) continue;
1541 28 : state.dataHeatBalIntRadExchg->LargestSurf = i;
1542 28 : break;
1543 : }
1544 28 : FixedAF(state.dataHeatBalIntRadExchg->LargestSurf, state.dataHeatBalIntRadExchg->LargestSurf) =
1545 28 : min(0.9, 1.2 * LargestArea / sum(A)); // Give self view to big surface
1546 : }
1547 :
1548 : // Set up AF matrix.
1549 20174 : Array2D<Real64> AF(N, N); // = (AREA * DIRECT VIEW FACTOR) MATRIX
1550 98574 : for (int i = 1; i <= N; ++i) {
1551 1026292 : for (int j = 1; j <= N; ++j) {
1552 937805 : AF(j, i) = FixedAF(j, i) * A(i);
1553 : }
1554 : }
1555 :
1556 : // Enforce reciprocity by averaging AiFij and AjFji
1557 10087 : FixedAF = 0.5 * (AF + transpose(AF)); // Performance Slow way to average with transpose (heap use)
1558 :
1559 10087 : AF.deallocate();
1560 :
1561 20174 : Array2D<Real64> FixedF(N, N); // CORRECTED MATRIX OF VIEW FACTORS (N X N)
1562 :
1563 10087 : NumIterations = 0;
1564 10087 : RowSum = 0.0;
1565 : // Check for physically unreasonable enclosures.
1566 :
1567 10087 : if (N <= 3) {
1568 8 : for (int i = 1; i <= N; ++i) {
1569 24 : for (int j = 1; j <= N; ++j) {
1570 18 : FixedF(j, i) = FixedAF(j, i) / A(i);
1571 : }
1572 : }
1573 :
1574 2 : ShowWarningError(state, format("Surfaces in Zone/Enclosure=\"{}\" do not define an enclosure.", enclName));
1575 2 : ShowContinueError(state, "Number of surfaces <= 3, view factors are set to force reciprocity but may not fulfill completeness.");
1576 2 : ShowContinueError(state, "Reciprocity means that radiant exchange between two surfaces will match and not lead to an energy loss.");
1577 2 : ShowContinueError(state,
1578 : "Completeness means that all of the view factors between a surface and the other surfaces in a zone add up to unity.");
1579 2 : ShowContinueError(state,
1580 : "So, when there are three or less surfaces in a zone, EnergyPlus will make sure there are no losses of energy but");
1581 2 : ShowContinueError(
1582 : state, "it will not exchange the full amount of radiation with the rest of the zone as it would if there was a completed enclosure.");
1583 :
1584 2 : RowSum = sum(FixedF);
1585 2 : if (RowSum > (N + 0.01)) {
1586 : // Reciprocity enforced but there is more radiation than possible somewhere since the sum of one of the rows
1587 : // is now greater than unity. This should not be allowed as it can cause issues with the heat balance.
1588 : // Correct this by finding the largest row summation and dividing all of the elements in the F matrix by
1589 : // this max summation. This will provide a cap on radiation so that no row has a sum greater than unity
1590 : // and will still maintain reciprocity.
1591 0 : Array1D<Real64> sumFixedF;
1592 : Real64 MaxFixedFRowSum;
1593 0 : sumFixedF.allocate(N);
1594 0 : sumFixedF = 0.0;
1595 0 : for (int i = 1; i <= N; ++i) {
1596 0 : for (int j = 1; j <= N; ++j) {
1597 0 : sumFixedF(i) += FixedF(i, j);
1598 : }
1599 0 : if (i == 1) {
1600 0 : MaxFixedFRowSum = sumFixedF(i);
1601 : } else {
1602 0 : if (sumFixedF(i) > MaxFixedFRowSum) MaxFixedFRowSum = sumFixedF(i);
1603 : }
1604 : }
1605 0 : sumFixedF.deallocate();
1606 0 : if (MaxFixedFRowSum < 1.0) {
1607 0 : ShowFatalError(state, " FixViewFactors: Three surface or less zone failing ViewFactorFix correction which should never happen.");
1608 : } else {
1609 0 : FixedF *= (1.0 / MaxFixedFRowSum);
1610 : }
1611 0 : RowSum = sum(FixedF); // needs to be recalculated
1612 0 : }
1613 2 : FinalCheckValue = FixedCheckValue = std::abs(RowSum - N);
1614 2 : F = FixedF;
1615 4 : for (int spaceNum : spaceNums) {
1616 2 : state.dataHeatBal->Zone(state.dataHeatBal->space(spaceNum).zoneNum).EnforcedReciprocity = true;
1617 2 : }
1618 2 : return; // Do not iterate, stop with reciprocity satisfied.
1619 :
1620 : } // N <= 3 Case
1621 :
1622 : // Regular fix cases
1623 10085 : Array1D<Real64> RowCoefficient(N);
1624 10085 : Converged = false;
1625 220729 : while (!Converged) {
1626 210644 : ++NumIterations;
1627 2199091 : for (int i = 1; i <= N; ++i) {
1628 : // Determine row coefficients which will enforce closure.
1629 1988447 : Real64 const sum_FixedAF_i(sum(FixedAF(_, i)));
1630 1988447 : if (std::abs(sum_FixedAF_i) > 1.0e-10) {
1631 1988447 : RowCoefficient(i) = A(i) / sum_FixedAF_i;
1632 : } else {
1633 0 : RowCoefficient(i) = 1.0;
1634 : }
1635 1988447 : FixedAF(_, i) *= RowCoefficient(i);
1636 : }
1637 :
1638 : // Enforce reciprocity by averaging AiFij and AjFji
1639 210644 : FixedAF = 0.5 * (FixedAF + transpose(FixedAF));
1640 :
1641 : // Form FixedF matrix
1642 2199091 : for (int i = 1; i <= N; ++i) {
1643 26989798 : for (int j = 1; j <= N; ++j) {
1644 25001351 : FixedF(j, i) = FixedAF(j, i) / A(i);
1645 25001351 : if (std::abs(FixedF(j, i)) < 1.e-10) {
1646 6324393 : FixedF(j, i) = 0.0;
1647 6324393 : FixedAF(j, i) = 0.0;
1648 : }
1649 : }
1650 : }
1651 :
1652 210644 : ConvrgNew = std::abs(sum(FixedF) - N);
1653 210644 : if (std::abs(ConvrgOld - ConvrgNew) < DifferenceConvergence || ConvrgNew <= PrimaryConvergence) { // Change in sum of Fs must be small.
1654 10085 : Converged = true;
1655 : }
1656 210644 : ConvrgOld = ConvrgNew;
1657 210644 : if (NumIterations > 400) { // If everything goes bad,enforce reciprocity and go home.
1658 : // Enforce reciprocity by averaging AiFij and AjFji
1659 0 : FixedAF = 0.5 * (FixedAF + transpose(FixedAF));
1660 :
1661 : // Form FixedF matrix
1662 0 : for (int i = 1; i <= N; ++i) {
1663 0 : for (int j = 1; j <= N; ++j) {
1664 0 : FixedF(j, i) = FixedAF(j, i) / A(i);
1665 : }
1666 : }
1667 0 : Real64 const sum_FixedF(sum(FixedF));
1668 0 : FinalCheckValue = FixedCheckValue = CheckConvergeTolerance = std::abs(sum_FixedF - N);
1669 0 : RowSum = sum_FixedF;
1670 0 : if (CheckConvergeTolerance > 0.005) {
1671 0 : if (CheckConvergeTolerance > 0.1) {
1672 0 : ShowSevereError(
1673 : state,
1674 0 : format("FixViewFactors: View factors convergence has failed and will lead to heat balance errors in zone=\"{}\".",
1675 : enclName));
1676 : }
1677 0 : ShowWarningError(
1678 : state,
1679 0 : format("FixViewFactors: View factors not complete. Check for bad surface descriptions or unenclosed zone=\"{}\".", enclName));
1680 0 : ShowContinueError(state,
1681 0 : format("Enforced reciprocity has tolerance (ideal is "
1682 : "0)=[{:.6R}], Row Sum (ideal is {})=[{:.2R}].",
1683 : CheckConvergeTolerance,
1684 : N,
1685 : RowSum));
1686 0 : ShowContinueError(state,
1687 : "If zone is unusual or tolerance is on the order of 0.001, view "
1688 : "factors might be OK but results should be checked carefully.");
1689 0 : if (anyIntMassInZone) {
1690 0 : ShowContinueError(state,
1691 : "For zones with internal mass like this one, this"
1692 : "can happen when the internal mass has an area that"
1693 : "is much larger than the other surfaces in the zone.");
1694 0 : ShowContinueError(state,
1695 : "If a single thermal mass element exists in this zone"
1696 : "that has an area that is larger than the sum of the"
1697 : "rest of the surface areas, consider breaking it up"
1698 : "into two or more separate internal mass elements.");
1699 : }
1700 : }
1701 0 : if (std::abs(FixedCheckValue) < std::abs(OriginalCheckValue)) {
1702 0 : F = FixedF;
1703 0 : FinalCheckValue = FixedCheckValue;
1704 : }
1705 0 : return;
1706 : }
1707 : }
1708 10085 : FixedCheckValue = ConvrgNew;
1709 10085 : if (FixedCheckValue < OriginalCheckValue) {
1710 0 : F = FixedF;
1711 0 : FinalCheckValue = FixedCheckValue;
1712 : } else {
1713 10085 : FinalCheckValue = OriginalCheckValue;
1714 10085 : RowSum = sum(FixedF);
1715 10085 : if (std::abs(RowSum - N) < PrimaryConvergence) {
1716 10085 : F = FixedF;
1717 10085 : FinalCheckValue = FixedCheckValue;
1718 : } else {
1719 0 : ShowWarningError(
1720 : state,
1721 0 : format("FixViewFactors: View factors not complete. Check for bad surface descriptions or unenclosed zone=\"{}\".", enclName));
1722 : }
1723 : }
1724 10085 : if (severeErrorPresent) {
1725 0 : ShowFatalError(state,
1726 : "FixViewFactors: View factor calculations significantly out "
1727 : "of tolerance. See above messages for more information.");
1728 : }
1729 10091 : }
1730 :
1731 10087 : bool DoesZoneHaveInternalMass(EnergyPlusData &state, int const numZoneSurfaces, const Array1D_int &surfPointer)
1732 : {
1733 85895 : for (int i = 1; i <= numZoneSurfaces; ++i) {
1734 80380 : if (state.dataSurface->Surface(surfPointer(i)).Class == DataSurfaces::SurfaceClass::IntMass) return true;
1735 : }
1736 5515 : return false;
1737 : }
1738 :
1739 94645 : void CalcScriptF(EnergyPlusData &state,
1740 : int const N, // Number of surfaces
1741 : Array1D<Real64> const &A, // AREA VECTOR- ASSUMED,BE N ELEMENTS LONG
1742 : Array2<Real64> const &F, // DIRECT VIEW FACTOR MATRIX (N X N)
1743 : Array1D<Real64> &EMISS, // VECTOR OF SURFACE EMISSIVITIES
1744 : Array2<Real64> &ScriptF // MATRIX OF SCRIPT F FACTORS (N X N) //Tuned Transposed
1745 : )
1746 : {
1747 :
1748 : // SUBROUTINE INFORMATION:
1749 : // AUTHOR Curt Pedersen
1750 : // DATE WRITTEN 1980
1751 : // MODIFIED July 2000 (COP for the ASHRAE Loads Toolkit)
1752 : // RE-ENGINEERED September 2000 (RKS for EnergyPlus)
1753 : // RE-ENGINEERED June 2014 (Stuart Mentzer): Performance tuned
1754 :
1755 : // PURPOSE OF THIS SUBROUTINE:
1756 : // Determines Hottel's ScriptF coefficients which account for the total
1757 : // grey interchange between surfaces in an enclosure.
1758 :
1759 : // REFERENCES:
1760 : // Hottel, H. C. and A. F. Sarofim, Radiative Transfer, Ch 3, McGraw Hill, 1967.
1761 :
1762 : // SUBROUTINE ARGUMENTS:
1763 : // --Must satisfy reciprocity and completeness:
1764 : // A(i)*F(i,j)=A(j)*F(j,i); F(i,i)=0.; SUM(F(i,j)=1.0, j=1,N)
1765 :
1766 : // SUBROUTINE PARAMETER DEFINITIONS:
1767 94645 : Real64 constexpr MaxEmissLimit(0.99999); // Limit the emissivity internally/avoid a divide by zero error
1768 :
1769 : // Validate argument array dimensions
1770 94645 : assert(N >= 0); // Do we need to allow for N==0?
1771 94645 : assert((A.l() == 1) && (A.u() == N));
1772 94645 : assert((F.l1() == 1) && (F.u1() == N));
1773 94645 : assert((F.l2() == 1) && (F.u2() == N));
1774 94645 : assert((EMISS.l() == 1) && (EMISS.u() == N));
1775 94645 : assert(equal_dimensions(F, ScriptF));
1776 :
1777 : #ifdef EP_Count_Calls
1778 : ++state.dataTimingsData->NumCalcScriptF_Calls;
1779 : #endif
1780 :
1781 : // Load Cmatrix with AF (AREA * DIRECT VIEW FACTOR) matrix
1782 189290 : Array2D<Real64> Cmatrix(N, N); // = (AF - EMISS/REFLECTANCE) matrix (but plays other roles)
1783 94645 : assert(equal_dimensions(Cmatrix, F)); // For linear indexing
1784 94645 : Array2D<Real64>::size_type l(0u);
1785 878522 : for (int j = 1; j <= N; ++j) {
1786 8449430 : for (int i = 1; i <= N; ++i, ++l) {
1787 7665553 : Cmatrix[l] = A(i) * F[l]; // [ l ] == ( i, j )
1788 : }
1789 : }
1790 :
1791 : // Load Cmatrix with (AF - EMISS/REFLECTANCE) matrix
1792 94645 : Array1D<Real64> Excite(N); // Excitation vector = A*EMISS/REFLECTANCE
1793 94645 : l = 0u;
1794 878522 : for (int i = 1; i <= N; ++i, l += N + 1) {
1795 783877 : Real64 EMISS_i(EMISS(i));
1796 783877 : if (EMISS_i > MaxEmissLimit) { // Check/limit EMISS for this surface to avoid divide by zero below
1797 0 : EMISS_i = EMISS(i) = MaxEmissLimit;
1798 0 : ShowWarningError(state, "A thermal emissivity above 0.99999 was detected. This is not allowed. Value was reset to 0.99999");
1799 : }
1800 783877 : Real64 const EMISS_i_fac(A(i) / (1.0 - EMISS_i));
1801 783877 : Excite(i) = -EMISS_i * EMISS_i_fac; // Set up matrix columns for partial radiosity calculation
1802 783877 : Cmatrix[l] -= EMISS_i_fac; // Coefficient matrix for partial radiosity calculation // [ l ] == ( i, i )
1803 : }
1804 :
1805 189290 : Array2D<Real64> Cinverse(N, N); // Inverse of Cmatrix
1806 94645 : CalcMatrixInverse(Cmatrix, Cinverse); // SOLVE THE LINEAR SYSTEM
1807 94645 : Cmatrix.clear(); // Release memory ASAP
1808 :
1809 : // Scale Cinverse colums by excitation to get partial radiosity matrix
1810 94645 : l = 0u;
1811 878522 : for (int j = 1; j <= N; ++j) {
1812 783877 : Real64 const e_j(Excite(j));
1813 8449430 : for (int i = 1; i <= N; ++i, ++l) {
1814 7665553 : Cinverse[l] *= e_j; // [ l ] == ( i, j )
1815 : }
1816 : }
1817 94645 : Excite.clear(); // Release memory ASAP
1818 :
1819 : // Form Script F matrix transposed
1820 94645 : assert(equal_dimensions(Cinverse, ScriptF)); // For linear indexing
1821 94645 : Array2D<Real64>::size_type m(0u);
1822 878522 : for (int i = 1; i <= N; ++i) { // Inefficient order for cache but can reuse multiplier so faster choice depends on N
1823 783877 : Real64 const EMISS_i(EMISS(i));
1824 783877 : Real64 const EMISS_fac(EMISS_i / (1.0 - EMISS_i));
1825 783877 : l = static_cast<Array2D<Real64>::size_type>(i - 1);
1826 8449430 : for (int j = 1; j <= N; ++j, l += N, ++m) {
1827 7665553 : if (i == j) {
1828 : // ScriptF(I,J) = EMISS(I)/(1.0d0-EMISS(I))*(Jmatrix(I,J)-Delta*EMISS(I)), where Delta=1
1829 783877 : ScriptF[m] = EMISS_fac * (Cinverse[l] - EMISS_i); // [ l ] = ( i, j ), [ m ] == ( j, i )
1830 : } else {
1831 : // ScriptF(I,J) = EMISS(I)/(1.0d0-EMISS(I))*(Jmatrix(I,J)-Delta*EMISS(I)), where Delta=0
1832 6881676 : ScriptF[m] = EMISS_fac * Cinverse[l]; // [ l ] == ( i, j ), [ m ] == ( j, i )
1833 : }
1834 : }
1835 : }
1836 94645 : }
1837 :
1838 94645 : void CalcMatrixInverse(Array2<Real64> &A, // Matrix: Gets reduced to L\U form
1839 : Array2<Real64> &I // Returned as inverse matrix
1840 : )
1841 : {
1842 : // SUBROUTINE INFORMATION:
1843 : // AUTHOR Jakob Asmundsson
1844 : // DATE WRITTEN January 1999
1845 : // MODIFIED September 2000 (RKS for EnergyPlus)
1846 : // RE-ENGINEERED June 2014 (Stuart Mentzer): Performance/memory tuning rewrite
1847 :
1848 : // PURPOSE OF THIS SUBROUTINE:
1849 : // To find the inverse of Matrix, using partial pivoting.
1850 :
1851 : // METHODOLOGY EMPLOYED:
1852 : // Inverse is found using partial pivoting and Gauss elimination
1853 :
1854 : // REFERENCES:
1855 : // Any Linear Algebra book
1856 :
1857 : // Validation
1858 94645 : assert(A.square());
1859 94645 : assert(A.I1() == A.I2());
1860 94645 : assert(equal_dimensions(A, I));
1861 :
1862 : // Initialization
1863 94645 : int const l(A.l1());
1864 94645 : int const u(A.u1());
1865 94645 : int const n(u - l + 1);
1866 94645 : I.to_identity(); // I starts out as identity
1867 :
1868 : // Could do row scaling here to improve condition and then check min pivot isn't too small
1869 :
1870 : // Compute in-place LU decomposition of [A|I] with row pivoting
1871 878522 : for (int i = l; i <= u; ++i) {
1872 :
1873 : // Find pivot row in column i below diagonal
1874 783877 : int iPiv = i;
1875 783877 : Real64 aPiv(std::abs(A(i, i)));
1876 783877 : int ik(A.index(i, i + 1));
1877 4224715 : for (int k = i + 1; k <= u; ++k, ++ik) {
1878 3440838 : Real64 const aAki(std::abs(A[ik])); // [ ik ] == ( i, k )
1879 3440838 : if (aAki > aPiv) {
1880 0 : iPiv = k;
1881 0 : aPiv = aAki;
1882 : }
1883 : }
1884 783877 : assert(aPiv != 0.0); //? Is zero pivot possible for some user inputs? If so if test/handler needed
1885 :
1886 : // Swap row i with pivot row
1887 783877 : if (iPiv != i) {
1888 0 : int ji(A.index(l, i)); // [ ji ] == ( j, i )
1889 0 : int pj(A.index(l, iPiv)); // [ pj ] == ( j, iPiv )
1890 0 : for (int j = l; j <= u; ++j, ji += n, pj += n) {
1891 0 : Real64 const Aij(A[ji]);
1892 0 : A[ji] = A[pj];
1893 0 : A[pj] = Aij;
1894 0 : Real64 const Iij(I[ji]);
1895 0 : I[ji] = I[pj];
1896 0 : I[pj] = Iij;
1897 : }
1898 : }
1899 :
1900 : // Put multipliers in column i and reduce block below A(i,i)
1901 783877 : Real64 const Aii_inv(1.0 / A(i, i));
1902 4224715 : for (int k = i + 1; k <= u; ++k) {
1903 3440838 : Real64 const multiplier(A(i, k) * Aii_inv);
1904 3440838 : A(i, k) = multiplier;
1905 3440838 : if (multiplier != 0.0) {
1906 3347929 : int ji(A.index(i + 1, i)); // [ ji ] == ( j, i )
1907 3347929 : int jk(A.index(i + 1, k)); // [ jk ] == ( j, k )
1908 41438855 : for (int j = i + 1; j <= u; ++j, ji += n, jk += n) {
1909 38090926 : A[jk] -= multiplier * A[ji];
1910 : }
1911 3347929 : ji = A.index(l, i);
1912 3347929 : jk = A.index(l, k);
1913 62537893 : for (int j = l; j <= u; ++j, ji += n, jk += n) {
1914 59189964 : Real64 const Iij(I[ji]);
1915 59189964 : if (Iij != 0.0) {
1916 21050724 : I[jk] -= multiplier * Iij;
1917 : }
1918 : }
1919 : }
1920 : }
1921 : }
1922 :
1923 : // Perform back-substitution on [U|I] to put inverse in I
1924 878522 : for (int k = u; k >= l; --k) {
1925 783877 : Real64 const Akk_inv(1.0 / A(k, k));
1926 783877 : int jk(A.index(l, k)); // [ jk ] == ( j, k )
1927 8449430 : for (int j = l; j <= u; ++j, jk += n) {
1928 7665553 : I[jk] *= Akk_inv;
1929 : }
1930 783877 : int ik(A.index(k, l)); // [ ik ] == ( i, k )
1931 4224715 : for (int i = l; i < k; ++i, ++ik) { // Eliminate kth column entries from I in rows above k
1932 3440838 : Real64 const Aik(A[ik]);
1933 3440838 : int ji(A.index(l, i)); // [ ji ] == ( j, i )
1934 3440838 : int jm(A.index(l, k)); // [ jm ] == ( k, j )
1935 63637515 : for (int j = l; j <= u; ++j, ji += n, jm += n) {
1936 60196677 : I[ji] -= Aik * I[jm];
1937 : }
1938 : }
1939 : }
1940 94645 : }
1941 :
1942 19 : void CalcFMRT(EnergyPlusData &state,
1943 : int const N, // Number of surfaces
1944 : Array1D<Real64> const &A, // AREA VECTOR- ASSUMED,BE N ELEMENTS LONG
1945 : Array1D<Real64> &FMRT // VECTOR OF MEAN RADIANT TEMPERATURE "VIEW FACTORS"
1946 : )
1947 : {
1948 19 : double sumAF = 0.0;
1949 177 : for (int iS = 0; iS < N; iS++) {
1950 158 : FMRT[iS] = 1.0;
1951 158 : sumAF += A[iS];
1952 : }
1953 :
1954 19 : constexpr int maxIt = 100;
1955 19 : constexpr double tol = 0.0001;
1956 : double fLast;
1957 19 : double sumAFNew = sumAF;
1958 687 : for (unsigned i = 0; i < maxIt; i++) {
1959 687 : double fChange = 0.;
1960 687 : bool errorsFound(false);
1961 687 : sumAF = sumAFNew;
1962 687 : sumAFNew = 0.0;
1963 6726 : for (int iS = 0; iS < N; iS++) {
1964 6039 : fLast = FMRT[iS];
1965 6039 : FMRT[iS] = 1. / (1. - A[iS] * FMRT[iS] / (sumAF));
1966 6039 : if (FMRT[iS] > 100.) {
1967 0 : errorsFound = true;
1968 0 : ShowSevereError(state, "Geometry not compatible with Carroll MRT Zone Radiant Exchange method.");
1969 0 : break;
1970 : }
1971 6039 : fChange += fabs(FMRT[iS] - fLast);
1972 6039 : sumAFNew += A[iS] * FMRT[iS];
1973 : }
1974 :
1975 687 : if (errorsFound || fChange / N < tol) {
1976 : break;
1977 : }
1978 668 : if (i >= maxIt) {
1979 0 : errorsFound = true;
1980 0 : ShowSevereError(state, "Carroll MRT Zone Radiant Exchange method unable to converge on \"view factor\" calculation.");
1981 : }
1982 668 : if (errorsFound) {
1983 0 : ShowFatalError(state, "CalcFMRT: Errors found while calculating mean radiant temperature view factors. Program terminated.");
1984 : }
1985 : }
1986 19 : return;
1987 : }
1988 :
1989 475 : void CalcFp(int const N, // Number of surfaces
1990 : Array1D<Real64> const &EMISS, // VECTOR OF SURFACE EMISSIVITIES
1991 : Array1D<Real64> const &FMRT, // VECTOR OF MEAN RADIANT TEMPERATURE "VIEW FACTORS"
1992 : Array1D<Real64> &Fp // VECTOR OF OPPENHEIM RESISTANCE VALUES
1993 : )
1994 : {
1995 4425 : for (int iS = 0; iS < N; iS++) {
1996 3950 : Fp[iS] = Constant::StefanBoltzmann * EMISS[iS] / (EMISS[iS] / FMRT[iS] + 1. - EMISS[iS]); // actually sigma *
1997 : }
1998 475 : return;
1999 : }
2000 :
2001 113 : int GetRadiantSystemSurface(EnergyPlusData &state,
2002 : std::string const &cCurrentModuleObject, // Calling Object type
2003 : std::string const &RadSysName, // Calling Object name
2004 : int const RadSysZoneNum, // Radiant system zone number
2005 : std::string const &SurfaceName, // Referenced surface name
2006 : bool &ErrorsFound // True when errors are found
2007 : )
2008 : {
2009 : static constexpr std::string_view routineName("GetRadiantSystemSurface: "); // include trailing blank space
2010 :
2011 : // For radiant zone equipment, find the referenced surface and check if it is in the same zone or radiant enclosure
2012 113 : int const surfNum = Util::FindItemInList(SurfaceName, state.dataSurface->Surface);
2013 :
2014 : // Trap for surfaces that do not exist
2015 113 : if (surfNum == 0) {
2016 0 : ShowSevereError(state, format("{}Invalid Surface name = {}", routineName, SurfaceName));
2017 0 : ShowContinueError(state, format("Occurs for {} = {}", cCurrentModuleObject, RadSysName));
2018 0 : ErrorsFound = true;
2019 0 : return surfNum;
2020 : }
2021 :
2022 113 : if (RadSysZoneNum == 0) {
2023 0 : ShowSevereError(state, format("{}Invalid Zone number passed by {} = {}", routineName, cCurrentModuleObject, RadSysName));
2024 0 : ErrorsFound = true;
2025 0 : return surfNum;
2026 : }
2027 :
2028 : // Check if the surface and equipment are in the same zone
2029 113 : int const surfZoneNum = state.dataSurface->Surface(surfNum).Zone;
2030 113 : if (surfZoneNum == 0) {
2031 : // This should never happen
2032 0 : ShowSevereError(state,
2033 0 : format("{}Somehow the surface zone number is zero for{} = {} and Surface = {}",
2034 : routineName,
2035 : cCurrentModuleObject,
2036 : RadSysName,
2037 : SurfaceName)); // LCOV_EXCL_LINE
2038 : ErrorsFound = true; // LCOV_EXCL_LINE
2039 113 : } else if (surfZoneNum != RadSysZoneNum) {
2040 0 : ShowSevereError(state, format("{}Surface = {} is not in the same zone as the radiant equipment.", routineName, SurfaceName));
2041 0 : ShowContinueError(state, format("Surface zone or enclosure = {}", state.dataHeatBal->Zone(surfZoneNum).Name));
2042 0 : ShowContinueError(state, format("Radiant equipment zone or enclosure = {}", state.dataHeatBal->Zone(RadSysZoneNum).Name));
2043 0 : ShowContinueError(state, format("Occurs for {} = {}", cCurrentModuleObject, RadSysName));
2044 0 : ErrorsFound = true;
2045 : }
2046 113 : return surfNum;
2047 : }
2048 :
2049 : } // namespace HeatBalanceIntRadExchange
2050 :
2051 : } // namespace EnergyPlus
|