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