LCOV - code coverage report
Current view: top level - EnergyPlus - DataHeatBalance.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 569 723 78.7 %
Date: 2023-01-17 19:17:23 Functions: 20 20 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, 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/DataDaylighting.hh>
      59             : #include <EnergyPlus/DataEnvironment.hh>
      60             : #include <EnergyPlus/DataHeatBalSurface.hh>
      61             : #include <EnergyPlus/DataHeatBalance.hh>
      62             : #include <EnergyPlus/DataSurfaces.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 DataSurfaces::MaxSlatAngs;
      96             : using namespace DataVectorTypes;
      97             : using DataBSDFWindow::BSDFLayerAbsorpStruct;
      98             : using DataBSDFWindow::BSDFWindowInputStruct;
      99             : 
     100             : // Functions
     101             : 
     102      971050 : Real64 SpaceData::sumHATsurf(EnergyPlusData &state)
     103             : {
     104             :     // PURPOSE OF THIS FUNCTION:
     105             :     // This function calculates the space sum of Hc*Area*Tsurf.
     106             : 
     107      971050 :     Real64 sumHATsurf = 0.0;
     108             : 
     109     7999460 :     for (int surfNum = this->HTSurfaceFirst; surfNum <= this->HTSurfaceLast; ++surfNum) {
     110     7028410 :         Real64 Area = state.dataSurface->Surface(surfNum).Area;
     111             : 
     112     7028410 :         if (state.dataSurface->Surface(surfNum).Class == DataSurfaces::SurfaceClass::Window) {
     113      523860 :             if (state.dataSurface->SurfWinDividerArea(surfNum) > 0.0) {
     114           0 :                 if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(surfNum))) {
     115             :                     // The area is the shade or blind area = sum of the glazing area and the divider area (which is zero if no divider)
     116           0 :                     Area += state.dataSurface->SurfWinDividerArea(surfNum);
     117             :                 } else {
     118             :                     // Window divider contribution (only for window with divider and no interior shade or blind)
     119           0 :                     sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * state.dataSurface->SurfWinDividerArea(surfNum) *
     120           0 :                                   (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(surfNum)) * state.dataSurface->SurfWinDividerTempIn(surfNum);
     121             :                 }
     122             :             }
     123             : 
     124      523860 :             if (state.dataSurface->SurfWinFrameArea(surfNum) > 0.0) {
     125             :                 // Window frame contribution
     126           0 :                 sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * state.dataSurface->SurfWinFrameArea(surfNum) *
     127           0 :                               (1.0 + state.dataSurface->SurfWinProjCorrFrIn(surfNum)) * state.dataSurface->SurfWinFrameTempIn(surfNum);
     128             :             }
     129             :         }
     130             : 
     131     7028410 :         sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(surfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(surfNum);
     132             :     }
     133             : 
     134      971050 :     return sumHATsurf;
     135             : }
     136             : 
     137      971050 : Real64 ZoneData::sumHATsurf(EnergyPlusData &state)
     138             : {
     139      971050 :     Real64 sumHATsurf = 0.0;
     140     1942100 :     for (int spaceNum : this->spaceIndexes) {
     141      971050 :         sumHATsurf += state.dataHeatBal->space(spaceNum).sumHATsurf(state);
     142             :     }
     143      971050 :     return sumHATsurf;
     144             : }
     145    18450203 : void ZoneData::SetOutBulbTempAt(EnergyPlusData &state)
     146             : {
     147             :     // SUBROUTINE INFORMATION:
     148             :     //       AUTHOR         Noel Keen (LBL)/Linda Lawrie
     149             :     //       DATE WRITTEN   August 2010
     150             :     //       MODIFIED       na
     151             :     //       RE-ENGINEERED  na
     152             : 
     153             :     // PURPOSE OF THIS SUBROUTINE:
     154             :     // Routine provides facility for doing bulk Set Temperature at Height.
     155             : 
     156    18450203 :     if (state.dataEnvrn->SiteTempGradient == 0.0) {
     157           0 :         OutDryBulbTemp = state.dataEnvrn->OutDryBulbTemp;
     158           0 :         OutWetBulbTemp = state.dataEnvrn->OutWetBulbTemp;
     159             :     } else {
     160             :         // Base temperatures at Z = 0 (C)
     161    18450203 :         Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     162    18450203 :         Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
     163             : 
     164    18450203 :         Real64 const Z(Centroid.z); // Centroid value
     165    18450203 :         if (Z <= 0.0) {
     166      201105 :             OutDryBulbTemp = BaseDryTemp;
     167      201105 :             OutWetBulbTemp = BaseWetTemp;
     168             :         } else {
     169    18249098 :             OutDryBulbTemp = BaseDryTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
     170    18249098 :             OutWetBulbTemp = BaseWetTemp - state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z / (DataEnvironment::EarthRadius + Z);
     171             :         }
     172             :     }
     173    18450203 : }
     174             : 
     175    18445389 : void ZoneData::SetWindSpeedAt(EnergyPlusData &state, Real64 const fac)
     176             : {
     177             :     // SUBROUTINE INFORMATION:
     178             :     //       AUTHOR         Linda Lawrie
     179             :     //       DATE WRITTEN   June 2013
     180             :     //       MODIFIED       na
     181             :     //       RE-ENGINEERED  na
     182             : 
     183             :     // PURPOSE OF THIS SUBROUTINE:
     184             :     // Routine provides facility for doing bulk Set Windspeed at Height.
     185             : 
     186    18445389 :     if (state.dataEnvrn->SiteWindExp == 0.0) {
     187           0 :         WindSpeed = state.dataEnvrn->WindSpeed;
     188             :     } else {
     189    18445389 :         Real64 const Z(Centroid.z); // Centroid value
     190    18445389 :         if (Z <= 0.0) {
     191      201054 :             WindSpeed = 0.0;
     192             :         } else {
     193             :             //  [Met] - at meterological Station, Height of measurement is usually 10m above ground
     194             :             //  LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
     195             :             //                     * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
     196    18244335 :             WindSpeed = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
     197             :         }
     198             :     }
     199    18445389 : }
     200             : 
     201    18445389 : void ZoneData::SetWindDirAt(Real64 const fac)
     202             : {
     203    18445389 :     WindDir = fac;
     204    18445389 : }
     205             : 
     206        4820 : void AirReportVars::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string_view name)
     207             : {
     208       14460 :     SetupOutputVariable(state,
     209        9640 :                         format("{} Mean Air Temperature", prefix),
     210             :                         OutputProcessor::Unit::C,
     211             :                         this->MeanAirTemp,
     212             :                         OutputProcessor::SOVTimeStepType::Zone,
     213             :                         OutputProcessor::SOVStoreType::Average,
     214             :                         name);
     215       14460 :     SetupOutputVariable(state,
     216        9640 :                         format("{} Operative Temperature", prefix),
     217             :                         OutputProcessor::Unit::C,
     218             :                         this->OperativeTemp,
     219             :                         OutputProcessor::SOVTimeStepType::Zone,
     220             :                         OutputProcessor::SOVStoreType::Average,
     221             :                         name);
     222       14460 :     SetupOutputVariable(state,
     223        9640 :                         format("{} Mean Air Dewpoint Temperature", prefix),
     224             :                         OutputProcessor::Unit::C,
     225             :                         this->MeanAirDewPointTemp,
     226             :                         OutputProcessor::SOVTimeStepType::Zone,
     227             :                         OutputProcessor::SOVStoreType::Average,
     228             :                         name);
     229       14460 :     SetupOutputVariable(state,
     230        9640 :                         format("{} Mean Air Humidity Ratio", prefix),
     231             :                         OutputProcessor::Unit::kgWater_kgDryAir,
     232             :                         this->MeanAirHumRat,
     233             :                         OutputProcessor::SOVTimeStepType::Zone,
     234             :                         OutputProcessor::SOVStoreType::Average,
     235             :                         name);
     236       14460 :     SetupOutputVariable(state,
     237        9640 :                         format("{} Air Heat Balance Internal Convective Heat Gain Rate", prefix),
     238             :                         OutputProcessor::Unit::W,
     239             :                         this->SumIntGains,
     240             :                         OutputProcessor::SOVTimeStepType::System,
     241             :                         OutputProcessor::SOVStoreType::Average,
     242             :                         name);
     243       14460 :     SetupOutputVariable(state,
     244        9640 :                         format("{} Air Heat Balance Surface Convection Rate", prefix),
     245             :                         OutputProcessor::Unit::W,
     246             :                         this->SumHADTsurfs,
     247             :                         OutputProcessor::SOVTimeStepType::System,
     248             :                         OutputProcessor::SOVStoreType::Average,
     249             :                         name);
     250       14460 :     SetupOutputVariable(state,
     251        9640 :                         format("{} Air Heat Balance Interzone Air Transfer Rate", prefix),
     252             :                         OutputProcessor::Unit::W,
     253             :                         this->SumMCpDTzones,
     254             :                         OutputProcessor::SOVTimeStepType::System,
     255             :                         OutputProcessor::SOVStoreType::Average,
     256             :                         name);
     257       14460 :     SetupOutputVariable(state,
     258        9640 :                         format("{} Air Heat Balance Outdoor Air Transfer Rate", prefix),
     259             :                         OutputProcessor::Unit::W,
     260             :                         this->SumMCpDtInfil,
     261             :                         OutputProcessor::SOVTimeStepType::System,
     262             :                         OutputProcessor::SOVStoreType::Average,
     263             :                         name);
     264       14460 :     SetupOutputVariable(state,
     265        9640 :                         format("{} Air Heat Balance System Air Transfer Rate", prefix),
     266             :                         OutputProcessor::Unit::W,
     267             :                         this->SumMCpDTsystem,
     268             :                         OutputProcessor::SOVTimeStepType::System,
     269             :                         OutputProcessor::SOVStoreType::Average,
     270             :                         name);
     271       14460 :     SetupOutputVariable(state,
     272        9640 :                         format("{} Air Heat Balance System Convective Heat Gain Rate", prefix),
     273             :                         OutputProcessor::Unit::W,
     274             :                         this->SumNonAirSystem,
     275             :                         OutputProcessor::SOVTimeStepType::System,
     276             :                         OutputProcessor::SOVStoreType::Average,
     277             :                         name);
     278       14460 :     SetupOutputVariable(state,
     279        9640 :                         format("{} Air Heat Balance Air Energy Storage Rate", prefix),
     280             :                         OutputProcessor::Unit::W,
     281             :                         this->CzdTdt,
     282             :                         OutputProcessor::SOVTimeStepType::System,
     283             :                         OutputProcessor::SOVStoreType::Average,
     284             :                         name);
     285        4820 :     if (state.dataGlobal->DisplayAdvancedReportVariables) {
     286         171 :         SetupOutputVariable(state,
     287         114 :                             format("{} Air Heat Balance Deviation Rate", prefix),
     288             :                             OutputProcessor::Unit::W,
     289             :                             this->imBalance,
     290             :                             OutputProcessor::SOVTimeStepType::System,
     291             :                             OutputProcessor::SOVStoreType::Average,
     292             :                             name);
     293             :     }
     294        4820 : }
     295             : 
     296     2569084 : void SetZoneOutBulbTempAt(EnergyPlusData &state)
     297             : {
     298    21019287 :     for (auto &zone : state.dataHeatBal->Zone) {
     299    18450203 :         zone.SetOutBulbTempAt(state);
     300             :     }
     301     2569084 : }
     302             : 
     303     2569084 : void CheckZoneOutBulbTempAt(EnergyPlusData &state)
     304             : {
     305             :     // Using/Aliasing
     306             :     using DataEnvironment::SetOutBulbTempAt_error;
     307             : 
     308     2569084 :     Real64 minBulb = 0.0;
     309    21019287 :     for (auto &zone : state.dataHeatBal->Zone) {
     310    18450203 :         minBulb = min(minBulb, zone.OutDryBulbTemp, zone.OutWetBulbTemp);
     311    18450203 :         if (minBulb < -100.0) SetOutBulbTempAt_error(state, "Zone", zone.Centroid.z, zone.Name);
     312             :     }
     313     2569084 : }
     314             : 
     315     2568313 : void SetZoneWindSpeedAt(EnergyPlusData &state)
     316             : {
     317     2568313 :     Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
     318     2568313 :                      std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
     319    21013702 :     for (auto &zone : state.dataHeatBal->Zone) {
     320    18445389 :         zone.SetWindSpeedAt(state, fac);
     321             :     }
     322     2568313 : }
     323             : 
     324     2568313 : void SetZoneWindDirAt(EnergyPlusData &state)
     325             : {
     326             :     // Using/Aliasing
     327     2568313 :     Real64 const fac(state.dataEnvrn->WindDir);
     328    21013702 :     for (auto &zone : state.dataHeatBal->Zone) {
     329    18445389 :         zone.SetWindDirAt(fac);
     330             :     }
     331     2568313 : }
     332             : 
     333        5827 : void CheckAndSetConstructionProperties(EnergyPlusData &state,
     334             :                                        int const ConstrNum, // Construction number to be set/checked
     335             :                                        bool &ErrorsFound    // error flag that is set when certain errors have occurred
     336             : )
     337             : {
     338             : 
     339             :     // SUBROUTINE INFORMATION:
     340             :     //       AUTHOR         Linda Lawrie
     341             :     //       DATE WRITTEN   December 2006
     342             : 
     343             :     // This routine checks some properties of entered constructions; sets some properties; and sets
     344             :     // an error flag for certain error conditions.
     345             : 
     346             :     int InsideLayer;             // Inside Layer of Construct; for window construct, layer no. of inside glass
     347             :     int MaterNum;                // Counters to keep track of the material number for a layer
     348             :     int OutsideMaterNum;         // Material "number" of the Outside layer
     349             :     int InsideMaterNum;          // Material "number" of the Inside layer
     350             :     int Layer;                   // loop index for each of the construction layers
     351             :     int TotLayers;               // Number of layers in a construction
     352             :     int TotGlassLayers;          // Number of glass layers in a construction
     353             :     int TotShadeLayers;          // Number of shade layers in a construction
     354             :     int TotGasLayers;            // Number of gas layers in a construction
     355             :     bool WrongMaterialsMix;      // True if window construction has a layer that is not glass, gas or shade
     356             :     bool WrongWindowLayering;    // True if error in layering of a window construction
     357             :     int MaterNumNext;            // Next material number in the layer sequence
     358             :     int IGas;                    // Index for gases in a mixture of gases in a window gap
     359             :     int LayNumSh;                // Number of shade/blind layer in a construction
     360             :     int MatSh;                   // Material number of a shade/blind layer
     361             :     int MatGapL;                 // Material number of the gas layer to the left (outer side) of a shade/blind layer
     362             :     int MatGapR;                 // Material number of the gas layer to the right (innner side) of a shade/blind layer
     363             :     int BlNum;                   // Blind number
     364             :     bool ValidBGShadeBlindConst; // True if a valid window construction with between-glass shade/blind
     365             :     int GlassLayNum;             // Glass layer number
     366             : 
     367        5827 :     TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
     368        5827 :     if (TotLayers == 0) return; // error condition, hopefully caught elsewhere
     369        5827 :     InsideLayer = TotLayers;
     370        5827 :     if (state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer) <= 0) return; // Error condition
     371             : 
     372             :     //   window screen is not allowed on inside layer
     373             : 
     374        5827 :     state.dataConstruction->Construct(ConstrNum).DayltPropPtr = 0;
     375        5827 :     InsideMaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer);
     376        5827 :     if (InsideMaterNum != 0) {
     377        5827 :         state.dataConstruction->Construct(ConstrNum).InsideAbsorpVis = state.dataMaterial->Material(InsideMaterNum).AbsorpVisible;
     378        5827 :         state.dataConstruction->Construct(ConstrNum).InsideAbsorpSolar = state.dataMaterial->Material(InsideMaterNum).AbsorpSolar;
     379             : 
     380             :         // Following line applies only to opaque surfaces; it is recalculated later for windows.
     381        5827 :         state.dataConstruction->Construct(ConstrNum).ReflectVisDiffBack = 1.0 - state.dataMaterial->Material(InsideMaterNum).AbsorpVisible;
     382             :     }
     383             : 
     384        5827 :     OutsideMaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(1);
     385        5827 :     if (OutsideMaterNum != 0) {
     386        5827 :         state.dataConstruction->Construct(ConstrNum).OutsideAbsorpVis = state.dataMaterial->Material(OutsideMaterNum).AbsorpVisible;
     387        5827 :         state.dataConstruction->Construct(ConstrNum).OutsideAbsorpSolar = state.dataMaterial->Material(OutsideMaterNum).AbsorpSolar;
     388             :     }
     389             : 
     390        5827 :     state.dataConstruction->Construct(ConstrNum).TotSolidLayers = 0;
     391        5827 :     state.dataConstruction->Construct(ConstrNum).TotGlassLayers = 0;
     392        5827 :     state.dataConstruction->Construct(ConstrNum).AbsDiffShade = 0.0;
     393             : 
     394             :     // Check if any layer is glass, gas, shade, screen or blind; if so it is considered a window construction for
     395             :     // purposes of error checking.
     396             : 
     397        5827 :     state.dataConstruction->Construct(ConstrNum).TypeIsWindow = false;
     398       19518 :     for (Layer = 1; Layer <= TotLayers; ++Layer) {
     399       13691 :         MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
     400       13691 :         if (MaterNum == 0) continue; // error -- has been caught will stop program later
     401       13691 :         switch (state.dataMaterial->Material(MaterNum).Group) {
     402        2606 :         case DataHeatBalance::MaterialGroup::WindowGlass:
     403             :         case DataHeatBalance::MaterialGroup::WindowGas:
     404             :         case DataHeatBalance::MaterialGroup::WindowGasMixture:
     405             :         case DataHeatBalance::MaterialGroup::Shade:
     406             :         case DataHeatBalance::MaterialGroup::WindowBlind:
     407             :         case DataHeatBalance::MaterialGroup::Screen:
     408             :         case DataHeatBalance::MaterialGroup::WindowSimpleGlazing:
     409             :         case DataHeatBalance::MaterialGroup::ComplexWindowShade:
     410             :         case DataHeatBalance::MaterialGroup::ComplexWindowGap:
     411             :         case DataHeatBalance::MaterialGroup::GlassEquivalentLayer:
     412             :         case DataHeatBalance::MaterialGroup::ShadeEquivalentLayer:
     413             :         case DataHeatBalance::MaterialGroup::DrapeEquivalentLayer:
     414             :         case DataHeatBalance::MaterialGroup::ScreenEquivalentLayer:
     415             :         case DataHeatBalance::MaterialGroup::BlindEquivalentLayer:
     416             :         case DataHeatBalance::MaterialGroup::GapEquivalentLayer:
     417        2606 :             state.dataConstruction->Construct(ConstrNum).TypeIsWindow = true;
     418        2606 :             break;
     419       11085 :         case DataHeatBalance::MaterialGroup::Invalid:
     420             :         case DataHeatBalance::MaterialGroup::Air:
     421             :         case DataHeatBalance::MaterialGroup::RegularMaterial:
     422             :         case DataHeatBalance::MaterialGroup::EcoRoof:
     423             :         case DataHeatBalance::MaterialGroup::IRTMaterial:
     424       11085 :             break; // Purposely not doing anything
     425           0 :         default:
     426           0 :             assert(false);
     427             :         }
     428             :     }
     429             : 
     430        5827 :     if (InsideMaterNum == 0) return;
     431        5827 :     if (OutsideMaterNum == 0) return;
     432             : 
     433        5827 :     if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) {
     434             : 
     435        1294 :         state.dataConstruction->Construct(ConstrNum).NumCTFTerms = 0;
     436        1294 :         state.dataConstruction->Construct(ConstrNum).NumHistories = 0;
     437        1294 :         WrongMaterialsMix = false;
     438        1294 :         WrongWindowLayering = false;
     439        3900 :         for (Layer = 1; Layer <= TotLayers; ++Layer) {
     440        2606 :             MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
     441        2606 :             if (MaterNum == 0) continue; // error -- has been caught will stop program later
     442        2606 :             switch (state.dataMaterial->Material(MaterNum).Group) {
     443        2606 :             case DataHeatBalance::MaterialGroup::WindowGlass:
     444             :             case DataHeatBalance::MaterialGroup::WindowGas:
     445             :             case DataHeatBalance::MaterialGroup::WindowGasMixture:
     446             :             case DataHeatBalance::MaterialGroup::Shade:
     447             :             case DataHeatBalance::MaterialGroup::WindowBlind:
     448             :             case DataHeatBalance::MaterialGroup::Screen:
     449             :             case DataHeatBalance::MaterialGroup::WindowSimpleGlazing:
     450             :             case DataHeatBalance::MaterialGroup::ComplexWindowShade:
     451             :             case DataHeatBalance::MaterialGroup::ComplexWindowGap:
     452             :             case DataHeatBalance::MaterialGroup::GlassEquivalentLayer:
     453             :             case DataHeatBalance::MaterialGroup::ShadeEquivalentLayer:
     454             :             case DataHeatBalance::MaterialGroup::DrapeEquivalentLayer:
     455             :             case DataHeatBalance::MaterialGroup::ScreenEquivalentLayer:
     456             :             case DataHeatBalance::MaterialGroup::BlindEquivalentLayer:
     457             :             case DataHeatBalance::MaterialGroup::GapEquivalentLayer:
     458        2606 :                 break; // everything is OK
     459           0 :             default:
     460           0 :                 WrongMaterialsMix = true; // found a bad one
     461             :             }
     462             :         }
     463             : 
     464        1294 :         if (WrongMaterialsMix) { // Illegal material for a window construction
     465           0 :             ShowSevereError(state,
     466           0 :                             "Error: Window construction=" + state.dataConstruction->Construct(ConstrNum).Name +
     467             :                                 " has materials other than glass, gas, shade, screen, blind, complex shading, complex gap, or simple system.");
     468           0 :             ErrorsFound = true;
     469             :             // Do not check number of layers for BSDF type of window since that can be handled
     470        1295 :         } else if ((TotLayers > 8) && (!state.dataConstruction->Construct(ConstrNum).WindowTypeBSDF) &&
     471           1 :                    (!state.dataConstruction->Construct(ConstrNum).WindowTypeEQL)) { // Too many layers for a window construction
     472           0 :             ShowSevereError(state,
     473           0 :                             "CheckAndSetConstructionProperties: Window construction=" + state.dataConstruction->Construct(ConstrNum).Name +
     474             :                                 " has too many layers (max of 8 allowed -- 4 glass + 3 gap + 1 shading device).");
     475           0 :             ErrorsFound = true;
     476             : 
     477        1294 :         } else if (TotLayers == 1) {
     478             : 
     479         712 :             if (BITF_TEST_ANY(BITF(state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group),
     480             :                               BITF(DataHeatBalance::MaterialGroup::Shade) | BITF(DataHeatBalance::MaterialGroup::WindowGas) |
     481             :                                   BITF(DataHeatBalance::MaterialGroup::WindowGasMixture) | BITF(DataHeatBalance::MaterialGroup::WindowBlind) |
     482             :                                   BITF(DataHeatBalance::MaterialGroup::Screen) | BITF(DataHeatBalance::MaterialGroup::ComplexWindowShade) |
     483             :                                   BITF(DataHeatBalance::MaterialGroup::ComplexWindowGap))) {
     484           0 :                 ShowSevereError(
     485             :                     state,
     486           0 :                     "CheckAndSetConstructionProperties: The single-layer window construction=" + state.dataConstruction->Construct(ConstrNum).Name +
     487             :                         " has a gas, complex gap, shade, complex shade, screen or blind material; it should be glass of simple glazing system.");
     488           0 :                 ErrorsFound = true;
     489             :             }
     490             :         }
     491             : 
     492             :         // Find total glass layers, total shade/blind layers and total gas layers in a window construction
     493             : 
     494        1294 :         TotGlassLayers = 0;
     495        1294 :         TotShadeLayers = 0; // Includes shades, blinds, and screens
     496        1294 :         TotGasLayers = 0;
     497        3900 :         for (Layer = 1; Layer <= TotLayers; ++Layer) {
     498        2606 :             MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
     499        2606 :             if (MaterNum == 0) continue; // error -- has been caught will stop program later
     500        2606 :             if (state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::WindowGlass) ++TotGlassLayers;
     501        2606 :             if (state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::WindowSimpleGlazing) ++TotGlassLayers;
     502        2606 :             if (BITF_TEST_ANY(BITF(state.dataMaterial->Material(MaterNum).Group),
     503             :                               BITF(DataHeatBalance::MaterialGroup::Shade) | BITF(DataHeatBalance::MaterialGroup::WindowBlind) |
     504             :                                   BITF(DataHeatBalance::MaterialGroup::Screen) | BITF(DataHeatBalance::MaterialGroup::ComplexWindowShade)))
     505          40 :                 ++TotShadeLayers;
     506        2606 :             if (BITF_TEST_ANY(BITF(state.dataMaterial->Material(MaterNum).Group),
     507             :                               BITF(DataHeatBalance::MaterialGroup::WindowGas) | BITF(DataHeatBalance::MaterialGroup::WindowGasMixture) |
     508             :                                   BITF(DataHeatBalance::MaterialGroup::ComplexWindowGap)))
     509         635 :                 ++TotGasLayers;
     510        2606 :             if (Layer < TotLayers) {
     511        1312 :                 MaterNumNext = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer + 1);
     512             :                 // Adjacent layers of same type not allowed
     513        1312 :                 if (MaterNumNext == 0) continue;
     514        1312 :                 if (state.dataMaterial->Material(MaterNum).Group == state.dataMaterial->Material(MaterNumNext).Group) WrongWindowLayering = true;
     515             :             }
     516             :         }
     517             : 
     518             :         // It is not necessary to check rest of BSDF window structure since that is performed inside TARCOG90 routine.
     519             :         // That routine also allow structures which are not allowed in rest of this routine
     520        1294 :         if (state.dataConstruction->Construct(ConstrNum).WindowTypeBSDF) {
     521          14 :             state.dataConstruction->Construct(ConstrNum).TotGlassLayers = TotGlassLayers;
     522          14 :             state.dataConstruction->Construct(ConstrNum).TotSolidLayers = TotGlassLayers + TotShadeLayers;
     523          14 :             state.dataConstruction->Construct(ConstrNum).InsideAbsorpThermal =
     524          14 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).AbsorpThermalBack;
     525          14 :             state.dataConstruction->Construct(ConstrNum).OutsideAbsorpThermal =
     526          14 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermalFront;
     527          14 :             return;
     528             :         }
     529             : 
     530        1280 :         if (state.dataConstruction->Construct(ConstrNum).WindowTypeEQL) {
     531           3 :             state.dataConstruction->Construct(ConstrNum).InsideAbsorpThermal =
     532           3 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).AbsorpThermalBack;
     533           3 :             state.dataConstruction->Construct(ConstrNum).OutsideAbsorpThermal =
     534           3 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermalFront;
     535           3 :             return;
     536             :         }
     537             : 
     538        2554 :         if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     539        1277 :                 DataHeatBalance::MaterialGroup::WindowGas ||
     540        1277 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     541        1277 :                 DataHeatBalance::MaterialGroup::WindowGasMixture ||
     542        1277 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(TotLayers)).Group ==
     543        2554 :                 DataHeatBalance::MaterialGroup::WindowGas ||
     544        1277 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(TotLayers)).Group ==
     545             :                 DataHeatBalance::MaterialGroup::WindowGasMixture)
     546           0 :             WrongWindowLayering = true;                     // Gas cannot be first or last layer
     547        1277 :         if (TotShadeLayers > 1) WrongWindowLayering = true; // At most one shade, screen or blind allowed
     548             : 
     549             :         // If there is a diffusing glass layer no shade, screen or blind is allowed
     550        3802 :         for (Layer = 1; Layer <= TotLayers; ++Layer) {
     551        2525 :             MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
     552        2525 :             if (MaterNum == 0) continue; // error -- has been caught will stop program later
     553        2525 :             if (state.dataMaterial->Material(MaterNum).SolarDiffusing && TotShadeLayers > 0) {
     554           0 :                 ErrorsFound = true;
     555           0 :                 ShowSevereError(state, "CheckAndSetConstructionProperties: Window construction=" + state.dataConstruction->Construct(ConstrNum).Name);
     556           0 :                 ShowContinueError(state,
     557           0 :                                   "has diffusing glass=" + state.dataMaterial->Material(MaterNum).Name + " and a shade, screen or blind layer.");
     558           0 :                 break;
     559             :             }
     560             :         }
     561             : 
     562             :         // If there is a diffusing glass layer it must be the innermost layer
     563        1277 :         if (TotGlassLayers > 1) {
     564         546 :             GlassLayNum = 0;
     565        2321 :             for (Layer = 1; Layer <= TotLayers; ++Layer) {
     566        1775 :                 MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
     567        1775 :                 if (MaterNum == 0) continue; // error -- has been caught will stop program later
     568        1775 :                 if (state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::WindowGlass) {
     569        1152 :                     ++GlassLayNum;
     570        1152 :                     if (GlassLayNum < TotGlassLayers && state.dataMaterial->Material(MaterNum).SolarDiffusing) {
     571           0 :                         ErrorsFound = true;
     572           0 :                         ShowSevereError(
     573           0 :                             state, "CheckAndSetConstructionProperties: Window construction=" + state.dataConstruction->Construct(ConstrNum).Name);
     574           0 :                         ShowContinueError(
     575           0 :                             state, "has diffusing glass=" + state.dataMaterial->Material(MaterNum).Name + " that is not the innermost glass layer.");
     576             :                     }
     577             :                 }
     578             :             }
     579             :         }
     580             : 
     581             :         // interior window screen is not allowed. Check for invalid between-glass screen is checked below.
     582        1308 :         if (TotShadeLayers == 1 &&
     583          31 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(TotLayers)).Group ==
     584        1277 :                 DataHeatBalance::MaterialGroup::Screen &&
     585             :             TotLayers != 1) {
     586           0 :             WrongWindowLayering = true;
     587             :         }
     588             : 
     589             :         // Consistency checks for a construction with a between-glass shade or blind
     590             : 
     591        1308 :         if (TotShadeLayers == 1 &&
     592          59 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group != DataHeatBalance::MaterialGroup::Shade &&
     593          28 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group !=
     594          26 :                 DataHeatBalance::MaterialGroup::WindowBlind &&
     595          26 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group !=
     596          24 :                 DataHeatBalance::MaterialGroup::Screen &&
     597          24 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(TotLayers)).Group !=
     598          17 :                 DataHeatBalance::MaterialGroup::Shade &&
     599          17 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(TotLayers)).Group !=
     600           5 :                 DataHeatBalance::MaterialGroup::WindowBlind &&
     601           5 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(TotLayers)).Group !=
     602        1282 :                 DataHeatBalance::MaterialGroup::ComplexWindowShade &&
     603           5 :             !WrongWindowLayering) {
     604             : 
     605             :             // This is a construction with a between-glass shade or blind
     606             : 
     607           5 :             if (TotGlassLayers >= 4) {
     608             :                 // Quadruple pane not allowed.
     609           0 :                 WrongWindowLayering = true;
     610           5 :             } else if (TotGlassLayers == 2 || TotGlassLayers == 3) {
     611           5 :                 ValidBGShadeBlindConst = false;
     612           5 :                 if (TotGlassLayers == 2) {
     613           3 :                     if (TotLayers != 5) {
     614           0 :                         WrongWindowLayering = true;
     615             :                     } else {
     616           6 :                         if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     617           3 :                                 DataHeatBalance::MaterialGroup::WindowGlass &&
     618           3 :                             (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(2)).Group ==
     619           0 :                                  DataHeatBalance::MaterialGroup::WindowGas ||
     620           0 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(2)).Group ==
     621           3 :                                  DataHeatBalance::MaterialGroup::WindowGasMixture) &&
     622           3 :                             ((state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(3)).Group ==
     623           2 :                                   DataHeatBalance::MaterialGroup::Shade ||
     624           2 :                               state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(3)).Group ==
     625           3 :                                   DataHeatBalance::MaterialGroup::WindowBlind) &&
     626           3 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(3)).Group !=
     627           3 :                                  DataHeatBalance::MaterialGroup::Screen) &&
     628           3 :                             (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(4)).Group ==
     629           0 :                                  DataHeatBalance::MaterialGroup::WindowGas ||
     630           0 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(4)).Group ==
     631           6 :                                  DataHeatBalance::MaterialGroup::WindowGasMixture) &&
     632           3 :                             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(5)).Group ==
     633             :                                 DataHeatBalance::MaterialGroup::WindowGlass)
     634           3 :                             ValidBGShadeBlindConst = true;
     635             :                     }
     636             :                 } else { // TotGlassLayers = 3
     637           2 :                     if (TotLayers != 7) {
     638           0 :                         WrongWindowLayering = true;
     639             :                     } else {
     640           4 :                         if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     641           2 :                                 DataHeatBalance::MaterialGroup::WindowGlass &&
     642           2 :                             (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(2)).Group ==
     643           0 :                                  DataHeatBalance::MaterialGroup::WindowGas ||
     644           0 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(2)).Group ==
     645           2 :                                  DataHeatBalance::MaterialGroup::WindowGasMixture) &&
     646           2 :                             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(3)).Group ==
     647           2 :                                 DataHeatBalance::MaterialGroup::WindowGlass &&
     648           2 :                             (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(4)).Group ==
     649           0 :                                  DataHeatBalance::MaterialGroup::WindowGas ||
     650           0 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(4)).Group ==
     651           2 :                                  DataHeatBalance::MaterialGroup::WindowGasMixture) &&
     652           2 :                             ((state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(5)).Group ==
     653           1 :                                   DataHeatBalance::MaterialGroup::Shade ||
     654           1 :                               state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(5)).Group ==
     655           2 :                                   DataHeatBalance::MaterialGroup::WindowBlind) &&
     656           2 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(5)).Group !=
     657           2 :                                  DataHeatBalance::MaterialGroup::Screen) &&
     658           2 :                             (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(6)).Group ==
     659           0 :                                  DataHeatBalance::MaterialGroup::WindowGas ||
     660           0 :                              state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(6)).Group ==
     661           4 :                                  DataHeatBalance::MaterialGroup::WindowGasMixture) &&
     662           2 :                             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(7)).Group ==
     663             :                                 DataHeatBalance::MaterialGroup::WindowGlass)
     664           2 :                             ValidBGShadeBlindConst = true;
     665             :                     }
     666             :                 } // End of check if TotGlassLayers = 2 or 3
     667           5 :                 if (!ValidBGShadeBlindConst) WrongWindowLayering = true;
     668           5 :                 if (!WrongWindowLayering) {
     669           5 :                     LayNumSh = 2 * TotGlassLayers - 1;
     670           5 :                     MatSh = state.dataConstruction->Construct(ConstrNum).LayerPoint(LayNumSh);
     671             :                     // For double pane, shade/blind must be layer #3.
     672             :                     // For triple pane, it must be layer #5 (i.e., between two inner panes).
     673           8 :                     if (state.dataMaterial->Material(MatSh).Group != DataHeatBalance::MaterialGroup::Shade &&
     674           3 :                         state.dataMaterial->Material(MatSh).Group != DataHeatBalance::MaterialGroup::WindowBlind)
     675           0 :                         WrongWindowLayering = true;
     676           5 :                     if (TotLayers != 2 * TotGlassLayers + 1) WrongWindowLayering = true;
     677           5 :                     if (!WrongWindowLayering) {
     678             :                         // Gas on either side of a between-glass shade/blind must be the same
     679           5 :                         MatGapL = state.dataConstruction->Construct(ConstrNum).LayerPoint(LayNumSh - 1);
     680           5 :                         MatGapR = state.dataConstruction->Construct(ConstrNum).LayerPoint(LayNumSh + 1);
     681          30 :                         for (IGas = 1; IGas <= 5; ++IGas) {
     682          50 :                             if ((state.dataMaterial->Material(MatGapL).GasType(IGas) != state.dataMaterial->Material(MatGapR).GasType(IGas)) ||
     683          25 :                                 (state.dataMaterial->Material(MatGapL).GasFract(IGas) != state.dataMaterial->Material(MatGapR).GasFract(IGas)))
     684           0 :                                 WrongWindowLayering = true;
     685             :                         }
     686             :                         // Gap width on either side of a between-glass shade/blind must be the same
     687           5 :                         if (std::abs(state.dataMaterial->Material(MatGapL).Thickness - state.dataMaterial->Material(MatGapR).Thickness) > 0.0005)
     688           0 :                             WrongWindowLayering = true;
     689           5 :                         if (state.dataMaterial->Material(MatSh).Group == DataHeatBalance::MaterialGroup::WindowBlind) {
     690           3 :                             BlNum = state.dataMaterial->Material(MatSh).BlindDataPtr;
     691           3 :                             if (BlNum > 0) {
     692           6 :                                 if ((state.dataMaterial->Material(MatGapL).Thickness + state.dataMaterial->Material(MatGapR).Thickness) <
     693           3 :                                     state.dataHeatBal->Blind(BlNum).SlatWidth) {
     694           0 :                                     ErrorsFound = true;
     695           0 :                                     ShowSevereError(state,
     696           0 :                                                     "CheckAndSetConstructionProperties: For window construction " +
     697           0 :                                                         state.dataConstruction->Construct(ConstrNum).Name);
     698           0 :                                     ShowContinueError(state, "the slat width of the between-glass blind is greater than");
     699           0 :                                     ShowContinueError(state, "the sum of the widths of the gas layers adjacent to the blind.");
     700             :                                 }
     701             :                             } // End of check if BlNum > 0
     702             :                         }     // End of check if material is window blind
     703             :                     }         // End of check if WrongWindowLayering
     704             :                 }             // End of check if WrongWindowLayering
     705             :             }                 // End of check on total glass layers
     706             :         }                     // End of check if construction has between-glass shade/blind
     707             : 
     708             :         // Check Simple Windows,
     709        1277 :         if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     710             :             DataHeatBalance::MaterialGroup::WindowSimpleGlazing) {
     711         114 :             if (TotLayers > 1) {
     712             :                 // check that none of the other layers are glazing or gas
     713          12 :                 for (Layer = 1; Layer <= TotLayers; ++Layer) {
     714           8 :                     MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
     715           8 :                     if (MaterNum == 0) continue; // error -- has been caught will stop program later
     716           8 :                     if (state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::WindowGlass) {
     717           0 :                         ErrorsFound = true;
     718           0 :                         ShowSevereError(state,
     719           0 :                                         "CheckAndSetConstructionProperties: Error in window construction " +
     720           0 :                                             state.dataConstruction->Construct(ConstrNum).Name + "--");
     721           0 :                         ShowContinueError(state, "For simple window constructions, no other glazing layers are allowed.");
     722             :                     }
     723           8 :                     if (state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::WindowGas) {
     724           0 :                         ErrorsFound = true;
     725           0 :                         ShowSevereError(state,
     726           0 :                                         "CheckAndSetConstructionProperties: Error in window construction " +
     727           0 :                                             state.dataConstruction->Construct(ConstrNum).Name + "--");
     728           0 :                         ShowContinueError(state, "For simple window constructions, no other gas layers are allowed.");
     729             :                     }
     730             :                 }
     731             :             }
     732             :         }
     733             : 
     734        1277 :         if (WrongWindowLayering) {
     735           0 :             ShowSevereError(
     736           0 :                 state, "CheckAndSetConstructionProperties: Error in window construction " + state.dataConstruction->Construct(ConstrNum).Name + "--");
     737           0 :             ShowContinueError(state, "  For multi-layer window constructions the following rules apply:");
     738           0 :             ShowContinueError(state, "    --The first and last layer must be a solid layer (glass or shade/screen/blind),");
     739           0 :             ShowContinueError(state, "    --Adjacent glass layers must be separated by one and only one gas layer,");
     740           0 :             ShowContinueError(state, "    --Adjacent layers must not be of the same type,");
     741           0 :             ShowContinueError(state, "    --Only one shade/screen/blind layer is allowed,");
     742           0 :             ShowContinueError(state, "    --An exterior shade/screen/blind must be the first layer,");
     743           0 :             ShowContinueError(state, "    --An interior shade/blind must be the last layer,");
     744           0 :             ShowContinueError(state, "    --An interior screen is not allowed,");
     745           0 :             ShowContinueError(state, "    --For an exterior shade/screen/blind or interior shade/blind, there should not be a gas layer");
     746           0 :             ShowContinueError(state, "    ----between the shade/screen/blind and adjacent glass,");
     747           0 :             ShowContinueError(state, "    --A between-glass screen is not allowed,");
     748           0 :             ShowContinueError(state, "    --A between-glass shade/blind is allowed only for double and triple glazing,");
     749           0 :             ShowContinueError(state, "    --A between-glass shade/blind must have adjacent gas layers of the same type and width,");
     750           0 :             ShowContinueError(state, "    --For triple glazing the between-glass shade/blind must be between the two inner glass layers,");
     751           0 :             ShowContinueError(state, "    --The slat width of a between-glass blind must be less than the sum of the widths");
     752           0 :             ShowContinueError(state, "    ----of the gas layers adjacent to the blind.");
     753           0 :             ErrorsFound = true;
     754             :         }
     755             : 
     756        1277 :         state.dataConstruction->Construct(ConstrNum).TotGlassLayers = TotGlassLayers;
     757        1277 :         state.dataConstruction->Construct(ConstrNum).TotSolidLayers = TotGlassLayers + TotShadeLayers;
     758             : 
     759             :         // In following, InsideLayer is layer number of inside glass and InsideAbsorpThermal applies
     760             :         // only to inside glass; it is corrected later in InitGlassOpticalCalculations
     761             :         // if construction has inside shade or blind.
     762        2554 :         if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).Group ==
     763        2547 :                 DataHeatBalance::MaterialGroup::Shade ||
     764        1270 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).Group ==
     765             :                 DataHeatBalance::MaterialGroup::WindowBlind) {
     766          19 :             --InsideLayer;
     767             :         }
     768        1277 :         if (InsideLayer > 0) {
     769        1277 :             InsideMaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer);
     770        1277 :             state.dataConstruction->Construct(ConstrNum).InsideAbsorpThermal =
     771        1277 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).AbsorpThermalBack;
     772             :         }
     773        1277 :         if (InsideMaterNum != 0) {
     774        1277 :             state.dataConstruction->Construct(ConstrNum).InsideAbsorpVis = state.dataMaterial->Material(InsideMaterNum).AbsorpVisible;
     775        1277 :             state.dataConstruction->Construct(ConstrNum).InsideAbsorpSolar = state.dataMaterial->Material(InsideMaterNum).AbsorpSolar;
     776             :         }
     777             : 
     778        2554 :         if ((state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     779        1398 :              DataHeatBalance::MaterialGroup::WindowGlass) ||
     780         121 :             (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     781             :              DataHeatBalance::MaterialGroup::WindowSimpleGlazing)) { // Glass
     782        1270 :             state.dataConstruction->Construct(ConstrNum).OutsideAbsorpThermal =
     783        1270 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermalFront;
     784             :         } else { // Exterior shade, blind or screen
     785           7 :             state.dataConstruction->Construct(ConstrNum).OutsideAbsorpThermal =
     786           7 :                 state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermal;
     787             :         }
     788             : 
     789             :     } else { // Opaque surface
     790        4533 :         state.dataConstruction->Construct(ConstrNum).InsideAbsorpThermal =
     791        4533 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).AbsorpThermal;
     792        4533 :         state.dataConstruction->Construct(ConstrNum).OutsideAbsorpThermal =
     793        4533 :             state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).AbsorpThermal;
     794             :     }
     795             : 
     796        5810 :     state.dataConstruction->Construct(ConstrNum).OutsideRoughness =
     797        5810 :         state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Roughness;
     798             : 
     799        5810 :     if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group == DataHeatBalance::MaterialGroup::Air) {
     800           0 :         ShowSevereError(
     801           0 :             state, "CheckAndSetConstructionProperties: Outside Layer is Air for construction " + state.dataConstruction->Construct(ConstrNum).Name);
     802           0 :         ShowContinueError(state,
     803           0 :                           "  Error in material " + state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Name);
     804           0 :         ErrorsFound = true;
     805             :     }
     806        5810 :     if (InsideLayer > 0) {
     807        5810 :         if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).Group ==
     808             :             DataHeatBalance::MaterialGroup::Air) {
     809           0 :             ShowSevereError(state,
     810           0 :                             "CheckAndSetConstructionProperties: Inside Layer is Air for construction " +
     811           0 :                                 state.dataConstruction->Construct(ConstrNum).Name);
     812           0 :             ShowContinueError(state,
     813           0 :                               "  Error in material " +
     814           0 :                                   state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(InsideLayer)).Name);
     815           0 :             ErrorsFound = true;
     816             :         }
     817             :     }
     818             : 
     819        5810 :     if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group == DataHeatBalance::MaterialGroup::EcoRoof) {
     820           4 :         state.dataConstruction->Construct(ConstrNum).TypeIsEcoRoof = true;
     821             :         // need to check EcoRoof is not non-outside layer
     822          12 :         for (Layer = 2; Layer <= TotLayers; ++Layer) {
     823           8 :             if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer)).Group ==
     824             :                 DataHeatBalance::MaterialGroup::EcoRoof) {
     825           0 :                 ShowSevereError(state,
     826           0 :                                 "CheckAndSetConstructionProperties: Interior Layer is EcoRoof for construction " +
     827           0 :                                     state.dataConstruction->Construct(ConstrNum).Name);
     828           0 :                 ShowContinueError(state,
     829           0 :                                   "  Error in material " +
     830           0 :                                       state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer)).Name);
     831           0 :                 ErrorsFound = true;
     832             :             }
     833             :         }
     834             :     }
     835             : 
     836        5810 :     if (state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Group ==
     837             :         DataHeatBalance::MaterialGroup::IRTMaterial) {
     838           2 :         state.dataConstruction->Construct(ConstrNum).TypeIsIRT = true;
     839           2 :         if (state.dataConstruction->Construct(ConstrNum).TotLayers != 1) {
     840           0 :             ShowSevereError(state,
     841           0 :                             "CheckAndSetConstructionProperties: Infrared Transparent (IRT) Construction is limited to 1 layer " +
     842           0 :                                 state.dataConstruction->Construct(ConstrNum).Name);
     843           0 :             ShowContinueError(state, "  Too many layers in referenced construction.");
     844           0 :             ErrorsFound = true;
     845             :         }
     846             :     }
     847             : }
     848             : 
     849         368 : int AssignReverseConstructionNumber(EnergyPlusData &state,
     850             :                                     int const ConstrNum, // Existing Construction number of first surface
     851             :                                     bool &ErrorsFound)
     852             : {
     853             : 
     854             :     // FUNCTION INFORMATION:
     855             :     //       AUTHOR         Linda Lawrie
     856             :     //       DATE WRITTEN   December 2006
     857             :     //       MODIFIED       na
     858             :     //       RE-ENGINEERED  na
     859             : 
     860             :     // PURPOSE OF THIS FUNCTION:
     861             :     // For interzone, unentered surfaces, we need to have "reverse" constructions
     862             :     // assigned to the created surfaces.  These need to be the reverse (outside to inside layer)
     863             :     // of existing surfaces.  Plus, there may be one already in the data structure so this is looked for as well.
     864             : 
     865             :     // METHODOLOGY EMPLOYED:
     866             :     // Create reverse layers.  Look in current constructions to see if match.  If no match, create a new one.
     867             : 
     868             :     // Return value
     869             :     int NewConstrNum; // Reverse Construction Number
     870             : 
     871             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
     872             :     int nLayer;
     873             :     int Loop;
     874             :     bool Found;
     875             : 
     876         368 :     if (ConstrNum == 0) {
     877             :         // error caught elsewhere
     878           0 :         NewConstrNum = 0;
     879           0 :         return NewConstrNum;
     880             :     }
     881             : 
     882         368 :     state.dataConstruction->Construct(ConstrNum).IsUsed = true;
     883         368 :     nLayer = 0;
     884         368 :     state.dataConstruction->LayerPoint = 0;
     885        1108 :     for (Loop = state.dataConstruction->Construct(ConstrNum).TotLayers; Loop >= 1; --Loop) {
     886         740 :         ++nLayer;
     887         740 :         state.dataConstruction->LayerPoint(nLayer) = state.dataConstruction->Construct(ConstrNum).LayerPoint(Loop);
     888             :     }
     889             : 
     890             :     // now, got thru and see if there is a match already....
     891         368 :     NewConstrNum = 0;
     892        2200 :     for (Loop = 1; Loop <= state.dataHeatBal->TotConstructs; ++Loop) {
     893        2196 :         Found = true;
     894        6205 :         for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
     895        5841 :             if (state.dataConstruction->Construct(Loop).LayerPoint(nLayer) != state.dataConstruction->LayerPoint(nLayer)) {
     896        1832 :                 Found = false;
     897        1832 :                 break;
     898             :             }
     899             :         }
     900        2196 :         if (Found) {
     901         364 :             NewConstrNum = Loop;
     902         364 :             state.dataConstruction->Construct(Loop).IsUsed = true;
     903         364 :             break;
     904             :         }
     905             :     }
     906             : 
     907             :     // if need new one, bunch o stuff
     908         368 :     if (NewConstrNum == 0) {
     909           4 :         ++state.dataHeatBal->TotConstructs;
     910           4 :         state.dataConstruction->Construct.redimension(state.dataHeatBal->TotConstructs);
     911           4 :         state.dataHeatBal->NominalRforNominalUCalculation.redimension(state.dataHeatBal->TotConstructs);
     912           4 :         state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) = 0.0;
     913           4 :         state.dataHeatBal->NominalU.redimension(state.dataHeatBal->TotConstructs);
     914           4 :         state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) = 0.0;
     915           4 :         state.dataHeatBal->NominalUBeforeAdjusted.redimension(state.dataHeatBal->TotConstructs);
     916           4 :         state.dataHeatBal->NominalUBeforeAdjusted(state.dataHeatBal->TotConstructs) = 0.0;
     917           4 :         state.dataHeatBal->CoeffAdjRatio.redimension(state.dataHeatBal->TotConstructs) = 1.0;
     918             :         //  Put in new attributes
     919           4 :         NewConstrNum = state.dataHeatBal->TotConstructs;
     920           4 :         state.dataConstruction->Construct(NewConstrNum).IsUsed = true;
     921           4 :         state.dataConstruction->Construct(state.dataHeatBal->TotConstructs) =
     922           4 :             state.dataConstruction->Construct(ConstrNum); // preserve some of the attributes.
     923             :         // replace others...
     924           4 :         state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).Name = "iz-" + state.dataConstruction->Construct(ConstrNum).Name;
     925           4 :         state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).TotLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
     926          48 :         for (nLayer = 1; nLayer <= Construction::MaxLayersInConstruct; ++nLayer) {
     927          44 :             state.dataConstruction->Construct(state.dataHeatBal->TotConstructs).LayerPoint(nLayer) = state.dataConstruction->LayerPoint(nLayer);
     928          44 :             if (state.dataConstruction->LayerPoint(nLayer) != 0) {
     929          13 :                 state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) +=
     930          13 :                     state.dataHeatBal->NominalR(state.dataConstruction->LayerPoint(nLayer));
     931             :             }
     932             :         }
     933             : 
     934             :         // no error if zero -- that will have been caught with earlier construction
     935             :         // the following line was changed to fix CR7601
     936           4 :         if (state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs) != 0.0) {
     937           4 :             state.dataHeatBal->NominalU(state.dataHeatBal->TotConstructs) =
     938           4 :                 1.0 / state.dataHeatBal->NominalRforNominalUCalculation(state.dataHeatBal->TotConstructs);
     939             :         }
     940             : 
     941           4 :         CheckAndSetConstructionProperties(state, state.dataHeatBal->TotConstructs, ErrorsFound);
     942             :     }
     943             : 
     944         368 :     return NewConstrNum;
     945             : }
     946             : 
     947           3 : void AddVariableSlatBlind(EnergyPlusData &state,
     948             :                           int const inBlindNumber, // current Blind Number/pointer to name
     949             :                           int &outBlindNumber,     // resultant Blind Number to pass back
     950             :                           bool &errFlag            // error flag should one be needed
     951             : )
     952             : {
     953             : 
     954             :     // SUBROUTINE INFORMATION:
     955             :     //       AUTHOR         Linda Lawrie
     956             :     //       DATE WRITTEN   September 2009
     957             :     //       MODIFIED       na
     958             :     //       RE-ENGINEERED  na
     959             : 
     960             :     // PURPOSE OF THIS SUBROUTINE:
     961             :     // Window Blinds are presented as "fixed" slat blinds.  However for certain Window Shading Controls,
     962             :     // the program needs to set the property to "variable"/movable slats.  Since a blind could be in use
     963             :     // elsewhere with "fixed", a material needs to be added with variable properties -- having most of the
     964             :     // "fixed" properties in tact.
     965             : 
     966             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     967             :     int Found;
     968             :     Real64 MinSlatAngGeom;
     969             :     Real64 MaxSlatAngGeom;
     970             : 
     971             :     // Object Data
     972             : 
     973             :     // maybe it's already there
     974           3 :     errFlag = false;
     975           3 :     Found = UtilityRoutines::FindItemInList("~" + state.dataHeatBal->Blind(inBlindNumber).Name, state.dataHeatBal->Blind);
     976           3 :     if (Found == 0) {
     977             :         // Add a new blind
     978           3 :         state.dataHeatBal->Blind.redimension(++state.dataHeatBal->TotBlinds);
     979           3 :         state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds) = state.dataHeatBal->Blind(inBlindNumber);
     980           3 :         state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).Name = "~" + state.dataHeatBal->Blind(inBlindNumber).Name;
     981           3 :         outBlindNumber = state.dataHeatBal->TotBlinds;
     982           3 :         state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatAngleType = DataWindowEquivalentLayer::AngleType::Variable;
     983             : 
     984             :         // Minimum and maximum slat angles allowed by slat geometry
     985           6 :         if (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatWidth >
     986           3 :             state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatSeparation) {
     987           9 :             MinSlatAngGeom = std::asin(state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatThickness /
     988           6 :                                        (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatThickness +
     989           6 :                                         state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatSeparation)) /
     990             :                              DataGlobalConstants::DegToRadians;
     991             :         } else {
     992           0 :             MinSlatAngGeom = 0.0;
     993             :         }
     994           3 :         MaxSlatAngGeom = 180.0 - MinSlatAngGeom;
     995             : 
     996             :         // Error if maximum slat angle less than minimum
     997             : 
     998           6 :         if (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle <
     999           3 :             state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle) {
    1000           0 :             errFlag = true;
    1001           0 :             ShowSevereError(state, "WindowMaterial:Blind=\"" + state.dataHeatBal->Blind(inBlindNumber).Name + "\", Illegal value combination.");
    1002           0 :             ShowContinueError(state,
    1003           0 :                               format("Minimum Slat Angle=[{:.1R}], is greater than Maximum Slat Angle=[{:.1R}] deg.",
    1004           0 :                                      state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle,
    1005           0 :                                      state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle));
    1006             :         }
    1007             : 
    1008             :         // Error if input slat angle not in input min/max range
    1009             : 
    1010           6 :         if (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle >
    1011           6 :                 state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle &&
    1012           6 :             (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatAngle < state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle ||
    1013           3 :              state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatAngle >
    1014           3 :                  state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle)) {
    1015           0 :             errFlag = true;
    1016           0 :             ShowSevereError(state, "WindowMaterial:Blind=\"" + state.dataHeatBal->Blind(inBlindNumber).Name + "\", Illegal value combination.");
    1017           0 :             ShowContinueError(state,
    1018           0 :                               format("Slat Angle=[{:.1R}] is outside of the input min/max range, min=[{:.1R}], max=[{:.1R}] deg.",
    1019           0 :                                      state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).SlatAngle,
    1020           0 :                                      state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle,
    1021           0 :                                      state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle));
    1022             :         }
    1023             : 
    1024             :         // Warning if input minimum slat angle is less than that allowed by slat geometry
    1025             : 
    1026           3 :         if (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle < MinSlatAngGeom) {
    1027           3 :             ShowWarningError(state, "WindowMaterial:Blind=\"" + state.dataHeatBal->Blind(inBlindNumber).Name + "\", Illegal value combination.");
    1028           9 :             ShowContinueError(
    1029             :                 state,
    1030           9 :                 format("Minimum Slat Angle=[{:.1R}] is less than the smallest allowed by slat dimensions and spacing, min=[{:.1R}] deg.",
    1031           3 :                        state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle,
    1032           3 :                        MinSlatAngGeom));
    1033           3 :             ShowContinueError(state, format("Minimum Slat Angle will be set to {:.1R} deg.", MinSlatAngGeom));
    1034           3 :             state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MinSlatAngle = MinSlatAngGeom;
    1035             :         }
    1036             : 
    1037             :         // Warning if input maximum slat angle is greater than that allowed by slat geometry
    1038             : 
    1039           3 :         if (state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle > MaxSlatAngGeom) {
    1040           3 :             ShowWarningError(state, "WindowMaterial:Blind=\"" + state.dataHeatBal->Blind(inBlindNumber).Name + "\", Illegal value combination.");
    1041           9 :             ShowContinueError(state,
    1042           9 :                               format("Maximum Slat Angle=[{:.1R}] is greater than the largest allowed by slat dimensions and spacing, [{:.1R}] deg.",
    1043           3 :                                      state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle,
    1044           3 :                                      MaxSlatAngGeom));
    1045           3 :             ShowContinueError(state, format("Maximum Slat Angle will be set to {:.1R} deg.", MaxSlatAngGeom));
    1046           3 :             state.dataHeatBal->Blind(state.dataHeatBal->TotBlinds).MaxSlatAngle = MaxSlatAngGeom;
    1047             :         }
    1048             :     } else {
    1049           0 :         outBlindNumber = Found;
    1050             :     }
    1051           3 : }
    1052             : 
    1053        6562 : void CalcScreenTransmittance(EnergyPlusData &state,
    1054             :                              int const SurfaceNum,
    1055             :                              Optional<Real64 const> Phi,     // Optional sun altitude relative to surface outward normal (radians)
    1056             :                              Optional<Real64 const> Theta,   // Optional sun azimuth relative to surface outward normal (radians)
    1057             :                              Optional_int_const ScreenNumber // Optional screen number
    1058             : )
    1059             : {
    1060             : 
    1061             :     // FUNCTION INFORMATION:
    1062             :     //       AUTHOR         Richard Raustad
    1063             :     //       DATE WRITTEN   May 2006
    1064             :     //       MODIFIED       na
    1065             :     //       RE-ENGINEERED  na
    1066             : 
    1067             :     // PURPOSE OF THIS FUNCTION:
    1068             :     //  Calculate transmittance of window screen given azimuth and altitude angle
    1069             :     //  of sun and surface orientation.
    1070             : 
    1071             :     // METHODOLOGY EMPLOYED:
    1072             :     //  Window screen solar beam transmittance varies as the sun moves across the sky
    1073             :     //  due to the geometry of the screen material and the angle of incidence
    1074             :     //  of the solar beam. Azimuth and altitude angle are calculated with respect
    1075             :     //  to the surface outward normal. Solar beam reflectance and absorptance are also
    1076             :     //  accounted for.
    1077             : 
    1078             :     //  CALLs to CalcScreenTransmittance are primarily based on surface index. A typical call is:
    1079             :     //  CALL CalcScreenTransmittance(SurfaceNum)
    1080             :     //  Since a single Material:WindowScreen object may be used for multiple windows, the
    1081             :     //  screen's direct beam properties are calculated for the screen material attached to this surface.
    1082             :     //  If a single Material:WindowScreen object is used for 3 windows then SurfaceScreens(3) is allocated.
    1083             : 
    1084             :     //  CALLs to CalcScreenTransmittance may be done by using the optional arguments as follows:
    1085             :     //  CALLs to CalcScreenTransmittance at normal incidence are:
    1086             :     //  CALL with a screen number and relative azimuth and altitude angles
    1087             :     //  CALL CalcScreenTransmittance(0, Phi=0.0, Theta=0.0, ScreenNumber=ScNum)
    1088             :     //   -OR-
    1089             :     //  CALL same as above using the material structure
    1090             :     //  CALL CalcScreenTransmittance(0, Phi=0.0, Theta=0.0, ScreenNumber=dataMaterial.Material(MatShade)%ScreenDataPtr)
    1091             :     //   -OR-
    1092             :     //  CALL with the surface number and relative azimuth and altitude angles
    1093             :     //  CALL CalcScreenTransmittance(SurfaceNum, Phi=0.0, Theta=0.0)
    1094             : 
    1095             :     //  CALL's passing the screen number without the relative azimuth and altitude angles is not allowed
    1096             :     //  CALL CalcScreenTransmittance(0, ScreenNumber=ScNum) ! DO NOT use this syntax
    1097             : 
    1098             :     // Locals
    1099             :     // FUNCTION ARGUMENT DEFINITIONS:
    1100             :     // The optional arguments Phi and Theta are used to integrate over a hemisphere and are passed as pairs
    1101             :     // The optional argument ScreenNumber is used during CalcWindowScreenProperties to integrate over a quarter hemispere
    1102             :     // "before" the surface # is known. Theta and Phi can be passed without ScreenNumber, but DO NOT pass ScreenNumber
    1103             :     // without Theta and Phi.
    1104             : 
    1105             :     // FUNCTION PARAMETER DEFINITIONS:
    1106        6562 :     Real64 constexpr Small(1.E-9); // Small Number used to approximate zero
    1107             : 
    1108             :     // FUNCTION PARAMETER DEFINITIONS:
    1109             :     int ScNum;                        // Index to screen data
    1110             :     Real64 Tdirect;                   // Beam solar transmitted through screen (dependent on sun angle)
    1111             :     Real64 Tscattered;                // Beam solar reflected through screen (dependent on sun angle)
    1112             :     Real64 TscatteredVis;             // Visible beam solar reflected through screen (dependent on sun angle)
    1113             :     Real64 SunAzimuth;                // Solar azimuth angle from north (rad)
    1114             :     Real64 SunAltitude;               // Solar altitude angle from horizon (rad)
    1115             :     Real64 SurfaceAzimuth;            // Surface azimuth angle from north (rad)
    1116             :     Real64 SurfaceTilt;               // Surface tilt angle from vertical (rad)
    1117             :     Real64 SunAzimuthToScreenNormal;  // Relative solar azimuth (sun angle from screen normal, 0 to PiOvr2, rad)
    1118             :     Real64 SunAltitudeToScreenNormal; // Relative solar altitude (sun angle from screen normal, -PiOvr2 to PiOvr2, rad)
    1119             :     Real64 Beta;                      // Compliment of relative solar azimuth (rad)
    1120             :     Real64 TransXDir;                 // Horizontal component of direct beam transmittance
    1121             :     Real64 TransYDir;                 // Vertical component of direct beam transmittance
    1122             :     Real64 Delta;                     // Intermediate variable used for Tscatter calculation (deg)
    1123             :     Real64 DeltaMax;                  // Intermediate variable used for Tscatter calculation (deg)
    1124             :     Real64 Tscattermax;               // Maximum solar beam  scattered transmittance
    1125             :     Real64 TscattermaxVis;            // Maximum visible beam scattered transmittance
    1126             :     Real64 ExponentInterior;          // Exponent used in scattered transmittance calculation
    1127             :     // when Delta < DeltaMax (0,0 to peak)
    1128             :     Real64 ExponentExterior; // Exponent used in scattered transmittance calculation
    1129             :     // when Delta > DeltaMax (peak to max)
    1130             :     Real64 AlphaDblPrime; // Intermediate variables (used in Eng. Doc.)
    1131             :     Real64 COSMu;
    1132             :     Real64 Epsilon;
    1133             :     Real64 Eta;
    1134             :     Real64 MuPrime;
    1135             :     Real64 Gamma;
    1136             :     Real64 NormalAltitude; // Actual altitude angle of sun wrt surface outward normal (rad)
    1137             :     Real64 NormalAzimuth;  // Actual azimuth angle of sun wrt surface outward normal (rad)
    1138             :     Real64 IncidentAngle;  // Solar angle wrt surface outward normal to determine
    1139             :     // if sun is in front of screen (rad)
    1140             :     Real64 PeakToPlateauRatio;    // Ratio of peak scattering to plateau at 0,0 incident angle
    1141             :     Real64 PeakToPlateauRatioVis; // Ratio of peak visible scattering to plateau at 0,0 incident angle
    1142             :     Real64 ReflectCyl;            // Screen material reflectance
    1143             :     Real64 ReflectCylVis;         // Screen material visible reflectance
    1144             : 
    1145             :     // SurfaceScreens structure may be accessed using either the surface or screen index
    1146             :     // The screen index is based on the number of Surface:HeatTransfer:Sub objects using any Material:WindowScreen object
    1147        6562 :     if (present(ScreenNumber)) {
    1148        3314 :         ScNum = ScreenNumber;
    1149        3314 :         if (!present(Theta) || !present(Phi)) {
    1150           0 :             ShowFatalError(state, "Syntax error, optional arguments Theta and Phi must be present when optional ScreenNumber is used.");
    1151             :         }
    1152             :     } else {
    1153        3248 :         ScNum = state.dataSurface->SurfWinScreenNumber(SurfaceNum);
    1154             :     }
    1155             : 
    1156        6562 :     if (present(Theta)) {
    1157        3314 :         SunAzimuthToScreenNormal = std::abs(Theta);
    1158        3314 :         if (SunAzimuthToScreenNormal > DataGlobalConstants::Pi) {
    1159           0 :             SunAzimuthToScreenNormal = 0.0;
    1160             :         } else {
    1161        3314 :             if (SunAzimuthToScreenNormal > DataGlobalConstants::PiOvr2) {
    1162           0 :                 SunAzimuthToScreenNormal = DataGlobalConstants::Pi - SunAzimuthToScreenNormal;
    1163             :             }
    1164             :         }
    1165        3314 :         NormalAzimuth = SunAzimuthToScreenNormal;
    1166             :     } else {
    1167        3248 :         SunAzimuth = std::atan2(state.dataEnvrn->SOLCOS(1), state.dataEnvrn->SOLCOS(2));
    1168        3248 :         if (SunAzimuth < 0.0) SunAzimuth += 2.0 * DataGlobalConstants::Pi;
    1169        3248 :         SurfaceAzimuth = state.dataSurface->Surface(SurfaceNum).Azimuth * DataGlobalConstants::DegToRadians;
    1170        3248 :         NormalAzimuth = SunAzimuth - SurfaceAzimuth;
    1171             :         //   Calculate the transmittance whether sun is in front of or behind screen, place result in BmBmTrans or BmBmTransBack
    1172        3248 :         if (std::abs(SunAzimuth - SurfaceAzimuth) > DataGlobalConstants::PiOvr2) {
    1173        1792 :             SunAzimuthToScreenNormal = std::abs(SunAzimuth - SurfaceAzimuth) - DataGlobalConstants::PiOvr2;
    1174             :         } else {
    1175        1456 :             SunAzimuthToScreenNormal = std::abs(SunAzimuth - SurfaceAzimuth);
    1176             :         }
    1177             :     }
    1178             : 
    1179        6562 :     if (present(Phi)) {
    1180        3314 :         SunAltitudeToScreenNormal = std::abs(Phi);
    1181        3314 :         if (SunAltitudeToScreenNormal > DataGlobalConstants::PiOvr2) {
    1182           0 :             SunAltitudeToScreenNormal = DataGlobalConstants::Pi - SunAltitudeToScreenNormal;
    1183             :         }
    1184        3314 :         SunAltitude = SunAltitudeToScreenNormal;
    1185             :     } else {
    1186        3248 :         SunAltitude = (DataGlobalConstants::PiOvr2 - std::acos(state.dataEnvrn->SOLCOS(3)));
    1187        3248 :         SurfaceTilt = state.dataSurface->Surface(SurfaceNum).Tilt * DataGlobalConstants::DegToRadians;
    1188        3248 :         SunAltitudeToScreenNormal = std::abs(SunAltitude + (SurfaceTilt - DataGlobalConstants::PiOvr2));
    1189        3248 :         if (SunAltitudeToScreenNormal > DataGlobalConstants::PiOvr2) {
    1190           0 :             SunAltitudeToScreenNormal -= DataGlobalConstants::PiOvr2;
    1191             :         }
    1192             :     }
    1193             : 
    1194        6562 :     if (SurfaceNum == 0 || !present(ScreenNumber)) {
    1195        6562 :         NormalAltitude = SunAltitude;
    1196             :     } else {
    1197           0 :         NormalAltitude = SunAltitude + (SurfaceTilt - DataGlobalConstants::PiOvr2);
    1198             :     }
    1199             : 
    1200        6562 :     if (NormalAltitude != 0.0 && NormalAzimuth != 0.0) {
    1201        6208 :         IncidentAngle = std::acos(std::sin(NormalAltitude) / (std::tan(NormalAzimuth) * std::tan(NormalAltitude) / std::sin(NormalAzimuth)));
    1202         354 :     } else if (NormalAltitude != 0.0 && NormalAzimuth == 0.0) {
    1203          36 :         IncidentAngle = NormalAltitude;
    1204         318 :     } else if (NormalAltitude == 0.0 && NormalAzimuth != 0.0) {
    1205         172 :         IncidentAngle = NormalAzimuth;
    1206             :     } else {
    1207         146 :         IncidentAngle = 0.0;
    1208             :     }
    1209             : 
    1210        6562 :     auto &thisScreen = state.dataHeatBal->SurfaceScreens(ScNum);
    1211             : 
    1212             :     // ratio of screen material diameter to screen material spacing
    1213        6562 :     Gamma = thisScreen.ScreenDiameterToSpacingRatio;
    1214             : 
    1215             :     // ************************************************************************************************
    1216             :     // * calculate transmittance of totally absorbing screen material (beam passing through open area)*
    1217             :     // ************************************************************************************************
    1218             : 
    1219             :     // calculate compliment of relative solar azimuth
    1220        6562 :     Beta = DataGlobalConstants::PiOvr2 - SunAzimuthToScreenNormal;
    1221             : 
    1222             :     // Catch all divide by zero instances
    1223        6562 :     if (Beta > Small) {
    1224        5782 :         if (std::abs(SunAltitudeToScreenNormal - DataGlobalConstants::PiOvr2) > Small) {
    1225        5746 :             AlphaDblPrime = std::atan(std::tan(SunAltitudeToScreenNormal) / std::cos(SunAzimuthToScreenNormal));
    1226       11492 :             TransYDir = 1.0 - Gamma * (std::cos(AlphaDblPrime) +
    1227        5746 :                                        std::sin(AlphaDblPrime) * std::tan(SunAltitudeToScreenNormal) * std::sqrt(1.0 + pow_2(1.0 / std::tan(Beta))));
    1228        5746 :             TransYDir = max(0.0, TransYDir);
    1229             :         } else {
    1230          36 :             TransYDir = 0.0;
    1231             :         }
    1232             :     } else {
    1233         780 :         TransYDir = 0.0;
    1234             :     }
    1235             : 
    1236       13124 :     COSMu = std::sqrt(pow_2(std::cos(SunAltitudeToScreenNormal)) * pow_2(std::cos(SunAzimuthToScreenNormal)) +
    1237        6562 :                       pow_2(std::sin(SunAltitudeToScreenNormal)));
    1238        6562 :     if (COSMu > Small) {
    1239        6560 :         Epsilon = std::acos(std::cos(SunAltitudeToScreenNormal) * std::cos(SunAzimuthToScreenNormal) / COSMu);
    1240        6560 :         Eta = DataGlobalConstants::PiOvr2 - Epsilon;
    1241        6560 :         if (std::cos(Epsilon) != 0.0) {
    1242        6560 :             MuPrime = std::atan(std::tan(std::acos(COSMu)) / std::cos(Epsilon));
    1243        6560 :             if (Eta != 0.0) {
    1244        6494 :                 TransXDir =
    1245        6494 :                     1.0 - Gamma * (std::cos(MuPrime) + std::sin(MuPrime) * std::tan(std::acos(COSMu)) * std::sqrt(1.0 + pow_2(1.0 / std::tan(Eta))));
    1246        6494 :                 TransXDir = max(0.0, TransXDir);
    1247             :             } else {
    1248          66 :                 TransXDir = 0.0;
    1249             :             }
    1250             :         } else {
    1251           0 :             TransXDir = 0.0;
    1252             :         }
    1253             :     } else {
    1254           2 :         TransXDir = 1.0 - Gamma;
    1255             :     }
    1256        6562 :     Tdirect = max(0.0, TransXDir * TransYDir);
    1257             : 
    1258             :     // *******************************************************************************
    1259             :     // * calculate transmittance of scattered beam due to reflecting screen material *
    1260             :     // *******************************************************************************
    1261             : 
    1262        6562 :     ReflectCyl = thisScreen.ReflectCylinder;
    1263        6562 :     ReflectCylVis = thisScreen.ReflectCylinderVis;
    1264             : 
    1265       13086 :     if (std::abs(SunAzimuthToScreenNormal - DataGlobalConstants::PiOvr2) < Small ||
    1266        6524 :         std::abs(SunAltitudeToScreenNormal - DataGlobalConstants::PiOvr2) < Small) {
    1267          74 :         Tscattered = 0.0;
    1268          74 :         TscatteredVis = 0.0;
    1269             :     } else {
    1270             :         //   DeltaMax and Delta are in degrees
    1271        6488 :         DeltaMax = 89.7 - (10.0 * Gamma / 0.16);
    1272       12976 :         Delta = std::sqrt(pow_2(SunAzimuthToScreenNormal / DataGlobalConstants::DegToRadians) +
    1273        6488 :                           pow_2(SunAltitudeToScreenNormal / DataGlobalConstants::DegToRadians));
    1274             : 
    1275             :         //   Use empirical model to determine maximum (peak) scattering
    1276        6488 :         Tscattermax = 0.0229 * Gamma + 0.2971 * ReflectCyl - 0.03624 * pow_2(Gamma) + 0.04763 * pow_2(ReflectCyl) - 0.44416 * Gamma * ReflectCyl;
    1277        6488 :         TscattermaxVis =
    1278        6488 :             0.0229 * Gamma + 0.2971 * ReflectCylVis - 0.03624 * pow_2(Gamma) + 0.04763 * pow_2(ReflectCylVis) - 0.44416 * Gamma * ReflectCylVis;
    1279             : 
    1280             :         //   Vary slope of interior and exterior surface of scattering model
    1281        6488 :         ExponentInterior = -pow_2(Delta - DeltaMax) / 600.0;
    1282        6488 :         ExponentExterior = -std::pow(std::abs(Delta - DeltaMax), 2.5) / 600.0;
    1283             : 
    1284             :         //   Determine ratio of scattering at 0,0 incident angle to maximum (peak) scattering
    1285        6488 :         PeakToPlateauRatio = 1.0 / (0.2 * (1 - Gamma) * ReflectCyl);
    1286        6488 :         PeakToPlateauRatioVis = 1.0 / (0.2 * (1 - Gamma) * ReflectCylVis);
    1287             : 
    1288        6488 :         if (Delta > DeltaMax) {
    1289             :             //     Apply offset for plateau and use exterior exponential function to simulate actual scattering as a function of solar angles
    1290        2266 :             Tscattered = 0.2 * (1.0 - Gamma) * ReflectCyl * Tscattermax * (1.0 + (PeakToPlateauRatio - 1.0) * std::exp(ExponentExterior));
    1291        2266 :             TscatteredVis = 0.2 * (1.0 - Gamma) * ReflectCylVis * TscattermaxVis * (1.0 + (PeakToPlateauRatioVis - 1.0) * std::exp(ExponentExterior));
    1292             :             //     Trim off offset if solar angle (delta) is greater than maximum (peak) scattering angle
    1293        2266 :             Tscattered -= (0.2 * (1.0 - Gamma) * ReflectCyl * Tscattermax) * max(0.0, (Delta - DeltaMax) / (90.0 - DeltaMax));
    1294        2266 :             TscatteredVis -= (0.2 * (1.0 - Gamma) * ReflectCylVis * TscattermaxVis) * max(0.0, (Delta - DeltaMax) / (90.0 - DeltaMax));
    1295             :         } else {
    1296             :             //     Apply offset for plateau and use interior exponential function to simulate actual scattering as a function of solar angles
    1297        4222 :             Tscattered = 0.2 * (1.0 - Gamma) * ReflectCyl * Tscattermax * (1.0 + (PeakToPlateauRatio - 1.0) * std::exp(ExponentInterior));
    1298        4222 :             TscatteredVis = 0.2 * (1.0 - Gamma) * ReflectCylVis * TscattermaxVis * (1.0 + (PeakToPlateauRatioVis - 1.0) * std::exp(ExponentInterior));
    1299             :         }
    1300             :     }
    1301        6562 :     Tscattered = max(0.0, Tscattered);
    1302        6562 :     TscatteredVis = max(0.0, TscatteredVis);
    1303             : 
    1304        6562 :     if (thisScreen.screenBeamReflectanceModel == DataSurfaces::ScreenBeamReflectanceModel::DoNotModel) {
    1305           0 :         if (std::abs(IncidentAngle) <= DataGlobalConstants::PiOvr2) {
    1306           0 :             thisScreen.BmBmTrans = Tdirect;
    1307           0 :             thisScreen.BmBmTransVis = Tdirect;
    1308           0 :             thisScreen.BmBmTransBack = 0.0;
    1309             :         } else {
    1310           0 :             thisScreen.BmBmTrans = 0.0;
    1311           0 :             thisScreen.BmBmTransVis = 0.0;
    1312           0 :             thisScreen.BmBmTransBack = Tdirect;
    1313             :         }
    1314           0 :         Tscattered = 0.0;
    1315           0 :         TscatteredVis = 0.0;
    1316        6562 :     } else if (thisScreen.screenBeamReflectanceModel == DataSurfaces::ScreenBeamReflectanceModel::DirectBeam) {
    1317           0 :         if (std::abs(IncidentAngle) <= DataGlobalConstants::PiOvr2) {
    1318           0 :             thisScreen.BmBmTrans = Tdirect + Tscattered;
    1319           0 :             thisScreen.BmBmTransVis = Tdirect + TscatteredVis;
    1320           0 :             thisScreen.BmBmTransBack = 0.0;
    1321             :         } else {
    1322           0 :             thisScreen.BmBmTrans = 0.0;
    1323           0 :             thisScreen.BmBmTransVis = 0.0;
    1324           0 :             thisScreen.BmBmTransBack = Tdirect + Tscattered;
    1325             :         }
    1326           0 :         Tscattered = 0.0;
    1327           0 :         TscatteredVis = 0.0;
    1328        6562 :     } else if (thisScreen.screenBeamReflectanceModel == DataSurfaces::ScreenBeamReflectanceModel::Diffuse) {
    1329        6562 :         if (std::abs(IncidentAngle) <= DataGlobalConstants::PiOvr2) {
    1330        4938 :             thisScreen.BmBmTrans = Tdirect;
    1331        4938 :             thisScreen.BmBmTransVis = Tdirect;
    1332        4938 :             thisScreen.BmBmTransBack = 0.0;
    1333             :         } else {
    1334        1624 :             thisScreen.BmBmTrans = 0.0;
    1335        1624 :             thisScreen.BmBmTransVis = 0.0;
    1336        1624 :             thisScreen.BmBmTransBack = Tdirect;
    1337             :         }
    1338             :     }
    1339             : 
    1340        6562 :     if (std::abs(IncidentAngle) <= DataGlobalConstants::PiOvr2) {
    1341        4938 :         thisScreen.BmDifTrans = Tscattered;
    1342        4938 :         thisScreen.BmDifTransVis = TscatteredVis;
    1343        4938 :         thisScreen.BmDifTransBack = 0.0;
    1344        4938 :         thisScreen.ReflectSolBeamFront = max(0.0, ReflectCyl * (1.0 - Tdirect) - Tscattered);
    1345        4938 :         thisScreen.ReflectVisBeamFront = max(0.0, ReflectCylVis * (1.0 - Tdirect) - TscatteredVis);
    1346        4938 :         thisScreen.AbsorpSolarBeamFront = max(0.0, (1.0 - Tdirect) * (1.0 - ReflectCyl));
    1347        4938 :         thisScreen.ReflectSolBeamBack = 0.0;
    1348        4938 :         thisScreen.ReflectVisBeamBack = 0.0;
    1349        4938 :         thisScreen.AbsorpSolarBeamBack = 0.0;
    1350             :     } else {
    1351        1624 :         thisScreen.BmDifTrans = 0.0;
    1352        1624 :         thisScreen.BmDifTransVis = 0.0;
    1353        1624 :         thisScreen.BmDifTransBack = Tscattered;
    1354        1624 :         thisScreen.ReflectSolBeamBack = max(0.0, ReflectCyl * (1.0 - Tdirect) - Tscattered);
    1355        1624 :         thisScreen.ReflectVisBeamBack = max(0.0, ReflectCylVis * (1.0 - Tdirect) - TscatteredVis);
    1356        1624 :         thisScreen.AbsorpSolarBeamBack = max(0.0, (1.0 - Tdirect) * (1.0 - ReflectCyl));
    1357        1624 :         thisScreen.ReflectSolBeamFront = 0.0;
    1358        1624 :         thisScreen.ReflectVisBeamFront = 0.0;
    1359        1624 :         thisScreen.AbsorpSolarBeamFront = 0.0;
    1360             :     }
    1361        6562 : }
    1362             : 
    1363        2034 : std::string DisplayMaterialRoughness(DataSurfaces::SurfaceRoughness const Roughness) // Roughness String
    1364             : {
    1365             : 
    1366             :     // SUBROUTINE INFORMATION:
    1367             :     //       AUTHOR         Linda K. Lawrie
    1368             :     //       DATE WRITTEN   October 2005
    1369             :     //       MODIFIED       na
    1370             :     //       RE-ENGINEERED  na
    1371             : 
    1372             :     // PURPOSE OF THIS SUBROUTINE:
    1373             :     // This subroutine is given a roughness value and returns the character representation.
    1374             : 
    1375             :     // Return value
    1376        2034 :     std::string cRoughness; // Character representation of Roughness
    1377             : 
    1378             :     // Select the correct Number for the associated ascii name for the roughness type
    1379        2034 :     switch (Roughness) {
    1380         191 :     case DataSurfaces::SurfaceRoughness::VeryRough: {
    1381         191 :         cRoughness = "VeryRough";
    1382         191 :     } break;
    1383         515 :     case DataSurfaces::SurfaceRoughness::Rough: {
    1384         515 :         cRoughness = "Rough";
    1385         515 :     } break;
    1386         481 :     case DataSurfaces::SurfaceRoughness::MediumRough: {
    1387         481 :         cRoughness = "MediumRough";
    1388         481 :     } break;
    1389         294 :     case DataSurfaces::SurfaceRoughness::MediumSmooth: {
    1390         294 :         cRoughness = "MediumSmooth";
    1391         294 :     } break;
    1392         533 :     case DataSurfaces::SurfaceRoughness::Smooth: {
    1393         533 :         cRoughness = "Smooth";
    1394         533 :     } break;
    1395          20 :     case DataSurfaces::SurfaceRoughness::VerySmooth: {
    1396          20 :         cRoughness = "VerySmooth";
    1397          20 :     } break;
    1398           0 :     default: {
    1399           0 :         cRoughness = "";
    1400           0 :     } break;
    1401             :     }
    1402             : 
    1403        2034 :     return cRoughness;
    1404             : }
    1405             : 
    1406       42220 : Real64 ComputeNominalUwithConvCoeffs(EnergyPlusData &state,
    1407             :                                      int const numSurf, // index for Surface array.
    1408             :                                      bool &isValid      // returns true if result is valid
    1409             : )
    1410             : {
    1411             : 
    1412             :     // SUBROUTINE INFORMATION:
    1413             :     //       AUTHOR         Jason Glazer
    1414             :     //       DATE WRITTEN   September 2013
    1415             :     //       MODIFIED       na
    1416             :     //       RE-ENGINEERED  na
    1417             : 
    1418             :     // PURPOSE OF THIS SUBROUTINE:
    1419             :     // Calculate Nominal U-value with convection/film coefficients for reporting by
    1420             :     // adding on prescribed R-values for interior and exterior convection coefficients
    1421             :     // as found in ASHRAE 90.1-2004, Appendix A. Used in EIO and tabular reports.
    1422             :     // ASHRAE 90.1-2004 Section A9.4.1 shows the following:
    1423             :     //      R-value Condition
    1424             :     //      All exterior conditions                        IP: 0.17  SI: 0.0299
    1425             :     //      All semi-exterior surfaces                     IP: 0.46  SI: 0.0810
    1426             :     //      Interior horizontal surfaces, heat flow up     IP: 0.61  SI: 0.1074
    1427             :     //      Interior horizontal surfaces, heat flow down   IP: 0.92  SI: 0.1620
    1428             :     //      Interior vertical surfaces                     IP: 0.68  SI: 0.1198
    1429             :     // This section shows the same value in 90.1-2010 and 90.2-2010
    1430             :     // Note that this report does not use the semi-exterior surface value because
    1431             :     // EnergyPlus does not have a way to specifically tell whether or not a surface
    1432             :     // is connected to a semi-exterior area of the building.  Users can always use
    1433             :     // the Nominal U-Value to manually calculated this.  The values calculated here
    1434             :     // are simply reported to the EIO file and not used for any calculations.
    1435             : 
    1436             :     // Return value
    1437             :     Real64 NominalUwithConvCoeffs; // return value
    1438             : 
    1439             :     static constexpr std::array<Real64, static_cast<int>(DataSurfaces::SurfaceClass::Num)> filmCoefs = {
    1440             :         0.0,       // None
    1441             :         0.1197548, // Wall
    1442             :         0.1620212, // Floor
    1443             :         0.1074271, // Roof
    1444             :         0.0,       // IntMass
    1445             :         0.0,       // Detached_B
    1446             :         0.0,       // Detached_F
    1447             :         0.1197548, // Window
    1448             :         0.1197548, // GlassDoor
    1449             :         0.1197548, // Door
    1450             :         0.0,       // Shading
    1451             :         0.0,       // Overhang
    1452             :         0.0,       // Fin
    1453             :         0.0,       // TDD_Dome
    1454             :         0.0        // TDD_Diffuser
    1455             :     };             // If anything added to the enum SurfaceClass, adjust this list appropriately
    1456             : 
    1457             :     Real64 insideFilm;
    1458             :     Real64 outsideFilm;
    1459             : 
    1460       42220 :     isValid = true;
    1461             : 
    1462       42220 :     auto &thisSurface = state.dataSurface->Surface(numSurf);
    1463             : 
    1464             :     // exterior conditions
    1465       42220 :     switch (thisSurface.ExtBoundCond) {
    1466       17079 :     case DataSurfaces::ExternalEnvironment: { // ExtBoundCond = 0
    1467       17079 :         outsideFilm = 0.0299387;              // All exterior conditions
    1468       17079 :     } break;
    1469           1 :     case DataSurfaces::OtherSideCoefCalcExt: {
    1470           1 :         outsideFilm = state.dataSurface->OSC(thisSurface.OSCPtr).SurfFilmCoef;
    1471           1 :     } break;
    1472        2408 :     case DataSurfaces::Ground:
    1473             :     case DataSurfaces::OtherSideCoefNoCalcExt:
    1474             :     case DataSurfaces::OtherSideCondModeledExt:
    1475             :     case DataSurfaces::GroundFCfactorMethod:
    1476             :     case DataSurfaces::KivaFoundation: { // All these cases have a negative ExtBoundCond so don't use film coefficients
    1477        2408 :         outsideFilm = 0.0;
    1478        2408 :     } break;
    1479       22732 :     default: { // Interior Surface Attached to a Zone (ExtBoundCond is a surface)
    1480       22732 :         outsideFilm = filmCoefs[static_cast<int>(state.dataSurface->Surface(thisSurface.ExtBoundCond).Class)];
    1481       22732 :     } break;
    1482             :     }
    1483             :     // interior conditions and calculate the return value
    1484       42220 :     if (state.dataHeatBal->NominalU(thisSurface.Construction) > 0.0) {
    1485       42206 :         insideFilm = filmCoefs[static_cast<int>(thisSurface.Class)];
    1486       42206 :         if (insideFilm == 0.0) outsideFilm = 0.0;
    1487       42206 :         NominalUwithConvCoeffs =
    1488       42206 :             1.0 / (insideFilm + (1.0 / state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction)) + outsideFilm);
    1489             :     } else {
    1490          14 :         isValid = false;
    1491          14 :         NominalUwithConvCoeffs = state.dataHeatBal->NominalU(state.dataSurface->Surface(numSurf).Construction);
    1492             :     }
    1493             : 
    1494       42220 :     return NominalUwithConvCoeffs;
    1495             : }
    1496             : 
    1497         771 : void SetFlagForWindowConstructionWithShadeOrBlindLayer(EnergyPlusData &state)
    1498             : {
    1499             : 
    1500             :     // PURPOSE OF THIS SUBROUTINE:
    1501             :     // check fenestrations with shading control and set a flag to true if its construction has
    1502             :     // either shade or blind material layer
    1503             : 
    1504             :     // METHODOLOGY EMPLOYED:
    1505             :     // Loop through Surface and register any shading controls, and loop through the construction
    1506             :     // material layer
    1507             : 
    1508             :     // Using/Aliasing
    1509             :     using DataSurfaces::ExternalEnvironment;
    1510             : 
    1511             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1512         771 :     int loopSurfNum(0); // surface index
    1513         771 :     int ConstrNum(0);   // construction index
    1514         771 :     int NumLayers(0);   // number of material layers in a construction
    1515         771 :     int Layer(0);       // construction material layer index
    1516         771 :     int MaterNum(0);    // construction material index
    1517             : 
    1518       44533 :     for (loopSurfNum = 1; loopSurfNum <= state.dataSurface->TotSurfaces; ++loopSurfNum) {
    1519             : 
    1520       43762 :         if (state.dataSurface->Surface(loopSurfNum).Class != DataSurfaces::SurfaceClass::Window) continue;
    1521        5974 :         if (state.dataSurface->Surface(loopSurfNum).ExtBoundCond != ExternalEnvironment) continue;
    1522        5960 :         if (!state.dataSurface->Surface(loopSurfNum).HasShadeControl) continue;
    1523         143 :         if (state.dataSurface->Surface(loopSurfNum).activeShadedConstruction == 0) continue;
    1524             : 
    1525         143 :         ConstrNum = state.dataSurface->Surface(loopSurfNum).activeShadedConstruction;
    1526         143 :         if (state.dataConstruction->Construct(ConstrNum).TypeIsWindow) {
    1527         143 :             NumLayers = state.dataConstruction->Construct(ConstrNum).TotLayers;
    1528         586 :             for (Layer = 1; Layer <= NumLayers; ++Layer) {
    1529         443 :                 MaterNum = state.dataConstruction->Construct(ConstrNum).LayerPoint(Layer);
    1530         443 :                 if (MaterNum == 0) continue;
    1531         815 :                 if (state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::Shade ||
    1532         372 :                     state.dataMaterial->Material(MaterNum).Group == DataHeatBalance::MaterialGroup::WindowBlind)
    1533         105 :                     state.dataSurface->SurfWinHasShadeOrBlindLayer(loopSurfNum) = true;
    1534             :             }
    1535             :         }
    1536             :     }
    1537         771 : }
    1538             : 
    1539         771 : void AllocateIntGains(EnergyPlusData &state)
    1540             : {
    1541         771 :     state.dataHeatBal->ZoneIntGain.allocate(state.dataGlobal->NumOfZones);
    1542         771 :     state.dataHeatBal->spaceIntGain.allocate(state.dataGlobal->numSpaces);
    1543         771 :     state.dataHeatBal->spaceIntGainDevices.allocate(state.dataGlobal->numSpaces);
    1544         771 :     state.dataDaylightingData->spacePowerReductionFactor.dimension(state.dataGlobal->numSpaces, 1.0);
    1545         771 : }
    1546             : 
    1547        2313 : } // namespace EnergyPlus::DataHeatBalance

Generated by: LCOV version 1.13