LCOV - code coverage report
Current view: top level - EnergyPlus - ThermalComfort.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 52.1 % 1631 850
Test Date: 2025-05-22 16:09:37 Functions: 73.3 % 30 22

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <algorithm>
      50              : #include <boost/math/tools/roots.hpp>
      51              : #include <cassert>
      52              : #include <cmath>
      53              : 
      54              : // ObjexxFCL Headers
      55              : #include <ObjexxFCL/string.functions.hh>
      56              : 
      57              : // EnergyPlus Headers
      58              : #include <EnergyPlus/Construction.hh>
      59              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      60              : #include <EnergyPlus/DataEnvironment.hh>
      61              : #include <EnergyPlus/DataHeatBalFanSys.hh>
      62              : #include <EnergyPlus/DataHeatBalSurface.hh>
      63              : #include <EnergyPlus/DataHeatBalance.hh>
      64              : #include <EnergyPlus/DataIPShortCuts.hh>
      65              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      66              : #include <EnergyPlus/DataRoomAirModel.hh>
      67              : #include <EnergyPlus/DataViewFactorInformation.hh>
      68              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      69              : #include <EnergyPlus/FileSystem.hh>
      70              : #include <EnergyPlus/General.hh>
      71              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      72              : #include <EnergyPlus/OutputProcessor.hh>
      73              : #include <EnergyPlus/OutputReportPredefined.hh>
      74              : #include <EnergyPlus/OutputReportTabular.hh>
      75              : #include <EnergyPlus/Psychrometrics.hh>
      76              : #include <EnergyPlus/ScheduleManager.hh>
      77              : #include <EnergyPlus/ThermalComfort.hh>
      78              : #include <EnergyPlus/UtilityRoutines.hh>
      79              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      80              : 
      81              : namespace EnergyPlus {
      82              : 
      83              : namespace ThermalComfort {
      84              : 
      85              :     // Module containing the routines dealing with the CalcThermalComfortFanger,
      86              :     // CalcThermalComfortPierce, and CalcThermalComfortKSU
      87              : 
      88              :     // MODULE INFORMATION:
      89              :     //       AUTHOR         Jaewook Lee
      90              :     //       DATE WRITTEN   January 2000
      91              :     //       MODIFIED       Rick Strand (for E+ implementation February 2000)
      92              : 
      93              :     // PURPOSE OF THIS MODULE:
      94              :     // To calculate thermal comfort indices based on the
      95              :     // three thermal comfort prediction models (Fanger, Pierce, KSU)
      96              : 
      97              :     // METHODOLOGY EMPLOYED:
      98              :     // For each thermal comfort model type, the subroutines will loop through
      99              :     // the people statements and perform the requested thermal comfort evaluations
     100              : 
     101              :     // Using/Aliasing
     102              :     using DataHeatBalance::PeopleData;
     103              :     using Psychrometrics::PsyRhFnTdbWPb;
     104              : 
     105       249945 :     void ManageThermalComfort(EnergyPlusData &state, bool const InitializeOnly) // when called from ZTPC and calculations aren't needed
     106              :     {
     107              : 
     108              :         // SUBROUTINE INFORMATION:
     109              :         //     AUTHOR         Rick Strand
     110              :         //     DATE WRITTEN   February 2000
     111              : 
     112       249945 :         if (state.dataThermalComforts->FirstTimeFlag) {
     113          103 :             InitThermalComfort(state); // Mainly sets up output stuff
     114          103 :             state.dataThermalComforts->FirstTimeFlag = false;
     115              :         }
     116              : 
     117       249945 :         if (state.dataGlobal->DayOfSim == 1) {
     118        57959 :             if (state.dataGlobal->HourOfDay < 7) {
     119        14819 :                 state.dataThermalComforts->TemporarySixAMTemperature = 1.868132;
     120        43140 :             } else if (state.dataGlobal->HourOfDay == 7) {
     121         2382 :                 if (state.dataGlobal->TimeStep == 1) {
     122          432 :                     state.dataThermalComforts->TemporarySixAMTemperature = state.dataEnvrn->OutDryBulbTemp;
     123              :                 }
     124              :             }
     125              :         } else {
     126       191986 :             if (state.dataGlobal->HourOfDay == 7) {
     127         7999 :                 if (state.dataGlobal->TimeStep == 1) {
     128         1601 :                     state.dataThermalComforts->TemporarySixAMTemperature = state.dataEnvrn->OutDryBulbTemp;
     129              :                 }
     130              :             }
     131              :         }
     132              : 
     133       249945 :         if (InitializeOnly) return;
     134              : 
     135       249945 :         if (state.dataGlobal->BeginEnvrnFlag) {
     136          482 :             state.dataThermalComforts->ZoneOccHrs = 0.0;
     137              :         }
     138              : 
     139       249945 :         if (!state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
     140        18995 :             CalcThermalComfortFanger(state);
     141        18995 :             if (state.dataHeatBal->AnyThermalComfortPierceModel) CalcThermalComfortPierceASHRAE(state);
     142        18995 :             if (state.dataHeatBal->AnyThermalComfortKSUModel) CalcThermalComfortKSU(state);
     143        18995 :             if (state.dataHeatBal->AnyThermalComfortCoolingEffectModel) CalcThermalComfortCoolingEffectASH(state);
     144        18995 :             if (state.dataHeatBal->AnyThermalComfortAnkleDraftModel) CalcThermalComfortAnkleDraftASH(state);
     145        18995 :             CalcThermalComfortSimpleASH55(state);
     146        18995 :             CalcIfSetPointMet(state);
     147        18995 :             if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) CalcThermalComfortAdaptiveASH55(state, false);
     148        18995 :             if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) CalcThermalComfortAdaptiveCEN15251(state, false);
     149              :         }
     150              :     }
     151              : 
     152          103 :     void InitThermalComfort(EnergyPlusData &state)
     153              :     {
     154              : 
     155              :         // SUBROUTINE INFORMATION:
     156              :         //     AUTHOR         Rick Strand
     157              :         //     DATE WRITTEN   February 2000
     158              : 
     159              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     160              :         int Loop; // DO loop counter
     161          103 :         std::string CurrentGroupName;
     162              : 
     163          103 :         state.dataThermalComforts->ThermalComfortData.allocate(state.dataHeatBal->TotPeople);
     164              : 
     165          154 :         for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
     166              : 
     167           51 :             CurrentGroupName = state.dataHeatBal->People(Loop).Name;
     168              : 
     169              :             // CurrentModuleObject='People'
     170              :             // MJW MRT ToDo: Rename most Zone Thermal Comfort output variables to People Thermal Comfort ('cause they're keyed by People name)
     171           51 :             if (state.dataHeatBal->People(Loop).Fanger) {
     172           24 :                 SetupOutputVariable(state,
     173              :                                     "Zone Thermal Comfort Fanger Model PMV",
     174              :                                     Constant::Units::None,
     175           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).FangerPMV,
     176              :                                     OutputProcessor::TimeStepType::Zone,
     177              :                                     OutputProcessor::StoreType::Average,
     178           12 :                                     state.dataHeatBal->People(Loop).Name);
     179           24 :                 SetupOutputVariable(state,
     180              :                                     "Zone Thermal Comfort Fanger Model PPD",
     181              :                                     Constant::Units::Perc,
     182           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).FangerPPD,
     183              :                                     OutputProcessor::TimeStepType::Zone,
     184              :                                     OutputProcessor::StoreType::Average,
     185           12 :                                     state.dataHeatBal->People(Loop).Name);
     186           24 :                 SetupOutputVariable(state,
     187              :                                     "Zone Thermal Comfort Clothing Surface Temperature",
     188              :                                     Constant::Units::C,
     189           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CloSurfTemp,
     190              :                                     OutputProcessor::TimeStepType::Zone,
     191              :                                     OutputProcessor::StoreType::Average,
     192           12 :                                     state.dataHeatBal->People(Loop).Name);
     193              :             }
     194              : 
     195           51 :             if (state.dataHeatBal->People(Loop).Pierce) {
     196            0 :                 SetupOutputVariable(state,
     197              :                                     "Zone Thermal Comfort Pierce Model Effective Temperature PMV",
     198              :                                     Constant::Units::None,
     199            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PiercePMVET,
     200              :                                     OutputProcessor::TimeStepType::Zone,
     201              :                                     OutputProcessor::StoreType::Average,
     202            0 :                                     state.dataHeatBal->People(Loop).Name);
     203            0 :                 SetupOutputVariable(state,
     204              :                                     "Zone Thermal Comfort Pierce Model Standard Effective Temperature PMV",
     205              :                                     Constant::Units::None,
     206            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PiercePMVSET,
     207              :                                     OutputProcessor::TimeStepType::Zone,
     208              :                                     OutputProcessor::StoreType::Average,
     209            0 :                                     state.dataHeatBal->People(Loop).Name);
     210            0 :                 SetupOutputVariable(state,
     211              :                                     "Zone Thermal Comfort Pierce Model Discomfort Index",
     212              :                                     Constant::Units::None,
     213            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PierceDISC,
     214              :                                     OutputProcessor::TimeStepType::Zone,
     215              :                                     OutputProcessor::StoreType::Average,
     216            0 :                                     state.dataHeatBal->People(Loop).Name);
     217            0 :                 SetupOutputVariable(state,
     218              :                                     "Zone Thermal Comfort Pierce Model Thermal Sensation Index",
     219              :                                     Constant::Units::None,
     220            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PierceTSENS,
     221              :                                     OutputProcessor::TimeStepType::Zone,
     222              :                                     OutputProcessor::StoreType::Average,
     223            0 :                                     state.dataHeatBal->People(Loop).Name);
     224            0 :                 SetupOutputVariable(state,
     225              :                                     "Zone Thermal Comfort Pierce Model Standard Effective Temperature",
     226              :                                     Constant::Units::C,
     227            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PierceSET,
     228              :                                     OutputProcessor::TimeStepType::Zone,
     229              :                                     OutputProcessor::StoreType::Average,
     230            0 :                                     state.dataHeatBal->People(Loop).Name);
     231              :             }
     232              : 
     233           51 :             if (state.dataHeatBal->People(Loop).KSU) {
     234            0 :                 SetupOutputVariable(state,
     235              :                                     "Zone Thermal Comfort KSU Model Thermal Sensation Vote",
     236              :                                     Constant::Units::None,
     237            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).KsuTSV,
     238              :                                     OutputProcessor::TimeStepType::Zone,
     239              :                                     OutputProcessor::StoreType::Average,
     240            0 :                                     state.dataHeatBal->People(Loop).Name);
     241              :             }
     242              : 
     243           51 :             if ((state.dataHeatBal->People(Loop).Fanger) || (state.dataHeatBal->People(Loop).Pierce) || (state.dataHeatBal->People(Loop).KSU)) {
     244           24 :                 SetupOutputVariable(state,
     245              :                                     "Zone Thermal Comfort Mean Radiant Temperature",
     246              :                                     Constant::Units::C,
     247           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortMRT,
     248              :                                     OutputProcessor::TimeStepType::Zone,
     249              :                                     OutputProcessor::StoreType::Average,
     250           12 :                                     state.dataHeatBal->People(Loop).Name);
     251           24 :                 SetupOutputVariable(state,
     252              :                                     "Zone Thermal Comfort Operative Temperature",
     253              :                                     Constant::Units::C,
     254           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortOpTemp,
     255              :                                     OutputProcessor::TimeStepType::Zone,
     256              :                                     OutputProcessor::StoreType::Average,
     257           12 :                                     state.dataHeatBal->People(Loop).Name);
     258           24 :                 SetupOutputVariable(state,
     259              :                                     "Zone Thermal Comfort Clothing Value",
     260              :                                     Constant::Units::clo,
     261           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ClothingValue,
     262              :                                     OutputProcessor::TimeStepType::Zone,
     263              :                                     OutputProcessor::StoreType::Average,
     264           12 :                                     state.dataHeatBal->People(Loop).Name);
     265              :             }
     266              : 
     267           51 :             if (state.dataHeatBal->People(Loop).AdaptiveASH55) {
     268            0 :                 SetupOutputVariable(state,
     269              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model 90% Acceptability Status",
     270              :                                     Constant::Units::None,
     271            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveASH5590,
     272              :                                     OutputProcessor::TimeStepType::Zone,
     273              :                                     OutputProcessor::StoreType::Average,
     274            0 :                                     state.dataHeatBal->People(Loop).Name);
     275            0 :                 SetupOutputVariable(state,
     276              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model 80% Acceptability Status",
     277              :                                     Constant::Units::None,
     278            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveASH5580,
     279              :                                     OutputProcessor::TimeStepType::Zone,
     280              :                                     OutputProcessor::StoreType::Average,
     281            0 :                                     state.dataHeatBal->People(Loop).Name);
     282            0 :                 SetupOutputVariable(state,
     283              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model Running Average Outdoor Air Temperature",
     284              :                                     Constant::Units::C,
     285            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ASHRAE55RunningMeanOutdoorTemp,
     286              :                                     OutputProcessor::TimeStepType::Zone,
     287              :                                     OutputProcessor::StoreType::Average,
     288            0 :                                     state.dataHeatBal->People(Loop).Name);
     289            0 :                 SetupOutputVariable(state,
     290              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model Temperature",
     291              :                                     Constant::Units::C,
     292            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).TComfASH55,
     293              :                                     OutputProcessor::TimeStepType::Zone,
     294              :                                     OutputProcessor::StoreType::Average,
     295            0 :                                     state.dataHeatBal->People(Loop).Name);
     296              :             }
     297              : 
     298           51 :             if (state.dataHeatBal->People(Loop).AdaptiveCEN15251) {
     299            0 :                 SetupOutputVariable(state,
     300              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Category I Status",
     301              :                                     Constant::Units::None,
     302            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatI,
     303              :                                     OutputProcessor::TimeStepType::Zone,
     304              :                                     OutputProcessor::StoreType::Average,
     305            0 :                                     state.dataHeatBal->People(Loop).Name);
     306            0 :                 SetupOutputVariable(state,
     307              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Category II Status",
     308              :                                     Constant::Units::None,
     309            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatII,
     310              :                                     OutputProcessor::TimeStepType::Zone,
     311              :                                     OutputProcessor::StoreType::Average,
     312            0 :                                     state.dataHeatBal->People(Loop).Name);
     313            0 :                 SetupOutputVariable(state,
     314              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Category III Status",
     315              :                                     Constant::Units::None,
     316            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatIII,
     317              :                                     OutputProcessor::TimeStepType::Zone,
     318              :                                     OutputProcessor::StoreType::Average,
     319            0 :                                     state.dataHeatBal->People(Loop).Name);
     320            0 :                 SetupOutputVariable(state,
     321              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Running Average Outdoor Air Temperature",
     322              :                                     Constant::Units::C,
     323            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CEN15251RunningMeanOutdoorTemp,
     324              :                                     OutputProcessor::TimeStepType::Zone,
     325              :                                     OutputProcessor::StoreType::Average,
     326            0 :                                     state.dataHeatBal->People(Loop).Name);
     327            0 :                 SetupOutputVariable(state,
     328              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Temperature",
     329              :                                     Constant::Units::C,
     330            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).TComfCEN15251,
     331              :                                     OutputProcessor::TimeStepType::Zone,
     332              :                                     OutputProcessor::StoreType::Average,
     333            0 :                                     state.dataHeatBal->People(Loop).Name);
     334              :             }
     335           51 :             if (state.dataHeatBal->People(Loop).CoolingEffectASH55) {
     336            0 :                 SetupOutputVariable(state,
     337              :                                     "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect",
     338              :                                     Constant::Units::C,
     339            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectASH55,
     340              :                                     OutputProcessor::TimeStepType::Zone,
     341              :                                     OutputProcessor::StoreType::Average,
     342            0 :                                     state.dataHeatBal->People(Loop).Name);
     343            0 :                 SetupOutputVariable(state,
     344              :                                     "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect Adjusted PMV",
     345              :                                     Constant::Units::None,
     346            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectAdjustedPMVASH55,
     347              :                                     OutputProcessor::TimeStepType::Zone,
     348              :                                     OutputProcessor::StoreType::Average,
     349            0 :                                     state.dataHeatBal->People(Loop).Name);
     350            0 :                 SetupOutputVariable(state,
     351              :                                     "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect Adjusted PPD",
     352              :                                     Constant::Units::None,
     353            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectAdjustedPPDASH55,
     354              :                                     OutputProcessor::TimeStepType::Zone,
     355              :                                     OutputProcessor::StoreType::Average,
     356            0 :                                     state.dataHeatBal->People(Loop).Name);
     357              :             }
     358           51 :             if (state.dataHeatBal->People(Loop).AnkleDraftASH55) {
     359            0 :                 SetupOutputVariable(state,
     360              :                                     "Zone Thermal Comfort ASHRAE 55 Ankle Draft PPD",
     361              :                                     Constant::Units::None,
     362            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).AnkleDraftPPDASH55,
     363              :                                     OutputProcessor::TimeStepType::Zone,
     364              :                                     OutputProcessor::StoreType::Average,
     365            0 :                                     state.dataHeatBal->People(Loop).Name);
     366              :             }
     367              :         }
     368          103 :         state.dataThermalComforts->ThermalComfortInASH55.allocate(state.dataGlobal->NumOfZones);
     369              : 
     370              :         // ASHRAE 55 Warning. If any people statement for a zone is true, set that zone to true
     371          154 :         for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
     372           51 :             if (state.dataHeatBal->People(Loop).Show55Warning) {
     373            2 :                 state.dataThermalComforts->ThermalComfortInASH55(state.dataHeatBal->People(Loop).ZonePtr).Enable55Warning = true;
     374              :             }
     375              :         }
     376              : 
     377              :         // CurrentModuleObject='Zone'
     378          226 :         for (Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
     379          246 :             SetupOutputVariable(state,
     380              :                                 "Zone Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time",
     381              :                                 Constant::Units::hr,
     382          123 :                                 state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotSummer,
     383              :                                 OutputProcessor::TimeStepType::Zone,
     384              :                                 OutputProcessor::StoreType::Sum,
     385          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     386          246 :             SetupOutputVariable(state,
     387              :                                 "Zone Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time",
     388              :                                 Constant::Units::hr,
     389          123 :                                 state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotWinter,
     390              :                                 OutputProcessor::TimeStepType::Zone,
     391              :                                 OutputProcessor::StoreType::Sum,
     392          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     393          246 :             SetupOutputVariable(state,
     394              :                                 "Zone Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time",
     395              :                                 Constant::Units::hr,
     396          123 :                                 state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotEither,
     397              :                                 OutputProcessor::TimeStepType::Zone,
     398              :                                 OutputProcessor::StoreType::Sum,
     399          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     400              :         }
     401          412 :         SetupOutputVariable(state,
     402              :                             "Facility Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time",
     403              :                             Constant::Units::hr,
     404          103 :                             state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer,
     405              :                             OutputProcessor::TimeStepType::Zone,
     406              :                             OutputProcessor::StoreType::Sum,
     407              :                             "Facility");
     408          412 :         SetupOutputVariable(state,
     409              :                             "Facility Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time",
     410              :                             Constant::Units::hr,
     411          103 :                             state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter,
     412              :                             OutputProcessor::TimeStepType::Zone,
     413              :                             OutputProcessor::StoreType::Sum,
     414              :                             "Facility");
     415          412 :         SetupOutputVariable(state,
     416              :                             "Facility Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time",
     417              :                             Constant::Units::hr,
     418          103 :                             state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either,
     419              :                             OutputProcessor::TimeStepType::Zone,
     420              :                             OutputProcessor::StoreType::Sum,
     421              :                             "Facility");
     422              : 
     423          103 :         state.dataThermalComforts->ThermalComfortSetPoint.allocate(state.dataGlobal->NumOfZones);
     424          226 :         for (Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
     425          246 :             SetupOutputVariable(state,
     426              :                                 "Zone Heating Setpoint Not Met Time",
     427              :                                 Constant::Units::hr,
     428          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetHeating,
     429              :                                 OutputProcessor::TimeStepType::Zone,
     430              :                                 OutputProcessor::StoreType::Sum,
     431          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     432          246 :             SetupOutputVariable(state,
     433              :                                 "Zone Heating Setpoint Not Met While Occupied Time",
     434              :                                 Constant::Units::hr,
     435          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetHeatingOccupied,
     436              :                                 OutputProcessor::TimeStepType::Zone,
     437              :                                 OutputProcessor::StoreType::Sum,
     438          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     439          246 :             SetupOutputVariable(state,
     440              :                                 "Zone Cooling Setpoint Not Met Time",
     441              :                                 Constant::Units::hr,
     442          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetCooling,
     443              :                                 OutputProcessor::TimeStepType::Zone,
     444              :                                 OutputProcessor::StoreType::Sum,
     445          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     446          246 :             SetupOutputVariable(state,
     447              :                                 "Zone Cooling Setpoint Not Met While Occupied Time",
     448              :                                 Constant::Units::hr,
     449          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetCoolingOccupied,
     450              :                                 OutputProcessor::TimeStepType::Zone,
     451              :                                 OutputProcessor::StoreType::Sum,
     452          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     453              :         }
     454              : 
     455          412 :         SetupOutputVariable(state,
     456              :                             "Facility Heating Setpoint Not Met Time",
     457              :                             Constant::Units::hr,
     458          103 :                             state.dataThermalComforts->AnyZoneNotMetHeating,
     459              :                             OutputProcessor::TimeStepType::Zone,
     460              :                             OutputProcessor::StoreType::Sum,
     461              :                             "Facility");
     462          412 :         SetupOutputVariable(state,
     463              :                             "Facility Cooling Setpoint Not Met Time",
     464              :                             Constant::Units::hr,
     465          103 :                             state.dataThermalComforts->AnyZoneNotMetCooling,
     466              :                             OutputProcessor::TimeStepType::Zone,
     467              :                             OutputProcessor::StoreType::Sum,
     468              :                             "Facility");
     469          412 :         SetupOutputVariable(state,
     470              :                             "Facility Heating Setpoint Not Met While Occupied Time",
     471              :                             Constant::Units::hr,
     472          103 :                             state.dataThermalComforts->AnyZoneNotMetHeatingOccupied,
     473              :                             OutputProcessor::TimeStepType::Zone,
     474              :                             OutputProcessor::StoreType::Sum,
     475              :                             "Facility");
     476          412 :         SetupOutputVariable(state,
     477              :                             "Facility Cooling Setpoint Not Met While Occupied Time",
     478              :                             Constant::Units::hr,
     479          103 :                             state.dataThermalComforts->AnyZoneNotMetCoolingOccupied,
     480              :                             OutputProcessor::TimeStepType::Zone,
     481              :                             OutputProcessor::StoreType::Sum,
     482              :                             "Facility");
     483              : 
     484          103 :         GetAngleFactorList(state);
     485              : 
     486          103 :         state.dataThermalComforts->ZoneOccHrs.dimension(state.dataGlobal->NumOfZones, 0.0);
     487          103 :     }
     488              : 
     489        19000 :     void CalcThermalComfortFanger(EnergyPlusData &state,
     490              :                                   ObjexxFCL::Optional_int_const PNum,     // People number for thermal comfort control
     491              :                                   ObjexxFCL::Optional<Real64 const> Tset, // Temperature setpoint for thermal comfort control
     492              :                                   ObjexxFCL::Optional<Real64> PMVResult   // PMV value for thermal comfort control
     493              :     )
     494              :     {
     495              : 
     496              :         // SUBROUTINE INFORMATION:
     497              :         //     AUTHOR         Jaewook Lee
     498              :         //     DATE WRITTEN   January 2000
     499              :         //     MODIFIED       Rick Strand (for E+ implementation February 2000)
     500              :         //                    Brent Griffith modifications for CR 5641 (October 2005)
     501              :         //                    L. Gu, Added optional arguments for thermal comfort control (May 2006)
     502              :         //                    T. Hong, added Fanger PPD (April 2009)
     503              : 
     504              :         // PURPOSE OF THIS SUBROUTINE:
     505              :         // This subroutine calculates PMV(Predicted Mean Vote) using the Fanger thermal
     506              :         // comfort model. This subroutine is also used for thermal comfort control by determining
     507              :         // the temperature at which the PMV is equal to a PMV setpoint specified by the user.
     508              : 
     509              :         // METHODOLOGY EMPLOYED:
     510              :         // This subroutine is based heavily upon the work performed by Dan Maloney for
     511              :         // the BLAST program.  Many of the equations are based on the original Fanger
     512              :         // development.  See documentation for further details and references.
     513              : 
     514              :         // REFERENCES:
     515              :         // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
     516              :         // BG note (10/21/2005),  This formulation is based on the the BASIC program
     517              :         // that is included in ASHRAE Standard 55 Normative Appendix D.
     518              : 
     519        29642 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
     520        10642 :              ++state.dataThermalComforts->PeopleNum) { // Is there a reason why this is a state variable and not a local variable?
     521              : 
     522        10642 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
     523        10642 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
     524              : 
     525              :             // Optional argument is used to access people object when thermal comfort control is used
     526        10642 :             if (present(PNum)) {
     527         8813 :                 if (state.dataThermalComforts->PeopleNum != PNum) continue;
     528              :             }
     529              : 
     530              :             // If optional argument is used do not cycle regardless of thermal comfort reporting type
     531        10642 :             if ((!people.Fanger) && (!present(PNum))) continue;
     532              : 
     533         1829 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
     534         1829 :             auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
     535         1829 :             if (present(PNum)) {
     536            0 :                 state.dataThermalComforts->AirTemp = Tset;
     537              :             } else {
     538         1829 :                 state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
     539              :             }
     540         1829 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
     541            0 :                 state.dataThermalComforts->ZoneNum = people.ZonePtr;
     542            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
     543            0 :                     state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
     544            0 :                     state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum); // PH 3/7/04
     545              :                     // UCSD-CV
     546            0 :                 } else if (state.dataRoomAir->IsZoneCrossVent(state.dataThermalComforts->ZoneNum)) {
     547            0 :                     if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Jet) {
     548            0 :                         state.dataThermalComforts->AirTemp = state.dataRoomAir->ZTJET(state.dataThermalComforts->ZoneNum);
     549            0 :                     } else if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Recirculation) {
     550            0 :                         state.dataThermalComforts->AirTemp = state.dataRoomAir->ZTJET(state.dataThermalComforts->ZoneNum);
     551              :                     }
     552              :                 }
     553              :             }
     554         1829 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
     555              :             // Use mean air temp for calculating RH when thermal comfort control is used
     556         1829 :             if (present(PNum)) {
     557            0 :                 state.dataThermalComforts->RelHum =
     558            0 :                     PsyRhFnTdbWPb(state,
     559            0 :                                   state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).MAT,
     560              :                                   thisZoneHB.airHumRatAvgComf,
     561            0 :                                   state.dataEnvrn->OutBaroPress);
     562              :             } else {
     563         3658 :                 state.dataThermalComforts->RelHum =
     564         1829 :                     PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
     565              :             }
     566         1829 :             people.TemperatureInZone = state.dataThermalComforts->AirTemp;
     567         1829 :             people.RelativeHumidityInZone = state.dataThermalComforts->RelHum * 100.0;
     568              : 
     569              :             // Metabolic rate of body (W/m2)
     570         1829 :             state.dataThermalComforts->ActLevel = people.activityLevelSched->getCurrentVal() / BodySurfArea;
     571              :             // Energy consumption by external work (W/m2)
     572         1829 :             state.dataThermalComforts->WorkEff = people.workEffSched->getCurrentVal() * state.dataThermalComforts->ActLevel;
     573              :             // Clothing unit
     574              :             Real64 IntermediateClothing;
     575         1829 :             switch (people.clothingType) {
     576         1829 :             case DataHeatBalance::ClothingType::InsulationSchedule:
     577         1829 :                 state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     578         1829 :                 break;
     579            0 :             case DataHeatBalance::ClothingType::DynamicAshrae55:
     580            0 :                 comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     581            0 :                 comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     582            0 :                 DynamicClothingModel(state);
     583            0 :                 state.dataThermalComforts->CloUnit = comfort.ClothingValue;
     584            0 :                 break;
     585            0 :             case DataHeatBalance::ClothingType::CalculationSchedule:
     586            0 :                 IntermediateClothing = people.clothingMethodSched->getCurrentVal();
     587            0 :                 if (IntermediateClothing == 1.0) {
     588            0 :                     state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     589            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     590            0 :                 } else if (IntermediateClothing == 2.0) {
     591            0 :                     comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     592            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     593            0 :                     DynamicClothingModel(state);
     594            0 :                     state.dataThermalComforts->CloUnit = comfort.ClothingValue;
     595              :                 } else {
     596            0 :                     state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     597            0 :                     ShowWarningError(
     598            0 :                         state, format("PEOPLE=\"{}\", Scheduled clothing value will be used rather than clothing calculation method.", people.Name));
     599              :                 }
     600            0 :                 break;
     601            0 :             default:
     602            0 :                 ShowSevereError(state, format("PEOPLE=\"{}\", Incorrect Clothing Type", people.Name));
     603              :             }
     604              : 
     605         1829 :             if (state.dataRoomAir->anyNonMixingRoomAirModel && state.dataRoomAir->IsZoneCrossVent(state.dataThermalComforts->ZoneNum)) {
     606            0 :                 if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Jet) {
     607            0 :                     state.dataThermalComforts->AirVel = state.dataRoomAir->Ujet(state.dataThermalComforts->ZoneNum);
     608            0 :                 } else if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Recirculation) {
     609            0 :                     state.dataThermalComforts->AirVel = state.dataRoomAir->Urec(state.dataThermalComforts->ZoneNum);
     610              :                 } else {
     611            0 :                     state.dataThermalComforts->AirVel = 0.2;
     612              :                 }
     613              :             } else {
     614         1829 :                 state.dataThermalComforts->AirVel = people.airVelocitySched->getCurrentVal();
     615              :                 // Ensure air velocity within the reasonable range. Otherwise reccusive warnings is provided
     616         1829 :                 if (present(PNum) && (state.dataThermalComforts->AirVel < 0.1 || state.dataThermalComforts->AirVel > 0.5)) {
     617            0 :                     if (people.AirVelErrIndex == 0) {
     618            0 :                         ShowWarningMessage(
     619              :                             state,
     620            0 :                             format("PEOPLE=\"{}\", Air velocity is beyond the reasonable range (0.1,0.5) for thermal comfort control.", people.Name));
     621            0 :                         ShowContinueErrorTimeStamp(state, "");
     622              :                     }
     623            0 :                     ShowRecurringWarningErrorAtEnd(state,
     624            0 :                                                    "PEOPLE=\"" + people.Name + "\",Air velocity is still beyond the reasonable range (0.1,0.5)",
     625            0 :                                                    people.AirVelErrIndex,
     626            0 :                                                    state.dataThermalComforts->AirVel,
     627            0 :                                                    state.dataThermalComforts->AirVel,
     628              :                                                    _,
     629              :                                                    "[m/s]",
     630              :                                                    "[m/s]");
     631              :                 }
     632              :             }
     633              : 
     634         1829 :             Real64 PMV = CalcFangerPMV(state,
     635         1829 :                                        state.dataThermalComforts->AirTemp,
     636         1829 :                                        state.dataThermalComforts->RadTemp,
     637         1829 :                                        state.dataThermalComforts->RelHum,
     638         1829 :                                        state.dataThermalComforts->AirVel,
     639         1829 :                                        state.dataThermalComforts->ActLevel,
     640         1829 :                                        state.dataThermalComforts->CloUnit,
     641         1829 :                                        state.dataThermalComforts->WorkEff);
     642              : 
     643         1829 :             comfort.FangerPMV = PMV;
     644              :             // Pass resulting PMV based on temperature setpoint (Tset) when using thermal comfort control
     645         1829 :             if (present(PNum)) {
     646            0 :                 PMVResult = PMV;
     647              :             }
     648         1829 :             comfort.ThermalComfortMRT = state.dataThermalComforts->RadTemp;
     649         1829 :             comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     650         1829 :             comfort.CloSurfTemp = state.dataThermalComforts->CloSurfTemp;
     651              : 
     652              :             // Calculate the Fanger PPD (Predicted Percentage of Dissatisfied), as a %
     653         1829 :             Real64 PPD = CalcFangerPPD(PMV);
     654         1829 :             comfort.FangerPPD = PPD;
     655              :         }
     656        19000 :     }
     657              : 
     658         1834 :     Real64 CalcFangerPMV(
     659              :         EnergyPlusData &state, Real64 AirTemp, Real64 RadTemp, Real64 RelHum, Real64 AirVel, Real64 ActLevel, Real64 CloUnit, Real64 WorkEff)
     660              :     {
     661              : 
     662              :         // Using/Aliasing
     663              :         using Psychrometrics::PsyPsatFnTemp;
     664              : 
     665              :         // SUBROUTINE PARAMETER DEFINITIONS:
     666         1834 :         int constexpr MaxIter(150);             // Limit of iteration
     667         1834 :         Real64 constexpr StopIterCrit(0.00015); // Stop criteria for iteration
     668              : 
     669              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     670              :         Real64 P1;  // Intermediate variables to calculate clothed body ratio and clothing temperature
     671              :         Real64 P2;  // Intermediate variables to calculate clothed body ratio and clothing temperature
     672              :         Real64 P3;  // Intermediate variables to calculate clothed body ratio and clothing temperature
     673              :         Real64 P4;  // Intermediate variables to calculate clothed body ratio and clothing temperature
     674              :         Real64 XF;  // Intermediate variables to calculate clothed body ratio and clothing temperature
     675              :         Real64 XN;  // Intermediate variables to calculate clothed body ratio and clothing temperature
     676              :         Real64 PMV; // temporary variable to store calculated Fanger PMV value
     677              :         // VapPress    = CalcSatVapPressFromTemp(AirTemp)  !original
     678              :         // VapPress    = RelHum*VapPress                   !original might be in torrs
     679              : 
     680         1834 :         state.dataThermalComforts->VapPress = PsyPsatFnTemp(state, AirTemp); // use psych routines inside E+ , returns Pa
     681              : 
     682         1834 :         state.dataThermalComforts->VapPress *= RelHum; // in units of [Pa]
     683              : 
     684         1834 :         state.dataThermalComforts->IntHeatProd = ActLevel - WorkEff;
     685              : 
     686              :         // Compute the Corresponding Clothed Body Ratio
     687         1834 :         state.dataThermalComforts->CloBodyRat = 1.05 + 0.1 * CloUnit; // The ratio of the surface area of the clothed body
     688              :         // to the surface area of nude body
     689              : 
     690         1834 :         if (CloUnit < 0.5) state.dataThermalComforts->CloBodyRat = state.dataThermalComforts->CloBodyRat - 0.05 + 0.1 * CloUnit;
     691              : 
     692         1834 :         state.dataThermalComforts->AbsRadTemp = RadTemp + TAbsConv;
     693         1834 :         state.dataThermalComforts->AbsAirTemp = AirTemp + TAbsConv;
     694              : 
     695         1834 :         state.dataThermalComforts->CloInsul = CloUnit * state.dataThermalComforts->CloBodyRat * 0.155; // Thermal resistance of the clothing // icl
     696              : 
     697         1834 :         P2 = state.dataThermalComforts->CloInsul * 3.96;
     698         1834 :         P3 = state.dataThermalComforts->CloInsul * 100.0;
     699         1834 :         P1 = state.dataThermalComforts->CloInsul * state.dataThermalComforts->AbsAirTemp;                                        // p4
     700         1834 :         P4 = 308.7 - 0.028 * state.dataThermalComforts->IntHeatProd + P2 * pow_4(state.dataThermalComforts->AbsRadTemp / 100.0); // p5
     701              : 
     702              :         // First guess for clothed surface temperature
     703         1834 :         state.dataThermalComforts->AbsCloSurfTemp = state.dataThermalComforts->AbsAirTemp + (35.5 - AirTemp) / (3.5 * (CloUnit + 0.1));
     704         1834 :         XN = state.dataThermalComforts->AbsCloSurfTemp / 100.0;
     705         1834 :         state.dataThermalComforts->HcFor = 12.1 * std::sqrt(AirVel); // Heat transfer coefficient by forced convection
     706         1834 :         state.dataThermalComforts->IterNum = 0;
     707         1834 :         XF = XN;
     708              : 
     709              :         // COMPUTE SURFACE TEMPERATURE OF CLOTHING BY ITERATIONS
     710        11578 :         while (((std::abs(XN - XF) > StopIterCrit) || (state.dataThermalComforts->IterNum == 0)) && (state.dataThermalComforts->IterNum < MaxIter)) {
     711         9744 :             XF = (XF + XN) / 2.0;
     712        19488 :             state.dataThermalComforts->HcNat =
     713         9744 :                 2.38 * root_4(std::abs(100.0 * XF - state.dataThermalComforts->AbsAirTemp)); // Heat transfer coefficient by natural convection
     714         9744 :             state.dataThermalComforts->Hc =
     715         9744 :                 max(state.dataThermalComforts->HcFor, state.dataThermalComforts->HcNat); // Determination of convective heat transfer coefficient
     716         9744 :             XN = (P4 + P1 * state.dataThermalComforts->Hc - P2 * pow_4(XF)) / (100.0 + P3 * state.dataThermalComforts->Hc);
     717         9744 :             ++state.dataThermalComforts->IterNum;
     718         9744 :             if (state.dataThermalComforts->IterNum > MaxIter) {
     719            0 :                 ShowWarningError(state, "Max iteration exceeded in CalcThermalFanger");
     720              :             }
     721              :         }
     722         1834 :         state.dataThermalComforts->AbsCloSurfTemp = 100.0 * XN;
     723         1834 :         state.dataThermalComforts->CloSurfTemp = state.dataThermalComforts->AbsCloSurfTemp - TAbsConv;
     724              : 
     725              :         // COMPUTE PREDICTED MEAN VOTE
     726              :         // Sensible heat loss
     727              :         // RadHeatLoss = RadSurfEff*CloBodyRat*SkinEmiss*StefanBoltz* &   !original
     728              :         //                            (AbsCloSurfTemp**4 - AbsRadTemp**4) ! Heat loss by radiation
     729              : 
     730              :         // following line is ln 480 in ASHRAE 55 append. D
     731         3668 :         state.dataThermalComforts->RadHeatLoss =
     732         1834 :             3.96 * state.dataThermalComforts->CloBodyRat *
     733         1834 :             (pow_4(state.dataThermalComforts->AbsCloSurfTemp * 0.01) - pow_4(state.dataThermalComforts->AbsRadTemp * 0.01));
     734              : 
     735         1834 :         state.dataThermalComforts->ConvHeatLoss = state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->Hc *
     736         1834 :                                                   (state.dataThermalComforts->CloSurfTemp - AirTemp); // Heat loss by convection
     737              : 
     738         1834 :         state.dataThermalComforts->DryHeatLoss = state.dataThermalComforts->RadHeatLoss + state.dataThermalComforts->ConvHeatLoss;
     739              : 
     740              :         // Evaporative heat loss
     741              :         // Heat loss by regulatory sweating
     742         1834 :         state.dataThermalComforts->EvapHeatLossRegComf = 0.0;
     743         1834 :         if (state.dataThermalComforts->IntHeatProd > 58.2) {
     744         1445 :             state.dataThermalComforts->EvapHeatLossRegComf = 0.42 * (state.dataThermalComforts->IntHeatProd - ActLevelConv);
     745              :         }
     746              :         // SkinTempComf = 35.7 - 0.028*IntHeatProd ! Skin temperature required to achieve thermal comfort
     747              :         // SatSkinVapPress = 1.92*SkinTempComf - 25.3 ! Water vapor pressure at required skin temperature
     748              :         // Heat loss by diffusion
     749              :         // EvapHeatLossDiff = 0.4148*(SatSkinVapPress - VapPress) !original
     750         3668 :         state.dataThermalComforts->EvapHeatLossDiff =
     751         1834 :             3.05 * 0.001 *
     752         1834 :             (5733.0 - 6.99 * state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->VapPress); // ln 440 in ASHRAE 55 Append. D
     753              : 
     754         1834 :         state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossRegComf + state.dataThermalComforts->EvapHeatLossDiff;
     755              :         // Heat loss by respiration
     756              :         // original: LatRespHeatLoss = 0.0023*ActLevel*(44. - VapPress) ! Heat loss by latent respiration
     757         3668 :         state.dataThermalComforts->LatRespHeatLoss =
     758         1834 :             1.7 * 0.00001 * ActLevel * (5867.0 - state.dataThermalComforts->VapPress); // ln 460 in ASHRAE 55 Append. D
     759              : 
     760              :         // LatRespHeatLoss = 0.017251*ActLevel*(5.8662 - VapPress)
     761              :         // V-1.2.2 'fix' BG 3/2005 5th term in LHS Eq (58)  in 2001 HOF Ch. 8
     762              :         // this was wrong because VapPress needed to be kPa
     763              : 
     764         1834 :         state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevel * (34.0 - AirTemp); // Heat loss by dry respiration.
     765              : 
     766         1834 :         state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
     767              : 
     768         1834 :         state.dataThermalComforts->ThermSensTransCoef = 0.303 * std::exp(-0.036 * ActLevel) + 0.028; // Thermal transfer coefficient to calculate PMV
     769              : 
     770         1834 :         PMV = state.dataThermalComforts->ThermSensTransCoef * (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->EvapHeatLoss -
     771         1834 :                                                                state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLoss);
     772              : 
     773         1834 :         return PMV;
     774              :     }
     775              : 
     776         1831 :     Real64 CalcFangerPPD(Real64 PMV)
     777              :     {
     778              :         Real64 PPD;
     779         1831 :         Real64 expTest1 = -0.03353 * pow_4(PMV) - 0.2179 * pow_2(PMV);
     780         1831 :         if (expTest1 > DataPrecisionGlobals::EXP_LowerLimit) {
     781         1604 :             PPD = 100.0 - 95.0 * std::exp(expTest1);
     782              :         } else {
     783          227 :             PPD = 100.0;
     784              :         }
     785              : 
     786         1831 :         if (PPD < 0.0) {
     787            0 :             PPD = 0.0;
     788         1831 :         } else if (PPD > 100.0) {
     789            0 :             PPD = 100.0;
     790              :         }
     791         1831 :         return PPD;
     792              :     }
     793              : 
     794            5 :     Real64 CalcRelativeAirVelocity(Real64 AirVel, Real64 ActMet)
     795              :     {
     796            5 :         if (ActMet > 1) {
     797            3 :             return AirVel + 0.3 * (ActMet - 1);
     798              :         } else {
     799            2 :             return AirVel;
     800              :         }
     801              :     }
     802              : 
     803            3 :     void GetThermalComfortInputsASHRAE(EnergyPlusData &state)
     804              :     {
     805            3 :         auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
     806            3 :         auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
     807              : 
     808            3 :         state.dataThermalComforts->ZoneNum = people.ZonePtr;
     809            3 :         auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
     810              :         // (var TA)
     811            3 :         state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
     812            3 :         if (state.dataRoomAir->anyNonMixingRoomAirModel) {
     813            0 :             if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
     814            0 :                 state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
     815            0 :                 state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum); // PH 3/7/04
     816              :             }
     817              :         }
     818              :         // (var TR)
     819            3 :         state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
     820              :         // (var RH)
     821            6 :         state.dataThermalComforts->RelHum =
     822            3 :             PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
     823              :         // Metabolic rate of body (W/m2) (var RM, M)
     824            3 :         state.dataThermalComforts->ActLevel = people.activityLevelSched->getCurrentVal() / BodySurfAreaPierce;
     825              :         // Energy consumption by external work (W/m2) (var WME)
     826            3 :         state.dataThermalComforts->WorkEff = people.workEffSched->getCurrentVal() * state.dataThermalComforts->ActLevel;
     827              : 
     828              :         // Clothing unit  (var CLO)
     829              :         Real64 IntermediateClothing;
     830            3 :         switch (people.clothingType) {
     831            3 :         case DataHeatBalance::ClothingType::InsulationSchedule:
     832            3 :             state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     833            3 :             break;
     834            0 :         case DataHeatBalance::ClothingType::DynamicAshrae55:
     835            0 :             comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     836            0 :             comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     837            0 :             DynamicClothingModel(state);
     838            0 :             state.dataThermalComforts->CloUnit = comfort.ClothingValue;
     839            0 :             break;
     840            0 :         case DataHeatBalance::ClothingType::CalculationSchedule:
     841            0 :             IntermediateClothing = people.clothingMethodSched->getCurrentVal();
     842            0 :             if (IntermediateClothing == 1.0) {
     843            0 :                 state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     844            0 :                 comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     845            0 :             } else if (IntermediateClothing == 2.0) {
     846            0 :                 comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     847            0 :                 comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     848            0 :                 DynamicClothingModel(state);
     849            0 :                 state.dataThermalComforts->CloUnit = comfort.ClothingValue;
     850              :             } else {
     851            0 :                 state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     852            0 :                 ShowWarningError(state, "Scheduled clothing value will be used rather than clothing calculation method.");
     853              :             }
     854            0 :             break;
     855            0 :         default:
     856            0 :             ShowSevereError(state, "Incorrect Clothing Type");
     857              :         }
     858              :         // (var VEL)
     859            3 :         state.dataThermalComforts->AirVel = people.airVelocitySched->getCurrentVal();
     860              :         // (var MET)
     861            3 :         state.dataThermalComforts->ActMet = state.dataThermalComforts->ActLevel / ActLevelConv;
     862            3 :     }
     863              : 
     864           44 :     Real64 CalcStandardEffectiveTemp(
     865              :         EnergyPlusData &state, Real64 AirTemp, Real64 RadTemp, Real64 RelHum, Real64 AirVel, Real64 ActMet, Real64 CloUnit, Real64 WorkEff)
     866              :     {
     867              : 
     868              :         // Thermal const
     869           44 :         constexpr Real64 CloFac(0.25);                  // Clothing factor determined experimentally (var KCLO)
     870           44 :         constexpr Real64 BodyWeight(69.9);              // (var BODYWEIGHT)
     871           44 :         constexpr Real64 SweatContConst(170.0);         // Proportionality constant for sweat control; g/m2.hr (var CSW)
     872           44 :         constexpr Real64 DriCoeffVasodilation(120);     // driving coefficient for vasodilation (var CDIL)
     873           44 :         constexpr Real64 DriCoeffVasoconstriction(0.5); // (var CSTR)
     874           44 :         constexpr Real64 MaxSkinBloodFlow(90.0);        // Max. value of skin blood flow
     875           44 :         constexpr Real64 MinSkinBloodFlow(0.5);         // Min. value of skin blood flow
     876           44 :         constexpr Real64 RegSweatMax(500);              // Max. value of regulatory sweating; w/m2
     877              : 
     878              :         // Standard condition const
     879              :         // Definition of vascular control signals CoreTempSet, SkinTempSet, and AvgBodyTempSet are the setpoints for core, skin and
     880              :         // average body temperatures corresponding to physiol.  neutrality SkinMassRatSet is the ratio of skin mass to total body mass (skin+core)
     881              :         // Typical values for CoreTempSet, SkinTempSet and SkinMassRatSet are 36.8, 33.7 and 0.10 SkinMassRat is the actual skin to total body mass
     882              :         // ratio
     883           44 :         constexpr Real64 SkinTempSet(33.7);     // (var TempSkinNeutral)
     884           44 :         constexpr Real64 CoreTempSet(36.8);     // (var TempCoreNeutral)
     885           44 :         constexpr Real64 SkinBloodFlowSet(6.3); // (var SkinBloodFlowNeutral)
     886           44 :         constexpr Real64 SkinMassRatSet(0.1);   // (var ALFA)
     887              : 
     888           44 :         if (AirVel < 0.1) AirVel = 0.1;
     889              : 
     890              :         // (var VaporPressure)
     891           44 :         state.dataThermalComforts->VapPress = RelHum * CalcSatVapPressFromTempTorr(AirTemp);
     892           44 :         Real64 ActLevel = ActLevelConv * ActMet;
     893           44 :         state.dataThermalComforts->IntHeatProd = ActLevel - WorkEff;
     894              : 
     895              :         // Step 1: CALCULATE VARIABLESS THAT REMAIN CONSTANT FOR AN HOUR
     896           44 :         Real64 PInAtmospheres = state.dataEnvrn->OutBaroPress / 101325;
     897           44 :         Real64 RClo = CloUnit * 0.155; // (var RCL)
     898           44 :         Real64 TotCloFac = 1.0 + 0.15 * CloUnit;
     899           44 :         Real64 LewisRatio = 2.2 / PInAtmospheres; // Lewis Relation is 2.2 at sea level, 25C (var LR)
     900              :         Real64 EvapEff;                           // evaporative efficiency
     901              : 
     902              :         // APPROXIMATE THE FOLLOWING VALUES TO START
     903           44 :         state.dataThermalComforts->SkinTemp = SkinTempSet;
     904           44 :         state.dataThermalComforts->CoreTemp = CoreTempSet;
     905           44 :         Real64 SkinBloodFlow = SkinBloodFlowSet;
     906           44 :         Real64 SkinMassRat = SkinMassRatSet;
     907              : 
     908              :         // Mass transfer equation between skin and environment
     909              :         // CloInsul is efficiency of mass transfer for CloUnit.
     910           44 :         if (CloUnit <= 0) {
     911            0 :             EvapEff = 0.38 * std::pow(AirVel, -0.29); // (var WCRIT)
     912            0 :             state.dataThermalComforts->CloInsul = 1.0;
     913              :         } else {
     914           44 :             EvapEff = 0.59 * std::pow(AirVel, -0.08); // (var ICL)
     915           44 :             state.dataThermalComforts->CloInsul = 0.45;
     916              :         }
     917              : 
     918           44 :         Real64 CorrectedHC = 3.0 * std::pow(PInAtmospheres, 0.53);              // corrected convective heat transfer coefficient
     919           44 :         Real64 ForcedHC = 8.600001 * std::pow((AirVel * PInAtmospheres), 0.53); // forced convective heat transfer coefficient, W/(m2 °C) (CHCV)
     920           44 :         state.dataThermalComforts->Hc = std::max(CorrectedHC, ForcedHC);        // (CHC)
     921           44 :         state.dataThermalComforts->Hr = 4.7;                                    // (CHR)
     922           44 :         state.dataThermalComforts->EvapHeatLoss = 0.1 * ActMet;
     923           44 :         Real64 RAir = 1.0 / (TotCloFac * (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr)); // resistance of air layer to dry heat (RA)
     924           44 :         state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hr * RadTemp + state.dataThermalComforts->Hc * AirTemp) /
     925           44 :                                             (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr); // operative temperature (TOP)
     926           44 :         Real64 ActLevelStart = ActLevel; // ActLevel gets increased by shivering in the following
     927           44 :         Real64 AvgBodyTempSet = SkinMassRatSet * SkinTempSet + (1.0 - SkinMassRatSet) * CoreTempSet; // (var TempBodyNeutral)
     928              : 
     929              :         // Step 2: BEGIN MINUTE BY MINUTE CALCULATIONS FOR ONE HOUR SIMULATION OF TEMPERATURE REGULATION.
     930              :         // This section simulates the temperature regulation over 1 minute.
     931              :         // Inputs are the physiological data from the previous time step and the current environmental conditions. Loop and must be increased from the
     932              :         // start level, not perpetually increased
     933         2684 :         for (int IterMin = 1; IterMin <= 60; ++IterMin) {
     934              :             // Dry heat balance: solve for CloSurfTemp and Hr, GUESS CloSurfTemp TO START
     935         5280 :             state.dataThermalComforts->CloSurfTemp =
     936         2640 :                 (RAir * state.dataThermalComforts->SkinTemp + RClo * state.dataThermalComforts->OpTemp) / (RAir + RClo);
     937         2640 :             bool converged = false;
     938         5336 :             while (!converged) {
     939         5392 :                 state.dataThermalComforts->Hr =
     940         2696 :                     4.0 * StefanBoltz * std::pow((state.dataThermalComforts->CloSurfTemp + RadTemp) / 2.0 + 273.15, 3) * 0.72;
     941         2696 :                 RAir = 1.0 / (TotCloFac * (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr));
     942         2696 :                 state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hr * RadTemp + state.dataThermalComforts->Hc * AirTemp) /
     943         2696 :                                                     (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr);
     944         2696 :                 Real64 CloSurfTempNew = (RAir * state.dataThermalComforts->SkinTemp + RClo * state.dataThermalComforts->OpTemp) / (RAir + RClo);
     945         2696 :                 if (std::abs(CloSurfTempNew - state.dataThermalComforts->CloSurfTemp) <= 0.01) {
     946         2640 :                     converged = true;
     947              :                 }
     948         2696 :                 state.dataThermalComforts->CloSurfTemp = CloSurfTempNew;
     949              :             }
     950              : 
     951              :             // CALCULATE THE COMBINED HEAT TRANSFER COEFF. (H)
     952         2640 :             state.dataThermalComforts->H = state.dataThermalComforts->Hr + state.dataThermalComforts->Hc;
     953              :             // Heat flow from Clothing surface to environment
     954         2640 :             state.dataThermalComforts->DryHeatLoss = (state.dataThermalComforts->SkinTemp - state.dataThermalComforts->OpTemp) / (RAir + RClo);
     955              : 
     956              :             // dry and latent respiratory heat losses
     957         5280 :             state.dataThermalComforts->LatRespHeatLoss =
     958         2640 :                 0.0023 * ActLevel * (44.0 - state.dataThermalComforts->VapPress); // latent heat loss due to respiration
     959         2640 :             state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevel * (34.0 - AirTemp);
     960              : 
     961         2640 :             state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
     962              : 
     963              :             // Heat flows to skin and core: 5.28 is skin conductance in the absence of skin blood flow
     964         5280 :             state.dataThermalComforts->HeatFlow =
     965         2640 :                 (state.dataThermalComforts->CoreTemp - state.dataThermalComforts->SkinTemp) * (5.28 + 1.163 * SkinBloodFlow);
     966              : 
     967         2640 :             Real64 CoreHeatStorage = ActLevel - state.dataThermalComforts->HeatFlow - state.dataThermalComforts->RespHeatLoss -
     968         2640 :                                      WorkEff; // rate of energy storage in the core
     969         2640 :             Real64 SkinHeatStorage = state.dataThermalComforts->HeatFlow - state.dataThermalComforts->DryHeatLoss -
     970         2640 :                                      state.dataThermalComforts->EvapHeatLoss; // rate of energy storage in the skin
     971              : 
     972              :             // Thermal capacities
     973         2640 :             state.dataThermalComforts->CoreThermCap = 0.97 * (1 - SkinMassRat) * BodyWeight;
     974         2640 :             state.dataThermalComforts->SkinThermCap = 0.97 * SkinMassRat * BodyWeight;
     975              : 
     976              :             // Temperature changes in 1 minute
     977         2640 :             state.dataThermalComforts->CoreTempChange = (CoreHeatStorage * BodySurfAreaPierce / (state.dataThermalComforts->CoreThermCap * 60.0));
     978         2640 :             state.dataThermalComforts->SkinTempChange = (SkinHeatStorage * BodySurfAreaPierce) / (state.dataThermalComforts->SkinThermCap * 60.0);
     979              : 
     980         2640 :             state.dataThermalComforts->CoreTemp += state.dataThermalComforts->CoreTempChange;
     981         2640 :             state.dataThermalComforts->SkinTemp += state.dataThermalComforts->SkinTempChange;
     982         5280 :             state.dataThermalComforts->AvgBodyTemp =
     983         2640 :                 SkinMassRat * state.dataThermalComforts->SkinTemp + (1.0 - SkinMassRat) * state.dataThermalComforts->CoreTemp;
     984              : 
     985              :             Real64 SkinThermSigWarm;                                               // vasodilation signal (WARMS)
     986              :             Real64 SkinThermSigCold;                                               // vasoconstriction signal
     987         2640 :             Real64 SkinSignal = state.dataThermalComforts->SkinTemp - SkinTempSet; // thermoregulatory control signal from the skin
     988         2640 :             if (SkinSignal > 0) {
     989          765 :                 SkinThermSigWarm = SkinSignal;
     990          765 :                 SkinThermSigCold = 0.0;
     991              :             } else {
     992         1875 :                 SkinThermSigCold = -SkinSignal;
     993         1875 :                 SkinThermSigWarm = 0.0;
     994              :             }
     995              : 
     996              :             Real64 CoreThermSigWarm;                                               // vasodialtion signal (WARMC)
     997              :             Real64 CoreThermSigCold;                                               // vasoconstriction signal
     998         2640 :             Real64 CoreSignal = state.dataThermalComforts->CoreTemp - CoreTempSet; // thermoregulatory control signal from the skin, °C
     999         2640 :             if (CoreSignal > 0) {
    1000         2356 :                 CoreThermSigWarm = CoreSignal;
    1001         2356 :                 CoreThermSigCold = 0.0;
    1002              :             } else {
    1003          284 :                 CoreThermSigCold = -CoreSignal;
    1004          284 :                 CoreThermSigWarm = 0.0;
    1005              :             }
    1006              : 
    1007              :             Real64 BodyThermSigWarm; // WARMB
    1008         2640 :             Real64 BodySignal = state.dataThermalComforts->AvgBodyTemp - AvgBodyTempSet;
    1009              : 
    1010         2640 :             if (BodySignal > 0) {
    1011          666 :                 BodyThermSigWarm = BodySignal;
    1012              :             } else {
    1013         1974 :                 BodyThermSigWarm = 0.0;
    1014              :             }
    1015              : 
    1016         2640 :             state.dataThermalComforts->VasodilationFac = DriCoeffVasodilation * CoreThermSigWarm;
    1017         2640 :             state.dataThermalComforts->VasoconstrictFac = DriCoeffVasoconstriction * SkinThermSigCold;
    1018         2640 :             SkinBloodFlow = (SkinBloodFlowSet + state.dataThermalComforts->VasodilationFac) / (1.0 + state.dataThermalComforts->VasoconstrictFac);
    1019              :             // SkinBloodFlow is never below 0.5 liter/(m2.hr) nor above 90 liter/(m2.hr)
    1020         2640 :             if (SkinBloodFlow < MinSkinBloodFlow) SkinBloodFlow = MinSkinBloodFlow;
    1021         2640 :             if (SkinBloodFlow > MaxSkinBloodFlow) SkinBloodFlow = MaxSkinBloodFlow;
    1022         2640 :             SkinMassRat = 0.0417737 + 0.7451832 / (SkinBloodFlow + 0.585417); // ratio of skin-core masses change with SkinBloodFlow
    1023              : 
    1024         2640 :             Real64 RegSweat = SweatContConst * BodyThermSigWarm * std::exp(SkinThermSigWarm / 10.7); // control of regulatory sweating
    1025         2640 :             if (RegSweat > RegSweatMax) RegSweat = RegSweatMax;
    1026         2640 :             state.dataThermalComforts->EvapHeatLossRegSweat = 0.68 * RegSweat; // heat lost by vaporization sweat
    1027              : 
    1028              :             // adjustment of metabolic heat due to shivering (Stolwijk, Hardy)
    1029         2640 :             state.dataThermalComforts->ShivResponse = 19.4 * SkinThermSigCold * CoreThermSigCold;
    1030         2640 :             ActLevel = ActLevelStart + state.dataThermalComforts->ShivResponse;
    1031              : 
    1032              :             // Evaluation of heat transfer by evaporation at skin surface
    1033         2640 :             Real64 AirEvapHeatResist = 1.0 / (LewisRatio * TotCloFac * state.dataThermalComforts->Hc); // evaporative resistance air layer
    1034         2640 :             Real64 CloEvapHeatResist = RClo / (LewisRatio * state.dataThermalComforts->CloInsul);
    1035         2640 :             Real64 TotEvapHeatResist = AirEvapHeatResist + CloEvapHeatResist;
    1036         2640 :             state.dataThermalComforts->SatSkinVapPress = CalcSatVapPressFromTempTorr(state.dataThermalComforts->SkinTemp); // PSSK
    1037         5280 :             state.dataThermalComforts->EvapHeatLossMax =
    1038         2640 :                 (state.dataThermalComforts->SatSkinVapPress - state.dataThermalComforts->VapPress) / TotEvapHeatResist; // TotEvapHeatResist;
    1039         5280 :             state.dataThermalComforts->SkinWetSweat =
    1040         2640 :                 state.dataThermalComforts->EvapHeatLossRegSweat /
    1041         2640 :                 state.dataThermalComforts->EvapHeatLossMax; // ratio heat loss sweating to max heat loss sweating
    1042              : 
    1043         5280 :             state.dataThermalComforts->SkinWetDiff =
    1044         2640 :                 (1.0 - state.dataThermalComforts->SkinWetSweat) * 0.06; // 0.06 if SkinWetDiff for nonsweating skin --- Kerslake
    1045         2640 :             state.dataThermalComforts->EvapHeatLossDiff = state.dataThermalComforts->SkinWetDiff * state.dataThermalComforts->EvapHeatLossMax;
    1046         2640 :             state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossRegSweat + state.dataThermalComforts->EvapHeatLossDiff;
    1047         2640 :             state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
    1048              : 
    1049              :             // Beginning of dripping (Sweat not evaporated on skin surface)
    1050         2640 :             if (state.dataThermalComforts->SkinWetTot >= EvapEff) {
    1051            0 :                 state.dataThermalComforts->SkinWetTot = EvapEff;
    1052            0 :                 state.dataThermalComforts->SkinWetSweat = EvapEff / 0.94;
    1053            0 :                 state.dataThermalComforts->EvapHeatLossRegSweat =
    1054            0 :                     state.dataThermalComforts->SkinWetSweat * state.dataThermalComforts->EvapHeatLossMax;
    1055            0 :                 state.dataThermalComforts->SkinWetDiff = (1.0 - state.dataThermalComforts->SkinWetSweat) * 0.06;
    1056            0 :                 state.dataThermalComforts->EvapHeatLossDiff = state.dataThermalComforts->SkinWetDiff * state.dataThermalComforts->EvapHeatLossMax;
    1057            0 :                 state.dataThermalComforts->EvapHeatLoss =
    1058            0 :                     state.dataThermalComforts->EvapHeatLossRegSweat + state.dataThermalComforts->EvapHeatLossDiff;
    1059              :             }
    1060              : 
    1061              :             // When EvapHeatLossMax<0. condensation on skin occurs.
    1062         2640 :             if (state.dataThermalComforts->EvapHeatLossMax < 0.0) {
    1063            0 :                 state.dataThermalComforts->SkinWetDiff = 0.0;
    1064            0 :                 state.dataThermalComforts->EvapHeatLossDiff = 0.0;
    1065            0 :                 state.dataThermalComforts->EvapHeatLoss = 0.0;
    1066            0 :                 state.dataThermalComforts->SkinWetTot = EvapEff;
    1067            0 :                 state.dataThermalComforts->SkinWetSweat = EvapEff;
    1068            0 :                 state.dataThermalComforts->EvapHeatLossRegSweat = 0.0;
    1069              :             }
    1070              :             // Vapor pressure at skin (as measured by dewpoint sensors)
    1071         5280 :             state.dataThermalComforts->SkinVapPress = state.dataThermalComforts->SkinWetTot * state.dataThermalComforts->SatSkinVapPress +
    1072         2640 :                                                       (1.0 - state.dataThermalComforts->SkinWetTot) * state.dataThermalComforts->VapPress;
    1073              :         } // END OF MINUTE BY MINUTE TEMPERATURE REGULATION LOOP
    1074              : 
    1075              :         // EvapHeatLossMax is readjusted for EvapEff
    1076           44 :         state.dataThermalComforts->EvapHeatLossMax *= EvapEff;
    1077              : 
    1078              :         // Step 3: Heat transfer indices in real environment. Computation of comfort indices.
    1079              :         // Inputs to this SECTION are the physiological data from the simulation of temperature regulation loop.
    1080           44 :         Real64 EffectSkinHeatLoss = state.dataThermalComforts->DryHeatLoss + state.dataThermalComforts->EvapHeatLoss;
    1081              :         // ET*(standardization humidity/REAL(r64) CloUnit, StdAtm and Hc)
    1082           44 :         state.dataThermalComforts->CloBodyRat = 1.0 + CloFac * CloUnit;
    1083              :         Real64 EffectCloUnit =
    1084           44 :             CloUnit - (state.dataThermalComforts->CloBodyRat - 1.0) / (0.155 * state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->H);
    1085           44 :         Real64 EffectCloThermEff = 1.0 / (1.0 + 0.155 * state.dataThermalComforts->Hc * EffectCloUnit);
    1086           88 :         state.dataThermalComforts->CloPermeatEff =
    1087           44 :             1.0 / (1.0 + (0.155 / state.dataThermalComforts->CloInsul) * state.dataThermalComforts->Hc * EffectCloUnit);
    1088              :         // Get a low approximation for ET* and solve balance equation by iteration
    1089           44 :         Real64 ET = state.dataThermalComforts->SkinTemp - EffectSkinHeatLoss / (state.dataThermalComforts->H * EffectCloThermEff);
    1090              :         Real64 EnergyBalErrET;
    1091              :         while (true) {
    1092          865 :             Real64 StdVapPressET = CalcSatVapPressFromTempTorr(ET); // THE STANDARD VAPOR PRESSURE AT THE EFFECTIVE TEMP : StdVapPressET
    1093          865 :             EnergyBalErrET = EffectSkinHeatLoss - state.dataThermalComforts->H * EffectCloThermEff * (state.dataThermalComforts->SkinTemp - ET) -
    1094          865 :                              state.dataThermalComforts->SkinWetTot * LewisRatio * state.dataThermalComforts->Hc *
    1095          865 :                                  state.dataThermalComforts->CloPermeatEff * (state.dataThermalComforts->SatSkinVapPress - StdVapPressET / 2.0);
    1096          865 :             if (EnergyBalErrET >= 0.0) break;
    1097          821 :             ET += 0.1;
    1098          821 :         }
    1099           44 :         state.dataThermalComforts->EffTemp = ET;
    1100              : 
    1101              :         // Standard effective temperature SET* standardized humidity.  Hc, CloUnit, StdAtm normalized for given ActLel AirVel
    1102              :         // Standard environment
    1103           44 :         Real64 StdHr = state.dataThermalComforts->Hr;
    1104              :         Real64 StdHc; // standard conv. heat tr. coeff. (level walking/still air)
    1105           44 :         if (ActMet <= 0.85) {
    1106            0 :             StdHc = 3.0; // minimum value of Hc at sea leAirVel = 3.0 (AirVel = .137 m/s)
    1107              :         } else {
    1108           44 :             StdHc = 5.66 * std::pow(ActMet - 0.85, 0.39);
    1109              :         }
    1110           44 :         if (StdHc <= 3.0) StdHc = 3.0;
    1111           44 :         Real64 StdH = StdHc + StdHr; // StdH Standard combined heat transfer coefficient
    1112              :         // standard MET - StdCloUnit relation gives SET* = 24 C when PMV = 0
    1113           44 :         Real64 StdCloUnit = 1.52 / (ActMet - WorkEff / ActLevelConv + 0.6944) - 0.1835;        // RCLOS
    1114           44 :         Real64 StdRClo = 0.155 * StdCloUnit;                                                   // RCLS
    1115           44 :         Real64 StdCloBodyRat = 1.0 + CloFac * StdCloUnit;                                      // FACLS
    1116           44 :         Real64 StdEffectCloThermEff = 1.0 / (1.0 + 0.155 * StdCloBodyRat * StdH * StdCloUnit); // FCLS
    1117           44 :         Real64 StdCloInsul = state.dataThermalComforts->CloInsul * StdHc / StdH * (1 - StdEffectCloThermEff) /
    1118           44 :                              (StdHc / StdH - state.dataThermalComforts->CloInsul * StdEffectCloThermEff);
    1119           44 :         Real64 StdREvap = 1.0 / (LewisRatio * StdCloBodyRat * StdHc);
    1120           44 :         Real64 StdREvapClo = StdRClo / (LewisRatio * StdCloInsul);
    1121           44 :         Real64 StdHEvap = 1.0 / (StdREvap + StdREvapClo);
    1122           44 :         Real64 StdRAir = 1.0 / (StdCloBodyRat * StdH);
    1123           44 :         Real64 StdHDry = 1.0 / (StdRAir + StdRClo);
    1124              : 
    1125              :         // Get a low approximation for SET* and solve balance equ. by iteration
    1126           44 :         Real64 StdEffectSkinHeatLoss = state.dataThermalComforts->DryHeatLoss + state.dataThermalComforts->EvapHeatLoss;
    1127           44 :         Real64 OldSET = round((state.dataThermalComforts->SkinTemp - StdEffectSkinHeatLoss / StdHDry) * 100) / 100;
    1128           44 :         Real64 delta = 0.0001;
    1129           44 :         Real64 err = 100.0;
    1130          138 :         while (std::abs(err) > 0.01) {
    1131           94 :             Real64 StdVapPressSET_1 = CalcSatVapPressFromTempTorr(OldSET); // StdVapPressSET *= VapPressConv;
    1132              :             Real64 EnergyBalErrSET_1 =
    1133           94 :                 StdEffectSkinHeatLoss - StdHDry * (state.dataThermalComforts->SkinTemp - OldSET) -
    1134           94 :                 state.dataThermalComforts->SkinWetTot * StdHEvap * (state.dataThermalComforts->SatSkinVapPress - StdVapPressSET_1 / 2.0);
    1135           94 :             Real64 StdVapPressSET_2 = CalcSatVapPressFromTempTorr(OldSET + delta);
    1136              :             Real64 EnergyBalErrSET_2 =
    1137           94 :                 StdEffectSkinHeatLoss - StdHDry * (state.dataThermalComforts->SkinTemp - (OldSET + delta)) -
    1138           94 :                 state.dataThermalComforts->SkinWetTot * StdHEvap * (state.dataThermalComforts->SatSkinVapPress - StdVapPressSET_2 / 2.0);
    1139           94 :             Real64 NewSET = OldSET - delta * EnergyBalErrSET_1 / (EnergyBalErrSET_2 - EnergyBalErrSET_1);
    1140           94 :             err = NewSET - OldSET;
    1141           94 :             OldSET = NewSET;
    1142              :         }
    1143           44 :         Real64 SET = OldSET;
    1144              :         // PMV*(PMVET in prgm) uses ET instead of OpTemp
    1145           44 :         state.dataThermalComforts->DryHeatLossET = StdH * StdEffectCloThermEff * (state.dataThermalComforts->SkinTemp - ET);
    1146              :         // SPMV*(PMVSET in prgm) uses SET instead of OpTemp
    1147           44 :         state.dataThermalComforts->DryHeatLossSET = StdH * StdEffectCloThermEff * (state.dataThermalComforts->SkinTemp - SET);
    1148           44 :         return SET;
    1149              :     }
    1150              : 
    1151            0 :     void CalcThermalComfortPierceASHRAE(EnergyPlusData &state)
    1152              :     {
    1153              :         // This subroutine calculates ET, SET, SETPMV, SETPPD using Pierce two-node model.
    1154              :         // Reference: ANSI/ASHRAE Standard 55-2017 Appendix D.
    1155              : 
    1156            0 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1157            0 :              ++state.dataThermalComforts->PeopleNum) {
    1158            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1159            0 :             if (!people.Pierce) continue;
    1160              : 
    1161            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1162              : 
    1163              :             // STEP 1: Get input (TA, TR, RH, VEL, CLO, MET, WME)
    1164            0 :             GetThermalComfortInputsASHRAE(state);
    1165              : 
    1166              :             // STEP 2: Calculate SET.
    1167            0 :             Real64 SET = CalcStandardEffectiveTemp(state,
    1168            0 :                                                    state.dataThermalComforts->AirTemp,
    1169            0 :                                                    state.dataThermalComforts->RadTemp,
    1170            0 :                                                    state.dataThermalComforts->RelHum,
    1171            0 :                                                    state.dataThermalComforts->AirVel,
    1172            0 :                                                    state.dataThermalComforts->ActMet,
    1173            0 :                                                    state.dataThermalComforts->CloUnit,
    1174            0 :                                                    state.dataThermalComforts->WorkEff);
    1175              : 
    1176              :             // STEP 3: Report SET related variables.
    1177              :             // Fanger's comfort equation. Thermal transfer coefficient to calculate PMV
    1178            0 :             state.dataThermalComforts->ThermSensTransCoef = 0.303 * std::exp(-0.036 * state.dataThermalComforts->ActLevel) + 0.028;
    1179              :             // Fanger's reg. sweating at comfort threshold (PMV=0) is:
    1180            0 :             state.dataThermalComforts->EvapHeatLossRegComf = (state.dataThermalComforts->IntHeatProd - ActLevelConv) * 0.42;
    1181            0 :             comfort.PiercePMVET =
    1182            0 :                 state.dataThermalComforts->ThermSensTransCoef *
    1183            0 :                 (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLossET -
    1184            0 :                  state.dataThermalComforts->EvapHeatLossDiff - state.dataThermalComforts->EvapHeatLossRegComf);
    1185            0 :             comfort.PiercePMVSET =
    1186            0 :                 state.dataThermalComforts->ThermSensTransCoef *
    1187            0 :                 (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLossSET -
    1188            0 :                  state.dataThermalComforts->EvapHeatLossDiff - state.dataThermalComforts->EvapHeatLossRegComf);
    1189              : 
    1190              :             // PHeat stress and heat strain indices derived from EvapHeatLoss, DISC (discomfort) varies with relative thermoregulatory strain
    1191            0 :             comfort.PierceDISC = 5.0 * (state.dataThermalComforts->EvapHeatLossRegSweat - state.dataThermalComforts->EvapHeatLossRegComf) /
    1192            0 :                                  (state.dataThermalComforts->EvapHeatLossMax - state.dataThermalComforts->EvapHeatLossRegComf -
    1193            0 :                                   state.dataThermalComforts->EvapHeatLossDiff);
    1194              : 
    1195              :             // Thermal sensation TSENS as function of mean body temp.-
    1196              :             // AvgBodyTempLow is AvgBodyTemp when DISC is 0. (lower limit of zone of evap. regul.)
    1197            0 :             Real64 AvgBodyTempLow = (0.185 / ActLevelConv) * (state.dataThermalComforts->ActLevel - state.dataThermalComforts->WorkEff) + 36.313;
    1198              :             // AvgBodyTempHigh is AvgBodyTemp when HSI=100 (upper limit of zone of evap. regul.)
    1199            0 :             Real64 AvgBodyTempHigh = (0.359 / ActLevelConv) * (state.dataThermalComforts->ActLevel - state.dataThermalComforts->WorkEff) + 36.664;
    1200              : 
    1201              :             // TSENS=DISC=4.7 when HSI =1 00 (HSI is Belding's classic heat stress index)
    1202              :             // In cold, DISC &TSENS are the same and neg. fct of AvgBodyTemp
    1203            0 :             if (state.dataThermalComforts->AvgBodyTemp > AvgBodyTempLow) {
    1204            0 :                 comfort.PierceTSENS = 4.7 * (state.dataThermalComforts->AvgBodyTemp - AvgBodyTempLow) / (AvgBodyTempHigh - AvgBodyTempLow);
    1205              : 
    1206              :             } else {
    1207            0 :                 comfort.PierceTSENS = 0.68175 * (state.dataThermalComforts->AvgBodyTemp - AvgBodyTempLow);
    1208            0 :                 comfort.PierceDISC = comfort.PierceTSENS;
    1209              :             }
    1210              : 
    1211            0 :             comfort.ThermalComfortMRT = state.dataThermalComforts->RadTemp;
    1212            0 :             comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1213            0 :             comfort.PierceSET = SET;
    1214              :         }
    1215            0 :     }
    1216              : 
    1217            2 :     void CalcThermalComfortCoolingEffectASH(EnergyPlusData &state)
    1218              :     {
    1219              :         // This subroutine calculates ASHRAE Cooling effect adjusted PMV and PPD
    1220              :         // Reference: ANSI/ASHRAE Standard 55-2017 Appendix D.
    1221              : 
    1222            4 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1223            2 :              ++state.dataThermalComforts->PeopleNum) {
    1224            2 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1225            2 :             if (!people.CoolingEffectASH55) continue;
    1226              : 
    1227            2 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1228              : 
    1229              :             // Get input (TA, TR, RH, VEL, CLO, MET, WME)
    1230            2 :             GetThermalComfortInputsASHRAE(state);
    1231              : 
    1232              :             // Calculate elevated air cooling effect using the SET function.
    1233            2 :             Real64 CoolingEffect = 0;
    1234              :             Real64 CoolingEffectAdjustedPMV;
    1235            2 :             CalcCoolingEffectAdjustedPMV(state, CoolingEffect, CoolingEffectAdjustedPMV);
    1236              : 
    1237              :             // Report.
    1238            2 :             comfort.CoolingEffectASH55 = CoolingEffect;
    1239            2 :             comfort.CoolingEffectAdjustedPMVASH55 = CoolingEffectAdjustedPMV;
    1240            2 :             comfort.CoolingEffectAdjustedPPDASH55 = CalcFangerPPD(CoolingEffectAdjustedPMV);
    1241              :         }
    1242            2 :     }
    1243              : 
    1244            2 :     void CalcCoolingEffectAdjustedPMV(EnergyPlusData &state, Real64 &CoolingEffect, Real64 &CoolingEffectAdjustedPMV)
    1245              :     {
    1246            2 :         auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1247              : 
    1248              :         // Calculate SET without cooling effect.
    1249            2 :         Real64 RelAirVel = CalcRelativeAirVelocity(state.dataThermalComforts->AirVel, state.dataThermalComforts->ActMet);
    1250            2 :         Real64 SET = CalcStandardEffectiveTemp(state,
    1251            2 :                                                state.dataThermalComforts->AirTemp,
    1252            2 :                                                state.dataThermalComforts->RadTemp,
    1253            2 :                                                state.dataThermalComforts->RelHum,
    1254              :                                                RelAirVel,
    1255            2 :                                                state.dataThermalComforts->ActMet,
    1256            2 :                                                state.dataThermalComforts->CloUnit,
    1257            2 :                                                state.dataThermalComforts->WorkEff);
    1258              : 
    1259              :         // TODO - This should use the ASHRAE55-2017 PMV calc program. The current Fanger PMV program are not consistent with the new standard.
    1260            2 :         Real64 ASHRAE55PMV = CalcFangerPMV(state,
    1261            2 :                                            state.dataThermalComforts->AirTemp,
    1262            2 :                                            state.dataThermalComforts->RadTemp,
    1263            2 :                                            state.dataThermalComforts->RelHum,
    1264              :                                            RelAirVel,
    1265            2 :                                            state.dataThermalComforts->ActLevel,
    1266            2 :                                            state.dataThermalComforts->CloUnit,
    1267            2 :                                            state.dataThermalComforts->WorkEff);
    1268              : 
    1269            2 :         Real64 StillAirVel = 0.1;
    1270           30 :         auto ce_root_function = [&state, &StillAirVel, &SET](Real64 x) {
    1271          210 :             return CalcStandardEffectiveTemp(state,
    1272           30 :                                              state.dataThermalComforts->AirTemp - x,
    1273           30 :                                              state.dataThermalComforts->RadTemp - x,
    1274           30 :                                              state.dataThermalComforts->RelHum,
    1275              :                                              StillAirVel,
    1276           30 :                                              state.dataThermalComforts->ActMet,
    1277           30 :                                              state.dataThermalComforts->CloUnit,
    1278           30 :                                              state.dataThermalComforts->WorkEff) -
    1279           30 :                    SET;
    1280            2 :         };
    1281              : 
    1282           28 :         auto ce_root_termination = [](Real64 min, Real64 max) { return abs(max - min) <= 0.01; };
    1283            2 :         Real64 lowerBound = 0.0;
    1284            2 :         Real64 upperBound = 50.0;
    1285              : 
    1286              :         // We have yet another solver?
    1287              :         try {
    1288            2 :             std::pair<Real64, Real64> solverResult = boost::math::tools::bisect(ce_root_function, lowerBound, upperBound, ce_root_termination);
    1289            2 :             CoolingEffect = (solverResult.first + solverResult.second) / 2;
    1290            0 :         } catch (const std::exception &e) {
    1291            0 :             ShowRecurringWarningErrorAtEnd(state,
    1292            0 :                                            "The cooling effect could not be solved for People=\"" + people.Name + "\"" +
    1293              :                                                "As a result, no cooling effect will be applied to adjust the PMV and PPD results.",
    1294            0 :                                            state.dataThermalComforts->CoolingEffectWarningInd);
    1295            0 :             CoolingEffect = 0;
    1296            0 :         }
    1297              : 
    1298            2 :         if (CoolingEffect > 0) {
    1299            2 :             CoolingEffectAdjustedPMV = CalcFangerPMV(state,
    1300            2 :                                                      state.dataThermalComforts->AirTemp - CoolingEffect,
    1301            2 :                                                      state.dataThermalComforts->RadTemp - CoolingEffect,
    1302            2 :                                                      state.dataThermalComforts->RelHum,
    1303              :                                                      StillAirVel,
    1304            2 :                                                      state.dataThermalComforts->ActLevel,
    1305            2 :                                                      state.dataThermalComforts->CloUnit,
    1306            2 :                                                      state.dataThermalComforts->WorkEff);
    1307              :         } else {
    1308            0 :             CoolingEffectAdjustedPMV = ASHRAE55PMV;
    1309              :         }
    1310            2 :     }
    1311              : 
    1312            1 :     void CalcThermalComfortAnkleDraftASH(EnergyPlusData &state)
    1313              :     {
    1314              :         // This subroutine calculates ASHRAE Ankle draft PPD
    1315              :         // Reference: ANSI/ASHRAE Standard 55-2017 Appendix I.
    1316              : 
    1317            2 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1318            1 :              ++state.dataThermalComforts->PeopleNum) {
    1319              : 
    1320            1 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1321            1 :             if (!people.AnkleDraftASH55) continue;
    1322              : 
    1323            1 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1324              : 
    1325            1 :             GetThermalComfortInputsASHRAE(state);
    1326            1 :             Real64 RelAirVel = CalcRelativeAirVelocity(state.dataThermalComforts->AirVel, state.dataThermalComforts->ActMet);
    1327            1 :             Real64 PPD_AD = -1.0;
    1328            1 :             if (state.dataThermalComforts->ActMet < 1.3 && state.dataThermalComforts->CloUnit < 0.7 && RelAirVel < 0.2) {
    1329            1 :                 Real64 AnkleAirVel = people.ankleAirVelocitySched->getCurrentVal();
    1330            1 :                 Real64 PMV = CalcFangerPMV(state,
    1331            1 :                                            state.dataThermalComforts->AirTemp,
    1332            1 :                                            state.dataThermalComforts->RadTemp,
    1333            1 :                                            state.dataThermalComforts->RelHum,
    1334              :                                            RelAirVel,
    1335            1 :                                            state.dataThermalComforts->ActLevel,
    1336            1 :                                            state.dataThermalComforts->CloUnit,
    1337            1 :                                            state.dataThermalComforts->WorkEff);
    1338            1 :                 PPD_AD = (std::exp(-2.58 + 3.05 * AnkleAirVel - 1.06 * PMV) / (1 + std::exp(-2.58 + 3.05 * AnkleAirVel - 1.06 * PMV))) * 100.0;
    1339              : 
    1340              :             } else {
    1341            0 :                 if (state.dataGlobal->DisplayExtraWarnings) {
    1342            0 :                     if (RelAirVel >= 0.2) {
    1343            0 :                         ShowRecurringWarningErrorAtEnd(
    1344              :                             state,
    1345              :                             "Relative air velocity is above 0.2 m/s in Ankle draft PPD calculations. PPD at ankle draft will be set to -1.0.",
    1346            0 :                             state.dataThermalComforts->AnkleDraftAirVelWarningInd,
    1347              :                             RelAirVel,
    1348              :                             RelAirVel,
    1349              :                             _,
    1350              :                             "[m/s]",
    1351              :                             "[m/s]");
    1352              :                     }
    1353            0 :                     if (state.dataThermalComforts->ActMet >= 1.3) {
    1354            0 :                         ShowRecurringWarningErrorAtEnd(
    1355              :                             state,
    1356              :                             "Metabolic rate is above 1.3 met in Ankle draft PPD calculations. PPD at ankle draft will be set to -1.0.",
    1357            0 :                             state.dataThermalComforts->AnkleDraftActMetWarningInd,
    1358            0 :                             state.dataThermalComforts->ActMet,
    1359            0 :                             state.dataThermalComforts->ActMet,
    1360              :                             _,
    1361              :                             "[m/s]",
    1362              :                             "[m/s]");
    1363              :                     }
    1364            0 :                     if (state.dataThermalComforts->CloUnit >= 0.7) {
    1365            0 :                         ShowRecurringWarningErrorAtEnd(
    1366              :                             state,
    1367              :                             "Clothing unit is above 0.7 in Ankle draft PPD calculations. PPD at ankle draft will be set to -1.0.",
    1368            0 :                             state.dataThermalComforts->AnkleDraftCloUnitWarningInd,
    1369            0 :                             state.dataThermalComforts->CloUnit,
    1370            0 :                             state.dataThermalComforts->CloUnit,
    1371              :                             _,
    1372              :                             "[m/s]",
    1373              :                             "[m/s]");
    1374              :                     }
    1375              :                 }
    1376              :             }
    1377            1 :             comfort.AnkleDraftPPDASH55 = PPD_AD;
    1378              :         }
    1379            1 :     }
    1380              : 
    1381            0 :     void CalcThermalComfortKSU(EnergyPlusData &state)
    1382              :     {
    1383              : 
    1384              :         // SUBROUTINE INFORMATION:
    1385              :         //     AUTHOR         Jaewook Lee
    1386              :         //     DATE WRITTEN   January 2000
    1387              :         //     MODIFIED       Rick Strand (for E+ implementation February 2000)
    1388              : 
    1389              :         // PURPOSE OF THIS SUBROUTINE:
    1390              :         // This subroutine calculates TSV using the KSU 2 Node model.
    1391              : 
    1392              :         // METHODOLOGY EMPLOYED:
    1393              :         // This subroutine is based heavily upon the work performed by Dan Maloney for
    1394              :         // the BLAST program.  Many of the equations are based on the original Pierce
    1395              :         // development.  See documentation for further details and references.
    1396              : 
    1397              :         // REFERENCES:
    1398              :         // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
    1399              : 
    1400              :         // SUBROUTINE PARAMETER DEFINITIONS:
    1401            0 :         Real64 constexpr CloEmiss(0.8); // Clothing Emissivity
    1402              : 
    1403              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1404              :         Real64 BodyWt;            // Weight of body, kg
    1405              :         Real64 DayNum;            // Number of days of acclimation
    1406              :         int NumDay;               // Loop counter for DayNum
    1407              :         Real64 EmissAvg;          // Average emissivity
    1408              :         int IncreDayNum;          // Number of days of increment in the outputs as desired
    1409              :         Real64 IntHeatProdMet;    // Internal heat production in MET
    1410              :         Real64 IntHeatProdMetMax; // Maximum value of internal heat production in MET
    1411              :         int LastDayNum;           // Number of days for the last print out
    1412              :         Real64 SkinWetFac;        // Skin wettedness factor
    1413              :         Real64 SkinWetNeut;       // Skin wettedness at neutral state
    1414              :         int StartDayNum;          // Number of days for the first print out
    1415              :         // Unacclimated man = 1, Acclimated man = 14
    1416              :         Real64 SweatSuppFac; // Sweat suppression factor due to skin wettedness
    1417              :         Real64 TempDiffer;   // Temperature difference between the rectal and esophageal temperatures
    1418              :         // If not measured, set it to be 0.5 Deg. C.
    1419              :         int TempIndiceNum;     // Number of temperature indices
    1420              :         Real64 ThermCndctMin;  // Minimum value of thermal conductance
    1421              :         Real64 ThermCndctNeut; // Thermal conductance at neutral state
    1422              :         Real64 TimeExpos;      // Time period in the exposure, hr
    1423              :         Real64 TimeInterval;   // Time interval of outputs desired, hr
    1424              :         Real64 TSVMax;         // Maximum value of thermal sensation vote
    1425              :         Real64 IntermediateClothing;
    1426              : 
    1427            0 :         TempIndiceNum = 2;
    1428              : 
    1429              :         // NEXT GROUP OF VARIABLE ARE FIXED FOR BLAST PROGRAM - UNACCLIMATED MAN
    1430              :         // THE TSV MODEL CAN BE APPLIED TO UNACCLIMATED MAN ONLY.
    1431            0 :         TimeInterval = 1.0;
    1432            0 :         TSVMax = 4.0;
    1433            0 :         StartDayNum = 1;
    1434            0 :         LastDayNum = 1;
    1435            0 :         IncreDayNum = 1;
    1436            0 :         TimeExpos = 1.0;
    1437            0 :         TempDiffer = 0.5;
    1438              : 
    1439            0 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1440            0 :              ++state.dataThermalComforts->PeopleNum) {
    1441              :             // THE NEXT SIX VARIABLES WILL BE READ IN FROM INPUT DECK
    1442            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1443            0 :             if (!people.KSU) continue;
    1444              : 
    1445            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1446              : 
    1447            0 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    1448            0 :             auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
    1449              : 
    1450            0 :             state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
    1451            0 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    1452            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
    1453            0 :                     state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
    1454            0 :                     state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum); // PH 3/7/04
    1455              :                 }
    1456              :             }
    1457            0 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
    1458            0 :             state.dataThermalComforts->RelHum =
    1459            0 :                 PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
    1460            0 :             state.dataThermalComforts->ActLevel = people.activityLevelSched->getCurrentVal() / BodySurfArea;
    1461            0 :             state.dataThermalComforts->WorkEff = people.workEffSched->getCurrentVal() * state.dataThermalComforts->ActLevel;
    1462              : 
    1463            0 :             switch (people.clothingType) {
    1464            0 :             case DataHeatBalance::ClothingType::InsulationSchedule: {
    1465            0 :                 state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
    1466            0 :             } break;
    1467            0 :             case DataHeatBalance::ClothingType::DynamicAshrae55: {
    1468            0 :                 comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1469            0 :                 comfort.ClothingValue = state.dataThermalComforts->CloUnit;
    1470            0 :                 DynamicClothingModel(state);
    1471            0 :                 state.dataThermalComforts->CloUnit = comfort.ClothingValue;
    1472            0 :             } break;
    1473            0 :             case DataHeatBalance::ClothingType::CalculationSchedule: {
    1474            0 :                 IntermediateClothing = people.clothingMethodSched->getCurrentVal();
    1475            0 :                 if (IntermediateClothing == 1.0) {
    1476            0 :                     state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
    1477            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
    1478            0 :                 } else if (IntermediateClothing == 2.0) {
    1479            0 :                     comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1480            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
    1481            0 :                     DynamicClothingModel(state);
    1482            0 :                     state.dataThermalComforts->CloUnit = comfort.ClothingValue;
    1483              :                 } else {
    1484            0 :                     state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
    1485            0 :                     ShowWarningError(
    1486            0 :                         state, format("PEOPLE=\"{}\", Scheduled clothing value will be used rather than clothing calculation method.", people.Name));
    1487              :                 }
    1488            0 :             } break;
    1489            0 :             default:
    1490            0 :                 ShowSevereError(state, format("PEOPLE=\"{}\", Incorrect Clothing Type", people.Name));
    1491              :             }
    1492              : 
    1493            0 :             state.dataThermalComforts->AirVel = people.airVelocitySched->getCurrentVal();
    1494            0 :             state.dataThermalComforts->IntHeatProd = state.dataThermalComforts->ActLevel - state.dataThermalComforts->WorkEff;
    1495              :             // THE FOLLOWING ARE TYPICAL VALUES SET FOR BLAST RUNS
    1496              :             // STANDARD MAN: 70. KG WEIGHT, 1.8 M2 SURFACE AREA
    1497            0 :             BodyWt = 70.0;
    1498            0 :             state.dataThermalComforts->CoreTemp = 37.0;
    1499            0 :             state.dataThermalComforts->SkinTemp = 31.0;
    1500              : 
    1501              :             //   CALCULATIONS NEEDED FOR THE PASSIVE STATE EQUATIONS
    1502            0 :             state.dataThermalComforts->CoreThermCap = 0.9 * BodyWt * 0.97 / BodySurfArea;
    1503            0 :             state.dataThermalComforts->SkinThermCap = 0.1 * BodyWt * 0.97 / BodySurfArea;
    1504              :             //   KERSLAKE'S FORMULA (0.05<AirVel<5. M/S)
    1505            0 :             if (state.dataThermalComforts->AirVel < 0.137) state.dataThermalComforts->AirVel = 0.137;
    1506            0 :             state.dataThermalComforts->Hc = 8.3 * std::sqrt(state.dataThermalComforts->AirVel);
    1507            0 :             EmissAvg = RadSurfEff * CloEmiss + (1.0 - RadSurfEff) * 1.0;
    1508              :             //   IBERALL EQUATION
    1509            0 :             state.dataThermalComforts->Hr = EmissAvg * (3.87 + 0.031 * state.dataThermalComforts->RadTemp);
    1510            0 :             state.dataThermalComforts->H = state.dataThermalComforts->Hr + state.dataThermalComforts->Hc;
    1511            0 :             state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hc * state.dataThermalComforts->AirTemp +
    1512            0 :                                                  state.dataThermalComforts->Hr * state.dataThermalComforts->RadTemp) /
    1513            0 :                                                 state.dataThermalComforts->H;
    1514            0 :             state.dataThermalComforts->VapPress = CalcSatVapPressFromTemp(state.dataThermalComforts->AirTemp);
    1515            0 :             state.dataThermalComforts->VapPress *= state.dataThermalComforts->RelHum;
    1516            0 :             state.dataThermalComforts->CloBodyRat = 1.0 + 0.2 * state.dataThermalComforts->CloUnit;
    1517            0 :             state.dataThermalComforts->CloThermEff =
    1518            0 :                 1.0 / (1.0 + 0.155 * state.dataThermalComforts->H * state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->CloUnit);
    1519            0 :             state.dataThermalComforts->CloPermeatEff = 1.0 / (1.0 + 0.143 * state.dataThermalComforts->Hc * state.dataThermalComforts->CloUnit);
    1520              :             //  BASIC INFORMATION FOR THERMAL SENSATION.
    1521            0 :             IntHeatProdMet = state.dataThermalComforts->IntHeatProd / ActLevelConv;
    1522            0 :             IntHeatProdMetMax = max(1.0, IntHeatProdMet);
    1523            0 :             ThermCndctNeut = 12.05 * std::exp(0.2266 * (IntHeatProdMetMax - 1.0));
    1524            0 :             SkinWetNeut = 0.02 + 0.4 * (1.0 - std::exp(-0.6 * (IntHeatProdMetMax - 1.0)));
    1525            0 :             ThermCndctMin = (ThermCndctNeut - 5.3) * 0.26074074 + 5.3;
    1526            0 :             Real64 const ThemCndct_75_fac(1.0 / (75.0 - ThermCndctNeut));
    1527            0 :             Real64 const ThemCndct_fac(1.0 / (ThermCndctNeut - ThermCndctMin));
    1528              :             //  CALCULATE THE PHYSIOLOGICAL REACTIONS OF AN UNACCLIMATED
    1529              :             //  MAN (LastDayNum = 1), OR AN ACCLIMATED MAN (LastDayNum = 14, IncreDayNum = 13),
    1530            0 :             assert(IncreDayNum > 0); // Autodesk:F2C++ Loop setup assumption
    1531            0 :             for (NumDay = StartDayNum; NumDay <= LastDayNum; NumDay += IncreDayNum) {
    1532              :                 //  INITIAL CONDITIONS IN AN EXPOSURE
    1533            0 :                 DayNum = double(NumDay);
    1534            0 :                 state.dataThermalComforts->Time = 0.0;
    1535            0 :                 state.dataThermalComforts->TimeChange = 0.01;
    1536            0 :                 SweatSuppFac = 1.0;
    1537            0 :                 state.dataThermalComforts->Temp(1) = state.dataThermalComforts->CoreTemp;
    1538            0 :                 state.dataThermalComforts->Temp(2) = state.dataThermalComforts->SkinTemp;
    1539            0 :                 state.dataThermalComforts->Coeff(1) = state.dataThermalComforts->Coeff(2) = 0.0;
    1540              :                 //  PHYSIOLOGICAL ADJUSTMENTS IN HEAT ACCLIMATION.
    1541            0 :                 state.dataThermalComforts->AcclPattern = 1.0 - std::exp(-0.12 * (DayNum - 1.0));
    1542            0 :                 state.dataThermalComforts->CoreTempNeut = 36.9 - 0.6 * state.dataThermalComforts->AcclPattern;
    1543            0 :                 state.dataThermalComforts->SkinTempNeut = 33.8 - 1.6 * state.dataThermalComforts->AcclPattern;
    1544            0 :                 state.dataThermalComforts->ActLevel -= 0.07 * state.dataThermalComforts->ActLevel * state.dataThermalComforts->AcclPattern;
    1545            0 :                 Real64 const SkinTempNeut_fac(1.0 / (1.0 - SkinWetNeut));
    1546              :                 //  CALCULATION OF CoreTempChange/TempChange & SkinTempChange/TempChange
    1547            0 :                 DERIV(state, TempIndiceNum, state.dataThermalComforts->Temp, state.dataThermalComforts->TempChange);
    1548              :                 while (true) {
    1549              :                     //  CALCULATION OF THERMAL SENSATION VOTE (TSV).
    1550              :                     //  THE TSV MODEL CAN BE APPLIED TO UNACCLIMATED MAN ONLY.
    1551            0 :                     SkinWetFac = (state.dataThermalComforts->SkinWetSweat - SkinWetNeut) * SkinTempNeut_fac;
    1552            0 :                     state.dataThermalComforts->VasodilationFac = (state.dataThermalComforts->ThermCndct - ThermCndctNeut) * ThemCndct_75_fac;
    1553            0 :                     state.dataThermalComforts->VasoconstrictFac = (ThermCndctNeut - state.dataThermalComforts->ThermCndct) * ThemCndct_fac;
    1554              :                     //  IF VasodilationFac < 0.0, VASOCONSTRICTION OCCURS AND RESULTS IN COLD SENSATION.
    1555              :                     //  OTHERWISE NORMAL BLOOD FLOW OR VASODILATION OCCURS AND RESULTS IN
    1556              :                     //  THERMAL NEUTRALITY OR WARM SENSATION.
    1557            0 :                     if (state.dataThermalComforts->VasodilationFac < 0) {
    1558            0 :                         comfort.KsuTSV = -1.46153 * state.dataThermalComforts->VasoconstrictFac +
    1559            0 :                                          3.74721 * pow_2(state.dataThermalComforts->VasoconstrictFac) -
    1560            0 :                                          6.168856 * pow_3(state.dataThermalComforts->VasoconstrictFac);
    1561              :                     } else {
    1562            0 :                         comfort.KsuTSV = (5.0 - 6.56 * (state.dataThermalComforts->RelHum - 0.50)) * SkinWetFac;
    1563            0 :                         if (comfort.KsuTSV > TSVMax) comfort.KsuTSV = TSVMax;
    1564              :                     }
    1565              : 
    1566            0 :                     comfort.ThermalComfortMRT = state.dataThermalComforts->RadTemp;
    1567            0 :                     comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1568              : 
    1569            0 :                     state.dataThermalComforts->CoreTemp = state.dataThermalComforts->Temp(1);
    1570            0 :                     state.dataThermalComforts->SkinTemp = state.dataThermalComforts->Temp(2);
    1571            0 :                     state.dataThermalComforts->EvapHeatLossSweatPrev = state.dataThermalComforts->EvapHeatLossSweat;
    1572              : 
    1573            0 :                     RKG(state,
    1574              :                         TempIndiceNum,
    1575            0 :                         state.dataThermalComforts->TimeChange,
    1576            0 :                         state.dataThermalComforts->Time,
    1577            0 :                         state.dataThermalComforts->Temp,
    1578            0 :                         state.dataThermalComforts->TempChange,
    1579            0 :                         state.dataThermalComforts->Coeff);
    1580              : 
    1581            0 :                     if (state.dataThermalComforts->Time > TimeExpos) break;
    1582              :                 }
    1583              :             }
    1584              :         }
    1585            0 :     }
    1586              : 
    1587            0 :     void DERIV(EnergyPlusData &state,
    1588              :                [[maybe_unused]] int &TempIndiceNum,    // Number of temperature indices  unused1208
    1589              :                [[maybe_unused]] Array1D<Real64> &Temp, // Temperature unused1208
    1590              :                Array1D<Real64> &TempChange             // Change of temperature
    1591              :     )
    1592              :     {
    1593              : 
    1594              :         // SUBROUTINE INFORMATION:
    1595              :         //     AUTHOR         Jaewook Lee
    1596              :         //     DATE WRITTEN   January 2000
    1597              :         //     MODIFIED       Rick Strand (for E+ implementation February 2000)
    1598              : 
    1599              :         // PURPOSE OF THIS SUBROUTINE:
    1600              :         // THIS SUBROUTINE CALCULATES HEAT TRANSFER TERMS INVOLVED IN THE
    1601              :         // THERMOREGULATORY SYSTEM TO OBTAIN THE RATES OF CHANGE OF CoreTemp & SkinTemp
    1602              :         // VIZ., CoreTempChange/TempChange & SkinTempChange/TempChange RESPECTIVELY.
    1603              : 
    1604              :         // METHODOLOGY EMPLOYED:
    1605              :         // This subroutine is based heavily upon the work performed by Dan Maloney for
    1606              :         // the BLAST program.  Many of the equations are based on the original Pierce
    1607              :         // development.  See documentation for further details and references.
    1608              : 
    1609              :         // REFERENCES:
    1610              :         // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
    1611              : 
    1612              :         // Argument array dimensioning
    1613              :         // EP_SIZE_CHECK(Temp, 2);
    1614            0 :         EP_SIZE_CHECK(TempChange, 2);
    1615              : 
    1616              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1617              :         Real64 ActLevelTot;             // Total activity level
    1618              :         Real64 CoreSignalShiv;          // Core signal when shivering occurs
    1619              :         Real64 CoreSignalShivMax;       // Maximum value of core signal when shivering occurs
    1620              :         Real64 CoreSignalSkinSens;      // The sensitivity of the skin signal increases
    1621              :         Real64 CoreSignalSweatMax;      // Maximum value of core signal when sweating occurs
    1622              :         Real64 CoreSignalSweatWarm;     // Core signal when sweating occurs
    1623              :         Real64 CoreTempSweat;           // Core temperature when sweating occurs
    1624              :         Real64 CoreSignalWarm;          // Warm core signal
    1625              :         Real64 CoreSignalWarmMax;       // Maximum value of warm core signal
    1626              :         Real64 EvapHeatLossDrySweat;    // Evaporative heat loss by sweating when total skin wettedness < 0.4
    1627              :         Real64 Err;                     // Stop criteria for iteration
    1628              :         Real64 ErrPrev;                 // Previous value of stop criteria for iteration
    1629              :         Real64 EvapHeatLossSweatEst;    // Estimated evaporative heat loss by sweating
    1630              :         Real64 EvapHeatLossSweatEstNew; // New value of estimated evaporative heat loss by sweating
    1631              :         Real64 IntHeatProdTot;          // Total internal heat production
    1632              :         Real64 SkinCndctMax;            // Maximum value of skin conductance
    1633              :         Real64 SkinSignalCold;          // Cold skin signal
    1634              :         Real64 SkinSignalColdMax;       // Maximum value of cold skin signal
    1635              :         Real64 SkinSignalSweatCold;     // Cold skin signal for sweat inhibition
    1636              :         Real64 SkinSignalSweatColdMax;  // Maximum value of cold skin signal for sweat inhibition
    1637              :         Real64 SkinCndctDilation;       // Overall skin conductance due to vasodilation
    1638              :         Real64 SkinCndctConstriction;   // Overall skin conductance due to vasoconstriction
    1639              :         Real64 SkinSignalShiv;          // Skin signal when shivering occurs
    1640              :         Real64 SkinSignalShivMax;       // Maximum value of skin signal when shivering occurs
    1641              :         Real64 SkinSignalSweatMax;      // Skin signal when sweating occurs
    1642              :         Real64 SkinSignalSweatWarm;     // Maximum value of skin signal when sweating occurs
    1643              :         Real64 SkinSignalWarm;          // Warm skin signal
    1644              :         Real64 SkinSignalWarmMax;       // Maximum value of warm skin signal
    1645              :         Real64 SkinTempSweat;           // Skin temperature when sweating occurs
    1646              :         Real64 SkinWetSignal;           // Skin wettedness signal
    1647              :         Real64 SweatCtrlFac;            // Sweat control factor
    1648              :         Real64 SweatSuppFac;            // Sweat suppression factor due to skin wettedness
    1649              :         Real64 WeighFac;                // Weighting factor of core signal
    1650              : 
    1651              :         // THE CONTROLLING SYSTEM.
    1652              :         // THE CONTROLLING SIGNALS :
    1653              :         // SIGNALS FOR KS.
    1654            0 :         CoreSignalWarm = state.dataThermalComforts->CoreTemp - 36.98;
    1655            0 :         SkinSignalWarm = state.dataThermalComforts->SkinTemp - 33.8;
    1656            0 :         SkinSignalCold = 32.1 - state.dataThermalComforts->SkinTemp;
    1657            0 :         CoreSignalSkinSens = state.dataThermalComforts->CoreTemp - 35.15;
    1658            0 :         CoreSignalWarmMax = max(0.0, CoreSignalWarm);
    1659            0 :         SkinSignalWarmMax = max(0.0, SkinSignalWarm);
    1660            0 :         SkinSignalColdMax = max(0.0, SkinSignalCold);
    1661              : 
    1662              :         // SIGNALS FOR EvapHeatLossSweat.
    1663            0 :         CoreTempSweat = state.dataThermalComforts->CoreTemp;
    1664            0 :         if (CoreTempSweat > 38.29) CoreTempSweat = 38.29;
    1665            0 :         CoreSignalSweatWarm = CoreTempSweat - state.dataThermalComforts->CoreTempNeut;
    1666            0 :         SkinTempSweat = state.dataThermalComforts->SkinTemp;
    1667            0 :         if (SkinTempSweat > 36.1) SkinTempSweat = 36.1;
    1668            0 :         SkinSignalSweatWarm = SkinTempSweat - state.dataThermalComforts->SkinTempNeut;
    1669            0 :         CoreSignalSweatMax = max(0.0, CoreSignalSweatWarm);
    1670            0 :         SkinSignalSweatMax = max(0.0, SkinSignalSweatWarm);
    1671            0 :         SkinSignalSweatCold = 33.37 - state.dataThermalComforts->SkinTemp;
    1672            0 :         if (state.dataThermalComforts->SkinTempNeut < 33.37)
    1673            0 :             SkinSignalSweatCold = state.dataThermalComforts->SkinTempNeut - state.dataThermalComforts->SkinTemp;
    1674            0 :         SkinSignalSweatColdMax = max(0.0, SkinSignalSweatCold);
    1675              : 
    1676              :         // SIGNALS FOR SHIVERING.
    1677            0 :         CoreSignalShiv = 36.9 - state.dataThermalComforts->CoreTemp;
    1678            0 :         SkinSignalShiv = 32.5 - state.dataThermalComforts->SkinTemp;
    1679            0 :         CoreSignalShivMax = max(0.0, CoreSignalShiv);
    1680            0 :         SkinSignalShivMax = max(0.0, SkinSignalShiv);
    1681              : 
    1682              :         // CONTROLLING FUNCTIONS :
    1683              :         // SHIVERING RESPONSE IN W/M**2.
    1684            0 :         state.dataThermalComforts->ShivResponse = 20.0 * CoreSignalShivMax * SkinSignalShivMax + 5.0 * SkinSignalShivMax;
    1685            0 :         if (state.dataThermalComforts->CoreTemp >= 37.1) state.dataThermalComforts->ShivResponse = 0.0;
    1686              : 
    1687              :         // SWEAT FUNCTION IN W/M**2.
    1688            0 :         WeighFac = 260.0 + 70.0 * state.dataThermalComforts->AcclPattern;
    1689            0 :         SweatCtrlFac = 1.0 + 0.05 * std::pow(SkinSignalSweatColdMax, 2.4);
    1690              : 
    1691              :         // EvapHeatLossDrySweat = SWEAT WHEN SkinWetTot < 0.4.
    1692            0 :         EvapHeatLossDrySweat =
    1693            0 :             ((WeighFac * CoreSignalSweatMax + 0.1 * WeighFac * SkinSignalSweatMax) * std::exp(SkinSignalSweatMax / 8.5)) / SweatCtrlFac;
    1694              : 
    1695              :         // MAXIMUM EVAPORATIVE POWER, EvapHeatLossMax, IN W/M**2.
    1696            0 :         state.dataThermalComforts->SkinVapPress = CalcSatVapPressFromTemp(state.dataThermalComforts->SkinTemp);
    1697            0 :         state.dataThermalComforts->EvapHeatLossMax = 2.2 * state.dataThermalComforts->Hc *
    1698            0 :                                                      (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress) *
    1699            0 :                                                      state.dataThermalComforts->CloPermeatEff;
    1700            0 :         if (state.dataThermalComforts->EvapHeatLossMax > 0.0) {
    1701            0 :             state.dataThermalComforts->SkinWetSweat = EvapHeatLossDrySweat / state.dataThermalComforts->EvapHeatLossMax;
    1702            0 :             state.dataThermalComforts->EvapHeatLossDiff = 0.408 * (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress);
    1703            0 :             state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->SkinWetSweat * state.dataThermalComforts->EvapHeatLossMax +
    1704            0 :                                                       (1.0 - state.dataThermalComforts->SkinWetSweat) * state.dataThermalComforts->EvapHeatLossDiff;
    1705            0 :             state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
    1706            0 :             if (state.dataThermalComforts->Time == 0.0) {
    1707            0 :                 state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossDrySweat;
    1708            0 :                 state.dataThermalComforts->EvapHeatLossSweatPrev = EvapHeatLossDrySweat;
    1709              :             }
    1710            0 :             if (state.dataThermalComforts->SkinWetTot > 0.4) {
    1711              : 
    1712              :                 // ITERATION  FOR SWEAT WHEN SkinWetTot IS GREATER THAT 0.4.
    1713            0 :                 state.dataThermalComforts->IterNum = 0;
    1714            0 :                 if (state.dataThermalComforts->SkinWetSweat > 1.0) state.dataThermalComforts->SkinWetSweat = 1.0;
    1715              :                 while (true) {
    1716            0 :                     EvapHeatLossSweatEst = state.dataThermalComforts->EvapHeatLossSweatPrev;
    1717            0 :                     state.dataThermalComforts->SkinWetSweat = EvapHeatLossSweatEst / state.dataThermalComforts->EvapHeatLossMax;
    1718              : 
    1719            0 :                     if (state.dataThermalComforts->SkinWetSweat > 1.0) state.dataThermalComforts->SkinWetSweat = 1.0;
    1720              : 
    1721            0 :                     state.dataThermalComforts->EvapHeatLossDiff =
    1722            0 :                         0.408 * (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress);
    1723            0 :                     state.dataThermalComforts->EvapHeatLoss =
    1724            0 :                         (1.0 - state.dataThermalComforts->SkinWetTot) * state.dataThermalComforts->EvapHeatLossDiff +
    1725            0 :                         state.dataThermalComforts->EvapHeatLossSweat;
    1726            0 :                     state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
    1727              : 
    1728            0 :                     if (state.dataThermalComforts->SkinWetTot > 1.0) state.dataThermalComforts->SkinWetTot = 1.0;
    1729              : 
    1730            0 :                     SkinWetSignal = max(0.0, state.dataThermalComforts->SkinWetTot - 0.4);
    1731            0 :                     SweatSuppFac = 0.5 + 0.5 * std::exp(-5.6 * SkinWetSignal);
    1732            0 :                     EvapHeatLossSweatEstNew = SweatSuppFac * EvapHeatLossDrySweat;
    1733              : 
    1734            0 :                     if (state.dataThermalComforts->IterNum == 0) state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossSweatEstNew;
    1735              : 
    1736            0 :                     Err = EvapHeatLossSweatEst - EvapHeatLossSweatEstNew;
    1737              : 
    1738            0 :                     if (state.dataThermalComforts->IterNum != 0) {
    1739            0 :                         if ((ErrPrev * Err) < 0.0)
    1740            0 :                             state.dataThermalComforts->EvapHeatLossSweat = (EvapHeatLossSweatEst + EvapHeatLossSweatEstNew) / 2.0;
    1741            0 :                         if ((ErrPrev * Err) >= 0.0) state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossSweatEstNew;
    1742              :                     }
    1743              : 
    1744              :                     // STOP CRITERION FOR THE ITERATION.
    1745            0 :                     if ((std::abs(Err) <= 0.5) || (state.dataThermalComforts->IterNum >= 10)) break;
    1746            0 :                     ++state.dataThermalComforts->IterNum;
    1747            0 :                     state.dataThermalComforts->EvapHeatLossSweatPrev = state.dataThermalComforts->EvapHeatLossSweat;
    1748            0 :                     ErrPrev = Err;
    1749              :                 }
    1750              : 
    1751              :             } else {
    1752            0 :                 state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossDrySweat;
    1753              :             }
    1754              : 
    1755              :         } else {
    1756            0 :             state.dataThermalComforts->SkinWetSweat = 1.0;
    1757            0 :             state.dataThermalComforts->SkinWetTot = 1.0;
    1758            0 :             state.dataThermalComforts->EvapHeatLossSweat = 0.5 * EvapHeatLossDrySweat;
    1759            0 :             state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossSweat;
    1760              :         }
    1761              : 
    1762              :         // OVERALL SKIN CONDUCTANCE, KS, IN W/M**2/C.
    1763              :         // SkinCndctDilation = EFFECT DUE TO VASODILATION.
    1764              :         // SkinCndctConstriction = EFFECT DUE TO VASOCONSTRICTION.
    1765            0 :         SkinCndctDilation = 42.45 * CoreSignalWarmMax + 8.15 * std::pow(CoreSignalSkinSens, 0.8) * SkinSignalWarmMax;
    1766            0 :         SkinCndctConstriction = 1.0 + 0.4 * SkinSignalColdMax;
    1767              :         // ThermCndct IS EQUIVALENT TO KS
    1768            0 :         state.dataThermalComforts->ThermCndct = 5.3 + (6.75 + SkinCndctDilation) / SkinCndctConstriction;
    1769            0 :         SkinCndctMax = 75.0 + 10.0 * state.dataThermalComforts->AcclPattern;
    1770            0 :         if (state.dataThermalComforts->ThermCndct > SkinCndctMax) state.dataThermalComforts->ThermCndct = SkinCndctMax;
    1771              : 
    1772              :         // PASSIVE ENERGY BALANCE EQUATIONS.
    1773              :         // TOTAL METABOLIC HEAT PRODUCTION RATE, ActLevel, IN W/M**2.
    1774            0 :         ActLevelTot = state.dataThermalComforts->ActLevel + state.dataThermalComforts->ShivResponse;
    1775            0 :         IntHeatProdTot = ActLevelTot - state.dataThermalComforts->WorkEff;
    1776              :         // RESPIRATION HEAT LOSS, RespHeatLoss, IN W/M**0.
    1777            0 :         state.dataThermalComforts->LatRespHeatLoss = 0.0023 * ActLevelTot * (44.0 - state.dataThermalComforts->VapPress);
    1778            0 :         state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevelTot * (34.0 - state.dataThermalComforts->AirTemp);
    1779            0 :         state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
    1780              :         // HEAT FLOW FROM CORE TO SKIN, HeatFlow, IN W/M**2.
    1781            0 :         state.dataThermalComforts->HeatFlow =
    1782            0 :             state.dataThermalComforts->ThermCndct * (state.dataThermalComforts->CoreTemp - state.dataThermalComforts->SkinTemp);
    1783              :         // TempChange(1) = CoreTempChange/TempChange, IN C/HR.
    1784            0 :         TempChange(1) = (IntHeatProdTot - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->HeatFlow) /
    1785            0 :                         state.dataThermalComforts->CoreThermCap;
    1786            0 :         if (state.dataThermalComforts->EvapHeatLoss > state.dataThermalComforts->EvapHeatLossMax)
    1787            0 :             state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossMax;
    1788              : 
    1789              :         // DRY HEAT EXCHANGE BY RADIATION & CONVECTION, R+C, IN W/M**2.
    1790            0 :         state.dataThermalComforts->DryHeatLoss = state.dataThermalComforts->H * state.dataThermalComforts->CloBodyRat *
    1791            0 :                                                  state.dataThermalComforts->CloThermEff *
    1792            0 :                                                  (state.dataThermalComforts->SkinTemp - state.dataThermalComforts->OpTemp);
    1793              :         // TempChange(2) = SkinTempChange/TempChange, IN C/HR.
    1794            0 :         TempChange(2) = (state.dataThermalComforts->HeatFlow - state.dataThermalComforts->EvapHeatLoss - state.dataThermalComforts->DryHeatLoss) /
    1795            0 :                         state.dataThermalComforts->SkinThermCap;
    1796            0 :     }
    1797              : 
    1798            0 :     void RKG(EnergyPlusData &state, int &NEQ, Real64 const H, Real64 &X, Array1D<Real64> &Y, Array1D<Real64> &DY, Array1D<Real64> &C)
    1799              :     {
    1800              : 
    1801              :         // SUBROUTINE INFORMATION:
    1802              :         //     AUTHOR         Jaewook Lee
    1803              :         //     DATE WRITTEN   January 2000
    1804              :         //     MODIFIED       Rick Strand (for E+ implementation February 2000)
    1805              : 
    1806              :         // PURPOSE OF THIS SUBROUTINE:
    1807              :         // This is a subroutine for integration by Runga-Kutta's method.
    1808              : 
    1809              :         // METHODOLOGY EMPLOYED:
    1810              :         // This subroutine is based heavily upon the work performed by Dan Maloney for
    1811              :         // the BLAST program.  Many of the equations are based on the original Pierce
    1812              :         // development.  See documentation for further details and references.
    1813              : 
    1814              :         // REFERENCES:
    1815              :         // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
    1816              : 
    1817              :         // Argument array dimensioning
    1818            0 :         EP_SIZE_CHECK(Y, NEQ);
    1819            0 :         EP_SIZE_CHECK(DY, NEQ);
    1820            0 :         EP_SIZE_CHECK(C, NEQ);
    1821              : 
    1822              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1823              :         int I;
    1824              :         int J;
    1825              :         Real64 B;
    1826              :         Real64 H2;
    1827              :         static constexpr std::array<Real64, 2> A = {0.29289321881345, 1.70710678118654};
    1828              : 
    1829            0 :         H2 = 0.5 * H;
    1830              : 
    1831            0 :         DERIV(state, NEQ, Y, DY);
    1832            0 :         for (I = 1; I <= NEQ; ++I) {
    1833            0 :             B = H2 * DY(I) - C(I);
    1834            0 :             Y(I) += B;
    1835            0 :             C(I) += 3.0 * B - H2 * DY(I);
    1836              :         }
    1837              : 
    1838            0 :         X += H2;
    1839              : 
    1840            0 :         for (J = 0; J < 2; ++J) {
    1841            0 :             DERIV(state, NEQ, Y, DY);
    1842            0 :             for (I = 1; I <= NEQ; ++I) {
    1843            0 :                 B = A[J] * (H * DY(I) - C(I));
    1844            0 :                 Y(I) += B;
    1845            0 :                 C(I) += 3.0 * B - A[J] * H * DY(I);
    1846              :             }
    1847              :         }
    1848              : 
    1849            0 :         X += H2;
    1850            0 :         DERIV(state, NEQ, Y, DY);
    1851              : 
    1852            0 :         for (I = 1; I <= NEQ; ++I) {
    1853            0 :             B = (H * DY(I) - 2.0 * C(I)) / 6.0;
    1854            0 :             Y(I) += B;
    1855            0 :             C(I) += 3.0 * B - H2 * DY(I);
    1856              :         }
    1857              : 
    1858            0 :         DERIV(state, NEQ, Y, DY);
    1859            0 :     }
    1860              : 
    1861          104 :     void GetAngleFactorList(EnergyPlusData &state)
    1862              :     {
    1863              : 
    1864              :         // SUBROUTINE INFORMATION:
    1865              :         //     AUTHOR         Jaewook Lee
    1866              :         //     DATE WRITTEN   July 2001
    1867              : 
    1868              :         static constexpr std::string_view routineName("GetAngleFactorList: "); // include trailing blank space
    1869          104 :         Real64 constexpr AngleFacLimit(0.01);                                  // To set the limit of sum of angle factors
    1870              : 
    1871          104 :         bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
    1872              :         int IOStatus;
    1873              :         int NumAlphas;  // Number of Alphas from InputProcessor
    1874              :         int NumNumbers; // Number of Numbers from Input Processor
    1875          104 :         auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
    1876              : 
    1877          104 :         cCurrentModuleObject = "ComfortViewFactorAngles";
    1878          104 :         int NumOfAngleFactorLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
    1879          104 :         state.dataThermalComforts->AngleFactorList.allocate(NumOfAngleFactorLists);
    1880              : 
    1881          105 :         for (int Item = 1; Item <= NumOfAngleFactorLists; ++Item) {
    1882              : 
    1883            1 :             Real64 AllAngleFacSummed = 0.0; // Sum of angle factors in each zone
    1884            1 :             auto &thisAngFacList(state.dataThermalComforts->AngleFactorList(Item));
    1885              : 
    1886            2 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    1887              :                                                                      cCurrentModuleObject,
    1888              :                                                                      Item,
    1889            1 :                                                                      state.dataIPShortCut->cAlphaArgs,
    1890              :                                                                      NumAlphas,
    1891            1 :                                                                      state.dataIPShortCut->rNumericArgs,
    1892              :                                                                      NumNumbers,
    1893              :                                                                      IOStatus,
    1894            1 :                                                                      state.dataIPShortCut->lNumericFieldBlanks,
    1895            1 :                                                                      state.dataIPShortCut->lAlphaFieldBlanks,
    1896            1 :                                                                      state.dataIPShortCut->cAlphaFieldNames,
    1897            1 :                                                                      state.dataIPShortCut->cNumericFieldNames);
    1898              : 
    1899            1 :             thisAngFacList.Name = state.dataIPShortCut->cAlphaArgs(1); // no need for verification/uniqueness.
    1900              : 
    1901            1 :             thisAngFacList.TotAngleFacSurfaces = NumNumbers;
    1902            1 :             thisAngFacList.SurfaceName.allocate(thisAngFacList.TotAngleFacSurfaces);
    1903            1 :             thisAngFacList.SurfacePtr.allocate(thisAngFacList.TotAngleFacSurfaces);
    1904            1 :             thisAngFacList.AngleFactor.allocate(thisAngFacList.TotAngleFacSurfaces);
    1905              : 
    1906          101 :             for (int SurfNum = 1; SurfNum <= thisAngFacList.TotAngleFacSurfaces; ++SurfNum) {
    1907          100 :                 thisAngFacList.SurfaceName(SurfNum) = state.dataIPShortCut->cAlphaArgs(SurfNum + 1);
    1908          100 :                 thisAngFacList.SurfacePtr(SurfNum) = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(SurfNum + 1), state.dataSurface->Surface);
    1909          100 :                 thisAngFacList.AngleFactor(SurfNum) = state.dataIPShortCut->rNumericArgs(SurfNum);
    1910              :                 // Error trap for surfaces that do not exist or surfaces not in the zone
    1911          100 :                 if (thisAngFacList.SurfacePtr(SurfNum) == 0) {
    1912            0 :                     ShowSevereError(state,
    1913            0 :                                     format("{}: invalid {}, entered value={}",
    1914              :                                            cCurrentModuleObject,
    1915            0 :                                            state.dataIPShortCut->cAlphaFieldNames(SurfNum + 1),
    1916            0 :                                            state.dataIPShortCut->cAlphaArgs(SurfNum + 1)));
    1917            0 :                     ShowContinueError(state,
    1918            0 :                                       format("ref {}={} not found in {}={}",
    1919            0 :                                              state.dataIPShortCut->cAlphaFieldNames(1),
    1920            0 :                                              state.dataIPShortCut->cAlphaArgs(1),
    1921            0 :                                              state.dataIPShortCut->cAlphaFieldNames(2),
    1922            0 :                                              state.dataIPShortCut->cAlphaArgs(2)));
    1923            0 :                     ErrorsFound = true;
    1924              :                 } else {
    1925              :                     // Found Surface, is it in same enclosure?
    1926          100 :                     auto &thisSurf = state.dataSurface->Surface(thisAngFacList.SurfacePtr(SurfNum));
    1927          100 :                     if (SurfNum == 1) thisAngFacList.EnclosurePtr = thisSurf.RadEnclIndex; // Save enclosure num of first surface
    1928          100 :                     if (thisAngFacList.EnclosurePtr != thisSurf.RadEnclIndex) {
    1929            0 :                         ShowWarningError(state,
    1930            0 :                                          format("{}: For {}=\"{}\", surfaces are not all in the same radiant enclosure.",
    1931              :                                                 routineName,
    1932              :                                                 cCurrentModuleObject,
    1933            0 :                                                 thisAngFacList.Name));
    1934            0 :                         ShowContinueError(state,
    1935            0 :                                           format("... Surface=\"{}\" is in enclosure=\"{}\"",
    1936            0 :                                                  state.dataSurface->Surface(thisAngFacList.SurfacePtr(1)).Name,
    1937            0 :                                                  state.dataViewFactor->EnclRadInfo(thisAngFacList.EnclosurePtr).Name));
    1938            0 :                         ShowContinueError(state,
    1939            0 :                                           format("... Surface=\"{}\" is in enclosure=\"{}\"",
    1940            0 :                                                  thisSurf.Name,
    1941            0 :                                                  state.dataViewFactor->EnclRadInfo(thisSurf.RadEnclIndex).Name));
    1942              :                     }
    1943              :                 }
    1944              : 
    1945          100 :                 AllAngleFacSummed += thisAngFacList.AngleFactor(SurfNum);
    1946              :             }
    1947              : 
    1948            1 :             if (std::abs(AllAngleFacSummed - 1.0) > AngleFacLimit) {
    1949            0 :                 ShowSevereError(state, format("{}=\"{}\", invalid - Sum[AngleFactors]", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
    1950            0 :                 ShowContinueError(state,
    1951            0 :                                   format("...Sum of Angle Factors [{:.3R}] should not deviate from expected sum [1.0] by more than limit [{:.3R}].",
    1952              :                                          AllAngleFacSummed,
    1953              :                                          AngleFacLimit));
    1954            0 :                 ErrorsFound = true;
    1955              :             }
    1956              :         }
    1957              : 
    1958          104 :         if (ErrorsFound) {
    1959            0 :             ShowFatalError(state, "GetAngleFactorList: Program terminated due to preceding errors.");
    1960              :         }
    1961              : 
    1962          155 :         for (int Item = 1; Item <= state.dataHeatBal->TotPeople; ++Item) {
    1963           51 :             auto &thisPeople = state.dataHeatBal->People(Item);
    1964           51 :             if (thisPeople.MRTCalcType != DataHeatBalance::CalcMRT::AngleFactor) continue;
    1965            0 :             thisPeople.AngleFactorListPtr = Util::FindItemInList(thisPeople.AngleFactorListName, state.dataThermalComforts->AngleFactorList);
    1966            0 :             int WhichAFList = thisPeople.AngleFactorListPtr;
    1967            0 :             if (WhichAFList == 0 && (thisPeople.Fanger || thisPeople.Pierce || thisPeople.KSU)) {
    1968            0 :                 ShowSevereError(state, format("{}{}=\"{}\", invalid", routineName, cCurrentModuleObject, thisPeople.AngleFactorListName));
    1969            0 :                 ShowContinueError(state, format("... Angle Factor List Name not found for PEOPLE=\"{}\"", thisPeople.Name));
    1970            0 :                 ErrorsFound = true;
    1971              :             } else {
    1972            0 :                 auto &thisAngFacList = state.dataThermalComforts->AngleFactorList(WhichAFList);
    1973            0 :                 if (state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum != thisAngFacList.EnclosurePtr &&
    1974            0 :                     (thisPeople.Fanger || thisPeople.Pierce || thisPeople.KSU)) {
    1975            0 :                     ShowWarningError(state,
    1976            0 :                                      format("{}{}=\"{}\", radiant enclosure mismatch.", routineName, cCurrentModuleObject, thisAngFacList.Name));
    1977            0 :                     ShowContinueError(
    1978              :                         state,
    1979            0 :                         format("...Enclosure=\"{}\" doe not match enclosure=\"{}\" for PEOPLE=\"{}\"",
    1980            0 :                                state.dataViewFactor->EnclRadInfo(thisAngFacList.EnclosurePtr).Name,
    1981            0 :                                state.dataViewFactor->EnclRadInfo(state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum).Name,
    1982            0 :                                thisPeople.Name));
    1983              :                 }
    1984              :             }
    1985              :         }
    1986              : 
    1987          104 :         if (ErrorsFound) {
    1988            0 :             ShowFatalError(state, "GetAngleFactorList: Program terminated due to preceding errors.");
    1989              :         }
    1990          104 :     }
    1991              : 
    1992            1 :     Real64 CalcAngleFactorMRT(EnergyPlusData &state, int const AngleFacNum)
    1993              :     {
    1994              : 
    1995              :         // SUBROUTINE INFORMATION:
    1996              :         //     AUTHOR         Jaewook Lee
    1997              :         //     DATE WRITTEN   July 2001
    1998              :         //     MODIFIED       November 2017 (R Strand): Added fourth power and emissivity to calculation
    1999              : 
    2000              :         // Return value
    2001              :         Real64 CalcAngleFactorMRT;
    2002              : 
    2003            1 :         Real64 SurfTempEmissAngleFacSummed = 0.0;
    2004            1 :         Real64 SumSurfaceEmissAngleFactor = 0.0;
    2005              : 
    2006            1 :         auto &thisAngFacList(state.dataThermalComforts->AngleFactorList(AngleFacNum));
    2007              : 
    2008            4 :         for (int SurfNum = 1; SurfNum <= thisAngFacList.TotAngleFacSurfaces; ++SurfNum) {
    2009            3 :             Real64 SurfaceTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(thisAngFacList.SurfacePtr(SurfNum)) + Constant::Kelvin;
    2010              :             Real64 SurfEAF =
    2011            3 :                 state.dataConstruction->Construct(state.dataSurface->Surface(thisAngFacList.SurfacePtr(SurfNum)).Construction).InsideAbsorpThermal *
    2012            3 :                 thisAngFacList.AngleFactor(SurfNum);
    2013            3 :             SurfTempEmissAngleFacSummed += SurfEAF * pow_4(SurfaceTemp);
    2014            3 :             SumSurfaceEmissAngleFactor += SurfEAF;
    2015              :         }
    2016              : 
    2017            1 :         CalcAngleFactorMRT = root_4(SurfTempEmissAngleFacSummed / SumSurfaceEmissAngleFactor) - Constant::Kelvin;
    2018              : 
    2019            1 :         return CalcAngleFactorMRT;
    2020              :     }
    2021              : 
    2022           18 :     Real64 CalcSurfaceWeightedMRT(EnergyPlusData &state, int const SurfNum, bool AverageWithSurface)
    2023              :     {
    2024              : 
    2025              :         // Purpose: Calculate a modified zone MRT that excludes the Surface( SurfNum ).
    2026              :         //          This is necessary for the surface weighted option to not in essence
    2027              :         //          double count SurfNum in the MRT calculation when averaged with the Surface( SurfNum ).
    2028              :         //          Other than that, the method here is the same as CalculateZoneMRT.  Once a modified zone
    2029              :         //          MRT is calculated, the subroutine then calculates and returns the
    2030              :         //          RadTemp (radiant temperature) for use by the thermal comfort routines
    2031              :         //          that is the average of the surface temperature to be weighted and
    2032              :         //          the modified zone MRT.
    2033              : 
    2034              :         // Return value
    2035           18 :         Real64 CalcSurfaceWeightedMRT = 0.0;
    2036              : 
    2037              :         // Initialize ZoneAESum for all zones and SurfaceAE for all surfaces at the start of the simulation
    2038           18 :         if (state.dataThermalComforts->FirstTimeSurfaceWeightedFlag) {
    2039           18 :             state.dataThermalComforts->FirstTimeError = true;
    2040           18 :             state.dataThermalComforts->FirstTimeSurfaceWeightedFlag = false;
    2041           36 :             for (auto const &thisRadEnclosure : state.dataViewFactor->EnclRadInfo) {
    2042          180 :                 for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2043          162 :                     auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
    2044          162 :                     thisSurface2.AE = thisSurface2.Area * state.dataConstruction->Construct(thisSurface2.Construction).InsideAbsorpThermal;
    2045              :                 }
    2046              :                 // Do NOT include the contribution of the Surface that is being surface weighted in this calculation since it will already be
    2047              :                 // accounted for
    2048          180 :                 for (int const SurfNum1 : thisRadEnclosure.SurfacePtr) {
    2049          162 :                     auto &thisSurface1 = state.dataSurface->Surface(SurfNum1);
    2050          162 :                     thisSurface1.enclAESum = 0.0;
    2051         1944 :                     for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2052         1782 :                         if (SurfNum2 == SurfNum1) continue;
    2053         1620 :                         auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
    2054         1620 :                         thisSurface1.enclAESum += thisSurface2.AE;
    2055              :                     }
    2056              :                 }
    2057              :             }
    2058              :         }
    2059              : 
    2060              :         // Calculate the sum of area*emissivity and area*emissivity*temperature for all surfaces in the zone EXCEPT the surface being weighted
    2061           18 :         Real64 sumAET = 0.0; // Intermediate calculational variable (area*emissivity*T) sum
    2062              : 
    2063           18 :         auto &thisSurface = state.dataSurface->Surface(SurfNum);
    2064           18 :         auto &thisRadEnclosure = state.dataViewFactor->EnclRadInfo(thisSurface.RadEnclIndex);
    2065              :         // Recalc SurfaceEnclAESum only if needed due to window shades or EMS
    2066           18 :         if (thisRadEnclosure.radReCalc) {
    2067            0 :             thisSurface.enclAESum = 0.0;
    2068            0 :             for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2069            0 :                 if (SurfNum2 == SurfNum) continue;
    2070            0 :                 auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
    2071            0 :                 thisSurface2.AE = thisSurface2.Area * state.dataConstruction->Construct(thisSurface2.Construction).InsideAbsorpThermal;
    2072            0 :                 thisSurface.enclAESum += thisSurface2.AE;
    2073              :             }
    2074              :         }
    2075          180 :         for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2076          162 :             if (SurfNum2 == SurfNum) continue;
    2077          144 :             sumAET += state.dataSurface->Surface(SurfNum2).AE * state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum2);
    2078              :         }
    2079              : 
    2080              :         // Now weight the MRT
    2081           18 :         auto &thisSurfaceTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum);
    2082           18 :         if (thisSurface.enclAESum > 0.01) {
    2083           18 :             CalcSurfaceWeightedMRT = sumAET / thisSurface.enclAESum;
    2084              :             // if averaged with surface--half comes from the surface used for weighting (SurfNum) and the rest from the calculated MRT that excludes
    2085              :             // this surface
    2086           18 :             if (AverageWithSurface) {
    2087            9 :                 CalcSurfaceWeightedMRT = 0.5 * (thisSurfaceTemp + CalcSurfaceWeightedMRT);
    2088              :             }
    2089              :         } else {
    2090            0 :             if (state.dataThermalComforts->FirstTimeError) {
    2091            0 :                 int spaceNum = thisSurface.spaceNum;
    2092            0 :                 ShowWarningError(state,
    2093            0 :                                  format("CalcSurfaceWeightedMRT: Areas*Inside surface emissivities are summing to zero for Enclosure=\"{}\"",
    2094            0 :                                         thisRadEnclosure.Name));
    2095            0 :                 ShowContinueError(state,
    2096            0 :                                   format("As a result, the MAT for Space={} will be used for MRT when calculating the surface weighted MRT.",
    2097            0 :                                          state.dataHeatBal->space(spaceNum).Name));
    2098            0 :                 ShowContinueError(state, format("for Surface={}", thisSurface.Name));
    2099            0 :                 state.dataThermalComforts->FirstTimeError = false;
    2100            0 :                 CalcSurfaceWeightedMRT = state.dataZoneTempPredictorCorrector->spaceHeatBalance(spaceNum).MAT;
    2101            0 :                 if (AverageWithSurface) {
    2102            0 :                     CalcSurfaceWeightedMRT = 0.5 * (thisSurfaceTemp + CalcSurfaceWeightedMRT);
    2103              :                 }
    2104              :             }
    2105              :         }
    2106              : 
    2107           18 :         return CalcSurfaceWeightedMRT;
    2108              :     }
    2109              : 
    2110            0 :     Real64 CalcSatVapPressFromTemp(Real64 const Temp)
    2111              :     {
    2112              : 
    2113              :         // FUNCTION INFORMATION:
    2114              :         //     AUTHOR         Jaewook Lee
    2115              :         //     DATE WRITTEN   January 2000
    2116              :         //     MODIFIED       Rick Strand (for E+ implementation February 2000)
    2117              : 
    2118              :         // PURPOSE OF THIS FUNCTION:
    2119              :         // THIS IS A FUNCTION TO CALCULATE THE SATURATED VAPOR PRESSURE
    2120              :         // FROM AIR TEMPERATURE
    2121              : 
    2122              :         // METHODOLOGY EMPLOYED:
    2123              :         // This function is based upon the work performed by Dan Maloney for
    2124              :         // the BLAST program.
    2125              :         // REFERENCES:
    2126              :         // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
    2127              : 
    2128            0 :         Real64 const XT(Temp / 100.0);
    2129            0 :         return 6.16796 + 358.1855 * pow_2(XT) - 550.3543 * pow_3(XT) + 1048.8115 * pow_4(XT);
    2130              : 
    2131              :         // Helper function for pierceSET calculates Saturated Vapor Pressure (Torr) at Temperature T (°C)
    2132              :         //        return Math.exp(18.6686 - 4030.183/(T + 235.0));
    2133              :     }
    2134              : 
    2135         3737 :     Real64 CalcSatVapPressFromTempTorr(Real64 const Temp)
    2136              :     {
    2137              :         // Helper function for pierceSET calculates Saturated Vapor Pressure (Torr) at Temperature T (°C)
    2138         3737 :         return std::exp(18.6686 - 4030.183 / (Temp + 235.0));
    2139              :     }
    2140              : 
    2141         1832 :     Real64 CalcRadTemp(EnergyPlusData &state, int const PeopleListNum)
    2142              :     {
    2143              : 
    2144              :         // FUNCTION INFORMATION:
    2145              :         //     AUTHOR         Jaewook Lee
    2146              :         //     DATE WRITTEN   November 2000
    2147              :         //     MODIFIED       Rick Strand (for E+ implementation November 2000)
    2148              :         //                    Rick Strand (for high temperature radiant heaters March 2001)
    2149              : 
    2150              :         // PURPOSE OF THIS FUNCTION:
    2151              :         // THIS IS A FUNCTION TO CALCULATE EITHER ZONE AVERAGED MRT OR
    2152              :         // SURFACE WEIGHTED MRT
    2153              : 
    2154              :         // METHODOLOGY EMPLOYED:
    2155              :         // The method here is fairly straight-forward.  If the user has selected
    2156              :         // a zone average MRT calculation, then there is nothing to do other than
    2157              :         // to assign the function value because the zone MRT has already been
    2158              :         // calculated.  Note that this value is an "area-emissivity" weighted value.
    2159              :         // If the user wants to place the occupant "near" a particular surface,
    2160              :         // then at the limit half of the radiant field will be from this surface.
    2161              :         // As a result, an average of the zone MRT and the surface temperature
    2162              :         // is taken to arrive at an approximate radiant temperature.
    2163              :         // If a high temperature radiant heater is present, then this must also be
    2164              :         // taken into account.  The equation used to account for this factor is
    2165              :         // based on equation 49 on page 150 of Fanger's text (see reference below).
    2166              :         // The additional assumptions for EnergyPlus are that the radiant energy
    2167              :         // from the heater must be spread over the average area of a human being
    2168              :         // (see parameter below) and that the emissivity and absorptivity of the
    2169              :         // occupant are equivalent for the dominant wavelength of radiant energy
    2170              :         // from the heater.  These assumptions might be off slightly, but it does
    2171              :         // allow for an approximation of the effects of surfaces and heaters
    2172              :         // within a space.  Future additions might include the effect of direct
    2173              :         // solar energy on occupants.
    2174              : 
    2175         1832 :         Real64 CalcRadTemp = 0.0;
    2176         1832 :         Real64 constexpr AreaEff = 1.8;                    // Effective area of a "standard" person in meters squared
    2177         1832 :         Real64 constexpr StefanBoltzmannConst = 5.6697e-8; // Stefan-Boltzmann constant in W/(m2*K4)
    2178              : 
    2179         1832 :         auto &thisPeople = state.dataHeatBal->People(PeopleListNum);
    2180         1832 :         switch (thisPeople.MRTCalcType) {
    2181         1832 :         case DataHeatBalance::CalcMRT::EnclosureAveraged: {
    2182         1832 :             int enclNum = state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum;
    2183         1832 :             state.dataThermalComforts->RadTemp = state.dataViewFactor->EnclRadInfo(enclNum).MRT;
    2184         1832 :         } break;
    2185            0 :         case DataHeatBalance::CalcMRT::SurfaceWeighted: {
    2186            0 :             state.dataThermalComforts->RadTemp = CalcSurfaceWeightedMRT(state, thisPeople.SurfacePtr);
    2187            0 :         } break;
    2188            0 :         case DataHeatBalance::CalcMRT::AngleFactor: {
    2189            0 :             state.dataThermalComforts->RadTemp = CalcAngleFactorMRT(state, thisPeople.AngleFactorListPtr);
    2190            0 :         } break;
    2191            0 :         default:
    2192            0 :             break;
    2193              :         }
    2194              : 
    2195              :         // If high temperature radiant heater present and on, then must account for this in MRT calculation
    2196              :         // MJW MRT ToDo: Think about what happens here - at a minimum, set a flag to skip this if there isn't any radiant HVAC
    2197         1832 :         state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) =
    2198         1832 :             state.dataHeatBalFanSys->ZoneQHTRadSysToPerson(state.dataThermalComforts->ZoneNum) +
    2199         1832 :             state.dataHeatBalFanSys->ZoneQCoolingPanelToPerson(state.dataThermalComforts->ZoneNum) +
    2200         1832 :             state.dataHeatBalFanSys->ZoneQHWBaseboardToPerson(state.dataThermalComforts->ZoneNum) +
    2201         1832 :             state.dataHeatBalFanSys->ZoneQSteamBaseboardToPerson(state.dataThermalComforts->ZoneNum) +
    2202         1832 :             state.dataHeatBalFanSys->ZoneQElecBaseboardToPerson(state.dataThermalComforts->ZoneNum);
    2203         1832 :         if (state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) > 0.0) {
    2204            0 :             state.dataThermalComforts->RadTemp += Constant::Kelvin; // Convert to Kelvin
    2205            0 :             state.dataThermalComforts->RadTemp =
    2206            0 :                 root_4(pow_4(state.dataThermalComforts->RadTemp) +
    2207            0 :                        (state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) / AreaEff / StefanBoltzmannConst));
    2208            0 :             state.dataThermalComforts->RadTemp -= Constant::Kelvin; // Convert back to Celsius
    2209              :         }
    2210              : 
    2211         1832 :         CalcRadTemp = state.dataThermalComforts->RadTemp;
    2212              : 
    2213         1832 :         return CalcRadTemp;
    2214              :     }
    2215              : 
    2216        18995 :     void CalcThermalComfortSimpleASH55(EnergyPlusData &state)
    2217              :     {
    2218              :         // SUBROUTINE INFORMATION:
    2219              :         //       AUTHOR         Jason Glazer
    2220              :         //       DATE WRITTEN   June 2005
    2221              : 
    2222              :         // PURPOSE OF THIS SUBROUTINE:
    2223              :         //   Determines if the space is within the ASHRAE 55-2004 comfort region
    2224              :         //   based on operative temperature and humidity ratio
    2225              : 
    2226              :         // Using/Aliasing
    2227              :         using OutputReportTabular::isInQuadrilateral;
    2228              :         using namespace OutputReportPredefined;
    2229              : 
    2230              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2231              :         Real64 OperTemp;
    2232              :         Real64 NumberOccupants;
    2233              :         bool isComfortableWithSummerClothes;
    2234              :         bool isComfortableWithWinterClothes;
    2235              :         int iPeople;
    2236              :         int iZone;
    2237              :         Real64 allowedHours;
    2238              :         bool showWarning;
    2239              : 
    2240        18995 :         state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer = 0.0;
    2241        18995 :         state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter = 0.0;
    2242        18995 :         state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either = 0.0;
    2243              : 
    2244              :         // assume the zone is unoccupied
    2245        45070 :         for (auto &e : state.dataThermalComforts->ThermalComfortInASH55)
    2246        26075 :             e.ZoneIsOccupied = false;
    2247              :         // loop through the people objects and determine if the zone is currently occupied
    2248        29627 :         for (auto const &people : state.dataHeatBal->People) {
    2249        10632 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    2250        10632 :             NumberOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    2251        10632 :             if (NumberOccupants > 0) {
    2252         7816 :                 state.dataThermalComforts->ThermalComfortInASH55(state.dataThermalComforts->ZoneNum).ZoneIsOccupied = true;
    2253              :             }
    2254              :         }
    2255              :         // loop through the zones and determine if in simple ashrae 55 comfort regions
    2256              :         // MJW MRT ToDo: Extend ASHRAE 55 to spaces?
    2257        45070 :         for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2258        26075 :             if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    2259         7624 :                 auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone);
    2260              :                 // keep track of occupied hours
    2261         7624 :                 state.dataThermalComforts->ZoneOccHrs(iZone) += state.dataGlobal->TimeStepZone;
    2262         7624 :                 Real64 CurAirTemp = thisZoneHB.ZTAVComf;
    2263         7624 :                 if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    2264            0 :                     if (state.dataRoomAir->IsZoneDispVent3Node(iZone) || state.dataRoomAir->IsZoneUFAD(iZone)) {
    2265            0 :                         CurAirTemp = state.dataRoomAir->TCMF(iZone);
    2266              :                     }
    2267              :                 }
    2268         7624 :                 Real64 CurMeanRadiantTemp = thisZoneHB.MRT;
    2269         7624 :                 OperTemp = CurAirTemp * 0.5 + CurMeanRadiantTemp * 0.5;
    2270              :                 // for debugging
    2271              :                 // ThermalComfortInASH55(iZone)%dCurAirTemp = CurAirTemp
    2272              :                 // ThermalComfortInASH55(iZone)%dCurMeanRadiantTemp = CurMeanRadiantTemp
    2273              :                 // ThermalComfortInASH55(iZone)%dOperTemp = OperTemp
    2274              :                 // ThermalComfortInASH55(iZone)%dHumidRatio = HumidRatio
    2275              :                 // From ASHRAE Standard 55-2004 Appendix D
    2276              :                 //  Run    AirTemp(C)   RH(%)  Season  HumidRatio
    2277              :                 //   1       19.6        86    Winter    0.012
    2278              :                 //   2       23.9        66    Winter    0.012
    2279              :                 //   3       25.7        15    Winter    0.003
    2280              :                 //   4       21.2        20    Winter    0.003
    2281              :                 //   5       23.6        67    Summer    0.012
    2282              :                 //   6       26.8        56    Summer    0.012
    2283              :                 //   7       27.9        13    Summer    0.003
    2284              :                 //   8       24.7        16    Summer    0.003
    2285              :                 // But the standard says "no recommended lower humidity limit" so it should
    2286              :                 // really extend down to the 0.0 Humidity ratio line.  Extrapolating we get
    2287              :                 // the values that are shown in the following table
    2288              :                 //  Run    AirTemp(C)    Season  HumidRatio
    2289              :                 //   1       19.6        Winter    0.012
    2290              :                 //   2       23.9        Winter    0.012
    2291              :                 //   3       26.3        Winter    0.000
    2292              :                 //   4       21.7        Winter    0.000
    2293              :                 //   5       23.6        Summer    0.012
    2294              :                 //   6       26.8        Summer    0.012
    2295              :                 //   7       28.3        Summer    0.000
    2296              :                 //   8       25.1        Summer    0.000
    2297              :                 // check summer clothing conditions
    2298              :                 isComfortableWithSummerClothes =
    2299         7624 :                     isInQuadrilateral(OperTemp, thisZoneHB.airHumRatAvgComf, 25.1, 0.0, 23.6, 0.012, 26.8, 0.012, 28.3, 0.0);
    2300              :                 // check winter clothing conditions
    2301              :                 isComfortableWithWinterClothes =
    2302         7624 :                     isInQuadrilateral(OperTemp, thisZoneHB.airHumRatAvgComf, 21.7, 0.0, 19.6, 0.012, 23.9, 0.012, 26.3, 0.0);
    2303         7624 :                 if (isComfortableWithSummerClothes) {
    2304         1342 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = 0.0;
    2305              :                 } else {
    2306         6282 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = state.dataGlobal->TimeStepZone;
    2307         6282 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer += state.dataGlobal->TimeStepZone;
    2308         6282 :                     state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer = state.dataGlobal->TimeStepZone;
    2309              :                 }
    2310         7624 :                 if (isComfortableWithWinterClothes) {
    2311         1427 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = 0.0;
    2312              :                 } else {
    2313         6197 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = state.dataGlobal->TimeStepZone;
    2314         6197 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter += state.dataGlobal->TimeStepZone;
    2315         6197 :                     state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter = state.dataGlobal->TimeStepZone;
    2316              :                 }
    2317         7624 :                 if (isComfortableWithSummerClothes || isComfortableWithWinterClothes) {
    2318         2654 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = 0.0;
    2319              :                 } else {
    2320         4970 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = state.dataGlobal->TimeStepZone;
    2321         4970 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither += state.dataGlobal->TimeStepZone;
    2322         4970 :                     state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either = state.dataGlobal->TimeStepZone;
    2323              :                 }
    2324              :             } else {
    2325              :                 // when no one present in that portion of the zone then no one can be uncomfortable
    2326        18451 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = 0.0;
    2327        18451 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = 0.0;
    2328        18451 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = 0.0;
    2329              :             }
    2330              :         }
    2331              :         // accumulate total time
    2332        18995 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer;
    2333        18995 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter;
    2334        18995 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either;
    2335              : 
    2336        18995 :         if (state.dataGlobal->EndDesignDayEnvrnsFlag) {
    2337           87 :             allowedHours = double(state.dataGlobal->NumOfDayInEnvrn) * 24.0 * 0.04;
    2338              :             // first check if warning should be printed
    2339           87 :             showWarning = false;
    2340          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2341          118 :                 if (state.dataThermalComforts->ThermalComfortInASH55(iZone).Enable55Warning) {
    2342            2 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither > allowedHours) {
    2343            2 :                         showWarning = true;
    2344              :                     }
    2345              :                 }
    2346              :             }
    2347              :             // if any zones should be warning print it out
    2348           87 :             if (showWarning) {
    2349            2 :                 ShowWarningError(state, format("More than 4% of time ({:.1R} hours) uncomfortable in one or more zones ", allowedHours));
    2350            4 :                 ShowContinueError(state, "Based on ASHRAE 55-2004 graph (Section 5.2.1.1)");
    2351            2 :                 if (state.dataEnvrn->RunPeriodEnvironment) {
    2352            0 :                     ShowContinueError(state,
    2353            0 :                                       format("During Environment [{}]: {}", state.dataEnvrn->EnvironmentStartEnd, state.dataEnvrn->EnvironmentName));
    2354              :                 } else {
    2355            4 :                     ShowContinueError(
    2356              :                         state,
    2357            4 :                         format("During SizingPeriod Environment [{}]: {}", state.dataEnvrn->EnvironmentStartEnd, state.dataEnvrn->EnvironmentName));
    2358              :                 }
    2359            6 :                 for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2360            4 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).Enable55Warning) {
    2361            2 :                         if (state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither > allowedHours) {
    2362            4 :                             ShowContinueError(state,
    2363            4 :                                               format("{:.1R} hours were uncomfortable in zone: {}",
    2364            2 :                                                      state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither,
    2365            2 :                                                      state.dataHeatBal->Zone(iZone).Name));
    2366              :                         }
    2367              :                     }
    2368              :                 }
    2369              :             }
    2370              :             // put in predefined reports
    2371          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2372          236 :                 PreDefTableEntry(state,
    2373          118 :                                  state.dataOutRptPredefined->pdchSCwinterClothes,
    2374          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2375          118 :                                  state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter);
    2376          236 :                 PreDefTableEntry(state,
    2377          118 :                                  state.dataOutRptPredefined->pdchSCsummerClothes,
    2378          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2379          118 :                                  state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer);
    2380          236 :                 PreDefTableEntry(state,
    2381          118 :                                  state.dataOutRptPredefined->pdchSCeitherClothes,
    2382          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2383          118 :                                  state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither);
    2384              :             }
    2385          261 :             PreDefTableEntry(
    2386          174 :                 state, state.dataOutRptPredefined->pdchSCwinterClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter);
    2387          261 :             PreDefTableEntry(
    2388          174 :                 state, state.dataOutRptPredefined->pdchSCsummerClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer);
    2389          261 :             PreDefTableEntry(
    2390          174 :                 state, state.dataOutRptPredefined->pdchSCeitherClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either);
    2391              :             // set value for ABUPS report
    2392           87 :             state.dataOutRptPredefined->TotalTimeNotSimpleASH55EitherForABUPS = state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either;
    2393              :             // reset accumulation for new environment
    2394          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2395          118 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter = 0.0;
    2396          118 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer = 0.0;
    2397          118 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither = 0.0;
    2398              :             }
    2399           87 :             state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter = 0.0;
    2400           87 :             state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer = 0.0;
    2401           87 :             state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either = 0.0;
    2402              :             // report how the aggregation is conducted
    2403           87 :             switch (state.dataGlobal->KindOfSim) {
    2404           87 :             case Constant::KindOfSim::DesignDay: {
    2405           87 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the Design Days");
    2406           87 :             } break;
    2407            0 :             case Constant::KindOfSim::RunPeriodDesign: {
    2408            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the RunPeriods for Design");
    2409            0 :             } break;
    2410            0 :             case Constant::KindOfSim::RunPeriodWeather: {
    2411            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the RunPeriods for Weather");
    2412            0 :             } break;
    2413            0 :             default:
    2414            0 :                 break;
    2415              :             }
    2416              :             // report number of occupied hours per week for LEED report
    2417          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2418          236 :                 PreDefTableEntry(state,
    2419          118 :                                  state.dataOutRptPredefined->pdchLeedSutHrsWeek,
    2420          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2421          118 :                                  7 * 24 * (state.dataThermalComforts->ZoneOccHrs(iZone) / (state.dataGlobal->NumOfDayInEnvrn * 24)));
    2422              :             }
    2423              :         }
    2424        18995 :     }
    2425              : 
    2426            0 :     void ResetThermalComfortSimpleASH55(EnergyPlusData &state)
    2427              :     {
    2428              :         // Jason Glazer - October 2015
    2429              :         // Reset thermal comfort table gathering arrays to zero for multi-year simulations
    2430              :         // so that only last year is reported in tabular reports
    2431              :         int iZone;
    2432            0 :         for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2433            0 :             state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter = 0.0;
    2434            0 :             state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer = 0.0;
    2435            0 :             state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither = 0.0;
    2436              :         }
    2437            0 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter = 0.0;
    2438            0 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer = 0.0;
    2439            0 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either = 0.0;
    2440            0 :     }
    2441              : 
    2442        19006 :     void CalcIfSetPointMet(EnergyPlusData &state)
    2443              :     {
    2444              :         // SUBROUTINE INFORMATION:
    2445              :         //       AUTHOR         Jason Glazer
    2446              :         //       DATE WRITTEN   July 2005
    2447              : 
    2448              :         // PURPOSE OF THIS SUBROUTINE:
    2449              :         //   Report if the setpoint temperature has been met.
    2450              :         //   Add calculation of how far away from setpoint and if setpoint was not met
    2451              :         //   during all times and during occupancy.
    2452              : 
    2453              :         // Using/Aliasing
    2454              :         using namespace OutputReportPredefined;
    2455        19006 :         Real64 const deviationFromSetPtThresholdClg = state.dataHVACGlobal->deviationFromSetPtThresholdClg;
    2456        19006 :         Real64 const deviationFromSetPtThresholdHtg = state.dataHVACGlobal->deviationFromSetPtThresholdHtg;
    2457              : 
    2458              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2459              :         Real64 SensibleLoadPredictedNoAdj;
    2460              :         Real64 deltaT;
    2461              :         int iZone;
    2462              :         bool testHeating;
    2463              :         bool testCooling;
    2464              : 
    2465              :         // Get the load predicted - the sign will indicate if heating or cooling
    2466              :         // was called for
    2467        19006 :         state.dataThermalComforts->AnyZoneNotMetHeating = 0.0;
    2468        19006 :         state.dataThermalComforts->AnyZoneNotMetCooling = 0.0;
    2469        19006 :         state.dataThermalComforts->AnyZoneNotMetOccupied = 0.0;
    2470        19006 :         state.dataThermalComforts->AnyZoneNotMetHeatingOccupied = 0.0;
    2471        19006 :         state.dataThermalComforts->AnyZoneNotMetCoolingOccupied = 0.0;
    2472        45092 :         for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2473        26086 :             auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(iZone);
    2474              : 
    2475        26086 :             SensibleLoadPredictedNoAdj = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(iZone).TotalOutputRequired;
    2476        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCooling = 0.0;
    2477        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeating = 0.0;
    2478        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCoolingOccupied = 0.0;
    2479        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeatingOccupied = 0.0;
    2480              : 
    2481        26086 :             testHeating = (state.dataHeatBalFanSys->TempControlType(iZone) != HVAC::SetptType::SingleCool);
    2482        26086 :             testCooling = (state.dataHeatBalFanSys->TempControlType(iZone) != HVAC::SetptType::SingleHeat);
    2483              : 
    2484        26086 :             if (testHeating && (SensibleLoadPredictedNoAdj > 0)) { // heating
    2485         6064 :                 if (state.dataRoomAir->AirModel(iZone).AirModel != RoomAir::RoomAirModel::Mixing) {
    2486            0 :                     deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - zoneTstatSetpt.setptLo;
    2487              :                 } else {
    2488         6064 :                     if (state.dataZoneTempPredictorCorrector->NumOnOffCtrZone > 0) {
    2489            1 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptLoAver;
    2490              :                     } else {
    2491         6063 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptLo;
    2492              :                     }
    2493              :                 }
    2494         6064 :                 if (deltaT < deviationFromSetPtThresholdHtg) {
    2495         1792 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeating = state.dataGlobal->TimeStepZone;
    2496         1792 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating += state.dataGlobal->TimeStepZone;
    2497         1792 :                     if (state.dataThermalComforts->AnyZoneNotMetHeating == 0.0)
    2498         1504 :                         state.dataThermalComforts->AnyZoneNotMetHeating = state.dataGlobal->TimeStepZone;
    2499         1792 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    2500         1060 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeatingOccupied = state.dataGlobal->TimeStepZone;
    2501         1060 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied += state.dataGlobal->TimeStepZone;
    2502         1060 :                         if (state.dataThermalComforts->AnyZoneNotMetHeatingOccupied == 0.0)
    2503          868 :                             state.dataThermalComforts->AnyZoneNotMetHeatingOccupied = state.dataGlobal->TimeStepZone;
    2504         1060 :                         if (state.dataThermalComforts->AnyZoneNotMetOccupied == 0.0)
    2505          868 :                             state.dataThermalComforts->AnyZoneNotMetOccupied = state.dataGlobal->TimeStepZone;
    2506              :                     }
    2507              :                 }
    2508        20022 :             } else if (testCooling && (SensibleLoadPredictedNoAdj < 0)) { // cooling
    2509         5684 :                 if (state.dataRoomAir->AirModel(iZone).AirModel != RoomAir::RoomAirModel::Mixing) {
    2510            0 :                     deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - zoneTstatSetpt.setptHi;
    2511              :                 } else {
    2512         5684 :                     if (state.dataZoneTempPredictorCorrector->NumOnOffCtrZone > 0) {
    2513            1 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptHiAver;
    2514              :                     } else {
    2515         5683 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptHi;
    2516              :                     }
    2517              :                 }
    2518              : 
    2519         5684 :                 if (state.dataHeatBal->Zone(iZone).HasAdjustedReturnTempByITE) {
    2520            0 :                     deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - state.dataHeatBal->Zone(iZone).AdjustedReturnTempByITE;
    2521              :                 }
    2522         5684 :                 if (deltaT > deviationFromSetPtThresholdClg) {
    2523         1418 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCooling = state.dataGlobal->TimeStepZone;
    2524         1418 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling += state.dataGlobal->TimeStepZone;
    2525         1418 :                     if (state.dataThermalComforts->AnyZoneNotMetCooling == 0.0)
    2526          781 :                         state.dataThermalComforts->AnyZoneNotMetCooling = state.dataGlobal->TimeStepZone;
    2527         1418 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    2528         1238 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCoolingOccupied = state.dataGlobal->TimeStepZone;
    2529         1238 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied += state.dataGlobal->TimeStepZone;
    2530         1238 :                         if (state.dataThermalComforts->AnyZoneNotMetCoolingOccupied == 0.0)
    2531          643 :                             state.dataThermalComforts->AnyZoneNotMetCoolingOccupied = state.dataGlobal->TimeStepZone;
    2532         1238 :                         if (state.dataThermalComforts->AnyZoneNotMetOccupied == 0.0)
    2533          643 :                             state.dataThermalComforts->AnyZoneNotMetOccupied = state.dataGlobal->TimeStepZone;
    2534              :                     }
    2535              :                 }
    2536              :             }
    2537              :         }
    2538        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetHeating += state.dataThermalComforts->AnyZoneNotMetHeating;
    2539        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetCooling += state.dataThermalComforts->AnyZoneNotMetCooling;
    2540        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied += state.dataThermalComforts->AnyZoneNotMetHeatingOccupied;
    2541        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied += state.dataThermalComforts->AnyZoneNotMetCoolingOccupied;
    2542        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetOccupied += state.dataThermalComforts->AnyZoneNotMetOccupied;
    2543              : 
    2544              :         // was EndEnvrnsFlag prior to CR7562
    2545        19006 :         if (state.dataGlobal->EndDesignDayEnvrnsFlag) {
    2546          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2547          236 :                 PreDefTableEntry(state,
    2548          118 :                                  state.dataOutRptPredefined->pdchULnotMetHeat,
    2549          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2550          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating);
    2551          236 :                 PreDefTableEntry(state,
    2552          118 :                                  state.dataOutRptPredefined->pdchULnotMetCool,
    2553          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2554          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling);
    2555          236 :                 PreDefTableEntry(state,
    2556          118 :                                  state.dataOutRptPredefined->pdchULnotMetHeatOcc,
    2557          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2558          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied);
    2559          236 :                 PreDefTableEntry(state,
    2560          118 :                                  state.dataOutRptPredefined->pdchULnotMetCoolOcc,
    2561          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2562          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied);
    2563              :             }
    2564           87 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchULnotMetHeat, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetHeating);
    2565           87 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchULnotMetCool, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetCooling);
    2566          261 :             PreDefTableEntry(
    2567          174 :                 state, state.dataOutRptPredefined->pdchULnotMetHeatOcc, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied);
    2568          261 :             PreDefTableEntry(
    2569          174 :                 state, state.dataOutRptPredefined->pdchULnotMetCoolOcc, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied);
    2570              :             // set value for ABUPS report
    2571           87 :             state.dataOutRptPredefined->TotalNotMetHeatingOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied;
    2572           87 :             state.dataOutRptPredefined->TotalNotMetCoolingOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied;
    2573           87 :             state.dataOutRptPredefined->TotalNotMetOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetOccupied;
    2574              :             // reset counters
    2575          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2576          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating = 0.0;
    2577          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling = 0.0;
    2578          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied = 0.0;
    2579          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied = 0.0;
    2580              :             }
    2581           87 :             state.dataThermalComforts->TotalAnyZoneNotMetHeating = 0.0;
    2582           87 :             state.dataThermalComforts->TotalAnyZoneNotMetCooling = 0.0;
    2583           87 :             state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied = 0.0;
    2584           87 :             state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied = 0.0;
    2585           87 :             state.dataThermalComforts->TotalAnyZoneNotMetOccupied = 0.0;
    2586              :             // report how the aggregation is conducted
    2587           87 :             switch (state.dataGlobal->KindOfSim) {
    2588           87 :             case Constant::KindOfSim::DesignDay: {
    2589           87 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the Design Days");
    2590           87 :             } break;
    2591            0 :             case Constant::KindOfSim::RunPeriodDesign: {
    2592            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the RunPeriods for Design");
    2593            0 :             } break;
    2594            0 :             case Constant::KindOfSim::RunPeriodWeather: {
    2595            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the RunPeriods for Weather");
    2596            0 :             } break;
    2597            0 :             default:
    2598            0 :                 break;
    2599              :             }
    2600              :         }
    2601        19006 :     }
    2602              : 
    2603            0 :     void ResetSetPointMet(EnergyPlusData &state)
    2604              :     {
    2605              :         // Jason Glazer - October 2015
    2606              :         // Reset set point not met table gathering arrays to zero for multi-year simulations
    2607              :         // so that only last year is reported in tabular reports
    2608              :         int iZone;
    2609            0 :         for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2610            0 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating = 0.0;
    2611            0 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling = 0.0;
    2612            0 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied = 0.0;
    2613            0 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied = 0.0;
    2614              :         }
    2615            0 :         state.dataThermalComforts->TotalAnyZoneNotMetHeating = 0.0;
    2616            0 :         state.dataThermalComforts->TotalAnyZoneNotMetCooling = 0.0;
    2617            0 :         state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied = 0.0;
    2618            0 :         state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied = 0.0;
    2619            0 :         state.dataThermalComforts->TotalAnyZoneNotMetOccupied = 0.0;
    2620            0 :     }
    2621              : 
    2622            1 :     void CalcThermalComfortAdaptiveASH55(
    2623              :         EnergyPlusData &state,
    2624              :         bool const initiate,                         // true if supposed to initiate
    2625              :         ObjexxFCL::Optional_bool_const wthrsim,      // true if this is a weather simulation
    2626              :         ObjexxFCL::Optional<Real64 const> avgdrybulb // approximate avg drybulb for design day.  will be used as previous period in design day
    2627              :     )
    2628              :     {
    2629              : 
    2630              :         // SUBROUTINE INFORMATION:
    2631              :         //       AUTHOR         Tyler Hoyt
    2632              :         //       DATE WRITTEN   July 2011
    2633              : 
    2634              :         // PURPOSE OF THIS SUBROUTINE:
    2635              :         // Sets up and carries out ASHRAE55-2010 adaptive comfort model calculations.
    2636              :         // Output provided are state variables for the 80% and 90% acceptability limits
    2637              :         // in the model, the comfort temperature, and the 30-day running average or
    2638              :         // monthly average outdoor air temperature as parsed from the .STAT file.
    2639              : 
    2640              :         // METHODOLOGY EMPLOYED:
    2641              :         // In order for the calculations to be possible the user must provide either
    2642              :         // a .STAT file or .EPW file for the purpose of computing a monthly average
    2643              :         // temperature or thirty-day running average. The subroutine need only open
    2644              :         // the relevant file once to initialize, and then operates within the loop.
    2645              : 
    2646              :         // Using/Aliasing
    2647            1 :         Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
    2648              :         using OutputReportTabular::GetColumnUsingTabs;
    2649              :         using OutputReportTabular::StrToReal;
    2650              : 
    2651              :         // SUBROUTINE PARAMETER DEFINITIONS:
    2652              : 
    2653              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2654            1 :         std::string lineAvg;
    2655            1 :         std::string epwLine;
    2656              :         Real64 dryBulb;
    2657              :         Real64 tComf;
    2658              :         Real64 numOccupants;
    2659              :         int readStat;
    2660              :         int jStartDay;
    2661              :         int calcStartDay;
    2662              :         int calcStartHr;
    2663              :         int calcEndDay;
    2664              :         int calcEndHr;
    2665              :         std::string::size_type pos;
    2666              :         int ind;
    2667              :         int i;
    2668              :         int j;
    2669              :         bool weathersimulation;
    2670              :         Real64 inavgdrybulb;
    2671              : 
    2672            1 :         if (initiate) { // not optional on initiate=true.  would otherwise check for presence
    2673            0 :             weathersimulation = wthrsim;
    2674            0 :             state.dataThermalComforts->avgDryBulbASH = 0.0;
    2675            0 :             state.dataThermalComforts->runningAverageASH = 0.0;
    2676            0 :             state.dataThermalComforts->monthlyTemp = 0.0;
    2677            0 :             inavgdrybulb = avgdrybulb;
    2678              :         } else {
    2679            1 :             weathersimulation = false;
    2680            1 :             inavgdrybulb = 0.0;
    2681              :         }
    2682              : 
    2683            1 :         if (initiate && weathersimulation) {
    2684            0 :             const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
    2685            0 :             const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
    2686              : 
    2687            0 :             readStat = 0;
    2688            0 :             if (statFileExists) {
    2689            0 :                 auto statFile = state.files.inStatFilePath.open(state, "CalcThermalComfortAdapctiveASH55");
    2690            0 :                 while (statFile.good()) {
    2691            0 :                     auto lineIn = statFile.readLine();
    2692            0 :                     if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
    2693            0 :                         for (i = 1; i <= 7; ++i) {
    2694            0 :                             lineIn = statFile.readLine();
    2695              :                         }
    2696            0 :                         lineIn = statFile.readLine();
    2697            0 :                         lineAvg = lineIn.data;
    2698            0 :                         break;
    2699              :                     }
    2700            0 :                 }
    2701            0 :                 for (i = 1; i <= 12; ++i) {
    2702            0 :                     state.dataThermalComforts->monthlyTemp(i) = StrToReal(GetColumnUsingTabs(lineAvg, i + 2));
    2703              :                 }
    2704            0 :                 state.dataThermalComforts->useStatData = true;
    2705            0 :             } else if (epwFileExists) {
    2706              :                 // determine number of days in year
    2707              :                 int DaysInYear;
    2708            0 :                 if (state.dataEnvrn->CurrentYearIsLeapYear) {
    2709            0 :                     DaysInYear = 366;
    2710              :                 } else {
    2711            0 :                     DaysInYear = 365;
    2712              :                 }
    2713            0 :                 state.dataThermalComforts->DailyAveOutTemp = 0.0;
    2714              : 
    2715            0 :                 auto epwFile = state.files.inputWeatherFilePath.open(state, "CalcThermalComfortAdaptiveASH55");
    2716            0 :                 for (i = 1; i <= 8; ++i) { // Headers
    2717            0 :                     epwLine = epwFile.readLine().data;
    2718              :                 }
    2719            0 :                 jStartDay = state.dataEnvrn->DayOfYear - 1;
    2720            0 :                 calcStartDay = jStartDay - 30;
    2721            0 :                 if (calcStartDay >= 0) {
    2722            0 :                     calcStartHr = 24 * calcStartDay + 1;
    2723            0 :                     for (i = 1; i <= calcStartHr - 1; ++i) {
    2724            0 :                         epwFile.readLine();
    2725              :                     }
    2726            0 :                     for (i = 1; i <= 30; ++i) {
    2727            0 :                         state.dataThermalComforts->avgDryBulbASH = 0.0;
    2728            0 :                         for (j = 1; j <= 24; ++j) {
    2729            0 :                             epwLine = epwFile.readLine().data;
    2730            0 :                             for (ind = 1; ind <= 6; ++ind) {
    2731            0 :                                 pos = index(epwLine, ',');
    2732            0 :                                 epwLine.erase(0, pos + 1);
    2733              :                             }
    2734            0 :                             pos = index(epwLine, ',');
    2735            0 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    2736            0 :                             state.dataThermalComforts->avgDryBulbASH += (dryBulb / 24.0);
    2737              :                         }
    2738            0 :                         state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->avgDryBulbASH;
    2739              :                     }
    2740              :                 } else { // Do special things for wrapping the epw
    2741            0 :                     calcEndDay = jStartDay;
    2742            0 :                     calcStartDay += DaysInYear;
    2743            0 :                     calcEndHr = 24 * calcEndDay;
    2744            0 :                     calcStartHr = 24 * calcStartDay + 1;
    2745            0 :                     for (i = 1; i <= calcEndDay; ++i) {
    2746            0 :                         state.dataThermalComforts->avgDryBulbASH = 0.0;
    2747            0 :                         for (j = 1; j <= 24; ++j) {
    2748            0 :                             epwLine = epwFile.readLine().data;
    2749            0 :                             for (ind = 1; ind <= 6; ++ind) {
    2750            0 :                                 pos = index(epwLine, ',');
    2751            0 :                                 epwLine.erase(0, pos + 1);
    2752              :                             }
    2753            0 :                             pos = index(epwLine, ',');
    2754            0 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    2755            0 :                             state.dataThermalComforts->avgDryBulbASH += (dryBulb / 24.0);
    2756              :                         }
    2757            0 :                         state.dataThermalComforts->DailyAveOutTemp(i + 30 - calcEndDay) = state.dataThermalComforts->avgDryBulbASH;
    2758              :                     }
    2759            0 :                     for (i = calcEndHr + 1; i <= calcStartHr - 1; ++i) {
    2760            0 :                         epwLine = epwFile.readLine().data;
    2761              :                     }
    2762            0 :                     for (i = 1; i <= 30 - calcEndDay; ++i) {
    2763            0 :                         state.dataThermalComforts->avgDryBulbASH = 0.0;
    2764            0 :                         for (j = 1; j <= 24; ++j) {
    2765            0 :                             epwLine = epwFile.readLine().data;
    2766            0 :                             for (ind = 1; ind <= 6; ++ind) {
    2767            0 :                                 pos = index(epwLine, ',');
    2768            0 :                                 epwLine.erase(0, pos + 1);
    2769              :                             }
    2770            0 :                             pos = index(epwLine, ',');
    2771            0 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    2772            0 :                             state.dataThermalComforts->avgDryBulbASH += (dryBulb / 24.0);
    2773              :                         }
    2774            0 :                         state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->avgDryBulbASH;
    2775              :                     }
    2776              :                 }
    2777            0 :                 state.dataThermalComforts->useEpwData = true;
    2778            0 :             }
    2779            1 :         } else if (initiate && !weathersimulation) {
    2780            0 :             state.dataThermalComforts->runningAverageASH = inavgdrybulb;
    2781            0 :             state.dataThermalComforts->monthlyTemp = inavgdrybulb;
    2782            0 :             state.dataThermalComforts->avgDryBulbASH = 0.0;
    2783              :         }
    2784              : 
    2785            1 :         if (initiate) return;
    2786              : 
    2787            1 :         if (state.dataGlobal->BeginDayFlag && state.dataThermalComforts->useEpwData) {
    2788              :             // Update the running average, reset the daily avg
    2789            1 :             state.dataThermalComforts->DailyAveOutTemp(30) = state.dataThermalComforts->avgDryBulbASH;
    2790            1 :             Real64 sum = 0.0;
    2791           30 :             for (i = 1; i <= 29; i++) {
    2792           29 :                 sum += state.dataThermalComforts->DailyAveOutTemp(i);
    2793              :             }
    2794            1 :             state.dataThermalComforts->runningAverageASH = (sum + state.dataThermalComforts->avgDryBulbASH) / 30.0;
    2795           30 :             for (i = 1; i <= 29; i++) {
    2796           29 :                 state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->DailyAveOutTemp(i + 1);
    2797              :             }
    2798            1 :             state.dataThermalComforts->avgDryBulbASH = 0.0;
    2799              :         }
    2800              : 
    2801              :         // If exists BeginMonthFlag we can use it to call InvJulianDay once per month.
    2802            1 :         if (state.dataGlobal->BeginDayFlag && state.dataThermalComforts->useStatData) {
    2803              :             //  CALL InvJulianDay(DayOfYear,pMonth,pDay,0)
    2804              :             //  runningAverageASH = monthlyTemp(pMonth)
    2805            0 :             state.dataThermalComforts->runningAverageASH = state.dataThermalComforts->monthlyTemp(state.dataEnvrn->Month);
    2806              :         }
    2807              : 
    2808              :         // Update the daily average
    2809              :         // IF (BeginHourFlag .and. useEpwData) THEN
    2810            1 :         if (state.dataGlobal->BeginHourFlag) {
    2811            0 :             state.dataThermalComforts->avgDryBulbASH += (state.dataEnvrn->OutDryBulbTemp / 24.0);
    2812              :         }
    2813              : 
    2814            1 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    2815            0 :              ++state.dataThermalComforts->PeopleNum) {
    2816              : 
    2817            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    2818            0 :             if (!people.AdaptiveASH55) continue;
    2819              : 
    2820            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    2821              : 
    2822            0 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    2823            0 :             state.dataThermalComforts->AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).ZTAVComf;
    2824            0 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    2825            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
    2826            0 :                     state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
    2827            0 :                     state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum);
    2828              :                 }
    2829              :             }
    2830            0 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
    2831            0 :             state.dataThermalComforts->OpTemp = (state.dataThermalComforts->AirTemp + state.dataThermalComforts->RadTemp) / 2.0;
    2832            0 :             comfort.ThermalComfortOpTemp = state.dataThermalComforts->OpTemp;
    2833            0 :             comfort.ASHRAE55RunningMeanOutdoorTemp = state.dataThermalComforts->runningAverageASH;
    2834            0 :             if (state.dataThermalComforts->runningAverageASH >= 10.0 && state.dataThermalComforts->runningAverageASH <= 33.5) {
    2835              :                 // Calculate the comfort here  (people/output handling loop)
    2836            0 :                 numOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    2837            0 :                 tComf = 0.31 * state.dataThermalComforts->runningAverageASH + 17.8;
    2838            0 :                 comfort.TComfASH55 = tComf;
    2839            0 :                 if (numOccupants > 0) {
    2840            0 :                     if (state.dataThermalComforts->OpTemp < tComf + 2.5 && state.dataThermalComforts->OpTemp > tComf - 2.5) {
    2841              :                         // 80% and 90% limits okay
    2842            0 :                         comfort.ThermalComfortAdaptiveASH5590 = 1;
    2843            0 :                         comfort.ThermalComfortAdaptiveASH5580 = 1;
    2844            0 :                     } else if (state.dataThermalComforts->OpTemp < tComf + 3.5 && state.dataThermalComforts->OpTemp > tComf - 3.5) {
    2845              :                         // 80% only
    2846            0 :                         comfort.ThermalComfortAdaptiveASH5590 = 0;
    2847            0 :                         comfort.ThermalComfortAdaptiveASH5580 = 1;
    2848            0 :                         people.TimeNotMetASH5590 += SysTimeElapsed;
    2849              :                     } else {
    2850              :                         // Neither
    2851            0 :                         comfort.ThermalComfortAdaptiveASH5590 = 0;
    2852            0 :                         comfort.ThermalComfortAdaptiveASH5580 = 0;
    2853            0 :                         people.TimeNotMetASH5580 += SysTimeElapsed;
    2854            0 :                         people.TimeNotMetASH5590 += SysTimeElapsed;
    2855              :                     }
    2856              :                 } else {
    2857              :                     // Unoccupied
    2858            0 :                     comfort.ThermalComfortAdaptiveASH5590 = -1;
    2859            0 :                     comfort.ThermalComfortAdaptiveASH5580 = -1;
    2860              :                 }
    2861              :             } else {
    2862              :                 // Monthly temp out of range
    2863            0 :                 comfort.ThermalComfortAdaptiveASH5590 = -1;
    2864            0 :                 comfort.ThermalComfortAdaptiveASH5580 = -1;
    2865            0 :                 comfort.TComfASH55 = -1.0;
    2866              :             }
    2867              :         }
    2868            1 :     }
    2869              : 
    2870           26 :     void CalcThermalComfortAdaptiveCEN15251(
    2871              :         EnergyPlusData &state,
    2872              :         bool const initiate,                         // true if supposed to initiate
    2873              :         ObjexxFCL::Optional_bool_const wthrsim,      // true if this is a weather simulation
    2874              :         ObjexxFCL::Optional<Real64 const> avgdrybulb // approximate avg drybulb for design day.  will be used as previous period in design day
    2875              :     )
    2876              :     {
    2877              : 
    2878              :         // SUBROUTINE INFORMATION:
    2879              :         //       AUTHOR         Tyler Hoyt
    2880              :         //       DATE WRITTEN   July 2011
    2881              : 
    2882              :         // PURPOSE OF THIS SUBROUTINE:
    2883              :         // Sets up and carries out CEN-15251 adaptive comfort model calculations.
    2884              :         // Output provided are state variables for the Category I, II, and III
    2885              :         // limits of the model, the comfort temperature, and the 5-day weighted
    2886              :         // moving average of the outdoor air temperature.
    2887              : 
    2888              :         // Using/Aliasing
    2889           26 :         Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
    2890              :         using OutputReportTabular::GetColumnUsingTabs;
    2891              :         using OutputReportTabular::StrToReal;
    2892              : 
    2893              :         // SUBROUTINE PARAMETER DEFINITIONS:
    2894              :         static Real64 constexpr alpha(0.8);
    2895              :         static constexpr std::array<Real64, 7> alpha_pow = {0.262144, 0.32768, 0.4096, 0.512, 0.64, 0.8, 1.0}; // alpha^(6-0)
    2896              : 
    2897              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2898           26 :         std::string epwLine;
    2899              :         Real64 dryBulb;
    2900              :         Real64 tComf;
    2901              :         Real64 tComfLow;
    2902              :         Real64 numOccupants;
    2903              :         int readStat;
    2904              :         int jStartDay;
    2905              :         int calcStartDay;
    2906              :         int calcStartHr;
    2907              :         int calcEndDay;
    2908              :         int calcEndHr;
    2909              :         std::string::size_type pos;
    2910              :         int ind;
    2911              :         int i;
    2912              :         int j;
    2913              :         bool weathersimulation;
    2914              :         Real64 inavgdrybulb;
    2915           26 :         int constexpr numHeaderRowsInEpw = 8;
    2916              : 
    2917           26 :         if (initiate) { // not optional on initiate=true.  would otherwise check for presence
    2918            1 :             weathersimulation = wthrsim;
    2919            1 :             inavgdrybulb = avgdrybulb;
    2920            1 :             state.dataThermalComforts->avgDryBulbCEN = 0.0;
    2921            1 :             state.dataThermalComforts->runningAverageCEN = 0.0;
    2922              :         } else {
    2923           25 :             weathersimulation = false;
    2924           25 :             inavgdrybulb = 0.0;
    2925              :         }
    2926              : 
    2927           26 :         if (initiate && weathersimulation) {
    2928            1 :             const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
    2929            1 :             readStat = 0;
    2930            1 :             if (epwFileExists) {
    2931              :                 // determine number of days in year
    2932              :                 int DaysInYear;
    2933            1 :                 if (state.dataEnvrn->CurrentYearIsLeapYear) {
    2934            0 :                     DaysInYear = 366;
    2935              :                 } else {
    2936            1 :                     DaysInYear = 365;
    2937              :                 }
    2938              : 
    2939            2 :                 auto epwFile = state.files.inputWeatherFilePath.open(state, "CalcThermalComfortAdaptiveCEN15251");
    2940            9 :                 for (i = 1; i <= numHeaderRowsInEpw; ++i) {
    2941            8 :                     epwFile.readLine();
    2942              :                 }
    2943            1 :                 jStartDay = state.dataEnvrn->DayOfYear - 1;
    2944            1 :                 calcStartDay = jStartDay - 7;
    2945            1 :                 if (calcStartDay > 0) {
    2946            0 :                     calcStartHr = 24 * calcStartDay + 1;
    2947            0 :                     for (i = 1; i <= calcStartHr - 1; ++i) {
    2948            0 :                         epwFile.readLine();
    2949              :                     }
    2950            0 :                     state.dataThermalComforts->runningAverageCEN = 0.0;
    2951            0 :                     for (i = 0; i < 7; ++i) {
    2952            0 :                         state.dataThermalComforts->avgDryBulbCEN = 0.0;
    2953            0 :                         for (j = 1; j <= 24; ++j) {
    2954            0 :                             epwLine = epwFile.readLine().data;
    2955            0 :                             for (ind = 1; ind <= 6; ++ind) {
    2956            0 :                                 pos = index(epwLine, ',');
    2957            0 :                                 epwLine.erase(0, pos + 1);
    2958              :                             }
    2959            0 :                             pos = index(epwLine, ',');
    2960            0 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    2961            0 :                             state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
    2962              :                         }
    2963            0 :                         state.dataThermalComforts->runningAverageCEN += alpha_pow[i] * state.dataThermalComforts->avgDryBulbCEN;
    2964              :                     }
    2965              :                 } else { // Do special things for wrapping the epw
    2966            1 :                     calcEndDay = jStartDay;
    2967            1 :                     calcStartDay += DaysInYear;
    2968            1 :                     calcEndHr = 24 * calcEndDay;
    2969            1 :                     calcStartHr = 24 * calcStartDay + 1;
    2970            1 :                     for (i = 1; i <= calcEndDay; ++i) {
    2971            0 :                         state.dataThermalComforts->avgDryBulbCEN = 0.0;
    2972            0 :                         for (j = 1; j <= 24; ++j) {
    2973            0 :                             epwLine = epwFile.readLine().data;
    2974            0 :                             for (ind = 1; ind <= 6; ++ind) {
    2975            0 :                                 pos = index(epwLine, ',');
    2976            0 :                                 epwLine.erase(0, pos + 1);
    2977              :                             }
    2978            0 :                             pos = index(epwLine, ',');
    2979            0 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    2980            0 :                             state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
    2981              :                         }
    2982            0 :                         state.dataThermalComforts->runningAverageCEN += std::pow(alpha, calcEndDay - i) * state.dataThermalComforts->avgDryBulbCEN;
    2983              :                     }
    2984         8593 :                     for (i = calcEndHr + 1; i <= calcStartHr - 1; ++i) {
    2985         8592 :                         epwFile.readLine();
    2986              :                     }
    2987            8 :                     for (i = 0; i < 7 - calcEndDay; ++i) {
    2988            7 :                         state.dataThermalComforts->avgDryBulbCEN = 0.0;
    2989          175 :                         for (j = 1; j <= 24; ++j) {
    2990          168 :                             epwLine = epwFile.readLine().data;
    2991         1176 :                             for (ind = 1; ind <= 6; ++ind) {
    2992         1008 :                                 pos = index(epwLine, ',');
    2993         1008 :                                 epwLine.erase(0, pos + 1);
    2994              :                             }
    2995          168 :                             pos = index(epwLine, ',');
    2996          168 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    2997          168 :                             state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
    2998              :                         }
    2999            7 :                         state.dataThermalComforts->runningAverageCEN += alpha_pow[i] * state.dataThermalComforts->avgDryBulbCEN;
    3000              :                     }
    3001              :                 }
    3002            1 :                 state.dataThermalComforts->runningAverageCEN *= (1.0 - alpha);
    3003            1 :                 state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3004            1 :                 state.dataThermalComforts->useEpwDataCEN = true;
    3005            1 :                 state.dataThermalComforts->firstDaySet = true;
    3006            1 :             }
    3007           26 :         } else if (initiate && !weathersimulation) {
    3008            0 :             state.dataThermalComforts->runningAverageCEN = inavgdrybulb;
    3009            0 :             state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3010              :         }
    3011           26 :         if (initiate) return;
    3012              : 
    3013           25 :         if (state.dataGlobal->BeginDayFlag && !state.dataThermalComforts->firstDaySet) {
    3014              :             // Update the running average, reset the daily avg
    3015            2 :             state.dataThermalComforts->runningAverageCEN =
    3016            1 :                 alpha * state.dataThermalComforts->runningAverageCEN + (1.0 - alpha) * state.dataThermalComforts->avgDryBulbCEN;
    3017            1 :             state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3018              :         }
    3019              : 
    3020           25 :         state.dataThermalComforts->firstDaySet = false;
    3021              : 
    3022              :         // Update the daily average
    3023           25 :         if (state.dataGlobal->BeginHourFlag) {
    3024           25 :             state.dataThermalComforts->avgDryBulbCEN += (state.dataEnvrn->OutDryBulbTemp / 24.0);
    3025              :         }
    3026              : 
    3027           25 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    3028            0 :              ++state.dataThermalComforts->PeopleNum) {
    3029            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    3030            0 :             if (!people.AdaptiveCEN15251) continue;
    3031              : 
    3032            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    3033            0 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    3034            0 :             state.dataThermalComforts->AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).ZTAVComf;
    3035            0 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    3036            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
    3037            0 :                     state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
    3038            0 :                     state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum);
    3039              :                 }
    3040              :             }
    3041            0 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
    3042            0 :             state.dataThermalComforts->OpTemp = (state.dataThermalComforts->AirTemp + state.dataThermalComforts->RadTemp) / 2.0;
    3043            0 :             comfort.ThermalComfortOpTemp = state.dataThermalComforts->OpTemp;
    3044            0 :             comfort.CEN15251RunningMeanOutdoorTemp = state.dataThermalComforts->runningAverageCEN;
    3045            0 :             if (state.dataThermalComforts->runningAverageCEN >= 10.0 && state.dataThermalComforts->runningAverageCEN <= 30.0) {
    3046              :                 // Calculate the comfort here (people/output handling loop)
    3047            0 :                 numOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    3048            0 :                 tComf = 0.33 * state.dataThermalComforts->runningAverageCEN + 18.8;
    3049            0 :                 comfort.TComfCEN15251 = tComf;
    3050            0 :                 if (numOccupants > 0) {
    3051            0 :                     if (state.dataThermalComforts->runningAverageCEN < 15) {
    3052            0 :                         tComfLow = 23.75; // Lower limit is constant in this region
    3053              :                     } else {
    3054            0 :                         tComfLow = tComf;
    3055              :                     }
    3056            0 :                     if (state.dataThermalComforts->OpTemp < tComf + 2.0 && state.dataThermalComforts->OpTemp > tComfLow - 2.0) {
    3057              :                         // Within Cat I, II, III Limits
    3058            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 1;
    3059            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 1;
    3060            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 1;
    3061            0 :                     } else if (state.dataThermalComforts->OpTemp < tComf + 3.0 && state.dataThermalComforts->OpTemp > tComfLow - 3.0) {
    3062              :                         // Within Cat II, III Limits
    3063            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 0;
    3064            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 1;
    3065            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 1;
    3066            0 :                         people.TimeNotMetCEN15251CatI += SysTimeElapsed;
    3067            0 :                     } else if (state.dataThermalComforts->OpTemp < tComf + 4.0 && state.dataThermalComforts->OpTemp > tComfLow - 4.0) {
    3068              :                         // Within Cat III Limits
    3069            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 0;
    3070            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 0;
    3071            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 1;
    3072            0 :                         people.TimeNotMetCEN15251CatI += SysTimeElapsed;
    3073            0 :                         people.TimeNotMetCEN15251CatII += SysTimeElapsed;
    3074              :                     } else {
    3075              :                         // None
    3076            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 0;
    3077            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 0;
    3078            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 0;
    3079            0 :                         people.TimeNotMetCEN15251CatI += SysTimeElapsed;
    3080            0 :                         people.TimeNotMetCEN15251CatII += SysTimeElapsed;
    3081            0 :                         people.TimeNotMetCEN15251CatIII += SysTimeElapsed;
    3082              :                     }
    3083              :                 } else {
    3084              :                     // Unoccupied
    3085            0 :                     comfort.ThermalComfortAdaptiveCEN15251CatI = -1;
    3086            0 :                     comfort.ThermalComfortAdaptiveCEN15251CatII = -1;
    3087            0 :                     comfort.ThermalComfortAdaptiveCEN15251CatIII = -1;
    3088              :                 }
    3089              :             } else {
    3090              :                 // Monthly temp out of range
    3091            0 :                 comfort.ThermalComfortAdaptiveCEN15251CatI = -1;
    3092            0 :                 comfort.ThermalComfortAdaptiveCEN15251CatII = -1;
    3093            0 :                 comfort.ThermalComfortAdaptiveCEN15251CatIII = -1;
    3094            0 :                 comfort.TComfCEN15251 = -1.0;
    3095              :             }
    3096              :         }
    3097           26 :     }
    3098              : 
    3099            0 :     void DynamicClothingModel(EnergyPlusData &state)
    3100              :     {
    3101              :         // SUBROUTINE INFORMATION:
    3102              :         //       AUTHOR         Kwang Ho Lee
    3103              :         //       DATE WRITTEN   June 2013
    3104              : 
    3105              :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3106              :         Real64 TemporaryVariable; // LOL
    3107              : 
    3108            0 :         auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    3109              : 
    3110            0 :         if (state.dataThermalComforts->TemporarySixAMTemperature < -5.0) { // A Temporary state variable?
    3111            0 :             comfort.ClothingValue = 1.0;
    3112            0 :         } else if ((state.dataThermalComforts->TemporarySixAMTemperature >= -5.0) && (state.dataThermalComforts->TemporarySixAMTemperature < 5.0)) {
    3113            0 :             comfort.ClothingValue = 0.818 - 0.0364 * state.dataThermalComforts->TemporarySixAMTemperature;
    3114            0 :         } else if ((state.dataThermalComforts->TemporarySixAMTemperature >= 5.0) && (state.dataThermalComforts->TemporarySixAMTemperature < 26.0)) {
    3115            0 :             TemporaryVariable = -0.1635 - 0.0066 * state.dataThermalComforts->TemporarySixAMTemperature;
    3116            0 :             comfort.ClothingValue = std::pow(10.0, TemporaryVariable);
    3117            0 :         } else if (state.dataThermalComforts->TemporarySixAMTemperature >= 26.0) {
    3118            0 :             comfort.ClothingValue = 0.46;
    3119              :         }
    3120            0 :     }
    3121              : 
    3122              : } // namespace ThermalComfort
    3123              : 
    3124              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1