LCOV - code coverage report
Current view: top level - EnergyPlus - ThermalComfort.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1391 1684 82.6 %
Date: 2024-08-23 23:50:59 Functions: 28 30 93.3 %

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

Generated by: LCOV version 1.14