LCOV - code coverage report
Current view: top level - EnergyPlus - HeatBalanceIntRadExchange.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 800 1013 79.0 %
Date: 2024-08-23 23:50:59 Functions: 14 15 93.3 %

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

Generated by: LCOV version 1.14