LCOV - code coverage report
Current view: top level - EnergyPlus - DataHeatBalance.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 80.2 % 515 413
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 15 15

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <cmath>
      50              : 
      51              : // ObjexxFCL Headers
      52              : #include <ObjexxFCL/Array.functions.hh>
      53              : #include <ObjexxFCL/Fmath.hh>
      54              : 
      55              : // EnergyPlus Headers
      56              : #include <EnergyPlus/Construction.hh>
      57              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      58              : #include <EnergyPlus/DataEnvironment.hh>
      59              : #include <EnergyPlus/DataHeatBalSurface.hh>
      60              : #include <EnergyPlus/DataHeatBalance.hh>
      61              : #include <EnergyPlus/DataSurfaces.hh>
      62              : #include <EnergyPlus/DaylightingManager.hh>
      63              : #include <EnergyPlus/Material.hh>
      64              : #include <EnergyPlus/OutputProcessor.hh>
      65              : #include <EnergyPlus/UtilityRoutines.hh>
      66              : 
      67              : namespace EnergyPlus::DataHeatBalance {
      68              : 
      69              : // MODULE INFORMATION:
      70              : //       AUTHOR         Rick Strand
      71              : //       DATE WRITTEN   August 1997 (rewritten)
      72              : //       MODIFIED       Aug-Oct 1997 RKS (added derived types)
      73              : //       MODIFIED       Feb 1999 (FW) Added visible radiation parameters,
      74              : //                      WindowShadingControl type and SurfaceWindowCalc type
      75              : //                      Sep 1999 (FW) Added/modified Window4 gas variables
      76              : //                      Jul 2003 (CC) Added reference temperature variable for air models
      77              : //                      Aug 2003 (FW) Added FractionReturnAirPlenTempCoeff1 and
      78              : //                      FractionReturnAirPlenTempCoeff2 to Type LightsData
      79              : //                      Nov 2003 (FW) Add FullExteriorWithRefl and FullInteriorExteriorWithRefl
      80              : //                       as SolarDistribution values
      81              : //                      Dec 2003 (PGE) Added Zone List and Zone Group; added SNLoad variables
      82              : //                      August 2006 (COP) Added variable k coefficient and PCM enthalpy.
      83              : //                      Dec 2006 (DJS-PSU) Added ecoroof material
      84              : //                      Dec 2008 TH added new properties to MaterialProperties and
      85              : //                              ConstructionData for thermochromic windows
      86              : //       RE-ENGINEERED  na
      87              : 
      88              : // PURPOSE OF THIS MODULE:
      89              : // This module should contain the information that is needed to pass
      90              : // from the Heat Balance Module and all of the Zone Initializations
      91              : // such as ConductionTransferFunction, GlassCalculation,
      92              : // SolarShading, etc. Modules.
      93              : 
      94              : // Using/Aliasing
      95              : using namespace DataVectorTypes;
      96              : using DataBSDFWindow::BSDFLayerAbsorpStruct;
      97              : using DataBSDFWindow::BSDFWindowInputStruct;
      98              : 
      99              : // Functions
     100              : 
     101       971135 : Real64 SpaceData::sumHATsurf(EnergyPlusData &state)
     102              : {
     103              :     // PURPOSE OF THIS FUNCTION:
     104              :     // This function calculates the space sum of Hc*Area*Tsurf.
     105              : 
     106       971135 :     Real64 sumHATsurf = 0.0;
     107              : 
     108      8013751 :     for (int surfNum = this->HTSurfaceFirst; surfNum <= this->HTSurfaceLast; ++surfNum) {
     109      7042616 :         Real64 Area = state.dataSurface->Surface(surfNum).Area;
     110              : 
     111      7042616 :         if (state.dataSurface->Surface(surfNum).Class == DataSurfaces::SurfaceClass::Window) {
     112       546925 :             if (state.dataSurface->SurfWinDividerArea(surfNum) > 0.0) {
     113            0 :                 if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(surfNum))) {
     114              :                     // The area is the shade or blind area = sum of the glazing area and the divider area (which is zero if no divider)
     115            0 :                     Area += state.dataSurface->SurfWinDividerArea(surfNum);
     116              :                 } else {
     117              :                     // Window divider contribution (only for window with divider and no interior shade or blind)
     118            0 :                     sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * state.dataSurface->SurfWinDividerArea(surfNum) *
     119            0 :                                   (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(surfNum)) * state.dataSurface->SurfWinDividerTempIn(surfNum);
     120              :                 }
     121              :             }
     122              : 
     123       546925 :             if (state.dataSurface->SurfWinFrameArea(surfNum) > 0.0) {
     124              :                 // Window frame contribution
     125            0 :                 sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * state.dataSurface->SurfWinFrameArea(surfNum) *
     126            0 :                               (1.0 + state.dataSurface->SurfWinProjCorrFrIn(surfNum)) * state.dataSurface->SurfWinFrameTempIn(surfNum);
     127              :             }
     128              :         }
     129              : 
     130      7042616 :         sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(surfNum);
     131              :     }
     132              : 
     133       971135 :     return sumHATsurf;
     134              : }
     135              : 
     136       971135 : Real64 ZoneData::sumHATsurf(EnergyPlusData &state)
     137              : {
     138       971135 :     Real64 sumHATsurf = 0.0;
     139      1942270 :     for (int spaceNum : this->spaceIndexes) {
     140       971135 :         sumHATsurf += state.dataHeatBal->space(spaceNum).sumHATsurf(state);
     141       971135 :     }
     142       971135 :     return sumHATsurf;
     143              : }
     144     20539436 : void ZoneData::SetOutBulbTempAt(EnergyPlusData &state)
     145              : {
     146              :     // SUBROUTINE INFORMATION:
     147              :     //       AUTHOR         Noel Keen (LBL)/Linda Lawrie
     148              :     //       DATE WRITTEN   August 2010
     149              :     //       MODIFIED       na
     150              :     //       RE-ENGINEERED  na
     151              : 
     152              :     // PURPOSE OF THIS SUBROUTINE:
     153              :     // Routine provides facility for doing bulk Set Temperature at Height.
     154              : 
     155     20539436 :     if (state.dataEnvrn->SiteTempGradient == 0.0) {
     156            0 :         OutDryBulbTemp = state.dataEnvrn->OutDryBulbTemp;
     157            0 :         OutWetBulbTemp = state.dataEnvrn->OutWetBulbTemp;
     158              :     } else {
     159              :         // Base temperatures at Z = 0 (C)
     160     20539436 :         Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     161     20539436 :         Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     162              : 
     163     20539436 :         Real64 const Z(Centroid.z); // Centroid value
     164     20539436 :         if (Z <= 0.0) {
     165       212881 :             OutDryBulbTemp = BaseDryTemp;
     166       212881 :             OutWetBulbTemp = BaseWetTemp;
     167              :         } else {
     168     20326555 :             OutDryBulbTemp = BaseDryTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
     169     20326555 :             OutWetBulbTemp = BaseWetTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
     170              :         }
     171              :     }
     172     20539436 : }
     173              : 
     174     20534235 : void ZoneData::SetWindSpeedAt(EnergyPlusData const &state, Real64 const fac)
     175              : {
     176              :     // SUBROUTINE INFORMATION:
     177              :     //       AUTHOR         Linda Lawrie
     178              :     //       DATE WRITTEN   June 2013
     179              :     //       MODIFIED       na
     180              :     //       RE-ENGINEERED  na
     181              : 
     182              :     // PURPOSE OF THIS SUBROUTINE:
     183              :     // Routine provides facility for doing bulk Set Windspeed at Height.
     184              : 
     185     20534235 :     if (state.dataEnvrn->SiteWindExp == 0.0) {
     186            0 :         WindSpeed = state.dataEnvrn->WindSpeed;
     187              :     } else {
     188     20534235 :         Real64 const Z(Centroid.z); // Centroid value
     189     20534235 :         if (Z <= 0.0) {
     190       212826 :             WindSpeed = 0.0;
     191              :         } else {
     192              :             //  [Met] - at meterological Station, Height of measurement is usually 10m above ground
     193              :             //  LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
     194              :             //                     * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
     195     20321409 :             WindSpeed = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
     196              :         }
     197              :     }
     198     20534235 : }
     199              : 
     200     20534235 : void ZoneData::SetWindDirAt(Real64 const fac)
     201              : {
     202     20534235 :     WindDir = fac;
     203     20534235 : }
     204              : 
     205         5217 : void AirReportVars::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string const &name)
     206              : {
     207        15651 :     SetupOutputVariable(state,
     208        10434 :                         format("{} Mean Air Temperature", prefix),
     209              :                         Constant::Units::C,
     210         5217 :                         this->MeanAirTemp,
     211              :                         OutputProcessor::TimeStepType::Zone,
     212              :                         OutputProcessor::StoreType::Average,
     213              :                         name);
     214        15651 :     SetupOutputVariable(state,
     215        10434 :                         format("{} Wetbulb Globe Temperature", prefix),
     216              :                         Constant::Units::C,
     217         5217 :                         this->WetbulbGlobeTemp,
     218              :                         OutputProcessor::TimeStepType::Zone,
     219              :                         OutputProcessor::StoreType::Average,
     220              :                         name);
     221        15651 :     SetupOutputVariable(state,
     222        10434 :                         format("{} Operative Temperature", prefix),
     223              :                         Constant::Units::C,
     224         5217 :                         this->OperativeTemp,
     225              :                         OutputProcessor::TimeStepType::Zone,
     226              :                         OutputProcessor::StoreType::Average,
     227              :                         name);
     228        15651 :     SetupOutputVariable(state,
     229        10434 :                         format("{} Mean Air Dewpoint Temperature", prefix),
     230              :                         Constant::Units::C,
     231         5217 :                         this->MeanAirDewPointTemp,
     232              :                         OutputProcessor::TimeStepType::Zone,
     233              :                         OutputProcessor::StoreType::Average,
     234              :                         name);
     235        15651 :     SetupOutputVariable(state,
     236        10434 :                         format("{} Mean Air Humidity Ratio", prefix),
     237              :                         Constant::Units::kgWater_kgDryAir,
     238         5217 :                         this->MeanAirHumRat,
     239              :                         OutputProcessor::TimeStepType::Zone,
     240              :                         OutputProcessor::StoreType::Average,
     241              :                         name);
     242        15651 :     SetupOutputVariable(state,
     243        10434 :                         format("{} Air Heat Balance Internal Convective Heat Gain Rate", prefix),
     244              :                         Constant::Units::W,
     245         5217 :                         this->SumIntGains,
     246              :                         OutputProcessor::TimeStepType::System,
     247              :                         OutputProcessor::StoreType::Average,
     248              :                         name);
     249        15651 :     SetupOutputVariable(state,
     250        10434 :                         format("{} Air Heat Balance Surface Convection Rate", prefix),
     251              :                         Constant::Units::W,
     252         5217 :                         this->SumHADTsurfs,
     253              :                         OutputProcessor::TimeStepType::System,
     254              :                         OutputProcessor::StoreType::Average,
     255              :                         name);
     256        15651 :     SetupOutputVariable(state,
     257        10434 :                         format("{} Air Heat Balance Interzone Air Transfer Rate", prefix),
     258              :                         Constant::Units::W,
     259         5217 :                         this->SumMCpDTzones,
     260              :                         OutputProcessor::TimeStepType::System,
     261              :                         OutputProcessor::StoreType::Average,
     262              :                         name);
     263        15651 :     SetupOutputVariable(state,
     264        10434 :                         format("{} Air Heat Balance Outdoor Air Transfer Rate", prefix),
     265              :                         Constant::Units::W,
     266         5217 :                         this->SumMCpDtInfil,
     267              :                         OutputProcessor::TimeStepType::System,
     268              :                         OutputProcessor::StoreType::Average,
     269              :                         name);
     270        15651 :     SetupOutputVariable(state,
     271        10434 :                         format("{} Air Heat Balance System Air Transfer Rate", prefix),
     272              :                         Constant::Units::W,
     273         5217 :                         this->SumMCpDTsystem,
     274              :                         OutputProcessor::TimeStepType::System,
     275              :                         OutputProcessor::StoreType::Average,
     276              :                         name);
     277        15651 :     SetupOutputVariable(state,
     278        10434 :                         format("{} Air Heat Balance System Convective Heat Gain Rate", prefix),
     279              :                         Constant::Units::W,
     280         5217 :                         this->SumNonAirSystem,
     281              :                         OutputProcessor::TimeStepType::System,
     282              :                         OutputProcessor::StoreType::Average,
     283              :                         name);
     284        15651 :     SetupOutputVariable(state,
     285        10434 :                         format("{} Air Heat Balance Air Energy Storage Rate", prefix),
     286              :                         Constant::Units::W,
     287         5217 :                         this->CzdTdt,
     288              :                         OutputProcessor::TimeStepType::System,
     289              :                         OutputProcessor::StoreType::Average,
     290              :                         name);
     291         5217 :     if (state.dataGlobal->DisplayAdvancedReportVariables) {
     292          180 :         SetupOutputVariable(state,
     293          120 :                             format("{} Air Heat Balance Deviation Rate", prefix),
     294              :                             Constant::Units::W,
     295           60 :                             this->imBalance,
     296              :                             OutputProcessor::TimeStepType::System,
     297              :                             OutputProcessor::StoreType::Average,
     298              :                             name);
     299              :     }
     300         5217 : }
     301              : 
     302      2829013 : void SetZoneOutBulbTempAt(EnergyPlusData &state)
     303              : {
     304     23368449 :     for (auto &zone : state.dataHeatBal->Zone) {
     305     20539436 :         zone.SetOutBulbTempAt(state);
     306      2829013 :     }
     307      2829013 : }
     308              : 
     309      2829013 : void CheckZoneOutBulbTempAt(EnergyPlusData &state)
     310              : {
     311              :     // Using/Aliasing
     312              :     using DataEnvironment::SetOutBulbTempAt_error;
     313              : 
     314      2829013 :     Real64 minBulb = 0.0;
     315     23368449 :     for (auto &zone : state.dataHeatBal->Zone) {
     316     20539436 :         minBulb = min(minBulb, zone.OutDryBulbTemp, zone.OutWetBulbTemp);
     317     20539436 :         if (minBulb < -100.0) {
     318            0 :             SetOutBulbTempAt_error(state, "Zone", zone.Centroid.z, zone.Name);
     319              :         }
     320      2829013 :     }
     321      2829013 : }
     322              : 
     323      2828212 : void SetZoneWindSpeedAt(EnergyPlusData &state)
     324              : {
     325      2828212 :     Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
     326      2828212 :                      std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
     327     23362447 :     for (auto &zone : state.dataHeatBal->Zone) {
     328     20534235 :         zone.SetWindSpeedAt(state, fac);
     329      2828212 :     }
     330      2828212 : }
     331              : 
     332      2828212 : void SetZoneWindDirAt(EnergyPlusData &state)
     333              : {
     334              :     // Using/Aliasing
     335      2828212 :     Real64 const fac(state.dataEnvrn->WindDir);
     336     23362447 :     for (auto &zone : state.dataHeatBal->Zone) {
     337     20534235 :         zone.SetWindDirAt(fac);
     338      2828212 :     }
     339      2828212 : }
     340              : 
     341         6062 : void CheckAndSetConstructionProperties(EnergyPlusData &state,
     342              :                                        int const ConstrNum, // Construction number to be set/checked
     343              :                                        bool &ErrorsFound    // error flag that is set when certain errors have occurred
     344              : )
     345              : {
     346              : 
     347              :     // SUBROUTINE INFORMATION:
     348              :     //       AUTHOR         Linda Lawrie
     349              :     //       DATE WRITTEN   December 2006
     350              : 
     351              :     // This routine checks some properties of entered constructions; sets some properties; and sets
     352              :     // an error flag for certain error conditions.
     353         6062 :     auto &s_mat = state.dataMaterial;
     354              : 
     355         6062 :     auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     356         6062 :     int TotLayers = thisConstruct.TotLayers; // Number of layers in a construction
     357         6062 :     if (TotLayers == 0) {
     358            0 :         return; // error condition, hopefully caught elsewhere
     359              :     }
     360         6062 :     int InsideLayer = TotLayers; // Inside Layer of Construct; for window construct, layer no. of inside glass
     361         6062 :     if (thisConstruct.LayerPoint(InsideLayer) <= 0) {
     362            0 :         return; // Error condition
     363              :     }
     364              : 
     365              :     //   window screen is not allowed on inside layer
     366              : 
     367         6062 :     thisConstruct.DayltPropPtr = 0;
     368         6062 :     int InsideMaterNum = thisConstruct.LayerPoint(InsideLayer); // Material "number" of the Inside layer
     369         6062 :     if (InsideMaterNum != 0) {
     370         6062 :         auto const *mat = s_mat->materials(InsideMaterNum);
     371         6062 :         thisConstruct.InsideAbsorpVis = mat->AbsorpVisible;
     372         6062 :         thisConstruct.InsideAbsorpSolar = mat->AbsorpSolar;
     373              : 
     374              :         // Following line applies only to opaque surfaces; it is recalculated later for windows.
     375         6062 :         thisConstruct.ReflectVisDiffBack = 1.0 - mat->AbsorpVisible;
     376              :     }
     377              : 
     378         6062 :     int OutsideMaterNum = thisConstruct.LayerPoint(1); // Material "number" of the Outside layer
     379         6062 :     if (OutsideMaterNum != 0) {
     380         6062 :         auto const *mat = s_mat->materials(OutsideMaterNum);
     381         6062 :         thisConstruct.OutsideAbsorpVis = mat->AbsorpVisible;
     382         6062 :         thisConstruct.OutsideAbsorpSolar = mat->AbsorpSolar;
     383              :     }
     384              : 
     385         6062 :     thisConstruct.TotSolidLayers = 0;
     386         6062 :     thisConstruct.TotGlassLayers = 0;
     387         6062 :     thisConstruct.AbsDiffShade = 0.0;
     388              : 
     389              :     // Check if any layer is glass, gas, shade, screen or blind; if so it is considered a window construction for
     390              :     // purposes of error checking.
     391              : 
     392         6062 :     thisConstruct.TypeIsWindow = false;
     393        20279 :     for (int Layer = 1; Layer <= TotLayers; ++Layer) {
     394        14217 :         int const MaterNum = thisConstruct.LayerPoint(Layer);
     395        14217 :         if (MaterNum == 0) {
     396            0 :             continue; // error -- has been caught will stop program later
     397              :         }
     398        14217 :         auto const *mat = s_mat->materials(MaterNum);
     399        14217 :         thisConstruct.TypeIsWindow =
     400        12368 :             (mat->group == Material::Group::Glass || mat->group == Material::Group::Gas || mat->group == Material::Group::GasMixture ||
     401        11742 :              mat->group == Material::Group::Shade || mat->group == Material::Group::Blind || mat->group == Material::Group::Screen ||
     402        11710 :              mat->group == Material::Group::GlassSimple || mat->group == Material::Group::ComplexShade ||
     403        11569 :              mat->group == Material::Group::ComplexWindowGap || mat->group == Material::Group::GlassEQL || mat->group == Material::Group::ShadeEQL ||
     404        38119 :              mat->group == Material::Group::DrapeEQL || mat->group == Material::Group::ScreenEQL || mat->group == Material::Group::BlindEQL ||
     405        11534 :              mat->group == Material::Group::WindowGapEQL);
     406        14217 :         bool TypeIsNotWindow =
     407        14217 :             (mat->group == Material::Group::Invalid || mat->group == Material::Group::AirGap || mat->group == Material::Group::Regular ||
     408        28434 :              mat->group == Material::Group::EcoRoof || mat->group == Material::Group::IRTransparent);
     409        14217 :         if (!thisConstruct.TypeIsWindow && !TypeIsNotWindow) {
     410            0 :             assert(false);
     411              :         }
     412              :     }
     413              : 
     414         6062 :     if (InsideMaterNum == 0) {
     415            0 :         return;
     416              :     }
     417         6062 :     auto const *matInside = s_mat->materials(InsideMaterNum);
     418         6062 :     if (OutsideMaterNum == 0) {
     419            0 :         return;
     420              :     }
     421         6062 :     auto const *matOutside = s_mat->materials(OutsideMaterNum);
     422              : 
     423         6062 :     if (thisConstruct.TypeIsWindow) {
     424              : 
     425         1348 :         bool WrongMaterialsMix = false;
     426         1348 :         thisConstruct.NumCTFTerms = 0;
     427         1348 :         thisConstruct.NumHistories = 0;
     428         4039 :         for (int Layer = 1; Layer <= TotLayers; ++Layer) {
     429         2691 :             int const MaterNum = thisConstruct.LayerPoint(Layer);
     430         2691 :             if (MaterNum == 0) {
     431            0 :                 continue; // error -- has been caught will stop program later
     432              :             }
     433         2691 :             auto const *mat = s_mat->materials(MaterNum);
     434         2691 :             WrongMaterialsMix =
     435         2907 :                 !((mat->group == Material::Group::Glass) || (mat->group == Material::Group::Gas) || (mat->group == Material::Group::GasMixture) ||
     436          216 :                   (mat->group == Material::Group::Shade) || (mat->group == Material::Group::Blind) || (mat->group == Material::Group::Screen) ||
     437          184 :                   (mat->group == Material::Group::GlassSimple) || (mat->group == Material::Group::ComplexShade) ||
     438           43 :                   (mat->group == Material::Group::ComplexWindowGap) || (mat->group == Material::Group::GlassEQL) ||
     439           11 :                   (mat->group == Material::Group::ShadeEQL) || (mat->group == Material::Group::DrapeEQL) ||
     440            9 :                   (mat->group == Material::Group::ScreenEQL) || (mat->group == Material::Group::BlindEQL) ||
     441            8 :                   (mat->group == Material::Group::WindowGapEQL));
     442              :         }
     443              : 
     444         1348 :         if (WrongMaterialsMix) { // Illegal material for a window construction
     445            0 :             ShowSevereError(state,
     446            0 :                             format("Error: Window construction={} has materials other than glass, gas, shade, screen, blind, complex shading, "
     447              :                                    "complex gap, or simple system.",
     448            0 :                                    thisConstruct.Name));
     449            0 :             ErrorsFound = true;
     450              :             // Do not check number of layers for BSDF type of window since that can be handled
     451         1348 :         } else if ((TotLayers > 8) && (!thisConstruct.WindowTypeBSDF) &&
     452            1 :                    (!thisConstruct.WindowTypeEQL)) { // Too many layers for a window construction
     453            0 :             ShowSevereError(state,
     454            0 :                             format("CheckAndSetConstructionProperties: Window construction={} has too many layers (max of 8 allowed -- 4 glass + 3 "
     455              :                                    "gap + 1 shading device).",
     456            0 :                                    thisConstruct.Name));
     457            0 :             ErrorsFound = true;
     458              : 
     459         1348 :         } else if (TotLayers == 1) {
     460          750 :             auto const *mat = s_mat->materials(thisConstruct.LayerPoint(1));
     461          750 :             Material::Group matGroup = mat->group;
     462          750 :             if ((matGroup == Material::Group::Shade) || (matGroup == Material::Group::Gas) || (matGroup == Material::Group::GasMixture) ||
     463          750 :                 (matGroup == Material::Group::Blind) || (matGroup == Material::Group::Screen) || (matGroup == Material::Group::ComplexShade) ||
     464              :                 (matGroup == Material::Group::ComplexWindowGap)) {
     465            0 :                 ShowSevereError(state,
     466            0 :                                 format("CheckAndSetConstructionProperties: The single-layer window construction={} has a gas, complex gap, shade, "
     467              :                                        "complex shade, screen or blind material; it should be glass of simple glazing system.",
     468            0 :                                        thisConstruct.Name));
     469            0 :                 ErrorsFound = true;
     470              :             }
     471              :         }
     472              : 
     473              :         // Find total glass layers, total shade/blind layers and total gas layers in a window construction
     474              : 
     475         1348 :         bool WrongWindowLayering = false;
     476         1348 :         int TotGlassLayers = 0;
     477         1348 :         int TotShadeLayers = 0; // Includes shades, blinds, and screens
     478         1348 :         int TotGasLayers = 0;
     479         4039 :         for (int Layer = 1; Layer <= TotLayers; ++Layer) {
     480         2691 :             int const MaterNum = thisConstruct.LayerPoint(Layer);
     481         2691 :             if (MaterNum == 0) {
     482            0 :                 continue; // error -- has been caught will stop program later
     483              :             }
     484         2691 :             auto const *mat = s_mat->materials(MaterNum);
     485         2691 :             if (mat->group == Material::Group::Glass) {
     486         1849 :                 ++TotGlassLayers;
     487              :             }
     488         2691 :             if (mat->group == Material::Group::GlassSimple) {
     489          132 :                 ++TotGlassLayers;
     490              :             }
     491         2691 :             if (mat->group == Material::Group::Shade || mat->group == Material::Group::Blind || mat->group == Material::Group::Screen ||
     492         2659 :                 mat->group == Material::Group::ComplexShade) {
     493           41 :                 ++TotShadeLayers;
     494              :             }
     495         2691 :             if (mat->group == Material::Group::Gas || mat->group == Material::Group::GasMixture || mat->group == Material::Group::ComplexWindowGap) {
     496          650 :                 ++TotGasLayers;
     497              :             }
     498         2691 :             if (Layer < TotLayers) {
     499         1343 :                 int const MaterNumNext = thisConstruct.LayerPoint(Layer + 1);
     500              :                 // Adjacent layers of same type not allowed
     501         1343 :                 if (MaterNumNext == 0) {
     502            0 :                     continue;
     503              :                 }
     504         1343 :                 if (mat->group == s_mat->materials(MaterNumNext)->group) {
     505            0 :                     WrongWindowLayering = true;
     506              :                 }
     507              :             }
     508              :         }
     509              : 
     510              :         // It is not necessary to check rest of BSDF window structure since that is performed inside TARCOG90 routine.
     511              :         // That routine also allow structures which are not allowed in rest of this routine
     512         1348 :         if (thisConstruct.WindowTypeBSDF) {
     513           14 :             thisConstruct.TotGlassLayers = TotGlassLayers;
     514           14 :             thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
     515           14 :             thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
     516           14 :             thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
     517           14 :             return;
     518              :         }
     519              : 
     520         1334 :         if (thisConstruct.WindowTypeEQL) {
     521            3 :             thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
     522            3 :             thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
     523            3 :             return;
     524              :         }
     525              : 
     526         1331 :         if (matOutside->group == Material::Group::Gas || matOutside->group == Material::Group::GasMixture ||
     527         1331 :             matInside->group == Material::Group::Gas || matInside->group == Material::Group::GasMixture) {
     528            0 :             WrongWindowLayering = true; // Gas cannot be first or last layer
     529              :         }
     530         1331 :         if (TotShadeLayers > 1) {
     531            0 :             WrongWindowLayering = true; // At most one shade, screen or blind allowed
     532              :         }
     533              : 
     534              :         // If there is a diffusing glass layer no shade, screen or blind is allowed
     535         3941 :         for (int Layer = 1; Layer <= TotLayers; ++Layer) {
     536         2610 :             int const MatNum = thisConstruct.LayerPoint(Layer);
     537         2610 :             if (MatNum == 0) {
     538            0 :                 continue; // error -- has been caught will stop program later
     539              :             }
     540         2610 :             auto const *mat = s_mat->materials(MatNum);
     541         2610 :             if (mat->group != Material::Group::Glass) {
     542          790 :                 continue;
     543              :             }
     544         1820 :             auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
     545         1820 :             assert(matGlass != nullptr);
     546         1820 :             if (matGlass->SolarDiffusing && TotShadeLayers > 0) {
     547            0 :                 ErrorsFound = true;
     548            0 :                 ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
     549            0 :                 ShowContinueError(state, format("has diffusing glass={} and a shade, screen or blind layer.", matGlass->Name));
     550            0 :                 break;
     551              :             }
     552              :         }
     553              : 
     554              :         // If there is a diffusing glass layer it must be the innermost layer
     555         1331 :         if (TotGlassLayers > 1) {
     556          561 :             int GlassLayNum = 0;
     557         2381 :             for (int Layer = 1; Layer <= TotLayers; ++Layer) {
     558         1820 :                 int const MatNum = thisConstruct.LayerPoint(Layer);
     559         1820 :                 if (MatNum == 0) {
     560            0 :                     continue; // error -- has been caught will stop program later
     561              :                 }
     562         1820 :                 auto const *mat = s_mat->materials(MatNum);
     563         1820 :                 if (mat->group != Material::Group::Glass) {
     564          638 :                     continue;
     565              :                 }
     566              : 
     567         1182 :                 auto const *matGlass = dynamic_cast<Material::MaterialGlass const *>(mat);
     568         1182 :                 assert(matGlass != nullptr);
     569         1182 :                 ++GlassLayNum;
     570         1182 :                 if (GlassLayNum < TotGlassLayers && matGlass->SolarDiffusing) {
     571            0 :                     ErrorsFound = true;
     572            0 :                     ShowSevereError(state, format("CheckAndSetConstructionProperties: Window construction={}", thisConstruct.Name));
     573            0 :                     ShowContinueError(state, format("has diffusing glass={} that is not the innermost glass layer.", matGlass->Name));
     574              :                 }
     575              :             }
     576              :         }
     577              : 
     578              :         // interior window screen is not allowed. Check for invalid between-glass screen is checked below.
     579         1331 :         if (TotShadeLayers == 1 && matInside->group == Material::Group::Screen && TotLayers != 1) {
     580            0 :             WrongWindowLayering = true;
     581              :         }
     582              : 
     583              :         // Consistency checks for a construction with a between-glass shade or blind
     584              : 
     585         1331 :         if (TotShadeLayers == 1 && matOutside->group != Material::Group::Shade && matOutside->group != Material::Group::Blind &&
     586           27 :             matOutside->group != Material::Group::Screen && matInside->group != Material::Group::Shade &&
     587           18 :             matInside->group != Material::Group::Blind && matInside->group != Material::Group::ComplexShade && !WrongWindowLayering) {
     588              : 
     589              :             // This is a construction with a between-glass shade or blind
     590              : 
     591            5 :             if (TotGlassLayers >= 4) {
     592              :                 // Quadruple pane not allowed.
     593            0 :                 WrongWindowLayering = true;
     594            5 :             } else if (TotGlassLayers == 2 || TotGlassLayers == 3) {
     595            5 :                 bool ValidBGShadeBlindConst = false;
     596            5 :                 if (TotGlassLayers == 2) {
     597            3 :                     if (TotLayers != 5) {
     598            0 :                         WrongWindowLayering = true;
     599              :                     } else {
     600            9 :                         if (matOutside->group == Material::Group::Glass &&
     601            3 :                             (s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::Gas ||
     602            0 :                              s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::GasMixture) &&
     603            3 :                             ((s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Shade ||
     604            2 :                               s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Blind) &&
     605            3 :                              s_mat->materials(thisConstruct.LayerPoint(3))->group != Material::Group::Screen) &&
     606            3 :                             (s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::Gas ||
     607            6 :                              s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::GasMixture) &&
     608            3 :                             s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Glass) {
     609            3 :                             ValidBGShadeBlindConst = true;
     610              :                         }
     611              :                     }
     612              :                 } else { // TotGlassLayers = 3
     613            2 :                     if (TotLayers != 7) {
     614            0 :                         WrongWindowLayering = true;
     615              :                     } else {
     616            6 :                         if (matOutside->group == Material::Group::Glass &&
     617            2 :                             (s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::Gas ||
     618            0 :                              s_mat->materials(thisConstruct.LayerPoint(2))->group == Material::Group::GasMixture) &&
     619            2 :                             s_mat->materials(thisConstruct.LayerPoint(3))->group == Material::Group::Glass &&
     620            2 :                             (s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::Gas ||
     621            0 :                              s_mat->materials(thisConstruct.LayerPoint(4))->group == Material::Group::GasMixture) &&
     622            2 :                             ((s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Shade ||
     623            1 :                               s_mat->materials(thisConstruct.LayerPoint(5))->group == Material::Group::Blind) &&
     624            2 :                              s_mat->materials(thisConstruct.LayerPoint(5))->group != Material::Group::Screen) &&
     625            2 :                             (s_mat->materials(thisConstruct.LayerPoint(6))->group == Material::Group::Gas ||
     626            4 :                              s_mat->materials(thisConstruct.LayerPoint(6))->group == Material::Group::GasMixture) &&
     627            2 :                             s_mat->materials(thisConstruct.LayerPoint(7))->group == Material::Group::Glass) {
     628            2 :                             ValidBGShadeBlindConst = true;
     629              :                         }
     630              :                     }
     631              :                 } // End of check if TotGlassLayers = 2 or 3
     632            5 :                 if (!ValidBGShadeBlindConst) {
     633            0 :                     WrongWindowLayering = true;
     634              :                 }
     635            5 :                 if (!WrongWindowLayering) {
     636            5 :                     int const LayNumSh = 2 * TotGlassLayers - 1;
     637            5 :                     int const MatSh = thisConstruct.LayerPoint(LayNumSh);
     638            5 :                     auto const *matSh = s_mat->materials(MatSh);
     639              :                     // For double pane, shade/blind must be layer #3.
     640              :                     // For triple pane, it must be layer #5 (i.e., between two inner panes).
     641            5 :                     if (matSh->group != Material::Group::Shade && matSh->group != Material::Group::Blind) {
     642            0 :                         WrongWindowLayering = true;
     643              :                     }
     644            5 :                     if (TotLayers != 2 * TotGlassLayers + 1) {
     645            0 :                         WrongWindowLayering = true;
     646              :                     }
     647            5 :                     if (!WrongWindowLayering) {
     648              :                         // Gas on either side of a between-glass shade/blind must be the same
     649            5 :                         int const MatGapL = thisConstruct.LayerPoint(LayNumSh - 1);
     650            5 :                         int const MatGapR = thisConstruct.LayerPoint(LayNumSh + 1);
     651            5 :                         auto const *matGapL = dynamic_cast<const Material::MaterialGasMix *>(s_mat->materials(MatGapL));
     652            5 :                         auto const *matGapR = dynamic_cast<const Material::MaterialGasMix *>(s_mat->materials(MatGapR));
     653           30 :                         for (int IGas = 0; IGas < Material::maxMixGases; ++IGas) {
     654           25 :                             if ((matGapL->gases[IGas].type != matGapR->gases[IGas].type) || (matGapL->gasFracts[IGas] != matGapR->gasFracts[IGas])) {
     655            0 :                                 WrongWindowLayering = true;
     656              :                             }
     657              :                         }
     658              :                         // Gap width on either side of a between-glass shade/blind must be the same
     659            5 :                         if (std::abs(matGapL->Thickness - matGapR->Thickness) > 0.0005) {
     660            0 :                             WrongWindowLayering = true;
     661              :                         }
     662            5 :                         if (matSh->group == Material::Group::Blind) {
     663            3 :                             auto const *matBlind = dynamic_cast<Material::MaterialBlind const *>(matSh);
     664            3 :                             assert(matBlind != nullptr);
     665            3 :                             if ((matGapL->Thickness + matGapR->Thickness) < matBlind->SlatWidth) {
     666            0 :                                 ErrorsFound = true;
     667            0 :                                 ShowSevereError(state, format("CheckAndSetConstructionProperties: For window construction {}", thisConstruct.Name));
     668            0 :                                 ShowContinueError(state, "the slat width of the between-glass blind is greater than");
     669            0 :                                 ShowContinueError(state, "the sum of the widths of the gas layers adjacent to the blind.");
     670              :                             }
     671              :                         } // End of check if material is window blind
     672              :                     } // End of check if WrongWindowLayering
     673              :                 } // End of check if WrongWindowLayering
     674              :             } // End of check on total glass layers
     675              :         } // End of check if construction has between-glass shade/blind
     676              : 
     677              :         // Check Simple Windows,
     678         1331 :         if (s_mat->materials(thisConstruct.LayerPoint(1))->group == Material::Group::GlassSimple) {
     679          131 :             if (TotLayers > 1) {
     680              :                 // check that none of the other layers are glazing or gas
     681           15 :                 for (int Layer = 1; Layer <= TotLayers; ++Layer) {
     682           10 :                     int const MaterNum = thisConstruct.LayerPoint(Layer);
     683           10 :                     if (MaterNum == 0) {
     684            0 :                         continue; // error -- has been caught will stop program later
     685              :                     }
     686           10 :                     auto const *mat = s_mat->materials(MaterNum);
     687           10 :                     if (mat->group == Material::Group::Glass) {
     688            0 :                         ErrorsFound = true;
     689            0 :                         ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
     690            0 :                         ShowContinueError(state, "For simple window constructions, no other glazing layers are allowed.");
     691              :                     }
     692           10 :                     if (mat->group == Material::Group::Gas) {
     693            0 :                         ErrorsFound = true;
     694            0 :                         ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
     695            0 :                         ShowContinueError(state, "For simple window constructions, no other gas layers are allowed.");
     696              :                     }
     697              :                 }
     698              :             }
     699              :         }
     700              : 
     701         1331 :         if (WrongWindowLayering) {
     702            0 :             ShowSevereError(state, format("CheckAndSetConstructionProperties: Error in window construction {}--", thisConstruct.Name));
     703            0 :             ShowContinueError(state, "  For multi-layer window constructions the following rules apply:");
     704            0 :             ShowContinueError(state, "    --The first and last layer must be a solid layer (glass or shade/screen/blind),");
     705            0 :             ShowContinueError(state, "    --Adjacent glass layers must be separated by one and only one gas layer,");
     706            0 :             ShowContinueError(state, "    --Adjacent layers must not be of the same type,");
     707            0 :             ShowContinueError(state, "    --Only one shade/screen/blind layer is allowed,");
     708            0 :             ShowContinueError(state, "    --An exterior shade/screen/blind must be the first layer,");
     709            0 :             ShowContinueError(state, "    --An interior shade/blind must be the last layer,");
     710            0 :             ShowContinueError(state, "    --An interior screen is not allowed,");
     711            0 :             ShowContinueError(state, "    --For an exterior shade/screen/blind or interior shade/blind, there should not be a gas layer");
     712            0 :             ShowContinueError(state, "    ----between the shade/screen/blind and adjacent glass,");
     713            0 :             ShowContinueError(state, "    --A between-glass screen is not allowed,");
     714            0 :             ShowContinueError(state, "    --A between-glass shade/blind is allowed only for double and triple glazing,");
     715            0 :             ShowContinueError(state, "    --A between-glass shade/blind must have adjacent gas layers of the same type and width,");
     716            0 :             ShowContinueError(state, "    --For triple glazing the between-glass shade/blind must be between the two inner glass layers,");
     717            0 :             ShowContinueError(state, "    --The slat width of a between-glass blind must be less than the sum of the widths");
     718            0 :             ShowContinueError(state, "    ----of the gas layers adjacent to the blind.");
     719            0 :             ErrorsFound = true;
     720              :         }
     721              : 
     722         1331 :         thisConstruct.TotGlassLayers = TotGlassLayers;
     723         1331 :         thisConstruct.TotSolidLayers = TotGlassLayers + TotShadeLayers;
     724              : 
     725              :         // In following, InsideLayer is layer number of inside glass and InsideAbsorpThermal applies
     726              :         // only to inside glass; it is corrected later in InitGlassOpticalCalculations
     727              :         // if construction has inside shade or blind.
     728         1331 :         if (matInside->group == Material::Group::Shade || matInside->group == Material::Group::Blind) {
     729           20 :             --InsideLayer;
     730              :         }
     731         1331 :         if (InsideLayer > 0) {
     732         1331 :             InsideMaterNum = thisConstruct.LayerPoint(InsideLayer);
     733         1331 :             thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermalBack;
     734              :         }
     735         1331 :         if (InsideMaterNum != 0) {
     736         1331 :             auto const *thisInsideMaterial = s_mat->materials(InsideMaterNum);
     737         1331 :             thisConstruct.InsideAbsorpVis = thisInsideMaterial->AbsorpVisible;
     738         1331 :             thisConstruct.InsideAbsorpSolar = thisInsideMaterial->AbsorpSolar;
     739              :         }
     740              : 
     741         1331 :         if ((matOutside->group == Material::Group::Glass) || (matOutside->group == Material::Group::GlassSimple)) { // Glass
     742         1324 :             thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermalFront;
     743              :         } else { // Exterior shade, blind or screen
     744            7 :             thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermal;
     745              :         }
     746              : 
     747              :     } else { // Opaque surface
     748         4714 :         thisConstruct.InsideAbsorpThermal = matInside->AbsorpThermal;
     749         4714 :         thisConstruct.OutsideAbsorpThermal = matOutside->AbsorpThermal;
     750              :     }
     751              : 
     752         6045 :     thisConstruct.OutsideRoughness = matOutside->Roughness;
     753              : 
     754         6045 :     if (matOutside->group == Material::Group::AirGap) {
     755            0 :         ShowSevereError(state, format("CheckAndSetConstructionProperties: Outside Layer is Air for construction {}", thisConstruct.Name));
     756            0 :         ShowContinueError(state, format("  Error in material {}", matOutside->Name));
     757            0 :         ErrorsFound = true;
     758              :     }
     759         6045 :     if (InsideLayer > 0) {
     760         6045 :         if (matInside->group == Material::Group::AirGap) {
     761            0 :             ShowSevereError(state, format("CheckAndSetConstructionProperties: Inside Layer is Air for construction {}", thisConstruct.Name));
     762            0 :             ShowContinueError(state, format("  Error in material {}", matInside->Name));
     763            0 :             ErrorsFound = true;
     764              :         }
     765              :     }
     766              : 
     767         6045 :     if (matOutside->group == Material::Group::EcoRoof) {
     768            4 :         thisConstruct.TypeIsEcoRoof = true;
     769              :         // need to check EcoRoof is not non-outside layer
     770           12 :         for (int Layer = 2; Layer <= TotLayers; ++Layer) {
     771            8 :             if (s_mat->materials(thisConstruct.LayerPoint(Layer))->group == Material::Group::EcoRoof) {
     772            0 :                 ShowSevereError(state,
     773            0 :                                 format("CheckAndSetConstructionProperties: Interior Layer is EcoRoof for construction {}", thisConstruct.Name));
     774            0 :                 ShowContinueError(state, format("  Error in material {}", s_mat->materials(thisConstruct.LayerPoint(Layer))->Name));
     775            0 :                 ErrorsFound = true;
     776              :             }
     777              :         }
     778              :     }
     779              : 
     780         6045 :     if (matOutside->group == Material::Group::IRTransparent) {
     781            2 :         thisConstruct.TypeIsIRT = true;
     782            2 :         if (thisConstruct.TotLayers != 1) {
     783            0 :             ShowSevereError(
     784              :                 state,
     785            0 :                 format("CheckAndSetConstructionProperties: Infrared Transparent (IRT) Construction is limited to 1 layer {}", thisConstruct.Name));
     786            0 :             ShowContinueError(state, "  Too many layers in referenced construction.");
     787            0 :             ErrorsFound = true;
     788              :         }
     789              :     }
     790              : }
     791              : 
     792          370 : int AssignReverseConstructionNumber(EnergyPlusData &state,
     793              :                                     int const ConstrNum, // Existing Construction number of first surface
     794              :                                     bool &ErrorsFound)
     795              : {
     796              : 
     797              :     // FUNCTION INFORMATION:
     798              :     //       AUTHOR         Linda Lawrie
     799              :     //       DATE WRITTEN   December 2006
     800              : 
     801              :     // PURPOSE OF THIS FUNCTION:
     802              :     // For interzone, unentered surfaces, we need to have "reverse" constructions
     803              :     // assigned to the created surfaces.  These need to be the reverse (outside to inside layer)
     804              :     // of existing surfaces.  Plus, there may be one already in the data structure so this is looked for as well.
     805              : 
     806              :     // METHODOLOGY EMPLOYED:
     807              :     // Create reverse layers.  Look in current constructions to see if match.  If no match, create a new one.
     808              : 
     809          370 :     auto &s_mat = state.dataMaterial;
     810              :     // Return value
     811              :     int NewConstrNum; // Reverse Construction Number
     812              : 
     813          370 :     if (ConstrNum == 0) {
     814              :         // error caught elsewhere
     815            0 :         NewConstrNum = 0;
     816            0 :         return NewConstrNum;
     817              :     }
     818              : 
     819          370 :     auto &thisConstruct = state.dataConstruction->Construct(ConstrNum);
     820          370 :     thisConstruct.IsUsed = true;
     821          370 :     int nLayer = 0;
     822          370 :     state.dataConstruction->LayerPoint = 0;
     823         1115 :     for (int Loop = thisConstruct.TotLayers; Loop >= 1; --Loop) {
     824          745 :         ++nLayer;
     825          745 :         state.dataConstruction->LayerPoint(nLayer) = thisConstruct.LayerPoint(Loop);
     826              :     }
     827              : 
     828              :     // now, got thru and see if there is a match already....
     829          370 :     NewConstrNum = 0;
     830         2232 :     for (int Loop = 1; Loop <= state.dataHeatBal->TotConstructs; ++Loop) {
     831         2227 :         bool Found = true;
     832         6249 :         for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
     833         5884 :             if (state.dataConstruction->Construct(Loop).LayerPoint(nLayer) != state.dataConstruction->LayerPoint(nLayer)) {
     834         1862 :                 Found = false;
     835         1862 :                 break;
     836              :             }
     837              :         }
     838         2227 :         if (Found) {
     839          365 :             NewConstrNum = Loop;
     840          365 :             state.dataConstruction->Construct(Loop).IsUsed = true;
     841          365 :             break;
     842              :         }
     843              :     }
     844              : 
     845              :     // if need new one, bunch o stuff
     846          370 :     if (NewConstrNum == 0) {
     847            5 :         ++state.dataHeatBal->TotConstructs;
     848            5 :         state.dataConstruction->Construct.redimension(state.dataHeatBal->TotConstructs);
     849            5 :         state.dataHeatBal->NominalRforNominalUCalculation.redimension(state.dataHeatBal->TotConstructs);
     850            5 :         state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) = 0.0;
     851            5 :         state.dataHeatBal->NominalU.redimension(state.dataHeatBal->TotConstructs);
     852            5 :         state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) = 0.0;
     853            5 :         state.dataHeatBal->NominalUBeforeAdjusted.redimension(state.dataHeatBal->TotConstructs);
     854            5 :         state.dataHeatBal->NominalUBeforeAdjusted(state.dataHeatBal->TotConstructs) = 0.0;
     855            5 :         state.dataHeatBal->CoeffAdjRatio.redimension(state.dataHeatBal->TotConstructs) = 1.0;
     856              :         //  Put in new attributes
     857            5 :         NewConstrNum = state.dataHeatBal->TotConstructs;
     858            5 :         state.dataConstruction->Construct(NewConstrNum).IsUsed = true;
     859            5 :         state.dataConstruction->Construct(state.dataHeatBal->TotConstructs) =
     860           10 :             state.dataConstruction->Construct(ConstrNum); // preserve some of the attributes.
     861              :         // replace others...
     862            5 :         state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).Name = "iz-" + state.dataConstruction->Construct(ConstrNum).Name;
     863            5 :         state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
     864           60 :         for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
     865           55 :             state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).LayerPoint(nLayer) = state.dataConstruction->LayerPoint(nLayer);
     866           55 :             if (state.dataConstruction->LayerPoint(nLayer) != 0) {
     867           16 :                 state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) +=
     868           16 :                     s_mat->materials(state.dataConstruction->LayerPoint(nLayer))->NominalR;
     869              :             }
     870              :         }
     871              : 
     872              :         // no error if zero -- that will have been caught with earlier construction
     873              :         // the following line was changed to fix CR7601
     874            5 :         if (state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) != 0.0) {
     875            5 :             state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) =
     876            5 :                 1.0 / state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs);
     877              :         }
     878              : 
     879            5 :         CheckAndSetConstructionProperties(state, state.dataHeatBal->TotConstructs, ErrorsFound);
     880              :     }
     881              : 
     882          370 :     return NewConstrNum;
     883              : }
     884              : 
     885        45600 : Real64 ComputeNominalUwithConvCoeffs(EnergyPlusData &state,
     886              :                                      int const numSurf, // index for Surface array.
     887              :                                      bool &isValid      // returns true if result is valid
     888              : )
     889              : {
     890              : 
     891              :     // SUBROUTINE INFORMATION:
     892              :     //       AUTHOR         Jason Glazer
     893              :     //       DATE WRITTEN   September 2013
     894              :     //       MODIFIED       na
     895              :     //       RE-ENGINEERED  na
     896              : 
     897              :     // PURPOSE OF THIS SUBROUTINE:
     898              :     // Calculate Nominal U-value with convection/film coefficients for reporting by
     899              :     // adding on prescribed R-values for interior and exterior convection coefficients
     900              :     // as found in ASHRAE 90.1-2004, Appendix A. Used in EIO and tabular reports.
     901              :     // ASHRAE 90.1-2004 Section A9.4.1 shows the following:
     902              :     //      R-value Condition
     903              :     //      All exterior conditions                        IP: 0.17  SI: 0.0299
     904              :     //      All semi-exterior surfaces                     IP: 0.46  SI: 0.0810
     905              :     //      Interior horizontal surfaces, heat flow up     IP: 0.61  SI: 0.1074
     906              :     //      Interior horizontal surfaces, heat flow down   IP: 0.92  SI: 0.1620
     907              :     //      Interior vertical surfaces                     IP: 0.68  SI: 0.1198
     908              :     // This section shows the same value in 90.1-2010 and 90.2-2010
     909              :     // Note that this report does not use the semi-exterior surface value because
     910              :     // EnergyPlus does not have a way to specifically tell whether or not a surface
     911              :     // is connected to a semi-exterior area of the building.  Users can always use
     912              :     // the Nominal U-Value to manually calculated this.  The values calculated here
     913              :     // are simply reported to the EIO file and not used for any calculations.
     914              : 
     915              :     // Return value
     916              :     Real64 NominalUwithConvCoeffs; // return value
     917              : 
     918              :     static constexpr std::array<Real64, static_cast<int>(DataSurfaces::SurfaceClass::Num)> filmCoefs = {
     919              :         0.0,       // None
     920              :         0.1197548, // Wall
     921              :         0.1620212, // Floor
     922              :         0.1074271, // Roof
     923              :         0.0,       // IntMass
     924              :         0.0,       // Detached_B
     925              :         0.0,       // Detached_F
     926              :         0.1197548, // Window
     927              :         0.1197548, // GlassDoor
     928              :         0.1197548, // Door
     929              :         0.0,       // Shading
     930              :         0.0,       // Overhang
     931              :         0.0,       // Fin
     932              :         0.0,       // TDD_Dome
     933              :         0.0        // TDD_Diffuser
     934              :     }; // If anything added to the enum SurfaceClass, adjust this list appropriately
     935              : 
     936              :     Real64 insideFilm;
     937              :     Real64 outsideFilm;
     938              : 
     939        45600 :     isValid = true;
     940              : 
     941        45600 :     auto &thisSurface = state.dataSurface->Surface(numSurf);
     942              : 
     943              :     // exterior conditions
     944        45600 :     switch (thisSurface.ExtBoundCond) {
     945        18171 :     case DataSurfaces::ExternalEnvironment: { // ExtBoundCond = 0
     946        18171 :         outsideFilm = 0.0299387;              // All exterior conditions
     947        18171 :     } break;
     948            1 :     case DataSurfaces::OtherSideCoefCalcExt: {
     949            1 :         outsideFilm = state.dataSurface->OSC(thisSurface.OSCPtr).SurfFilmCoef;
     950            1 :     } break;
     951         2557 :     case DataSurfaces::Ground:
     952              :     case DataSurfaces::OtherSideCoefNoCalcExt:
     953              :     case DataSurfaces::OtherSideCondModeledExt:
     954              :     case DataSurfaces::GroundFCfactorMethod:
     955              :     case DataSurfaces::KivaFoundation: { // All these cases have a negative ExtBoundCond so don't use film coefficients
     956         2557 :         outsideFilm = 0.0;
     957         2557 :     } break;
     958        24871 :     default: { // Interior Surface Attached to a Zone (ExtBoundCond is a surface)
     959        24871 :         outsideFilm = filmCoefs[static_cast<int>(state.dataSurface->Surface(thisSurface.ExtBoundCond).Class)];
     960        24871 :     } break;
     961              :     }
     962              :     // interior conditions and calculate the return value
     963        45600 :     if (state.dataHeatBal->NominalU(thisSurface.Construction) > 0.0) {
     964        45582 :         insideFilm = filmCoefs[static_cast<int>(thisSurface.Class)];
     965        45582 :         if (insideFilm == 0.0) {
     966         2539 :             outsideFilm = 0.0;
     967              :         }
     968        45582 :         NominalUwithConvCoeffs =
     969        45582 :             1.0 / (insideFilm + (1.0 / state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction)) + outsideFilm);
     970              :     } else {
     971           18 :         isValid = false;
     972           18 :         NominalUwithConvCoeffs = state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction);
     973              :     }
     974              : 
     975        45600 :     return NominalUwithConvCoeffs;
     976              : }
     977              : 
     978          801 : void SetFlagForWindowConstructionWithShadeOrBlindLayer(EnergyPlusData &state)
     979              : {
     980              : 
     981              :     // PURPOSE OF THIS SUBROUTINE:
     982              :     // check fenestrations with shading control and set a flag to true if its construction has
     983              :     // either shade or blind material layer
     984              : 
     985              :     // METHODOLOGY EMPLOYED:
     986              :     // Loop through Surface and register any shading controls, and loop through the construction
     987              :     // material layer
     988              : 
     989              :     // Using/Aliasing
     990              :     using DataSurfaces::ExternalEnvironment;
     991              : 
     992              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     993          801 :     int loopSurfNum(0); // surface index
     994          801 :     int ConstrNum(0);   // construction index
     995          801 :     int NumLayers(0);   // number of material layers in a construction
     996          801 :     int Layer(0);       // construction material layer index
     997          801 :     int MaterNum(0);    // construction material index
     998              : 
     999          801 :     auto &s_mat = state.dataMaterial;
    1000              : 
    1001        48091 :     for (loopSurfNum = 1; loopSurfNum <= state.dataSurface->TotSurfaces; ++loopSurfNum) {
    1002              : 
    1003        47290 :         if (state.dataSurface->Surface(loopSurfNum).Class != DataSurfaces::SurfaceClass::Window) {
    1004        40931 :             continue;
    1005              :         }
    1006         6359 :         if (state.dataSurface->Surface(loopSurfNum).ExtBoundCond != ExternalEnvironment) {
    1007           14 :             continue;
    1008              :         }
    1009         6345 :         if (!state.dataSurface->Surface(loopSurfNum).HasShadeControl) {
    1010         6194 :             continue;
    1011              :         }
    1012          151 :         if (state.dataSurface->Surface(loopSurfNum).activeShadedConstruction == 0) {
    1013            0 :             continue;
    1014              :         }
    1015              : 
    1016          151 :         ConstrNum = state.dataSurface->Surface(loopSurfNum).activeShadedConstruction;
    1017          151 :         auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
    1018          151 :         if (thisConstruct.TypeIsWindow) {
    1019          151 :             NumLayers = thisConstruct.TotLayers;
    1020          610 :             for (Layer = 1; Layer <= NumLayers; ++Layer) {
    1021          459 :                 MaterNum = thisConstruct.LayerPoint(Layer);
    1022          459 :                 if (MaterNum == 0) {
    1023            0 :                     continue;
    1024              :                 }
    1025          459 :                 auto const *mat = s_mat->materials(MaterNum);
    1026          459 :                 if (mat->group == Material::Group::Shade || mat->group == Material::Group::Blind) {
    1027          113 :                     state.dataSurface->SurfWinHasShadeOrBlindLayer(loopSurfNum) = true;
    1028              :                 }
    1029              :             }
    1030              :         }
    1031              :     }
    1032          801 : }
    1033              : 
    1034          801 : void AllocateIntGains(EnergyPlusData &state)
    1035              : {
    1036          801 :     state.dataHeatBal->ZoneIntGain.allocate(state.dataGlobal->NumOfZones);
    1037          801 :     state.dataHeatBal->spaceIntGain.allocate(state.dataGlobal->numSpaces);
    1038          801 :     state.dataHeatBal->spaceIntGainDevices.allocate(state.dataGlobal->numSpaces);
    1039          801 :     state.dataDayltg->spacePowerReductionFactor.dimension(state.dataGlobal->numSpaces, 1.0);
    1040          801 : }
    1041              : 
    1042              : } // namespace EnergyPlus::DataHeatBalance
        

Generated by: LCOV version 2.0-1