LCOV - code coverage report
Current view: top level - EnergyPlus - ThermalComfort.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 51.4 % 1676 861
Test Date: 2025-06-02 12:03:30 Functions: 73.3 % 30 22

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : #include <algorithm>
      50              : #include <boost/math/tools/roots.hpp>
      51              : #include <cassert>
      52              : #include <cmath>
      53              : 
      54              : // ObjexxFCL Headers
      55              : #include <ObjexxFCL/string.functions.hh>
      56              : 
      57              : // EnergyPlus Headers
      58              : #include <EnergyPlus/Construction.hh>
      59              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      60              : #include <EnergyPlus/DataEnvironment.hh>
      61              : #include <EnergyPlus/DataHeatBalFanSys.hh>
      62              : #include <EnergyPlus/DataHeatBalSurface.hh>
      63              : #include <EnergyPlus/DataHeatBalance.hh>
      64              : #include <EnergyPlus/DataIPShortCuts.hh>
      65              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      66              : #include <EnergyPlus/DataRoomAirModel.hh>
      67              : #include <EnergyPlus/DataViewFactorInformation.hh>
      68              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      69              : #include <EnergyPlus/FileSystem.hh>
      70              : #include <EnergyPlus/General.hh>
      71              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      72              : #include <EnergyPlus/OutputProcessor.hh>
      73              : #include <EnergyPlus/OutputReportPredefined.hh>
      74              : #include <EnergyPlus/OutputReportTabular.hh>
      75              : #include <EnergyPlus/Psychrometrics.hh>
      76              : #include <EnergyPlus/ScheduleManager.hh>
      77              : #include <EnergyPlus/ThermalComfort.hh>
      78              : #include <EnergyPlus/UtilityRoutines.hh>
      79              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      80              : 
      81              : namespace EnergyPlus {
      82              : 
      83              : namespace ThermalComfort {
      84              : 
      85              :     // Module containing the routines dealing with the CalcThermalComfortFanger,
      86              :     // CalcThermalComfortPierce, and CalcThermalComfortKSU
      87              : 
      88              :     // MODULE INFORMATION:
      89              :     //       AUTHOR         Jaewook Lee
      90              :     //       DATE WRITTEN   January 2000
      91              :     //       MODIFIED       Rick Strand (for E+ implementation February 2000)
      92              : 
      93              :     // PURPOSE OF THIS MODULE:
      94              :     // To calculate thermal comfort indices based on the
      95              :     // three thermal comfort prediction models (Fanger, Pierce, KSU)
      96              : 
      97              :     // METHODOLOGY EMPLOYED:
      98              :     // For each thermal comfort model type, the subroutines will loop through
      99              :     // the people statements and perform the requested thermal comfort evaluations
     100              : 
     101              :     // Using/Aliasing
     102              :     using DataHeatBalance::PeopleData;
     103              :     using Psychrometrics::PsyRhFnTdbWPb;
     104              : 
     105       249945 :     void ManageThermalComfort(EnergyPlusData &state, bool const InitializeOnly) // when called from ZTPC and calculations aren't needed
     106              :     {
     107              : 
     108              :         // SUBROUTINE INFORMATION:
     109              :         //     AUTHOR         Rick Strand
     110              :         //     DATE WRITTEN   February 2000
     111              : 
     112       249945 :         if (state.dataThermalComforts->FirstTimeFlag) {
     113          103 :             InitThermalComfort(state); // Mainly sets up output stuff
     114          103 :             state.dataThermalComforts->FirstTimeFlag = false;
     115              :         }
     116              : 
     117       249945 :         if (state.dataGlobal->DayOfSim == 1) {
     118        57959 :             if (state.dataGlobal->HourOfDay < 7) {
     119        14819 :                 state.dataThermalComforts->TemporarySixAMTemperature = 1.868132;
     120        43140 :             } else if (state.dataGlobal->HourOfDay == 7) {
     121         2382 :                 if (state.dataGlobal->TimeStep == 1) {
     122          432 :                     state.dataThermalComforts->TemporarySixAMTemperature = state.dataEnvrn->OutDryBulbTemp;
     123              :                 }
     124              :             }
     125              :         } else {
     126       191986 :             if (state.dataGlobal->HourOfDay == 7) {
     127         7999 :                 if (state.dataGlobal->TimeStep == 1) {
     128         1601 :                     state.dataThermalComforts->TemporarySixAMTemperature = state.dataEnvrn->OutDryBulbTemp;
     129              :                 }
     130              :             }
     131              :         }
     132              : 
     133       249945 :         if (InitializeOnly) {
     134            0 :             return;
     135              :         }
     136              : 
     137       249945 :         if (state.dataGlobal->BeginEnvrnFlag) {
     138          482 :             state.dataThermalComforts->ZoneOccHrs = 0.0;
     139              :         }
     140              : 
     141       249945 :         if (!state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
     142        18995 :             CalcThermalComfortFanger(state);
     143        18995 :             if (state.dataHeatBal->AnyThermalComfortPierceModel) {
     144            0 :                 CalcThermalComfortPierceASHRAE(state);
     145              :             }
     146        18995 :             if (state.dataHeatBal->AnyThermalComfortKSUModel) {
     147            0 :                 CalcThermalComfortKSU(state);
     148              :             }
     149        18995 :             if (state.dataHeatBal->AnyThermalComfortCoolingEffectModel) {
     150            0 :                 CalcThermalComfortCoolingEffectASH(state);
     151              :             }
     152        18995 :             if (state.dataHeatBal->AnyThermalComfortAnkleDraftModel) {
     153            0 :                 CalcThermalComfortAnkleDraftASH(state);
     154              :             }
     155        18995 :             CalcThermalComfortSimpleASH55(state);
     156        18995 :             CalcIfSetPointMet(state);
     157        18995 :             if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) {
     158            0 :                 CalcThermalComfortAdaptiveASH55(state, false);
     159              :             }
     160        18995 :             if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) {
     161            0 :                 CalcThermalComfortAdaptiveCEN15251(state, false);
     162              :             }
     163              :         }
     164              :     }
     165              : 
     166          103 :     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          103 :         std::string CurrentGroupName;
     176              : 
     177          103 :         state.dataThermalComforts->ThermalComfortData.allocate(state.dataHeatBal->TotPeople);
     178              : 
     179          153 :         for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
     180              : 
     181           50 :             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           50 :             if (state.dataHeatBal->People(Loop).Fanger) {
     186           24 :                 SetupOutputVariable(state,
     187              :                                     "Zone Thermal Comfort Fanger Model PMV",
     188              :                                     Constant::Units::None,
     189           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).FangerPMV,
     190              :                                     OutputProcessor::TimeStepType::Zone,
     191              :                                     OutputProcessor::StoreType::Average,
     192           12 :                                     state.dataHeatBal->People(Loop).Name);
     193           24 :                 SetupOutputVariable(state,
     194              :                                     "Zone Thermal Comfort Fanger Model PPD",
     195              :                                     Constant::Units::Perc,
     196           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).FangerPPD,
     197              :                                     OutputProcessor::TimeStepType::Zone,
     198              :                                     OutputProcessor::StoreType::Average,
     199           12 :                                     state.dataHeatBal->People(Loop).Name);
     200           24 :                 SetupOutputVariable(state,
     201              :                                     "Zone Thermal Comfort Clothing Surface Temperature",
     202              :                                     Constant::Units::C,
     203           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CloSurfTemp,
     204              :                                     OutputProcessor::TimeStepType::Zone,
     205              :                                     OutputProcessor::StoreType::Average,
     206           12 :                                     state.dataHeatBal->People(Loop).Name);
     207              :             }
     208              : 
     209           50 :             if (state.dataHeatBal->People(Loop).Pierce) {
     210            0 :                 SetupOutputVariable(state,
     211              :                                     "Zone Thermal Comfort Pierce Model Effective Temperature PMV",
     212              :                                     Constant::Units::None,
     213            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PiercePMVET,
     214              :                                     OutputProcessor::TimeStepType::Zone,
     215              :                                     OutputProcessor::StoreType::Average,
     216            0 :                                     state.dataHeatBal->People(Loop).Name);
     217            0 :                 SetupOutputVariable(state,
     218              :                                     "Zone Thermal Comfort Pierce Model Standard Effective Temperature PMV",
     219              :                                     Constant::Units::None,
     220            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PiercePMVSET,
     221              :                                     OutputProcessor::TimeStepType::Zone,
     222              :                                     OutputProcessor::StoreType::Average,
     223            0 :                                     state.dataHeatBal->People(Loop).Name);
     224            0 :                 SetupOutputVariable(state,
     225              :                                     "Zone Thermal Comfort Pierce Model Discomfort Index",
     226              :                                     Constant::Units::None,
     227            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PierceDISC,
     228              :                                     OutputProcessor::TimeStepType::Zone,
     229              :                                     OutputProcessor::StoreType::Average,
     230            0 :                                     state.dataHeatBal->People(Loop).Name);
     231            0 :                 SetupOutputVariable(state,
     232              :                                     "Zone Thermal Comfort Pierce Model Thermal Sensation Index",
     233              :                                     Constant::Units::None,
     234            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PierceTSENS,
     235              :                                     OutputProcessor::TimeStepType::Zone,
     236              :                                     OutputProcessor::StoreType::Average,
     237            0 :                                     state.dataHeatBal->People(Loop).Name);
     238            0 :                 SetupOutputVariable(state,
     239              :                                     "Zone Thermal Comfort Pierce Model Standard Effective Temperature",
     240              :                                     Constant::Units::C,
     241            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).PierceSET,
     242              :                                     OutputProcessor::TimeStepType::Zone,
     243              :                                     OutputProcessor::StoreType::Average,
     244            0 :                                     state.dataHeatBal->People(Loop).Name);
     245              :             }
     246              : 
     247           50 :             if (state.dataHeatBal->People(Loop).KSU) {
     248            0 :                 SetupOutputVariable(state,
     249              :                                     "Zone Thermal Comfort KSU Model Thermal Sensation Vote",
     250              :                                     Constant::Units::None,
     251            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).KsuTSV,
     252              :                                     OutputProcessor::TimeStepType::Zone,
     253              :                                     OutputProcessor::StoreType::Average,
     254            0 :                                     state.dataHeatBal->People(Loop).Name);
     255              :             }
     256              : 
     257           50 :             if ((state.dataHeatBal->People(Loop).Fanger) || (state.dataHeatBal->People(Loop).Pierce) || (state.dataHeatBal->People(Loop).KSU)) {
     258           24 :                 SetupOutputVariable(state,
     259              :                                     "Zone Thermal Comfort Mean Radiant Temperature",
     260              :                                     Constant::Units::C,
     261           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortMRT,
     262              :                                     OutputProcessor::TimeStepType::Zone,
     263              :                                     OutputProcessor::StoreType::Average,
     264           12 :                                     state.dataHeatBal->People(Loop).Name);
     265           24 :                 SetupOutputVariable(state,
     266              :                                     "Zone Thermal Comfort Operative Temperature",
     267              :                                     Constant::Units::C,
     268           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortOpTemp,
     269              :                                     OutputProcessor::TimeStepType::Zone,
     270              :                                     OutputProcessor::StoreType::Average,
     271           12 :                                     state.dataHeatBal->People(Loop).Name);
     272           24 :                 SetupOutputVariable(state,
     273              :                                     "Zone Thermal Comfort Clothing Value",
     274              :                                     Constant::Units::clo,
     275           12 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ClothingValue,
     276              :                                     OutputProcessor::TimeStepType::Zone,
     277              :                                     OutputProcessor::StoreType::Average,
     278           12 :                                     state.dataHeatBal->People(Loop).Name);
     279              :             }
     280              : 
     281           50 :             if (state.dataHeatBal->People(Loop).AdaptiveASH55) {
     282            0 :                 SetupOutputVariable(state,
     283              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model 90% Acceptability Status",
     284              :                                     Constant::Units::None,
     285            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveASH5590,
     286              :                                     OutputProcessor::TimeStepType::Zone,
     287              :                                     OutputProcessor::StoreType::Average,
     288            0 :                                     state.dataHeatBal->People(Loop).Name);
     289            0 :                 SetupOutputVariable(state,
     290              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model 80% Acceptability Status",
     291              :                                     Constant::Units::None,
     292            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveASH5580,
     293              :                                     OutputProcessor::TimeStepType::Zone,
     294              :                                     OutputProcessor::StoreType::Average,
     295            0 :                                     state.dataHeatBal->People(Loop).Name);
     296            0 :                 SetupOutputVariable(state,
     297              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model Running Average Outdoor Air Temperature",
     298              :                                     Constant::Units::C,
     299            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ASHRAE55RunningMeanOutdoorTemp,
     300              :                                     OutputProcessor::TimeStepType::Zone,
     301              :                                     OutputProcessor::StoreType::Average,
     302            0 :                                     state.dataHeatBal->People(Loop).Name);
     303            0 :                 SetupOutputVariable(state,
     304              :                                     "Zone Thermal Comfort ASHRAE 55 Adaptive Model Temperature",
     305              :                                     Constant::Units::C,
     306            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).TComfASH55,
     307              :                                     OutputProcessor::TimeStepType::Zone,
     308              :                                     OutputProcessor::StoreType::Average,
     309            0 :                                     state.dataHeatBal->People(Loop).Name);
     310              :             }
     311              : 
     312           50 :             if (state.dataHeatBal->People(Loop).AdaptiveCEN15251) {
     313            0 :                 SetupOutputVariable(state,
     314              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Category I Status",
     315              :                                     Constant::Units::None,
     316            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatI,
     317              :                                     OutputProcessor::TimeStepType::Zone,
     318              :                                     OutputProcessor::StoreType::Average,
     319            0 :                                     state.dataHeatBal->People(Loop).Name);
     320            0 :                 SetupOutputVariable(state,
     321              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Category II Status",
     322              :                                     Constant::Units::None,
     323            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatII,
     324              :                                     OutputProcessor::TimeStepType::Zone,
     325              :                                     OutputProcessor::StoreType::Average,
     326            0 :                                     state.dataHeatBal->People(Loop).Name);
     327            0 :                 SetupOutputVariable(state,
     328              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Category III Status",
     329              :                                     Constant::Units::None,
     330            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatIII,
     331              :                                     OutputProcessor::TimeStepType::Zone,
     332              :                                     OutputProcessor::StoreType::Average,
     333            0 :                                     state.dataHeatBal->People(Loop).Name);
     334            0 :                 SetupOutputVariable(state,
     335              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Running Average Outdoor Air Temperature",
     336              :                                     Constant::Units::C,
     337            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CEN15251RunningMeanOutdoorTemp,
     338              :                                     OutputProcessor::TimeStepType::Zone,
     339              :                                     OutputProcessor::StoreType::Average,
     340            0 :                                     state.dataHeatBal->People(Loop).Name);
     341            0 :                 SetupOutputVariable(state,
     342              :                                     "Zone Thermal Comfort CEN 15251 Adaptive Model Temperature",
     343              :                                     Constant::Units::C,
     344            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).TComfCEN15251,
     345              :                                     OutputProcessor::TimeStepType::Zone,
     346              :                                     OutputProcessor::StoreType::Average,
     347            0 :                                     state.dataHeatBal->People(Loop).Name);
     348              :             }
     349           50 :             if (state.dataHeatBal->People(Loop).CoolingEffectASH55) {
     350            0 :                 SetupOutputVariable(state,
     351              :                                     "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect",
     352              :                                     Constant::Units::C,
     353            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectASH55,
     354              :                                     OutputProcessor::TimeStepType::Zone,
     355              :                                     OutputProcessor::StoreType::Average,
     356            0 :                                     state.dataHeatBal->People(Loop).Name);
     357            0 :                 SetupOutputVariable(state,
     358              :                                     "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect Adjusted PMV",
     359              :                                     Constant::Units::None,
     360            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectAdjustedPMVASH55,
     361              :                                     OutputProcessor::TimeStepType::Zone,
     362              :                                     OutputProcessor::StoreType::Average,
     363            0 :                                     state.dataHeatBal->People(Loop).Name);
     364            0 :                 SetupOutputVariable(state,
     365              :                                     "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect Adjusted PPD",
     366              :                                     Constant::Units::None,
     367            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectAdjustedPPDASH55,
     368              :                                     OutputProcessor::TimeStepType::Zone,
     369              :                                     OutputProcessor::StoreType::Average,
     370            0 :                                     state.dataHeatBal->People(Loop).Name);
     371              :             }
     372           50 :             if (state.dataHeatBal->People(Loop).AnkleDraftASH55) {
     373            0 :                 SetupOutputVariable(state,
     374              :                                     "Zone Thermal Comfort ASHRAE 55 Ankle Draft PPD",
     375              :                                     Constant::Units::None,
     376            0 :                                     state.dataThermalComforts->ThermalComfortData(Loop).AnkleDraftPPDASH55,
     377              :                                     OutputProcessor::TimeStepType::Zone,
     378              :                                     OutputProcessor::StoreType::Average,
     379            0 :                                     state.dataHeatBal->People(Loop).Name);
     380              :             }
     381              :         }
     382          103 :         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          153 :         for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
     386           50 :             if (state.dataHeatBal->People(Loop).Show55Warning) {
     387            2 :                 state.dataThermalComforts->ThermalComfortInASH55(state.dataHeatBal->People(Loop).ZonePtr).Enable55Warning = true;
     388              :             }
     389              :         }
     390              : 
     391              :         // CurrentModuleObject='Zone'
     392          226 :         for (Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
     393          246 :             SetupOutputVariable(state,
     394              :                                 "Zone Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time",
     395              :                                 Constant::Units::hr,
     396          123 :                                 state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotSummer,
     397              :                                 OutputProcessor::TimeStepType::Zone,
     398              :                                 OutputProcessor::StoreType::Sum,
     399          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     400          246 :             SetupOutputVariable(state,
     401              :                                 "Zone Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time",
     402              :                                 Constant::Units::hr,
     403          123 :                                 state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotWinter,
     404              :                                 OutputProcessor::TimeStepType::Zone,
     405              :                                 OutputProcessor::StoreType::Sum,
     406          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     407          246 :             SetupOutputVariable(state,
     408              :                                 "Zone Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time",
     409              :                                 Constant::Units::hr,
     410          123 :                                 state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotEither,
     411              :                                 OutputProcessor::TimeStepType::Zone,
     412              :                                 OutputProcessor::StoreType::Sum,
     413          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     414              :         }
     415          412 :         SetupOutputVariable(state,
     416              :                             "Facility Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time",
     417              :                             Constant::Units::hr,
     418          103 :                             state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer,
     419              :                             OutputProcessor::TimeStepType::Zone,
     420              :                             OutputProcessor::StoreType::Sum,
     421              :                             "Facility");
     422          412 :         SetupOutputVariable(state,
     423              :                             "Facility Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time",
     424              :                             Constant::Units::hr,
     425          103 :                             state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter,
     426              :                             OutputProcessor::TimeStepType::Zone,
     427              :                             OutputProcessor::StoreType::Sum,
     428              :                             "Facility");
     429          412 :         SetupOutputVariable(state,
     430              :                             "Facility Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time",
     431              :                             Constant::Units::hr,
     432          103 :                             state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either,
     433              :                             OutputProcessor::TimeStepType::Zone,
     434              :                             OutputProcessor::StoreType::Sum,
     435              :                             "Facility");
     436              : 
     437          103 :         state.dataThermalComforts->ThermalComfortSetPoint.allocate(state.dataGlobal->NumOfZones);
     438          226 :         for (Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
     439          246 :             SetupOutputVariable(state,
     440              :                                 "Zone Heating Setpoint Not Met Time",
     441              :                                 Constant::Units::hr,
     442          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetHeating,
     443              :                                 OutputProcessor::TimeStepType::Zone,
     444              :                                 OutputProcessor::StoreType::Sum,
     445          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     446          246 :             SetupOutputVariable(state,
     447              :                                 "Zone Heating Setpoint Not Met While Occupied Time",
     448              :                                 Constant::Units::hr,
     449          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetHeatingOccupied,
     450              :                                 OutputProcessor::TimeStepType::Zone,
     451              :                                 OutputProcessor::StoreType::Sum,
     452          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     453          246 :             SetupOutputVariable(state,
     454              :                                 "Zone Cooling Setpoint Not Met Time",
     455              :                                 Constant::Units::hr,
     456          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetCooling,
     457              :                                 OutputProcessor::TimeStepType::Zone,
     458              :                                 OutputProcessor::StoreType::Sum,
     459          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     460          246 :             SetupOutputVariable(state,
     461              :                                 "Zone Cooling Setpoint Not Met While Occupied Time",
     462              :                                 Constant::Units::hr,
     463          123 :                                 state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetCoolingOccupied,
     464              :                                 OutputProcessor::TimeStepType::Zone,
     465              :                                 OutputProcessor::StoreType::Sum,
     466          123 :                                 state.dataHeatBal->Zone(Loop).Name);
     467              :         }
     468              : 
     469          412 :         SetupOutputVariable(state,
     470              :                             "Facility Heating Setpoint Not Met Time",
     471              :                             Constant::Units::hr,
     472          103 :                             state.dataThermalComforts->AnyZoneNotMetHeating,
     473              :                             OutputProcessor::TimeStepType::Zone,
     474              :                             OutputProcessor::StoreType::Sum,
     475              :                             "Facility");
     476          412 :         SetupOutputVariable(state,
     477              :                             "Facility Cooling Setpoint Not Met Time",
     478              :                             Constant::Units::hr,
     479          103 :                             state.dataThermalComforts->AnyZoneNotMetCooling,
     480              :                             OutputProcessor::TimeStepType::Zone,
     481              :                             OutputProcessor::StoreType::Sum,
     482              :                             "Facility");
     483          412 :         SetupOutputVariable(state,
     484              :                             "Facility Heating Setpoint Not Met While Occupied Time",
     485              :                             Constant::Units::hr,
     486          103 :                             state.dataThermalComforts->AnyZoneNotMetHeatingOccupied,
     487              :                             OutputProcessor::TimeStepType::Zone,
     488              :                             OutputProcessor::StoreType::Sum,
     489              :                             "Facility");
     490          412 :         SetupOutputVariable(state,
     491              :                             "Facility Cooling Setpoint Not Met While Occupied Time",
     492              :                             Constant::Units::hr,
     493          103 :                             state.dataThermalComforts->AnyZoneNotMetCoolingOccupied,
     494              :                             OutputProcessor::TimeStepType::Zone,
     495              :                             OutputProcessor::StoreType::Sum,
     496              :                             "Facility");
     497              : 
     498          103 :         GetAngleFactorList(state);
     499              : 
     500          103 :         state.dataThermalComforts->ZoneOccHrs.dimension(state.dataGlobal->NumOfZones, 0.0);
     501          103 :     }
     502              : 
     503        19000 :     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        29450 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
     534        10450 :              ++state.dataThermalComforts->PeopleNum) { // Is there a reason why this is a state variable and not a local variable?
     535              : 
     536        10450 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
     537        10450 :             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        10450 :             if (present(PNum)) {
     541            0 :                 if (state.dataThermalComforts->PeopleNum != PNum) {
     542         8621 :                     continue;
     543              :                 }
     544              :             }
     545              : 
     546              :             // If optional argument is used do not cycle regardless of thermal comfort reporting type
     547        10450 :             if ((!people.Fanger) && (!present(PNum))) {
     548         8621 :                 continue;
     549              :             }
     550              : 
     551         1829 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
     552         1829 :             auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
     553         1829 :             if (present(PNum)) {
     554            0 :                 state.dataThermalComforts->AirTemp = Tset;
     555              :             } else {
     556         1829 :                 state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
     557              :             }
     558         1829 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
     559            0 :                 state.dataThermalComforts->ZoneNum = people.ZonePtr;
     560            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
     561            0 :                     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            0 :                 } 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         1829 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
     573              :             // Use mean air temp for calculating RH when thermal comfort control is used
     574         1829 :             if (present(PNum)) {
     575            0 :                 state.dataThermalComforts->RelHum =
     576            0 :                     PsyRhFnTdbWPb(state,
     577            0 :                                   state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).MAT,
     578              :                                   thisZoneHB.airHumRatAvgComf,
     579            0 :                                   state.dataEnvrn->OutBaroPress);
     580              :             } else {
     581         3658 :                 state.dataThermalComforts->RelHum =
     582         1829 :                     PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
     583              :             }
     584         1829 :             people.TemperatureInZone = state.dataThermalComforts->AirTemp;
     585         1829 :             people.RelativeHumidityInZone = state.dataThermalComforts->RelHum * 100.0;
     586              : 
     587              :             // Metabolic rate of body (W/m2)
     588         1829 :             state.dataThermalComforts->ActLevel = people.activityLevelSched->getCurrentVal() / BodySurfArea;
     589              :             // Energy consumption by external work (W/m2)
     590         1829 :             state.dataThermalComforts->WorkEff = people.workEffSched->getCurrentVal() * state.dataThermalComforts->ActLevel;
     591              :             // Clothing unit
     592              :             Real64 IntermediateClothing;
     593         1829 :             switch (people.clothingType) {
     594         1829 :             case DataHeatBalance::ClothingType::InsulationSchedule:
     595         1829 :                 state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     596         1829 :                 break;
     597            0 :             case DataHeatBalance::ClothingType::DynamicAshrae55:
     598            0 :                 comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     599            0 :                 comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     600            0 :                 DynamicClothingModel(state);
     601            0 :                 state.dataThermalComforts->CloUnit = comfort.ClothingValue;
     602            0 :                 break;
     603            0 :             case DataHeatBalance::ClothingType::CalculationSchedule:
     604            0 :                 IntermediateClothing = people.clothingMethodSched->getCurrentVal();
     605            0 :                 if (IntermediateClothing == 1.0) {
     606            0 :                     state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     607            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     608            0 :                 } else if (IntermediateClothing == 2.0) {
     609            0 :                     comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     610            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     611            0 :                     DynamicClothingModel(state);
     612            0 :                     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            0 :                 break;
     619            0 :             default:
     620            0 :                 ShowSevereError(state, format("PEOPLE=\"{}\", Incorrect Clothing Type", people.Name));
     621              :             }
     622              : 
     623         1829 :             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         1829 :                 state.dataThermalComforts->AirVel = people.airVelocitySched->getCurrentVal();
     633              :                 // Ensure air velocity within the reasonable range. Otherwise reccusive warnings is provided
     634         1829 :                 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         1829 :             Real64 PMV = CalcFangerPMV(state,
     653         1829 :                                        state.dataThermalComforts->AirTemp,
     654         1829 :                                        state.dataThermalComforts->RadTemp,
     655         1829 :                                        state.dataThermalComforts->RelHum,
     656         1829 :                                        state.dataThermalComforts->AirVel,
     657         1829 :                                        state.dataThermalComforts->ActLevel,
     658         1829 :                                        state.dataThermalComforts->CloUnit,
     659         1829 :                                        state.dataThermalComforts->WorkEff);
     660              : 
     661         1829 :             comfort.FangerPMV = PMV;
     662              :             // Pass resulting PMV based on temperature setpoint (Tset) when using thermal comfort control
     663         1829 :             if (present(PNum)) {
     664            0 :                 PMVResult = PMV;
     665              :             }
     666         1829 :             comfort.ThermalComfortMRT = state.dataThermalComforts->RadTemp;
     667         1829 :             comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     668         1829 :             comfort.CloSurfTemp = state.dataThermalComforts->CloSurfTemp;
     669              : 
     670              :             // Calculate the Fanger PPD (Predicted Percentage of Dissatisfied), as a %
     671         1829 :             Real64 PPD = CalcFangerPPD(PMV);
     672         1829 :             comfort.FangerPPD = PPD;
     673              :         }
     674        19000 :     }
     675              : 
     676         1834 :     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         1834 :         int constexpr MaxIter(150);             // Limit of iteration
     685         1834 :         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         1834 :         state.dataThermalComforts->VapPress = PsyPsatFnTemp(state, AirTemp); // use psych routines inside E+ , returns Pa
     699              : 
     700         1834 :         state.dataThermalComforts->VapPress *= RelHum; // in units of [Pa]
     701              : 
     702         1834 :         state.dataThermalComforts->IntHeatProd = ActLevel - WorkEff;
     703              : 
     704              :         // Compute the Corresponding Clothed Body Ratio
     705         1834 :         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         1834 :         if (CloUnit < 0.5) {
     709            0 :             state.dataThermalComforts->CloBodyRat = state.dataThermalComforts->CloBodyRat - 0.05 + 0.1 * CloUnit;
     710              :         }
     711              : 
     712         1834 :         state.dataThermalComforts->AbsRadTemp = RadTemp + TAbsConv;
     713         1834 :         state.dataThermalComforts->AbsAirTemp = AirTemp + TAbsConv;
     714              : 
     715         1834 :         state.dataThermalComforts->CloInsul = CloUnit * state.dataThermalComforts->CloBodyRat * 0.155; // Thermal resistance of the clothing // icl
     716              : 
     717         1834 :         P2 = state.dataThermalComforts->CloInsul * 3.96;
     718         1834 :         P3 = state.dataThermalComforts->CloInsul * 100.0;
     719         1834 :         P1 = state.dataThermalComforts->CloInsul * state.dataThermalComforts->AbsAirTemp;                                        // p4
     720         1834 :         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         1834 :         state.dataThermalComforts->AbsCloSurfTemp = state.dataThermalComforts->AbsAirTemp + (35.5 - AirTemp) / (3.5 * (CloUnit + 0.1));
     724         1834 :         XN = state.dataThermalComforts->AbsCloSurfTemp / 100.0;
     725         1834 :         state.dataThermalComforts->HcFor = 12.1 * std::sqrt(AirVel); // Heat transfer coefficient by forced convection
     726         1834 :         state.dataThermalComforts->IterNum = 0;
     727         1834 :         XF = XN;
     728              : 
     729              :         // COMPUTE SURFACE TEMPERATURE OF CLOTHING BY ITERATIONS
     730        11578 :         while (((std::abs(XN - XF) > StopIterCrit) || (state.dataThermalComforts->IterNum == 0)) && (state.dataThermalComforts->IterNum < MaxIter)) {
     731         9744 :             XF = (XF + XN) / 2.0;
     732        19488 :             state.dataThermalComforts->HcNat =
     733         9744 :                 2.38 * root_4(std::abs(100.0 * XF - state.dataThermalComforts->AbsAirTemp)); // Heat transfer coefficient by natural convection
     734         9744 :             state.dataThermalComforts->Hc =
     735         9744 :                 max(state.dataThermalComforts->HcFor, state.dataThermalComforts->HcNat); // Determination of convective heat transfer coefficient
     736         9744 :             XN = (P4 + P1 * state.dataThermalComforts->Hc - P2 * pow_4(XF)) / (100.0 + P3 * state.dataThermalComforts->Hc);
     737         9744 :             ++state.dataThermalComforts->IterNum;
     738         9744 :             if (state.dataThermalComforts->IterNum > MaxIter) {
     739            0 :                 ShowWarningError(state, "Max iteration exceeded in CalcThermalFanger");
     740              :             }
     741              :         }
     742         1834 :         state.dataThermalComforts->AbsCloSurfTemp = 100.0 * XN;
     743         1834 :         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         3668 :         state.dataThermalComforts->RadHeatLoss =
     752         1834 :             3.96 * state.dataThermalComforts->CloBodyRat *
     753         1834 :             (pow_4(state.dataThermalComforts->AbsCloSurfTemp * 0.01) - pow_4(state.dataThermalComforts->AbsRadTemp * 0.01));
     754              : 
     755         1834 :         state.dataThermalComforts->ConvHeatLoss = state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->Hc *
     756         1834 :                                                   (state.dataThermalComforts->CloSurfTemp - AirTemp); // Heat loss by convection
     757              : 
     758         1834 :         state.dataThermalComforts->DryHeatLoss = state.dataThermalComforts->RadHeatLoss + state.dataThermalComforts->ConvHeatLoss;
     759              : 
     760              :         // Evaporative heat loss
     761              :         // Heat loss by regulatory sweating
     762         1834 :         state.dataThermalComforts->EvapHeatLossRegComf = 0.0;
     763         1834 :         if (state.dataThermalComforts->IntHeatProd > 58.2) {
     764         1445 :             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         3668 :         state.dataThermalComforts->EvapHeatLossDiff =
     771         1834 :             3.05 * 0.001 *
     772         1834 :             (5733.0 - 6.99 * state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->VapPress); // ln 440 in ASHRAE 55 Append. D
     773              : 
     774         1834 :         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         3668 :         state.dataThermalComforts->LatRespHeatLoss =
     778         1834 :             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         1834 :         state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevel * (34.0 - AirTemp); // Heat loss by dry respiration.
     785              : 
     786         1834 :         state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
     787              : 
     788         1834 :         state.dataThermalComforts->ThermSensTransCoef = 0.303 * std::exp(-0.036 * ActLevel) + 0.028; // Thermal transfer coefficient to calculate PMV
     789              : 
     790         1834 :         PMV = state.dataThermalComforts->ThermSensTransCoef * (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->EvapHeatLoss -
     791         1834 :                                                                state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLoss);
     792              : 
     793         1834 :         return PMV;
     794              :     }
     795              : 
     796         1831 :     Real64 CalcFangerPPD(Real64 PMV)
     797              :     {
     798              :         Real64 PPD;
     799         1831 :         Real64 expTest1 = -0.03353 * pow_4(PMV) - 0.2179 * pow_2(PMV);
     800         1831 :         if (expTest1 > DataPrecisionGlobals::EXP_LowerLimit) {
     801         1604 :             PPD = 100.0 - 95.0 * std::exp(expTest1);
     802              :         } else {
     803          227 :             PPD = 100.0;
     804              :         }
     805              : 
     806         1831 :         if (PPD < 0.0) {
     807            0 :             PPD = 0.0;
     808         1831 :         } else if (PPD > 100.0) {
     809            0 :             PPD = 100.0;
     810              :         }
     811         1831 :         return PPD;
     812              :     }
     813              : 
     814            5 :     Real64 CalcRelativeAirVelocity(Real64 AirVel, Real64 ActMet)
     815              :     {
     816            5 :         if (ActMet > 1) {
     817            3 :             return AirVel + 0.3 * (ActMet - 1);
     818              :         } else {
     819            2 :             return AirVel;
     820              :         }
     821              :     }
     822              : 
     823            3 :     void GetThermalComfortInputsASHRAE(EnergyPlusData &state)
     824              :     {
     825            3 :         auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
     826            3 :         auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
     827              : 
     828            3 :         state.dataThermalComforts->ZoneNum = people.ZonePtr;
     829            3 :         auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
     830              :         // (var TA)
     831            3 :         state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
     832            3 :         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            3 :         state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
     840              :         // (var RH)
     841            6 :         state.dataThermalComforts->RelHum =
     842            3 :             PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
     843              :         // Metabolic rate of body (W/m2) (var RM, M)
     844            3 :         state.dataThermalComforts->ActLevel = people.activityLevelSched->getCurrentVal() / BodySurfAreaPierce;
     845              :         // Energy consumption by external work (W/m2) (var WME)
     846            3 :         state.dataThermalComforts->WorkEff = people.workEffSched->getCurrentVal() * state.dataThermalComforts->ActLevel;
     847              : 
     848              :         // Clothing unit  (var CLO)
     849              :         Real64 IntermediateClothing;
     850            3 :         switch (people.clothingType) {
     851            3 :         case DataHeatBalance::ClothingType::InsulationSchedule:
     852            3 :             state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
     853            3 :             break;
     854            0 :         case DataHeatBalance::ClothingType::DynamicAshrae55:
     855            0 :             comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
     856            0 :             comfort.ClothingValue = state.dataThermalComforts->CloUnit;
     857            0 :             DynamicClothingModel(state);
     858            0 :             state.dataThermalComforts->CloUnit = comfort.ClothingValue;
     859            0 :             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            3 :         state.dataThermalComforts->AirVel = people.airVelocitySched->getCurrentVal();
     880              :         // (var MET)
     881            3 :         state.dataThermalComforts->ActMet = state.dataThermalComforts->ActLevel / ActLevelConv;
     882            3 :     }
     883              : 
     884           44 :     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           44 :         constexpr Real64 CloFac(0.25);                  // Clothing factor determined experimentally (var KCLO)
     890           44 :         constexpr Real64 BodyWeight(69.9);              // (var BODYWEIGHT)
     891           44 :         constexpr Real64 SweatContConst(170.0);         // Proportionality constant for sweat control; g/m2.hr (var CSW)
     892           44 :         constexpr Real64 DriCoeffVasodilation(120);     // driving coefficient for vasodilation (var CDIL)
     893           44 :         constexpr Real64 DriCoeffVasoconstriction(0.5); // (var CSTR)
     894           44 :         constexpr Real64 MaxSkinBloodFlow(90.0);        // Max. value of skin blood flow
     895           44 :         constexpr Real64 MinSkinBloodFlow(0.5);         // Min. value of skin blood flow
     896           44 :         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           44 :         constexpr Real64 SkinTempSet(33.7);     // (var TempSkinNeutral)
     904           44 :         constexpr Real64 CoreTempSet(36.8);     // (var TempCoreNeutral)
     905           44 :         constexpr Real64 SkinBloodFlowSet(6.3); // (var SkinBloodFlowNeutral)
     906           44 :         constexpr Real64 SkinMassRatSet(0.1);   // (var ALFA)
     907              : 
     908           44 :         if (AirVel < 0.1) {
     909            0 :             AirVel = 0.1;
     910              :         }
     911              : 
     912              :         // (var VaporPressure)
     913           44 :         state.dataThermalComforts->VapPress = RelHum * CalcSatVapPressFromTempTorr(AirTemp);
     914           44 :         Real64 ActLevel = ActLevelConv * ActMet;
     915           44 :         state.dataThermalComforts->IntHeatProd = ActLevel - WorkEff;
     916              : 
     917              :         // Step 1: CALCULATE VARIABLESS THAT REMAIN CONSTANT FOR AN HOUR
     918           44 :         Real64 PInAtmospheres = state.dataEnvrn->OutBaroPress / 101325;
     919           44 :         Real64 RClo = CloUnit * 0.155; // (var RCL)
     920           44 :         Real64 TotCloFac = 1.0 + 0.15 * CloUnit;
     921           44 :         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           44 :         state.dataThermalComforts->SkinTemp = SkinTempSet;
     926           44 :         state.dataThermalComforts->CoreTemp = CoreTempSet;
     927           44 :         Real64 SkinBloodFlow = SkinBloodFlowSet;
     928           44 :         Real64 SkinMassRat = SkinMassRatSet;
     929              : 
     930              :         // Mass transfer equation between skin and environment
     931              :         // CloInsul is efficiency of mass transfer for CloUnit.
     932           44 :         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           44 :             EvapEff = 0.59 * std::pow(AirVel, -0.08); // (var ICL)
     937           44 :             state.dataThermalComforts->CloInsul = 0.45;
     938              :         }
     939              : 
     940           44 :         Real64 CorrectedHC = 3.0 * std::pow(PInAtmospheres, 0.53);              // corrected convective heat transfer coefficient
     941           44 :         Real64 ForcedHC = 8.600001 * std::pow((AirVel * PInAtmospheres), 0.53); // forced convective heat transfer coefficient, W/(m2 °C) (CHCV)
     942           44 :         state.dataThermalComforts->Hc = std::max(CorrectedHC, ForcedHC);        // (CHC)
     943           44 :         state.dataThermalComforts->Hr = 4.7;                                    // (CHR)
     944           44 :         state.dataThermalComforts->EvapHeatLoss = 0.1 * ActMet;
     945           44 :         Real64 RAir = 1.0 / (TotCloFac * (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr)); // resistance of air layer to dry heat (RA)
     946           44 :         state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hr * RadTemp + state.dataThermalComforts->Hc * AirTemp) /
     947           44 :                                             (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr); // operative temperature (TOP)
     948           44 :         Real64 ActLevelStart = ActLevel; // ActLevel gets increased by shivering in the following
     949           44 :         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         2684 :         for (int IterMin = 1; IterMin <= 60; ++IterMin) {
     956              :             // Dry heat balance: solve for CloSurfTemp and Hr, GUESS CloSurfTemp TO START
     957         5280 :             state.dataThermalComforts->CloSurfTemp =
     958         2640 :                 (RAir * state.dataThermalComforts->SkinTemp + RClo * state.dataThermalComforts->OpTemp) / (RAir + RClo);
     959         2640 :             bool converged = false;
     960         5336 :             while (!converged) {
     961         5392 :                 state.dataThermalComforts->Hr =
     962         2696 :                     4.0 * StefanBoltz * std::pow((state.dataThermalComforts->CloSurfTemp + RadTemp) / 2.0 + 273.15, 3) * 0.72;
     963         2696 :                 RAir = 1.0 / (TotCloFac * (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr));
     964         2696 :                 state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hr * RadTemp + state.dataThermalComforts->Hc * AirTemp) /
     965         2696 :                                                     (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr);
     966         2696 :                 Real64 CloSurfTempNew = (RAir * state.dataThermalComforts->SkinTemp + RClo * state.dataThermalComforts->OpTemp) / (RAir + RClo);
     967         2696 :                 if (std::abs(CloSurfTempNew - state.dataThermalComforts->CloSurfTemp) <= 0.01) {
     968         2640 :                     converged = true;
     969              :                 }
     970         2696 :                 state.dataThermalComforts->CloSurfTemp = CloSurfTempNew;
     971              :             }
     972              : 
     973              :             // CALCULATE THE COMBINED HEAT TRANSFER COEFF. (H)
     974         2640 :             state.dataThermalComforts->H = state.dataThermalComforts->Hr + state.dataThermalComforts->Hc;
     975              :             // Heat flow from Clothing surface to environment
     976         2640 :             state.dataThermalComforts->DryHeatLoss = (state.dataThermalComforts->SkinTemp - state.dataThermalComforts->OpTemp) / (RAir + RClo);
     977              : 
     978              :             // dry and latent respiratory heat losses
     979         5280 :             state.dataThermalComforts->LatRespHeatLoss =
     980         2640 :                 0.0023 * ActLevel * (44.0 - state.dataThermalComforts->VapPress); // latent heat loss due to respiration
     981         2640 :             state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevel * (34.0 - AirTemp);
     982              : 
     983         2640 :             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         5280 :             state.dataThermalComforts->HeatFlow =
     987         2640 :                 (state.dataThermalComforts->CoreTemp - state.dataThermalComforts->SkinTemp) * (5.28 + 1.163 * SkinBloodFlow);
     988              : 
     989         2640 :             Real64 CoreHeatStorage = ActLevel - state.dataThermalComforts->HeatFlow - state.dataThermalComforts->RespHeatLoss -
     990         2640 :                                      WorkEff; // rate of energy storage in the core
     991         2640 :             Real64 SkinHeatStorage = state.dataThermalComforts->HeatFlow - state.dataThermalComforts->DryHeatLoss -
     992         2640 :                                      state.dataThermalComforts->EvapHeatLoss; // rate of energy storage in the skin
     993              : 
     994              :             // Thermal capacities
     995         2640 :             state.dataThermalComforts->CoreThermCap = 0.97 * (1 - SkinMassRat) * BodyWeight;
     996         2640 :             state.dataThermalComforts->SkinThermCap = 0.97 * SkinMassRat * BodyWeight;
     997              : 
     998              :             // Temperature changes in 1 minute
     999         2640 :             state.dataThermalComforts->CoreTempChange = (CoreHeatStorage * BodySurfAreaPierce / (state.dataThermalComforts->CoreThermCap * 60.0));
    1000         2640 :             state.dataThermalComforts->SkinTempChange = (SkinHeatStorage * BodySurfAreaPierce) / (state.dataThermalComforts->SkinThermCap * 60.0);
    1001              : 
    1002         2640 :             state.dataThermalComforts->CoreTemp += state.dataThermalComforts->CoreTempChange;
    1003         2640 :             state.dataThermalComforts->SkinTemp += state.dataThermalComforts->SkinTempChange;
    1004         5280 :             state.dataThermalComforts->AvgBodyTemp =
    1005         2640 :                 SkinMassRat * state.dataThermalComforts->SkinTemp + (1.0 - SkinMassRat) * state.dataThermalComforts->CoreTemp;
    1006              : 
    1007              :             Real64 SkinThermSigWarm;                                               // vasodilation signal (WARMS)
    1008              :             Real64 SkinThermSigCold;                                               // vasoconstriction signal
    1009         2640 :             Real64 SkinSignal = state.dataThermalComforts->SkinTemp - SkinTempSet; // thermoregulatory control signal from the skin
    1010         2640 :             if (SkinSignal > 0) {
    1011          765 :                 SkinThermSigWarm = SkinSignal;
    1012          765 :                 SkinThermSigCold = 0.0;
    1013              :             } else {
    1014         1875 :                 SkinThermSigCold = -SkinSignal;
    1015         1875 :                 SkinThermSigWarm = 0.0;
    1016              :             }
    1017              : 
    1018              :             Real64 CoreThermSigWarm;                                               // vasodialtion signal (WARMC)
    1019              :             Real64 CoreThermSigCold;                                               // vasoconstriction signal
    1020         2640 :             Real64 CoreSignal = state.dataThermalComforts->CoreTemp - CoreTempSet; // thermoregulatory control signal from the skin, °C
    1021         2640 :             if (CoreSignal > 0) {
    1022         2356 :                 CoreThermSigWarm = CoreSignal;
    1023         2356 :                 CoreThermSigCold = 0.0;
    1024              :             } else {
    1025          284 :                 CoreThermSigCold = -CoreSignal;
    1026          284 :                 CoreThermSigWarm = 0.0;
    1027              :             }
    1028              : 
    1029              :             Real64 BodyThermSigWarm; // WARMB
    1030         2640 :             Real64 BodySignal = state.dataThermalComforts->AvgBodyTemp - AvgBodyTempSet;
    1031              : 
    1032         2640 :             if (BodySignal > 0) {
    1033          666 :                 BodyThermSigWarm = BodySignal;
    1034              :             } else {
    1035         1974 :                 BodyThermSigWarm = 0.0;
    1036              :             }
    1037              : 
    1038         2640 :             state.dataThermalComforts->VasodilationFac = DriCoeffVasodilation * CoreThermSigWarm;
    1039         2640 :             state.dataThermalComforts->VasoconstrictFac = DriCoeffVasoconstriction * SkinThermSigCold;
    1040         2640 :             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         2640 :             if (SkinBloodFlow < MinSkinBloodFlow) {
    1043            0 :                 SkinBloodFlow = MinSkinBloodFlow;
    1044              :             }
    1045         2640 :             if (SkinBloodFlow > MaxSkinBloodFlow) {
    1046            0 :                 SkinBloodFlow = MaxSkinBloodFlow;
    1047              :             }
    1048         2640 :             SkinMassRat = 0.0417737 + 0.7451832 / (SkinBloodFlow + 0.585417); // ratio of skin-core masses change with SkinBloodFlow
    1049              : 
    1050         2640 :             Real64 RegSweat = SweatContConst * BodyThermSigWarm * std::exp(SkinThermSigWarm / 10.7); // control of regulatory sweating
    1051         2640 :             if (RegSweat > RegSweatMax) {
    1052            0 :                 RegSweat = RegSweatMax;
    1053              :             }
    1054         2640 :             state.dataThermalComforts->EvapHeatLossRegSweat = 0.68 * RegSweat; // heat lost by vaporization sweat
    1055              : 
    1056              :             // adjustment of metabolic heat due to shivering (Stolwijk, Hardy)
    1057         2640 :             state.dataThermalComforts->ShivResponse = 19.4 * SkinThermSigCold * CoreThermSigCold;
    1058         2640 :             ActLevel = ActLevelStart + state.dataThermalComforts->ShivResponse;
    1059              : 
    1060              :             // Evaluation of heat transfer by evaporation at skin surface
    1061         2640 :             Real64 AirEvapHeatResist = 1.0 / (LewisRatio * TotCloFac * state.dataThermalComforts->Hc); // evaporative resistance air layer
    1062         2640 :             Real64 CloEvapHeatResist = RClo / (LewisRatio * state.dataThermalComforts->CloInsul);
    1063         2640 :             Real64 TotEvapHeatResist = AirEvapHeatResist + CloEvapHeatResist;
    1064         2640 :             state.dataThermalComforts->SatSkinVapPress = CalcSatVapPressFromTempTorr(state.dataThermalComforts->SkinTemp); // PSSK
    1065         5280 :             state.dataThermalComforts->EvapHeatLossMax =
    1066         2640 :                 (state.dataThermalComforts->SatSkinVapPress - state.dataThermalComforts->VapPress) / TotEvapHeatResist; // TotEvapHeatResist;
    1067         5280 :             state.dataThermalComforts->SkinWetSweat =
    1068         2640 :                 state.dataThermalComforts->EvapHeatLossRegSweat /
    1069         2640 :                 state.dataThermalComforts->EvapHeatLossMax; // ratio heat loss sweating to max heat loss sweating
    1070              : 
    1071         5280 :             state.dataThermalComforts->SkinWetDiff =
    1072         2640 :                 (1.0 - state.dataThermalComforts->SkinWetSweat) * 0.06; // 0.06 if SkinWetDiff for nonsweating skin --- Kerslake
    1073         2640 :             state.dataThermalComforts->EvapHeatLossDiff = state.dataThermalComforts->SkinWetDiff * state.dataThermalComforts->EvapHeatLossMax;
    1074         2640 :             state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossRegSweat + state.dataThermalComforts->EvapHeatLossDiff;
    1075         2640 :             state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
    1076              : 
    1077              :             // Beginning of dripping (Sweat not evaporated on skin surface)
    1078         2640 :             if (state.dataThermalComforts->SkinWetTot >= EvapEff) {
    1079            0 :                 state.dataThermalComforts->SkinWetTot = EvapEff;
    1080            0 :                 state.dataThermalComforts->SkinWetSweat = EvapEff / 0.94;
    1081            0 :                 state.dataThermalComforts->EvapHeatLossRegSweat =
    1082            0 :                     state.dataThermalComforts->SkinWetSweat * state.dataThermalComforts->EvapHeatLossMax;
    1083            0 :                 state.dataThermalComforts->SkinWetDiff = (1.0 - state.dataThermalComforts->SkinWetSweat) * 0.06;
    1084            0 :                 state.dataThermalComforts->EvapHeatLossDiff = state.dataThermalComforts->SkinWetDiff * state.dataThermalComforts->EvapHeatLossMax;
    1085            0 :                 state.dataThermalComforts->EvapHeatLoss =
    1086            0 :                     state.dataThermalComforts->EvapHeatLossRegSweat + state.dataThermalComforts->EvapHeatLossDiff;
    1087              :             }
    1088              : 
    1089              :             // When EvapHeatLossMax<0. condensation on skin occurs.
    1090         2640 :             if (state.dataThermalComforts->EvapHeatLossMax < 0.0) {
    1091            0 :                 state.dataThermalComforts->SkinWetDiff = 0.0;
    1092            0 :                 state.dataThermalComforts->EvapHeatLossDiff = 0.0;
    1093            0 :                 state.dataThermalComforts->EvapHeatLoss = 0.0;
    1094            0 :                 state.dataThermalComforts->SkinWetTot = EvapEff;
    1095            0 :                 state.dataThermalComforts->SkinWetSweat = EvapEff;
    1096            0 :                 state.dataThermalComforts->EvapHeatLossRegSweat = 0.0;
    1097              :             }
    1098              :             // Vapor pressure at skin (as measured by dewpoint sensors)
    1099         5280 :             state.dataThermalComforts->SkinVapPress = state.dataThermalComforts->SkinWetTot * state.dataThermalComforts->SatSkinVapPress +
    1100         2640 :                                                       (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           44 :         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           44 :         Real64 EffectSkinHeatLoss = state.dataThermalComforts->DryHeatLoss + state.dataThermalComforts->EvapHeatLoss;
    1109              :         // ET*(standardization humidity/REAL(r64) CloUnit, StdAtm and Hc)
    1110           44 :         state.dataThermalComforts->CloBodyRat = 1.0 + CloFac * CloUnit;
    1111              :         Real64 EffectCloUnit =
    1112           44 :             CloUnit - (state.dataThermalComforts->CloBodyRat - 1.0) / (0.155 * state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->H);
    1113           44 :         Real64 EffectCloThermEff = 1.0 / (1.0 + 0.155 * state.dataThermalComforts->Hc * EffectCloUnit);
    1114           88 :         state.dataThermalComforts->CloPermeatEff =
    1115           44 :             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           44 :         Real64 ET = state.dataThermalComforts->SkinTemp - EffectSkinHeatLoss / (state.dataThermalComforts->H * EffectCloThermEff);
    1118              :         Real64 EnergyBalErrET;
    1119              :         while (true) {
    1120          865 :             Real64 StdVapPressET = CalcSatVapPressFromTempTorr(ET); // THE STANDARD VAPOR PRESSURE AT THE EFFECTIVE TEMP : StdVapPressET
    1121          865 :             EnergyBalErrET = EffectSkinHeatLoss - state.dataThermalComforts->H * EffectCloThermEff * (state.dataThermalComforts->SkinTemp - ET) -
    1122          865 :                              state.dataThermalComforts->SkinWetTot * LewisRatio * state.dataThermalComforts->Hc *
    1123          865 :                                  state.dataThermalComforts->CloPermeatEff * (state.dataThermalComforts->SatSkinVapPress - StdVapPressET / 2.0);
    1124          865 :             if (EnergyBalErrET >= 0.0) {
    1125           44 :                 break;
    1126              :             }
    1127          821 :             ET += 0.1;
    1128          821 :         }
    1129           44 :         state.dataThermalComforts->EffTemp = ET;
    1130              : 
    1131              :         // Standard effective temperature SET* standardized humidity.  Hc, CloUnit, StdAtm normalized for given ActLel AirVel
    1132              :         // Standard environment
    1133           44 :         Real64 StdHr = state.dataThermalComforts->Hr;
    1134              :         Real64 StdHc; // standard conv. heat tr. coeff. (level walking/still air)
    1135           44 :         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           44 :             StdHc = 5.66 * std::pow(ActMet - 0.85, 0.39);
    1139              :         }
    1140           44 :         if (StdHc <= 3.0) {
    1141           43 :             StdHc = 3.0;
    1142              :         }
    1143           44 :         Real64 StdH = StdHc + StdHr; // StdH Standard combined heat transfer coefficient
    1144              :         // standard MET - StdCloUnit relation gives SET* = 24 C when PMV = 0
    1145           44 :         Real64 StdCloUnit = 1.52 / (ActMet - WorkEff / ActLevelConv + 0.6944) - 0.1835;        // RCLOS
    1146           44 :         Real64 StdRClo = 0.155 * StdCloUnit;                                                   // RCLS
    1147           44 :         Real64 StdCloBodyRat = 1.0 + CloFac * StdCloUnit;                                      // FACLS
    1148           44 :         Real64 StdEffectCloThermEff = 1.0 / (1.0 + 0.155 * StdCloBodyRat * StdH * StdCloUnit); // FCLS
    1149           44 :         Real64 StdCloInsul = state.dataThermalComforts->CloInsul * StdHc / StdH * (1 - StdEffectCloThermEff) /
    1150           44 :                              (StdHc / StdH - state.dataThermalComforts->CloInsul * StdEffectCloThermEff);
    1151           44 :         Real64 StdREvap = 1.0 / (LewisRatio * StdCloBodyRat * StdHc);
    1152           44 :         Real64 StdREvapClo = StdRClo / (LewisRatio * StdCloInsul);
    1153           44 :         Real64 StdHEvap = 1.0 / (StdREvap + StdREvapClo);
    1154           44 :         Real64 StdRAir = 1.0 / (StdCloBodyRat * StdH);
    1155           44 :         Real64 StdHDry = 1.0 / (StdRAir + StdRClo);
    1156              : 
    1157              :         // Get a low approximation for SET* and solve balance equ. by iteration
    1158           44 :         Real64 StdEffectSkinHeatLoss = state.dataThermalComforts->DryHeatLoss + state.dataThermalComforts->EvapHeatLoss;
    1159           44 :         Real64 OldSET = round((state.dataThermalComforts->SkinTemp - StdEffectSkinHeatLoss / StdHDry) * 100) / 100;
    1160           44 :         Real64 delta = 0.0001;
    1161           44 :         Real64 err = 100.0;
    1162          138 :         while (std::abs(err) > 0.01) {
    1163           94 :             Real64 StdVapPressSET_1 = CalcSatVapPressFromTempTorr(OldSET); // StdVapPressSET *= VapPressConv;
    1164              :             Real64 EnergyBalErrSET_1 =
    1165           94 :                 StdEffectSkinHeatLoss - StdHDry * (state.dataThermalComforts->SkinTemp - OldSET) -
    1166           94 :                 state.dataThermalComforts->SkinWetTot * StdHEvap * (state.dataThermalComforts->SatSkinVapPress - StdVapPressSET_1 / 2.0);
    1167           94 :             Real64 StdVapPressSET_2 = CalcSatVapPressFromTempTorr(OldSET + delta);
    1168              :             Real64 EnergyBalErrSET_2 =
    1169           94 :                 StdEffectSkinHeatLoss - StdHDry * (state.dataThermalComforts->SkinTemp - (OldSET + delta)) -
    1170           94 :                 state.dataThermalComforts->SkinWetTot * StdHEvap * (state.dataThermalComforts->SatSkinVapPress - StdVapPressSET_2 / 2.0);
    1171           94 :             Real64 NewSET = OldSET - delta * EnergyBalErrSET_1 / (EnergyBalErrSET_2 - EnergyBalErrSET_1);
    1172           94 :             err = NewSET - OldSET;
    1173           94 :             OldSET = NewSET;
    1174              :         }
    1175           44 :         Real64 SET = OldSET;
    1176              :         // PMV*(PMVET in prgm) uses ET instead of OpTemp
    1177           44 :         state.dataThermalComforts->DryHeatLossET = StdH * StdEffectCloThermEff * (state.dataThermalComforts->SkinTemp - ET);
    1178              :         // SPMV*(PMVSET in prgm) uses SET instead of OpTemp
    1179           44 :         state.dataThermalComforts->DryHeatLossSET = StdH * StdEffectCloThermEff * (state.dataThermalComforts->SkinTemp - SET);
    1180           44 :         return SET;
    1181              :     }
    1182              : 
    1183            0 :     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            0 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1189            0 :              ++state.dataThermalComforts->PeopleNum) {
    1190            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1191            0 :             if (!people.Pierce) {
    1192            0 :                 continue;
    1193              :             }
    1194              : 
    1195            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1196              : 
    1197              :             // STEP 1: Get input (TA, TR, RH, VEL, CLO, MET, WME)
    1198            0 :             GetThermalComfortInputsASHRAE(state);
    1199              : 
    1200              :             // STEP 2: Calculate SET.
    1201            0 :             Real64 SET = CalcStandardEffectiveTemp(state,
    1202            0 :                                                    state.dataThermalComforts->AirTemp,
    1203            0 :                                                    state.dataThermalComforts->RadTemp,
    1204            0 :                                                    state.dataThermalComforts->RelHum,
    1205            0 :                                                    state.dataThermalComforts->AirVel,
    1206            0 :                                                    state.dataThermalComforts->ActMet,
    1207            0 :                                                    state.dataThermalComforts->CloUnit,
    1208            0 :                                                    state.dataThermalComforts->WorkEff);
    1209              : 
    1210              :             // STEP 3: Report SET related variables.
    1211              :             // Fanger's comfort equation. Thermal transfer coefficient to calculate PMV
    1212            0 :             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            0 :             state.dataThermalComforts->EvapHeatLossRegComf = (state.dataThermalComforts->IntHeatProd - ActLevelConv) * 0.42;
    1215            0 :             comfort.PiercePMVET =
    1216            0 :                 state.dataThermalComforts->ThermSensTransCoef *
    1217            0 :                 (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLossET -
    1218            0 :                  state.dataThermalComforts->EvapHeatLossDiff - state.dataThermalComforts->EvapHeatLossRegComf);
    1219            0 :             comfort.PiercePMVSET =
    1220            0 :                 state.dataThermalComforts->ThermSensTransCoef *
    1221            0 :                 (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLossSET -
    1222            0 :                  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            0 :             comfort.PierceDISC = 5.0 * (state.dataThermalComforts->EvapHeatLossRegSweat - state.dataThermalComforts->EvapHeatLossRegComf) /
    1226            0 :                                  (state.dataThermalComforts->EvapHeatLossMax - state.dataThermalComforts->EvapHeatLossRegComf -
    1227            0 :                                   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            0 :             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            0 :             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            0 :             if (state.dataThermalComforts->AvgBodyTemp > AvgBodyTempLow) {
    1238            0 :                 comfort.PierceTSENS = 4.7 * (state.dataThermalComforts->AvgBodyTemp - AvgBodyTempLow) / (AvgBodyTempHigh - AvgBodyTempLow);
    1239              : 
    1240              :             } else {
    1241            0 :                 comfort.PierceTSENS = 0.68175 * (state.dataThermalComforts->AvgBodyTemp - AvgBodyTempLow);
    1242            0 :                 comfort.PierceDISC = comfort.PierceTSENS;
    1243              :             }
    1244              : 
    1245            0 :             comfort.ThermalComfortMRT = state.dataThermalComforts->RadTemp;
    1246            0 :             comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1247            0 :             comfort.PierceSET = SET;
    1248              :         }
    1249            0 :     }
    1250              : 
    1251            2 :     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            4 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1257            2 :              ++state.dataThermalComforts->PeopleNum) {
    1258            2 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1259            2 :             if (!people.CoolingEffectASH55) {
    1260            0 :                 continue;
    1261              :             }
    1262              : 
    1263            2 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1264              : 
    1265              :             // Get input (TA, TR, RH, VEL, CLO, MET, WME)
    1266            2 :             GetThermalComfortInputsASHRAE(state);
    1267              : 
    1268              :             // Calculate elevated air cooling effect using the SET function.
    1269            2 :             Real64 CoolingEffect = 0;
    1270              :             Real64 CoolingEffectAdjustedPMV;
    1271            2 :             CalcCoolingEffectAdjustedPMV(state, CoolingEffect, CoolingEffectAdjustedPMV);
    1272              : 
    1273              :             // Report.
    1274            2 :             comfort.CoolingEffectASH55 = CoolingEffect;
    1275            2 :             comfort.CoolingEffectAdjustedPMVASH55 = CoolingEffectAdjustedPMV;
    1276            2 :             comfort.CoolingEffectAdjustedPPDASH55 = CalcFangerPPD(CoolingEffectAdjustedPMV);
    1277              :         }
    1278            2 :     }
    1279              : 
    1280            2 :     void CalcCoolingEffectAdjustedPMV(EnergyPlusData &state, Real64 &CoolingEffect, Real64 &CoolingEffectAdjustedPMV)
    1281              :     {
    1282            2 :         auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1283              : 
    1284              :         // Calculate SET without cooling effect.
    1285            2 :         Real64 RelAirVel = CalcRelativeAirVelocity(state.dataThermalComforts->AirVel, state.dataThermalComforts->ActMet);
    1286            2 :         Real64 SET = CalcStandardEffectiveTemp(state,
    1287            2 :                                                state.dataThermalComforts->AirTemp,
    1288            2 :                                                state.dataThermalComforts->RadTemp,
    1289            2 :                                                state.dataThermalComforts->RelHum,
    1290              :                                                RelAirVel,
    1291            2 :                                                state.dataThermalComforts->ActMet,
    1292            2 :                                                state.dataThermalComforts->CloUnit,
    1293            2 :                                                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            2 :         Real64 ASHRAE55PMV = CalcFangerPMV(state,
    1297            2 :                                            state.dataThermalComforts->AirTemp,
    1298            2 :                                            state.dataThermalComforts->RadTemp,
    1299            2 :                                            state.dataThermalComforts->RelHum,
    1300              :                                            RelAirVel,
    1301            2 :                                            state.dataThermalComforts->ActLevel,
    1302            2 :                                            state.dataThermalComforts->CloUnit,
    1303            2 :                                            state.dataThermalComforts->WorkEff);
    1304              : 
    1305            2 :         Real64 StillAirVel = 0.1;
    1306           30 :         auto ce_root_function = [&state, &StillAirVel, &SET](Real64 x) {
    1307          210 :             return CalcStandardEffectiveTemp(state,
    1308           30 :                                              state.dataThermalComforts->AirTemp - x,
    1309           30 :                                              state.dataThermalComforts->RadTemp - x,
    1310           30 :                                              state.dataThermalComforts->RelHum,
    1311              :                                              StillAirVel,
    1312           30 :                                              state.dataThermalComforts->ActMet,
    1313           30 :                                              state.dataThermalComforts->CloUnit,
    1314           30 :                                              state.dataThermalComforts->WorkEff) -
    1315           30 :                    SET;
    1316            2 :         };
    1317              : 
    1318           28 :         auto ce_root_termination = [](Real64 min, Real64 max) { return abs(max - min) <= 0.01; };
    1319            2 :         Real64 lowerBound = 0.0;
    1320            2 :         Real64 upperBound = 50.0;
    1321              : 
    1322              :         // We have yet another solver?
    1323              :         try {
    1324            2 :             std::pair<Real64, Real64> solverResult = boost::math::tools::bisect(ce_root_function, lowerBound, upperBound, ce_root_termination);
    1325            2 :             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            2 :         if (CoolingEffect > 0) {
    1335            2 :             CoolingEffectAdjustedPMV = CalcFangerPMV(state,
    1336            2 :                                                      state.dataThermalComforts->AirTemp - CoolingEffect,
    1337            2 :                                                      state.dataThermalComforts->RadTemp - CoolingEffect,
    1338            2 :                                                      state.dataThermalComforts->RelHum,
    1339              :                                                      StillAirVel,
    1340            2 :                                                      state.dataThermalComforts->ActLevel,
    1341            2 :                                                      state.dataThermalComforts->CloUnit,
    1342            2 :                                                      state.dataThermalComforts->WorkEff);
    1343              :         } else {
    1344            0 :             CoolingEffectAdjustedPMV = ASHRAE55PMV;
    1345              :         }
    1346            2 :     }
    1347              : 
    1348            1 :     void CalcThermalComfortAnkleDraftASH(EnergyPlusData &state)
    1349              :     {
    1350              :         // This subroutine calculates ASHRAE Ankle draft PPD
    1351              :         // Reference: ANSI/ASHRAE Standard 55-2017 Appendix I.
    1352              : 
    1353            2 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1354            1 :              ++state.dataThermalComforts->PeopleNum) {
    1355              : 
    1356            1 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1357            1 :             if (!people.AnkleDraftASH55) {
    1358            0 :                 continue;
    1359              :             }
    1360              : 
    1361            1 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1362              : 
    1363            1 :             GetThermalComfortInputsASHRAE(state);
    1364            1 :             Real64 RelAirVel = CalcRelativeAirVelocity(state.dataThermalComforts->AirVel, state.dataThermalComforts->ActMet);
    1365            1 :             Real64 PPD_AD = -1.0;
    1366            1 :             if (state.dataThermalComforts->ActMet < 1.3 && state.dataThermalComforts->CloUnit < 0.7 && RelAirVel < 0.2) {
    1367            1 :                 Real64 AnkleAirVel = people.ankleAirVelocitySched->getCurrentVal();
    1368            1 :                 Real64 PMV = CalcFangerPMV(state,
    1369            1 :                                            state.dataThermalComforts->AirTemp,
    1370            1 :                                            state.dataThermalComforts->RadTemp,
    1371            1 :                                            state.dataThermalComforts->RelHum,
    1372              :                                            RelAirVel,
    1373            1 :                                            state.dataThermalComforts->ActLevel,
    1374            1 :                                            state.dataThermalComforts->CloUnit,
    1375            1 :                                            state.dataThermalComforts->WorkEff);
    1376            1 :                 PPD_AD = (std::exp(-2.58 + 3.05 * AnkleAirVel - 1.06 * PMV) / (1 + std::exp(-2.58 + 3.05 * AnkleAirVel - 1.06 * PMV))) * 100.0;
    1377              : 
    1378              :             } else {
    1379            0 :                 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            1 :             comfort.AnkleDraftPPDASH55 = PPD_AD;
    1416              :         }
    1417            1 :     }
    1418              : 
    1419            0 :     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            0 :         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            0 :         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            0 :         TimeInterval = 1.0;
    1470            0 :         TSVMax = 4.0;
    1471            0 :         StartDayNum = 1;
    1472            0 :         LastDayNum = 1;
    1473            0 :         IncreDayNum = 1;
    1474            0 :         TimeExpos = 1.0;
    1475            0 :         TempDiffer = 0.5;
    1476              : 
    1477            0 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    1478            0 :              ++state.dataThermalComforts->PeopleNum) {
    1479              :             // THE NEXT SIX VARIABLES WILL BE READ IN FROM INPUT DECK
    1480            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    1481            0 :             if (!people.KSU) {
    1482            0 :                 continue;
    1483              :             }
    1484              : 
    1485            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    1486              : 
    1487            0 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    1488            0 :             auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
    1489              : 
    1490            0 :             state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
    1491            0 :             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            0 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
    1498            0 :             state.dataThermalComforts->RelHum =
    1499            0 :                 PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
    1500            0 :             state.dataThermalComforts->ActLevel = people.activityLevelSched->getCurrentVal() / BodySurfArea;
    1501            0 :             state.dataThermalComforts->WorkEff = people.workEffSched->getCurrentVal() * state.dataThermalComforts->ActLevel;
    1502              : 
    1503            0 :             switch (people.clothingType) {
    1504            0 :             case DataHeatBalance::ClothingType::InsulationSchedule: {
    1505            0 :                 state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
    1506            0 :             } break;
    1507            0 :             case DataHeatBalance::ClothingType::DynamicAshrae55: {
    1508            0 :                 comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1509            0 :                 comfort.ClothingValue = state.dataThermalComforts->CloUnit;
    1510            0 :                 DynamicClothingModel(state);
    1511            0 :                 state.dataThermalComforts->CloUnit = comfort.ClothingValue;
    1512            0 :             } break;
    1513            0 :             case DataHeatBalance::ClothingType::CalculationSchedule: {
    1514            0 :                 IntermediateClothing = people.clothingMethodSched->getCurrentVal();
    1515            0 :                 if (IntermediateClothing == 1.0) {
    1516            0 :                     state.dataThermalComforts->CloUnit = people.clothingSched->getCurrentVal();
    1517            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
    1518            0 :                 } else if (IntermediateClothing == 2.0) {
    1519            0 :                     comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1520            0 :                     comfort.ClothingValue = state.dataThermalComforts->CloUnit;
    1521            0 :                     DynamicClothingModel(state);
    1522            0 :                     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            0 :             } break;
    1529            0 :             default:
    1530            0 :                 ShowSevereError(state, format("PEOPLE=\"{}\", Incorrect Clothing Type", people.Name));
    1531              :             }
    1532              : 
    1533            0 :             state.dataThermalComforts->AirVel = people.airVelocitySched->getCurrentVal();
    1534            0 :             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            0 :             BodyWt = 70.0;
    1538            0 :             state.dataThermalComforts->CoreTemp = 37.0;
    1539            0 :             state.dataThermalComforts->SkinTemp = 31.0;
    1540              : 
    1541              :             //   CALCULATIONS NEEDED FOR THE PASSIVE STATE EQUATIONS
    1542            0 :             state.dataThermalComforts->CoreThermCap = 0.9 * BodyWt * 0.97 / BodySurfArea;
    1543            0 :             state.dataThermalComforts->SkinThermCap = 0.1 * BodyWt * 0.97 / BodySurfArea;
    1544              :             //   KERSLAKE'S FORMULA (0.05<AirVel<5. M/S)
    1545            0 :             if (state.dataThermalComforts->AirVel < 0.137) {
    1546            0 :                 state.dataThermalComforts->AirVel = 0.137;
    1547              :             }
    1548            0 :             state.dataThermalComforts->Hc = 8.3 * std::sqrt(state.dataThermalComforts->AirVel);
    1549            0 :             EmissAvg = RadSurfEff * CloEmiss + (1.0 - RadSurfEff) * 1.0;
    1550              :             //   IBERALL EQUATION
    1551            0 :             state.dataThermalComforts->Hr = EmissAvg * (3.87 + 0.031 * state.dataThermalComforts->RadTemp);
    1552            0 :             state.dataThermalComforts->H = state.dataThermalComforts->Hr + state.dataThermalComforts->Hc;
    1553            0 :             state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hc * state.dataThermalComforts->AirTemp +
    1554            0 :                                                  state.dataThermalComforts->Hr * state.dataThermalComforts->RadTemp) /
    1555            0 :                                                 state.dataThermalComforts->H;
    1556            0 :             state.dataThermalComforts->VapPress = CalcSatVapPressFromTemp(state.dataThermalComforts->AirTemp);
    1557            0 :             state.dataThermalComforts->VapPress *= state.dataThermalComforts->RelHum;
    1558            0 :             state.dataThermalComforts->CloBodyRat = 1.0 + 0.2 * state.dataThermalComforts->CloUnit;
    1559            0 :             state.dataThermalComforts->CloThermEff =
    1560            0 :                 1.0 / (1.0 + 0.155 * state.dataThermalComforts->H * state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->CloUnit);
    1561            0 :             state.dataThermalComforts->CloPermeatEff = 1.0 / (1.0 + 0.143 * state.dataThermalComforts->Hc * state.dataThermalComforts->CloUnit);
    1562              :             //  BASIC INFORMATION FOR THERMAL SENSATION.
    1563            0 :             IntHeatProdMet = state.dataThermalComforts->IntHeatProd / ActLevelConv;
    1564            0 :             IntHeatProdMetMax = max(1.0, IntHeatProdMet);
    1565            0 :             ThermCndctNeut = 12.05 * std::exp(0.2266 * (IntHeatProdMetMax - 1.0));
    1566            0 :             SkinWetNeut = 0.02 + 0.4 * (1.0 - std::exp(-0.6 * (IntHeatProdMetMax - 1.0)));
    1567            0 :             ThermCndctMin = (ThermCndctNeut - 5.3) * 0.26074074 + 5.3;
    1568            0 :             Real64 const ThemCndct_75_fac(1.0 / (75.0 - ThermCndctNeut));
    1569            0 :             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            0 :             assert(IncreDayNum > 0); // Autodesk:F2C++ Loop setup assumption
    1573            0 :             for (NumDay = StartDayNum; NumDay <= LastDayNum; NumDay += IncreDayNum) {
    1574              :                 //  INITIAL CONDITIONS IN AN EXPOSURE
    1575            0 :                 DayNum = double(NumDay);
    1576            0 :                 state.dataThermalComforts->Time = 0.0;
    1577            0 :                 state.dataThermalComforts->TimeChange = 0.01;
    1578            0 :                 SweatSuppFac = 1.0;
    1579            0 :                 state.dataThermalComforts->Temp(1) = state.dataThermalComforts->CoreTemp;
    1580            0 :                 state.dataThermalComforts->Temp(2) = state.dataThermalComforts->SkinTemp;
    1581            0 :                 state.dataThermalComforts->Coeff(1) = state.dataThermalComforts->Coeff(2) = 0.0;
    1582              :                 //  PHYSIOLOGICAL ADJUSTMENTS IN HEAT ACCLIMATION.
    1583            0 :                 state.dataThermalComforts->AcclPattern = 1.0 - std::exp(-0.12 * (DayNum - 1.0));
    1584            0 :                 state.dataThermalComforts->CoreTempNeut = 36.9 - 0.6 * state.dataThermalComforts->AcclPattern;
    1585            0 :                 state.dataThermalComforts->SkinTempNeut = 33.8 - 1.6 * state.dataThermalComforts->AcclPattern;
    1586            0 :                 state.dataThermalComforts->ActLevel -= 0.07 * state.dataThermalComforts->ActLevel * state.dataThermalComforts->AcclPattern;
    1587            0 :                 Real64 const SkinTempNeut_fac(1.0 / (1.0 - SkinWetNeut));
    1588              :                 //  CALCULATION OF CoreTempChange/TempChange & SkinTempChange/TempChange
    1589            0 :                 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            0 :                     SkinWetFac = (state.dataThermalComforts->SkinWetSweat - SkinWetNeut) * SkinTempNeut_fac;
    1594            0 :                     state.dataThermalComforts->VasodilationFac = (state.dataThermalComforts->ThermCndct - ThermCndctNeut) * ThemCndct_75_fac;
    1595            0 :                     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            0 :                     if (state.dataThermalComforts->VasodilationFac < 0) {
    1600            0 :                         comfort.KsuTSV = -1.46153 * state.dataThermalComforts->VasoconstrictFac +
    1601            0 :                                          3.74721 * pow_2(state.dataThermalComforts->VasoconstrictFac) -
    1602            0 :                                          6.168856 * pow_3(state.dataThermalComforts->VasoconstrictFac);
    1603              :                     } else {
    1604            0 :                         comfort.KsuTSV = (5.0 - 6.56 * (state.dataThermalComforts->RelHum - 0.50)) * SkinWetFac;
    1605            0 :                         if (comfort.KsuTSV > TSVMax) {
    1606            0 :                             comfort.KsuTSV = TSVMax;
    1607              :                         }
    1608              :                     }
    1609              : 
    1610            0 :                     comfort.ThermalComfortMRT = state.dataThermalComforts->RadTemp;
    1611            0 :                     comfort.ThermalComfortOpTemp = (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
    1612              : 
    1613            0 :                     state.dataThermalComforts->CoreTemp = state.dataThermalComforts->Temp(1);
    1614            0 :                     state.dataThermalComforts->SkinTemp = state.dataThermalComforts->Temp(2);
    1615            0 :                     state.dataThermalComforts->EvapHeatLossSweatPrev = state.dataThermalComforts->EvapHeatLossSweat;
    1616              : 
    1617            0 :                     RKG(state,
    1618              :                         TempIndiceNum,
    1619            0 :                         state.dataThermalComforts->TimeChange,
    1620            0 :                         state.dataThermalComforts->Time,
    1621            0 :                         state.dataThermalComforts->Temp,
    1622            0 :                         state.dataThermalComforts->TempChange,
    1623            0 :                         state.dataThermalComforts->Coeff);
    1624              : 
    1625            0 :                     if (state.dataThermalComforts->Time > TimeExpos) {
    1626            0 :                         break;
    1627              :                     }
    1628              :                 }
    1629              :             }
    1630              :         }
    1631            0 :     }
    1632              : 
    1633            0 :     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            0 :         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            0 :         CoreSignalWarm = state.dataThermalComforts->CoreTemp - 36.98;
    1701            0 :         SkinSignalWarm = state.dataThermalComforts->SkinTemp - 33.8;
    1702            0 :         SkinSignalCold = 32.1 - state.dataThermalComforts->SkinTemp;
    1703            0 :         CoreSignalSkinSens = state.dataThermalComforts->CoreTemp - 35.15;
    1704            0 :         CoreSignalWarmMax = max(0.0, CoreSignalWarm);
    1705            0 :         SkinSignalWarmMax = max(0.0, SkinSignalWarm);
    1706            0 :         SkinSignalColdMax = max(0.0, SkinSignalCold);
    1707              : 
    1708              :         // SIGNALS FOR EvapHeatLossSweat.
    1709            0 :         CoreTempSweat = state.dataThermalComforts->CoreTemp;
    1710            0 :         if (CoreTempSweat > 38.29) {
    1711            0 :             CoreTempSweat = 38.29;
    1712              :         }
    1713            0 :         CoreSignalSweatWarm = CoreTempSweat - state.dataThermalComforts->CoreTempNeut;
    1714            0 :         SkinTempSweat = state.dataThermalComforts->SkinTemp;
    1715            0 :         if (SkinTempSweat > 36.1) {
    1716            0 :             SkinTempSweat = 36.1;
    1717              :         }
    1718            0 :         SkinSignalSweatWarm = SkinTempSweat - state.dataThermalComforts->SkinTempNeut;
    1719            0 :         CoreSignalSweatMax = max(0.0, CoreSignalSweatWarm);
    1720            0 :         SkinSignalSweatMax = max(0.0, SkinSignalSweatWarm);
    1721            0 :         SkinSignalSweatCold = 33.37 - state.dataThermalComforts->SkinTemp;
    1722            0 :         if (state.dataThermalComforts->SkinTempNeut < 33.37) {
    1723            0 :             SkinSignalSweatCold = state.dataThermalComforts->SkinTempNeut - state.dataThermalComforts->SkinTemp;
    1724              :         }
    1725            0 :         SkinSignalSweatColdMax = max(0.0, SkinSignalSweatCold);
    1726              : 
    1727              :         // SIGNALS FOR SHIVERING.
    1728            0 :         CoreSignalShiv = 36.9 - state.dataThermalComforts->CoreTemp;
    1729            0 :         SkinSignalShiv = 32.5 - state.dataThermalComforts->SkinTemp;
    1730            0 :         CoreSignalShivMax = max(0.0, CoreSignalShiv);
    1731            0 :         SkinSignalShivMax = max(0.0, SkinSignalShiv);
    1732              : 
    1733              :         // CONTROLLING FUNCTIONS :
    1734              :         // SHIVERING RESPONSE IN W/M**2.
    1735            0 :         state.dataThermalComforts->ShivResponse = 20.0 * CoreSignalShivMax * SkinSignalShivMax + 5.0 * SkinSignalShivMax;
    1736            0 :         if (state.dataThermalComforts->CoreTemp >= 37.1) {
    1737            0 :             state.dataThermalComforts->ShivResponse = 0.0;
    1738              :         }
    1739              : 
    1740              :         // SWEAT FUNCTION IN W/M**2.
    1741            0 :         WeighFac = 260.0 + 70.0 * state.dataThermalComforts->AcclPattern;
    1742            0 :         SweatCtrlFac = 1.0 + 0.05 * std::pow(SkinSignalSweatColdMax, 2.4);
    1743              : 
    1744              :         // EvapHeatLossDrySweat = SWEAT WHEN SkinWetTot < 0.4.
    1745            0 :         EvapHeatLossDrySweat =
    1746            0 :             ((WeighFac * CoreSignalSweatMax + 0.1 * WeighFac * SkinSignalSweatMax) * std::exp(SkinSignalSweatMax / 8.5)) / SweatCtrlFac;
    1747              : 
    1748              :         // MAXIMUM EVAPORATIVE POWER, EvapHeatLossMax, IN W/M**2.
    1749            0 :         state.dataThermalComforts->SkinVapPress = CalcSatVapPressFromTemp(state.dataThermalComforts->SkinTemp);
    1750            0 :         state.dataThermalComforts->EvapHeatLossMax = 2.2 * state.dataThermalComforts->Hc *
    1751            0 :                                                      (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress) *
    1752            0 :                                                      state.dataThermalComforts->CloPermeatEff;
    1753            0 :         if (state.dataThermalComforts->EvapHeatLossMax > 0.0) {
    1754            0 :             state.dataThermalComforts->SkinWetSweat = EvapHeatLossDrySweat / state.dataThermalComforts->EvapHeatLossMax;
    1755            0 :             state.dataThermalComforts->EvapHeatLossDiff = 0.408 * (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress);
    1756            0 :             state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->SkinWetSweat * state.dataThermalComforts->EvapHeatLossMax +
    1757            0 :                                                       (1.0 - state.dataThermalComforts->SkinWetSweat) * state.dataThermalComforts->EvapHeatLossDiff;
    1758            0 :             state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
    1759            0 :             if (state.dataThermalComforts->Time == 0.0) {
    1760            0 :                 state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossDrySweat;
    1761            0 :                 state.dataThermalComforts->EvapHeatLossSweatPrev = EvapHeatLossDrySweat;
    1762              :             }
    1763            0 :             if (state.dataThermalComforts->SkinWetTot > 0.4) {
    1764              : 
    1765              :                 // ITERATION  FOR SWEAT WHEN SkinWetTot IS GREATER THAT 0.4.
    1766            0 :                 state.dataThermalComforts->IterNum = 0;
    1767            0 :                 if (state.dataThermalComforts->SkinWetSweat > 1.0) {
    1768            0 :                     state.dataThermalComforts->SkinWetSweat = 1.0;
    1769              :                 }
    1770              :                 while (true) {
    1771            0 :                     EvapHeatLossSweatEst = state.dataThermalComforts->EvapHeatLossSweatPrev;
    1772            0 :                     state.dataThermalComforts->SkinWetSweat = EvapHeatLossSweatEst / state.dataThermalComforts->EvapHeatLossMax;
    1773              : 
    1774            0 :                     if (state.dataThermalComforts->SkinWetSweat > 1.0) {
    1775            0 :                         state.dataThermalComforts->SkinWetSweat = 1.0;
    1776              :                     }
    1777              : 
    1778            0 :                     state.dataThermalComforts->EvapHeatLossDiff =
    1779            0 :                         0.408 * (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress);
    1780            0 :                     state.dataThermalComforts->EvapHeatLoss =
    1781            0 :                         (1.0 - state.dataThermalComforts->SkinWetTot) * state.dataThermalComforts->EvapHeatLossDiff +
    1782            0 :                         state.dataThermalComforts->EvapHeatLossSweat;
    1783            0 :                     state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
    1784              : 
    1785            0 :                     if (state.dataThermalComforts->SkinWetTot > 1.0) {
    1786            0 :                         state.dataThermalComforts->SkinWetTot = 1.0;
    1787              :                     }
    1788              : 
    1789            0 :                     SkinWetSignal = max(0.0, state.dataThermalComforts->SkinWetTot - 0.4);
    1790            0 :                     SweatSuppFac = 0.5 + 0.5 * std::exp(-5.6 * SkinWetSignal);
    1791            0 :                     EvapHeatLossSweatEstNew = SweatSuppFac * EvapHeatLossDrySweat;
    1792              : 
    1793            0 :                     if (state.dataThermalComforts->IterNum == 0) {
    1794            0 :                         state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossSweatEstNew;
    1795              :                     }
    1796              : 
    1797            0 :                     Err = EvapHeatLossSweatEst - EvapHeatLossSweatEstNew;
    1798              : 
    1799            0 :                     if (state.dataThermalComforts->IterNum != 0) {
    1800            0 :                         if ((ErrPrev * Err) < 0.0) {
    1801            0 :                             state.dataThermalComforts->EvapHeatLossSweat = (EvapHeatLossSweatEst + EvapHeatLossSweatEstNew) / 2.0;
    1802              :                         }
    1803            0 :                         if ((ErrPrev * Err) >= 0.0) {
    1804            0 :                             state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossSweatEstNew;
    1805              :                         }
    1806              :                     }
    1807              : 
    1808              :                     // STOP CRITERION FOR THE ITERATION.
    1809            0 :                     if ((std::abs(Err) <= 0.5) || (state.dataThermalComforts->IterNum >= 10)) {
    1810            0 :                         break;
    1811              :                     }
    1812            0 :                     ++state.dataThermalComforts->IterNum;
    1813            0 :                     state.dataThermalComforts->EvapHeatLossSweatPrev = state.dataThermalComforts->EvapHeatLossSweat;
    1814            0 :                     ErrPrev = Err;
    1815              :                 }
    1816              : 
    1817              :             } else {
    1818            0 :                 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            0 :         SkinCndctDilation = 42.45 * CoreSignalWarmMax + 8.15 * std::pow(CoreSignalSkinSens, 0.8) * SkinSignalWarmMax;
    1832            0 :         SkinCndctConstriction = 1.0 + 0.4 * SkinSignalColdMax;
    1833              :         // ThermCndct IS EQUIVALENT TO KS
    1834            0 :         state.dataThermalComforts->ThermCndct = 5.3 + (6.75 + SkinCndctDilation) / SkinCndctConstriction;
    1835            0 :         SkinCndctMax = 75.0 + 10.0 * state.dataThermalComforts->AcclPattern;
    1836            0 :         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            0 :         ActLevelTot = state.dataThermalComforts->ActLevel + state.dataThermalComforts->ShivResponse;
    1843            0 :         IntHeatProdTot = ActLevelTot - state.dataThermalComforts->WorkEff;
    1844              :         // RESPIRATION HEAT LOSS, RespHeatLoss, IN W/M**0.
    1845            0 :         state.dataThermalComforts->LatRespHeatLoss = 0.0023 * ActLevelTot * (44.0 - state.dataThermalComforts->VapPress);
    1846            0 :         state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevelTot * (34.0 - state.dataThermalComforts->AirTemp);
    1847            0 :         state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
    1848              :         // HEAT FLOW FROM CORE TO SKIN, HeatFlow, IN W/M**2.
    1849            0 :         state.dataThermalComforts->HeatFlow =
    1850            0 :             state.dataThermalComforts->ThermCndct * (state.dataThermalComforts->CoreTemp - state.dataThermalComforts->SkinTemp);
    1851              :         // TempChange(1) = CoreTempChange/TempChange, IN C/HR.
    1852            0 :         TempChange(1) = (IntHeatProdTot - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->HeatFlow) /
    1853            0 :                         state.dataThermalComforts->CoreThermCap;
    1854            0 :         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            0 :         state.dataThermalComforts->DryHeatLoss = state.dataThermalComforts->H * state.dataThermalComforts->CloBodyRat *
    1860            0 :                                                  state.dataThermalComforts->CloThermEff *
    1861            0 :                                                  (state.dataThermalComforts->SkinTemp - state.dataThermalComforts->OpTemp);
    1862              :         // TempChange(2) = SkinTempChange/TempChange, IN C/HR.
    1863            0 :         TempChange(2) = (state.dataThermalComforts->HeatFlow - state.dataThermalComforts->EvapHeatLoss - state.dataThermalComforts->DryHeatLoss) /
    1864            0 :                         state.dataThermalComforts->SkinThermCap;
    1865            0 :     }
    1866              : 
    1867            0 :     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            0 :         EP_SIZE_CHECK(Y, NEQ);
    1888            0 :         EP_SIZE_CHECK(DY, NEQ);
    1889            0 :         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            0 :         H2 = 0.5 * H;
    1899              : 
    1900            0 :         DERIV(state, NEQ, Y, DY);
    1901            0 :         for (I = 1; I <= NEQ; ++I) {
    1902            0 :             B = H2 * DY(I) - C(I);
    1903            0 :             Y(I) += B;
    1904            0 :             C(I) += 3.0 * B - H2 * DY(I);
    1905              :         }
    1906              : 
    1907            0 :         X += H2;
    1908              : 
    1909            0 :         for (J = 0; J < 2; ++J) {
    1910            0 :             DERIV(state, NEQ, Y, DY);
    1911            0 :             for (I = 1; I <= NEQ; ++I) {
    1912            0 :                 B = A[J] * (H * DY(I) - C(I));
    1913            0 :                 Y(I) += B;
    1914            0 :                 C(I) += 3.0 * B - A[J] * H * DY(I);
    1915              :             }
    1916              :         }
    1917              : 
    1918            0 :         X += H2;
    1919            0 :         DERIV(state, NEQ, Y, DY);
    1920              : 
    1921            0 :         for (I = 1; I <= NEQ; ++I) {
    1922            0 :             B = (H * DY(I) - 2.0 * C(I)) / 6.0;
    1923            0 :             Y(I) += B;
    1924            0 :             C(I) += 3.0 * B - H2 * DY(I);
    1925              :         }
    1926              : 
    1927            0 :         DERIV(state, NEQ, Y, DY);
    1928            0 :     }
    1929              : 
    1930          104 :     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          104 :         Real64 constexpr AngleFacLimit(0.01);                                  // To set the limit of sum of angle factors
    1939              : 
    1940          104 :         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          104 :         auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
    1945              : 
    1946          104 :         cCurrentModuleObject = "ComfortViewFactorAngles";
    1947          104 :         int NumOfAngleFactorLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
    1948          104 :         state.dataThermalComforts->AngleFactorList.allocate(NumOfAngleFactorLists);
    1949              : 
    1950          105 :         for (int Item = 1; Item <= NumOfAngleFactorLists; ++Item) {
    1951              : 
    1952            1 :             Real64 AllAngleFacSummed = 0.0; // Sum of angle factors in each zone
    1953            1 :             auto &thisAngFacList(state.dataThermalComforts->AngleFactorList(Item));
    1954              : 
    1955            2 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
    1956              :                                                                      cCurrentModuleObject,
    1957              :                                                                      Item,
    1958            1 :                                                                      state.dataIPShortCut->cAlphaArgs,
    1959              :                                                                      NumAlphas,
    1960            1 :                                                                      state.dataIPShortCut->rNumericArgs,
    1961              :                                                                      NumNumbers,
    1962              :                                                                      IOStatus,
    1963            1 :                                                                      state.dataIPShortCut->lNumericFieldBlanks,
    1964            1 :                                                                      state.dataIPShortCut->lAlphaFieldBlanks,
    1965            1 :                                                                      state.dataIPShortCut->cAlphaFieldNames,
    1966            1 :                                                                      state.dataIPShortCut->cNumericFieldNames);
    1967              : 
    1968            1 :             thisAngFacList.Name = state.dataIPShortCut->cAlphaArgs(1); // no need for verification/uniqueness.
    1969              : 
    1970            1 :             thisAngFacList.TotAngleFacSurfaces = NumNumbers;
    1971            1 :             thisAngFacList.SurfaceName.allocate(thisAngFacList.TotAngleFacSurfaces);
    1972            1 :             thisAngFacList.SurfacePtr.allocate(thisAngFacList.TotAngleFacSurfaces);
    1973            1 :             thisAngFacList.AngleFactor.allocate(thisAngFacList.TotAngleFacSurfaces);
    1974              : 
    1975          101 :             for (int SurfNum = 1; SurfNum <= thisAngFacList.TotAngleFacSurfaces; ++SurfNum) {
    1976          100 :                 thisAngFacList.SurfaceName(SurfNum) = state.dataIPShortCut->cAlphaArgs(SurfNum + 1);
    1977          100 :                 thisAngFacList.SurfacePtr(SurfNum) = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(SurfNum + 1), state.dataSurface->Surface);
    1978          100 :                 thisAngFacList.AngleFactor(SurfNum) = state.dataIPShortCut->rNumericArgs(SurfNum);
    1979              :                 // Error trap for surfaces that do not exist or surfaces not in the zone
    1980          100 :                 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          100 :                     auto &thisSurf = state.dataSurface->Surface(thisAngFacList.SurfacePtr(SurfNum));
    1996          100 :                     if (SurfNum == 1) {
    1997            1 :                         thisAngFacList.EnclosurePtr = thisSurf.RadEnclIndex; // Save enclosure num of first surface
    1998              :                     }
    1999          100 :                     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          100 :                 AllAngleFacSummed += thisAngFacList.AngleFactor(SurfNum);
    2017              :             }
    2018              : 
    2019            1 :             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          104 :         if (ErrorsFound) {
    2030            0 :             ShowFatalError(state, "GetAngleFactorList: Program terminated due to preceding errors.");
    2031              :         }
    2032              : 
    2033          154 :         for (int Item = 1; Item <= state.dataHeatBal->TotPeople; ++Item) {
    2034           50 :             auto &thisPeople = state.dataHeatBal->People(Item);
    2035           50 :             if (thisPeople.MRTCalcType != DataHeatBalance::CalcMRT::AngleFactor) {
    2036           50 :                 continue;
    2037              :             }
    2038            0 :             thisPeople.AngleFactorListPtr = Util::FindItemInList(thisPeople.AngleFactorListName, state.dataThermalComforts->AngleFactorList);
    2039            0 :             int WhichAFList = thisPeople.AngleFactorListPtr;
    2040            0 :             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            0 :                 auto &thisAngFacList = state.dataThermalComforts->AngleFactorList(WhichAFList);
    2046            0 :                 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          104 :         if (ErrorsFound) {
    2061            0 :             ShowFatalError(state, "GetAngleFactorList: Program terminated due to preceding errors.");
    2062              :         }
    2063          104 :     }
    2064              : 
    2065            1 :     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            1 :         Real64 SurfTempEmissAngleFacSummed = 0.0;
    2077            1 :         Real64 SumSurfaceEmissAngleFactor = 0.0;
    2078              : 
    2079            1 :         auto &thisAngFacList(state.dataThermalComforts->AngleFactorList(AngleFacNum));
    2080              : 
    2081            4 :         for (int SurfNum = 1; SurfNum <= thisAngFacList.TotAngleFacSurfaces; ++SurfNum) {
    2082            3 :             Real64 SurfaceTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(thisAngFacList.SurfacePtr(SurfNum)) + Constant::Kelvin;
    2083              :             Real64 SurfEAF =
    2084            3 :                 state.dataConstruction->Construct(state.dataSurface->Surface(thisAngFacList.SurfacePtr(SurfNum)).Construction).InsideAbsorpThermal *
    2085            3 :                 thisAngFacList.AngleFactor(SurfNum);
    2086            3 :             SurfTempEmissAngleFacSummed += SurfEAF * pow_4(SurfaceTemp);
    2087            3 :             SumSurfaceEmissAngleFactor += SurfEAF;
    2088              :         }
    2089              : 
    2090            1 :         CalcAngleFactorMRT = root_4(SurfTempEmissAngleFacSummed / SumSurfaceEmissAngleFactor) - Constant::Kelvin;
    2091              : 
    2092            1 :         return CalcAngleFactorMRT;
    2093              :     }
    2094              : 
    2095           18 :     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           18 :         Real64 CalcSurfaceWeightedMRT = 0.0;
    2109              : 
    2110              :         // Initialize ZoneAESum for all zones and SurfaceAE for all surfaces at the start of the simulation
    2111           18 :         if (state.dataThermalComforts->FirstTimeSurfaceWeightedFlag) {
    2112           18 :             state.dataThermalComforts->FirstTimeError = true;
    2113           18 :             state.dataThermalComforts->FirstTimeSurfaceWeightedFlag = false;
    2114           36 :             for (auto const &thisRadEnclosure : state.dataViewFactor->EnclRadInfo) {
    2115          180 :                 for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2116          162 :                     auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
    2117          162 :                     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          180 :                 for (int const SurfNum1 : thisRadEnclosure.SurfacePtr) {
    2122          162 :                     auto &thisSurface1 = state.dataSurface->Surface(SurfNum1);
    2123          162 :                     thisSurface1.enclAESum = 0.0;
    2124         1944 :                     for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2125         1782 :                         if (SurfNum2 == SurfNum1) {
    2126          162 :                             continue;
    2127              :                         }
    2128         1620 :                         auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
    2129         1620 :                         thisSurface1.enclAESum += thisSurface2.AE;
    2130              :                     }
    2131              :                 }
    2132           18 :             }
    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           18 :         Real64 sumAET = 0.0; // Intermediate calculational variable (area*emissivity*T) sum
    2137              : 
    2138           18 :         auto &thisSurface = state.dataSurface->Surface(SurfNum);
    2139           18 :         auto &thisRadEnclosure = state.dataViewFactor->EnclRadInfo(thisSurface.RadEnclIndex);
    2140              :         // Recalc SurfaceEnclAESum only if needed due to window shades or EMS
    2141           18 :         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          180 :         for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
    2153          162 :             if (SurfNum2 == SurfNum) {
    2154           18 :                 continue;
    2155              :             }
    2156          144 :             sumAET += state.dataSurface->Surface(SurfNum2).AE * state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum2);
    2157              :         }
    2158              : 
    2159              :         // Now weight the MRT
    2160           18 :         auto &thisSurfaceTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum);
    2161           18 :         if (thisSurface.enclAESum > 0.01) {
    2162           18 :             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           18 :             if (AverageWithSurface) {
    2166            9 :                 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           18 :         return CalcSurfaceWeightedMRT;
    2187              :     }
    2188              : 
    2189            0 :     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            0 :         Real64 const XT(Temp / 100.0);
    2208            0 :         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         3737 :     Real64 CalcSatVapPressFromTempTorr(Real64 const Temp)
    2215              :     {
    2216              :         // Helper function for pierceSET calculates Saturated Vapor Pressure (Torr) at Temperature T (°C)
    2217         3737 :         return std::exp(18.6686 - 4030.183 / (Temp + 235.0));
    2218              :     }
    2219              : 
    2220         1832 :     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         1832 :         Real64 CalcRadTemp = 0.0;
    2255         1832 :         Real64 constexpr AreaEff = 1.8;                    // Effective area of a "standard" person in meters squared
    2256         1832 :         Real64 constexpr StefanBoltzmannConst = 5.6697e-8; // Stefan-Boltzmann constant in W/(m2*K4)
    2257              : 
    2258         1832 :         auto &thisPeople = state.dataHeatBal->People(PeopleListNum);
    2259         1832 :         switch (thisPeople.MRTCalcType) {
    2260         1832 :         case DataHeatBalance::CalcMRT::EnclosureAveraged: {
    2261         1832 :             int enclNum = state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum;
    2262         1832 :             state.dataThermalComforts->RadTemp = state.dataViewFactor->EnclRadInfo(enclNum).MRT;
    2263         1832 :         } break;
    2264            0 :         case DataHeatBalance::CalcMRT::SurfaceWeighted: {
    2265            0 :             state.dataThermalComforts->RadTemp = CalcSurfaceWeightedMRT(state, thisPeople.SurfacePtr);
    2266            0 :         } break;
    2267            0 :         case DataHeatBalance::CalcMRT::AngleFactor: {
    2268            0 :             state.dataThermalComforts->RadTemp = CalcAngleFactorMRT(state, thisPeople.AngleFactorListPtr);
    2269            0 :         } 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         1832 :         state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) =
    2277         1832 :             state.dataHeatBalFanSys->ZoneQHTRadSysToPerson(state.dataThermalComforts->ZoneNum) +
    2278         1832 :             state.dataHeatBalFanSys->ZoneQCoolingPanelToPerson(state.dataThermalComforts->ZoneNum) +
    2279         1832 :             state.dataHeatBalFanSys->ZoneQHWBaseboardToPerson(state.dataThermalComforts->ZoneNum) +
    2280         1832 :             state.dataHeatBalFanSys->ZoneQSteamBaseboardToPerson(state.dataThermalComforts->ZoneNum) +
    2281         1832 :             state.dataHeatBalFanSys->ZoneQElecBaseboardToPerson(state.dataThermalComforts->ZoneNum);
    2282         1832 :         if (state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) > 0.0) {
    2283            0 :             state.dataThermalComforts->RadTemp += Constant::Kelvin; // Convert to Kelvin
    2284            0 :             state.dataThermalComforts->RadTemp =
    2285            0 :                 root_4(pow_4(state.dataThermalComforts->RadTemp) +
    2286            0 :                        (state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) / AreaEff / StefanBoltzmannConst));
    2287            0 :             state.dataThermalComforts->RadTemp -= Constant::Kelvin; // Convert back to Celsius
    2288              :         }
    2289              : 
    2290         1832 :         CalcRadTemp = state.dataThermalComforts->RadTemp;
    2291              : 
    2292         1832 :         return CalcRadTemp;
    2293              :     }
    2294              : 
    2295        18995 :     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        18995 :         state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer = 0.0;
    2320        18995 :         state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter = 0.0;
    2321        18995 :         state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either = 0.0;
    2322              : 
    2323              :         // assume the zone is unoccupied
    2324        45070 :         for (auto &e : state.dataThermalComforts->ThermalComfortInASH55) {
    2325        26075 :             e.ZoneIsOccupied = false;
    2326        18995 :         }
    2327              :         // loop through the people objects and determine if the zone is currently occupied
    2328        29435 :         for (auto const &people : state.dataHeatBal->People) {
    2329        10440 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    2330        10440 :             NumberOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    2331        10440 :             if (NumberOccupants > 0) {
    2332         7624 :                 state.dataThermalComforts->ThermalComfortInASH55(state.dataThermalComforts->ZoneNum).ZoneIsOccupied = true;
    2333              :             }
    2334        18995 :         }
    2335              :         // loop through the zones and determine if in simple ashrae 55 comfort regions
    2336              :         // MJW MRT ToDo: Extend ASHRAE 55 to spaces?
    2337        45070 :         for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2338        26075 :             if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    2339         7624 :                 auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone);
    2340              :                 // keep track of occupied hours
    2341         7624 :                 state.dataThermalComforts->ZoneOccHrs(iZone) += state.dataGlobal->TimeStepZone;
    2342         7624 :                 Real64 CurAirTemp = thisZoneHB.ZTAVComf;
    2343         7624 :                 if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    2344            0 :                     if (state.dataRoomAir->IsZoneDispVent3Node(iZone) || state.dataRoomAir->IsZoneUFAD(iZone)) {
    2345            0 :                         CurAirTemp = state.dataRoomAir->TCMF(iZone);
    2346              :                     }
    2347              :                 }
    2348         7624 :                 Real64 CurMeanRadiantTemp = thisZoneHB.MRT;
    2349         7624 :                 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         7624 :                     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         7624 :                     isInQuadrilateral(OperTemp, thisZoneHB.airHumRatAvgComf, 21.7, 0.0, 19.6, 0.012, 23.9, 0.012, 26.3, 0.0);
    2383         7624 :                 if (isComfortableWithSummerClothes) {
    2384         1342 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = 0.0;
    2385              :                 } else {
    2386         6282 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = state.dataGlobal->TimeStepZone;
    2387         6282 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer += state.dataGlobal->TimeStepZone;
    2388         6282 :                     state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer = state.dataGlobal->TimeStepZone;
    2389              :                 }
    2390         7624 :                 if (isComfortableWithWinterClothes) {
    2391         1427 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = 0.0;
    2392              :                 } else {
    2393         6197 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = state.dataGlobal->TimeStepZone;
    2394         6197 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter += state.dataGlobal->TimeStepZone;
    2395         6197 :                     state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter = state.dataGlobal->TimeStepZone;
    2396              :                 }
    2397         7624 :                 if (isComfortableWithSummerClothes || isComfortableWithWinterClothes) {
    2398         2654 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = 0.0;
    2399              :                 } else {
    2400         4970 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = state.dataGlobal->TimeStepZone;
    2401         4970 :                     state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither += state.dataGlobal->TimeStepZone;
    2402         4970 :                     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        18451 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = 0.0;
    2407        18451 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = 0.0;
    2408        18451 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = 0.0;
    2409              :             }
    2410              :         }
    2411              :         // accumulate total time
    2412        18995 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer;
    2413        18995 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter;
    2414        18995 :         state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either;
    2415              : 
    2416        18995 :         if (state.dataGlobal->EndDesignDayEnvrnsFlag) {
    2417           87 :             allowedHours = double(state.dataGlobal->NumOfDayInEnvrn) * 24.0 * 0.04;
    2418              :             // first check if warning should be printed
    2419           87 :             showWarning = false;
    2420          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2421          118 :                 if (state.dataThermalComforts->ThermalComfortInASH55(iZone).Enable55Warning) {
    2422            2 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither > allowedHours) {
    2423            2 :                         showWarning = true;
    2424              :                     }
    2425              :                 }
    2426              :             }
    2427              :             // if any zones should be warning print it out
    2428           87 :             if (showWarning) {
    2429            2 :                 ShowWarningError(state, format("More than 4% of time ({:.1R} hours) uncomfortable in one or more zones ", allowedHours));
    2430            4 :                 ShowContinueError(state, "Based on ASHRAE 55-2004 graph (Section 5.2.1.1)");
    2431            2 :                 if (state.dataEnvrn->RunPeriodEnvironment) {
    2432            0 :                     ShowContinueError(state,
    2433            0 :                                       format("During Environment [{}]: {}", state.dataEnvrn->EnvironmentStartEnd, state.dataEnvrn->EnvironmentName));
    2434              :                 } else {
    2435            4 :                     ShowContinueError(
    2436              :                         state,
    2437            4 :                         format("During SizingPeriod Environment [{}]: {}", state.dataEnvrn->EnvironmentStartEnd, state.dataEnvrn->EnvironmentName));
    2438              :                 }
    2439            6 :                 for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2440            4 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).Enable55Warning) {
    2441            2 :                         if (state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither > allowedHours) {
    2442            4 :                             ShowContinueError(state,
    2443            4 :                                               format("{:.1R} hours were uncomfortable in zone: {}",
    2444            2 :                                                      state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither,
    2445            2 :                                                      state.dataHeatBal->Zone(iZone).Name));
    2446              :                         }
    2447              :                     }
    2448              :                 }
    2449              :             }
    2450              :             // put in predefined reports
    2451          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2452          236 :                 PreDefTableEntry(state,
    2453          118 :                                  state.dataOutRptPredefined->pdchSCwinterClothes,
    2454          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2455          118 :                                  state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter);
    2456          236 :                 PreDefTableEntry(state,
    2457          118 :                                  state.dataOutRptPredefined->pdchSCsummerClothes,
    2458          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2459          118 :                                  state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer);
    2460          236 :                 PreDefTableEntry(state,
    2461          118 :                                  state.dataOutRptPredefined->pdchSCeitherClothes,
    2462          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2463          118 :                                  state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither);
    2464              :             }
    2465          261 :             PreDefTableEntry(
    2466          174 :                 state, state.dataOutRptPredefined->pdchSCwinterClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter);
    2467          261 :             PreDefTableEntry(
    2468          174 :                 state, state.dataOutRptPredefined->pdchSCsummerClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer);
    2469          261 :             PreDefTableEntry(
    2470          174 :                 state, state.dataOutRptPredefined->pdchSCeitherClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either);
    2471              :             // set value for ABUPS report
    2472           87 :             state.dataOutRptPredefined->TotalTimeNotSimpleASH55EitherForABUPS = state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either;
    2473              :             // reset accumulation for new environment
    2474          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2475          118 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter = 0.0;
    2476          118 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer = 0.0;
    2477          118 :                 state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither = 0.0;
    2478              :             }
    2479           87 :             state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter = 0.0;
    2480           87 :             state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer = 0.0;
    2481           87 :             state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either = 0.0;
    2482              :             // report how the aggregation is conducted
    2483           87 :             switch (state.dataGlobal->KindOfSim) {
    2484           87 :             case Constant::KindOfSim::DesignDay: {
    2485           87 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the Design Days");
    2486           87 :             } break;
    2487            0 :             case Constant::KindOfSim::RunPeriodDesign: {
    2488            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the RunPeriods for Design");
    2489            0 :             } break;
    2490            0 :             case Constant::KindOfSim::RunPeriodWeather: {
    2491            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the RunPeriods for Weather");
    2492            0 :             } break;
    2493            0 :             default:
    2494            0 :                 break;
    2495              :             }
    2496              :             // report number of occupied hours per week for LEED report
    2497          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2498          236 :                 PreDefTableEntry(state,
    2499          118 :                                  state.dataOutRptPredefined->pdchLeedSutHrsWeek,
    2500          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2501          118 :                                  7 * 24 * (state.dataThermalComforts->ZoneOccHrs(iZone) / (state.dataGlobal->NumOfDayInEnvrn * 24)));
    2502              :             }
    2503              :         }
    2504        18995 :     }
    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        19006 :     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        19006 :         Real64 const deviationFromSetPtThresholdClg = state.dataHVACGlobal->deviationFromSetPtThresholdClg;
    2536        19006 :         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        19006 :         state.dataThermalComforts->AnyZoneNotMetHeating = 0.0;
    2548        19006 :         state.dataThermalComforts->AnyZoneNotMetCooling = 0.0;
    2549        19006 :         state.dataThermalComforts->AnyZoneNotMetOccupied = 0.0;
    2550        19006 :         state.dataThermalComforts->AnyZoneNotMetHeatingOccupied = 0.0;
    2551        19006 :         state.dataThermalComforts->AnyZoneNotMetCoolingOccupied = 0.0;
    2552        45092 :         for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2553        26086 :             auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(iZone);
    2554              : 
    2555        26086 :             SensibleLoadPredictedNoAdj = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(iZone).TotalOutputRequired;
    2556        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCooling = 0.0;
    2557        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeating = 0.0;
    2558        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCoolingOccupied = 0.0;
    2559        26086 :             state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeatingOccupied = 0.0;
    2560              : 
    2561        26086 :             testHeating = (state.dataHeatBalFanSys->TempControlType(iZone) != HVAC::SetptType::SingleCool);
    2562        26086 :             testCooling = (state.dataHeatBalFanSys->TempControlType(iZone) != HVAC::SetptType::SingleHeat);
    2563              : 
    2564        26086 :             if (testHeating && (SensibleLoadPredictedNoAdj > 0)) { // heating
    2565         6064 :                 if (state.dataRoomAir->AirModel(iZone).AirModel != RoomAir::RoomAirModel::Mixing) {
    2566            0 :                     deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - zoneTstatSetpt.setptLo;
    2567              :                 } else {
    2568         6064 :                     if (state.dataZoneTempPredictorCorrector->NumOnOffCtrZone > 0) {
    2569            1 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptLoAver;
    2570              :                     } else {
    2571         6063 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptLo;
    2572              :                     }
    2573              :                 }
    2574         6064 :                 if (deltaT < deviationFromSetPtThresholdHtg) {
    2575         1792 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeating = state.dataGlobal->TimeStepZone;
    2576         1792 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating += state.dataGlobal->TimeStepZone;
    2577         1792 :                     if (state.dataThermalComforts->AnyZoneNotMetHeating == 0.0) {
    2578         1504 :                         state.dataThermalComforts->AnyZoneNotMetHeating = state.dataGlobal->TimeStepZone;
    2579              :                     }
    2580         1792 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    2581         1060 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeatingOccupied = state.dataGlobal->TimeStepZone;
    2582         1060 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied += state.dataGlobal->TimeStepZone;
    2583         1060 :                         if (state.dataThermalComforts->AnyZoneNotMetHeatingOccupied == 0.0) {
    2584          868 :                             state.dataThermalComforts->AnyZoneNotMetHeatingOccupied = state.dataGlobal->TimeStepZone;
    2585              :                         }
    2586         1060 :                         if (state.dataThermalComforts->AnyZoneNotMetOccupied == 0.0) {
    2587          868 :                             state.dataThermalComforts->AnyZoneNotMetOccupied = state.dataGlobal->TimeStepZone;
    2588              :                         }
    2589              :                     }
    2590              :                 }
    2591        20022 :             } else if (testCooling && (SensibleLoadPredictedNoAdj < 0)) { // cooling
    2592         5684 :                 if (state.dataRoomAir->AirModel(iZone).AirModel != RoomAir::RoomAirModel::Mixing) {
    2593            0 :                     deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - zoneTstatSetpt.setptHi;
    2594              :                 } else {
    2595         5684 :                     if (state.dataZoneTempPredictorCorrector->NumOnOffCtrZone > 0) {
    2596            1 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptHiAver;
    2597              :                     } else {
    2598         5683 :                         deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV - zoneTstatSetpt.setptHi;
    2599              :                     }
    2600              :                 }
    2601              : 
    2602         5684 :                 if (state.dataHeatBal->Zone(iZone).HasAdjustedReturnTempByITE) {
    2603            0 :                     deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - state.dataHeatBal->Zone(iZone).AdjustedReturnTempByITE;
    2604              :                 }
    2605         5684 :                 if (deltaT > deviationFromSetPtThresholdClg) {
    2606         1418 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCooling = state.dataGlobal->TimeStepZone;
    2607         1418 :                     state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling += state.dataGlobal->TimeStepZone;
    2608         1418 :                     if (state.dataThermalComforts->AnyZoneNotMetCooling == 0.0) {
    2609          781 :                         state.dataThermalComforts->AnyZoneNotMetCooling = state.dataGlobal->TimeStepZone;
    2610              :                     }
    2611         1418 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    2612         1238 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCoolingOccupied = state.dataGlobal->TimeStepZone;
    2613         1238 :                         state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied += state.dataGlobal->TimeStepZone;
    2614         1238 :                         if (state.dataThermalComforts->AnyZoneNotMetCoolingOccupied == 0.0) {
    2615          643 :                             state.dataThermalComforts->AnyZoneNotMetCoolingOccupied = state.dataGlobal->TimeStepZone;
    2616              :                         }
    2617         1238 :                         if (state.dataThermalComforts->AnyZoneNotMetOccupied == 0.0) {
    2618          643 :                             state.dataThermalComforts->AnyZoneNotMetOccupied = state.dataGlobal->TimeStepZone;
    2619              :                         }
    2620              :                     }
    2621              :                 }
    2622              :             }
    2623              :         }
    2624        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetHeating += state.dataThermalComforts->AnyZoneNotMetHeating;
    2625        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetCooling += state.dataThermalComforts->AnyZoneNotMetCooling;
    2626        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied += state.dataThermalComforts->AnyZoneNotMetHeatingOccupied;
    2627        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied += state.dataThermalComforts->AnyZoneNotMetCoolingOccupied;
    2628        19006 :         state.dataThermalComforts->TotalAnyZoneNotMetOccupied += state.dataThermalComforts->AnyZoneNotMetOccupied;
    2629              : 
    2630              :         // was EndEnvrnsFlag prior to CR7562
    2631        19006 :         if (state.dataGlobal->EndDesignDayEnvrnsFlag) {
    2632          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2633          236 :                 PreDefTableEntry(state,
    2634          118 :                                  state.dataOutRptPredefined->pdchULnotMetHeat,
    2635          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2636          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating);
    2637          236 :                 PreDefTableEntry(state,
    2638          118 :                                  state.dataOutRptPredefined->pdchULnotMetCool,
    2639          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2640          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling);
    2641          236 :                 PreDefTableEntry(state,
    2642          118 :                                  state.dataOutRptPredefined->pdchULnotMetHeatOcc,
    2643          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2644          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied);
    2645          236 :                 PreDefTableEntry(state,
    2646          118 :                                  state.dataOutRptPredefined->pdchULnotMetCoolOcc,
    2647          118 :                                  state.dataHeatBal->Zone(iZone).Name,
    2648          118 :                                  state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied);
    2649              :             }
    2650           87 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchULnotMetHeat, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetHeating);
    2651           87 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchULnotMetCool, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetCooling);
    2652          261 :             PreDefTableEntry(
    2653          174 :                 state, state.dataOutRptPredefined->pdchULnotMetHeatOcc, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied);
    2654          261 :             PreDefTableEntry(
    2655          174 :                 state, state.dataOutRptPredefined->pdchULnotMetCoolOcc, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied);
    2656              :             // set value for ABUPS report
    2657           87 :             state.dataOutRptPredefined->TotalNotMetHeatingOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied;
    2658           87 :             state.dataOutRptPredefined->TotalNotMetCoolingOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied;
    2659           87 :             state.dataOutRptPredefined->TotalNotMetOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetOccupied;
    2660              :             // reset counters
    2661          205 :             for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    2662          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating = 0.0;
    2663          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling = 0.0;
    2664          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied = 0.0;
    2665          118 :                 state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied = 0.0;
    2666              :             }
    2667           87 :             state.dataThermalComforts->TotalAnyZoneNotMetHeating = 0.0;
    2668           87 :             state.dataThermalComforts->TotalAnyZoneNotMetCooling = 0.0;
    2669           87 :             state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied = 0.0;
    2670           87 :             state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied = 0.0;
    2671           87 :             state.dataThermalComforts->TotalAnyZoneNotMetOccupied = 0.0;
    2672              :             // report how the aggregation is conducted
    2673           87 :             switch (state.dataGlobal->KindOfSim) {
    2674           87 :             case Constant::KindOfSim::DesignDay: {
    2675           87 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the Design Days");
    2676           87 :             } break;
    2677            0 :             case Constant::KindOfSim::RunPeriodDesign: {
    2678            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the RunPeriods for Design");
    2679            0 :             } break;
    2680            0 :             case Constant::KindOfSim::RunPeriodWeather: {
    2681            0 :                 addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the RunPeriods for Weather");
    2682            0 :             } break;
    2683            0 :             default:
    2684            0 :                 break;
    2685              :             }
    2686              :         }
    2687        19006 :     }
    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            1 :     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            1 :         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            1 :         std::string lineAvg;
    2741            1 :         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            1 :         if (initiate) { // not optional on initiate=true.  would otherwise check for presence
    2759            0 :             weathersimulation = wthrsim;
    2760            0 :             state.dataThermalComforts->avgDryBulbASH = 0.0;
    2761            0 :             state.dataThermalComforts->runningAverageASH = 0.0;
    2762            0 :             state.dataThermalComforts->monthlyTemp = 0.0;
    2763            0 :             inavgdrybulb = avgdrybulb;
    2764              :         } else {
    2765            1 :             weathersimulation = false;
    2766            1 :             inavgdrybulb = 0.0;
    2767              :         }
    2768              : 
    2769            1 :         if (initiate && weathersimulation) {
    2770            0 :             const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
    2771            0 :             const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
    2772              : 
    2773            0 :             readStat = 0;
    2774            0 :             if (statFileExists) {
    2775            0 :                 auto statFile = state.files.inStatFilePath.open(state, "CalcThermalComfortAdapctiveASH55");
    2776            0 :                 while (statFile.good()) {
    2777            0 :                     auto lineIn = statFile.readLine();
    2778            0 :                     if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
    2779            0 :                         for (i = 1; i <= 7; ++i) {
    2780            0 :                             lineIn = statFile.readLine();
    2781              :                         }
    2782            0 :                         lineIn = statFile.readLine();
    2783            0 :                         lineAvg = lineIn.data;
    2784            0 :                         break;
    2785              :                     }
    2786            0 :                 }
    2787            0 :                 for (i = 1; i <= 12; ++i) {
    2788            0 :                     state.dataThermalComforts->monthlyTemp(i) = StrToReal(GetColumnUsingTabs(lineAvg, i + 2));
    2789              :                 }
    2790            0 :                 state.dataThermalComforts->useStatData = true;
    2791            0 :             } 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            1 :         } else if (initiate && !weathersimulation) {
    2866            0 :             state.dataThermalComforts->runningAverageASH = inavgdrybulb;
    2867            0 :             state.dataThermalComforts->monthlyTemp = inavgdrybulb;
    2868            0 :             state.dataThermalComforts->avgDryBulbASH = 0.0;
    2869              :         }
    2870              : 
    2871            1 :         if (initiate) {
    2872            0 :             return;
    2873              :         }
    2874              : 
    2875            1 :         if (state.dataGlobal->BeginDayFlag && state.dataThermalComforts->useEpwData) {
    2876              :             // Update the running average, reset the daily avg
    2877            1 :             state.dataThermalComforts->DailyAveOutTemp(30) = state.dataThermalComforts->avgDryBulbASH;
    2878            1 :             Real64 sum = 0.0;
    2879           30 :             for (i = 1; i <= 29; i++) {
    2880           29 :                 sum += state.dataThermalComforts->DailyAveOutTemp(i);
    2881              :             }
    2882            1 :             state.dataThermalComforts->runningAverageASH = (sum + state.dataThermalComforts->avgDryBulbASH) / 30.0;
    2883           30 :             for (i = 1; i <= 29; i++) {
    2884           29 :                 state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->DailyAveOutTemp(i + 1);
    2885              :             }
    2886            1 :             state.dataThermalComforts->avgDryBulbASH = 0.0;
    2887              :         }
    2888              : 
    2889              :         // If exists BeginMonthFlag we can use it to call InvJulianDay once per month.
    2890            1 :         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            1 :         if (state.dataGlobal->BeginHourFlag) {
    2899            0 :             state.dataThermalComforts->avgDryBulbASH += (state.dataEnvrn->OutDryBulbTemp / 24.0);
    2900              :         }
    2901              : 
    2902            1 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    2903            0 :              ++state.dataThermalComforts->PeopleNum) {
    2904              : 
    2905            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    2906            0 :             if (!people.AdaptiveASH55) {
    2907            0 :                 continue;
    2908              :             }
    2909              : 
    2910            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    2911              : 
    2912            0 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    2913            0 :             state.dataThermalComforts->AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).ZTAVComf;
    2914            0 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    2915            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
    2916            0 :                     state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
    2917            0 :                     state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum);
    2918              :                 }
    2919              :             }
    2920            0 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
    2921            0 :             state.dataThermalComforts->OpTemp = (state.dataThermalComforts->AirTemp + state.dataThermalComforts->RadTemp) / 2.0;
    2922            0 :             comfort.ThermalComfortOpTemp = state.dataThermalComforts->OpTemp;
    2923            0 :             comfort.ASHRAE55RunningMeanOutdoorTemp = state.dataThermalComforts->runningAverageASH;
    2924            0 :             if (state.dataThermalComforts->runningAverageASH >= 10.0 && state.dataThermalComforts->runningAverageASH <= 33.5) {
    2925              :                 // Calculate the comfort here  (people/output handling loop)
    2926            0 :                 numOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    2927            0 :                 tComf = 0.31 * state.dataThermalComforts->runningAverageASH + 17.8;
    2928            0 :                 comfort.TComfASH55 = tComf;
    2929            0 :                 if (numOccupants > 0) {
    2930            0 :                     if (state.dataThermalComforts->OpTemp < tComf + 2.5 && state.dataThermalComforts->OpTemp > tComf - 2.5) {
    2931              :                         // 80% and 90% limits okay
    2932            0 :                         comfort.ThermalComfortAdaptiveASH5590 = 1;
    2933            0 :                         comfort.ThermalComfortAdaptiveASH5580 = 1;
    2934            0 :                     } else if (state.dataThermalComforts->OpTemp < tComf + 3.5 && state.dataThermalComforts->OpTemp > tComf - 3.5) {
    2935              :                         // 80% only
    2936            0 :                         comfort.ThermalComfortAdaptiveASH5590 = 0;
    2937            0 :                         comfort.ThermalComfortAdaptiveASH5580 = 1;
    2938            0 :                         people.TimeNotMetASH5590 += SysTimeElapsed;
    2939              :                     } else {
    2940              :                         // Neither
    2941            0 :                         comfort.ThermalComfortAdaptiveASH5590 = 0;
    2942            0 :                         comfort.ThermalComfortAdaptiveASH5580 = 0;
    2943            0 :                         people.TimeNotMetASH5580 += SysTimeElapsed;
    2944            0 :                         people.TimeNotMetASH5590 += SysTimeElapsed;
    2945              :                     }
    2946              :                 } else {
    2947              :                     // Unoccupied
    2948            0 :                     comfort.ThermalComfortAdaptiveASH5590 = -1;
    2949            0 :                     comfort.ThermalComfortAdaptiveASH5580 = -1;
    2950              :                 }
    2951              :             } else {
    2952              :                 // Monthly temp out of range
    2953            0 :                 comfort.ThermalComfortAdaptiveASH5590 = -1;
    2954            0 :                 comfort.ThermalComfortAdaptiveASH5580 = -1;
    2955            0 :                 comfort.TComfASH55 = -1.0;
    2956              :             }
    2957              :         }
    2958            1 :     }
    2959              : 
    2960           26 :     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           26 :         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           26 :         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           26 :         int constexpr numHeaderRowsInEpw = 8;
    3006              : 
    3007           26 :         if (initiate) { // not optional on initiate=true.  would otherwise check for presence
    3008            1 :             weathersimulation = wthrsim;
    3009            1 :             inavgdrybulb = avgdrybulb;
    3010            1 :             state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3011            1 :             state.dataThermalComforts->runningAverageCEN = 0.0;
    3012              :         } else {
    3013           25 :             weathersimulation = false;
    3014           25 :             inavgdrybulb = 0.0;
    3015              :         }
    3016              : 
    3017           26 :         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            0 :                     calcStartHr = 24 * calcStartDay + 1;
    3037            0 :                     for (i = 1; i <= calcStartHr - 1; ++i) {
    3038            0 :                         epwFile.readLine();
    3039              :                     }
    3040            0 :                     state.dataThermalComforts->runningAverageCEN = 0.0;
    3041            0 :                     for (i = 0; i < 7; ++i) {
    3042            0 :                         state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3043            0 :                         for (j = 1; j <= 24; ++j) {
    3044            0 :                             epwLine = epwFile.readLine().data;
    3045            0 :                             for (ind = 1; ind <= 6; ++ind) {
    3046            0 :                                 pos = index(epwLine, ',');
    3047            0 :                                 epwLine.erase(0, pos + 1);
    3048              :                             }
    3049            0 :                             pos = index(epwLine, ',');
    3050            0 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    3051            0 :                             state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
    3052              :                         }
    3053            0 :                         state.dataThermalComforts->runningAverageCEN += alpha_pow[i] * state.dataThermalComforts->avgDryBulbCEN;
    3054              :                     }
    3055              :                 } else { // Do special things for wrapping the epw
    3056            1 :                     calcEndDay = jStartDay;
    3057            1 :                     calcStartDay += DaysInYear;
    3058            1 :                     calcEndHr = 24 * calcEndDay;
    3059            1 :                     calcStartHr = 24 * calcStartDay + 1;
    3060            1 :                     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         8593 :                     for (i = calcEndHr + 1; i <= calcStartHr - 1; ++i) {
    3075         8592 :                         epwFile.readLine();
    3076              :                     }
    3077            8 :                     for (i = 0; i < 7 - calcEndDay; ++i) {
    3078            7 :                         state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3079          175 :                         for (j = 1; j <= 24; ++j) {
    3080          168 :                             epwLine = epwFile.readLine().data;
    3081         1176 :                             for (ind = 1; ind <= 6; ++ind) {
    3082         1008 :                                 pos = index(epwLine, ',');
    3083         1008 :                                 epwLine.erase(0, pos + 1);
    3084              :                             }
    3085          168 :                             pos = index(epwLine, ',');
    3086          168 :                             dryBulb = StrToReal(epwLine.substr(0, pos));
    3087          168 :                             state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
    3088              :                         }
    3089            7 :                         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           26 :         } else if (initiate && !weathersimulation) {
    3098            0 :             state.dataThermalComforts->runningAverageCEN = inavgdrybulb;
    3099            0 :             state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3100              :         }
    3101           26 :         if (initiate) {
    3102            1 :             return;
    3103              :         }
    3104              : 
    3105           25 :         if (state.dataGlobal->BeginDayFlag && !state.dataThermalComforts->firstDaySet) {
    3106              :             // Update the running average, reset the daily avg
    3107            2 :             state.dataThermalComforts->runningAverageCEN =
    3108            1 :                 alpha * state.dataThermalComforts->runningAverageCEN + (1.0 - alpha) * state.dataThermalComforts->avgDryBulbCEN;
    3109            1 :             state.dataThermalComforts->avgDryBulbCEN = 0.0;
    3110              :         }
    3111              : 
    3112           25 :         state.dataThermalComforts->firstDaySet = false;
    3113              : 
    3114              :         // Update the daily average
    3115           25 :         if (state.dataGlobal->BeginHourFlag) {
    3116           25 :             state.dataThermalComforts->avgDryBulbCEN += (state.dataEnvrn->OutDryBulbTemp / 24.0);
    3117              :         }
    3118              : 
    3119           25 :         for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
    3120            0 :              ++state.dataThermalComforts->PeopleNum) {
    3121            0 :             auto &people = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum);
    3122            0 :             if (!people.AdaptiveCEN15251) {
    3123            0 :                 continue;
    3124              :             }
    3125              : 
    3126            0 :             auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    3127            0 :             state.dataThermalComforts->ZoneNum = people.ZonePtr;
    3128            0 :             state.dataThermalComforts->AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).ZTAVComf;
    3129            0 :             if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    3130            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
    3131            0 :                     state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
    3132            0 :                     state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum);
    3133              :                 }
    3134              :             }
    3135            0 :             state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
    3136            0 :             state.dataThermalComforts->OpTemp = (state.dataThermalComforts->AirTemp + state.dataThermalComforts->RadTemp) / 2.0;
    3137            0 :             comfort.ThermalComfortOpTemp = state.dataThermalComforts->OpTemp;
    3138            0 :             comfort.CEN15251RunningMeanOutdoorTemp = state.dataThermalComforts->runningAverageCEN;
    3139            0 :             if (state.dataThermalComforts->runningAverageCEN >= 10.0 && state.dataThermalComforts->runningAverageCEN <= 30.0) {
    3140              :                 // Calculate the comfort here (people/output handling loop)
    3141            0 :                 numOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    3142            0 :                 tComf = 0.33 * state.dataThermalComforts->runningAverageCEN + 18.8;
    3143            0 :                 comfort.TComfCEN15251 = tComf;
    3144            0 :                 if (numOccupants > 0) {
    3145            0 :                     if (state.dataThermalComforts->runningAverageCEN < 15) {
    3146            0 :                         tComfLow = 23.75; // Lower limit is constant in this region
    3147              :                     } else {
    3148            0 :                         tComfLow = tComf;
    3149              :                     }
    3150            0 :                     if (state.dataThermalComforts->OpTemp < tComf + 2.0 && state.dataThermalComforts->OpTemp > tComfLow - 2.0) {
    3151              :                         // Within Cat I, II, III Limits
    3152            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 1;
    3153            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 1;
    3154            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 1;
    3155            0 :                     } else if (state.dataThermalComforts->OpTemp < tComf + 3.0 && state.dataThermalComforts->OpTemp > tComfLow - 3.0) {
    3156              :                         // Within Cat II, III Limits
    3157            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 0;
    3158            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 1;
    3159            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 1;
    3160            0 :                         people.TimeNotMetCEN15251CatI += SysTimeElapsed;
    3161            0 :                     } else if (state.dataThermalComforts->OpTemp < tComf + 4.0 && state.dataThermalComforts->OpTemp > tComfLow - 4.0) {
    3162              :                         // Within Cat III Limits
    3163            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 0;
    3164            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 0;
    3165            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 1;
    3166            0 :                         people.TimeNotMetCEN15251CatI += SysTimeElapsed;
    3167            0 :                         people.TimeNotMetCEN15251CatII += SysTimeElapsed;
    3168              :                     } else {
    3169              :                         // None
    3170            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatI = 0;
    3171            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatII = 0;
    3172            0 :                         comfort.ThermalComfortAdaptiveCEN15251CatIII = 0;
    3173            0 :                         people.TimeNotMetCEN15251CatI += SysTimeElapsed;
    3174            0 :                         people.TimeNotMetCEN15251CatII += SysTimeElapsed;
    3175            0 :                         people.TimeNotMetCEN15251CatIII += SysTimeElapsed;
    3176              :                     }
    3177              :                 } else {
    3178              :                     // Unoccupied
    3179            0 :                     comfort.ThermalComfortAdaptiveCEN15251CatI = -1;
    3180            0 :                     comfort.ThermalComfortAdaptiveCEN15251CatII = -1;
    3181            0 :                     comfort.ThermalComfortAdaptiveCEN15251CatIII = -1;
    3182              :                 }
    3183              :             } else {
    3184              :                 // Monthly temp out of range
    3185            0 :                 comfort.ThermalComfortAdaptiveCEN15251CatI = -1;
    3186            0 :                 comfort.ThermalComfortAdaptiveCEN15251CatII = -1;
    3187            0 :                 comfort.ThermalComfortAdaptiveCEN15251CatIII = -1;
    3188            0 :                 comfort.TComfCEN15251 = -1.0;
    3189              :             }
    3190              :         }
    3191           26 :     }
    3192              : 
    3193            0 :     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            0 :         auto &comfort = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum);
    3203              : 
    3204            0 :         if (state.dataThermalComforts->TemporarySixAMTemperature < -5.0) { // A Temporary state variable?
    3205            0 :             comfort.ClothingValue = 1.0;
    3206            0 :         } else if ((state.dataThermalComforts->TemporarySixAMTemperature >= -5.0) && (state.dataThermalComforts->TemporarySixAMTemperature < 5.0)) {
    3207            0 :             comfort.ClothingValue = 0.818 - 0.0364 * state.dataThermalComforts->TemporarySixAMTemperature;
    3208            0 :         } else if ((state.dataThermalComforts->TemporarySixAMTemperature >= 5.0) && (state.dataThermalComforts->TemporarySixAMTemperature < 26.0)) {
    3209            0 :             TemporaryVariable = -0.1635 - 0.0066 * state.dataThermalComforts->TemporarySixAMTemperature;
    3210            0 :             comfort.ClothingValue = std::pow(10.0, TemporaryVariable);
    3211            0 :         } else if (state.dataThermalComforts->TemporarySixAMTemperature >= 26.0) {
    3212            0 :             comfort.ClothingValue = 0.46;
    3213              :         }
    3214            0 :     }
    3215              : 
    3216              : } // namespace ThermalComfort
    3217              : 
    3218              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1