LCOV - code coverage report
Current view: top level - EnergyPlus - ThermalComfort.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 82.5 % 1676 1382
Test Date: 2025-06-02 07:23:51 Functions: 93.3 % 30 28

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

Generated by: LCOV version 2.0-1