LCOV - code coverage report
Current view: top level - EnergyPlus - HeatBalanceIntRadExchange.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 64.0 % 1027 657
Test Date: 2025-06-02 12:03:30 Functions: 93.3 % 15 14

            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
        

Generated by: LCOV version 2.0-1