LCOV - code coverage report
Current view: top level - EnergyPlus - ZoneTempPredictorCorrector.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 71.5 % 4084 2920
Test Date: 2025-06-02 07:23:51 Functions: 95.6 % 45 43

            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 <cmath>
      50              : #include <numeric>
      51              : #include <string>
      52              : 
      53              : // ObjexxFCL Headers
      54              : #include <ObjexxFCL/Array.functions.hh>
      55              : 
      56              : // EnergyPlus Headers
      57              : #include <AirflowNetwork/Elements.hpp>
      58              : #include <AirflowNetwork/Solver.hpp>
      59              : #include <EnergyPlus/Construction.hh>
      60              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      61              : #include <EnergyPlus/DataDefineEquip.hh>
      62              : #include <EnergyPlus/DataEnvironment.hh>
      63              : #include <EnergyPlus/DataHVACGlobals.hh>
      64              : #include <EnergyPlus/DataHeatBalFanSys.hh>
      65              : #include <EnergyPlus/DataHeatBalSurface.hh>
      66              : #include <EnergyPlus/DataHeatBalance.hh>
      67              : #include <EnergyPlus/DataIPShortCuts.hh>
      68              : #include <EnergyPlus/DataLoopNode.hh>
      69              : #include <EnergyPlus/DataPrecisionGlobals.hh>
      70              : #include <EnergyPlus/DataRoomAirModel.hh>
      71              : #include <EnergyPlus/DataSizing.hh>
      72              : #include <EnergyPlus/DataStringGlobals.hh>
      73              : #include <EnergyPlus/DataSurfaces.hh>
      74              : #include <EnergyPlus/DataZoneControls.hh>
      75              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      76              : #include <EnergyPlus/DataZoneEquipment.hh>
      77              : #include <EnergyPlus/FaultsManager.hh>
      78              : #include <EnergyPlus/FileSystem.hh>
      79              : #include <EnergyPlus/General.hh>
      80              : #include <EnergyPlus/GeneralRoutines.hh>
      81              : #include <EnergyPlus/GlobalNames.hh>
      82              : #include <EnergyPlus/HeatBalFiniteDiffManager.hh>
      83              : #include <EnergyPlus/HeatBalanceSurfaceManager.hh>
      84              : #include <EnergyPlus/HybridModel.hh>
      85              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      86              : #include <EnergyPlus/InternalHeatGains.hh>
      87              : #include <EnergyPlus/OutputProcessor.hh>
      88              : #include <EnergyPlus/OutputReportPredefined.hh>
      89              : #include <EnergyPlus/OutputReportTabular.hh>
      90              : #include <EnergyPlus/Psychrometrics.hh>
      91              : #include <EnergyPlus/RoomAirModelAirflowNetwork.hh>
      92              : #include <EnergyPlus/RoomAirModelManager.hh>
      93              : #include <EnergyPlus/ScheduleManager.hh>
      94              : #include <EnergyPlus/ThermalComfort.hh>
      95              : #include <EnergyPlus/UtilityRoutines.hh>
      96              : #include <EnergyPlus/WeatherManager.hh>
      97              : #include <EnergyPlus/ZonePlenum.hh>
      98              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      99              : 
     100              : namespace EnergyPlus::ZoneTempPredictorCorrector {
     101              : 
     102              : // MODULE INFORMATION:
     103              : //       AUTHOR         Russell D. Taylor
     104              : //       DATE WRITTEN   1997
     105              : //       MODIFIED       Aug 2001(FW): make SNLoadHeatRate public
     106              : //                      Nov 2010  BN(FSEC) added TemperatureAndHumidity Control
     107              : //       RE-ENGINEERED  July 2003 (Peter Graham Ellis)
     108              : //                      July 2006 (BG) added operative temp control
     109              : //                      February 2008 (BG) reworked zone air temp histories
     110              : 
     111              : // PURPOSE OF THIS MODULE:
     112              : // This module contains routines to predict and correct zone temperatures.
     113              : //  also includes zone thermostatic controlling
     114              : //  Model the "Air Heat Balance" part of the the "Zone Heat Balance Method."
     115              : 
     116              : // METHODOLOGY EMPLOYED:
     117              : // apply model equations for air heat balance solved for zone air temp.
     118              : //    sum up values for the terms (e.g SUMHAT, SUMHA etc. )
     119              : //    "Predict" step is used to get zone loads for HVAC equipment
     120              : //    "correct" step determines zone air temp with available HVAC
     121              : 
     122              : enum class ZoneControlTypes
     123              : {
     124              :     Invalid = -1,
     125              :     TStat = 1,
     126              :     TCTStat = 2,
     127              :     OTTStat = 3,
     128              :     HStat = 4,
     129              :     TandHStat = 5,
     130              :     StagedDual = 6,
     131              :     Num
     132              : };
     133              : 
     134              : enum class AdaptiveComfortModel
     135              : {
     136              :     Invalid = -1,
     137              :     ADAP_NONE = 1,
     138              :     ASH55_CENTRAL = 2,
     139              :     ASH55_UPPER_90 = 3,
     140              :     ASH55_UPPER_80 = 4,
     141              :     CEN15251_CENTRAL = 5,
     142              :     CEN15251_UPPER_I = 6,
     143              :     CEN15251_UPPER_II = 7,
     144              :     CEN15251_UPPER_III = 8,
     145              :     Num
     146              : };
     147              : 
     148              : static constexpr std::array<std::string_view, (int)HVAC::SetptType::Num> setptTypeNames = {"Uncontrolled",
     149              :                                                                                            "ThermostatSetpoint:SingleHeating",
     150              :                                                                                            "ThermostatSetpoint:SingleCooling",
     151              :                                                                                            "ThermostatSetpoint:SingleHeatingOrCooling",
     152              :                                                                                            "ThermostatSetpoint:DualSetpoint"};
     153              : 
     154              : static constexpr std::array<std::string_view, (int)HVAC::SetptType::Num> setptTypeNamesUC = {"UNCONTROLLED",
     155              :                                                                                              "THERMOSTATSETPOINT:SINGLEHEATING",
     156              :                                                                                              "THERMOSTATSETPOINT:SINGLECOOLING",
     157              :                                                                                              "THERMOSTATSETPOINT:SINGLEHEATINGORCOOLING",
     158              :                                                                                              "THERMOSTATSETPOINT:DUALSETPOINT"};
     159              : 
     160              : static constexpr std::array<std::string_view, (int)HVAC::SetptType::Num> comfortSetptTypeNames = {
     161              :     "Uncontrolled",
     162              :     "ThermostatSetpoint:ThermalComfort:Fanger:SingleHeating",
     163              :     "ThermostatSetpoint:ThermalComfort:Fanger:SingleCooling",
     164              :     "ThermostatSetpoint:ThermalComfort:Fanger:SingleHeatingOrCooling",
     165              :     "ThermostatSetpoint:ThermalComfort:Fanger:DualSetpoint"};
     166              : 
     167              : static constexpr std::array<std::string_view, (int)HVAC::SetptType::Num> comfortSetptTypeNamesUC = {
     168              :     "UNCONTROLLED",
     169              :     "THERMOSTATSETPOINT:THERMALCOMFORT:FANGER:SINGLEHEATING",
     170              :     "THERMOSTATSETPOINT:THERMALCOMFORT:FANGER:SINGLECOOLING",
     171              :     "THERMOSTATSETPOINT:THERMALCOMFORT:FANGER:SINGLEHEATINGORCOOLING",
     172              :     "THERMOSTATSETPOINT:THERMALCOMFORT:FANGER:DUALSETPOINT"};
     173              : 
     174              : Array1D_string const cZControlTypes(6,
     175              :                                     {"ZoneControl:Thermostat",
     176              :                                      "ZoneControl:Thermostat:ThermalComfort",
     177              :                                      "ZoneControl:Thermostat:OperativeTemperature",
     178              :                                      "ZoneControl:Humidistat",
     179              :                                      "ZoneControl:Thermostat:TemperatureAndHumidity",
     180              :                                      "ZoneControl:Thermostat:StagedDualSetpoint"});
     181              : 
     182              : Array1D_string const AdaptiveComfortModelTypes(8,
     183              :                                                {"None",
     184              :                                                 "AdaptiveASH55CentralLine",
     185              :                                                 "AdaptiveASH5590PercentUpperLine",
     186              :                                                 "AdaptiveASH5580PercentUpperLine",
     187              :                                                 "AdaptiveCEN15251CentralLine",
     188              :                                                 "AdaptiveCEN15251CategoryIUpperLine",
     189              :                                                 "AdaptiveCEN15251CategoryIIUpperLine",
     190              :                                                 "AdaptiveCEN15251CategoryIIIUpperLine"});
     191              : 
     192              : // Functions
     193     14518428 : void ManageZoneAirUpdates(EnergyPlusData &state,
     194              :                           DataHeatBalFanSys::PredictorCorrectorCtrl const UpdateType, // Can be iGetZoneSetPoints, iPredictStep, iCorrectStep
     195              :                           Real64 &ZoneTempChange,                                     // Temp change in zone air btw previous and current timestep
     196              :                           bool const ShortenTimeStepSys,
     197              :                           bool const UseZoneTimeStepHistory, // if true then use zone timestep history, if false use system time step
     198              :                           Real64 const PriorTimeStep         // the old value for timestep length is passed for possible use in interpolating
     199              : )
     200              : {
     201              : 
     202              :     // SUBROUTINE INFORMATION
     203              :     //       AUTHOR         Russ Taylor
     204              :     //       DATE WRITTEN   September 1998
     205              :     //       MODIFIED       na
     206              :     //       RE-ENGINEERED  Brent Griffith Feb. 2008,  added arguments
     207              : 
     208              :     // PURPOSE OF THIS SUBROUTINE:
     209              :     // This subroutine predicts or corrects the zone air temperature
     210              :     // depending on the simulation status and determines the correct
     211              :     // temperature setpoint for each zone from the schedule manager.
     212              : 
     213     14518428 :     if (state.dataZoneCtrls->GetZoneAirStatsInputFlag) {
     214          791 :         GetZoneAirSetPoints(state);
     215          791 :         state.dataZoneCtrls->GetZoneAirStatsInputFlag = false;
     216              :     }
     217              : 
     218     14518428 :     InitZoneAirSetPoints(state);
     219              : 
     220     14518428 :     switch (UpdateType) {
     221      2828450 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::GetZoneSetPoints: {
     222      2828450 :         CalcZoneAirTempSetPoints(state);
     223      2828450 :     } break;
     224      3896818 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::PredictStep: {
     225      3896818 :         PredictSystemLoads(state, ShortenTimeStepSys, UseZoneTimeStepHistory, PriorTimeStep);
     226      3896818 :     } break;
     227      3896580 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::CorrectStep: {
     228      3896580 :         ZoneTempChange = correctZoneAirTemps(state, UseZoneTimeStepHistory);
     229      3896580 :     } break;
     230            0 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::RevertZoneTimestepHistories: {
     231            0 :         RevertZoneTimestepHistories(state);
     232            0 :     } break;
     233      2828212 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::PushZoneTimestepHistories: {
     234      2828212 :         PushZoneTimestepHistories(state);
     235      2828212 :     } break;
     236      1068368 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::PushSystemTimestepHistories: {
     237      1068368 :         PushSystemTimestepHistories(state);
     238      1068368 :     } break;
     239            0 :     default:
     240            0 :         break;
     241              :     }
     242     14518428 : }
     243              : 
     244          801 : void GetZoneAirSetPoints(EnergyPlusData &state)
     245              : {
     246              : 
     247              :     // SUBROUTINE INFORMATION:
     248              :     //       AUTHOR         Russell Taylor
     249              :     //       DATE WRITTEN   September 1998
     250              :     //       MODIFIED       L.Gu, May 2006, B. Griffith June 2006
     251              :     //       RE-ENGINEERED  na
     252              : 
     253              :     // PURPOSE OF THIS SUBROUTINE:
     254              :     // This subroutine gets the inputs related to thermostatic control.
     255              : 
     256              :     // METHODOLOGY EMPLOYED:
     257              :     // Uses the status flags to trigger events.
     258              : 
     259              :     // Using/Aliasing
     260              :     using General::CheckCreatedZoneItemName;
     261              :     using General::FindNumberInList;
     262              : 
     263              :     // SUBROUTINE PARAMETER DEFINITIONS:
     264              :     static constexpr std::string_view RoutineName("GetZoneAirSetpoints: ");
     265              :     static constexpr std::string_view routineName = "GetZoneAirSetpoints";
     266              : 
     267              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     268              :     int TempControlledZoneNum; // The Splitter that you are currently loading input into
     269              :     int NumAlphas;
     270              :     int NumNums;
     271              :     int IOStat;
     272          801 :     bool ErrorsFound(false);
     273              :     bool errFlag;
     274              :     int HumidControlledZoneNum; // The Humidity Controller that information is being loaded into
     275              :     int ActualZoneNum;
     276              : 
     277              :     int ComfortControlledZoneNum; // The Splitter that you are currently loading input into
     278              :     int i;
     279              :     int found;
     280              :     int NumStageControlledZones; // Number of staged controlled objects
     281              : 
     282          801 :     Array1D_int CTSchedMapToControlledZone;
     283          801 :     Array1D_int CCmSchedMapToControlledZone;
     284              :     int Item;
     285              :     int Item1;
     286              :     int ZLItem;
     287              : 
     288              :     struct NeededControlTypes
     289              :     {
     290              :         // Members 4= the four control types + uncontrolled
     291              :         std::array<bool, static_cast<int>(HVAC::SetptType::Num)> MustHave = {false, false, false, false, false};
     292              :         std::array<bool, static_cast<int>(HVAC::SetptType::Num)> DidHave = {false, false, false, false, false};
     293              :     };
     294              : 
     295              :     struct NeededComfortControlTypes
     296              :     {
     297              :         // Members 4= the four control types + uncontrolled
     298              :         std::array<bool, static_cast<int>(HVAC::SetptType::Num)> MustHave = {false, false, false, false, false};
     299              :         std::array<bool, static_cast<int>(HVAC::SetptType::Num)> DidHave = {false, false, false, false, false};
     300              :     };
     301              : 
     302              :     // Object Data
     303          801 :     Array1D<NeededControlTypes> TStatControlTypes;
     304          801 :     Array1D<NeededComfortControlTypes> TComfortControlTypes;
     305              : 
     306              :     // Formats
     307              :     static constexpr std::string_view Header(
     308              :         "! <Zone Volume Capacitance Multiplier>, Sensible Heat Capacity Multiplier, Moisture Capacity Multiplier, Carbon "
     309              :         "Dioxide Capacity Multiplier, Generic Contaminant Capacity Multiplier\n");
     310              :     static constexpr std::string_view Format_701("Zone Volume Capacitance Multiplier,{:8.3F} ,{:8.3F},{:8.3F},{:8.3F}\n");
     311              : 
     312          801 :     auto &s_ipsc = state.dataIPShortCut;
     313          801 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
     314              : 
     315          801 :     auto &TStatObjects = state.dataZoneCtrls->TStatObjects;
     316          801 :     auto &Zone = state.dataHeatBal->Zone;
     317          801 :     auto &ZoneList = state.dataHeatBal->ZoneList;
     318          801 :     auto &ComfortTStatObjects = state.dataZoneCtrls->ComfortTStatObjects;
     319          801 :     int NumOfZones = state.dataGlobal->NumOfZones;
     320          801 :     auto &s_ip = state.dataInputProcessing->inputProcessor;
     321              : 
     322          801 :     s_ipsc->cCurrentModuleObject = cZControlTypes(static_cast<int>(ZoneControlTypes::TStat));
     323              :     // Update Num in state and make local convenience copy
     324          801 :     int NumTStatStatements = state.dataZoneCtrls->NumTStatStatements = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     325          801 :     TStatObjects.allocate(NumTStatStatements);
     326              : 
     327              :     // Pre-scan for use of Zone lists in TStat statements (i.e. Global application of TStat)
     328          801 :     state.dataZoneCtrls->NumTempControlledZones = 0;
     329         5100 :     for (Item = 1; Item <= NumTStatStatements; ++Item) {
     330         8598 :         s_ip->getObjectItem(state,
     331         4299 :                             s_ipsc->cCurrentModuleObject,
     332              :                             Item,
     333         4299 :                             s_ipsc->cAlphaArgs,
     334              :                             NumAlphas,
     335         4299 :                             s_ipsc->rNumericArgs,
     336              :                             NumNums,
     337              :                             IOStat,
     338         4299 :                             s_ipsc->lNumericFieldBlanks,
     339         4299 :                             s_ipsc->lAlphaFieldBlanks,
     340         4299 :                             s_ipsc->cAlphaFieldNames,
     341         4299 :                             s_ipsc->cNumericFieldNames);
     342              : 
     343         4299 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     344         4299 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     345              : 
     346         4299 :         TStatObjects(Item).Name = s_ipsc->cAlphaArgs(1);
     347         4299 :         Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     348         4299 :         ZLItem = 0;
     349         4299 :         if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
     350            9 :             ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
     351              :         }
     352         4299 :         if (Item1 > 0) {
     353         4290 :             TStatObjects(Item).TempControlledZoneStartPtr = state.dataZoneCtrls->NumTempControlledZones + 1;
     354         4290 :             ++state.dataZoneCtrls->NumTempControlledZones;
     355         4290 :             TStatObjects(Item).NumOfZones = 1;
     356         4290 :             TStatObjects(Item).ZoneListActive = false;
     357         4290 :             TStatObjects(Item).ZoneOrZoneListPtr = Item1;
     358            9 :         } else if (ZLItem > 0) {
     359            9 :             auto const &ZoneList = state.dataHeatBal->ZoneList(ZLItem);
     360            9 :             TStatObjects(Item).TempControlledZoneStartPtr = state.dataZoneCtrls->NumTempControlledZones + 1;
     361            9 :             state.dataZoneCtrls->NumTempControlledZones += ZoneList.NumOfZones;
     362            9 :             TStatObjects(Item).NumOfZones = ZoneList.NumOfZones;
     363            9 :             TStatObjects(Item).ZoneListActive = true;
     364            9 :             TStatObjects(Item).ZoneOrZoneListPtr = ZLItem;
     365              :         } else {
     366            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     367            0 :             ErrorsFound = true;
     368              :         }
     369              :     }
     370              : 
     371          801 :     if (ErrorsFound) {
     372            0 :         ShowSevereError(state, format("GetZoneAirSetpoints: Errors with invalid names in {} objects.", s_ipsc->cCurrentModuleObject));
     373            0 :         ShowContinueError(state, "...These will not be read in.  Other errors may occur.");
     374            0 :         state.dataZoneCtrls->NumTempControlledZones = 0;
     375              :     }
     376              : 
     377          801 :     if (state.dataZoneCtrls->NumTempControlledZones > 0) {
     378          690 :         state.dataZoneCtrls->TempControlledZone.allocate(state.dataZoneCtrls->NumTempControlledZones);
     379          690 :         TStatControlTypes.allocate(state.dataZoneCtrls->NumTempControlledZones); // Number of set point types
     380          690 :         CTSchedMapToControlledZone.dimension(state.dataZoneCtrls->NumTempControlledZones, 0);
     381              : 
     382          690 :         TempControlledZoneNum = 0;
     383          690 :         s_ztpc->NumOnOffCtrZone = 0;
     384         4989 :         for (Item = 1; Item <= NumTStatStatements; ++Item) {
     385         8598 :             s_ip->getObjectItem(state,
     386         4299 :                                 s_ipsc->cCurrentModuleObject,
     387              :                                 Item,
     388         4299 :                                 s_ipsc->cAlphaArgs,
     389              :                                 NumAlphas,
     390         4299 :                                 s_ipsc->rNumericArgs,
     391              :                                 NumNums,
     392              :                                 IOStat,
     393         4299 :                                 s_ipsc->lNumericFieldBlanks,
     394         4299 :                                 s_ipsc->lAlphaFieldBlanks,
     395         4299 :                                 s_ipsc->cAlphaFieldNames,
     396         4299 :                                 s_ipsc->cNumericFieldNames);
     397              : 
     398         4299 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     399              : 
     400         8632 :             for (Item1 = 1; Item1 <= TStatObjects(Item).NumOfZones; ++Item1) {
     401         4333 :                 ++TempControlledZoneNum;
     402         4333 :                 auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     403              : 
     404         4333 :                 if (TStatObjects(Item).ZoneListActive) {
     405           43 :                     s_ipsc->cAlphaArgs(2) = Zone(ZoneList(TStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name;
     406              :                 }
     407         4333 :                 int ZoneAssigned = Util::FindItemInList(s_ipsc->cAlphaArgs(2),
     408         4333 :                                                         state.dataZoneCtrls->TempControlledZone,
     409              :                                                         &DataZoneControls::ZoneTempControls::ZoneName,
     410              :                                                         TempControlledZoneNum - 1);
     411         4333 :                 if (ZoneAssigned == 0) {
     412         4333 :                     tempZone.ZoneName = s_ipsc->cAlphaArgs(2);
     413         4333 :                     tempZone.ActualZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     414         4333 :                     if (tempZone.ActualZoneNum == 0) {
     415            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     416            0 :                         ErrorsFound = true;
     417              :                     } else {
     418         4333 :                         Zone(tempZone.ActualZoneNum).TempControlledZoneIndex = TempControlledZoneNum;
     419              :                     }
     420              :                 } else {
     421            0 :                     tempZone.ZoneName = s_ipsc->cAlphaArgs(2); // for continuity
     422            0 :                     ShowSevereDuplicateAssignment(
     423            0 :                         state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2), state.dataZoneCtrls->TempControlledZone(ZoneAssigned).Name);
     424            0 :                     ErrorsFound = true;
     425            0 :                     continue;
     426              :                 }
     427              : 
     428         4333 :                 if (!TStatObjects(Item).ZoneListActive) {
     429         4290 :                     tempZone.Name = s_ipsc->cAlphaArgs(1);
     430              :                 } else {
     431           43 :                     auto &ZoneList = state.dataHeatBal->ZoneList(TStatObjects(Item).ZoneOrZoneListPtr);
     432          129 :                     CheckCreatedZoneItemName(state,
     433              :                                              RoutineName,
     434           43 :                                              s_ipsc->cCurrentModuleObject,
     435           43 :                                              Zone(ZoneList.Zone(Item1)).Name,
     436              :                                              ZoneList.MaxZoneNameLength,
     437           43 :                                              TStatObjects(Item).Name,
     438           43 :                                              state.dataZoneCtrls->TempControlledZone,
     439              :                                              TempControlledZoneNum - 1,
     440           43 :                                              tempZone.Name,
     441              :                                              errFlag);
     442           43 :                     if (errFlag) {
     443            0 :                         ErrorsFound = true;
     444              :                     }
     445              :                 }
     446              : 
     447         4333 :                 tempZone.setptTypeSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3));
     448         4333 :                 if (Item1 == 1) { // only show error on first of several if zone list
     449         4299 :                     if (tempZone.setptTypeSched == nullptr) {
     450            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     451            0 :                         ErrorsFound = true;
     452         4299 :                     } else if (!tempZone.setptTypeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 4.0)) {
     453            0 :                         Sched::ShowSevereBadMinMax(
     454            0 :                             state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), Clusive::In, 0.0, Clusive::In, 4.0);
     455            0 :                         ErrorsFound = true;
     456              :                     }
     457              :                 }
     458              : 
     459         4333 :                 if (s_ipsc->lAlphaFieldBlanks(7)) {
     460         3192 :                     NumAlphas = 5;
     461         1141 :                 } else if (s_ipsc->lAlphaFieldBlanks(9)) {
     462          474 :                     NumAlphas = 7;
     463          667 :                 } else if (s_ipsc->lAlphaFieldBlanks(11)) {
     464          667 :                     NumAlphas = 9;
     465              :                 }
     466              : 
     467         4333 :                 int NumSetptTypes = nint((NumAlphas - 3.0) / 2.0);
     468              : 
     469        10474 :                 for (int iSetpt = 1; iSetpt <= NumSetptTypes; ++iSetpt) {
     470              : 
     471         6141 :                     HVAC::SetptType setptType = HVAC::SetptType::Invalid;
     472         6141 :                     int spIdx = 2 * iSetpt - 1 + 3;
     473              : 
     474         6141 :                     if (s_ipsc->lAlphaFieldBlanks(spIdx)) {
     475            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(spIdx));
     476            0 :                         ErrorsFound = true;
     477            0 :                         continue;
     478         6141 :                     } else if ((setptType = static_cast<HVAC::SetptType>(getEnumValue(setptTypeNamesUC, s_ipsc->cAlphaArgs(spIdx)))) ==
     479              :                                HVAC::SetptType::Invalid) {
     480            0 :                         ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(spIdx), s_ipsc->cAlphaArgs(spIdx));
     481            0 :                         ErrorsFound = true;
     482            0 :                         continue;
     483              :                     }
     484              : 
     485         6141 :                     tempZone.setpts[(int)setptType].Name = s_ipsc->cAlphaArgs(2 * iSetpt + 3);
     486         6141 :                     tempZone.setpts[(int)setptType].isUsed = true;
     487              :                 }
     488              : 
     489         4333 :                 if (NumNums > 0) {
     490           11 :                     if (s_ipsc->rNumericArgs(1) >= 0.0) {
     491           11 :                         tempZone.DeltaTCutSet = s_ipsc->rNumericArgs(1);
     492           11 :                         if (s_ipsc->rNumericArgs(1) > 0.0) {
     493            7 :                             s_ztpc->NumOnOffCtrZone++;
     494              :                         }
     495              :                     } else {
     496            0 :                         ShowSevereError(state,
     497            0 :                                         format("{}=\"{} invalid {}=[{:.0T}].",
     498            0 :                                                s_ipsc->cCurrentModuleObject,
     499            0 :                                                s_ipsc->cAlphaArgs(1),
     500            0 :                                                s_ipsc->cNumericFieldNames(1),
     501            0 :                                                s_ipsc->rNumericArgs(1)));
     502            0 :                         ShowContinueError(state, "..Allowable values must be greater or equal to 0");
     503            0 :                         ErrorsFound = true;
     504              :                     }
     505              :                 }
     506              : 
     507         4333 :                 if (tempZone.DeltaTCutSet > 0.0 && tempZone.setpts[(int)HVAC::SetptType::SingleHeatCool].Name != "") {
     508            0 :                     ShowWarningError(state,
     509            0 :                                      format("{}=\"{}: The choice of Temperature Difference Between Cutout And Setpoint will not be applied "
     510              :                                             "to ThermostatSetpoint:SingleHeatingOrCooling.",
     511            0 :                                             s_ipsc->cCurrentModuleObject,
     512            0 :                                             s_ipsc->cAlphaArgs(1)));
     513              :                 }
     514              :             }
     515              :         } // NumTStatStatements
     516              :     } // Check on number of TempControlledZones
     517              : 
     518          801 :     s_ipsc->cCurrentModuleObject = setptTypeNamesUC[(int)HVAC::SetptType::SingleHeat];
     519          801 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     520              : 
     521          801 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat] > 0) {
     522          326 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeat].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat]);
     523              :     }
     524              : 
     525         1255 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat]; ++idx) {
     526          908 :         s_ip->getObjectItem(state,
     527          454 :                             s_ipsc->cCurrentModuleObject,
     528              :                             idx,
     529          454 :                             s_ipsc->cAlphaArgs,
     530              :                             NumAlphas,
     531          454 :                             s_ipsc->rNumericArgs,
     532              :                             NumNums,
     533              :                             IOStat,
     534          454 :                             s_ipsc->lNumericFieldBlanks,
     535          454 :                             s_ipsc->lAlphaFieldBlanks,
     536          454 :                             s_ipsc->cAlphaFieldNames,
     537          454 :                             s_ipsc->cNumericFieldNames);
     538              : 
     539          454 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     540              : 
     541          454 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     542          454 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeat](idx);
     543          454 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     544              : 
     545          454 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     546            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     547            0 :             ErrorsFound = true;
     548          454 :         } else if ((setpt.heatSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
     549            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     550            0 :             ErrorsFound = true;
     551              :         }
     552              : 
     553              :     } // SingleTempHeatingControlNum
     554              : 
     555          801 :     s_ipsc->cCurrentModuleObject = setptTypeNamesUC[(int)HVAC::SetptType::SingleCool];
     556          801 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     557              : 
     558          801 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool] > 0) {
     559          320 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleCool].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool]);
     560              :     }
     561              : 
     562         1249 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool]; ++idx) {
     563          896 :         s_ip->getObjectItem(state,
     564          448 :                             s_ipsc->cCurrentModuleObject,
     565              :                             idx,
     566          448 :                             s_ipsc->cAlphaArgs,
     567              :                             NumAlphas,
     568          448 :                             s_ipsc->rNumericArgs,
     569              :                             NumNums,
     570              :                             IOStat,
     571          448 :                             s_ipsc->lNumericFieldBlanks,
     572          448 :                             s_ipsc->lAlphaFieldBlanks,
     573          448 :                             s_ipsc->cAlphaFieldNames,
     574          448 :                             s_ipsc->cNumericFieldNames);
     575              : 
     576          448 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     577              : 
     578          448 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     579          448 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleCool](idx);
     580          448 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     581              : 
     582          448 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     583            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     584            0 :             ErrorsFound = true;
     585          448 :         } else if ((setpt.coolSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
     586            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     587            0 :             ErrorsFound = true;
     588              :         }
     589              : 
     590              :     } // SingleTempCoolingControlNum
     591              : 
     592          801 :     s_ipsc->cCurrentModuleObject = setptTypeNames[(int)HVAC::SetptType::SingleHeatCool];
     593          801 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     594              : 
     595          801 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool] > 0) {
     596           11 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeatCool].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool]);
     597              :     }
     598              : 
     599          812 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool]; ++idx) {
     600           22 :         s_ip->getObjectItem(state,
     601           11 :                             s_ipsc->cCurrentModuleObject,
     602              :                             idx,
     603           11 :                             s_ipsc->cAlphaArgs,
     604              :                             NumAlphas,
     605           11 :                             s_ipsc->rNumericArgs,
     606              :                             NumNums,
     607              :                             IOStat,
     608           11 :                             s_ipsc->lNumericFieldBlanks,
     609           11 :                             s_ipsc->lAlphaFieldBlanks,
     610           11 :                             s_ipsc->cAlphaFieldNames,
     611           11 :                             s_ipsc->cNumericFieldNames);
     612              : 
     613           11 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     614              : 
     615           11 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeatCool](idx);
     616           11 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     617              : 
     618           11 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     619            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     620            0 :             ErrorsFound = true;
     621           11 :         } else if ((setpt.heatSched = setpt.coolSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
     622            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     623            0 :             ErrorsFound = true;
     624              :         }
     625              : 
     626              :     } // SingleTempHeatCoolControlNum
     627              : 
     628          801 :     s_ipsc->cCurrentModuleObject = setptTypeNames[(int)HVAC::SetptType::DualHeatCool];
     629          801 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     630              : 
     631          801 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool] > 0) {
     632          499 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::DualHeatCool].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool]);
     633              :     }
     634              : 
     635         3402 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool]; ++idx) {
     636         5202 :         s_ip->getObjectItem(state,
     637         2601 :                             s_ipsc->cCurrentModuleObject,
     638              :                             idx,
     639         2601 :                             s_ipsc->cAlphaArgs,
     640              :                             NumAlphas,
     641         2601 :                             s_ipsc->rNumericArgs,
     642              :                             NumNums,
     643              :                             IOStat,
     644         2601 :                             s_ipsc->lNumericFieldBlanks,
     645         2601 :                             s_ipsc->lAlphaFieldBlanks,
     646         2601 :                             s_ipsc->cAlphaFieldNames,
     647         2601 :                             s_ipsc->cNumericFieldNames);
     648              : 
     649         2601 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     650         2601 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     651         2601 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::DualHeatCool](idx);
     652         2601 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     653              : 
     654         2601 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     655            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     656            0 :             ErrorsFound = true;
     657         2601 :         } else if ((setpt.heatSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
     658            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     659            0 :             ErrorsFound = true;
     660              :         }
     661              : 
     662         2601 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
     663            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
     664            0 :             ErrorsFound = true;
     665         2601 :         } else if ((setpt.coolSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
     666            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     667            0 :             ErrorsFound = true;
     668              :         }
     669              : 
     670              :     } // DualTempHeatCoolControlNum
     671              : 
     672              :     // Finish filling in Schedule pointing indexes
     673         5134 :     for (TempControlledZoneNum = 1; TempControlledZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++TempControlledZoneNum) {
     674         4333 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     675              : 
     676        21665 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
     677        17332 :             auto &setpt = tempZone.setpts[(int)setptType];
     678        17332 :             if (!setpt.isUsed) {
     679        11191 :                 continue;
     680              :             }
     681              : 
     682         6141 :             int setptIdx = Util::FindItem(setpt.Name, s_ztpc->tempSetptScheds[(int)setptType]);
     683              : 
     684         6141 :             if (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
     685              :                 setptType == HVAC::SetptType::DualHeatCool) {
     686         4997 :                 setpt.heatSetptSched = s_ztpc->tempSetptScheds[(int)setptType](setptIdx).heatSched;
     687              :             }
     688              : 
     689         6141 :             if (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
     690              :                 setptType == HVAC::SetptType::DualHeatCool) {
     691         4983 :                 setpt.coolSetptSched = s_ztpc->tempSetptScheds[(int)setptType](setptIdx).coolSched;
     692              :             }
     693              :         }
     694              :     }
     695              : 
     696              :     // Now, Check the schedule values/indices for validity
     697              : 
     698         5134 :     for (int TempControlledZoneNum = 1; TempControlledZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++TempControlledZoneNum) {
     699         4333 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     700              : 
     701         4333 :         if (tempZone.setptTypeSched == nullptr) {
     702            0 :             continue; // error will be caught elsewhere
     703              :         }
     704              : 
     705         4333 :         int SchedMin = tempZone.setptTypeSched->getMinVal(state);
     706         4333 :         int SchedMax = tempZone.setptTypeSched->getMaxVal(state);
     707              : 
     708         4333 :         if (SchedMin == (int)HVAC::SetptType::Uncontrolled && SchedMax == (int)HVAC::SetptType::Uncontrolled) {
     709            0 :             if (FindNumberInList(tempZone.setptTypeSched->Num, CTSchedMapToControlledZone, state.dataZoneCtrls->NumTempControlledZones) == 0) {
     710            0 :                 ShowSevereError(state, format("Control Type Schedule={}", tempZone.setptTypeSched->Name));
     711            0 :                 ShowContinueError(state, "..specifies control type 0 for all entries.");
     712            0 :                 ShowContinueError(state, "All zones using this Control Type Schedule have no heating or cooling available.");
     713              :             }
     714            0 :             CTSchedMapToControlledZone(TempControlledZoneNum) = tempZone.setptTypeSched->Num;
     715              :         }
     716              : 
     717        21665 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
     718        17332 :             auto const &setpt = tempZone.setpts[(int)setptType];
     719              : 
     720        17332 :             if (!setpt.isUsed) {
     721              :                 // Catch early issues
     722        11191 :                 if (tempZone.setptTypeSched->hasVal(state, (int)setptType)) {
     723            0 :                     ShowSevereError(state, format("Control Type Schedule={}", tempZone.setptTypeSched->Name));
     724            0 :                     ShowContinueError(
     725              :                         state,
     726            0 :                         format("..specifies {} ({}) as the control type. Not valid for this zone.", (int)setptType, setptTypeNames[(int)setptType]));
     727            0 :                     ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), tempZone.Name));
     728            0 :                     ShowContinueError(state, format("..reference ZONE={}", tempZone.ZoneName));
     729            0 :                     ErrorsFound = true;
     730              :                 }
     731        11191 :                 continue;
     732              :             }
     733              : 
     734         1144 :             if (setpt.heatSetptSched == nullptr &&
     735         1144 :                 (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
     736         7285 :                  setptType == HVAC::SetptType::DualHeatCool) &&
     737            0 :                 tempZone.setptTypeSched->hasVal(state, (int)setptType)) {
     738            0 :                 ShowSevereError(state, format("Control Type Schedule={}", tempZone.setptTypeSched->Name));
     739            0 :                 ShowContinueError(
     740              :                     state,
     741            0 :                     format("..specifies {} ({}) as the control type. Not valid for this zone.", (int)setptType, setptTypeNames[(int)setptType]));
     742            0 :                 ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), tempZone.Name));
     743            0 :                 ShowContinueError(state, format("..reference ZONE={}", tempZone.ZoneName));
     744            0 :                 ErrorsFound = true;
     745              :             }
     746              : 
     747         1158 :             if (setpt.coolSetptSched == nullptr &&
     748         1158 :                 (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
     749         7299 :                  setptType == HVAC::SetptType::DualHeatCool) &&
     750            0 :                 tempZone.setptTypeSched->hasVal(state, (int)setptType)) {
     751            0 :                 ShowSevereError(state, format("Control Type Schedule={}", tempZone.setptTypeSched->Name));
     752            0 :                 ShowContinueError(
     753              :                     state,
     754            0 :                     format("..specifies {} ({}) as the control type. Not valid for this zone.", (int)setptType, setptTypeNames[(int)setptType]));
     755            0 :                 ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), tempZone.Name));
     756            0 :                 ShowContinueError(state, format("..reference ZONE={}", tempZone.ZoneName));
     757            0 :                 ErrorsFound = true;
     758              :             }
     759              :         } // for (setptType)
     760              :     }
     761              : 
     762         5134 :     for (TempControlledZoneNum = 1; TempControlledZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++TempControlledZoneNum) {
     763         4333 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     764         4333 :         ActualZoneNum = tempZone.ActualZoneNum;
     765              : 
     766         4333 :         if (tempZone.setptTypeSched == nullptr) {
     767            0 :             continue; // error caught elsewhere -- would just be confusing here
     768              :         }
     769              : 
     770        21665 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
     771        17332 :             if (TStatControlTypes(TempControlledZoneNum).MustHave[(int)setptType] &&
     772            0 :                 TStatControlTypes(TempControlledZoneNum).DidHave[(int)setptType]) {
     773            0 :                 continue;
     774              :             }
     775              : 
     776        17332 :             if (!TStatControlTypes(TempControlledZoneNum).MustHave[(int)setptType]) {
     777        17332 :                 continue;
     778              :             }
     779            0 :             ShowWarningError(state, format("Schedule={}", tempZone.setptTypeSched->Name));
     780            0 :             ShowContinueError(state, format("...should include control type {} ({}) but does not.", (int)setptType, setptTypeNames[(int)setptType]));
     781            0 :             ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), tempZone.Name));
     782            0 :             ShowContinueError(state, format("..reference ZONE={}", tempZone.ZoneName));
     783              :         }
     784              :     }
     785              : 
     786          801 :     if (allocated(TStatControlTypes)) {
     787          690 :         TStatControlTypes.deallocate();
     788              :     }
     789              :     // This starts the Humidity Control Get Input section
     790          801 :     s_ipsc->cCurrentModuleObject = cZControlTypes(static_cast<int>(ZoneControlTypes::HStat));
     791          801 :     state.dataZoneCtrls->NumHumidityControlZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     792              : 
     793          801 :     if (state.dataZoneCtrls->NumHumidityControlZones > 0) {
     794           92 :         state.dataZoneCtrls->HumidityControlZone.allocate(state.dataZoneCtrls->NumHumidityControlZones);
     795           92 :         s_ztpc->HumidityControlZoneUniqueNames.reserve(static_cast<unsigned>(state.dataZoneCtrls->NumHumidityControlZones));
     796              :     }
     797              : 
     798          987 :     for (HumidControlledZoneNum = 1; HumidControlledZoneNum <= state.dataZoneCtrls->NumHumidityControlZones; ++HumidControlledZoneNum) {
     799          372 :         s_ip->getObjectItem(state,
     800          186 :                             s_ipsc->cCurrentModuleObject,
     801              :                             HumidControlledZoneNum,
     802          186 :                             s_ipsc->cAlphaArgs,
     803              :                             NumAlphas,
     804          186 :                             s_ipsc->rNumericArgs,
     805              :                             NumNums,
     806              :                             IOStat,
     807          186 :                             s_ipsc->lNumericFieldBlanks,
     808          186 :                             s_ipsc->lAlphaFieldBlanks,
     809          186 :                             s_ipsc->cAlphaFieldNames,
     810          186 :                             s_ipsc->cNumericFieldNames);
     811              : 
     812          186 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     813              : 
     814          186 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     815              : 
     816          186 :         auto &humidControlledZone = state.dataZoneCtrls->HumidityControlZone(HumidControlledZoneNum);
     817          186 :         humidControlledZone.ControlName = s_ipsc->cAlphaArgs(1);
     818          186 :         GlobalNames::IntraObjUniquenessCheck(state,
     819          186 :                                              s_ipsc->cAlphaArgs(2),
     820          186 :                                              s_ipsc->cCurrentModuleObject,
     821          186 :                                              s_ipsc->cAlphaFieldNames(2),
     822          186 :                                              s_ztpc->HumidityControlZoneUniqueNames,
     823              :                                              ErrorsFound);
     824              : 
     825          186 :         humidControlledZone.ZoneName = s_ipsc->cAlphaArgs(2);
     826          186 :         humidControlledZone.ActualZoneNum = Util::FindItem(s_ipsc->cAlphaArgs(2), Zone);
     827          186 :         if (humidControlledZone.ActualZoneNum == 0) {
     828            0 :             ShowSevereError(state,
     829            0 :                             format("{}=\"{} invalid {}=\"{}\" not found.",
     830            0 :                                    s_ipsc->cCurrentModuleObject,
     831            0 :                                    s_ipsc->cAlphaArgs(1),
     832            0 :                                    s_ipsc->cAlphaFieldNames(2),
     833            0 :                                    s_ipsc->cAlphaArgs(2)));
     834            0 :             ErrorsFound = true;
     835              :         } else {
     836          186 :             state.dataHeatBal->Zone(humidControlledZone.ActualZoneNum).humidityControlZoneIndex = HumidControlledZoneNum;
     837              :         }
     838              : 
     839          186 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
     840            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
     841            0 :             ErrorsFound = true;
     842          186 :         } else if ((humidControlledZone.humidifyingSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
     843            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     844            0 :             ErrorsFound = true;
     845              :         }
     846              : 
     847          186 :         if (NumAlphas < 4 || s_ipsc->lAlphaFieldBlanks(4)) {
     848           70 :             humidControlledZone.dehumidifyingSched = humidControlledZone.humidifyingSched;
     849          116 :         } else if ((humidControlledZone.dehumidifyingSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
     850            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
     851            0 :             ErrorsFound = true;
     852              :         }
     853              : 
     854              :     } // HumidControlledZoneNum
     855              : 
     856              :     // Start to read Thermal comfort control objects
     857          801 :     s_ipsc->cCurrentModuleObject = cZControlTypes(static_cast<int>(ZoneControlTypes::TCTStat));
     858          801 :     state.dataZoneCtrls->NumComfortTStatStatements = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     859          801 :     ComfortTStatObjects.allocate(state.dataZoneCtrls->NumComfortTStatStatements);
     860              : 
     861              :     // Pre-scan for use of Zone lists in TStat statements (i.e. Global application of TStat)
     862          801 :     state.dataZoneCtrls->NumComfortControlledZones = 0;
     863          801 :     errFlag = false;
     864          802 :     for (Item = 1; Item <= state.dataZoneCtrls->NumComfortTStatStatements; ++Item) {
     865            2 :         s_ip->getObjectItem(state,
     866            1 :                             s_ipsc->cCurrentModuleObject,
     867              :                             Item,
     868            1 :                             s_ipsc->cAlphaArgs,
     869              :                             NumAlphas,
     870            1 :                             s_ipsc->rNumericArgs,
     871              :                             NumNums,
     872              :                             IOStat,
     873            1 :                             s_ipsc->lNumericFieldBlanks,
     874            1 :                             s_ipsc->lAlphaFieldBlanks,
     875            1 :                             s_ipsc->cAlphaFieldNames,
     876            1 :                             s_ipsc->cNumericFieldNames);
     877            1 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     878              : 
     879            1 :         Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     880            1 :         ZLItem = 0;
     881            1 :         if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
     882            0 :             ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
     883              :         }
     884            1 :         ComfortTStatObjects(Item).Name = s_ipsc->cAlphaArgs(1);
     885            1 :         if (Item1 > 0) {
     886            1 :             ComfortTStatObjects(Item).ComfortControlledZoneStartPtr = state.dataZoneCtrls->NumComfortControlledZones + 1;
     887            1 :             ++state.dataZoneCtrls->NumComfortControlledZones;
     888            1 :             ComfortTStatObjects(Item).NumOfZones = 1;
     889            1 :             ComfortTStatObjects(Item).ZoneListActive = false;
     890            1 :             ComfortTStatObjects(Item).ZoneOrZoneListPtr = Item1;
     891            0 :         } else if (ZLItem > 0) {
     892            0 :             auto const &ZoneList = state.dataHeatBal->ZoneList(ZLItem);
     893            0 :             ComfortTStatObjects(Item).ComfortControlledZoneStartPtr = state.dataZoneCtrls->NumComfortControlledZones + 1;
     894            0 :             state.dataZoneCtrls->NumComfortControlledZones += ZoneList.NumOfZones;
     895            0 :             ComfortTStatObjects(Item).NumOfZones = ZoneList.NumOfZones;
     896            0 :             ComfortTStatObjects(Item).ZoneListActive = true;
     897            0 :             ComfortTStatObjects(Item).ZoneOrZoneListPtr = ZLItem;
     898              :         } else {
     899            0 :             ShowSevereError(state,
     900            0 :                             format("{}=\"{}\" invalid {}=\"{}\" not found.",
     901            0 :                                    s_ipsc->cCurrentModuleObject,
     902            0 :                                    s_ipsc->cAlphaArgs(1),
     903            0 :                                    s_ipsc->cAlphaFieldNames(2),
     904            0 :                                    s_ipsc->cAlphaArgs(2)));
     905            0 :             errFlag = true;
     906            0 :             ErrorsFound = true;
     907              :         }
     908              :     }
     909              : 
     910          801 :     if (errFlag) {
     911            0 :         ShowSevereError(state, format("GetZoneAirSetpoints: Errors with invalid names in {} objects.", s_ipsc->cCurrentModuleObject));
     912            0 :         ShowContinueError(state, "...These will not be read in.  Other errors may occur.");
     913            0 :         state.dataZoneCtrls->NumComfortControlledZones = 0;
     914              :     }
     915              : 
     916          801 :     if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
     917            1 :         state.dataZoneCtrls->ComfortControlledZone.allocate(state.dataZoneCtrls->NumComfortControlledZones);
     918            1 :         TComfortControlTypes.allocate(state.dataZoneCtrls->NumComfortControlledZones); // Number of set point types
     919            1 :         CCmSchedMapToControlledZone.dimension(state.dataZoneCtrls->NumComfortControlledZones, 0);
     920              : 
     921            1 :         ComfortControlledZoneNum = 0;
     922            2 :         for (Item = 1; Item <= state.dataZoneCtrls->NumComfortTStatStatements; ++Item) {
     923            2 :             s_ip->getObjectItem(state,
     924            1 :                                 s_ipsc->cCurrentModuleObject,
     925              :                                 Item,
     926            1 :                                 s_ipsc->cAlphaArgs,
     927              :                                 NumAlphas,
     928            1 :                                 s_ipsc->rNumericArgs,
     929              :                                 NumNums,
     930              :                                 IOStat,
     931            1 :                                 s_ipsc->lNumericFieldBlanks,
     932            1 :                                 s_ipsc->lAlphaFieldBlanks,
     933            1 :                                 s_ipsc->cAlphaFieldNames,
     934            1 :                                 s_ipsc->cNumericFieldNames);
     935              : 
     936            1 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     937              : 
     938            2 :             for (Item1 = 1; Item1 <= ComfortTStatObjects(Item).NumOfZones; ++Item1) {
     939            1 :                 ++ComfortControlledZoneNum;
     940              : 
     941            1 :                 auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
     942              : 
     943            1 :                 if (ComfortTStatObjects(Item).ZoneListActive) {
     944            0 :                     s_ipsc->cAlphaArgs(2) = state.dataHeatBal->Zone(ZoneList(ComfortTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name;
     945              :                 }
     946            1 :                 int ZoneAssigned = Util::FindItemInList(s_ipsc->cAlphaArgs(2),
     947            1 :                                                         state.dataZoneCtrls->ComfortControlledZone,
     948              :                                                         &DataZoneControls::ZoneComfortControls::ZoneName,
     949              :                                                         ComfortControlledZoneNum - 1);
     950            1 :                 if (ZoneAssigned == 0) {
     951            1 :                     comfortZone.ZoneName = s_ipsc->cAlphaArgs(2);
     952            1 :                     comfortZone.ActualZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     953            1 :                     if (comfortZone.ActualZoneNum == 0) {
     954            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     955            0 :                         ErrorsFound = true;
     956              :                     }
     957              :                 } else {
     958            0 :                     comfortZone.ZoneName = s_ipsc->cAlphaArgs(2); // for continuity
     959            0 :                     ShowSevereDuplicateAssignment(state,
     960              :                                                   eoh,
     961            0 :                                                   s_ipsc->cAlphaFieldNames(2),
     962            0 :                                                   s_ipsc->cAlphaArgs(2),
     963            0 :                                                   state.dataZoneCtrls->ComfortControlledZone(ZoneAssigned).Name);
     964            0 :                     ErrorsFound = true;
     965            0 :                     continue;
     966              :                 }
     967              : 
     968            1 :                 if (!ComfortTStatObjects(Item).ZoneListActive) {
     969            1 :                     comfortZone.Name = s_ipsc->cAlphaArgs(1);
     970              :                 } else {
     971            0 :                     comfortZone.Name = state.dataHeatBal->Zone(ZoneList(ComfortTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name + ' ' +
     972            0 :                                        ComfortTStatObjects(Item).Name;
     973              :                 }
     974              : 
     975              :                 // Read Fields A3 and A4 for averaging method
     976            1 :                 int IZoneCount = 0;
     977            4 :                 for (i = 1; i <= state.dataHeatBal->TotPeople; ++i) {
     978            3 :                     if (comfortZone.ActualZoneNum == state.dataHeatBal->People(i).ZonePtr) {
     979            1 :                         ++IZoneCount;
     980              :                     }
     981              :                 }
     982              :                 // Could not find a people object for this particular zone
     983            1 :                 if (IZoneCount == 0 && comfortZone.ActualZoneNum > 0) {
     984            0 :                     ShowSevereError(state,
     985            0 :                                     format("{}=\"{} no PEOPLE in {}=\"{}\" - cannot use Comfort Control.",
     986            0 :                                            s_ipsc->cCurrentModuleObject,
     987            0 :                                            s_ipsc->cAlphaArgs(1),
     988            0 :                                            s_ipsc->cAlphaFieldNames(2),
     989            0 :                                            s_ipsc->cAlphaArgs(2)));
     990            0 :                     ErrorsFound = true;
     991              :                 }
     992              : 
     993            1 :                 comfortZone.averageMethod = DataZoneControls::AverageMethod::NO;
     994            1 :                 if (IZoneCount > 1) {
     995            0 :                     comfortZone.averageMethod =
     996            0 :                         static_cast<DataZoneControls::AverageMethod>(getEnumValue(DataZoneControls::averageMethodNamesUC, s_ipsc->cAlphaArgs(3)));
     997            0 :                     if (comfortZone.averageMethod == DataZoneControls::AverageMethod::Invalid) {
     998            0 :                         ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     999            0 :                         ErrorsFound = true;
    1000            0 :                     } else if (comfortZone.averageMethod == DataZoneControls::AverageMethod::SPE) {
    1001            0 :                         comfortZone.AverageObjectName = s_ipsc->cAlphaArgs(4);
    1002            0 :                         if (Util::FindItem(s_ipsc->cAlphaArgs(4), state.dataHeatBal->People) == 0) {
    1003            0 :                             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    1004            0 :                             ErrorsFound = true;
    1005              :                         } else {
    1006            0 :                             comfortZone.SpecificObjectNum = Util::FindItem(s_ipsc->cAlphaArgs(4), state.dataHeatBal->People);
    1007              :                         }
    1008              :                     }
    1009              :                 } else {
    1010            2 :                     for (i = 1; i <= state.dataHeatBal->TotPeople; ++i) {
    1011            2 :                         if (comfortZone.ActualZoneNum == state.dataHeatBal->People(i).ZonePtr) {
    1012            1 :                             break;
    1013              :                         }
    1014              :                     }
    1015            1 :                     comfortZone.SpecificObjectNum = i;
    1016              :                 }
    1017              :                 // Check values used for thermal comfort calculation
    1018            4 :                 for (i = 1; i <= state.dataHeatBal->TotPeople; ++i) {
    1019            3 :                     auto &people = state.dataHeatBal->People(i);
    1020              : 
    1021            3 :                     if (comfortZone.ActualZoneNum != people.ZonePtr) {
    1022            2 :                         continue;
    1023              :                     }
    1024              : 
    1025              :                     // Check activity level
    1026            1 :                     if (people.activityLevelSched == nullptr) {
    1027            0 :                         ShowSevereError(state, format("GetPeople Activity Level: Activity level schedule is not found={}", people.Name));
    1028            0 :                         ShowContinueError(state, "Required when the zone has Thermal Comfort Controls.");
    1029            0 :                         ErrorsFound = true;
    1030            1 :                     } else if (!people.activityLevelSched->checkMinMaxVals(state, Clusive::In, 72.0, Clusive::In, 909.0)) {
    1031            0 :                         ShowSevereError(state, "GetPeople Activity Level: Invalid activity level values entered for thermal comfort calculation");
    1032            0 :                         ShowContinueError(state, format("Outside of range values [72,909], Reference object={}", people.Name));
    1033              :                     }
    1034              : 
    1035              :                     // Check Work Efficiency
    1036            1 :                     if (people.workEffSched == nullptr) {
    1037            0 :                         ShowSevereError(state, format("GetPeople work efficiency: Work efficiency schedule is not found={}", people.Name));
    1038            0 :                         ShowContinueError(state, "Required when the zone has Thermal Comfort Controls.");
    1039            0 :                         ErrorsFound = true;
    1040            1 :                     } else if (!people.workEffSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
    1041            0 :                         ShowSevereError(state, "GetPeople work efficiency: Invalid work efficiency values entered for thermal comfort calculation");
    1042            0 :                         ShowContinueError(state, format("Outside of range values [0,1], Reference object={}", people.Name));
    1043            0 :                         ErrorsFound = true;
    1044              :                     }
    1045              : 
    1046              :                     // Check Clothing Insulation
    1047            1 :                     if (people.clothingSched == nullptr) {
    1048            0 :                         ShowSevereError(state, format("GetPeople Clothing Insulation: Clothing Insulation schedule is not found={}", people.Name));
    1049            0 :                         ShowContinueError(state, "Required when the zone has Thermal Comfort Controls.");
    1050            0 :                         ErrorsFound = true;
    1051            1 :                     } else if (!people.clothingSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 2.0)) {
    1052            0 :                         ShowSevereError(state,
    1053              :                                         "GetPeople Clothing Insulation: Invalid Clothing Insulation values entered for thermal comfort calculation");
    1054            0 :                         ShowContinueError(state, format("Outside of range values [0.0,2.0], Reference object={}", people.Name));
    1055            0 :                         ErrorsFound = true;
    1056              :                     }
    1057              : 
    1058              :                     // Check Air velocity
    1059            1 :                     if (people.airVelocitySched == nullptr) {
    1060            0 :                         ShowSevereError(state, format("GetPeople Air Velocity: Air velocity schedule is not found={}", people.Name));
    1061            0 :                         ShowContinueError(state, "Required when the zone has Thermal Comfort Controls.");
    1062            0 :                         ErrorsFound = true;
    1063              :                     }
    1064              :                 }
    1065              : 
    1066              :                 // Read Max and Min temperature setpoint
    1067            1 :                 if (NumNums > 0) {
    1068            1 :                     comfortZone.TdbMinSetPoint = s_ipsc->rNumericArgs(1);
    1069            1 :                     if (s_ipsc->rNumericArgs(1) > 50 || s_ipsc->rNumericArgs(1) < 0) {
    1070            0 :                         ShowSevereError(state,
    1071            0 :                                         format("{}=\"{} invalid {}=[{:.0T}].",
    1072            0 :                                                s_ipsc->cCurrentModuleObject,
    1073            0 :                                                s_ipsc->cAlphaArgs(1),
    1074            0 :                                                s_ipsc->cNumericFieldNames(1),
    1075            0 :                                                s_ipsc->rNumericArgs(1)));
    1076            0 :                         ShowContinueError(state, "..Allowable values must be between 0 C and 50 C");
    1077            0 :                         ErrorsFound = true;
    1078              :                     }
    1079              :                 }
    1080            1 :                 if (NumNums > 1) {
    1081            1 :                     comfortZone.TdbMaxSetPoint = s_ipsc->rNumericArgs(2);
    1082            1 :                     if (s_ipsc->rNumericArgs(2) > 50 || s_ipsc->rNumericArgs(2) < 0) {
    1083            0 :                         ShowSevereError(state,
    1084            0 :                                         format("{}=\"{} invalid {}=[{:.0T}].",
    1085            0 :                                                s_ipsc->cCurrentModuleObject,
    1086            0 :                                                s_ipsc->cAlphaArgs(1),
    1087            0 :                                                s_ipsc->cNumericFieldNames(2),
    1088            0 :                                                s_ipsc->rNumericArgs(2)));
    1089            0 :                         ShowContinueError(state, "..Allowable values must be between 0 C and 50 C");
    1090            0 :                         ErrorsFound = true;
    1091              :                     }
    1092              :                 }
    1093              :                 // Ensure MaxTemp >= MinTemp
    1094            1 :                 if (comfortZone.TdbMinSetPoint > comfortZone.TdbMaxSetPoint) {
    1095            0 :                     ShowSevereError(state, format("{}=\"{}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    1096            0 :                     ShowContinueError(state, format("..{} > {}", s_ipsc->cNumericFieldNames(1), s_ipsc->cNumericFieldNames(2)));
    1097            0 :                     ShowContinueError(state, format("..[{:.0T}] > [{:.0T}].", s_ipsc->rNumericArgs(1), s_ipsc->rNumericArgs(2)));
    1098            0 :                     ErrorsFound = true;
    1099              :                 }
    1100              :                 // If MaxTemp = MinTemp, no thermal comfort control
    1101            1 :                 if (comfortZone.TdbMinSetPoint == comfortZone.TdbMaxSetPoint) {
    1102            0 :                     ShowSevereError(state, format("{}=\"{}", s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)));
    1103            0 :                     ShowContinueError(state, format("..{} = {}", s_ipsc->cNumericFieldNames(1), s_ipsc->cNumericFieldNames(2)));
    1104            0 :                     ShowContinueError(state, "The zone will be controlled using this dry-bulb temperature setpoint.");
    1105              :                 }
    1106              : 
    1107              :                 // read Thermal comfort type schedule name
    1108            1 :                 if (s_ipsc->lAlphaFieldBlanks(5)) {
    1109            0 :                     ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
    1110            0 :                     ErrorsFound = true;
    1111            1 :                 } else if ((comfortZone.setptTypeSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(5))) == nullptr) {
    1112            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
    1113            0 :                     ErrorsFound = true;
    1114            1 :                 } else if (!comfortZone.setptTypeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 4.0)) {
    1115            0 :                     Sched::ShowSevereBadMinMax(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), Clusive::In, 0.0, Clusive::In, 4.0);
    1116            0 :                     ErrorsFound = true;
    1117              :                 }
    1118              : 
    1119            1 :                 int NumSetptTypes = nint((NumAlphas - 5.0) / 2.0);
    1120              : 
    1121            5 :                 for (int iSetptType = 1; iSetptType <= NumSetptTypes; ++iSetptType) {
    1122              : 
    1123            4 :                     int ctIdx = 2 * iSetptType - 1 + 5;
    1124              : 
    1125            4 :                     HVAC::SetptType setptType = HVAC::SetptType::Invalid;
    1126            4 :                     if (s_ipsc->lAlphaFieldBlanks(ctIdx)) {
    1127            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(ctIdx));
    1128            0 :                         ErrorsFound = true;
    1129            0 :                         continue;
    1130            4 :                     } else if ((setptType = static_cast<HVAC::SetptType>(getEnumValue(comfortSetptTypeNamesUC, s_ipsc->cAlphaArgs(ctIdx)))) ==
    1131              :                                HVAC::SetptType::Invalid) {
    1132            0 :                         ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(ctIdx), s_ipsc->cAlphaFieldNames(ctIdx));
    1133            0 :                         ErrorsFound = true;
    1134            0 :                         continue;
    1135              :                     }
    1136              : 
    1137            4 :                     auto &setpt = comfortZone.setpts[(int)setptType];
    1138            4 :                     setpt.Name = s_ipsc->cAlphaArgs(nint(2.0 * iSetptType + 5));
    1139            4 :                     setpt.isUsed = true;
    1140              :                 }
    1141              :             }
    1142              :         } // NumComfortTStatStatements
    1143              :     }
    1144              :     // End of Thermal comfort control reading and checking
    1145              : 
    1146          801 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::SingleHeat];
    1147          801 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1148              : 
    1149          801 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat] > 0) {
    1150            1 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeat].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat]);
    1151              :     }
    1152              : 
    1153          802 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat]; ++idx) {
    1154            2 :         s_ip->getObjectItem(state,
    1155            1 :                             s_ipsc->cCurrentModuleObject,
    1156              :                             idx,
    1157            1 :                             s_ipsc->cAlphaArgs,
    1158              :                             NumAlphas,
    1159            1 :                             s_ipsc->rNumericArgs,
    1160              :                             NumNums,
    1161              :                             IOStat,
    1162            1 :                             s_ipsc->lNumericFieldBlanks,
    1163            1 :                             s_ipsc->lAlphaFieldBlanks,
    1164            1 :                             s_ipsc->cAlphaFieldNames,
    1165            1 :                             s_ipsc->cNumericFieldNames);
    1166              : 
    1167            1 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1168              : 
    1169            1 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1170            1 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeat](idx);
    1171            1 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1172              : 
    1173            1 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1174            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1175            0 :             ErrorsFound = true;
    1176            1 :         } else if ((setpt.heatSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    1177            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1178            0 :             ErrorsFound = true;
    1179            1 :         } else if (!setpt.heatSched->checkMinMaxVals(state, Clusive::In, -3.0, Clusive::In, 3.0)) {
    1180            0 :             Sched::ShowSevereBadMinMax(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2), Clusive::In, -3.0, Clusive::In, 3.0);
    1181            0 :             ErrorsFound = true;
    1182              :         }
    1183              :     } // SingleFangerHeatingControlNum
    1184              : 
    1185          801 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::SingleCool];
    1186          801 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1187              : 
    1188          801 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool] > 0) {
    1189            1 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleCool].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool]);
    1190              :     }
    1191              : 
    1192          802 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool]; ++idx) {
    1193            2 :         s_ip->getObjectItem(state,
    1194            1 :                             s_ipsc->cCurrentModuleObject,
    1195              :                             idx,
    1196            1 :                             s_ipsc->cAlphaArgs,
    1197              :                             NumAlphas,
    1198            1 :                             s_ipsc->rNumericArgs,
    1199              :                             NumNums,
    1200              :                             IOStat,
    1201            1 :                             s_ipsc->lNumericFieldBlanks,
    1202            1 :                             s_ipsc->lAlphaFieldBlanks,
    1203            1 :                             s_ipsc->cAlphaFieldNames,
    1204            1 :                             s_ipsc->cNumericFieldNames);
    1205              : 
    1206            1 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1207              : 
    1208            1 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1209            1 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleCool](idx);
    1210            1 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1211              : 
    1212            1 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1213            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1214            0 :             ErrorsFound = true;
    1215            1 :         } else if ((setpt.coolSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    1216            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1217            0 :             ErrorsFound = true;
    1218            1 :         } else if (!setpt.coolSched->checkMinMaxVals(state, Clusive::In, -3.0, Clusive::In, 3.0)) {
    1219            0 :             Sched::ShowSevereBadMinMax(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2), Clusive::In, -3.0, Clusive::In, 3.0);
    1220            0 :             ErrorsFound = true;
    1221              :         }
    1222              : 
    1223              :     } // SingleFangerCoolingControlNum
    1224              : 
    1225          801 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::SingleHeatCool];
    1226          801 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1227              : 
    1228          801 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool] > 0) {
    1229            1 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeatCool].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool]);
    1230              :     }
    1231              : 
    1232          802 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool]; ++idx) {
    1233            2 :         s_ip->getObjectItem(state,
    1234            1 :                             s_ipsc->cCurrentModuleObject,
    1235              :                             idx,
    1236            1 :                             s_ipsc->cAlphaArgs,
    1237              :                             NumAlphas,
    1238            1 :                             s_ipsc->rNumericArgs,
    1239              :                             NumNums,
    1240              :                             IOStat,
    1241            1 :                             s_ipsc->lNumericFieldBlanks,
    1242            1 :                             s_ipsc->lAlphaFieldBlanks,
    1243            1 :                             s_ipsc->cAlphaFieldNames,
    1244            1 :                             s_ipsc->cNumericFieldNames);
    1245              : 
    1246            1 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1247              : 
    1248            1 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1249            1 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeatCool](idx);
    1250            1 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1251              : 
    1252            1 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1253            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1254            0 :             ErrorsFound = true;
    1255            1 :         } else if ((setpt.heatSched = setpt.coolSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    1256            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1257            0 :             ErrorsFound = true;
    1258            1 :         } else if (!setpt.heatSched->checkMinMaxVals(state, Clusive::In, -3.0, Clusive::In, 3.0)) {
    1259            0 :             Sched::ShowSevereBadMinMax(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2), Clusive::In, -3.0, Clusive::In, 3.0);
    1260            0 :             ErrorsFound = true;
    1261              :         }
    1262              : 
    1263              :     } // SingleFangerHeatCoolControlNum
    1264              : 
    1265          801 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::DualHeatCool];
    1266          801 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1267              : 
    1268          801 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool] > 0) {
    1269            1 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::DualHeatCool].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool]);
    1270              :     }
    1271              : 
    1272          802 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool]; ++idx) {
    1273            2 :         s_ip->getObjectItem(state,
    1274            1 :                             s_ipsc->cCurrentModuleObject,
    1275              :                             idx,
    1276            1 :                             s_ipsc->cAlphaArgs,
    1277              :                             NumAlphas,
    1278            1 :                             s_ipsc->rNumericArgs,
    1279              :                             NumNums,
    1280              :                             IOStat,
    1281            1 :                             s_ipsc->lNumericFieldBlanks,
    1282            1 :                             s_ipsc->lAlphaFieldBlanks,
    1283            1 :                             s_ipsc->cAlphaFieldNames,
    1284            1 :                             s_ipsc->cNumericFieldNames);
    1285              : 
    1286            1 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1287              : 
    1288            1 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1289            1 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::DualHeatCool](idx);
    1290            1 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1291              : 
    1292            1 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1293            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1294            0 :             ErrorsFound = true;
    1295            1 :         } else if ((setpt.heatSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    1296            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1297            0 :             ErrorsFound = true;
    1298            1 :         } else if (!setpt.heatSched->checkMinMaxVals(state, Clusive::In, -3.0, Clusive::In, 3.0)) {
    1299            0 :             Sched::ShowSevereBadMinMax(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2), Clusive::In, -3.0, Clusive::In, 3.0);
    1300            0 :             ErrorsFound = true;
    1301              :         }
    1302              : 
    1303            1 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    1304            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
    1305            0 :             ErrorsFound = true;
    1306            1 :         } else if ((setpt.coolSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
    1307            0 :             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    1308            0 :             ErrorsFound = true;
    1309            1 :         } else if (!setpt.coolSched->checkMinMaxVals(state, Clusive::In, -3.0, Clusive::In, 3.0)) {
    1310            0 :             Sched::ShowSevereBadMinMax(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), Clusive::In, -3.0, Clusive::In, 3.0);
    1311            0 :             ErrorsFound = true;
    1312              :         }
    1313              : 
    1314              :     } // DualFangerHeatCoolControlNum
    1315              : 
    1316              :     // Finish filling in Schedule pointing indexes for Thermal Comfort Control
    1317          802 :     for (ComfortControlledZoneNum = 1; ComfortControlledZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++ComfortControlledZoneNum) {
    1318              : 
    1319            1 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
    1320              : 
    1321            5 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    1322            4 :             auto &setpt = comfortZone.setpts[(int)setptType];
    1323            4 :             if (!setpt.isUsed) {
    1324            0 :                 continue;
    1325              :             }
    1326              : 
    1327            4 :             int setptIdx = Util::FindItem(setpt.Name, s_ztpc->comfortSetptScheds[(int)setptType]);
    1328              : 
    1329            4 :             if (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
    1330              :                 setptType == HVAC::SetptType::DualHeatCool) {
    1331            3 :                 setpt.heatSetptSched = s_ztpc->comfortSetptScheds[(int)setptType](setptIdx).heatSched;
    1332              :             }
    1333              : 
    1334            4 :             if (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
    1335              :                 setptType == HVAC::SetptType::DualHeatCool) {
    1336            3 :                 setpt.coolSetptSched = s_ztpc->comfortSetptScheds[(int)setptType](setptIdx).coolSched;
    1337              :             }
    1338              : 
    1339            4 :             TComfortControlTypes(ComfortControlledZoneNum).MustHave[(int)setptType] = true;
    1340              :         }
    1341              :     }
    1342              : 
    1343              :     // Now, Check the schedule values/indices for validity for Thermal Comfort Control
    1344              : 
    1345          802 :     for (ComfortControlledZoneNum = 1; ComfortControlledZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++ComfortControlledZoneNum) {
    1346            1 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
    1347            1 :         ActualZoneNum = comfortZone.ActualZoneNum;
    1348            1 :         if (comfortZone.setptTypeSched == nullptr) {
    1349            0 :             continue;
    1350              :         }
    1351              : 
    1352            1 :         int SchedMin = comfortZone.setptTypeSched->getMinVal(state);
    1353            1 :         int SchedMax = comfortZone.setptTypeSched->getMaxVal(state);
    1354              : 
    1355            1 :         if (SchedMin == (int)HVAC::SetptType::Uncontrolled && SchedMax == (int)HVAC::SetptType::Uncontrolled) {
    1356            0 :             if (FindNumberInList(comfortZone.setptTypeSched->Num, CCmSchedMapToControlledZone, state.dataZoneCtrls->NumComfortControlledZones) == 0) {
    1357            0 :                 ShowWarningError(state, format("Control Type Schedule={}", comfortZone.setptTypeSched->Name));
    1358            0 :                 ShowContinueError(state, "..specifies control type 0 for all entries.");
    1359            0 :                 ShowContinueError(state, "All zones using this Control Type Schedule have no thermal comfort control.");
    1360              :             }
    1361            0 :             CCmSchedMapToControlledZone(ComfortControlledZoneNum) = comfortZone.setptTypeSched->Num;
    1362              :         }
    1363              : 
    1364            5 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    1365            4 :             auto const &setpt = comfortZone.setpts[(int)setptType];
    1366            4 :             if (!setpt.isUsed) {
    1367            0 :                 continue;
    1368              :             }
    1369              : 
    1370            4 :             TComfortControlTypes(ComfortControlledZoneNum).DidHave[(int)setptType] = true;
    1371              : 
    1372            1 :             if (setpt.heatSetptSched == nullptr &&
    1373            1 :                 (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
    1374            5 :                  setptType == HVAC::SetptType::DualHeatCool) &&
    1375            0 :                 comfortZone.setptTypeSched->hasVal(state, (int)setptType)) {
    1376            0 :                 ShowSevereError(state, format("Control Type Schedule={}", comfortZone.setptTypeSched->Name));
    1377            0 :                 ShowContinueError(
    1378              :                     state,
    1379            0 :                     format("..specifies {} ({}) as the control type. Not valid for this zone.", (int)setptType, setptTypeNames[(int)setptType]));
    1380            0 :                 ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), comfortZone.Name));
    1381            0 :                 ShowContinueError(state, format("..reference ZONE={}", comfortZone.ZoneName));
    1382            0 :                 ErrorsFound = true;
    1383              :             }
    1384              : 
    1385            1 :             if (setpt.coolSetptSched == nullptr &&
    1386            1 :                 (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
    1387            5 :                  setptType == HVAC::SetptType::DualHeatCool) &&
    1388            0 :                 comfortZone.setptTypeSched->hasVal(state, (int)setptType)) {
    1389            0 :                 ShowSevereError(state, format("Control Type Schedule={}", comfortZone.setptTypeSched->Name));
    1390            0 :                 ShowContinueError(
    1391              :                     state,
    1392            0 :                     format("..specifies {} ({}) as the control type. Not valid for this zone.", (int)setptType, setptTypeNames[(int)setptType]));
    1393            0 :                 ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), comfortZone.Name));
    1394            0 :                 ShowContinueError(state, format("..reference ZONE={}", comfortZone.ZoneName));
    1395            0 :                 ErrorsFound = true;
    1396              :             }
    1397              :         } // for (setptType)
    1398              :     } // for (ComfortControlledZoneNum)
    1399              : 
    1400          802 :     for (int ComfortControlledZoneNum = 1; ComfortControlledZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++ComfortControlledZoneNum) {
    1401              : 
    1402            1 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
    1403            1 :         ActualZoneNum = comfortZone.ActualZoneNum;
    1404            1 :         if (comfortZone.setptTypeSched == nullptr) {
    1405            0 :             continue;
    1406              :         }
    1407              : 
    1408            5 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    1409            8 :             if (TComfortControlTypes(ComfortControlledZoneNum).MustHave[(int)setptType] &&
    1410            4 :                 TComfortControlTypes(ComfortControlledZoneNum).DidHave[(int)setptType]) {
    1411            4 :                 continue;
    1412              :             }
    1413              : 
    1414            0 :             if (!TComfortControlTypes(ComfortControlledZoneNum).MustHave[(int)setptType]) {
    1415            0 :                 continue;
    1416              :             }
    1417              : 
    1418            0 :             ShowWarningError(state, format("Schedule={}", comfortZone.setptTypeSched->Name));
    1419            0 :             ShowContinueError(state,
    1420            0 :                               format("...should include control type {} ({}) but does not.", (int)setptType, comfortSetptTypeNames[(int)setptType]));
    1421            0 :             ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TCTStat), comfortZone.Name));
    1422            0 :             ShowContinueError(state, format("...reference ZONE={}", comfortZone.ZoneName));
    1423              :         }
    1424              :     }
    1425              : 
    1426          801 :     if (allocated(TComfortControlTypes)) {
    1427            1 :         TComfortControlTypes.deallocate();
    1428              :     }
    1429              : 
    1430              :     // Get the Hybrid Model setting inputs
    1431          801 :     HybridModel::GetHybridModelZone(state);
    1432              : 
    1433              :     // Default multiplier values
    1434          801 :     Real64 ZoneVolCapMultpSens = 1.0;
    1435          801 :     Real64 ZoneVolCapMultpMoist = 1.0;
    1436          801 :     Real64 ZoneVolCapMultpCO2 = 1.0;
    1437          801 :     Real64 ZoneVolCapMultpGenContam = 1.0;
    1438              : 
    1439              :     // Get the Zone Air Capacitance Multiplier for use in the Predictor-Corrector Procedure
    1440          801 :     s_ipsc->cCurrentModuleObject = "ZoneCapacitanceMultiplier:ResearchSpecial";
    1441          801 :     int NumZoneCapaMultiplier = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject); // Number of ZonesCapacityMultiplier object
    1442          801 :     if (NumZoneCapaMultiplier == 0) {
    1443              :         // Assign default multiplier values to all zones
    1444         5834 :         for (int ZoneNum = 1; ZoneNum <= NumOfZones; ZoneNum++) {
    1445         5071 :             auto &Zone = state.dataHeatBal->Zone(ZoneNum);
    1446         5071 :             Zone.ZoneVolCapMultpSens = ZoneVolCapMultpSens;
    1447         5071 :             Zone.ZoneVolCapMultpMoist = ZoneVolCapMultpMoist;
    1448         5071 :             Zone.ZoneVolCapMultpCO2 = ZoneVolCapMultpCO2;
    1449         5071 :             Zone.ZoneVolCapMultpGenContam = ZoneVolCapMultpGenContam;
    1450              :         }
    1451              : 
    1452              :     } else {
    1453              : 
    1454              :         // Allow user to specify ZoneCapacitanceMultiplier:ResearchSpecial at zone level
    1455              :         // Added by S. Lee and R. Zhang in Oct. 2016.
    1456              :         // Assign the user inputted multipliers to specified zones
    1457           85 :         for (int ZoneCapNum = 1; ZoneCapNum <= NumZoneCapaMultiplier; ZoneCapNum++) {
    1458           94 :             s_ip->getObjectItem(state,
    1459           47 :                                 s_ipsc->cCurrentModuleObject,
    1460              :                                 ZoneCapNum,
    1461           47 :                                 s_ipsc->cAlphaArgs,
    1462              :                                 NumAlphas,
    1463           47 :                                 s_ipsc->rNumericArgs,
    1464              :                                 NumNums,
    1465              :                                 IOStat,
    1466           47 :                                 s_ipsc->lNumericFieldBlanks,
    1467           47 :                                 s_ipsc->lAlphaFieldBlanks,
    1468           47 :                                 s_ipsc->cAlphaFieldNames,
    1469           47 :                                 s_ipsc->cNumericFieldNames);
    1470              : 
    1471           47 :             if (s_ipsc->lAlphaFieldBlanks(2)) {
    1472              :                 // default multiplier values for all the zones not specified (zone or zonelist name field is empty)
    1473           35 :                 ZoneVolCapMultpSens = s_ipsc->rNumericArgs(1);
    1474           35 :                 ZoneVolCapMultpMoist = s_ipsc->rNumericArgs(2);
    1475           35 :                 ZoneVolCapMultpCO2 = s_ipsc->rNumericArgs(3);
    1476           35 :                 ZoneVolCapMultpGenContam = s_ipsc->rNumericArgs(4);
    1477              :             } else {
    1478              :                 // multiplier values for the specified zone(s)
    1479           12 :                 ZLItem = 0;
    1480              : 
    1481           12 :                 Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
    1482           12 :                 if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
    1483            0 :                     ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
    1484              :                 }
    1485           12 :                 if (Item1 > 0) {
    1486           12 :                     int ZoneNum = Item1;
    1487           12 :                     Zone(ZoneNum).FlagCustomizedZoneCap = true;
    1488           12 :                     Zone(ZoneNum).ZoneVolCapMultpSens = s_ipsc->rNumericArgs(1);
    1489           12 :                     Zone(ZoneNum).ZoneVolCapMultpMoist = s_ipsc->rNumericArgs(2);
    1490           12 :                     Zone(ZoneNum).ZoneVolCapMultpCO2 = s_ipsc->rNumericArgs(3);
    1491           12 :                     Zone(ZoneNum).ZoneVolCapMultpGenContam = s_ipsc->rNumericArgs(4);
    1492            0 :                 } else if (ZLItem > 0) {
    1493            0 :                     for (int ZonePtrNum = 1; ZonePtrNum < ZoneList(ZLItem).NumOfZones; ZonePtrNum++) {
    1494            0 :                         int ZoneNum = ZoneList(ZLItem).Zone(ZonePtrNum);
    1495            0 :                         Zone(ZoneNum).FlagCustomizedZoneCap = true;
    1496            0 :                         Zone(ZoneNum).ZoneVolCapMultpSens = s_ipsc->rNumericArgs(1);
    1497            0 :                         Zone(ZoneNum).ZoneVolCapMultpMoist = s_ipsc->rNumericArgs(2);
    1498            0 :                         Zone(ZoneNum).ZoneVolCapMultpCO2 = s_ipsc->rNumericArgs(3);
    1499            0 :                         Zone(ZoneNum).ZoneVolCapMultpGenContam = s_ipsc->rNumericArgs(4);
    1500              :                     }
    1501              : 
    1502              :                 } else {
    1503            0 :                     ShowSevereError(state,
    1504            0 :                                     format("{}=\"{}\" invalid {}=\"{}\" not found.",
    1505            0 :                                            s_ipsc->cCurrentModuleObject,
    1506            0 :                                            s_ipsc->cAlphaArgs(1),
    1507            0 :                                            s_ipsc->cAlphaFieldNames(2),
    1508            0 :                                            s_ipsc->cAlphaArgs(2)));
    1509            0 :                     ErrorsFound = true;
    1510              :                 }
    1511              :             }
    1512              :         }
    1513              : 
    1514              :         // Assign default multiplier values to all the other zones
    1515          168 :         for (int ZoneNum = 1; ZoneNum <= NumOfZones; ZoneNum++) {
    1516          130 :             auto &Zone = state.dataHeatBal->Zone(ZoneNum);
    1517          130 :             if (!Zone.FlagCustomizedZoneCap) {
    1518          118 :                 Zone.ZoneVolCapMultpSens = ZoneVolCapMultpSens;
    1519          118 :                 Zone.ZoneVolCapMultpMoist = ZoneVolCapMultpMoist;
    1520          118 :                 Zone.ZoneVolCapMultpCO2 = ZoneVolCapMultpCO2;
    1521          118 :                 Zone.ZoneVolCapMultpGenContam = ZoneVolCapMultpGenContam;
    1522              :             }
    1523              :         }
    1524              : 
    1525              :         // Calculate the average multiplier value from all zones
    1526              :         {
    1527           38 :             Real64 ZoneVolCapMultpSens_temp = 0.0;
    1528           38 :             Real64 ZoneVolCapMultpMoist_temp = 0.0;
    1529           38 :             Real64 ZoneVolCapMultpCO2_temp = 0.0;
    1530           38 :             Real64 ZoneVolCapMultpGenContam_temp = 0.0;
    1531              : 
    1532          168 :             for (int ZoneNum = 1; ZoneNum <= NumOfZones; ZoneNum++) {
    1533          130 :                 auto const &Zone = state.dataHeatBal->Zone(ZoneNum);
    1534          130 :                 ZoneVolCapMultpSens_temp += Zone.ZoneVolCapMultpSens;
    1535          130 :                 ZoneVolCapMultpMoist_temp += Zone.ZoneVolCapMultpMoist;
    1536          130 :                 ZoneVolCapMultpCO2_temp += Zone.ZoneVolCapMultpCO2;
    1537          130 :                 ZoneVolCapMultpGenContam_temp += Zone.ZoneVolCapMultpGenContam;
    1538              :             }
    1539              : 
    1540           38 :             if (NumOfZones > 0) {
    1541           38 :                 ZoneVolCapMultpSens = ZoneVolCapMultpSens_temp / NumOfZones;
    1542           38 :                 ZoneVolCapMultpMoist = ZoneVolCapMultpMoist_temp / NumOfZones;
    1543           38 :                 ZoneVolCapMultpCO2 = ZoneVolCapMultpCO2_temp / NumOfZones;
    1544           38 :                 ZoneVolCapMultpGenContam = ZoneVolCapMultpGenContam_temp / NumOfZones;
    1545              :             }
    1546              :         }
    1547              :     }
    1548              : 
    1549          801 :     print(state.files.eio, Header);
    1550          801 :     print(state.files.eio, Format_701, ZoneVolCapMultpSens, ZoneVolCapMultpMoist, ZoneVolCapMultpCO2, ZoneVolCapMultpGenContam);
    1551              : 
    1552          801 :     s_ipsc->cCurrentModuleObject = cZControlTypes((int)ZoneControlTypes::OTTStat);
    1553          801 :     state.dataZoneCtrls->NumOpTempControlledZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1554              : 
    1555          801 :     if (state.dataZoneCtrls->NumOpTempControlledZones > 0) {
    1556            3 :         state.dataZoneCtrls->AnyOpTempControl = true;
    1557              : 
    1558            6 :         for (int idx = 1; idx <= state.dataZoneCtrls->NumOpTempControlledZones; ++idx) {
    1559            6 :             s_ip->getObjectItem(state,
    1560            3 :                                 s_ipsc->cCurrentModuleObject,
    1561              :                                 idx,
    1562            3 :                                 s_ipsc->cAlphaArgs,
    1563              :                                 NumAlphas,
    1564            3 :                                 s_ipsc->rNumericArgs,
    1565              :                                 NumNums,
    1566              :                                 IOStat,
    1567            3 :                                 s_ipsc->lNumericFieldBlanks,
    1568            3 :                                 s_ipsc->lAlphaFieldBlanks,
    1569            3 :                                 s_ipsc->cAlphaFieldNames,
    1570            3 :                                 s_ipsc->cNumericFieldNames);
    1571              : 
    1572            3 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1573              : 
    1574              :             // find matching name of  ZONECONTROL:THERMOSTAT object
    1575            3 :             if ((found = Util::FindItem(s_ipsc->cAlphaArgs(1), TStatObjects)) != 0) {
    1576            2 :                 auto const &TStatObjects = state.dataZoneCtrls->TStatObjects(found);
    1577            8 :                 for (Item = 1; Item <= TStatObjects.NumOfZones; ++Item) {
    1578            6 :                     TempControlledZoneNum = TStatObjects.TempControlledZoneStartPtr + Item - 1;
    1579            6 :                     auto &TempControlledZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1580            6 :                     if (state.dataZoneCtrls->NumTempControlledZones == 0) {
    1581            0 :                         continue;
    1582              :                     }
    1583            6 :                     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1584            6 :                     tempZone.OpTempCtrl =
    1585            6 :                         static_cast<DataZoneControls::TempCtrl>(getEnumValue(DataZoneControls::tempCtrlNamesUC, s_ipsc->cAlphaArgs(2)));
    1586              : 
    1587            6 :                     if (Item == 1) {
    1588            2 :                         if (tempZone.OpTempCtrl != DataZoneControls::TempCtrl::Constant &&
    1589            0 :                             tempZone.OpTempCtrl != DataZoneControls::TempCtrl::Scheduled) {
    1590            0 :                             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1591            0 :                             ErrorsFound = true;
    1592              :                         }
    1593              :                     }
    1594              : 
    1595            6 :                     tempZone.FixedRadiativeFraction = s_ipsc->rNumericArgs(1);
    1596              : 
    1597            6 :                     if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1598            0 :                         if (s_ipsc->lAlphaFieldBlanks(3)) {
    1599            0 :                             if (Item == 1) {
    1600            0 :                                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
    1601            0 :                                 ErrorsFound = true;
    1602              :                             }
    1603            0 :                         } else if ((tempZone.opTempRadiativeFractionSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
    1604            0 :                             if (Item == 1) {
    1605            0 :                                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    1606            0 :                                 ErrorsFound = true;
    1607              :                             }
    1608            0 :                         } else if (!tempZone.opTempRadiativeFractionSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::Ex, 0.9)) {
    1609            0 :                             if (Item == 1) {
    1610            0 :                                 Sched::ShowSevereBadMinMax(
    1611            0 :                                     state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), Clusive::In, 0.0, Clusive::Ex, 0.9);
    1612            0 :                                 ErrorsFound = true;
    1613              :                             }
    1614              :                         }
    1615              : 
    1616              :                     } else { // !tempZone.OpTempCntrlModeScheduled
    1617              : 
    1618              :                         // check validity of fixed radiative fraction
    1619            6 :                         if (Item == 1) {
    1620            2 :                             if (tempZone.FixedRadiativeFraction < 0.0) {
    1621            0 :                                 ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 0.0);
    1622            0 :                                 ErrorsFound = true;
    1623            2 :                             } else if (tempZone.FixedRadiativeFraction >= 0.9) {
    1624            0 :                                 ShowSevereBadMax(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::Ex, 0.9);
    1625            0 :                                 ErrorsFound = true;
    1626              :                             }
    1627              :                         }
    1628              :                     }
    1629              : 
    1630              :                     // added Jan, 2017 - Xuan Luo
    1631              :                     // read adaptive comfort model and calculate adaptive thermal comfort setpoint
    1632            6 :                     if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Constant || tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1633            6 :                         if (NumAlphas >= 4 && !s_ipsc->lAlphaFieldBlanks(4)) {
    1634              :                             int adaptiveComfortModelTypeIndex =
    1635            5 :                                 Util::FindItem(s_ipsc->cAlphaArgs(4), AdaptiveComfortModelTypes, AdaptiveComfortModelTypes.isize());
    1636            5 :                             if (!adaptiveComfortModelTypeIndex) {
    1637            0 :                                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    1638            0 :                                 ErrorsFound = true;
    1639            5 :                             } else if (adaptiveComfortModelTypeIndex != static_cast<int>(AdaptiveComfortModel::ADAP_NONE)) {
    1640            5 :                                 tempZone.AdaptiveComfortTempControl = true;
    1641            5 :                                 tempZone.AdaptiveComfortModelTypeIndex =
    1642            5 :                                     Util::FindItem(s_ipsc->cAlphaArgs(4), AdaptiveComfortModelTypes, AdaptiveComfortModelTypes.isize());
    1643            5 :                                 if (!s_ztpc->AdapComfortDailySetPointSchedule.initialized) {
    1644            1 :                                     Array1D<Real64> runningAverageASH(state.dataWeather->NumDaysInYear, 0.0);
    1645            1 :                                     Array1D<Real64> runningAverageCEN(state.dataWeather->NumDaysInYear, 0.0);
    1646            1 :                                     CalculateMonthlyRunningAverageDryBulb(state, runningAverageASH, runningAverageCEN);
    1647            1 :                                     CalculateAdaptiveComfortSetPointSchl(state, runningAverageASH, runningAverageCEN);
    1648            1 :                                 }
    1649              :                             }
    1650              :                         }
    1651              :                     }
    1652              : 
    1653              :                     // CurrentModuleObject='ZoneControl:Thermostat:OperativeTemperature'
    1654           12 :                     SetupOutputVariable(state,
    1655              :                                         "Zone Thermostat Operative Temperature",
    1656              :                                         Constant::Units::C,
    1657            6 :                                         state.dataHeatBal->ZnAirRpt(tempZone.ActualZoneNum).ThermOperativeTemp,
    1658              :                                         OutputProcessor::TimeStepType::Zone,
    1659              :                                         OutputProcessor::StoreType::Average,
    1660            6 :                                         Zone(tempZone.ActualZoneNum).Name);
    1661              :                 } // TStat Objects Loop
    1662              : 
    1663              :                 // It might be in the TempControlledZones
    1664            1 :             } else if ((found = Util::FindItem(s_ipsc->cAlphaArgs(1), state.dataZoneCtrls->TempControlledZone)) != 0) {
    1665              : 
    1666            1 :                 TempControlledZoneNum = found;
    1667            1 :                 auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1668            1 :                 tempZone.OpTempCtrl = static_cast<DataZoneControls::TempCtrl>(getEnumValue(DataZoneControls::tempCtrlNamesUC, s_ipsc->cAlphaArgs(2)));
    1669            1 :                 if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Invalid || tempZone.OpTempCtrl == DataZoneControls::TempCtrl::None) {
    1670            0 :                     ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1671            0 :                     ErrorsFound = true;
    1672              :                 }
    1673              : 
    1674            1 :                 tempZone.FixedRadiativeFraction = s_ipsc->rNumericArgs(1);
    1675              : 
    1676            1 :                 if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1677            0 :                     if (s_ipsc->lAlphaFieldBlanks(3)) {
    1678            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
    1679            0 :                         ErrorsFound = true;
    1680            0 :                     } else if ((tempZone.opTempRadiativeFractionSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
    1681            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    1682            0 :                         ErrorsFound = true;
    1683            0 :                     } else if (!tempZone.opTempRadiativeFractionSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::Ex, 0.9)) {
    1684            0 :                         Sched::ShowSevereBadMinMax(
    1685            0 :                             state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), Clusive::In, 0.0, Clusive::Ex, 0.9);
    1686            0 :                         ErrorsFound = true;
    1687              :                     }
    1688              : 
    1689              :                 } else { // !tempZone.OpTempCntrlModeScheduled
    1690              : 
    1691            1 :                     if (tempZone.FixedRadiativeFraction < 0.0) {
    1692            0 :                         ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 0.0);
    1693            0 :                         ErrorsFound = true;
    1694            1 :                     } else if (tempZone.FixedRadiativeFraction >= 0.9) {
    1695            0 :                         ShowSevereBadMax(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::Ex, 0.9);
    1696            0 :                         ErrorsFound = true;
    1697              :                     }
    1698              :                 }
    1699              : 
    1700              :                 // added Jan, 2017 - Xuan Luo
    1701              :                 // read adaptive comfort model and calculate adaptive thermal comfort setpoint
    1702            1 :                 if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Constant || tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1703            1 :                     if (NumAlphas >= 4 && !s_ipsc->lAlphaFieldBlanks(4)) {
    1704              :                         int adaptiveComfortModelTypeIndex =
    1705            0 :                             Util::FindItem(s_ipsc->cAlphaArgs(4), AdaptiveComfortModelTypes, AdaptiveComfortModelTypes.isize());
    1706            0 :                         if (!adaptiveComfortModelTypeIndex) {
    1707            0 :                             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    1708            0 :                             ErrorsFound = true;
    1709            0 :                         } else if (adaptiveComfortModelTypeIndex != static_cast<int>(AdaptiveComfortModel::ADAP_NONE)) {
    1710            0 :                             tempZone.AdaptiveComfortTempControl = true;
    1711            0 :                             tempZone.AdaptiveComfortModelTypeIndex = adaptiveComfortModelTypeIndex;
    1712            0 :                             if (!s_ztpc->AdapComfortDailySetPointSchedule.initialized) {
    1713            0 :                                 Array1D<Real64> runningAverageASH(state.dataWeather->NumDaysInYear, 0.0);
    1714            0 :                                 Array1D<Real64> runningAverageCEN(state.dataWeather->NumDaysInYear, 0.0);
    1715              :                                 // What does this accomplish?
    1716            0 :                                 CalculateMonthlyRunningAverageDryBulb(state, runningAverageASH, runningAverageCEN);
    1717            0 :                                 CalculateAdaptiveComfortSetPointSchl(state, runningAverageASH, runningAverageCEN);
    1718            0 :                             }
    1719              :                         }
    1720              :                     }
    1721              : 
    1722              :                     // CurrentModuleObject='ZoneControl:Thermostat:OperativeTemperature'
    1723            2 :                     SetupOutputVariable(state,
    1724              :                                         "Zone Thermostat Operative Temperature",
    1725              :                                         Constant::Units::C,
    1726            1 :                                         state.dataHeatBal->ZnAirRpt(tempZone.ActualZoneNum).ThermOperativeTemp,
    1727              :                                         OutputProcessor::TimeStepType::Zone,
    1728              :                                         OutputProcessor::StoreType::Average,
    1729            1 :                                         Zone(tempZone.ActualZoneNum).Name);
    1730              :                 }
    1731              :                 // throw error
    1732              :             } else {
    1733            0 :                 ShowSevereError(state,
    1734            0 :                                 format("{}={} invalid {} reference not found.",
    1735            0 :                                        s_ipsc->cCurrentModuleObject,
    1736            0 :                                        s_ipsc->cAlphaArgs(1),
    1737              :                                        cZControlTypes(static_cast<int>(ZoneControlTypes::TStat))));
    1738            0 :                 ErrorsFound = true;
    1739              :             }
    1740              :         } // loop over NumOpTempControlledZones
    1741              :     } // NumOpTempControlledZones > 0
    1742              : 
    1743              :     // Overcool dehumidificaton GetInput starts here
    1744          801 :     s_ipsc->cCurrentModuleObject = cZControlTypes((int)ZoneControlTypes::TandHStat);
    1745          801 :     state.dataZoneCtrls->NumTempAndHumidityControlledZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1746          801 :     if (state.dataZoneCtrls->NumTempAndHumidityControlledZones > 0) {
    1747            1 :         state.dataZoneCtrls->AnyZoneTempAndHumidityControl = true;
    1748              : 
    1749            2 :         for (int idx = 1; idx <= state.dataZoneCtrls->NumTempAndHumidityControlledZones; ++idx) {
    1750            2 :             s_ip->getObjectItem(state,
    1751            1 :                                 s_ipsc->cCurrentModuleObject,
    1752              :                                 idx,
    1753            1 :                                 s_ipsc->cAlphaArgs,
    1754              :                                 NumAlphas,
    1755            1 :                                 s_ipsc->rNumericArgs,
    1756              :                                 NumNums,
    1757              :                                 IOStat,
    1758            1 :                                 s_ipsc->lNumericFieldBlanks,
    1759            1 :                                 s_ipsc->lAlphaFieldBlanks,
    1760            1 :                                 s_ipsc->cAlphaFieldNames,
    1761            1 :                                 s_ipsc->cNumericFieldNames);
    1762              : 
    1763            1 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1764              : 
    1765              :             // find matching name of  ZONECONTROL:THERMOSTAT object
    1766            1 :             found = Util::FindItem(s_ipsc->cAlphaArgs(1), TStatObjects);
    1767            1 :             if (found == 0) {
    1768              :                 // It might be in the TempControlledZones
    1769            0 :                 found = Util::FindItem(s_ipsc->cAlphaArgs(1), state.dataZoneCtrls->TempControlledZone);
    1770            0 :                 if (found == 0) { // throw error
    1771            0 :                     ShowSevereError(state,
    1772            0 :                                     format("{}={} invalid {} reference not found.",
    1773            0 :                                            s_ipsc->cCurrentModuleObject,
    1774            0 :                                            s_ipsc->cAlphaArgs(1),
    1775              :                                            cZControlTypes(static_cast<int>(ZoneControlTypes::TStat))));
    1776            0 :                     ErrorsFound = true;
    1777              :                 } else {
    1778            0 :                     TempControlledZoneNum = found;
    1779            0 :                     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1780              : 
    1781            0 :                     if (s_ipsc->lAlphaFieldBlanks(2)) {
    1782            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1783            0 :                         ErrorsFound = true;
    1784            0 :                     } else if ((tempZone.dehumidifyingSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    1785            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1786            0 :                         ErrorsFound = true;
    1787              :                     }
    1788              : 
    1789            0 :                     tempZone.OvercoolCtrl =
    1790            0 :                         static_cast<DataZoneControls::TempCtrl>(getEnumValue(DataZoneControls::tempCtrlNamesUC, s_ipsc->cAlphaArgs(3)));
    1791            0 :                     if (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::Invalid) {
    1792            0 :                         ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    1793            0 :                         ErrorsFound = true;
    1794              :                     }
    1795              : 
    1796            0 :                     tempZone.ZoneOvercoolConstRange = s_ipsc->rNumericArgs(1);
    1797              : 
    1798            0 :                     if (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1799            0 :                         if (s_ipsc->lAlphaFieldBlanks(5)) {
    1800            0 :                             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
    1801            0 :                             ErrorsFound = true;
    1802            0 :                         } else if ((tempZone.zoneOvercoolRangeSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(5))) == nullptr) {
    1803            0 :                             ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5));
    1804            0 :                             ErrorsFound = true;
    1805            0 :                         } else if (!tempZone.zoneOvercoolRangeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 3.0)) {
    1806            0 :                             Sched::ShowSevereBadMinMax(
    1807            0 :                                 state, eoh, s_ipsc->cAlphaFieldNames(5), s_ipsc->cAlphaArgs(5), Clusive::In, 0.0, Clusive::In, 3.0);
    1808            0 :                             ErrorsFound = true;
    1809              :                         }
    1810              :                     } else { // !tempZone.OvercoolCntrlModeScheduled
    1811              : 
    1812            0 :                         if (tempZone.ZoneOvercoolConstRange < 0.0) {
    1813            0 :                             ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 0.0);
    1814            0 :                             ErrorsFound = true;
    1815            0 :                         } else if (tempZone.ZoneOvercoolConstRange > 3.0) {
    1816            0 :                             ShowSevereBadMax(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 3.0);
    1817            0 :                             ErrorsFound = true;
    1818              :                         }
    1819              : 
    1820              :                         // check Overcool Control Ratio limits
    1821            0 :                         tempZone.ZoneOvercoolControlRatio = s_ipsc->rNumericArgs(2);
    1822            0 :                         if (tempZone.ZoneOvercoolControlRatio < 0.0) {
    1823            0 :                             ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(2), s_ipsc->rNumericArgs(2), Clusive::In, 0.0);
    1824            0 :                             ErrorsFound = true;
    1825              :                         }
    1826              :                     }
    1827              :                 }
    1828              : 
    1829              :             } else {
    1830            2 :                 for (int Item = 1; Item <= TStatObjects(found).NumOfZones; ++Item) {
    1831            1 :                     TempControlledZoneNum = TStatObjects(found).TempControlledZoneStartPtr + Item - 1;
    1832              : 
    1833            1 :                     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1834              : 
    1835            1 :                     if (s_ipsc->lAlphaFieldBlanks(2)) {
    1836            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1837            0 :                         ErrorsFound = true;
    1838            1 :                     } else if ((tempZone.dehumidifyingSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(2))) == nullptr) {
    1839            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    1840            0 :                         ErrorsFound = true;
    1841              :                     }
    1842              : 
    1843              :                     // This (i.e., using two booleans to represent three options) is a bad idiom
    1844            1 :                     if (s_ipsc->cAlphaArgs(3) == "NONE") {
    1845            0 :                         tempZone.OvercoolCtrl = DataZoneControls::TempCtrl::None;
    1846            1 :                     } else if (s_ipsc->cAlphaArgs(3) != "OVERCOOL") {
    1847            0 :                         if (Item == 1) {
    1848            0 :                             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    1849            0 :                             ErrorsFound = true;
    1850              :                         }
    1851            1 :                     } else if ((tempZone.OvercoolCtrl = static_cast<DataZoneControls::TempCtrl>(
    1852            1 :                                     getEnumValue(DataZoneControls::tempCtrlNamesUC, s_ipsc->cAlphaArgs(4)))) == DataZoneControls::TempCtrl::Invalid) {
    1853            0 :                         if (Item == 1) {
    1854            0 :                             ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    1855            0 :                             ErrorsFound = true;
    1856              :                         }
    1857              :                     }
    1858              : 
    1859            1 :                     tempZone.ZoneOvercoolConstRange = s_ipsc->rNumericArgs(1);
    1860              : 
    1861            1 :                     if (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1862            0 :                         if (s_ipsc->lAlphaFieldBlanks(6)) {
    1863            0 :                             if (Item == 1) {
    1864            0 :                                 ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(6));
    1865            0 :                                 ErrorsFound = true;
    1866              :                             }
    1867            0 :                         } else if ((tempZone.zoneOvercoolRangeSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(6))) == nullptr) {
    1868            0 :                             if (Item == 1) {
    1869            0 :                                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
    1870            0 :                                 ErrorsFound = true;
    1871              :                             }
    1872            0 :                         } else if (!tempZone.zoneOvercoolRangeSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 3.0)) {
    1873            0 :                             if (Item == 1) {
    1874            0 :                                 Sched::ShowSevereBadMinMax(
    1875            0 :                                     state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6), Clusive::In, 0.0, Clusive::In, 3.0);
    1876            0 :                                 ErrorsFound = true;
    1877              :                             }
    1878              :                         }
    1879              :                     } else { // tempZone.OvercoolCntrlModeScheduled
    1880            1 :                         if (Item == 1) {
    1881            1 :                             if (tempZone.ZoneOvercoolConstRange < 0.0) {
    1882            0 :                                 ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 0.0);
    1883            0 :                                 ErrorsFound = true;
    1884            1 :                             } else if (tempZone.ZoneOvercoolConstRange > 3.0) {
    1885            0 :                                 ShowSevereBadMax(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 3.0);
    1886            0 :                                 ErrorsFound = true;
    1887              :                             }
    1888              :                         }
    1889              :                     }
    1890              : 
    1891            1 :                     tempZone.ZoneOvercoolControlRatio = s_ipsc->rNumericArgs(2);
    1892              :                     // check Overcool Control Ratio limits
    1893            1 :                     if (tempZone.ZoneOvercoolControlRatio < 0.0) {
    1894            0 :                         if (Item == 1) {
    1895            0 :                             ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(2), s_ipsc->rNumericArgs(2), Clusive::In, 0.0);
    1896            0 :                             ErrorsFound = true;
    1897              :                         }
    1898              :                     }
    1899              :                 } // TStat Objects Loop
    1900              :             } // found thermostat reference
    1901              :         } // loop over NumTempAndHumidityControlledZones
    1902              :     } // NumTempAndHumidityControlledZones > 0
    1903              : 
    1904              :     // Staged thermostat control inputs start
    1905          801 :     s_ipsc->cCurrentModuleObject = cZControlTypes((int)ZoneControlTypes::StagedDual);
    1906          801 :     NumStageControlledZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1907          801 :     if (NumStageControlledZones > 0) {
    1908            2 :         state.dataZoneCtrls->StagedTStatObjects.allocate(NumStageControlledZones);
    1909              :     }
    1910              : 
    1911              :     // Pre-scan for use of Zone lists in TStat statements (i.e. Global application of TStat)
    1912          801 :     s_ztpc->NumStageCtrZone = 0;
    1913          806 :     for (Item = 1; Item <= NumStageControlledZones; ++Item) {
    1914           10 :         s_ip->getObjectItem(state,
    1915            5 :                             s_ipsc->cCurrentModuleObject,
    1916              :                             Item,
    1917            5 :                             s_ipsc->cAlphaArgs,
    1918              :                             NumAlphas,
    1919            5 :                             s_ipsc->rNumericArgs,
    1920              :                             NumNums,
    1921              :                             IOStat,
    1922            5 :                             s_ipsc->lNumericFieldBlanks,
    1923            5 :                             s_ipsc->lAlphaFieldBlanks,
    1924            5 :                             s_ipsc->cAlphaFieldNames,
    1925            5 :                             s_ipsc->cNumericFieldNames);
    1926            5 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1927              : 
    1928            5 :         state.dataZoneCtrls->StagedTStatObjects(Item).Name = s_ipsc->cAlphaArgs(1);
    1929            5 :         Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
    1930            5 :         ZLItem = 0;
    1931            5 :         if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
    1932            1 :             ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
    1933              :         }
    1934            5 :         if (Item1 > 0) {
    1935            4 :             state.dataZoneCtrls->StagedTStatObjects(Item).StageControlledZoneStartPtr = s_ztpc->NumStageCtrZone + 1;
    1936            4 :             ++s_ztpc->NumStageCtrZone;
    1937            4 :             state.dataZoneCtrls->StagedTStatObjects(Item).NumOfZones = 1;
    1938            4 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive = false;
    1939            4 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr = Item1;
    1940            1 :         } else if (ZLItem > 0) {
    1941            1 :             state.dataZoneCtrls->StagedTStatObjects(Item).TempControlledZoneStartPtr = s_ztpc->NumStageCtrZone + 1;
    1942            1 :             s_ztpc->NumStageCtrZone += ZoneList(ZLItem).NumOfZones;
    1943            1 :             state.dataZoneCtrls->StagedTStatObjects(Item).NumOfZones = ZoneList(ZLItem).NumOfZones;
    1944            1 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive = true;
    1945            1 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr = ZLItem;
    1946              :         } else {
    1947            0 :             ShowSevereError(state,
    1948            0 :                             format("{}=\"{}\" invalid {}=\"{}\" not found.",
    1949            0 :                                    s_ipsc->cCurrentModuleObject,
    1950            0 :                                    s_ipsc->cAlphaArgs(1),
    1951            0 :                                    s_ipsc->cAlphaFieldNames(2),
    1952            0 :                                    s_ipsc->cAlphaArgs(2)));
    1953            0 :             ErrorsFound = true;
    1954              :         }
    1955              :     }
    1956              : 
    1957          801 :     if (ErrorsFound) {
    1958            0 :         ShowSevereError(state, format("GetStagedDualSetpoint: Errors with invalid names in {} objects.", s_ipsc->cCurrentModuleObject));
    1959            0 :         ShowContinueError(state, "...These will not be read in.  Other errors may occur.");
    1960            0 :         s_ztpc->NumStageCtrZone = 0;
    1961              :     }
    1962              : 
    1963          801 :     if (s_ztpc->NumStageCtrZone > 0) {
    1964            2 :         state.dataZoneCtrls->StageControlledZone.allocate(s_ztpc->NumStageCtrZone);
    1965            2 :         state.dataZoneCtrls->StageZoneLogic.dimension(NumOfZones, false);
    1966              : 
    1967            2 :         int StageControlledZoneNum = 0;
    1968            7 :         for (Item = 1; Item <= NumStageControlledZones; ++Item) {
    1969           10 :             s_ip->getObjectItem(state,
    1970            5 :                                 s_ipsc->cCurrentModuleObject,
    1971              :                                 Item,
    1972            5 :                                 s_ipsc->cAlphaArgs,
    1973              :                                 NumAlphas,
    1974            5 :                                 s_ipsc->rNumericArgs,
    1975              :                                 NumNums,
    1976              :                                 IOStat,
    1977            5 :                                 s_ipsc->lNumericFieldBlanks,
    1978            5 :                                 s_ipsc->lAlphaFieldBlanks,
    1979            5 :                                 s_ipsc->cAlphaFieldNames,
    1980            5 :                                 s_ipsc->cNumericFieldNames);
    1981              : 
    1982            5 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1983              : 
    1984           12 :             for (Item1 = 1; Item1 <= state.dataZoneCtrls->StagedTStatObjects(Item).NumOfZones; ++Item1) {
    1985            7 :                 ++StageControlledZoneNum;
    1986              : 
    1987            7 :                 auto &stageZone = state.dataZoneCtrls->StageControlledZone(StageControlledZoneNum);
    1988              : 
    1989            7 :                 if (state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive) {
    1990            3 :                     s_ipsc->cAlphaArgs(2) =
    1991            6 :                         state.dataHeatBal->Zone(ZoneList(state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name;
    1992              :                 }
    1993            7 :                 int ZoneAssigned = Util::FindItemInList(s_ipsc->cAlphaArgs(2),
    1994            7 :                                                         state.dataZoneCtrls->StageControlledZone,
    1995              :                                                         &DataZoneControls::ZoneStagedControls::ZoneName,
    1996              :                                                         StageControlledZoneNum - 1);
    1997            7 :                 if (ZoneAssigned == 0) {
    1998            7 :                     stageZone.ZoneName = s_ipsc->cAlphaArgs(2);
    1999            7 :                     if ((stageZone.ActualZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone)) == 0) {
    2000            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
    2001            0 :                         ErrorsFound = true;
    2002              :                     } else {
    2003              :                         //           Zone(stageControlledZone%ActualZoneNum)%StageControlledZoneIndex =
    2004              :                         //           StageControlledZoneNum
    2005              :                     }
    2006            7 :                     state.dataZoneCtrls->StageZoneLogic(stageZone.ActualZoneNum) = true;
    2007              :                 } else {
    2008            0 :                     stageZone.ZoneName = s_ipsc->cAlphaArgs(2); // for continuity
    2009            0 :                     ShowSevereError(state,
    2010            0 :                                     format("{}=\"{}\" invalid {}=\"{}\" zone previously assigned.",
    2011            0 :                                            s_ipsc->cCurrentModuleObject,
    2012            0 :                                            s_ipsc->cAlphaArgs(1),
    2013            0 :                                            s_ipsc->cAlphaFieldNames(2),
    2014            0 :                                            s_ipsc->cAlphaArgs(2)));
    2015            0 :                     ShowContinueError(
    2016              :                         state,
    2017            0 :                         format("...Zone was previously assigned to Setpt=\"{}\".", state.dataZoneCtrls->StageControlledZone(ZoneAssigned).Name));
    2018            0 :                     ErrorsFound = true;
    2019            0 :                     continue;
    2020              :                 }
    2021              : 
    2022            7 :                 if (!state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive) {
    2023            4 :                     stageZone.Name = s_ipsc->cAlphaArgs(1);
    2024              :                 } else {
    2025            9 :                     CheckCreatedZoneItemName(
    2026              :                         state,
    2027              :                         RoutineName,
    2028            3 :                         s_ipsc->cCurrentModuleObject,
    2029            3 :                         state.dataHeatBal->Zone(ZoneList(state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name,
    2030            3 :                         ZoneList(state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr).MaxZoneNameLength,
    2031            3 :                         state.dataZoneCtrls->StagedTStatObjects(Item).Name,
    2032            3 :                         state.dataZoneCtrls->StageControlledZone,
    2033              :                         StageControlledZoneNum - 1,
    2034            3 :                         stageZone.Name,
    2035              :                         errFlag);
    2036            3 :                     if (errFlag) {
    2037            0 :                         ErrorsFound = true;
    2038              :                     }
    2039              :                 }
    2040              : 
    2041            7 :                 stageZone.NumOfHeatStages = s_ipsc->rNumericArgs(1);
    2042            7 :                 if (s_ipsc->rNumericArgs(1) < 1 || s_ipsc->rNumericArgs(1) > 4) {
    2043            0 :                     ShowSevereBadMinMax(state, eoh, s_ipsc->cNumericFieldNames(1), s_ipsc->rNumericArgs(1), Clusive::In, 1, Clusive::In, 4);
    2044            0 :                     ErrorsFound = true;
    2045              :                 }
    2046              : 
    2047            7 :                 if (s_ipsc->lAlphaFieldBlanks(3)) {
    2048            0 :                     if (Item1 == 1) {
    2049            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
    2050            0 :                         ErrorsFound = true;
    2051              :                     }
    2052            7 :                 } else if ((stageZone.heatSetptBaseSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
    2053            0 :                     if (Item1 == 1) {
    2054            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
    2055            0 :                         ErrorsFound = true;
    2056              :                     }
    2057              :                 }
    2058              : 
    2059            7 :                 stageZone.HeatThroRange = s_ipsc->rNumericArgs(2);
    2060            7 :                 if (s_ipsc->rNumericArgs(1) < 0.0) {
    2061            0 :                     ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(2), s_ipsc->rNumericArgs(2), Clusive::In, 0.0);
    2062            0 :                     ErrorsFound = true;
    2063              :                 }
    2064              : 
    2065            7 :                 if (stageZone.NumOfHeatStages > 0) {
    2066            7 :                     stageZone.HeatTOffset.allocate(stageZone.NumOfHeatStages);
    2067           18 :                     for (i = 1; i <= stageZone.NumOfHeatStages; ++i) {
    2068           11 :                         stageZone.HeatTOffset(i) = s_ipsc->rNumericArgs(2 + i);
    2069           11 :                         if (s_ipsc->lNumericFieldBlanks(2 + i)) {
    2070            0 :                             ShowSevereEmptyField(state, eoh, s_ipsc->cNumericFieldNames(2 + i));
    2071            0 :                             ErrorsFound = true;
    2072           11 :                         } else if (s_ipsc->rNumericArgs(2 + i) > 0.0) {
    2073            0 :                             ShowSevereBadMax(state, eoh, s_ipsc->cNumericFieldNames(2 + i), s_ipsc->rNumericArgs(2 + i), Clusive::In, 0.0);
    2074            0 :                             ErrorsFound = true;
    2075              :                         }
    2076              : 
    2077           11 :                         if (i > 1) {
    2078            4 :                             if (s_ipsc->rNumericArgs(2 + i) >= s_ipsc->rNumericArgs(1 + i)) {
    2079            0 :                                 ShowSevereCustom(state,
    2080              :                                                  eoh,
    2081            0 :                                                  format("{} = {:.1R} must be less than than  {}={:.1R}",
    2082            0 :                                                         s_ipsc->cNumericFieldNames(2 + i),
    2083            0 :                                                         s_ipsc->rNumericArgs(2 + i),
    2084            0 :                                                         s_ipsc->cNumericFieldNames(1 + i),
    2085            0 :                                                         s_ipsc->rNumericArgs(1 + i)));
    2086            0 :                                 ErrorsFound = true;
    2087              :                             }
    2088              :                         }
    2089              :                     }
    2090              :                 }
    2091              : 
    2092            7 :                 stageZone.NumOfCoolStages = s_ipsc->rNumericArgs(7);
    2093            7 :                 if (s_ipsc->rNumericArgs(7) < 1 || s_ipsc->rNumericArgs(7) > 4) {
    2094            0 :                     ShowSevereBadMinMax(state, eoh, s_ipsc->cNumericFieldNames(7), s_ipsc->rNumericArgs(7), Clusive::In, 1, Clusive::In, 4);
    2095            0 :                     ErrorsFound = true;
    2096              :                 }
    2097              : 
    2098            7 :                 if (s_ipsc->lAlphaFieldBlanks(4)) {
    2099            0 :                     if (Item1 == 1) {
    2100            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(4));
    2101            0 :                         ErrorsFound = true;
    2102              :                     }
    2103            7 :                 } else if ((stageZone.coolSetptBaseSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(4))) == nullptr) {
    2104            0 :                     if (Item1 == 1) { // only show error on first of several if zone list
    2105            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    2106            0 :                         ErrorsFound = true;
    2107              :                     }
    2108              :                 }
    2109              : 
    2110            7 :                 stageZone.CoolThroRange = s_ipsc->rNumericArgs(8);
    2111            7 :                 if (s_ipsc->rNumericArgs(8) < 0.0) {
    2112            0 :                     ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(8), s_ipsc->rNumericArgs(8), Clusive::In, 0.0);
    2113            0 :                     ErrorsFound = true;
    2114              :                 }
    2115              : 
    2116            7 :                 if (stageZone.NumOfCoolStages > 0) {
    2117            7 :                     stageZone.CoolTOffset.allocate(stageZone.NumOfCoolStages);
    2118           26 :                     for (i = 1; i <= stageZone.NumOfCoolStages; ++i) {
    2119           19 :                         stageZone.CoolTOffset(i) = s_ipsc->rNumericArgs(8 + i);
    2120           19 :                         if (s_ipsc->lNumericFieldBlanks(8 + i)) {
    2121            0 :                             ShowSevereEmptyField(state, eoh, s_ipsc->cNumericFieldNames(8 + i));
    2122            0 :                             ErrorsFound = true;
    2123           19 :                         } else if (s_ipsc->rNumericArgs(8 + i) < 0.0) {
    2124            0 :                             ShowSevereBadMin(state, eoh, s_ipsc->cNumericFieldNames(8 + i), s_ipsc->rNumericArgs(8 + i), Clusive::In, 0.0);
    2125            0 :                             ErrorsFound = true;
    2126              :                         }
    2127              : 
    2128           19 :                         if (i > 1 && s_ipsc->rNumericArgs(8 + i) <= s_ipsc->rNumericArgs(7 + i)) {
    2129            0 :                             ShowSevereCustom(state,
    2130              :                                              eoh,
    2131            0 :                                              format("{} = {:.1R} must be greater than {} = {:.1R}",
    2132            0 :                                                     s_ipsc->cNumericFieldNames(8 + i),
    2133            0 :                                                     s_ipsc->rNumericArgs(8 + i),
    2134            0 :                                                     s_ipsc->cNumericFieldNames(7 + i),
    2135            0 :                                                     s_ipsc->rNumericArgs(7 + i)));
    2136            0 :                             ErrorsFound = true;
    2137              :                         }
    2138              :                     }
    2139              :                 }
    2140              :             }
    2141              :         } // loop over NumStageControlledZones
    2142              : 
    2143            2 :         if ((s_ip->getNumObjectsFound(state, "AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed") == 0) &&
    2144            1 :             (s_ip->getNumObjectsFound(state, "AirLoopHVAC:UnitarySystem") == 0) &&
    2145            3 :             (s_ip->getNumObjectsFound(state, "SetpointManager:SingleZone:OneStageCooling") == 0) &&
    2146            2 :             (s_ip->getNumObjectsFound(state, "SetpointManager:SingleZone:OneStageHeating") == 0)) {
    2147            0 :             ShowWarningError(state,
    2148            0 :                              format("{} is applicable to only selected HVAC objects which are missing from input.", s_ipsc->cCurrentModuleObject));
    2149            0 :             ShowContinueError(state, "Model should include one or more of the following objects:  ");
    2150            0 :             ShowContinueError(state, "AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed, AirLoopHVAC:UnitarySystem, ");
    2151            0 :             ShowContinueError(
    2152              :                 state, "SetpointManager:SingleZone:OneStageCooling, and/or SetpointManager:SingleZone:OneStageHeating. The simulation continues...");
    2153              :         }
    2154              :     } // NumStageControlledZones > 0
    2155              : 
    2156          801 :     if (ErrorsFound) {
    2157            0 :         ShowFatalError(state, "Errors getting Zone Control input data.  Preceding condition(s) cause termination.");
    2158              :     }
    2159          801 : }
    2160              : 
    2161            1 : void CalculateMonthlyRunningAverageDryBulb(EnergyPlusData &state, Array1D<Real64> &runningAverageASH, Array1D<Real64> &runningAverageCEN)
    2162              : {
    2163              :     // SUBROUTINE INFORMATION:
    2164              :     //       AUTHOR         Xuan Luo
    2165              :     //       DATE WRITTEN   January 2017
    2166              :     //       RE-ENGINEERED  na
    2167              : 
    2168              :     // PURPOSE OF THIS SUBROUTINE:
    2169              :     // This subroutine calculate the monthly running average dry bulb temperature;
    2170              : 
    2171              :     // Using/Aliasing
    2172              : 
    2173              :     using OutputReportTabular::GetColumnUsingTabs;
    2174              :     using OutputReportTabular::StrToReal;
    2175              : 
    2176              :     // SUBROUTINE PARAMETER DEFINITIONS:
    2177              : 
    2178              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2179              :     Real64 dryBulb;
    2180              :     Real64 avgDryBulb;
    2181              : 
    2182              :     std::string::size_type pos;
    2183              : 
    2184            1 :     Array1D<Real64> adaptiveTemp(state.dataWeather->NumDaysInYear, 0.0);
    2185            1 :     Array1D<Real64> dailyDryTemp(state.dataWeather->NumDaysInYear, 0.0);
    2186              : 
    2187            1 :     if (FileSystem::fileExists(state.files.inputWeatherFilePath.filePath)) {
    2188              :         // Read hourly dry bulb temperature first
    2189            2 :         auto epwFile = state.files.inputWeatherFilePath.open(state, "CalcThermalComfortAdaptive");
    2190           10 :         for (int i = 1; i <= 9; ++i) { // Headers
    2191            9 :             epwFile.readLine();
    2192              :         }
    2193          366 :         for (int i = 1; i <= state.dataWeather->NumDaysInYear; ++i) {
    2194          365 :             avgDryBulb = 0.0;
    2195         9125 :             for (int j = 1; j <= 24; ++j) {
    2196         8760 :                 std::string epwLine = epwFile.readLine().data;
    2197        61320 :                 for (int ind = 1; ind <= 6; ++ind) {
    2198        52560 :                     pos = index(epwLine, ',');
    2199        52560 :                     epwLine.erase(0, pos + 1);
    2200              :                 }
    2201         8760 :                 pos = index(epwLine, ',');
    2202         8760 :                 dryBulb = StrToReal(epwLine.substr(0, pos));
    2203         8760 :                 avgDryBulb += (dryBulb / 24.0);
    2204         8760 :             }
    2205          365 :             dailyDryTemp(i) = avgDryBulb;
    2206              :         }
    2207            1 :         epwFile.close();
    2208              : 
    2209              :         // Calculate monthly running average dry bulb temperature.
    2210            1 :         int dayOfYear = 0;
    2211          366 :         while (dayOfYear < state.dataWeather->NumDaysInYear) {
    2212          365 :             dayOfYear++;
    2213          365 :             int calcEndDay = dayOfYear - 1;
    2214          365 :             int calcStartDayASH = calcEndDay - 30;
    2215          365 :             int calcStartDayCEN = calcEndDay - 7;
    2216              : 
    2217          365 :             if (calcStartDayASH > 0) {
    2218        10688 :                 for (int i = calcStartDayASH; i <= calcStartDayASH + 30; i++) {
    2219        10354 :                     avgDryBulb = dailyDryTemp(i);
    2220        10354 :                     runningAverageASH(dayOfYear) = runningAverageASH(dayOfYear) + avgDryBulb;
    2221              :                 }
    2222          334 :                 runningAverageASH(dayOfYear) /= 30;
    2223              :             } else { // Do special things for wrapping the epw
    2224           31 :                 calcStartDayASH += state.dataWeather->NumDaysInYear;
    2225          496 :                 for (int i = 1; i <= calcEndDay; i++) {
    2226          465 :                     avgDryBulb = dailyDryTemp(i);
    2227          465 :                     runningAverageASH(dayOfYear) = runningAverageASH(dayOfYear) + avgDryBulb;
    2228              :                 }
    2229          496 :                 for (int i = calcStartDayASH; i < state.dataWeather->NumDaysInYear; i++) {
    2230          465 :                     avgDryBulb = dailyDryTemp(i);
    2231          465 :                     runningAverageASH(dayOfYear) = runningAverageASH(dayOfYear) + avgDryBulb;
    2232              :                 }
    2233           31 :                 runningAverageASH(dayOfYear) /= 30;
    2234              :             }
    2235              : 
    2236          365 :             if (calcStartDayCEN > 0) {
    2237         3213 :                 for (int i = calcStartDayCEN; i <= calcStartDayCEN + 7; i++) {
    2238         2856 :                     avgDryBulb = dailyDryTemp(i);
    2239         2856 :                     runningAverageCEN(dayOfYear) = runningAverageCEN(dayOfYear) + avgDryBulb;
    2240              :                 }
    2241          357 :                 runningAverageCEN(dayOfYear) /= 7;
    2242              :             } else { // Do special things for wrapping the epw
    2243            8 :                 calcStartDayCEN += state.dataWeather->NumDaysInYear;
    2244           36 :                 for (int i = 1; i <= calcEndDay; i++) {
    2245           28 :                     avgDryBulb = dailyDryTemp(i);
    2246           28 :                     runningAverageCEN(dayOfYear) = runningAverageCEN(dayOfYear) + avgDryBulb;
    2247              :                 }
    2248           36 :                 for (int i = calcStartDayCEN; i < state.dataWeather->NumDaysInYear; i++) {
    2249           28 :                     avgDryBulb = dailyDryTemp(i);
    2250           28 :                     runningAverageCEN(dayOfYear) = runningAverageCEN(dayOfYear) + avgDryBulb;
    2251              :                 }
    2252            8 :                 runningAverageCEN(dayOfYear) /= 7;
    2253              :             }
    2254              :         }
    2255            1 :     } else {
    2256            0 :         ShowFatalError(state,
    2257            0 :                        format("CalcThermalComfortAdaptive: Could not open file {} for input (read). (File does not exist)",
    2258            0 :                               state.files.inputWeatherFilePath.filePath));
    2259              :     }
    2260            1 : }
    2261              : 
    2262            1 : void CalculateAdaptiveComfortSetPointSchl(EnergyPlusData &state, Array1D<Real64> const &runningAverageASH, Array1D<Real64> const &runningAverageCEN)
    2263              : {
    2264              :     // SUBROUTINE INFORMATION:
    2265              :     //       AUTHOR         Xuan Luo
    2266              :     //       DATE WRITTEN   January 2017
    2267              :     //       RE-ENGINEERED  na
    2268              : 
    2269              :     // PURPOSE OF THIS SUBROUTINE:
    2270              :     // This subroutine calculates the zone operative temperature setpoint using adaptive comfort model.
    2271              : 
    2272              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2273            1 :     int constexpr summerDesignDayTypeIndex(9);
    2274            1 :     Real64 GrossApproxAvgDryBulbDesignDay(0.0);
    2275              : 
    2276            1 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    2277              : 
    2278            3 :     for (size_t i = 1; i <= state.dataWeather->DesDayInput.size(); i++) {
    2279              :         // Summer design day
    2280            2 :         if (state.dataWeather->DesDayInput(i).DayType == summerDesignDayTypeIndex) {
    2281            1 :             GrossApproxAvgDryBulbDesignDay = (state.dataWeather->DesDayInput(i).MaxDryBulb +
    2282            1 :                                               (state.dataWeather->DesDayInput(i).MaxDryBulb - state.dataWeather->DesDayInput(i).DailyDBRange)) /
    2283              :                                              2.0;
    2284            1 :             if (GrossApproxAvgDryBulbDesignDay > 10 && GrossApproxAvgDryBulbDesignDay < 33.5) {
    2285            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[0] = 0.31 * GrossApproxAvgDryBulbDesignDay + 17.8;
    2286            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[1] = 0.31 * GrossApproxAvgDryBulbDesignDay + 20.3;
    2287            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[2] = 0.31 * GrossApproxAvgDryBulbDesignDay + 21.3;
    2288              :             }
    2289            1 :             if (GrossApproxAvgDryBulbDesignDay > 10 && GrossApproxAvgDryBulbDesignDay < 30) {
    2290            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[3] = 0.33 * GrossApproxAvgDryBulbDesignDay + 18.8;
    2291            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[4] = 0.33 * GrossApproxAvgDryBulbDesignDay + 20.8;
    2292              :                 ; // What is this?
    2293            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[5] = 0.33 * GrossApproxAvgDryBulbDesignDay + 21.8;
    2294              :                 ;
    2295            1 :                 s_ztpc->AdapComfortSetPointSummerDesDay[6] = 0.33 * GrossApproxAvgDryBulbDesignDay + 22.8;
    2296              :                 ;
    2297              :             }
    2298              :         }
    2299              :     }
    2300              : 
    2301            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central.allocate(state.dataWeather->NumDaysInYear);
    2302            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_90.allocate(state.dataWeather->NumDaysInYear);
    2303            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_80.allocate(state.dataWeather->NumDaysInYear);
    2304            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central.allocate(state.dataWeather->NumDaysInYear);
    2305            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I.allocate(state.dataWeather->NumDaysInYear);
    2306            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II.allocate(state.dataWeather->NumDaysInYear);
    2307            1 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_III.allocate(state.dataWeather->NumDaysInYear);
    2308              : 
    2309              :     // Calculate the set points based on different models, set flag as -1 when running average temperature is not in the range.
    2310          366 :     for (int day = 1; day <= state.dataWeather->NumDaysInYear; day++) {
    2311          365 :         if (runningAverageASH(day) > 10 && runningAverageASH(day) < 33.5) {
    2312          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central(day) = 0.31 * runningAverageASH(day) + 17.8;
    2313          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_90(day) = 0.31 * runningAverageASH(day) + 20.3;
    2314          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_80(day) = 0.31 * runningAverageASH(day) + 21.3;
    2315              :         } else {
    2316            0 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central(day) = -1;
    2317            0 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_90(day) = -1;
    2318            0 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_80(day) = -1;
    2319              :         }
    2320          365 :         if (runningAverageCEN(day) > 10 && runningAverageCEN(day) < 30) {
    2321          216 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central(day) = 0.33 * runningAverageCEN(day) + 18.8;
    2322          216 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I(day) = 0.33 * runningAverageCEN(day) + 20.8;
    2323          216 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II(day) = 0.33 * runningAverageCEN(day) + 21.8;
    2324          216 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_III(day) = 0.33 * runningAverageCEN(day) + 22.8;
    2325              :         } else {
    2326          149 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central(day) = -1;
    2327          149 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I(day) = -1;
    2328          149 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II(day) = -1;
    2329          149 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_III(day) = -1;
    2330              :         }
    2331              :     }
    2332            1 :     s_ztpc->AdapComfortDailySetPointSchedule.initialized = true;
    2333            1 : }
    2334              : 
    2335     14518428 : void InitZoneAirSetPoints(EnergyPlusData &state)
    2336              : {
    2337              : 
    2338              :     // SUBROUTINE INFORMATION:
    2339              :     //       AUTHOR         Russell Taylor
    2340              :     //       DATE WRITTEN   September 1998
    2341              :     //       MODIFIED       November 2004, M. J. Witte additional report variables
    2342              :     //       MODIFIED       L.Gu, May 2006
    2343              :     //       RE-ENGINEERED  na
    2344              : 
    2345              :     // PURPOSE OF THIS SUBROUTINE:
    2346              :     // This subroutine initializes the data for the zone air setpoints.
    2347              : 
    2348              :     // METHODOLOGY EMPLOYED:
    2349              :     // Uses the status flags to trigger events.
    2350              : 
    2351              :     // SUBROUTINE PARAMETER DEFINITIONS:
    2352              :     static constexpr std::string_view RoutineName("InitZoneAirSetpoints: ");
    2353              : 
    2354              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2355              :     bool FirstSurfFlag;
    2356              :     int TRefFlag; // Flag for Reference Temperature process in Zones
    2357              : 
    2358     14518428 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    2359     14518428 :     auto &s_hbfs = state.dataHeatBalFanSys;
    2360              : 
    2361     14518428 :     auto &ZoneList = state.dataHeatBal->ZoneList;
    2362     14518428 :     auto &TempControlType = state.dataHeatBalFanSys->TempControlType;
    2363     14518428 :     auto &TempControlTypeRpt = state.dataHeatBalFanSys->TempControlTypeRpt;
    2364     14518428 :     int NumOfZones = state.dataGlobal->NumOfZones;
    2365              : 
    2366     14518428 :     if (s_ztpc->InitZoneAirSetPointsOneTimeFlag) {
    2367          801 :         s_hbfs->zoneTstatSetpts.allocate(NumOfZones);
    2368              : 
    2369          801 :         state.dataHeatBalFanSys->LoadCorrectionFactor.dimension(NumOfZones, 0.0);
    2370          801 :         TempControlType.dimension(NumOfZones, HVAC::SetptType::Uncontrolled);
    2371          801 :         TempControlTypeRpt.dimension(NumOfZones, 0);
    2372          801 :         if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
    2373            1 :             state.dataHeatBalFanSys->ComfortControlType.dimension(NumOfZones, HVAC::SetptType::Uncontrolled);
    2374            1 :             state.dataHeatBalFanSys->ComfortControlTypeRpt.dimension(NumOfZones, 0);
    2375            1 :             state.dataHeatBalFanSys->ZoneComfortControlsFanger.allocate(NumOfZones);
    2376              :         }
    2377          801 :         state.dataZoneEnergyDemand->Setback.dimension(NumOfZones, false);
    2378          801 :         state.dataZoneEnergyDemand->DeadBandOrSetback.dimension(NumOfZones, false);
    2379          801 :         state.dataZoneEnergyDemand->CurDeadBandOrSetback.dimension(NumOfZones, false);
    2380              : 
    2381          801 :         state.dataHeatBal->ZoneListSNLoadHeatEnergy.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2382          801 :         state.dataHeatBal->ZoneListSNLoadCoolEnergy.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2383          801 :         state.dataHeatBal->ZoneListSNLoadHeatRate.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2384          801 :         state.dataHeatBal->ZoneListSNLoadCoolRate.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2385              : 
    2386          801 :         state.dataHeatBal->ZoneGroupSNLoadHeatEnergy.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2387          801 :         state.dataHeatBal->ZoneGroupSNLoadCoolEnergy.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2388          801 :         state.dataHeatBal->ZoneGroupSNLoadHeatRate.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2389          801 :         state.dataHeatBal->ZoneGroupSNLoadCoolRate.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2390              : 
    2391              :         // Hybrid modeling
    2392          801 :         state.dataHeatBalFanSys->PreviousMeasuredZT1.dimension(NumOfZones, 0.0);
    2393          801 :         state.dataHeatBalFanSys->PreviousMeasuredZT2.dimension(NumOfZones, 0.0);
    2394          801 :         state.dataHeatBalFanSys->PreviousMeasuredZT3.dimension(NumOfZones, 0.0);
    2395          801 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat1.dimension(NumOfZones, 0.0);
    2396          801 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat2.dimension(NumOfZones, 0.0);
    2397          801 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat3.dimension(NumOfZones, 0.0);
    2398              : 
    2399              :         // Allocate Derived Types
    2400          801 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand.allocate(NumOfZones);
    2401          801 :         state.dataZoneEnergyDemand->ZoneSysMoistureDemand.allocate(NumOfZones);
    2402          801 :         if (state.dataHeatBal->doSpaceHeatBalanceSimulation || state.dataHeatBal->doSpaceHeatBalanceSizing) {
    2403            2 :             state.dataZoneEnergyDemand->spaceSysEnergyDemand.allocate(state.dataGlobal->numSpaces);
    2404            2 :             state.dataZoneEnergyDemand->spaceSysMoistureDemand.allocate(state.dataGlobal->numSpaces);
    2405              :         }
    2406              : 
    2407         6002 :         for (int zoneNum = 1; zoneNum <= NumOfZones; ++zoneNum) {
    2408         5201 :             bool FirstSurfFlag = true;
    2409        10414 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2410         5213 :                 auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    2411        50795 :                 for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
    2412        45582 :                     if (FirstSurfFlag) {
    2413         5201 :                         TRefFlag = state.dataSurface->SurfTAirRef(SurfNum);
    2414         5201 :                         FirstSurfFlag = false;
    2415              :                     }
    2416              :                     // for each particular zone, the reference air temperature(s) should be the same
    2417              :                     // (either mean air, bulk air, or supply air temp).
    2418        45582 :                     if (state.dataSurface->SurfTAirRef(SurfNum) != TRefFlag) {
    2419            0 :                         ShowWarningError(state,
    2420            0 :                                          format("Different reference air temperatures for difference surfaces encountered in zone {}",
    2421            0 :                                                 state.dataHeatBal->Zone(zoneNum).Name));
    2422              :                     }
    2423              :                 }
    2424         5201 :             }
    2425              :         }
    2426              : 
    2427              :         // CurrentModuleObject='Zone'
    2428         6002 :         for (int zoneNum = 1; zoneNum <= NumOfZones; ++zoneNum) {
    2429         5201 :             auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    2430         5201 :             s_ztpc->zoneHeatBalance(zoneNum).setUpOutputVars(state, DataStringGlobals::zonePrefix, thisZone.Name);
    2431         5201 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    2432           28 :                 for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2433           16 :                     s_ztpc->spaceHeatBalance(spaceNum).setUpOutputVars(
    2434           16 :                         state, DataStringGlobals::spacePrefix, state.dataHeatBal->space(spaceNum).Name);
    2435           12 :                 }
    2436              :             }
    2437         5201 :             bool staged = false;
    2438         5201 :             if (allocated(state.dataZoneCtrls->StageZoneLogic)) {
    2439            8 :                 staged = state.dataZoneCtrls->StageZoneLogic(zoneNum);
    2440              :             }
    2441              :             // If not doSpaceHeatBalanceSimulation then meter zones, not spaces
    2442         5201 :             bool attachMeters = !state.dataHeatBal->doSpaceHeatBalanceSimulation;
    2443         5201 :             state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).setUpOutputVars(
    2444         5201 :                 state, DataStringGlobals::zonePrefix, thisZone.Name, staged, attachMeters, thisZone.Multiplier, thisZone.ListMultiplier);
    2445         5201 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    2446              :                 // If doSpaceHeatBalanceSimulation then meter spaces, not zones
    2447           12 :                 attachMeters = state.dataHeatBal->doSpaceHeatBalanceSimulation;
    2448           28 :                 for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2449           16 :                     state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).setUpOutputVars(state,
    2450              :                                                                                                DataStringGlobals::spacePrefix,
    2451           16 :                                                                                                state.dataHeatBal->space(spaceNum).Name,
    2452              :                                                                                                staged,
    2453              :                                                                                                attachMeters,
    2454              :                                                                                                thisZone.Multiplier,
    2455              :                                                                                                thisZone.ListMultiplier);
    2456           12 :                 }
    2457              :             }
    2458         5201 :             state.dataZoneEnergyDemand->ZoneSysMoistureDemand(zoneNum).setUpOutputVars(state, DataStringGlobals::zonePrefix, thisZone.Name);
    2459         5201 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    2460           28 :                 for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2461           16 :                     state.dataZoneEnergyDemand->spaceSysMoistureDemand(spaceNum).setUpOutputVars(
    2462           16 :                         state, DataStringGlobals::spacePrefix, state.dataHeatBal->space(spaceNum).Name);
    2463           12 :                 }
    2464              :             }
    2465        10402 :             SetupOutputVariable(state,
    2466              :                                 "Zone Thermostat Air Temperature",
    2467              :                                 Constant::Units::C,
    2468         5201 :                                 state.dataHeatBalFanSys->TempTstatAir(zoneNum),
    2469              :                                 OutputProcessor::TimeStepType::System,
    2470              :                                 OutputProcessor::StoreType::Average,
    2471         5201 :                                 thisZone.Name);
    2472         5201 :             SetupOutputVariable(state,
    2473              :                                 "Zone Thermostat Control Type",
    2474              :                                 Constant::Units::None,
    2475         5201 :                                 state.dataHeatBalFanSys->TempControlTypeRpt(zoneNum),
    2476              :                                 OutputProcessor::TimeStepType::Zone,
    2477              :                                 OutputProcessor::StoreType::Average,
    2478         5201 :                                 thisZone.Name);
    2479        10402 :             SetupOutputVariable(state,
    2480              :                                 "Zone Thermostat Heating Setpoint Temperature",
    2481              :                                 Constant::Units::C,
    2482         5201 :                                 s_hbfs->zoneTstatSetpts(zoneNum).setptLo,
    2483              :                                 OutputProcessor::TimeStepType::System,
    2484              :                                 OutputProcessor::StoreType::Average,
    2485         5201 :                                 thisZone.Name);
    2486        10402 :             SetupOutputVariable(state,
    2487              :                                 "Zone Thermostat Cooling Setpoint Temperature",
    2488              :                                 Constant::Units::C,
    2489         5201 :                                 s_hbfs->zoneTstatSetpts(zoneNum).setptHi,
    2490              :                                 OutputProcessor::TimeStepType::System,
    2491              :                                 OutputProcessor::StoreType::Average,
    2492         5201 :                                 thisZone.Name);
    2493        10402 :             SetupOutputVariable(state,
    2494              :                                 "Zone Adaptive Comfort Operative Temperature Set Point",
    2495              :                                 Constant::Units::C,
    2496         5201 :                                 s_hbfs->zoneTstatSetpts(zoneNum).setptAdapComfortCool,
    2497              :                                 OutputProcessor::TimeStepType::Zone,
    2498              :                                 OutputProcessor::StoreType::Average,
    2499         5201 :                                 thisZone.Name);
    2500        10402 :             SetupOutputVariable(state,
    2501              :                                 "Zone Predicted Sensible Load Room Air Correction Factor",
    2502              :                                 Constant::Units::None,
    2503         5201 :                                 state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum),
    2504              :                                 OutputProcessor::TimeStepType::System,
    2505              :                                 OutputProcessor::StoreType::Average,
    2506         5201 :                                 thisZone.Name);
    2507              :         } // zoneNum
    2508              : 
    2509              :         // Thermal comfort control output
    2510          801 :         if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
    2511              :             // CurrentModuleObject='ZoneControl:Thermostat:ThermalComfort'
    2512            2 :             for (int Loop = 1; Loop <= state.dataZoneCtrls->NumComfortControlledZones; ++Loop) {
    2513            1 :                 int zoneNum = state.dataZoneCtrls->ComfortControlledZone(Loop).ActualZoneNum;
    2514            1 :                 auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    2515            1 :                 SetupOutputVariable(state,
    2516              :                                     "Zone Thermal Comfort Control Type",
    2517              :                                     Constant::Units::None,
    2518            1 :                                     state.dataHeatBalFanSys->ComfortControlTypeRpt(zoneNum),
    2519              :                                     OutputProcessor::TimeStepType::Zone,
    2520              :                                     OutputProcessor::StoreType::Average,
    2521            1 :                                     thisZone.Name);
    2522            2 :                 SetupOutputVariable(state,
    2523              :                                     "Zone Thermal Comfort Control Fanger Low Setpoint PMV",
    2524              :                                     Constant::Units::None,
    2525            1 :                                     state.dataHeatBalFanSys->ZoneComfortControlsFanger(zoneNum).LowPMV,
    2526              :                                     OutputProcessor::TimeStepType::Zone,
    2527              :                                     OutputProcessor::StoreType::Average,
    2528            1 :                                     thisZone.Name);
    2529            2 :                 SetupOutputVariable(state,
    2530              :                                     "Zone Thermal Comfort Control Fanger High Setpoint PMV",
    2531              :                                     Constant::Units::None,
    2532            1 :                                     state.dataHeatBalFanSys->ZoneComfortControlsFanger(zoneNum).HighPMV,
    2533              :                                     OutputProcessor::TimeStepType::Zone,
    2534              :                                     OutputProcessor::StoreType::Average,
    2535            1 :                                     thisZone.Name);
    2536              :             }
    2537              :         }
    2538              : 
    2539              :         // CurrentModuleObject='ZoneList'
    2540          849 :         for (int Loop = 1; Loop <= state.dataHeatBal->NumOfZoneLists; ++Loop) {
    2541           48 :             auto &zoneList = state.dataHeatBal->ZoneList(Loop);
    2542           96 :             SetupOutputVariable(state,
    2543              :                                 "Zone List Sensible Heating Energy",
    2544              :                                 Constant::Units::J,
    2545           48 :                                 state.dataHeatBal->ZoneListSNLoadHeatEnergy(Loop),
    2546              :                                 OutputProcessor::TimeStepType::System,
    2547              :                                 OutputProcessor::StoreType::Sum,
    2548           48 :                                 zoneList.Name);
    2549           96 :             SetupOutputVariable(state,
    2550              :                                 "Zone List Sensible Cooling Energy",
    2551              :                                 Constant::Units::J,
    2552           48 :                                 state.dataHeatBal->ZoneListSNLoadCoolEnergy(Loop),
    2553              :                                 OutputProcessor::TimeStepType::System,
    2554              :                                 OutputProcessor::StoreType::Sum,
    2555           48 :                                 zoneList.Name);
    2556           96 :             SetupOutputVariable(state,
    2557              :                                 "Zone List Sensible Heating Rate",
    2558              :                                 Constant::Units::W,
    2559           48 :                                 state.dataHeatBal->ZoneListSNLoadHeatRate(Loop),
    2560              :                                 OutputProcessor::TimeStepType::System,
    2561              :                                 OutputProcessor::StoreType::Average,
    2562           48 :                                 zoneList.Name);
    2563           96 :             SetupOutputVariable(state,
    2564              :                                 "Zone List Sensible Cooling Rate",
    2565              :                                 Constant::Units::W,
    2566           48 :                                 state.dataHeatBal->ZoneListSNLoadCoolRate(Loop),
    2567              :                                 OutputProcessor::TimeStepType::System,
    2568              :                                 OutputProcessor::StoreType::Average,
    2569           48 :                                 zoneList.Name);
    2570              :         } // Loop
    2571              : 
    2572              :         // CurrentModuleObject='ZoneGroup'
    2573          805 :         for (int Loop = 1; Loop <= state.dataHeatBal->NumOfZoneGroups; ++Loop) {
    2574            4 :             auto &zoneGroup = state.dataHeatBal->ZoneGroup(Loop);
    2575            8 :             SetupOutputVariable(state,
    2576              :                                 "Zone Group Sensible Heating Energy",
    2577              :                                 Constant::Units::J,
    2578            4 :                                 state.dataHeatBal->ZoneGroupSNLoadHeatEnergy(Loop),
    2579              :                                 OutputProcessor::TimeStepType::System,
    2580              :                                 OutputProcessor::StoreType::Sum,
    2581            4 :                                 zoneGroup.Name);
    2582            8 :             SetupOutputVariable(state,
    2583              :                                 "Zone Group Sensible Cooling Energy",
    2584              :                                 Constant::Units::J,
    2585            4 :                                 state.dataHeatBal->ZoneGroupSNLoadCoolEnergy(Loop),
    2586              :                                 OutputProcessor::TimeStepType::System,
    2587              :                                 OutputProcessor::StoreType::Sum,
    2588            4 :                                 zoneGroup.Name);
    2589            8 :             SetupOutputVariable(state,
    2590              :                                 "Zone Group Sensible Heating Rate",
    2591              :                                 Constant::Units::W,
    2592            4 :                                 state.dataHeatBal->ZoneGroupSNLoadHeatRate(Loop),
    2593              :                                 OutputProcessor::TimeStepType::System,
    2594              :                                 OutputProcessor::StoreType::Average,
    2595            4 :                                 zoneGroup.Name);
    2596            8 :             SetupOutputVariable(state,
    2597              :                                 "Zone Group Sensible Cooling Rate",
    2598              :                                 Constant::Units::W,
    2599            4 :                                 state.dataHeatBal->ZoneGroupSNLoadCoolRate(Loop),
    2600              :                                 OutputProcessor::TimeStepType::System,
    2601              :                                 OutputProcessor::StoreType::Average,
    2602            4 :                                 zoneGroup.Name);
    2603              :         } // Loop
    2604              : 
    2605          801 :         s_ztpc->InitZoneAirSetPointsOneTimeFlag = false;
    2606              :     }
    2607              : 
    2608              :     // Do the Begin Environment initializations
    2609     14518428 :     if (s_ztpc->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
    2610        57005 :         for (auto &thisZoneHB : s_ztpc->zoneHeatBalance) {
    2611        50509 :             thisZoneHB.beginEnvironmentInit(state);
    2612         6496 :         }
    2613         6496 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    2614          209 :             for (auto &thisSpaceHB : s_ztpc->spaceHeatBalance) {
    2615          186 :                 thisSpaceHB.beginEnvironmentInit(state);
    2616           23 :             }
    2617              :         }
    2618              : 
    2619        57005 :         for (auto &zoneTstatSetpt : s_hbfs->zoneTstatSetpts) {
    2620        50509 :             zoneTstatSetpt.setpt = zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi = 0.0;
    2621              :         }
    2622              : 
    2623         6496 :         state.dataHeatBalFanSys->LoadCorrectionFactor = 1.0;
    2624         6496 :         TempControlType = HVAC::SetptType::Uncontrolled;
    2625        57005 :         for (auto &e : state.dataZoneEnergyDemand->ZoneSysEnergyDemand) {
    2626        50509 :             e.beginEnvironmentInit();
    2627         6496 :         }
    2628        57005 :         for (auto &e : state.dataZoneEnergyDemand->ZoneSysMoistureDemand) {
    2629        50509 :             e.beginEnvironmentInit();
    2630         6496 :         }
    2631         6496 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    2632          209 :             for (auto &e : state.dataZoneEnergyDemand->spaceSysEnergyDemand) {
    2633          186 :                 e.beginEnvironmentInit();
    2634           23 :             }
    2635          209 :             for (auto &e : state.dataZoneEnergyDemand->spaceSysMoistureDemand) {
    2636          186 :                 e.beginEnvironmentInit();
    2637           23 :             }
    2638              :         }
    2639              : 
    2640         6496 :         state.dataZoneEnergyDemand->DeadBandOrSetback = false;
    2641              : 
    2642        57005 :         for (auto &e : state.dataHeatBal->Zone) {
    2643        50509 :             e.NoHeatToReturnAir = false;
    2644         6496 :         }
    2645         6496 :         state.dataHeatBalFanSys->PreviousMeasuredZT1 = 0.0;     // Hybrid modeling
    2646         6496 :         state.dataHeatBalFanSys->PreviousMeasuredZT2 = 0.0;     // Hybrid modeling
    2647         6496 :         state.dataHeatBalFanSys->PreviousMeasuredZT3 = 0.0;     // Hybrid modeling
    2648         6496 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat1 = 0.0; // Hybrid modeling
    2649         6496 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat2 = 0.0; // Hybrid modeling
    2650         6496 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat3 = 0.0; // Hybrid modeling
    2651              : 
    2652         6496 :         s_ztpc->MyEnvrnFlag = false;
    2653              :     }
    2654              : 
    2655     14518428 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    2656     14460215 :         s_ztpc->MyEnvrnFlag = true;
    2657              :     }
    2658              : 
    2659              :     // Do the Begin Day initializations
    2660     14518428 :     if (s_ztpc->MyDayFlag && state.dataGlobal->BeginDayFlag) {
    2661        25938 :         s_ztpc->MyDayFlag = false;
    2662              :     }
    2663              : 
    2664     14518428 :     if (!state.dataGlobal->BeginDayFlag) {
    2665     14361828 :         s_ztpc->MyDayFlag = true;
    2666              :     }
    2667              : 
    2668    111976506 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumTempControlledZones; ++Loop) {
    2669     97458078 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(Loop);
    2670     97458078 :         if (state.dataZoneEquip->ZoneEquipInputsFilled && !s_ztpc->ControlledZonesChecked) {
    2671         4333 :             if (!VerifyControlledZoneForThermostat(state, tempZone.ZoneName)) {
    2672            0 :                 ShowSevereError(
    2673              :                     state,
    2674            0 :                     format("{}Zone=\"{}\" has specified a Thermostatic control but is not a controlled zone.", RoutineName, tempZone.ZoneName));
    2675            0 :                 ShowContinueError(state, "...must have a ZoneHVAC:EquipmentConnections specification for this zone.");
    2676            0 :                 s_ztpc->ErrorsFound = true;
    2677              :             }
    2678              :         }
    2679              : 
    2680     97458078 :         if (tempZone.ManageDemand) {
    2681            0 :             int ZoneNum = tempZone.ActualZoneNum;
    2682              : 
    2683            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    2684              : 
    2685            0 :             switch (TempControlType(ZoneNum)) {
    2686            0 :             case HVAC::SetptType::SingleHeat: {
    2687            0 :                 if (zoneTstatSetpt.setpt > tempZone.HeatingResetLimit) {
    2688            0 :                     zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt = tempZone.HeatingResetLimit;
    2689              :                 }
    2690            0 :             } break;
    2691              : 
    2692            0 :             case HVAC::SetptType::SingleCool: {
    2693            0 :                 if (zoneTstatSetpt.setpt < tempZone.CoolingResetLimit) {
    2694            0 :                     zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt = tempZone.CoolingResetLimit;
    2695              :                 }
    2696            0 :             } break;
    2697              : 
    2698            0 :             case HVAC::SetptType::SingleHeatCool: {
    2699            0 :                 if ((zoneTstatSetpt.setpt > tempZone.HeatingResetLimit) || (zoneTstatSetpt.setpt < tempZone.CoolingResetLimit)) {
    2700              : 
    2701            0 :                     TempControlType(ZoneNum) = HVAC::SetptType::DualHeatCool;
    2702            0 :                     TempControlTypeRpt(ZoneNum) = static_cast<int>(TempControlType(ZoneNum));
    2703            0 :                     zoneTstatSetpt.setptHi = zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    2704              : 
    2705            0 :                     if (zoneTstatSetpt.setptLo > tempZone.HeatingResetLimit) {
    2706            0 :                         zoneTstatSetpt.setptLo = tempZone.HeatingResetLimit;
    2707              :                     }
    2708            0 :                     if (zoneTstatSetpt.setptHi < tempZone.CoolingResetLimit) {
    2709            0 :                         zoneTstatSetpt.setptHi = tempZone.CoolingResetLimit;
    2710              :                     }
    2711              :                 }
    2712            0 :             } break;
    2713              : 
    2714            0 :             case HVAC::SetptType::DualHeatCool: {
    2715            0 :                 if (zoneTstatSetpt.setptLo > tempZone.HeatingResetLimit) {
    2716            0 :                     zoneTstatSetpt.setptLo = tempZone.HeatingResetLimit;
    2717              :                 }
    2718            0 :                 if (zoneTstatSetpt.setptHi < tempZone.CoolingResetLimit) {
    2719            0 :                     zoneTstatSetpt.setptHi = tempZone.CoolingResetLimit;
    2720              :                 }
    2721            0 :             } break;
    2722              : 
    2723            0 :             default: {
    2724            0 :             } break;
    2725              :             } // switch (setptType)
    2726              :         }
    2727              :     }
    2728              : 
    2729     14528700 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumComfortControlledZones; ++Loop) {
    2730        10272 :         auto const &comfortZone = state.dataZoneCtrls->ComfortControlledZone(Loop);
    2731        10272 :         if (state.dataZoneEquip->ZoneEquipInputsFilled && !s_ztpc->ControlledZonesChecked) {
    2732            1 :             if (!VerifyControlledZoneForThermostat(state, comfortZone.ZoneName)) {
    2733            0 :                 ShowSevereError(
    2734            0 :                     state, format("{}Zone=\"{}\" has specified a Comfort control but is not a controlled zone.", RoutineName, comfortZone.ZoneName));
    2735            0 :                 ShowContinueError(state, "...must have a ZoneHVAC:EquipmentConnections specification for this zone.");
    2736            0 :                 s_ztpc->ErrorsFound = true;
    2737              :             }
    2738              :         }
    2739              : 
    2740        10272 :         if (comfortZone.ManageDemand) {
    2741            0 :             int ZoneNum = comfortZone.ActualZoneNum;
    2742            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    2743            0 :             switch (s_hbfs->ComfortControlType(ZoneNum)) {
    2744            0 :             case HVAC::SetptType::SingleHeat: {
    2745            0 :                 if (zoneTstatSetpt.setpt >= comfortZone.HeatingResetLimit) {
    2746            0 :                     zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt = comfortZone.HeatingResetLimit;
    2747            0 :                     TempControlType(ZoneNum) = HVAC::SetptType::SingleHeat;
    2748            0 :                     TempControlTypeRpt(ZoneNum) = static_cast<int>(TempControlType(ZoneNum));
    2749              :                 }
    2750            0 :             } break;
    2751              : 
    2752            0 :             case HVAC::SetptType::SingleCool: {
    2753            0 :                 if (zoneTstatSetpt.setpt <= comfortZone.CoolingResetLimit) {
    2754            0 :                     zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt = comfortZone.CoolingResetLimit;
    2755            0 :                     TempControlType(ZoneNum) = HVAC::SetptType::SingleCool;
    2756            0 :                     TempControlTypeRpt(ZoneNum) = static_cast<int>(TempControlType(ZoneNum));
    2757              :                 }
    2758            0 :             } break;
    2759              : 
    2760            0 :             case HVAC::SetptType::SingleHeatCool: {
    2761            0 :                 if ((zoneTstatSetpt.setpt >= comfortZone.HeatingResetLimit) || (zoneTstatSetpt.setpt <= comfortZone.CoolingResetLimit)) {
    2762              : 
    2763            0 :                     TempControlType(ZoneNum) = HVAC::SetptType::DualHeatCool;
    2764            0 :                     TempControlTypeRpt(ZoneNum) = static_cast<int>(TempControlType(ZoneNum));
    2765            0 :                     zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    2766              : 
    2767            0 :                     if (zoneTstatSetpt.setptLo >= comfortZone.HeatingResetLimit) {
    2768            0 :                         zoneTstatSetpt.setptLo = comfortZone.HeatingResetLimit;
    2769              :                     }
    2770            0 :                     if (zoneTstatSetpt.setptHi <= comfortZone.CoolingResetLimit) {
    2771            0 :                         zoneTstatSetpt.setptHi = comfortZone.CoolingResetLimit;
    2772              :                     }
    2773              :                 }
    2774            0 :             } break;
    2775              : 
    2776            0 :             case HVAC::SetptType::DualHeatCool: {
    2777            0 :                 TempControlType(ZoneNum) = HVAC::SetptType::DualHeatCool;
    2778            0 :                 TempControlTypeRpt(ZoneNum) = static_cast<int>(TempControlType(ZoneNum));
    2779            0 :                 if (zoneTstatSetpt.setptLo >= comfortZone.HeatingResetLimit) {
    2780            0 :                     zoneTstatSetpt.setptLo = comfortZone.HeatingResetLimit;
    2781              :                 }
    2782            0 :                 if (zoneTstatSetpt.setptHi <= comfortZone.CoolingResetLimit) {
    2783            0 :                     zoneTstatSetpt.setptHi = comfortZone.CoolingResetLimit;
    2784              :                 }
    2785            0 :             } break;
    2786              : 
    2787            0 :             default: {
    2788            0 :             } break;
    2789              :             } // switch
    2790              :         } // Demand manager
    2791              :     }
    2792              : 
    2793     14518428 :     if (s_ztpc->ErrorsFound) {
    2794            0 :         ShowFatalError(state, "InitZoneAirSetpoints - program terminates due to previous condition.");
    2795              :     }
    2796              : 
    2797     14518428 :     if (state.dataZoneEquip->ZoneEquipInputsFilled) {
    2798     14518428 :         s_ztpc->ControlledZonesChecked = true;
    2799              :     }
    2800     14518428 : }
    2801              : 
    2802        50695 : void ZoneSpaceHeatBalanceData::beginEnvironmentInit(EnergyPlusData &state)
    2803              : {
    2804       253475 :     for (int i = 0; i <= 3; ++i) {
    2805       202780 :         this->ZTM[i] = 0.0;
    2806       202780 :         this->WPrevZoneTS[i] = state.dataEnvrn->OutHumRat;
    2807       202780 :         this->DSWPrevZoneTS[i] = state.dataEnvrn->OutHumRat;
    2808       202780 :         this->WPrevZoneTSTemp[i] = 0.0;
    2809              :     }
    2810        50695 :     this->WTimeMinusP = state.dataEnvrn->OutHumRat;
    2811        50695 :     this->W1 = state.dataEnvrn->OutHumRat;
    2812        50695 :     this->WMX = state.dataEnvrn->OutHumRat;
    2813        50695 :     this->WM2 = state.dataEnvrn->OutHumRat;
    2814        50695 :     this->airHumRatTemp = 0.0;
    2815        50695 :     this->tempIndLoad = 0.0;
    2816        50695 :     this->tempDepLoad = 0.0;
    2817        50695 :     this->airRelHum = 0.0;
    2818        50695 :     this->AirPowerCap = 0.0;
    2819        50695 :     this->T1 = 0.0;
    2820        50695 : }
    2821              : 
    2822         5217 : void ZoneSpaceHeatBalanceData::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string const &name)
    2823              : {
    2824        15651 :     SetupOutputVariable(state,
    2825        10434 :                         format("{} Air Temperature", prefix),
    2826              :                         Constant::Units::C,
    2827         5217 :                         this->ZT,
    2828              :                         OutputProcessor::TimeStepType::System,
    2829              :                         OutputProcessor::StoreType::Average,
    2830              :                         name);
    2831        15651 :     SetupOutputVariable(state,
    2832        10434 :                         format("{} Air Humidity Ratio", prefix),
    2833              :                         Constant::Units::None,
    2834         5217 :                         this->airHumRat,
    2835              :                         OutputProcessor::TimeStepType::System,
    2836              :                         OutputProcessor::StoreType::Average,
    2837              :                         name);
    2838        15651 :     SetupOutputVariable(state,
    2839        10434 :                         format("{} Air Relative Humidity", prefix),
    2840              :                         Constant::Units::Perc,
    2841         5217 :                         this->airRelHum,
    2842              :                         OutputProcessor::TimeStepType::System,
    2843              :                         OutputProcessor::StoreType::Average,
    2844              :                         name);
    2845        15651 :     SetupOutputVariable(state,
    2846        10434 :                         format("{} Mean Radiant Temperature", prefix),
    2847              :                         Constant::Units::C,
    2848         5217 :                         this->MRT,
    2849              :                         OutputProcessor::TimeStepType::Zone,
    2850              :                         OutputProcessor::StoreType::Average,
    2851              :                         name);
    2852         5217 : }
    2853              : 
    2854      3896818 : void PredictSystemLoads(EnergyPlusData &state,
    2855              :                         bool const ShortenTimeStepSys,
    2856              :                         bool const UseZoneTimeStepHistory, // if true then use zone timestep history, if false use system time step
    2857              :                         Real64 const PriorTimeStep         // the old value for timestep length is passed for possible use in interpolating
    2858              : )
    2859              : {
    2860              : 
    2861              :     // SUBROUTINE INFORMATION:
    2862              :     //       AUTHOR         Russ Taylor
    2863              :     //       DATE WRITTEN   May 1997
    2864              :     //       MODIFIED       na
    2865              :     //       RE-ENGINEERED  July 2003 (Peter Graham Ellis)
    2866              : 
    2867              :     // PURPOSE OF THIS SUBROUTINE:
    2868              :     // This subroutine is responsible for determining
    2869              :     // how much of each type of energy every zone requires.
    2870              :     // In effect, this subroutine defines and simulates all
    2871              :     // the system types and in the case of hybrid systems
    2872              :     // which use more than one type of energy must determine
    2873              :     // how to apportion the load. An example of a hybrid system
    2874              :     // is a water loop heat pump with supplemental air.  In
    2875              :     // this case, a zone will require water from the loop and
    2876              :     // cooled or heated air from the air system. A simpler
    2877              :     // example would be a VAV system with baseboard heaters.
    2878              : 
    2879              :     //  Basic Air System Types
    2880              :     //  1) Constant Volume Single Duct
    2881              :     //  2) Variable Volume Single Duct
    2882              :     //  3) Constant Volume Dual Duct
    2883              :     //  4) Variable Volume Dual Duct
    2884              : 
    2885              :     // METHODOLOGY EMPLOYED:
    2886              :     // 0.  Determine if simulation has downstepped and readjust history and revert node results
    2887              :     // 1.  Determine zone load - this is zone temperature dependent
    2888              :     // 2.  Determine balance point - the temperature at which the
    2889              :     //     zone load is balanced by the system output. The way the
    2890              :     //     balance point is determined will be different depending on
    2891              :     //     the type of system being simulated.
    2892              :     // 3.  Calculate zone energy requirements
    2893              : 
    2894      3896818 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    2895      3896818 :     auto const &s_hbfs = state.dataHeatBalFanSys;
    2896              : 
    2897              :     // Staged thermostat setpoint
    2898      3896818 :     if (s_ztpc->NumStageCtrZone > 0) {
    2899       127321 :         for (int RelativeZoneNum = 1; RelativeZoneNum <= s_ztpc->NumStageCtrZone; ++RelativeZoneNum) {
    2900       100893 :             auto &thisStageControlZone = state.dataZoneCtrls->StageControlledZone(RelativeZoneNum);
    2901       100893 :             int ActualZoneNum = thisStageControlZone.ActualZoneNum;
    2902              : 
    2903       100893 :             auto &thisZoneHB = s_ztpc->zoneHeatBalance(ActualZoneNum);
    2904       100893 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ActualZoneNum);
    2905       100893 :             Real64 ZoneT = thisZoneHB.MAT; // Zone temperature at previous time step
    2906       100893 :             if (ShortenTimeStepSys) {
    2907         1893 :                 ZoneT = thisZoneHB.XMPT;
    2908              :             }
    2909       100893 :             thisStageControlZone.HeatSetPoint = thisStageControlZone.heatSetptBaseSched->getCurrentVal();
    2910       100893 :             thisStageControlZone.CoolSetPoint = thisStageControlZone.coolSetptBaseSched->getCurrentVal();
    2911              : 
    2912       100893 :             if (thisStageControlZone.HeatSetPoint >= thisStageControlZone.CoolSetPoint) {
    2913            0 :                 ++thisStageControlZone.StageErrCount;
    2914            0 :                 if (thisStageControlZone.StageErrCount < 2) {
    2915            0 :                     ShowWarningError(
    2916              :                         state,
    2917            0 :                         format("ZoneControl:Thermostat:StagedDualSetpoint: The heating setpoint is equal to or above the cooling setpoint in {}",
    2918            0 :                                thisStageControlZone.Name));
    2919            0 :                     ShowContinueError(state, "The zone heating setpoint is set to the cooling setpoint - 0.1C.");
    2920            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    2921              :                 } else {
    2922            0 :                     ShowRecurringWarningErrorAtEnd(state,
    2923              :                                                    "The heating setpoint is still above the cooling setpoint",
    2924            0 :                                                    thisStageControlZone.StageErrIndex,
    2925            0 :                                                    thisStageControlZone.HeatSetPoint,
    2926            0 :                                                    thisStageControlZone.HeatSetPoint);
    2927              :                 }
    2928            0 :                 thisStageControlZone.HeatSetPoint = thisStageControlZone.CoolSetPoint - 0.1; //???????????
    2929              :             }
    2930              :             // Determine either cooling or heating
    2931       100893 :             if (thisStageControlZone.CoolSetPoint < ZoneT) { // Cooling
    2932        19407 :                 Real64 SetpointOffset = ZoneT - thisStageControlZone.CoolSetPoint;
    2933        19407 :                 int Itemp = 0;
    2934        69285 :                 for (int I = 1; I <= thisStageControlZone.NumOfCoolStages; ++I) {
    2935        49878 :                     if (SetpointOffset >= thisStageControlZone.CoolTOffset(I)) {
    2936        30440 :                         Itemp = -I;
    2937              :                     }
    2938              :                 }
    2939        19407 :                 state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ActualZoneNum).StageNum = Itemp;
    2940        19407 :                 if (SetpointOffset >= 0.5 * thisStageControlZone.CoolThroRange) {
    2941         7226 :                     zoneTstatSetpt.setptHi = thisStageControlZone.CoolSetPoint - 0.5 * thisStageControlZone.CoolThroRange;
    2942              :                 } else {
    2943        12181 :                     zoneTstatSetpt.setptHi = thisStageControlZone.CoolSetPoint + 0.5 * thisStageControlZone.CoolThroRange;
    2944              :                 }
    2945        19407 :                 zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi;
    2946              : 
    2947        81486 :             } else if (thisStageControlZone.HeatSetPoint > ZoneT) { // heating
    2948        37113 :                 Real64 SetpointOffset = ZoneT - thisStageControlZone.HeatSetPoint;
    2949        37113 :                 int Itemp = 0;
    2950        86506 :                 for (int I = 1; I <= thisStageControlZone.NumOfHeatStages; ++I) {
    2951        49393 :                     if (std::abs(SetpointOffset) >= std::abs(thisStageControlZone.HeatTOffset(I))) {
    2952        46615 :                         Itemp = I;
    2953              :                     }
    2954              :                 }
    2955        37113 :                 state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ActualZoneNum).StageNum = Itemp;
    2956        37113 :                 if (std::abs(SetpointOffset) >= 0.5 * thisStageControlZone.CoolThroRange) {
    2957        25332 :                     zoneTstatSetpt.setptLo = thisStageControlZone.HeatSetPoint + 0.5 * thisStageControlZone.HeatThroRange;
    2958              :                 } else {
    2959        11781 :                     zoneTstatSetpt.setptLo = thisStageControlZone.HeatSetPoint - 0.5 * thisStageControlZone.HeatThroRange;
    2960              :                 }
    2961        37113 :                 zoneTstatSetpt.setptHi = zoneTstatSetpt.setptLo;
    2962              : 
    2963              :             } else {
    2964        44373 :                 zoneTstatSetpt.setptHi = thisStageControlZone.CoolSetPoint + 0.5 * thisStageControlZone.CoolThroRange;
    2965        44373 :                 zoneTstatSetpt.setptLo = thisStageControlZone.HeatSetPoint - 0.5 * thisStageControlZone.HeatThroRange;
    2966        44373 :                 state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ActualZoneNum).StageNum = 0;
    2967              :             }
    2968              :             // SpaceHB TODO: For now, set space stagenum to zone stagenum - later need to see what space the thermostat is in
    2969       100893 :             if (state.dataHeatBal->doSpaceHeatBalance) {
    2970            0 :                 for (int spaceNum : state.dataHeatBal->Zone(ActualZoneNum).spaceIndexes) {
    2971            0 :                     state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).StageNum =
    2972            0 :                         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ActualZoneNum).StageNum;
    2973            0 :                 }
    2974              :             }
    2975              :         }
    2976              :     }
    2977              : 
    2978              :     // Setpoint revision for onoff thermostat
    2979      3896818 :     if (s_ztpc->NumOnOffCtrZone > 0) {
    2980        93112 :         Real64 TempTole = 0.02;
    2981              :         Real64 Tprev;
    2982       186224 :         for (int RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++RelativeZoneNum) {
    2983        93112 :             auto &thisTempControlledZone = state.dataZoneCtrls->TempControlledZone(RelativeZoneNum);
    2984        93112 :             if (thisTempControlledZone.DeltaTCutSet > 0.0) {
    2985        93112 :                 if (ShortenTimeStepSys) {
    2986        16254 :                     thisTempControlledZone.HeatModeLast = thisTempControlledZone.HeatModeLastSave;
    2987        16254 :                     thisTempControlledZone.CoolModeLast = thisTempControlledZone.CoolModeLastSave;
    2988              :                 } else {
    2989        76858 :                     thisTempControlledZone.HeatModeLastSave = thisTempControlledZone.HeatModeLast;
    2990        76858 :                     thisTempControlledZone.CoolModeLastSave = thisTempControlledZone.CoolModeLast;
    2991              :                 }
    2992              : 
    2993        93112 :                 auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(thisTempControlledZone.ActualZoneNum);
    2994        93112 :                 auto &thisZoneHB = s_ztpc->zoneHeatBalance(thisTempControlledZone.ActualZoneNum);
    2995        93112 :                 thisTempControlledZone.CoolOffFlag = false;
    2996        93112 :                 thisTempControlledZone.HeatOffFlag = false;
    2997        93112 :                 if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::ThirdOrder) {
    2998        93112 :                     Tprev = thisZoneHB.MAT;
    2999        93112 :                     if (ShortenTimeStepSys) {
    3000        16254 :                         Tprev = thisZoneHB.XMPT;
    3001              :                     }
    3002              :                 } else {
    3003            0 :                     Tprev = thisZoneHB.T1;
    3004              :                 }
    3005              : 
    3006        93112 :                 switch (state.dataHeatBalFanSys->TempControlType(thisTempControlledZone.ActualZoneNum)) {
    3007              : 
    3008            0 :                 case HVAC::SetptType::SingleHeat: {
    3009            0 :                     zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo;
    3010            0 :                     zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3011            0 :                     if (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + TempTole) {
    3012            0 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3013            0 :                         zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    3014            0 :                     } else if (Tprev > thisTempControlledZone.ZoneThermostatSetPointLo &&
    3015            0 :                                (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet - TempTole)) {
    3016            0 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3017            0 :                         zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    3018              :                     } else {
    3019            0 :                         thisTempControlledZone.HeatOffFlag = true;
    3020              :                     }
    3021            0 :                     if (thisTempControlledZone.HeatModeLast && Tprev > thisTempControlledZone.ZoneThermostatSetPointLo) {
    3022            0 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo;
    3023            0 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3024            0 :                         thisTempControlledZone.HeatOffFlag = true;
    3025              :                     }
    3026            0 :                 } break;
    3027              : 
    3028            0 :                 case HVAC::SetptType::SingleCool: {
    3029            0 :                     zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi;
    3030            0 :                     zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3031            0 :                     if (Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - TempTole) {
    3032            0 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3033            0 :                         zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3034            0 :                     } else if (Tprev < thisTempControlledZone.ZoneThermostatSetPointHi &&
    3035            0 :                                Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet + TempTole) {
    3036            0 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3037            0 :                         zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3038              :                     } else {
    3039            0 :                         thisTempControlledZone.CoolOffFlag = true;
    3040              :                     }
    3041            0 :                     if (thisTempControlledZone.CoolModeLast && Tprev < thisTempControlledZone.ZoneThermostatSetPointHi) {
    3042            0 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi;
    3043            0 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3044            0 :                         thisTempControlledZone.CoolOffFlag = true;
    3045              :                     }
    3046            0 :                 } break;
    3047              : 
    3048        93112 :                 case HVAC::SetptType::DualHeatCool: {
    3049        93112 :                     zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3050        93112 :                     zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3051        93112 :                     if (Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - TempTole) {
    3052         6917 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3053        86195 :                     } else if (Tprev < thisTempControlledZone.ZoneThermostatSetPointHi &&
    3054        86195 :                                Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet + TempTole) {
    3055        31484 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3056              :                     } else {
    3057        54711 :                         thisTempControlledZone.CoolOffFlag = true;
    3058              :                     }
    3059        93112 :                     if (thisTempControlledZone.CoolModeLast && Tprev < thisTempControlledZone.ZoneThermostatSetPointHi) {
    3060        66971 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3061        66971 :                         thisTempControlledZone.CoolOffFlag = true;
    3062              :                     }
    3063              : 
    3064        93112 :                     if (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + TempTole) {
    3065        14898 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3066        78214 :                     } else if (Tprev > thisTempControlledZone.ZoneThermostatSetPointLo &&
    3067        78214 :                                (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet - TempTole)) {
    3068        23406 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3069              :                     } else {
    3070        54808 :                         thisTempControlledZone.HeatOffFlag = true;
    3071              :                     }
    3072        93112 :                     if (thisTempControlledZone.HeatModeLast && Tprev > thisTempControlledZone.ZoneThermostatSetPointLo) {
    3073        46868 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3074        46868 :                         thisTempControlledZone.HeatOffFlag = true;
    3075              :                     }
    3076              : 
    3077              :                     // check setpoint for both and provde an error message
    3078        93112 :                     if (zoneTstatSetpt.setptLo >= zoneTstatSetpt.setptHi) {
    3079            0 :                         ShowSevereError(state,
    3080              :                                         "DualSetPointWithDeadBand: When Temperature Difference Between Cutout And Setpoint is applied, the heating "
    3081              :                                         "setpoint is greater than the cooling setpoint. ");
    3082            0 :                         ShowContinueErrorTimeStamp(state,
    3083            0 :                                                    format("occurs in Zone={}", state.dataHeatBal->Zone(thisTempControlledZone.ActualZoneNum).Name));
    3084            0 :                         ShowContinueError(state, format("Zone Heating ThermostatSetPoint={:.2R}", zoneTstatSetpt.setptLo));
    3085            0 :                         ShowContinueError(state, format("Zone Cooling ThermostatSetPoint={:.2R}", zoneTstatSetpt.setptHi));
    3086            0 :                         ShowFatalError(state, "Program terminates due to above conditions.");
    3087              :                     }
    3088        93112 :                 } break;
    3089              : 
    3090            0 :                 default: {
    3091            0 :                 } break;
    3092              :                 } // switch (setptType)
    3093              :             }
    3094              :         }
    3095              :     }
    3096              : 
    3097     35243058 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    3098     31346240 :         auto &thisZoneHB = s_ztpc->zoneHeatBalance(zoneNum);
    3099     31346240 :         thisZoneHB.predictSystemLoad(state, ShortenTimeStepSys, UseZoneTimeStepHistory, PriorTimeStep, zoneNum);
    3100     62756344 :         for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    3101     31410104 :             if (state.dataHeatBal->doSpaceHeatBalance) {
    3102        86394 :                 s_ztpc->spaceHeatBalance(spaceNum).predictSystemLoad(
    3103              :                     state, ShortenTimeStepSys, UseZoneTimeStepHistory, PriorTimeStep, zoneNum, spaceNum);
    3104     31323710 :             } else if (ShortenTimeStepSys) {
    3105      3162218 :                 s_ztpc->spaceHeatBalance(spaceNum).MAT = thisZoneHB.MAT;
    3106      3162218 :                 s_ztpc->spaceHeatBalance(spaceNum).airHumRat = thisZoneHB.airHumRat;
    3107              :             }
    3108     31346240 :         }
    3109              :     }
    3110      3896818 :     if (s_ztpc->NumOnOffCtrZone > 0) {
    3111       186224 :         for (int RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++RelativeZoneNum) {
    3112        93112 :             auto &thisTempControlledZone = state.dataZoneCtrls->TempControlledZone(RelativeZoneNum);
    3113        93112 :             if (thisTempControlledZone.DeltaTCutSet > 0.0) {
    3114        93112 :                 int ZoneNum = thisTempControlledZone.ActualZoneNum;
    3115        93112 :                 if (thisTempControlledZone.CoolOffFlag && state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).TotalOutputRequired >= 0.0) {
    3116        66033 :                     thisTempControlledZone.CoolModeLast = true;
    3117              :                 } else {
    3118        27079 :                     thisTempControlledZone.CoolModeLast = false;
    3119              :                 }
    3120        93112 :                 if (thisTempControlledZone.HeatOffFlag && state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).TotalOutputRequired <= 0.0) {
    3121        46851 :                     thisTempControlledZone.HeatModeLast = true;
    3122              :                 } else {
    3123        46261 :                     thisTempControlledZone.HeatModeLast = false;
    3124              :                 }
    3125              :             }
    3126              :         }
    3127              :     }
    3128      3896818 : }
    3129     31432634 : void ZoneSpaceHeatBalanceData::predictSystemLoad(
    3130              :     EnergyPlusData &state,
    3131              :     bool const shortenTimeStepSys,
    3132              :     bool const useZoneTimeStepHistory, // if true then use zone timestep history, if false use system time step
    3133              :     Real64 const priorTimeStep,        // the old value for timestep length is passed for possible use in interpolating
    3134              :     int zoneNum,
    3135              :     int spaceNum)
    3136              : {
    3137     31432634 :     assert(zoneNum > 0);
    3138     31432634 :     this->updateTemperatures(state, shortenTimeStepSys, useZoneTimeStepHistory, priorTimeStep, zoneNum, spaceNum);
    3139              : 
    3140     31432634 :     Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    3141     31432634 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    3142              : 
    3143     31432634 :     Real64 volume = 0.0;
    3144     31432634 :     if (spaceNum > 0) {
    3145        86394 :         volume = state.dataHeatBal->space(spaceNum).Volume;
    3146              :     } else {
    3147     31346240 :         volume = state.dataHeatBal->Zone(zoneNum).Volume;
    3148              :     }
    3149              : 
    3150     31432634 :     this->AirPowerCap = volume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpSens *
    3151     31432634 :                         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->MAT, this->airHumRat) *
    3152     31432634 :                         Psychrometrics::PsyCpAirFnW(this->airHumRat) / TimeStepSysSec;
    3153     31432634 :     Real64 RAFNFrac = 0.0;
    3154              : 
    3155              :     // Calculate the various heat balance sums
    3156              : 
    3157              :     // NOTE: SumSysMCp and SumSysMCpT are not used in the predict step
    3158     31432634 :     this->calcZoneOrSpaceSums(state, false, zoneNum, spaceNum);
    3159              : 
    3160              :     // Sum all convective internal gains except for people: SumIntGainExceptPeople
    3161     31432634 :     if (spaceNum == 0 && state.dataHybridModel->FlagHybridModel_PC) {
    3162         8212 :         this->SumIntGainExceptPeople = 0.0;
    3163         8212 :         this->SumIntGainExceptPeople = InternalHeatGains::SumAllInternalConvectionGainsExceptPeople(state, zoneNum);
    3164              :     }
    3165              : 
    3166     31432634 :     this->TempDepCoef = this->SumHA + this->SumMCp;
    3167     31432634 :     this->TempIndCoef = this->SumIntGain + this->SumHATsurf - this->SumHATref + this->SumMCpT + this->SysDepZoneLoadsLagged;
    3168     31432634 :     this->TempHistoryTerm = this->AirPowerCap * (3.0 * this->ZTM[0] - (3.0 / 2.0) * this->ZTM[1] + (1.0 / 3.0) * this->ZTM[2]);
    3169     31432634 :     this->tempDepLoad = (11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef;
    3170     31432634 :     this->tempIndLoad = this->TempHistoryTerm + this->TempIndCoef;
    3171     31432634 :     if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    3172       160955 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    3173              :             // RoomAirflowNetworkModel - make dynamic term independent of TimeStepSys
    3174         7413 :             auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
    3175         7413 :             if (afnZoneInfo.IsUsed) {
    3176         7413 :                 int RoomAirNode = afnZoneInfo.ControlAirNodeID;
    3177         7413 :                 RoomAir::LoadPredictionRoomAirModelAFN(state, zoneNum, RoomAirNode);
    3178         7413 :                 this->TempDepCoef = afnZoneInfo.Node(RoomAirNode).SumHA + afnZoneInfo.Node(RoomAirNode).SumLinkMCp;
    3179         7413 :                 this->TempIndCoef = afnZoneInfo.Node(RoomAirNode).SumIntSensibleGain + afnZoneInfo.Node(RoomAirNode).SumHATsurf -
    3180         7413 :                                     afnZoneInfo.Node(RoomAirNode).SumHATref + afnZoneInfo.Node(RoomAirNode).SumLinkMCpT +
    3181         7413 :                                     afnZoneInfo.Node(RoomAirNode).SysDepZoneLoadsLagged;
    3182         7413 :                 this->AirPowerCap = afnZoneInfo.Node(RoomAirNode).AirVolume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpSens *
    3183         7413 :                                     afnZoneInfo.Node(RoomAirNode).RhoAir * afnZoneInfo.Node(RoomAirNode).CpAir / TimeStepSysSec;
    3184         7413 :                 this->TempHistoryTerm = this->AirPowerCap * (3.0 * this->ZTM[0] - (3.0 / 2.0) * this->ZTM[1] + (1.0 / 3.0) * this->ZTM[2]);
    3185         7413 :                 this->tempDepLoad = (11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef;
    3186         7413 :                 this->tempIndLoad = this->TempHistoryTerm + this->TempIndCoef;
    3187         7413 :                 if (afnZoneInfo.Node(RoomAirNode).HasHVACAssigned) {
    3188         7413 :                     RAFNFrac = afnZoneInfo.Node(RoomAirNode).HVAC(1).SupplyFraction;
    3189              :                 }
    3190              :             }
    3191              :         }
    3192              :     }
    3193              : 
    3194              :     // Exact solution or Euler method
    3195     31432634 :     state.dataHVACGlobal->ShortenTimeStepSysRoomAir = false;
    3196     31432634 :     if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
    3197     14341335 :         if (shortenTimeStepSys && TimeStepSys < state.dataGlobal->TimeStepZone) {
    3198      1866675 :             if (state.dataHVACGlobal->PreviousTimeStep < state.dataGlobal->TimeStepZone) {
    3199      1554480 :                 this->T1 = this->TM2;
    3200      1554480 :                 this->W1 = this->WM2;
    3201      1554480 :                 if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    3202            0 :                     auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
    3203            0 :                     for (auto &afnNode : afnZoneInfo.Node) {
    3204            0 :                         afnNode.AirTempT1 = afnNode.AirTempT2;
    3205            0 :                         afnNode.HumRatT1 = afnNode.HumRatT2;
    3206              :                     }
    3207              :                 }
    3208              :             } else {
    3209       312195 :                 this->T1 = this->TMX;
    3210       312195 :                 this->W1 = this->WMX;
    3211       312195 :                 if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    3212            0 :                     auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
    3213            0 :                     for (auto &afnNode : afnZoneInfo.Node) {
    3214            0 :                         afnNode.AirTempT1 = afnNode.AirTempTX;
    3215            0 :                         afnNode.HumRatT1 = afnNode.HumRatTX;
    3216              :                     }
    3217              :                 }
    3218              :             }
    3219      1866675 :             state.dataHVACGlobal->ShortenTimeStepSysRoomAir = true;
    3220              :         } else {
    3221     12474660 :             this->T1 = this->ZT;
    3222     12474660 :             this->W1 = this->airHumRat;
    3223     12474660 :             if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    3224            0 :                 auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
    3225            0 :                 for (auto &afnNode : afnZoneInfo.Node) {
    3226            0 :                     afnNode.AirTempT1 = afnNode.AirTemp;
    3227            0 :                     afnNode.HumRatT1 = afnNode.HumRat;
    3228              :                 }
    3229              :             }
    3230              :         }
    3231     14341335 :         this->tempDepLoad = this->TempDepCoef;
    3232     14341335 :         this->tempIndLoad = this->TempIndCoef;
    3233              :     }
    3234              : 
    3235              :     // Calculate the predicted zone load to be provided by the system with the given desired zone air temperature
    3236     31432634 :     this->calcPredictedSystemLoad(state, RAFNFrac, zoneNum, spaceNum);
    3237              : 
    3238              :     // Calculate the predicted zone load to be provided by the system with the given desired humidity ratio
    3239     31432634 :     this->calcPredictedHumidityRatio(state, RAFNFrac, zoneNum, spaceNum);
    3240     31432634 : }
    3241              : 
    3242      2828450 : void CalcZoneAirTempSetPoints(EnergyPlusData &state)
    3243              : {
    3244              : 
    3245              :     // SUBROUTINE INFORMATION:
    3246              :     //       AUTHOR         Russ Taylor
    3247              :     //       DATE WRITTEN   Nov 1997
    3248              :     //       MODIFIED       Aug 2013, Xiufeng Pang (XP) - Added code for updating set points during
    3249              :     //                      optimum start period
    3250              :     //       RE-ENGINEERED  na
    3251              : 
    3252              :     // PURPOSE OF THIS SUBROUTINE:
    3253              :     // This routine sets what the setpoints for each controlled zone should be based on schedules.
    3254              :     // This is called each time step.
    3255              : 
    3256              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3257              :     int RelativeZoneNum;
    3258              :     int ActualZoneNum;
    3259              :     int OccStartTime; // Occupancy start time - for optimum start
    3260              :     Real64 DeltaT;    // Temperature difference between cutout and setpoint
    3261              : 
    3262      2828450 :     auto &s_hbfs = state.dataHeatBalFanSys;
    3263              : 
    3264      2828450 :     auto &Zone = state.dataHeatBal->Zone;
    3265      2828450 :     auto &TempControlledZone = state.dataZoneCtrls->TempControlledZone;
    3266      2828450 :     auto &TempControlType = state.dataHeatBalFanSys->TempControlType;
    3267      2828450 :     auto &TempControlTypeRpt = state.dataHeatBalFanSys->TempControlTypeRpt;
    3268      2828450 :     int NumOfZones = state.dataGlobal->NumOfZones;
    3269              : 
    3270      2828450 :     TempControlType = HVAC::SetptType::Uncontrolled; // Default
    3271              : 
    3272              :     // Place holder for occupied heating and cooling set points - for optimum start
    3273      2828450 :     if (!allocated(state.dataZoneCtrls->OccRoomTSetPointHeat)) {
    3274          801 :         state.dataZoneCtrls->OccRoomTSetPointHeat.allocate(NumOfZones);
    3275              :     }
    3276      2828450 :     if (!allocated(state.dataZoneCtrls->OccRoomTSetPointCool)) {
    3277          801 :         state.dataZoneCtrls->OccRoomTSetPointCool.allocate(NumOfZones);
    3278              :     }
    3279      2828450 :     state.dataZoneCtrls->OccRoomTSetPointHeat = 0.0;
    3280      2828450 :     state.dataZoneCtrls->OccRoomTSetPointCool = 100.0;
    3281      2828450 :     DeltaT = 0.0;
    3282              : 
    3283     20391270 :     for (RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++RelativeZoneNum) {
    3284     17562820 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(RelativeZoneNum);
    3285              :         // What if this zone not controlled???
    3286              : 
    3287     17562820 :         int ActualZoneNum = tempZone.ActualZoneNum;
    3288     17562820 :         TempControlType(ActualZoneNum) = static_cast<HVAC::SetptType>(tempZone.setptTypeSched->getCurrentVal());
    3289     17562820 :         TempControlTypeRpt(ActualZoneNum) = static_cast<int>(TempControlType(ActualZoneNum));
    3290              :         // Error detection for these values is done in the Get routine
    3291              : 
    3292     17562820 :         auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(tempZone.ActualZoneNum);
    3293              : 
    3294     17562820 :         switch (TempControlType(ActualZoneNum)) {
    3295         2034 :         case HVAC::SetptType::Uncontrolled: {
    3296         2034 :         } break;
    3297              : 
    3298      1911933 :         case HVAC::SetptType::SingleHeat: {
    3299      1911933 :             if (tempZone.setpts[(int)HVAC::SetptType::SingleHeat].isUsed) {
    3300      1911933 :                 zoneTstatSetpt.setpt = tempZone.setpts[(int)HVAC::SetptType::SingleHeat].heatSetptSched->getCurrentVal();
    3301      1911933 :                 tempZone.ZoneThermostatSetPointLo = zoneTstatSetpt.setpt;
    3302              : 
    3303      1911933 :                 AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setpt);
    3304      1911933 :                 zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    3305              :             }
    3306      1911933 :         } break;
    3307              : 
    3308      1926233 :         case HVAC::SetptType::SingleCool: {
    3309      1926233 :             zoneTstatSetpt.setpt = tempZone.setpts[(int)HVAC::SetptType::SingleCool].coolSetptSched->getCurrentVal();
    3310      1926233 :             tempZone.ZoneThermostatSetPointHi = zoneTstatSetpt.setpt;
    3311              : 
    3312              :             // Added Jan 17 (X. Luo)
    3313              :             // Adjust operative temperature based on adaptive comfort model
    3314      1926233 :             if (tempZone.AdaptiveComfortTempControl) {
    3315         6750 :                 AdjustOperativeSetPointsforAdapComfort(state, RelativeZoneNum, zoneTstatSetpt.setpt);
    3316         6750 :                 zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setpt;
    3317              :             }
    3318              : 
    3319      1926233 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setpt);
    3320      1926233 :             zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3321              : 
    3322      1926233 :             AdjustCoolingSetPointforTempAndHumidityControl(state, RelativeZoneNum, ActualZoneNum);
    3323      1926233 :         } break;
    3324              : 
    3325        79254 :         case HVAC::SetptType::SingleHeatCool: {
    3326        79254 :             zoneTstatSetpt.setpt = tempZone.setpts[(int)HVAC::SetptType::SingleHeatCool].heatSetptSched->getCurrentVal();
    3327              : 
    3328              :             // Added Jan 17 (X. Luo)
    3329              :             // Adjust operative temperature based on adaptive comfort model
    3330        79254 :             if (tempZone.AdaptiveComfortTempControl) {
    3331            0 :                 AdjustOperativeSetPointsforAdapComfort(state, RelativeZoneNum, zoneTstatSetpt.setpt);
    3332            0 :                 zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setpt;
    3333              :             }
    3334              : 
    3335        79254 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setpt);
    3336              : 
    3337        79254 :             zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3338              : 
    3339              :             // Change the room set point to occupied set point during optimum start period--------------
    3340              : 
    3341        79254 :             if (allocated(state.dataAvail->OptStart)) {
    3342            0 :                 if (state.dataAvail->OptStart(ActualZoneNum).ActualZoneNum == ActualZoneNum) {
    3343              : 
    3344            0 :                     OccStartTime = CEILING(state.dataAvail->OptStart(ActualZoneNum).OccStartTime) + 1;
    3345            0 :                     zoneTstatSetpt.setpt = tempZone.setpts[(int)HVAC::SetptType::SingleHeat].heatSetptSched->getDayVals(
    3346            0 :                         state)[OccStartTime * state.dataGlobal->TimeStepsInHour];
    3347              :                 }
    3348              : 
    3349            0 :                 if (state.dataAvail->OptStart(ActualZoneNum).OptStartFlag) {
    3350            0 :                     zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3351              :                 }
    3352              :             }
    3353              :             //--------------------------------------------------------------------------------------------
    3354        79254 :         } break;
    3355              : 
    3356     13643366 :         case HVAC::SetptType::DualHeatCool: {
    3357     13643366 :             zoneTstatSetpt.setptHi = tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].coolSetptSched->getCurrentVal();
    3358     13643366 :             TempControlledZone(RelativeZoneNum).ZoneThermostatSetPointHi = zoneTstatSetpt.setptHi;
    3359              : 
    3360              :             // Added Jan 17 (X. Luo)
    3361              :             // Adjust operative temperature based on adaptive comfort model
    3362     13643366 :             if ((TempControlledZone(RelativeZoneNum).AdaptiveComfortTempControl)) {
    3363           15 :                 AdjustOperativeSetPointsforAdapComfort(state, RelativeZoneNum, zoneTstatSetpt.setptHi);
    3364           15 :                 zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setptHi;
    3365              :             }
    3366              : 
    3367     13643366 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setptHi);
    3368              : 
    3369     13643366 :             zoneTstatSetpt.setptLo = tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].heatSetptSched->getCurrentVal();
    3370     13643366 :             TempControlledZone(RelativeZoneNum).ZoneThermostatSetPointLo = zoneTstatSetpt.setptLo;
    3371     13643366 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setptLo);
    3372              : 
    3373              :             // Change the room set point to occupied set point during optimum start period--------------
    3374              : 
    3375     13643366 :             if (allocated(state.dataAvail->OptStart)) {
    3376        36848 :                 if (state.dataAvail->OptStart(ActualZoneNum).ActualZoneNum == ActualZoneNum) {
    3377              :                     // TODO: Why are we getting all day values if all we want is the value at (1, OccStartTime);
    3378        11515 :                     OccStartTime = CEILING(state.dataAvail->OptStart(ActualZoneNum).OccStartTime) + 1;
    3379        11515 :                     state.dataZoneCtrls->OccRoomTSetPointCool(ActualZoneNum) =
    3380        11515 :                         tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].coolSetptSched->getDayVals(
    3381        11515 :                             state)[OccStartTime * state.dataGlobal->TimeStepsInHour];
    3382        11515 :                     state.dataZoneCtrls->OccRoomTSetPointHeat(ActualZoneNum) =
    3383        11515 :                         tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].heatSetptSched->getDayVals(
    3384        11515 :                             state)[OccStartTime * state.dataGlobal->TimeStepsInHour];
    3385              :                 }
    3386              : 
    3387        36848 :                 if (state.dataAvail->OptStart(ActualZoneNum).OptStartFlag) {
    3388         1740 :                     zoneTstatSetpt.setptHi = state.dataZoneCtrls->OccRoomTSetPointCool(ActualZoneNum);
    3389         1740 :                     zoneTstatSetpt.setptLo = state.dataZoneCtrls->OccRoomTSetPointHeat(ActualZoneNum);
    3390              :                 }
    3391              :             }
    3392              :             //--------------------------------------------------------------------------------------------
    3393              : 
    3394     13643366 :             AdjustCoolingSetPointforTempAndHumidityControl(state, RelativeZoneNum, ActualZoneNum);
    3395     13643366 :         } break;
    3396              : 
    3397            0 :         default: {
    3398            0 :             ShowSevereError(state,
    3399            0 :                             format("CalcZoneAirTempSetpoints: Illegal control type for Zone={}, Found value={}, in Schedule={}",
    3400            0 :                                    Zone(ActualZoneNum).Name,
    3401              :                                    TempControlType(ActualZoneNum),
    3402            0 :                                    tempZone.setptTypeSched->Name));
    3403              : 
    3404            0 :         } break;
    3405              :         } // switch
    3406              : 
    3407              :         // Apply offset for faulty thermostats
    3408     17567428 :         if ((state.dataFaultsMgr->NumFaultyThermostat > 0) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
    3409         4608 :             (!state.dataGlobal->KickOffSimulation)) {
    3410              :             //  loop through the FaultsThermostatOffset objects to find the one for the zone
    3411        34848 :             for (int iFault = 1; iFault <= state.dataFaultsMgr->NumFaultyThermostat; ++iFault) {
    3412              :                 // Why are we doing this here?
    3413        34848 :                 if (Util::SameString(TempControlledZone(RelativeZoneNum).Name,
    3414        34848 :                                      state.dataFaultsMgr->FaultsThermostatOffset(iFault).FaultyThermostatName)) {
    3415              : 
    3416              :                     // Check fault availability schedules
    3417         4608 :                     if (state.dataFaultsMgr->FaultsThermostatOffset(iFault).availSched->getCurrentVal() > 0.0) {
    3418              : 
    3419              :                         // Check fault severity schedules to update the reference thermostat offset
    3420         4608 :                         Real64 rSchVal = 1.0;
    3421              :                         Real64 offsetUpdated;
    3422         4608 :                         if (state.dataFaultsMgr->FaultsThermostatOffset(iFault).severitySched != nullptr) {
    3423         4608 :                             rSchVal = state.dataFaultsMgr->FaultsThermostatOffset(iFault).severitySched->getCurrentVal();
    3424              :                         }
    3425         4608 :                         offsetUpdated = rSchVal * state.dataFaultsMgr->FaultsThermostatOffset(iFault).Offset;
    3426              : 
    3427              :                         // Positive offset means the sensor reading is higher than the actual value
    3428         4608 :                         zoneTstatSetpt.setpt -= offsetUpdated;
    3429         4608 :                         zoneTstatSetpt.setptLo -= offsetUpdated;
    3430         4608 :                         zoneTstatSetpt.setptHi -= offsetUpdated;
    3431              :                     }
    3432              : 
    3433              :                     // Stop searching the FaultsThermostatOffset object for the zone
    3434         4608 :                     break;
    3435              :                 }
    3436              :             }
    3437              :         }
    3438              :     }
    3439              : 
    3440      2828450 :     if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
    3441         2025 :         CalcZoneAirComfortSetPoints(state);
    3442              :     }
    3443      2828450 :     OverrideAirSetPointsforEMSCntrl(state);
    3444      2828450 : }
    3445              : 
    3446     31432634 : void ZoneSpaceHeatBalanceData::calcPredictedHumidityRatio(EnergyPlusData &state, Real64 const RAFNFrac, int const zoneNum, int const spaceNum)
    3447              : {
    3448              : 
    3449              :     // SUBROUTINE INFORMATION:
    3450              :     //       AUTHOR         Richard J. Liesen
    3451              :     //       DATE WRITTEN   May 2001
    3452              : 
    3453              :     // PURPOSE OF THIS SUBROUTINE:
    3454              :     // This subroutine does the prediction step for humidity control
    3455              : 
    3456              :     // METHODOLOGY EMPLOYED:
    3457              :     // This solves for the required system moisture required to try and achieve the desired
    3458              :     // Humidity Ratio in the Zone
    3459              : 
    3460              :     // REFERENCES:
    3461              :     // Routine FinalZnCalcs - FINAL ZONE CALCULATIONS, authored by Dale Herron
    3462              :     // for BLAST.
    3463              : 
    3464              :     static constexpr std::string_view RoutineName("calcPredictedHumidityRatio");
    3465              : 
    3466     31432634 :     Real64 ZoneRHHumidifyingSetPoint = 0.0;   // Zone humidifying set point (%)
    3467     31432634 :     Real64 ZoneRHDehumidifyingSetPoint = 0.0; // Zone dehumidifying set point (%)
    3468              : 
    3469     31432634 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    3470     31432634 :     bool SingleSetPoint = false; // This determines whether both setpoint are equal or not
    3471              : 
    3472              :     // Check to see if this is a "humidity controlled zone"
    3473     31432634 :     bool ControlledHumidZoneFlag = false;
    3474              :     // Check all the controlled zones to see if it matches the zone simulated
    3475     31432634 :     if (thisZone.humidityControlZoneIndex > 0) {
    3476      1184325 :         auto &humidityControlZone = state.dataZoneCtrls->HumidityControlZone(thisZone.humidityControlZoneIndex);
    3477      1184325 :         assert(humidityControlZone.ActualZoneNum == zoneNum);
    3478      1184325 :         ZoneRHHumidifyingSetPoint = humidityControlZone.humidifyingSched->getCurrentVal();
    3479      1184325 :         ZoneRHDehumidifyingSetPoint = humidityControlZone.dehumidifyingSched->getCurrentVal();
    3480              : 
    3481              :         // Apply EMS values to overwrite the humidistat values
    3482      1184325 :         if (humidityControlZone.EMSOverrideHumidifySetPointOn) {
    3483            0 :             ZoneRHHumidifyingSetPoint = humidityControlZone.EMSOverrideHumidifySetPointValue;
    3484              :         }
    3485      1184325 :         if (humidityControlZone.EMSOverrideDehumidifySetPointOn) {
    3486            0 :             ZoneRHDehumidifyingSetPoint = humidityControlZone.EMSOverrideDehumidifySetPointValue;
    3487              :         }
    3488              : 
    3489              :         // Apply offsets for faulty humidistats
    3490      1184947 :         if ((state.dataFaultsMgr->NumFaultyHumidistat > 0) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
    3491          622 :             (!state.dataGlobal->KickOffSimulation)) {
    3492              : 
    3493              :             //  loop through the FaultsHumidistatOffset objects to find the one for the zone
    3494          622 :             for (int iFault = 1; iFault <= state.dataFaultsMgr->NumFaultyHumidistat; ++iFault) {
    3495              : 
    3496          622 :                 if (Util::SameString(humidityControlZone.ControlName, state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyHumidistatName)) {
    3497              : 
    3498          622 :                     if (Util::SameString(state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyHumidistatType, "ThermostatOffsetDependent")) {
    3499              :                         // For Humidistat Offset Type I: ThermostatOffsetDependent
    3500              : 
    3501          304 :                         bool IsThermostatFound = false;
    3502          304 :                         Real64 offsetThermostat = 0.0;
    3503              : 
    3504              :                         // Get the offset value of the corresponding thermostat fault object
    3505          304 :                         if (state.dataFaultsMgr->NumFaultyThermostat > 0) {
    3506              : 
    3507              :                             //  loop through the FaultsThermostatOffset objects to find the one causes the Humidistat Offset
    3508          304 :                             for (int iFaultThermo = 1; iFaultThermo <= state.dataFaultsMgr->NumFaultyThermostat; ++iFaultThermo) {
    3509              : 
    3510          304 :                                 if (Util::SameString(state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyThermostatName,
    3511          304 :                                                      state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).Name)) {
    3512          304 :                                     IsThermostatFound = true;
    3513              : 
    3514              :                                     // Check fault availability schedules
    3515          304 :                                     if (state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).availSched->getCurrentVal() > 0.0) {
    3516              : 
    3517              :                                         // Check fault severity schedules to update the reference thermostat offset
    3518          304 :                                         Real64 rSchVal = 1.0;
    3519          304 :                                         if (state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).severitySched != nullptr) {
    3520          304 :                                             rSchVal = state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).severitySched->getCurrentVal();
    3521              :                                         }
    3522          304 :                                         offsetThermostat = rSchVal * state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).Offset;
    3523              :                                     }
    3524              : 
    3525              :                                     // Stop searching the FaultsThermostatOffset object for the Humidistat Offset
    3526          304 :                                     break;
    3527              :                                 }
    3528              :                             }
    3529              :                         }
    3530              : 
    3531              :                         // The FaultsThermostatOffset specified in the FaultHumidistatOffset is not found
    3532          304 :                         if (!IsThermostatFound) {
    3533            0 :                             ShowSevereError(
    3534              :                                 state,
    3535            0 :                                 format("FaultModel:HumidistatOffset = \"{}\" invalid Reference Humidistat Offset Name = \"{}\" not found.",
    3536            0 :                                        state.dataFaultsMgr->FaultsHumidistatOffset(iFault).Name,
    3537            0 :                                        state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyThermostatName));
    3538            0 :                             ShowFatalError(state, "Errors getting FaultModel input data.  Preceding condition(s) cause termination.");
    3539              :                         }
    3540              : 
    3541          304 :                         if (offsetThermostat != 0.0) {
    3542              :                             // Calculate the humidistat offset value from the thermostat offset value
    3543          304 :                             Real64 faultZoneWHumidifyingSetPoint = Psychrometrics::PsyWFnTdbRhPb(
    3544          304 :                                 state, (this->MAT + offsetThermostat), (ZoneRHHumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress);
    3545          304 :                             Real64 faultZoneWDehumidifyingSetPoint = Psychrometrics::PsyWFnTdbRhPb(
    3546          304 :                                 state, (this->MAT + offsetThermostat), (ZoneRHDehumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress);
    3547              :                             Real64 offsetZoneRHHumidifyingSetPoint =
    3548          304 :                                 ZoneRHHumidifyingSetPoint -
    3549          304 :                                 Psychrometrics::PsyRhFnTdbWPb(state, this->MAT, faultZoneWHumidifyingSetPoint, state.dataEnvrn->OutBaroPress) * 100.0;
    3550              :                             Real64 offsetZoneRHDehumidifyingSetPoint =
    3551          304 :                                 ZoneRHDehumidifyingSetPoint -
    3552          304 :                                 Psychrometrics::PsyRhFnTdbWPb(state, this->MAT, faultZoneWDehumidifyingSetPoint, state.dataEnvrn->OutBaroPress) *
    3553          304 :                                     100.0;
    3554              : 
    3555              :                             // Apply the calculated humidistat offset value
    3556              :                             // Positive offset means the sensor reading is higher than the actual value
    3557          304 :                             ZoneRHHumidifyingSetPoint -= offsetZoneRHHumidifyingSetPoint;
    3558          304 :                             ZoneRHDehumidifyingSetPoint -= offsetZoneRHDehumidifyingSetPoint;
    3559              : 
    3560              :                             // constrain value to something reasonable
    3561          304 :                             ZoneRHHumidifyingSetPoint = min(100.0, max(0.0, ZoneRHHumidifyingSetPoint));
    3562          304 :                             ZoneRHDehumidifyingSetPoint = min(100.0, max(0.0, ZoneRHDehumidifyingSetPoint));
    3563              :                         }
    3564              : 
    3565              :                     } else {
    3566              :                         // For Humidistat Offset Type II: ThermostatOffsetIndependent
    3567              : 
    3568              :                         // Check fault availability schedules
    3569          318 :                         if (state.dataFaultsMgr->FaultsHumidistatOffset(iFault).availSched->getCurrentVal() > 0.0) {
    3570              : 
    3571              :                             // Check fault severity schedules to update the reference humidistat offset
    3572          318 :                             Real64 rSchVal = 1.0;
    3573              :                             Real64 offsetUpdated;
    3574          318 :                             if (state.dataFaultsMgr->FaultsHumidistatOffset(iFault).severitySched != nullptr) {
    3575          318 :                                 rSchVal = state.dataFaultsMgr->FaultsHumidistatOffset(iFault).severitySched->getCurrentVal();
    3576              :                             }
    3577          318 :                             offsetUpdated = rSchVal * state.dataFaultsMgr->FaultsHumidistatOffset(iFault).Offset;
    3578              : 
    3579              :                             // Positive offset means the sensor reading is higher than the actual value
    3580          318 :                             ZoneRHHumidifyingSetPoint -= offsetUpdated;
    3581          318 :                             ZoneRHDehumidifyingSetPoint -= offsetUpdated;
    3582              : 
    3583              :                             // constrain value to something reasonable
    3584          318 :                             ZoneRHHumidifyingSetPoint = min(100.0, max(0.0, ZoneRHHumidifyingSetPoint));
    3585          318 :                             ZoneRHDehumidifyingSetPoint = min(100.0, max(0.0, ZoneRHDehumidifyingSetPoint));
    3586              :                         }
    3587              :                     }
    3588          622 :                     break;
    3589              :                 }
    3590              :             }
    3591              :         }
    3592              : 
    3593              :         // Run-time error check
    3594      1184325 :         if (ZoneRHHumidifyingSetPoint > ZoneRHDehumidifyingSetPoint) {
    3595            0 :             if (humidityControlZone.ErrorIndex == 0) {
    3596            0 :                 ShowWarningMessage(
    3597            0 :                     state, format("HUMIDISTAT: The humidifying setpoint is above the dehumidifying setpoint in {}", humidityControlZone.ControlName));
    3598            0 :                 ShowContinueError(state, "The zone humidifying setpoint is set to the dehumidifying setpoint.");
    3599            0 :                 ShowContinueErrorTimeStamp(state, "Occurrence info:");
    3600              :             }
    3601            0 :             ShowRecurringWarningErrorAtEnd(state,
    3602              :                                            "The humidifying setpoint is still above the dehumidifying setpoint",
    3603            0 :                                            humidityControlZone.ErrorIndex,
    3604              :                                            ZoneRHHumidifyingSetPoint,
    3605              :                                            ZoneRHHumidifyingSetPoint);
    3606            0 :             ZoneRHHumidifyingSetPoint = ZoneRHDehumidifyingSetPoint;
    3607              :         }
    3608      1184325 :         if (ZoneRHHumidifyingSetPoint == ZoneRHDehumidifyingSetPoint) {
    3609       605618 :             SingleSetPoint = true;
    3610              :         }
    3611      1184325 :         ControlledHumidZoneFlag = true;
    3612              : 
    3613              :     } // HumidControlledZoneNum
    3614              : 
    3615              :     // if zone latent sizing is requested but no humidistat exists
    3616     31432634 :     if (state.dataGlobal->DoingSizing && !ControlledHumidZoneFlag && state.dataHeatBal->DoLatentSizing) {
    3617        43500 :         for (size_t zoneEqConfigNum = 1; zoneEqConfigNum <= state.dataZoneEquip->ZoneEquipConfig.size(); ++zoneEqConfigNum) {
    3618        43500 :             auto &zoneEqConfig = state.dataZoneEquip->ZoneEquipConfig(zoneEqConfigNum);
    3619        43500 :             if (!zoneEqConfig.IsControlled) {
    3620        19686 :                 continue;
    3621              :             }
    3622        23814 :             int ZoneSizNum = Util::FindItemInList(zoneEqConfig.ZoneName, state.dataSize->ZoneSizingInput, &DataSizing::ZoneSizingInputData::ZoneName);
    3623              :             // should use the first Sizing:Zone object if not found
    3624        23814 :             if (ZoneSizNum == 0 && !state.dataSize->ZoneSizingInput.empty()) {
    3625            0 :                 ZoneSizNum = 1;
    3626              :             }
    3627        23814 :             if (ZoneSizNum > 0) {
    3628        23814 :                 auto &zoneSizingInput = state.dataSize->ZoneSizingInput(ZoneSizNum);
    3629        23814 :                 if (zoneSizingInput.zoneLatentSizing) {
    3630        47628 :                     ZoneRHDehumidifyingSetPoint = (zoneSizingInput.zoneRHDehumidifySched != nullptr)
    3631        23814 :                                                       ? zoneSizingInput.zoneRHDehumidifySched->getCurrentVal()
    3632              :                                                       : zoneSizingInput.zoneRHDehumidifySetPoint;
    3633        47628 :                     ZoneRHHumidifyingSetPoint = (zoneSizingInput.zoneRHHumidifySched != nullptr)
    3634        23814 :                                                     ? zoneSizingInput.zoneRHHumidifySched->getCurrentVal()
    3635              :                                                     : zoneSizingInput.zoneRHHumidifySetPoint;
    3636        23814 :                     if (ZoneRHHumidifyingSetPoint > ZoneRHDehumidifyingSetPoint) {
    3637            0 :                         ZoneRHHumidifyingSetPoint = ZoneRHDehumidifyingSetPoint;
    3638              :                     }
    3639        23814 :                     if (ZoneRHHumidifyingSetPoint == ZoneRHDehumidifyingSetPoint) {
    3640        19686 :                         SingleSetPoint = true;
    3641              :                     }
    3642        23814 :                     ControlledHumidZoneFlag = true;
    3643              :                 }
    3644              :             }
    3645        23814 :             break;
    3646              :         }
    3647              :     }
    3648              : 
    3649     31432634 :     Real64 LoadToHumidifySetPoint = 0.0;   // Moisture load at humidifying set point
    3650     31432634 :     Real64 LoadToDehumidifySetPoint = 0.0; // Moisture load at dehumidifying set point
    3651     31432634 :     Real64 totalOutputRequired = 0.0;
    3652     31432634 :     if (ControlledHumidZoneFlag) {
    3653              : 
    3654              :         // Calculate hourly humidity ratio from infiltration + humidity added from latent load
    3655              :         // to determine system added/subtracted moisture.
    3656      1208139 :         Real64 LatentGain = this->latentGain + state.dataHeatBalFanSys->SumLatentHTRadSys(zoneNum) + state.dataHeatBalFanSys->SumLatentPool(zoneNum);
    3657              : 
    3658      1208139 :         Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    3659              : 
    3660              :         // Calculate the coefficients for the 3rd Order derivative for final
    3661              :         // zone humidity ratio.  The A, B, C coefficients are analogous to the heat balance.
    3662              :         // SumHmARaW and SumHmARa will be used with the Moisture Balance on the building elements and
    3663              :         // are currently set to zero when the CTF only version is used.
    3664              : 
    3665              :         // The density of air and latent heat of vaporization are calculated as functions.
    3666      1208139 :         Real64 RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->ZT, this->airHumRat, RoutineName);
    3667      1208139 :         Real64 H2OHtOfVap = Psychrometrics::PsyHgAirFnWTdb(this->airHumRat, this->ZT);
    3668              : 
    3669              :         // Assume that the system will have flow
    3670      1208139 :         Real64 A = 0.0;
    3671      1208139 :         Real64 B = 0.0;
    3672      1208139 :         Real64 C = 0.0;
    3673      2375201 :         if (state.afn->multizone_always_simulated ||
    3674      1167062 :             (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    3675            0 :              state.afn->AirflowNetworkFanActivated)) {
    3676              :             // Multizone airflow calculated in AirflowNetwork
    3677        41077 :             B = (LatentGain / H2OHtOfVap) + state.afn->exchangeData(zoneNum).SumMHrW + state.afn->exchangeData(zoneNum).SumMMHrW + this->SumHmARaW;
    3678        41077 :             A = state.afn->exchangeData(zoneNum).SumMHr + state.afn->exchangeData(zoneNum).SumMMHr + this->SumHmARa;
    3679              :         } else {
    3680      1167062 :             B = (LatentGain / H2OHtOfVap) + ((this->OAMFL + this->VAMFL + this->CTMFL) * state.dataEnvrn->OutHumRat) + this->EAMFLxHumRat +
    3681      1167062 :                 this->SumHmARaW + this->MixingMassFlowXHumRat + this->MDotOA * state.dataEnvrn->OutHumRat;
    3682      1167062 :             A = this->OAMFL + this->VAMFL + this->EAMFL + this->CTMFL + this->SumHmARa + this->MixingMassFlowZone + this->MDotOA;
    3683              :         }
    3684      1208139 :         Real64 volume = 0.0;
    3685      1208139 :         if (spaceNum > 0) {
    3686            0 :             volume = state.dataHeatBal->space(spaceNum).Volume;
    3687              :         } else {
    3688      1208139 :             volume = thisZone.Volume;
    3689              :         }
    3690      1208139 :         C = RhoAir * volume * thisZone.ZoneVolCapMultpMoist / TimeStepSysSec;
    3691              : 
    3692      1208139 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    3693            0 :             auto &roomAFNInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
    3694            0 :             int RoomAirNode = roomAFNInfo.ControlAirNodeID;
    3695            0 :             H2OHtOfVap = Psychrometrics::PsyHgAirFnWTdb(roomAFNInfo.Node(RoomAirNode).HumRat, roomAFNInfo.Node(RoomAirNode).AirTemp);
    3696            0 :             A = roomAFNInfo.Node(RoomAirNode).SumLinkM + roomAFNInfo.Node(RoomAirNode).SumHmARa;
    3697            0 :             B = (roomAFNInfo.Node(RoomAirNode).SumIntLatentGain / H2OHtOfVap) + roomAFNInfo.Node(RoomAirNode).SumLinkMW +
    3698            0 :                 roomAFNInfo.Node(RoomAirNode).SumHmARaW;
    3699            0 :             C = roomAFNInfo.Node(RoomAirNode).RhoAir * roomAFNInfo.Node(RoomAirNode).AirVolume * thisZone.ZoneVolCapMultpMoist / TimeStepSysSec;
    3700              :         }
    3701              : 
    3702              :         // Use a 3rd Order derivative to predict zone moisture addition or removal and
    3703              :         // smooth the changes using the zone air capacitance.  Positive values of Moist Load means that
    3704              :         // this amount of moisture must be added to the zone to reach the setpoint.  Negative values represent
    3705              :         // the amount of moisture that must be removed by the system.
    3706              :         // MoistLoadHumidSetPoint = massflow * HumRat = kgDryAir/s * kgWater/kgDryAir = kgWater/s
    3707              :         Real64 WZoneSetPoint =
    3708      1208139 :             Psychrometrics::PsyWFnTdbRhPb(state, this->ZT, (ZoneRHHumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress, RoutineName);
    3709      1208139 :         Real64 exp_700_A_C(0.0);
    3710      1208139 :         if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::ThirdOrder) {
    3711       902292 :             LoadToHumidifySetPoint =
    3712      1804584 :                 ((11.0 / 6.0) * C + A) * WZoneSetPoint -
    3713       902292 :                 (B + C * (3.0 * this->WPrevZoneTSTemp[0] - (3.0 / 2.0) * this->WPrevZoneTSTemp[1] + (1.0 / 3.0) * this->WPrevZoneTSTemp[2]));
    3714              :             // Exact solution
    3715       305847 :         } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
    3716       305847 :             if (A == 0.0) { // B=0
    3717        49711 :                 LoadToHumidifySetPoint = C * (WZoneSetPoint - this->W1) - B;
    3718              :             } else {
    3719       256136 :                 exp_700_A_C = std::exp(min(700.0, -A / C)); // Tuned Save expensive value
    3720       256136 :                 LoadToHumidifySetPoint = A * (WZoneSetPoint - this->W1 * exp_700_A_C) / (1.0 - exp_700_A_C) - B;
    3721              :             }
    3722            0 :         } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::EulerMethod) {
    3723            0 :             LoadToHumidifySetPoint = C * (WZoneSetPoint - this->W1) + A * WZoneSetPoint - B;
    3724              :         }
    3725      1208139 :         if (RAFNFrac > 0.0) {
    3726            0 :             LoadToHumidifySetPoint = LoadToHumidifySetPoint / RAFNFrac;
    3727              :         }
    3728              :         WZoneSetPoint =
    3729      1208139 :             Psychrometrics::PsyWFnTdbRhPb(state, this->ZT, (ZoneRHDehumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress, RoutineName);
    3730      1208139 :         if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::ThirdOrder) {
    3731       902292 :             LoadToDehumidifySetPoint =
    3732      1804584 :                 ((11.0 / 6.0) * C + A) * WZoneSetPoint -
    3733       902292 :                 (B + C * (3.0 * this->WPrevZoneTSTemp[0] - (3.0 / 2.0) * this->WPrevZoneTSTemp[1] + (1.0 / 3.0) * this->WPrevZoneTSTemp[2]));
    3734              :             // Exact solution
    3735       305847 :         } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
    3736       305847 :             if (A == 0.0) { // B=0
    3737        49711 :                 LoadToDehumidifySetPoint = C * (WZoneSetPoint - this->W1) - B;
    3738              :             } else {
    3739       256136 :                 LoadToDehumidifySetPoint = A * (WZoneSetPoint - this->W1 * exp_700_A_C) / (1.0 - exp_700_A_C) - B; // exp_700_A_C set above
    3740              :             }
    3741            0 :         } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::EulerMethod) {
    3742            0 :             LoadToDehumidifySetPoint = C * (WZoneSetPoint - this->W1) + A * WZoneSetPoint - B;
    3743              :         }
    3744      1208139 :         if (RAFNFrac > 0.0) {
    3745            0 :             LoadToDehumidifySetPoint = LoadToDehumidifySetPoint / RAFNFrac;
    3746              :         }
    3747              : 
    3748              :         // The load is added to the TotalOutputRequired as in the Temperature Predictor.  There is also the remaining
    3749              :         // output variable for those who will use this for humidity control and stored in DataZoneEnergyDemands with the
    3750              :         // analogous temperature terms.
    3751              : 
    3752      1208139 :         if (SingleSetPoint) {
    3753       625304 :             totalOutputRequired = LoadToHumidifySetPoint;
    3754              :         } else {
    3755       582835 :             if (LoadToHumidifySetPoint > 0.0 && LoadToDehumidifySetPoint > 0.0) {
    3756       142560 :                 totalOutputRequired = LoadToHumidifySetPoint;
    3757       440275 :             } else if (LoadToHumidifySetPoint < 0.0 && LoadToDehumidifySetPoint < 0.0) {
    3758       131052 :                 totalOutputRequired = LoadToDehumidifySetPoint;
    3759       309223 :             } else if (LoadToHumidifySetPoint <= 0.0 && LoadToDehumidifySetPoint >= 0.0) { // deadband includes zero loads
    3760       309223 :                 totalOutputRequired = 0.0;
    3761              :             } else { // this should never occur!
    3762            0 :                 ShowSevereError(
    3763              :                     state, "Humidistat: Unanticipated combination of humidifying and dehumidifying loads - report to EnergyPlus Development Team");
    3764            0 :                 ShowContinueErrorTimeStamp(state, format("occurs in Zone = {}", thisZone.Name));
    3765            0 :                 ShowContinueError(
    3766              :                     state,
    3767            0 :                     format("LoadToHumidifySetPoint={:.5R}, LoadToDehumidifySetPoint={:.5R}", LoadToHumidifySetPoint, LoadToDehumidifySetPoint));
    3768            0 :                 ShowContinueError(state, format("Zone RH Humidifying Set-point={:.1R}", ZoneRHHumidifyingSetPoint));
    3769            0 :                 ShowContinueError(state, format("Zone RH Dehumidifying Set-point={:.2R}", ZoneRHDehumidifyingSetPoint));
    3770            0 :                 ShowFatalError(state, "Program terminates due to above conditions.");
    3771              :             }
    3772              :         }
    3773              :     }
    3774              : 
    3775              :     // Apply zone multipliers as needed or set to zero
    3776     31432634 :     if (spaceNum > 0) {
    3777        86394 :         auto &thisspaceSysMoistureDemand = state.dataZoneEnergyDemand->spaceSysMoistureDemand(spaceNum);
    3778        86394 :         if (ControlledHumidZoneFlag) {
    3779            0 :             thisspaceSysMoistureDemand.reportMoistLoadsZoneMultiplier(
    3780              :                 state, zoneNum, totalOutputRequired, LoadToHumidifySetPoint, LoadToDehumidifySetPoint);
    3781              :         } else {
    3782        86394 :             thisspaceSysMoistureDemand.TotalOutputRequired = 0.0;
    3783        86394 :             thisspaceSysMoistureDemand.OutputRequiredToDehumidifyingSP = 0.0;
    3784        86394 :             thisspaceSysMoistureDemand.OutputRequiredToHumidifyingSP = 0.0;
    3785              :         }
    3786              :     } else {
    3787     31346240 :         auto &thisZoneSysMoistureDemand = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(zoneNum);
    3788     31346240 :         if (ControlledHumidZoneFlag) {
    3789      1208139 :             thisZoneSysMoistureDemand.reportMoistLoadsZoneMultiplier(
    3790              :                 state, zoneNum, totalOutputRequired, LoadToHumidifySetPoint, LoadToDehumidifySetPoint);
    3791              :         } else {
    3792     30138101 :             thisZoneSysMoistureDemand.TotalOutputRequired = 0.0;
    3793     30138101 :             thisZoneSysMoistureDemand.OutputRequiredToDehumidifyingSP = 0.0;
    3794     30138101 :             thisZoneSysMoistureDemand.OutputRequiredToHumidifyingSP = 0.0;
    3795              :         }
    3796              :     }
    3797     31432634 : }
    3798              : 
    3799      3896580 : Real64 correctZoneAirTemps(EnergyPlusData &state,
    3800              :                            bool useZoneTimeStepHistory // if true then use zone timestep history, if false use system time step history
    3801              : )
    3802              : {
    3803      3896580 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    3804      3896580 :     Real64 maxTempChange = DataPrecisionGlobals::constant_zero; // Max absolute air temperature change between previous and current timestep
    3805     35241392 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    3806     31344812 :         auto &thisZoneHB = s_ztpc->zoneHeatBalance(zoneNum);
    3807     31344812 :         Real64 zoneTempChange = thisZoneHB.correctAirTemp(state, useZoneTimeStepHistory, zoneNum);
    3808     31344812 :         auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    3809     62753488 :         for (int spaceNum : thisZone.spaceIndexes) {
    3810     31408676 :             auto &thisSpaceHB = s_ztpc->spaceHeatBalance(spaceNum);
    3811     31495070 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation &&
    3812        86394 :                 !state.dataGlobal->DoingSizing) { // Need space air temps to match zone temps for sizing
    3813        29170 :                 Real64 spaceTempChange = thisSpaceHB.correctAirTemp(state, useZoneTimeStepHistory, zoneNum, spaceNum);
    3814        29170 :                 maxTempChange = max(maxTempChange, spaceTempChange);
    3815              :             } else {
    3816              :                 // If doing sizing and zone is controlled, then set space node to match zone node
    3817     31379506 :                 if (state.dataHeatBal->doSpaceHeatBalanceSizing && thisZone.IsControlled) {
    3818        50212 :                     auto const &thisZoneNode = state.dataLoopNodes->Node(thisZone.SystemZoneNodeNumber);
    3819        50212 :                     auto &thisSpaceNode = state.dataLoopNodes->Node(state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber);
    3820        50212 :                     thisSpaceNode.Temp = thisZoneNode.Temp;
    3821        50212 :                     thisSpaceNode.HumRat = thisZoneNode.HumRat;
    3822        50212 :                     thisSpaceNode.Enthalpy = thisZoneNode.Enthalpy;
    3823              :                 }
    3824              :                 // If no SpaceHB or doing sizing, then set space temps and humrat to match zone
    3825     31379506 :                 thisSpaceHB.ZT = thisZoneHB.ZT;
    3826     31379506 :                 thisSpaceHB.ZTM = thisZoneHB.ZTM;
    3827     31379506 :                 thisSpaceHB.MAT = thisZoneHB.MAT;
    3828     31379506 :                 thisSpaceHB.airHumRat = thisZoneHB.airHumRat;
    3829     31379506 :                 thisSpaceHB.airRelHum = thisZoneHB.airRelHum;
    3830              :                 // thisSpaceHB.ZTAVComf = thisZoneHB.ZTAVComf;
    3831              :             }
    3832     31344812 :         }
    3833     31344812 :         maxTempChange = max(maxTempChange, zoneTempChange);
    3834              : 
    3835     31344812 :         CalcZoneComponentLoadSums(state, zoneNum, &s_ztpc->zoneHeatBalance(zoneNum), state.dataHeatBal->ZnAirRpt(zoneNum));
    3836     31344812 :         if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    3837       150192 :             for (int spaceNum : thisZone.spaceIndexes) {
    3838        86394 :                 CalcZoneComponentLoadSums(state, zoneNum, &s_ztpc->spaceHeatBalance(spaceNum), state.dataHeatBal->spaceAirRpt(spaceNum));
    3839        63798 :             }
    3840              :         }
    3841              :     }
    3842      3896580 :     return maxTempChange;
    3843              : }
    3844              : 
    3845     31373982 : Real64 ZoneSpaceHeatBalanceData::correctAirTemp(
    3846              :     EnergyPlusData &state,
    3847              :     bool const useZoneTimeStepHistory, // if true then use zone timestep history, if false use system time step history
    3848              :     int const zoneNum,
    3849              :     int const spaceNum)
    3850              : {
    3851              : 
    3852              :     // SUBROUTINE INFORMATION:
    3853              :     //       AUTHOR         Russell Taylor
    3854              :     //       MODIFIED       November 1999, LKL; November 2016 Sang Hoon Lee, Tianzhen Hong, Rongpeng Zhang;
    3855              :     //       RE-ENGINEERED  July 2003 (Peter Graham Ellis)
    3856              :     //                      February 2008 (Brent Griffith reworked history )
    3857              : 
    3858              :     // PURPOSE OF THIS SUBROUTINE:
    3859              :     // This subroutine updates the zone air temperature and modifies the system
    3860              :     // time step.
    3861              : 
    3862              :     static constexpr std::string_view RoutineName("correctAirTemp");
    3863              : 
    3864     31373982 :     Real64 tempChange = DataPrecisionGlobals::constant_zero; // Zone or space air temperature change between previous and current timestep
    3865              : 
    3866     31373982 :     assert(zoneNum > 0);
    3867     31373982 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    3868              : 
    3869              :     // Update zone temperatures
    3870              : 
    3871     31373982 :     Real64 ZoneMult = thisZone.Multiplier * thisZone.ListMultiplier;
    3872              : 
    3873     31373982 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    3874              : 
    3875              :     // update the variables actually used in the balance equations.
    3876     31373982 :     if (!useZoneTimeStepHistory) {
    3877     10818069 :         this->ZTM = this->DSXMAT;
    3878     10818069 :         this->WPrevZoneTSTemp = this->DSWPrevZoneTS;
    3879              :     } else {
    3880     20555913 :         this->ZTM = this->XMAT;
    3881     20555913 :         this->WPrevZoneTSTemp = this->WPrevZoneTS;
    3882              :     }
    3883              : 
    3884     31373982 :     Real64 volume = 0.0;
    3885     31373982 :     if (spaceNum > 0) {
    3886        29170 :         volume = state.dataHeatBal->space(spaceNum).Volume;
    3887              :     } else {
    3888     31344812 :         volume = thisZone.Volume;
    3889              :     }
    3890     94121946 :     this->AirPowerCap = volume * thisZone.ZoneVolCapMultpSens *
    3891     31373982 :                         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->MAT, this->airHumRat, RoutineName) *
    3892     31373982 :                         Psychrometrics::PsyCpAirFnW(this->airHumRat) / TimeStepSysSec;
    3893              : 
    3894              :     // SpaceHB TODO: For now, room air model is only for zones
    3895     31373982 :     if (spaceNum == 0) {
    3896     31344812 :         RoomAir::ManageAirModel(state, zoneNum);
    3897              :     }
    3898              : 
    3899              :     // Calculate the various heat balance sums
    3900     31373982 :     this->calcZoneOrSpaceSums(state, true, zoneNum, spaceNum);
    3901              : 
    3902              :     // Sum all convective internal gains except for people: SumIntGainExceptPeople
    3903     31373982 :     if (state.dataHybridModel->FlagHybridModel_PC) {
    3904              :         // TODO: For now, don't do space heat balance with hybrid model
    3905         8212 :         this->SumIntGainExceptPeople = InternalHeatGains::SumAllInternalConvectionGainsExceptPeople(state, zoneNum);
    3906              :     }
    3907              : 
    3908              :     //    ZoneTempHistoryTerm = (3.0D0 * ZTM1(zoneNum) - (3.0D0/2.0D0) * ZTM2(zoneNum) + (1.0D0/3.0D0) * ZTM3(zoneNum))
    3909     31373982 :     int ZoneNodeNum = thisZone.SystemZoneNodeNumber;
    3910     31373982 :     if (spaceNum > 0) {
    3911        29170 :         ZoneNodeNum = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    3912              :     }
    3913              : 
    3914     31373982 :     Real64 SNLoad = 0.0;
    3915              : 
    3916     31373982 :     if (ZoneNodeNum > 0) { // This zone is controlled by a zone equipment configuration or zone plenum
    3917     28233887 :         auto &thisSystemNode = state.dataLoopNodes->Node(ZoneNodeNum);
    3918              : 
    3919              :         // Heat balance coefficients for controlled zone, i.e. with system air flow
    3920     28233887 :         this->TempDepCoef = this->SumHA + this->SumMCp + this->SumSysMCp;
    3921     28233887 :         this->TempIndCoef = this->SumIntGain + this->SumHATsurf - this->SumHATref + this->SumMCpT + this->SumSysMCpT +
    3922     28233887 :                             (this->NonAirSystemResponse / ZoneMult + this->SysDepZoneLoadsLagged);
    3923              : 
    3924     28233887 :         if (state.afn->distribution_simulated) {
    3925       233502 :             this->TempIndCoef += state.afn->exchangeData(zoneNum).TotalSen;
    3926              :         }
    3927              : 
    3928              :         // Solve for zone air temperature
    3929     28233887 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    3930     14825878 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    3931     14825878 :             this->ZT = (this->TempIndCoef + this->AirPowerCap * (3.0 * this->ZTM[0] - (3.0 / 2.0) * this->ZTM[1] + (1.0 / 3.0) * this->ZTM[2])) /
    3932     14825878 :                        ((11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef);
    3933     14825878 :         } break;
    3934     13376407 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    3935     13376407 :             if (this->TempDepCoef == 0.0) { // B=0
    3936            0 :                 this->ZT = this->T1 + this->TempIndCoef / this->AirPowerCap;
    3937              :             } else {
    3938     13376407 :                 this->ZT = (this->T1 - this->TempIndCoef / this->TempDepCoef) * std::exp(min(700.0, -this->TempDepCoef / this->AirPowerCap)) +
    3939     13376407 :                            this->TempIndCoef / this->TempDepCoef;
    3940              :             }
    3941     13376407 :         } break;
    3942        31602 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    3943        31602 :             this->ZT = (this->AirPowerCap * this->T1 + this->TempIndCoef) / (this->AirPowerCap + this->TempDepCoef);
    3944        31602 :         } break;
    3945            0 :         default:
    3946            0 :             break;
    3947              :         }
    3948              :         // Update zone node temperature and thermostat temperature unless already updated in Room Air Model,
    3949              :         // calculate load correction factor
    3950     28233887 :         if (!state.dataRoomAir->anyNonMixingRoomAirModel) {
    3951              :             // Fully mixed
    3952     28124920 :             thisSystemNode.Temp = this->ZT;
    3953              :             // SpaceHB TODO: What to do here if this is for space
    3954     28124920 :             if (spaceNum == 0) {
    3955     28095750 :                 state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    3956              :             }
    3957     28124920 :             state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    3958              :         } else {
    3959       108967 :             auto const &thisAirModel = state.dataRoomAir->AirModel(zoneNum);
    3960       108967 :             if ((thisAirModel.AirModel == RoomAir::RoomAirModel::Mixing) || (!thisAirModel.SimAirModel)) {
    3961              :                 // Fully mixed
    3962        41760 :                 thisSystemNode.Temp = this->ZT;
    3963              :                 // SpaceHB TODO: What to do here if this is for space
    3964        41760 :                 if (spaceNum == 0) {
    3965        41760 :                     state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    3966              :                 }
    3967        41760 :                 state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    3968        67207 :             } else if (state.dataRoomAir->IsZoneDispVent3Node(zoneNum) || state.dataRoomAir->IsZoneUFAD(zoneNum)) {
    3969              :                 // UCSDDV: Not fully mixed - calculate factor to correct load for fully mixed assumption
    3970              :                 // Space HB TODO: Space HB doesn't mix with DV etc.
    3971        13152 :                 if (this->SumSysMCp > HVAC::SmallMassFlow) {
    3972        12303 :                     Real64 TempSupplyAir = this->SumSysMCpT / this->SumSysMCp; // Non-negligible flow, calculate supply air temperature
    3973        12303 :                     if (std::abs(TempSupplyAir - this->ZT) > state.dataHeatBal->TempConvergTol) {
    3974        12035 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = (TempSupplyAir - thisSystemNode.Temp) / (TempSupplyAir - this->ZT);
    3975              :                         // constrain value to something reasonable
    3976        12035 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = max(-3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3977        12035 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = min(3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3978              : 
    3979              :                     } else {
    3980          268 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0; // Indeterminate
    3981              :                     }
    3982              :                 } else {
    3983              :                     // Negligible flow, assume mixed - reasonable lagged starting value for first step time with significant flow
    3984          849 :                     state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    3985              :                 }
    3986        54055 :             } else if (thisAirModel.SimAirModel && ((thisAirModel.AirModel == RoomAir::RoomAirModel::UserDefined) ||
    3987        16039 :                                                     (thisAirModel.AirModel == RoomAir::RoomAirModel::DispVent1Node))) {
    3988        46642 :                 if (this->SumSysMCp > HVAC::SmallMassFlow) {
    3989        46282 :                     Real64 TempSupplyAir = this->SumSysMCpT / this->SumSysMCp; // Non-negligible flow, calculate supply air temperature
    3990        46282 :                     if (std::abs(TempSupplyAir - this->ZT) > state.dataHeatBal->TempConvergTol) {
    3991        46278 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = (TempSupplyAir - thisSystemNode.Temp) / (TempSupplyAir - this->ZT);
    3992              :                         // constrain value
    3993        46278 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = max(-3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3994        46278 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = min(3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3995              : 
    3996              :                     } else {
    3997            4 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0; // Indeterminate
    3998              :                     }
    3999              :                 } else {
    4000              :                     // Negligible flow, assume mixed - reasonable lagged starting value for first step time with significant flow
    4001          360 :                     state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    4002              :                 }
    4003        54055 :             } else if (thisAirModel.AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4004              :                 // Zone node used in the RoomAirflowNetwork model
    4005         7413 :                 this->ZT = state.dataRoomAir->AFNZoneInfo(zoneNum).Node(state.dataRoomAir->AFNZoneInfo(zoneNum).ControlAirNodeID).AirTemp;
    4006         7413 :                 thisSystemNode.Temp = this->ZT;
    4007              :                 // SpaceHB TODO: What to do here if this is for space
    4008         7413 :                 if (spaceNum == 0) {
    4009         7413 :                     state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    4010              :                 }
    4011         7413 :                 state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    4012              :             } else {
    4013            0 :                 thisSystemNode.Temp = this->ZT;
    4014              :                 // SpaceHB TODO: What to do here if this is for space
    4015            0 :                 if (spaceNum == 0) {
    4016            0 :                     state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    4017              :                 }
    4018            0 :                 state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    4019              :             }
    4020              :         }
    4021              : 
    4022              :         // Sensible load is the enthalpy into the zone minus the enthalpy that leaves the zone.
    4023     28233887 :         Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->airHumRat);
    4024     28233887 :         Real64 ZoneEnthalpyIn = this->SumSysMCpT;
    4025              : 
    4026              :         // SNLOAD is the single zone load, without Zone Multiplier or Zone List Multiplier
    4027     28233887 :         SNLoad = ZoneEnthalpyIn - (thisSystemNode.MassFlowRate / ZoneMult) * CpAir * thisSystemNode.Temp + this->NonAirSystemResponse / ZoneMult +
    4028     28233887 :                  this->SysDepZoneLoadsLagged;
    4029              : 
    4030              :     } else {
    4031              : 
    4032              :         // Heat balance coefficients for uncontrolled zone, i.e. without system air flow
    4033      3140095 :         this->TempDepCoef = this->SumHA + this->SumMCp;
    4034      3140095 :         this->TempIndCoef = this->SumIntGain + this->SumHATsurf - this->SumHATref + this->SumMCpT;
    4035              : 
    4036      3140095 :         if (state.afn->distribution_simulated) {
    4037       146998 :             this->TempIndCoef += state.afn->exchangeData(zoneNum).TotalSen;
    4038              :         }
    4039              : 
    4040              :         // Solve for zone air temperature
    4041      3140095 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    4042      2206769 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    4043      2206769 :             this->ZT = (this->TempIndCoef + this->AirPowerCap * (3.0 * this->ZTM[0] - (3.0 / 2.0) * this->ZTM[1] + (1.0 / 3.0) * this->ZTM[2])) /
    4044      2206769 :                        ((11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef);
    4045              :             // Exact solution
    4046      2206769 :         } break;
    4047       926718 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    4048       926718 :             if (this->TempDepCoef == 0.0) { // B=0
    4049            0 :                 this->ZT = this->T1 + this->TempIndCoef / this->AirPowerCap;
    4050              :             } else {
    4051       926718 :                 this->ZT = (this->T1 - this->TempIndCoef / this->TempDepCoef) * std::exp(min(700.0, -this->TempDepCoef / this->AirPowerCap)) +
    4052       926718 :                            this->TempIndCoef / this->TempDepCoef;
    4053              :             }
    4054       926718 :         } break;
    4055         6608 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    4056         6608 :             this->ZT = (this->AirPowerCap * this->T1 + this->TempIndCoef) / (this->AirPowerCap + this->TempDepCoef);
    4057         6608 :         } break;
    4058            0 :         default:
    4059            0 :             break;
    4060              :         }
    4061              : 
    4062              :         // SpaceHB TODO: For now, room air model is only for zones
    4063      3140095 :         if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    4064        51988 :             if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4065            0 :                 this->ZT = state.dataRoomAir->AFNZoneInfo(zoneNum).Node(state.dataRoomAir->AFNZoneInfo(zoneNum).ControlAirNodeID).AirTemp;
    4066              :             }
    4067              :         }
    4068              : 
    4069              :         // No sensible load
    4070      3140095 :         SNLoad = 0.0;
    4071              :     }
    4072              : 
    4073              :     // Hybrid modeling start
    4074              :     // SpaceHB TODO: For now, hybrid model is only for zones
    4075     31373982 :     if (spaceNum == 0 && state.dataHybridModel->FlagHybridModel) {
    4076        30204 :         auto &hmZone = state.dataHybridModel->hybridModelZones(zoneNum);
    4077        32004 :         if ((hmZone.InfiltrationCalc_T || hmZone.InternalThermalMassCalc_T || hmZone.PeopleCountCalc_T) && (!state.dataGlobal->WarmupFlag) &&
    4078         1800 :             (!state.dataGlobal->DoingSizing)) {
    4079         1800 :             InverseModelTemperature(state,
    4080              :                                     zoneNum,
    4081              :                                     this->SumIntGain,
    4082              :                                     this->SumIntGainExceptPeople,
    4083              :                                     this->SumHA,
    4084              :                                     this->SumHATsurf,
    4085              :                                     this->SumHATref,
    4086              :                                     this->SumMCp,
    4087              :                                     this->SumMCpT,
    4088              :                                     this->SumSysMCp,
    4089              :                                     this->SumSysMCpT,
    4090              :                                     this->AirPowerCap);
    4091              :         }
    4092              :     }
    4093              : 
    4094     31373982 :     this->MAT = this->ZT;
    4095              : 
    4096              :     // Determine sensible load heating/cooling rate and energy
    4097     31373982 :     if (spaceNum > 0) {
    4098        29170 :         state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).reportZoneAirSystemSensibleLoads(state, SNLoad);
    4099              :     } else {
    4100     31344812 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).reportZoneAirSystemSensibleLoads(state, SNLoad);
    4101              :     }
    4102              : 
    4103              :     // Final humidity calcs
    4104     31373982 :     this->correctHumRat(state, zoneNum, spaceNum);
    4105              : 
    4106     31373982 :     this->airHumRat = this->airHumRatTemp;
    4107     31373982 :     this->airRelHum = 100.0 * Psychrometrics::PsyRhFnTdbWPb(state, this->ZT, this->airHumRat, state.dataEnvrn->OutBaroPress, RoutineName);
    4108              : 
    4109              :     // tempChange is used by HVACManager to determine if the timestep needs to be shortened.
    4110     31373982 :     bool isMixed = true;
    4111              :     // SpaceHB TODO: For now, room air model is only for zones
    4112     31373982 :     if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    4113       315590 :         isMixed = !((state.dataRoomAir->IsZoneDispVent3Node(zoneNum) && !state.dataRoomAir->ZoneDispVent3NodeMixedFlag(zoneNum)) ||
    4114       154635 :                     (state.dataRoomAir->IsZoneUFAD(zoneNum) && !state.dataRoomAir->ZoneUFADMixedFlag(zoneNum)));
    4115              :     }
    4116     31373982 :     switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    4117     17032647 :     case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    4118     17032647 :         if (isMixed) {
    4119     17019495 :             tempChange = max(tempChange, std::abs(this->ZT - this->ZTM[0]));
    4120              :         } else {
    4121        26304 :             tempChange = max(tempChange,
    4122        13152 :                              max(std::abs(state.dataRoomAir->ZTOC(zoneNum) - state.dataRoomAir->ZTMOC(zoneNum)[0]),
    4123        13152 :                                  std::abs(state.dataRoomAir->ZTMX(zoneNum) - state.dataRoomAir->ZTMMX(zoneNum)[0])));
    4124              :         }
    4125     17032647 :     } break;
    4126     14341335 :     case DataHeatBalance::SolutionAlgo::AnalyticalSolution:
    4127              :     case DataHeatBalance::SolutionAlgo::EulerMethod: {
    4128     14341335 :         if (isMixed) {
    4129     14339906 :             tempChange = max(tempChange, std::abs(this->ZT - this->T1));
    4130              :         } else {
    4131         1429 :             tempChange = max(tempChange,
    4132         1429 :                              max(std::abs(state.dataRoomAir->ZTOC(zoneNum) - state.dataRoomAir->Zone1OC(zoneNum)),
    4133         1429 :                                  std::abs(state.dataRoomAir->ZTMX(zoneNum) - state.dataRoomAir->Zone1MX(zoneNum))));
    4134              :         }
    4135     14341335 :     } break;
    4136            0 :     default:
    4137            0 :         break;
    4138              :     }
    4139              : 
    4140     31373982 :     return tempChange;
    4141              : }
    4142              : 
    4143      2828212 : void PushZoneTimestepHistories(EnergyPlusData &state)
    4144              : {
    4145              : 
    4146              :     // SUBROUTINE INFORMATION:
    4147              :     //       AUTHOR         Brent Griffith
    4148              :     //       DATE WRITTEN   February 2008
    4149              : 
    4150              :     // PURPOSE OF THIS SUBROUTINE:
    4151              :     // push histories for timestep advancing
    4152      2828212 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4153     23362447 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    4154     20534235 :         s_ztpc->zoneHeatBalance(zoneNum).pushZoneTimestepHistory(state, zoneNum);
    4155     20534235 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    4156       113364 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4157        64782 :                 s_ztpc->spaceHeatBalance(spaceNum).pushZoneTimestepHistory(state, zoneNum, spaceNum);
    4158        48582 :             }
    4159              :         }
    4160              :     }
    4161      2828212 : }
    4162              : 
    4163     20599017 : void ZoneSpaceHeatBalanceData::pushZoneTimestepHistory(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    4164              : {
    4165              : 
    4166     20599017 :     constexpr std::string_view routineName("pushTimestepHistories");
    4167     20599017 :     assert(zoneNum > 0);
    4168              : 
    4169     20599017 :     auto &thisAirModel = state.dataRoomAir->AirModel(zoneNum);
    4170              : 
    4171              :     // Push the temperature and humidity ratio histories
    4172              : 
    4173     82396068 :     for (int iHistory = 3; iHistory >= 1; --iHistory) {
    4174     61797051 :         this->XMAT[iHistory] = this->XMAT[iHistory - 1];
    4175     61797051 :         this->WPrevZoneTS[iHistory] = this->WPrevZoneTS[iHistory - 1];
    4176              :     }
    4177     20599017 :     this->XMAT[0] = this->ZTAV; // using average for whole zone time step.
    4178     20599017 :     this->XMPT = this->ZT;
    4179     20599017 :     this->WPrevZoneTS[0] = this->airHumRatAvg; // using average for whole zone time step.
    4180     20599017 :     this->airHumRat = this->airHumRatTemp;
    4181     20599017 :     this->WTimeMinusP = this->airHumRatTemp;
    4182     20599017 :     this->airRelHum = 100.0 * Psychrometrics::PsyRhFnTdbWPb(state, this->ZT, this->airHumRat, state.dataEnvrn->OutBaroPress, routineName);
    4183              : 
    4184              :     // SpaceHB TODO: For now, room air model is only for zones
    4185     20599017 :     if (spaceNum == 0) {
    4186     20534235 :         if (thisAirModel.AirModel == RoomAir::RoomAirModel::DispVent3Node || thisAirModel.AirModel == RoomAir::RoomAirModel::UFADInt ||
    4187     20518617 :             thisAirModel.AirModel == RoomAir::RoomAirModel::UFADExt) {
    4188        26442 :             state.dataRoomAir->XMATFloor(zoneNum)[3] = state.dataRoomAir->XMATFloor(zoneNum)[2];
    4189        26442 :             state.dataRoomAir->XMATFloor(zoneNum)[2] = state.dataRoomAir->XMATFloor(zoneNum)[1];
    4190        26442 :             state.dataRoomAir->XMATFloor(zoneNum)[1] = state.dataRoomAir->XMATFloor(zoneNum)[0];
    4191        26442 :             state.dataRoomAir->XMATFloor(zoneNum)[0] = state.dataRoomAir->ZTFloor(zoneNum);
    4192        26442 :             state.dataRoomAir->MATFloor(zoneNum) = state.dataRoomAir->ZTFloor(zoneNum);
    4193              : 
    4194        26442 :             state.dataRoomAir->XMATOC(zoneNum)[3] = state.dataRoomAir->XMATOC(zoneNum)[2];
    4195        26442 :             state.dataRoomAir->XMATOC(zoneNum)[2] = state.dataRoomAir->XMATOC(zoneNum)[1];
    4196        26442 :             state.dataRoomAir->XMATOC(zoneNum)[1] = state.dataRoomAir->XMATOC(zoneNum)[0];
    4197        26442 :             state.dataRoomAir->XMATOC(zoneNum)[0] = state.dataRoomAir->ZTOC(zoneNum);
    4198        26442 :             state.dataRoomAir->MATOC(zoneNum) = state.dataRoomAir->ZTOC(zoneNum);
    4199              : 
    4200        26442 :             state.dataRoomAir->XMATMX(zoneNum)[3] = state.dataRoomAir->XMATMX(zoneNum)[2];
    4201        26442 :             state.dataRoomAir->XMATMX(zoneNum)[2] = state.dataRoomAir->XMATMX(zoneNum)[1];
    4202        26442 :             state.dataRoomAir->XMATMX(zoneNum)[1] = state.dataRoomAir->XMATMX(zoneNum)[0];
    4203        26442 :             state.dataRoomAir->XMATMX(zoneNum)[0] = state.dataRoomAir->ZTMX(zoneNum);
    4204        26442 :             state.dataRoomAir->MATMX(zoneNum) = state.dataRoomAir->ZTMX(zoneNum);
    4205              :         }
    4206              : 
    4207              :         // for RoomAirflowNetwork model
    4208     20534235 :         if (thisAirModel.AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4209        40425 :             for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    4210        34650 :                 afnNode.AirTempX[3] = afnNode.AirTempX[2];
    4211        34650 :                 afnNode.AirTempX[2] = afnNode.AirTempX[1];
    4212        34650 :                 afnNode.AirTempX[1] = afnNode.AirTempX[0];
    4213        34650 :                 afnNode.AirTempX[0] = afnNode.AirTemp;
    4214              : 
    4215        34650 :                 afnNode.HumRatX[3] = afnNode.HumRatX[2];
    4216        34650 :                 afnNode.HumRatX[2] = afnNode.HumRatX[1];
    4217        34650 :                 afnNode.HumRatX[1] = afnNode.HumRatX[0];
    4218        34650 :                 afnNode.HumRatX[0] = afnNode.HumRat;
    4219              :             }
    4220              :         }
    4221              :     }
    4222              : 
    4223     20599017 :     if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
    4224      7787097 :         this->TM2 = this->TMX;
    4225      7787097 :         this->TMX = this->ZTAV; // using average for whole zone time step.
    4226      7787097 :         this->WM2 = this->WMX;
    4227      7787097 :         this->WMX = this->airHumRatAvg; // using average for whole zone time step.
    4228              :         // SpaceHB TODO: For now, room air model is only for zones
    4229      7787097 :         if (spaceNum == 0) {
    4230      7787097 :             if (thisAirModel.AirModel == RoomAir::RoomAirModel::DispVent3Node || thisAirModel.AirModel == RoomAir::RoomAirModel::UFADInt ||
    4231      7781319 :                 thisAirModel.AirModel == RoomAir::RoomAirModel::UFADExt) {
    4232         5778 :                 state.dataRoomAir->ZoneM2Floor(zoneNum) = state.dataRoomAir->ZoneMXFloor(zoneNum);
    4233         5778 :                 state.dataRoomAir->ZoneMXFloor(zoneNum) = state.dataRoomAir->ZTFloor(zoneNum); // using average for whole zone time step.
    4234         5778 :                 state.dataRoomAir->ZoneM2OC(zoneNum) = state.dataRoomAir->ZoneMXOC(zoneNum);
    4235         5778 :                 state.dataRoomAir->ZoneMXOC(zoneNum) = state.dataRoomAir->ZTOC(zoneNum); // using average for whole zone time step.
    4236         5778 :                 state.dataRoomAir->ZoneM2MX(zoneNum) = state.dataRoomAir->ZoneMXMX(zoneNum);
    4237         5778 :                 state.dataRoomAir->ZoneMXMX(zoneNum) = state.dataRoomAir->ZTMX(zoneNum); // using average for whole zone time step.
    4238              :             }
    4239              : 
    4240      7787097 :             if (thisAirModel.AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4241            0 :                 for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    4242            0 :                     afnNode.AirTempT2 = afnNode.AirTempTX;
    4243            0 :                     afnNode.AirTempTX = afnNode.AirTemp;
    4244              : 
    4245            0 :                     afnNode.HumRatT2 = afnNode.HumRatTX;
    4246            0 :                     afnNode.HumRatTX = afnNode.HumRat;
    4247              :                 }
    4248              :             }
    4249              :         }
    4250              :     }
    4251     20599017 : }
    4252              : 
    4253      1068368 : void PushSystemTimestepHistories(EnergyPlusData &state)
    4254              : {
    4255              : 
    4256              :     // SUBROUTINE INFORMATION:
    4257              :     //       AUTHOR         Brent Griffith
    4258              :     //       DATE WRITTEN   April 2008
    4259              : 
    4260              :     // PURPOSE OF THIS SUBROUTINE:
    4261              :     // Push the temperature and humidity ratio histories back in time
    4262      1068368 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4263     11878945 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    4264     10810577 :         s_ztpc->zoneHeatBalance(zoneNum).pushSystemTimestepHistory(state, zoneNum);
    4265     10810577 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    4266        36828 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4267        21612 :                 s_ztpc->spaceHeatBalance(spaceNum).pushSystemTimestepHistory(state, zoneNum, spaceNum);
    4268        15216 :             }
    4269              :         }
    4270              :     }
    4271      1068368 : }
    4272              : 
    4273     10832189 : void ZoneSpaceHeatBalanceData::pushSystemTimestepHistory(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    4274              : {
    4275     10832189 :     assert(zoneNum > 0);
    4276     43328756 :     for (int iHistory = 3; iHistory >= 1; --iHistory) {
    4277     32496567 :         this->DSXMAT[iHistory] = this->DSXMAT[iHistory - 1];
    4278     32496567 :         this->DSWPrevZoneTS[iHistory] = this->DSWPrevZoneTS[iHistory - 1];
    4279              :     }
    4280     10832189 :     this->DSXMAT[0] = this->MAT;
    4281     10832189 :     this->DSWPrevZoneTS[0] = this->airHumRat;
    4282              : 
    4283              :     // SpaceHB TODO: For now, room air model is only for zones
    4284     10832189 :     if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    4285        34733 :         if (state.dataRoomAir->IsZoneDispVent3Node(zoneNum) || state.dataRoomAir->IsZoneUFAD(zoneNum)) {
    4286         5868 :             state.dataRoomAir->DSXMATFloor(zoneNum)[3] = state.dataRoomAir->DSXMATFloor(zoneNum)[2];
    4287         5868 :             state.dataRoomAir->DSXMATFloor(zoneNum)[2] = state.dataRoomAir->DSXMATFloor(zoneNum)[1];
    4288         5868 :             state.dataRoomAir->DSXMATFloor(zoneNum)[1] = state.dataRoomAir->DSXMATFloor(zoneNum)[0];
    4289         5868 :             state.dataRoomAir->DSXMATFloor(zoneNum)[0] = state.dataRoomAir->MATFloor(zoneNum);
    4290              : 
    4291         5868 :             state.dataRoomAir->DSXMATOC(zoneNum)[3] = state.dataRoomAir->DSXMATOC(zoneNum)[2];
    4292         5868 :             state.dataRoomAir->DSXMATOC(zoneNum)[2] = state.dataRoomAir->DSXMATOC(zoneNum)[1];
    4293         5868 :             state.dataRoomAir->DSXMATOC(zoneNum)[1] = state.dataRoomAir->DSXMATOC(zoneNum)[0];
    4294         5868 :             state.dataRoomAir->DSXMATOC(zoneNum)[0] = state.dataRoomAir->MATOC(zoneNum);
    4295              : 
    4296         5868 :             state.dataRoomAir->DSXMATMX(zoneNum)[3] = state.dataRoomAir->DSXMATMX(zoneNum)[2];
    4297         5868 :             state.dataRoomAir->DSXMATMX(zoneNum)[2] = state.dataRoomAir->DSXMATMX(zoneNum)[1];
    4298         5868 :             state.dataRoomAir->DSXMATMX(zoneNum)[1] = state.dataRoomAir->DSXMATMX(zoneNum)[0];
    4299         5868 :             state.dataRoomAir->DSXMATMX(zoneNum)[0] = state.dataRoomAir->MATMX(zoneNum);
    4300              :         }
    4301        34733 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4302        11466 :             for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    4303         9828 :                 afnNode.AirTempDSX[3] = afnNode.AirTempDSX[2];
    4304         9828 :                 afnNode.AirTempDSX[2] = afnNode.AirTempDSX[1];
    4305         9828 :                 afnNode.AirTempDSX[1] = afnNode.AirTempDSX[0];
    4306         9828 :                 afnNode.AirTempDSX[0] = afnNode.AirTemp;
    4307              : 
    4308         9828 :                 afnNode.HumRatDSX[3] = afnNode.HumRatDSX[2];
    4309         9828 :                 afnNode.HumRatDSX[2] = afnNode.HumRatDSX[1];
    4310         9828 :                 afnNode.HumRatDSX[1] = afnNode.HumRatDSX[0];
    4311         9828 :                 afnNode.HumRatDSX[0] = afnNode.HumRat;
    4312              :             }
    4313              :         }
    4314              :     }
    4315              : 
    4316     10832189 :     if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
    4317      6554238 :         this->TM2 = this->TMX;
    4318      6554238 :         this->TMX = this->MAT; // using average for whole zone time step.
    4319      6554238 :         this->WM2 = this->WMX;
    4320      6554238 :         this->WMX = this->airHumRatTemp; // using average for whole zone time step.
    4321              : 
    4322              :         // SpaceHB TODO: For now, room air model is only for zones
    4323      6554238 :         if (spaceNum == 0) {
    4324      6554238 :             if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::DispVent3Node ||
    4325     13107646 :                 state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::UFADInt ||
    4326      6553408 :                 state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::UFADExt) {
    4327          830 :                 state.dataRoomAir->ZoneM2Floor(zoneNum) = state.dataRoomAir->ZoneMXFloor(zoneNum);
    4328          830 :                 state.dataRoomAir->ZoneMXFloor(zoneNum) = state.dataRoomAir->ZTFloor(zoneNum); // using average for whole zone time step.
    4329          830 :                 state.dataRoomAir->ZoneM2OC(zoneNum) = state.dataRoomAir->ZoneMXOC(zoneNum);
    4330          830 :                 state.dataRoomAir->ZoneMXOC(zoneNum) = state.dataRoomAir->ZTOC(zoneNum); // using average for whole zone time step.
    4331          830 :                 state.dataRoomAir->ZoneM2MX(zoneNum) = state.dataRoomAir->ZoneMXMX(zoneNum);
    4332          830 :                 state.dataRoomAir->ZoneMXMX(zoneNum) = state.dataRoomAir->ZTMX(zoneNum); // using average for whole zone time step.
    4333              :             }
    4334      6554238 :             if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4335            0 :                 for (int LoopNode = 1; LoopNode <= state.dataRoomAir->AFNZoneInfo(zoneNum).NumOfAirNodes; ++LoopNode) {
    4336            0 :                     auto &afnNode = state.dataRoomAir->AFNZoneInfo(zoneNum).Node(LoopNode);
    4337            0 :                     afnNode.AirTempT2 = afnNode.AirTempTX;
    4338            0 :                     afnNode.AirTempTX = afnNode.AirTemp;
    4339              : 
    4340            0 :                     afnNode.HumRatT2 = afnNode.HumRatTX;
    4341            0 :                     afnNode.HumRatTX = afnNode.HumRat;
    4342              :                 }
    4343              :             }
    4344              :         }
    4345              :     }
    4346     10832189 : }
    4347              : 
    4348            0 : void RevertZoneTimestepHistories(EnergyPlusData &state)
    4349              : {
    4350              :     // SUBROUTINE INFORMATION:
    4351              :     //       AUTHOR         Brent Griffith
    4352              :     //       DATE WRITTEN   February 2008
    4353              : 
    4354              :     // PURPOSE OF THIS SUBROUTINE:
    4355              :     // Revert the temperature and humidity ratio histories
    4356            0 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4357            0 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    4358            0 :         s_ztpc->zoneHeatBalance(zoneNum).revertZoneTimestepHistory(state, zoneNum);
    4359            0 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    4360            0 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4361            0 :                 s_ztpc->spaceHeatBalance(spaceNum).revertZoneTimestepHistory(state, zoneNum, spaceNum);
    4362            0 :             }
    4363              :         }
    4364              :     }
    4365            0 : }
    4366              : 
    4367            0 : void ZoneSpaceHeatBalanceData::revertZoneTimestepHistory(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    4368              : {
    4369            0 :     assert(zoneNum > 0);
    4370              : 
    4371            0 :     for (int iHistory = 0; iHistory <= 2; ++iHistory) {
    4372            0 :         this->XMAT[iHistory] = this->XMAT[iHistory + 1];
    4373            0 :         this->WPrevZoneTS[iHistory] = this->WPrevZoneTS[iHistory + 1];
    4374              :     }
    4375              : 
    4376              :     // SpaceHB TODO: For now, room air model is only for zones
    4377            0 :     if (spaceNum == 0) {
    4378            0 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::DispVent3Node ||
    4379            0 :             state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::UFADInt ||
    4380            0 :             state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::UFADExt) {
    4381              : 
    4382            0 :             state.dataRoomAir->XMATFloor(zoneNum)[0] = state.dataRoomAir->XMATFloor(zoneNum)[1];
    4383            0 :             state.dataRoomAir->XMATFloor(zoneNum)[1] = state.dataRoomAir->XMATFloor(zoneNum)[2];
    4384            0 :             state.dataRoomAir->XMATFloor(zoneNum)[2] = state.dataRoomAir->XMATFloor(zoneNum)[3];
    4385              : 
    4386            0 :             state.dataRoomAir->XMATOC(zoneNum)[0] = state.dataRoomAir->XMATOC(zoneNum)[1];
    4387            0 :             state.dataRoomAir->XMATOC(zoneNum)[1] = state.dataRoomAir->XMATOC(zoneNum)[2];
    4388            0 :             state.dataRoomAir->XMATOC(zoneNum)[2] = state.dataRoomAir->XMATOC(zoneNum)[3];
    4389              : 
    4390            0 :             state.dataRoomAir->XMATMX(zoneNum)[0] = state.dataRoomAir->XMATMX(zoneNum)[1];
    4391            0 :             state.dataRoomAir->XMATMX(zoneNum)[1] = state.dataRoomAir->XMATMX(zoneNum)[2];
    4392            0 :             state.dataRoomAir->XMATMX(zoneNum)[3] = state.dataRoomAir->XMATMX(zoneNum)[3];
    4393              :         }
    4394              : 
    4395            0 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4396            0 :             for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    4397            0 :                 afnNode.AirTempX[0] = afnNode.AirTempX[1];
    4398            0 :                 afnNode.AirTempX[1] = afnNode.AirTempX[2];
    4399            0 :                 afnNode.AirTempX[2] = afnNode.AirTempX[3];
    4400              : 
    4401            0 :                 afnNode.HumRatX[0] = afnNode.HumRatX[1];
    4402            0 :                 afnNode.HumRatX[1] = afnNode.HumRatX[2];
    4403            0 :                 afnNode.HumRatX[2] = afnNode.HumRatX[3];
    4404              :             }
    4405              :         }
    4406              :     }
    4407            0 : }
    4408              : 
    4409     31373982 : void ZoneSpaceHeatBalanceData::correctHumRat(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    4410              : {
    4411              : 
    4412              :     // SUBROUTINE INFORMATION:
    4413              :     //       AUTHOR         Richard Liesen
    4414              :     //       DATE WRITTEN   2000
    4415              :     // REFERENCES: Routine FinalZnCalcs - FINAL ZONE CALCULATIONS, authored by Dale Herron for BLAST.
    4416              : 
    4417     31373982 :     assert(zoneNum > 0);
    4418              :     static constexpr std::string_view RoutineName("correctHumRat");
    4419              : 
    4420     31373982 :     Real64 MoistureMassFlowRate = 0.0;
    4421     31373982 :     Real64 ZoneMassFlowRate = 0.0;
    4422     31373982 :     auto &zone = state.dataHeatBal->Zone(zoneNum);
    4423     31373982 :     int ZoneMult = zone.Multiplier * zone.ListMultiplier;
    4424     31373982 :     bool ControlledZoneAirFlag = zone.IsControlled;
    4425     31373982 :     bool ZoneRetPlenumAirFlag = zone.IsReturnPlenum;
    4426     31373982 :     bool ZoneSupPlenumAirFlag = zone.IsSupplyPlenum;
    4427              : 
    4428     31373982 :     if (ControlledZoneAirFlag) { // If there is system flow then calculate the flow rates
    4429     27124722 :         auto &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
    4430              :         // Calculate moisture flow rate into each zone
    4431     56010835 :         for (int NodeNum = 1; NodeNum <= zoneEquipConfig.NumInletNodes; ++NodeNum) {
    4432     28886113 :             auto const &inletNode = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(NodeNum));
    4433     28886113 :             MoistureMassFlowRate += (inletNode.MassFlowRate * inletNode.HumRat) / ZoneMult;
    4434     28886113 :             ZoneMassFlowRate += inletNode.MassFlowRate / ZoneMult;
    4435              :         }
    4436              : 
    4437              :         // Do the calculations for the plenum zone
    4438      4249260 :     } else if (ZoneRetPlenumAirFlag) {
    4439      1089639 :         int ZoneRetPlenumNum = zone.PlenumCondNum;
    4440      1089639 :         auto &zoneRetPlenCond = state.dataZonePlenum->ZoneRetPlenCond(ZoneRetPlenumNum);
    4441      6283299 :         for (int NodeNum = 1; NodeNum <= zoneRetPlenCond.NumInletNodes; ++NodeNum) {
    4442      5193660 :             auto const &inletNode = state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(NodeNum));
    4443      5193660 :             MoistureMassFlowRate += (inletNode.MassFlowRate * inletNode.HumRat) / ZoneMult;
    4444      5193660 :             ZoneMassFlowRate += inletNode.MassFlowRate / ZoneMult;
    4445              :         }
    4446              :         // add in the leak flow
    4447      6301939 :         for (int ADUListIndex = 1; ADUListIndex <= zoneRetPlenCond.NumADUs; ++ADUListIndex) {
    4448      5212300 :             int ADUNum = zoneRetPlenCond.ADUIndex(ADUListIndex);
    4449      5212300 :             auto const &airDistUnit = state.dataDefineEquipment->AirDistUnit(ADUNum);
    4450      5212300 :             if (airDistUnit.UpStreamLeak) {
    4451       106595 :                 int ADUInNode = airDistUnit.InletNodeNum;
    4452       106595 :                 MoistureMassFlowRate += (airDistUnit.MassFlowRateUpStrLk * state.dataLoopNodes->Node(ADUInNode).HumRat) / ZoneMult;
    4453       106595 :                 ZoneMassFlowRate += airDistUnit.MassFlowRateUpStrLk / ZoneMult;
    4454              :             }
    4455      5212300 :             if (airDistUnit.DownStreamLeak) {
    4456       106595 :                 int ADUOutNode = airDistUnit.OutletNodeNum;
    4457       106595 :                 MoistureMassFlowRate += (airDistUnit.MassFlowRateDnStrLk * state.dataLoopNodes->Node(ADUOutNode).HumRat) / ZoneMult;
    4458       106595 :                 ZoneMassFlowRate += airDistUnit.MassFlowRateDnStrLk / ZoneMult;
    4459              :             }
    4460              :         }
    4461              : 
    4462      3159621 :     } else if (ZoneSupPlenumAirFlag) {
    4463        19526 :         int ZoneSupPlenumNum = zone.PlenumCondNum;
    4464        19526 :         auto const &inletNode = state.dataLoopNodes->Node(state.dataZonePlenum->ZoneSupPlenCond(ZoneSupPlenumNum).InletNode);
    4465        19526 :         MoistureMassFlowRate += (inletNode.MassFlowRate * inletNode.HumRat) / ZoneMult;
    4466        19526 :         ZoneMassFlowRate += inletNode.MassFlowRate / ZoneMult;
    4467              :     }
    4468              : 
    4469              :     // Calculate hourly humidity ratio from infiltration + humidity added from latent load + system added moisture
    4470     31373982 :     Real64 LatentGain = this->latentGain + state.dataHeatBalFanSys->SumLatentHTRadSys(zoneNum) + state.dataHeatBalFanSys->SumLatentPool(zoneNum);
    4471              : 
    4472     31373982 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    4473              : 
    4474              :     // Calculate the coefficients for the 3rd order derivative for final
    4475              :     // zone humidity ratio.  The A, B, C coefficients are analogous to the
    4476              :     // heat balance.  There are 2 cases that should be considered, system
    4477              :     // operating and system shutdown.
    4478              : 
    4479     31373982 :     Real64 const RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->ZT, this->airHumRat, RoutineName);
    4480     31373982 :     Real64 const H2OHtOfVap = Psychrometrics::PsyHgAirFnWTdb(this->airHumRat, this->ZT);
    4481              : 
    4482     31373982 :     Real64 B = (LatentGain / H2OHtOfVap) + ((this->OAMFL + this->VAMFL + this->CTMFL) * state.dataEnvrn->OutHumRat) + this->EAMFLxHumRat +
    4483     31373982 :                (MoistureMassFlowRate) + this->SumHmARaW + this->MixingMassFlowXHumRat + this->MDotOA * state.dataEnvrn->OutHumRat;
    4484     31373982 :     Real64 A = ZoneMassFlowRate + this->OAMFL + this->VAMFL + this->EAMFL + this->CTMFL + this->SumHmARa + this->MixingMassFlowZone + this->MDotOA;
    4485              : 
    4486     62254337 :     if (state.afn->multizone_always_simulated ||
    4487     30880355 :         (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    4488        23258 :          state.afn->AirflowNetworkFanActivated)) {
    4489       511861 :         auto const &exchangeData = state.afn->exchangeData(zoneNum);
    4490              :         // Multizone airflow calculated in AirflowNetwork
    4491       511861 :         B = (LatentGain / H2OHtOfVap) + (exchangeData.SumMHrW + exchangeData.SumMMHrW) + (MoistureMassFlowRate) + this->SumHmARaW;
    4492       511861 :         A = ZoneMassFlowRate + exchangeData.SumMHr + exchangeData.SumMMHr + this->SumHmARa;
    4493              :     }
    4494     31373982 :     Real64 C = RhoAir * zone.Volume * zone.ZoneVolCapMultpMoist / TimeStepSysSec;
    4495              : 
    4496     31373982 :     if (state.afn->distribution_simulated) {
    4497       380500 :         B += state.afn->exchangeData(zoneNum).TotalLat;
    4498              :     }
    4499              : 
    4500              :     // Use a 3rd order derivative to predict final zone humidity ratio and
    4501              :     // smooth the changes using the zone air capacitance.
    4502              :     // auto &zoneAirHumRatTemp = this->ZoneAirHumRatTemp;
    4503              :     // auto &zoneW1 = s_ztpc->zoneHeatBalance(ZoneNum).ZoneW1;
    4504     31373982 :     switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    4505     17032647 :     case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    4506     17032647 :         this->airHumRatTemp =
    4507     17032647 :             (B + C * (3.0 * this->WPrevZoneTSTemp[0] - (3.0 / 2.0) * this->WPrevZoneTSTemp[1] + (1.0 / 3.0) * this->WPrevZoneTSTemp[2])) /
    4508     17032647 :             ((11.0 / 6.0) * C + A);
    4509              :         // Exact solution
    4510     17032647 :     } break;
    4511     14303125 :     case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    4512     14303125 :         if (A == 0.0) { // B=0
    4513       532784 :             this->airHumRatTemp = this->W1 + B / C;
    4514              :         } else {
    4515     13770341 :             this->airHumRatTemp = (this->W1 - B / A) * std::exp(min(700.0, -A / C)) + B / A;
    4516              :         }
    4517     14303125 :     } break;
    4518        38210 :     case DataHeatBalance::SolutionAlgo::EulerMethod: {
    4519        38210 :         this->airHumRatTemp = (C * this->W1 + B) / (C + A);
    4520        38210 :     } break;
    4521            0 :     default:
    4522            0 :         break;
    4523              :     }
    4524              : 
    4525              :     // Set the humidity ratio to zero if the zone has been dried out
    4526     31373982 :     if (this->airHumRatTemp < 0.0) {
    4527          577 :         this->airHumRatTemp = 0.0;
    4528              :     }
    4529              : 
    4530              :     // Check to make sure that is saturated there is condensation in the zone
    4531              :     // by resetting to saturation conditions.
    4532     31373982 :     Real64 const WZSat = Psychrometrics::PsyWFnTdbRhPb(state, this->ZT, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
    4533              : 
    4534     31373982 :     if (this->airHumRatTemp > WZSat) {
    4535       839709 :         this->airHumRatTemp = WZSat;
    4536              :     }
    4537              : 
    4538     31373982 :     if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4539         7413 :         this->airHumRatTemp = state.dataRoomAir->AFNZoneInfo(zoneNum).Node(state.dataRoomAir->AFNZoneInfo(zoneNum).ControlAirNodeID).HumRat;
    4540              :     }
    4541              : 
    4542              :     // HybridModel with measured humidity ratio begins
    4543              :     // SpaceHB TODO: For now, hybrid model is only for zones
    4544     31373982 :     if (spaceNum == 0 && state.dataHybridModel->FlagHybridModel) {
    4545        30204 :         auto &hmZone = state.dataHybridModel->hybridModelZones(zoneNum);
    4546        30204 :         if ((hmZone.InfiltrationCalc_H || hmZone.PeopleCountCalc_H) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing)) {
    4547          600 :             Real64 LatentGainExceptPeople = 0.0;
    4548          600 :             if (hmZone.PeopleCountCalc_H) {
    4549          298 :                 LatentGainExceptPeople = this->latentGainExceptPeople + state.dataHeatBalFanSys->SumLatentHTRadSys(zoneNum) +
    4550          298 :                                          state.dataHeatBalFanSys->SumLatentPool(zoneNum);
    4551              :             }
    4552              : 
    4553          600 :             InverseModelHumidity(state, zoneNum, LatentGain, LatentGainExceptPeople, ZoneMassFlowRate, MoistureMassFlowRate, H2OHtOfVap, RhoAir);
    4554              :         }
    4555              :     }
    4556              : 
    4557              :     // Now put the calculated info into the actual zone nodes; ONLY if there is zone air flow, i.e. controlled zone or plenum zone
    4558     31373982 :     int ZoneNodeNum = zone.SystemZoneNodeNumber;
    4559     31373982 :     if (spaceNum > 0) {
    4560        29170 :         ZoneNodeNum = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    4561              :     }
    4562     31373982 :     if (ZoneNodeNum > 0) {
    4563     28233887 :         state.dataLoopNodes->Node(ZoneNodeNum).HumRat = this->airHumRatTemp;
    4564     28233887 :         state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy = Psychrometrics::PsyHFnTdbW(this->ZT, this->airHumRatTemp);
    4565              :     }
    4566     31373982 :     if (state.dataHeatBal->DoLatentSizing) {
    4567        38334 :         Real64 sensibleLoad = 0.0;
    4568        38334 :         Real64 pSat = Psychrometrics::PsyPsatFnTemp(state, this->ZT, RoutineName);
    4569        38334 :         Real64 Tdp = Psychrometrics::PsyTdpFnWPb(state, this->airHumRatTemp, state.dataEnvrn->StdBaroPress);
    4570        38334 :         Real64 vaporPressureDiff = pSat - Psychrometrics::PsyPsatFnTemp(state, Tdp, RoutineName);
    4571        38334 :         if (spaceNum > 0) {
    4572            0 :             sensibleLoad = state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).airSysHeatRate +
    4573            0 :                            state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).airSysCoolRate;
    4574            0 :             state.dataZoneEnergyDemand->spaceSysMoistureDemand(spaceNum).reportZoneAirSystemMoistureLoads(
    4575              :                 state, LatentGain, sensibleLoad, vaporPressureDiff);
    4576              :         } else {
    4577        38334 :             sensibleLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).airSysHeatRate +
    4578        38334 :                            state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).airSysCoolRate;
    4579        38334 :             state.dataZoneEnergyDemand->ZoneSysMoistureDemand(zoneNum).reportZoneAirSystemMoistureLoads(
    4580              :                 state, LatentGain, sensibleLoad, vaporPressureDiff);
    4581              :         }
    4582              :     }
    4583     31373982 : }
    4584              : 
    4585        60400 : void DownInterpolate4HistoryValues(Real64 const OldTimeStep,
    4586              :                                    Real64 const NewTimeStep,
    4587              :                                    Real64 const oldVal0,
    4588              :                                    Real64 const oldVal1,
    4589              :                                    Real64 const oldVal2,
    4590              :                                    Real64 &newVal0,
    4591              :                                    Real64 &newVal1,
    4592              :                                    Real64 &newVal2,
    4593              :                                    Real64 &newVal3,
    4594              :                                    Real64 &newVal4)
    4595              : {
    4596              :     // SUBROUTINE INFORMATION:
    4597              :     //       AUTHOR         Brent Griffith
    4598              :     //       DATE WRITTEN   Feb 2008
    4599              : 
    4600              :     // PURPOSE OF THIS SUBROUTINE:
    4601              :     // provide a reusable routine for the various places that need to
    4602              :     // interpolate a new set of history values on a different time scale
    4603              :     // Once the systemtimestep has shortened, the new history terms need to be interpolated
    4604              : 
    4605              :     // METHODOLOGY EMPLOYED:
    4606              :     // This routine assumes that the direction is to a shorter timestep.
    4607              :     // The down step ratio, DSRatio = OldTimeStep/ NewTimeStep
    4608              :     //  is expected to be roughly integer-valued and near 2.0 or 3.0 or 4.0 or more.
    4609              : 
    4610              :     // old math variables
    4611              :     // Real64 const oldTime0 = 0.0;
    4612              :     // Real64 const oldTime1 = oldTime0 - OldTimeStep;
    4613              :     // Real64 const newTime0 = 0.0;
    4614              :     // Real64 const newTime1 = newTime0 - NewTimeStep;
    4615              :     // Real64 const newTime2 = newTime1 - NewTimeStep;
    4616              :     // Real64 const newTime3 = newTime2 - NewTimeStep;
    4617              :     // Real64 const newTime4 = newTime3 - NewTimeStep;
    4618              : 
    4619        60400 :     Real64 constexpr realTWO = 2.0;
    4620        60400 :     Real64 constexpr realTHREE = 3.0;
    4621              :     // first determine the ratio of system time step to zone time step
    4622        60400 :     Real64 const DSRatio = OldTimeStep / NewTimeStep; // should pretty much be an integer value 2, 3, 4, etc.
    4623              : 
    4624        60400 :     newVal0 = oldVal0;
    4625              : 
    4626        60400 :     if (std::abs(DSRatio - realTWO) < 0.01) { // DSRatio = 2
    4627              :         // when DSRatio = 2 the 1st point lies exactly between old points, and 2nd point is old 1st point
    4628              :         // first two points lie between oldVal0 and oldVal1
    4629              :         // old math example
    4630              :         // newVal1 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime1) / (OldTimeStep));
    4631              :         // newVal2 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime2) / (OldTimeStep));
    4632        11800 :         newVal1 = (oldVal0 + oldVal1) / realTWO;
    4633        11800 :         newVal2 = oldVal1;
    4634              :         // when DSRatio = 2 the 3rd point lies exactly between old points, and 4th point is old 2nd point
    4635              :         // last two points lie between oldVal1 and oldVal2
    4636              :         // newVal3 = oldVal1 + (oldVal2 - oldVal1) * ((oldTime1 - newTime3) / (OldTimeStep));
    4637              :         // newVal4 = oldVal1 + (oldVal2 - oldVal1) * ((oldTime1 - newTime4) / (OldTimeStep));
    4638        11800 :         newVal3 = (oldVal1 + oldVal2) / realTWO;
    4639        11800 :         newVal4 = oldVal2;
    4640        48600 :     } else if (std::abs(DSRatio - realTHREE) < 0.01) { // DSRatio = 3
    4641              :         // when DSRatio = 3 the 1st point lies 1/3 way between old points, and 2nd and 3rd points are 2/3 and 3/3 the way
    4642              :         // first three points lie between oldVal0 and oldVal1
    4643              :         // newVal1 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime1) / (OldTimeStep));
    4644              :         // newVal2 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime2) / (OldTimeStep));
    4645              :         // newVal3 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime3) / (OldTimeStep));
    4646         7751 :         Real64 delta10 = (oldVal1 - oldVal0) / realTHREE;
    4647         7751 :         newVal1 = oldVal0 + delta10;
    4648         7751 :         newVal2 = newVal1 + delta10;
    4649         7751 :         newVal3 = oldVal1;
    4650              :         // last point lies 1/3 way between oldVal1 and oldVal2
    4651              :         // newVal4 = oldVal1 + (oldVal2 - oldVal1) * ((oldTime1 - newTime4) / (OldTimeStep));
    4652         7751 :         newVal4 = oldVal1 + (oldVal2 - oldVal1) / realTHREE;
    4653              : 
    4654              :     } else { // DSRatio = 4 or more
    4655              :         // all new points lie between oldVal0 and oldVal1 (if DSRatio = 4, newVal4 = oldVal1)
    4656              :         // newVal1 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime1) / (OldTimeStep));
    4657              :         // newVal2 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime2) / (OldTimeStep));
    4658              :         // newVal3 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime3) / (OldTimeStep));
    4659              :         // newVal4 = oldVal0 + (oldVal1 - oldVal0) * ((oldTime0 - newTime4) / (OldTimeStep));
    4660        40849 :         Real64 delta10 = (oldVal1 - oldVal0) / DSRatio;
    4661        40849 :         newVal1 = oldVal0 + delta10;
    4662        40849 :         newVal2 = newVal1 + delta10;
    4663        40849 :         newVal3 = newVal2 + delta10;
    4664        40849 :         newVal4 = newVal3 + delta10;
    4665              :     }
    4666        60400 : }
    4667              : 
    4668      2880660 : Real64 DownInterpolate4HistoryValues(Real64 OldTimeStep, Real64 NewTimeStep, std::array<Real64, 4> const &oldVals, std::array<Real64, 4> &newVals)
    4669              : {
    4670      2880660 :     Real64 constexpr realTWO = 2.0;
    4671      2880660 :     Real64 constexpr realTHREE = 3.0;
    4672              :     // first determine the ratio of system time step to zone time step
    4673      2880660 :     Real64 const DSRatio = OldTimeStep / NewTimeStep; // should pretty much be an integer value 2, 3, 4, etc.
    4674              : 
    4675      2880660 :     newVals[0] = oldVals[0];
    4676              : 
    4677      2880660 :     if (std::abs(DSRatio - realTWO) < 0.01) { // DSRatio = 2
    4678              :         // first point lies exactly between (oldVals[0] and oldVals[1])
    4679       964012 :         newVals[1] = (oldVals[0] + oldVals[1]) / realTWO;
    4680              :         // 2nd point is oldVal[1] and last point lies exactly between (oldVals[1] and oldVals[2])
    4681       964012 :         newVals[2] = oldVals[1];
    4682       964012 :         newVals[3] = (oldVals[1] + oldVals[2]) / realTWO;
    4683              : 
    4684      1916648 :     } else if (std::abs(DSRatio - realTHREE) < 0.01) { // DSRatio = 3
    4685              :         // first two points lie between (oldVals[0] and oldVals[1])
    4686       488137 :         Real64 delta10 = (oldVals[1] - oldVals[0]) / realTHREE;
    4687       488137 :         newVals[1] = oldVals[0] + delta10;
    4688       488137 :         newVals[2] = newVals[1] + delta10;
    4689              :         // last point is oldVals[1]
    4690       488137 :         newVals[3] = oldVals[1];
    4691              : 
    4692              :     } else { // DSRatio = 4 or more
    4693              :         // all new points lie between (oldVals[0] and oldVals[1])
    4694      1428511 :         Real64 delta10 = (oldVals[1] - oldVals[0]) / DSRatio;
    4695      1428511 :         newVals[1] = oldVals[0] + delta10;
    4696      1428511 :         newVals[2] = newVals[1] + delta10;
    4697      1428511 :         newVals[3] = newVals[2] + delta10;
    4698              :     }
    4699      2880660 :     return oldVals[0];
    4700              : }
    4701         1800 : void InverseModelTemperature(EnergyPlusData &state,
    4702              :                              int const ZoneNum,                   // Zone number
    4703              :                              Real64 const SumIntGain,             // Zone sum of convective internal gains
    4704              :                              Real64 const SumIntGainExceptPeople, // Zone sum of convective internal gains except for people
    4705              :                              Real64 const SumHA,                  // Zone sum of Hc*Area
    4706              :                              Real64 const SumHATsurf,             // Zone sum of Hc*Area*Tsurf
    4707              :                              Real64 const SumHATref,              // Zone sum of Hc*Area*Tref, for ceiling diffuser convection correlation
    4708              :                              Real64 const SumMCp,                 // Zone sum of MassFlowRate*Cp
    4709              :                              Real64 const SumMCpT,                // Zone sum of MassFlowRate*Cp*T
    4710              :                              Real64 const SumSysMCp,              // Zone sum of air system MassFlowRate*Cp
    4711              :                              Real64 const SumSysMCpT,             // Zone sum of air system MassFlowRate*Cp*T
    4712              :                              Real64 const AirCap                  // Formerly CoefAirrat, coef in zone temp eqn with dim of "air power capacity"rd
    4713              : )
    4714              : {
    4715              :     // SUBROUTINE INFORMATION:
    4716              :     //       AUTHOR         Han Li
    4717              :     //       DATE WRITTEN   February 2019
    4718              : 
    4719              :     // PURPOSE OF THIS SUBROUTINE:
    4720              :     // This subroutine inversely solve infiltration airflow rate or people count with zone air temperatures measurements.
    4721              : 
    4722              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4723         1800 :     Real64 AirCapHM(0.0); // Air power capacity for hybrid modeling
    4724         1800 :     Real64 AA(0.0);
    4725         1800 :     Real64 BB(0.0);
    4726         1800 :     Real64 FractionConvection(0.0); // Default convection portion of the sensible heat from people
    4727              : 
    4728         1800 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4729         1800 :     auto &zone = state.dataHeatBal->Zone(ZoneNum);
    4730         1800 :     auto &hmZone = state.dataHybridModel->hybridModelZones(ZoneNum);
    4731         1800 :     auto &thisZoneHB = s_ztpc->zoneHeatBalance(ZoneNum);
    4732              : 
    4733         1800 :     int ZoneMult = zone.Multiplier * zone.ListMultiplier;
    4734         1800 :     zone.ZoneMeasuredTemperature = (hmZone.measuredTempSched != nullptr) ? hmZone.measuredTempSched->getCurrentVal() : 0.0;
    4735         1800 :     zone.ZoneVolCapMultpSensHM = 1.0; // Initialize to 1.0 in case hybrid not active
    4736              : 
    4737              :     // HM calculation only HM calculation period start
    4738         1800 :     if (state.dataEnvrn->DayOfYear >= hmZone.HybridStartDayOfYear && state.dataEnvrn->DayOfYear <= hmZone.HybridEndDayOfYear) {
    4739         1800 :         Real64 MultpHM(1.0);
    4740              : 
    4741         1800 :         thisZoneHB.ZT = zone.ZoneMeasuredTemperature; // Array1D<Real64> ZT -- Zone
    4742              :                                                       // Air Temperature Averaged over
    4743              :                                                       // the System Time Increment
    4744         1800 :         if (hmZone.InfiltrationCalc_T && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    4745              :             static constexpr std::string_view RoutineNameInfiltration("CalcAirFlowSimple:Infiltration");
    4746              : 
    4747          576 :             if (hmZone.IncludeSystemSupplyParameters) {
    4748            0 :                 zone.ZoneMeasuredSupplyAirTemperature = hmZone.supplyAirTempSched->getCurrentVal();
    4749            0 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched ? hmZone.supplyAirMassFlowRateSched->getCurrentVal() : 0.0;
    4750            0 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched ? hmZone.supplyAirHumRatSched->getCurrentVal() : 0.0;
    4751              :                 // Calculate the air humidity ratio at supply air inlet.
    4752            0 :                 Real64 CpAirInlet(0.0);
    4753            0 :                 CpAirInlet = Psychrometrics::PsyCpAirFnW(zone.ZoneMeasuredSupplyAirHumidityRatio);
    4754              : 
    4755            0 :                 Real64 SumSysMCp_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet;
    4756            0 :                 Real64 SumSysMCpT_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet * zone.ZoneMeasuredSupplyAirTemperature;
    4757              : 
    4758            0 :                 AA = SumSysMCp_HM + SumHA + thisZoneHB.MCPV + thisZoneHB.MCPM + thisZoneHB.MCPE + thisZoneHB.MCPC + thisZoneHB.MDotCPOA;
    4759            0 :                 BB = SumSysMCpT_HM + SumIntGain + SumHATsurf - SumHATref + thisZoneHB.MCPTV + thisZoneHB.MCPTM + thisZoneHB.MCPTE + thisZoneHB.MCPTC +
    4760            0 :                      thisZoneHB.MDotCPOA * zone.OutDryBulbTemp + (thisZoneHB.NonAirSystemResponse / ZoneMult + thisZoneHB.SysDepZoneLoadsLagged);
    4761              :             } else {
    4762          576 :                 AA = SumHA + thisZoneHB.MCPV + thisZoneHB.MCPM + thisZoneHB.MCPE + thisZoneHB.MCPC + thisZoneHB.MDotCPOA;
    4763          576 :                 BB = SumIntGain + SumHATsurf - SumHATref + thisZoneHB.MCPTV + thisZoneHB.MCPTM + thisZoneHB.MCPTE + thisZoneHB.MCPTC +
    4764          576 :                      thisZoneHB.MDotCPOA * zone.OutDryBulbTemp;
    4765              :             }
    4766          576 :             Real64 CC = AirCap;
    4767              :             Real64 DD =
    4768          576 :                 (3.0 * state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) - (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum) +
    4769          576 :                  (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredZT3(ZoneNum));
    4770              : 
    4771          576 :             Real64 delta_T = (zone.ZoneMeasuredTemperature - zone.OutDryBulbTemp);
    4772          576 :             Real64 CpAir = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
    4773          576 :             Real64 AirDensity = Psychrometrics::PsyRhoAirFnPbTdbW(
    4774          576 :                 state, state.dataEnvrn->OutBaroPress, zone.OutDryBulbTemp, state.dataEnvrn->OutHumRat, RoutineNameInfiltration);
    4775          576 :             zone.delta_T = delta_T;
    4776              : 
    4777              :             // s4 - Set ACH to 0 when delta_T <= 0.5, add max and min limits to ach
    4778          576 :             Real64 M_inf = 0.0;
    4779          576 :             if (std::abs(delta_T) > 0.5) {
    4780          558 :                 M_inf = (BB + CC * DD - ((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredTemperature) / (CpAir * delta_T);
    4781              :             }
    4782          576 :             Real64 ACH_inf = max(0.0, min(10.0, (M_inf / AirDensity) / zone.Volume * Constant::rSecsInHour));
    4783          576 :             M_inf = (ACH_inf / Constant::rSecsInHour) * zone.Volume * AirDensity;
    4784              : 
    4785              :             // Overwrite variable with inverse solution
    4786          576 :             zone.MCPIHM = M_inf;
    4787          576 :             zone.InfilOAAirChangeRateHM = ACH_inf;
    4788              : 
    4789              :         } // Hybrid model infiltration calculation end
    4790              : 
    4791              :         // Hybrid modeling internal thermal mass calculation start
    4792         2664 :         if (hmZone.InternalThermalMassCalc_T && SumSysMCpT == 0 && thisZoneHB.ZT != state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) &&
    4793          864 :             state.dataHVACGlobal->UseZoneTimeStepHistory) { // HM calculation only when SumSysMCpT =0,
    4794              :                                                             // TimeStepZone (not @ TimeStepSys)
    4795          864 :             Real64 TempDepCoef = SumHA + SumMCp + SumSysMCp;
    4796          864 :             Real64 TempIndCoef = SumIntGain + SumHATsurf - SumHATref + SumMCpT + SumSysMCpT +
    4797          864 :                                  (thisZoneHB.NonAirSystemResponse / ZoneMult + thisZoneHB.SysDepZoneLoadsLagged);
    4798              :             //    TempHistoryTerm = AirCap * (3.0 * ZTM1(ZoneNum) - (3.0/2.0) * ZTM2(ZoneNum) + (1.0/3.0) * ZTM3(ZoneNum)) !debug only
    4799              : 
    4800          864 :             if (state.afn->distribution_simulated) {
    4801            0 :                 TempIndCoef += state.afn->exchangeData(ZoneNum).TotalSen;
    4802              :             }
    4803              :             // Calculate air capacity using DataHeatBalance::SolutionAlgo::AnalyticalSolution
    4804          864 :             if (TempDepCoef == 0.0) {
    4805              :                 // Is this correct? Shouldn't we use log?? What if thisZT ==
    4806              :                 // PreviousMeasuredZT1(ZoneNum)??
    4807            0 :                 AirCapHM = TempIndCoef / (thisZoneHB.ZT - state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum)); // Inverse equation
    4808              :             } else {
    4809          864 :                 Real64 AirCapHM_temp = 0.0;
    4810          864 :                 if (TempIndCoef == TempDepCoef * thisZoneHB.ZT) {
    4811            0 :                     AirCapHM_temp = 0.0; //  This is the denominator.
    4812              :                 } else {
    4813          864 :                     AirCapHM_temp = (TempIndCoef - TempDepCoef * state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum)) /
    4814          864 :                                     (TempIndCoef - TempDepCoef * thisZoneHB.ZT);
    4815              :                 }
    4816              : 
    4817          864 :                 if ((AirCapHM_temp > 0) && (AirCapHM_temp != 1)) {    // Avoid IND
    4818          857 :                     AirCapHM = TempDepCoef / std::log(AirCapHM_temp); // Inverse equation
    4819              :                 } else {
    4820            7 :                     AirCapHM = TempIndCoef / (thisZoneHB.ZT - state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum));
    4821              :                 }
    4822              :             }
    4823              : 
    4824              :             // Calculate multiplier
    4825          864 :             if (std::abs(thisZoneHB.ZT - state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum)) > 0.05) { // Filter
    4826          288 :                 MultpHM = AirCapHM /
    4827          288 :                           (zone.Volume *
    4828          288 :                            Psychrometrics::PsyRhoAirFnPbTdbW(state,
    4829          144 :                                                              state.dataEnvrn->OutBaroPress,
    4830              :                                                              thisZoneHB.ZT,
    4831          144 :                                                              thisZoneHB.airHumRat) *
    4832          144 :                            Psychrometrics::PsyCpAirFnW(thisZoneHB.airHumRat)) *
    4833          144 :                           (state.dataGlobal->TimeStepZone * Constant::rSecsInHour); // Inverse equation
    4834              :             } else {
    4835          720 :                 MultpHM = 1.0; // Default value 1.0
    4836              :             }
    4837              : 
    4838          864 :             processInverseModelMultpHM(
    4839          864 :                 state, MultpHM, zone.ZoneVolCapMultpSensHMSum, zone.ZoneVolCapMultpSensHMCountSum, zone.ZoneVolCapMultpSensHMAverage, ZoneNum);
    4840          864 :             zone.ZoneVolCapMultpSensHM = MultpHM;
    4841              : 
    4842              :         } // Hybrid model internal thermal mass calculation end
    4843              : 
    4844              :         // Hybrid model people count calculation
    4845         1800 :         if (hmZone.PeopleCountCalc_T && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    4846          288 :             zone.ZoneMeasuredTemperature = hmZone.measuredTempSched->getCurrentVal();
    4847          288 :             zone.ZonePeopleActivityLevel = hmZone.peopleActivityLevelSched ? hmZone.peopleActivityLevelSched->getCurrentVal() : 0.0;
    4848          288 :             zone.ZonePeopleSensibleHeatFraction = hmZone.peopleSensibleFracSched ? hmZone.peopleSensibleFracSched->getCurrentVal() : 0.0;
    4849          288 :             zone.ZonePeopleRadiantHeatFraction = hmZone.peopleRadiantFracSched ? hmZone.peopleRadiantFracSched->getCurrentVal() : 0.0;
    4850              : 
    4851          288 :             Real64 FractionSensible = zone.ZonePeopleSensibleHeatFraction;
    4852          288 :             Real64 FractionRadiation = zone.ZonePeopleRadiantHeatFraction;
    4853          288 :             Real64 ActivityLevel = hmZone.peopleActivityLevelSched ? hmZone.peopleActivityLevelSched->getCurrentVal() : 0.0;
    4854              : 
    4855          288 :             if (FractionSensible <= 0.0) {
    4856            0 :                 FractionSensible = 0.6;
    4857              :             }
    4858              : 
    4859          288 :             if (FractionRadiation <= 0.0) {
    4860            0 :                 FractionConvection = 0.7;
    4861              :             } else {
    4862          288 :                 FractionConvection = 1.0 - FractionRadiation;
    4863              :             }
    4864              : 
    4865          288 :             if (ActivityLevel <= 0.0) {
    4866            0 :                 ActivityLevel = 130.0;
    4867              :             }
    4868              : 
    4869          288 :             if (hmZone.IncludeSystemSupplyParameters) {
    4870            0 :                 zone.ZoneMeasuredSupplyAirTemperature = hmZone.supplyAirTempSched->getCurrentVal();
    4871            0 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched ? hmZone.supplyAirMassFlowRateSched->getCurrentVal() : 0.0;
    4872            0 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched ? hmZone.supplyAirHumRatSched->getCurrentVal() : 0.0;
    4873              : 
    4874              :                 // Calculate the air humidity ratio at supply air inlet.
    4875            0 :                 Real64 CpAirInlet = Psychrometrics::PsyCpAirFnW(zone.ZoneMeasuredSupplyAirHumidityRatio);
    4876              : 
    4877            0 :                 Real64 SumSysMCp_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet;
    4878            0 :                 Real64 SumSysMCpT_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet * zone.ZoneMeasuredSupplyAirTemperature;
    4879              : 
    4880            0 :                 AA = SumSysMCp_HM + SumHA + SumMCp;
    4881            0 :                 BB = SumSysMCpT_HM + SumIntGainExceptPeople + SumHATsurf - SumHATref + SumMCpT +
    4882            0 :                      (thisZoneHB.NonAirSystemResponse / ZoneMult + thisZoneHB.SysDepZoneLoadsLagged);
    4883              :             } else {
    4884          288 :                 AA = SumHA + SumMCp;
    4885          288 :                 BB = SumIntGainExceptPeople + SumHATsurf - SumHATref + SumMCpT;
    4886              :             }
    4887              : 
    4888          288 :             Real64 CC = AirCap;
    4889              :             Real64 DD =
    4890          288 :                 (3.0 * state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) - (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum) +
    4891          288 :                  (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredZT3(ZoneNum));
    4892              : 
    4893          288 :             Real64 SumIntGainPeople = ((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredTemperature - BB - CC * DD;
    4894          288 :             Real64 UpperBound = max(0.0, SumIntGain / (ActivityLevel * FractionSensible * FractionConvection));
    4895          288 :             Real64 NumPeople = min(UpperBound, max(0.0, SumIntGainPeople / (ActivityLevel * FractionSensible * FractionConvection)));
    4896              : 
    4897          288 :             if (NumPeople < 0.05) {
    4898          286 :                 NumPeople = 0;
    4899              :             }
    4900          288 :             zone.NumOccHM = NumPeople;
    4901              :         }
    4902              :     }
    4903              : 
    4904              :     // Update zone temperatures in the previous steps
    4905         1800 :     state.dataHeatBalFanSys->PreviousMeasuredZT3(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum);
    4906         1800 :     state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum);
    4907         1800 :     state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) = thisZoneHB.ZT;
    4908         1800 : }
    4909              : 
    4910          864 : void processInverseModelMultpHM(EnergyPlusData &state,
    4911              :                                 Real64 &multiplierHM, // Hybrid model thermal mass multiplier
    4912              :                                 Real64 &multSumHM,    // Sum of Hybrid model thermal mass multipliers
    4913              :                                 Real64 &countSumHM,   // Count of number of points in sum
    4914              :                                 Real64 &multAvgHM,    // Average of hybrid model mass multiplier
    4915              :                                 int zoneNum           // Zone number for the hybrid model
    4916              : )
    4917              : {
    4918          864 :     Real64 constexpr minHMMultValue = 1.0;
    4919          864 :     Real64 constexpr maxHMMultValue = 30.0;
    4920              : 
    4921          864 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4922          864 :     auto &zone = state.dataHeatBal->Zone(zoneNum);
    4923          864 :     auto &thisZoneHB = s_ztpc->zoneHeatBalance(zoneNum);
    4924              : 
    4925              :     // Apply limits and generate warnings as needed
    4926          864 :     if (multiplierHM < minHMMultValue) { // don't allow this to be less than minimum (potential for instability)
    4927           70 :         multiplierHM = minHMMultValue;
    4928          794 :     } else if (multiplierHM > maxHMMultValue) { // as per suggestions in Defect #10508, only warn if greater than the max
    4929            4 :         if (thisZoneHB.hmThermalMassMultErrIndex == 0) {
    4930            2 :             ShowWarningMessage(state, format("Hybrid model thermal mass multiplier higher than the limit for {}", zone.Name));
    4931            4 :             ShowContinueError(state, "This means that the ratio of the zone air heat capacity for the current time step to the");
    4932            2 :             ShowContinueError(state, format("zone air heat storage is higher than the maximum limit of {:.1R}.", maxHMMultValue));
    4933              :         }
    4934           32 :         ShowRecurringWarningErrorAtEnd(
    4935            8 :             state, "Hybrid model thermal mass multiplier limit exceeded in zone " + zone.Name, thisZoneHB.hmThermalMassMultErrIndex);
    4936              :     }
    4937              : 
    4938              :     // Update running totals (but only when there is a valid multiplier, i.e. multiplier is greater than min but not higher than the max)
    4939          864 :     if (multiplierHM > minHMMultValue) {
    4940           74 :         multSumHM += multiplierHM;
    4941           74 :         countSumHM++;
    4942              :     }
    4943              : 
    4944              :     // Calculate average (always so that it does get calculated)
    4945          864 :     if (countSumHM >= 1) {
    4946          468 :         multAvgHM = multSumHM / countSumHM;
    4947              :     }
    4948          864 : }
    4949              : 
    4950          600 : void InverseModelHumidity(EnergyPlusData &state,
    4951              :                           int const ZoneNum,                   // Zone number
    4952              :                           Real64 const LatentGain,             // Zone sum of latent gain
    4953              :                           Real64 const LatentGainExceptPeople, // Zone sum of latent gain except for people
    4954              :                           Real64 const ZoneMassFlowRate,       // Zone air mass flow rate
    4955              :                           Real64 const MoistureMassFlowRate,   // Zone moisture mass flow rate
    4956              :                           Real64 const H2OHtOfVap,             // Heat of vaporization of air
    4957              :                           Real64 const RhoAir                  // Air density
    4958              : )
    4959              : {
    4960              :     // SUBROUTINE INFORMATION:
    4961              :     //       AUTHOR         Han Li
    4962              :     //       DATE WRITTEN   February 2019
    4963              : 
    4964              :     // PURPOSE OF THIS SUBROUTINE:
    4965              :     // This subroutine inversely solve infiltration airflow rate or people count with zone air humidity measurements.
    4966              : 
    4967              :     // SUBROUTINE PARAMETER DEFINITIONS:
    4968              :     static constexpr std::string_view RoutineName("InverseModelHumidity");
    4969              : 
    4970              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4971          600 :     Real64 AA(0.0);
    4972          600 :     Real64 BB(0.0);
    4973          600 :     Real64 ActivityLevel(0.0);
    4974          600 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    4975              : 
    4976          600 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4977          600 :     auto &zone = state.dataHeatBal->Zone(ZoneNum);
    4978          600 :     auto &hmZone = state.dataHybridModel->hybridModelZones(ZoneNum);
    4979          600 :     auto &thisZoneHB = s_ztpc->zoneHeatBalance(ZoneNum);
    4980              : 
    4981              :     // Get measured zone humidity ratio
    4982          600 :     zone.ZoneMeasuredHumidityRatio = hmZone.measuredHumRatSched->getCurrentVal();
    4983              : 
    4984          600 :     if (state.dataEnvrn->DayOfYear >= hmZone.HybridStartDayOfYear && state.dataEnvrn->DayOfYear <= hmZone.HybridEndDayOfYear) {
    4985          600 :         thisZoneHB.airHumRat = zone.ZoneMeasuredHumidityRatio;
    4986              : 
    4987              :         // Hybrid Model calculate air infiltration rate
    4988          600 :         if (hmZone.InfiltrationCalc_H && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    4989              :             // Conditionally calculate the time dependent and time independent terms
    4990          288 :             if (hmZone.IncludeSystemSupplyParameters) {
    4991            0 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched->getCurrentVal();
    4992            0 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched->getCurrentVal();
    4993              : 
    4994            0 :                 Real64 SumSysM_HM = zone.ZoneMeasuredSupplyAirFlowRate;
    4995            0 :                 Real64 SumSysMHumRat_HM = zone.ZoneMeasuredSupplyAirFlowRate * zone.ZoneMeasuredSupplyAirHumidityRatio;
    4996              : 
    4997            0 :                 AA = SumSysM_HM + thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa + thisZoneHB.MixingMassFlowZone +
    4998            0 :                      thisZoneHB.MDotOA;
    4999            0 :                 BB = SumSysMHumRat_HM + (LatentGain / H2OHtOfVap) + ((thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) +
    5000            0 :                      thisZoneHB.EAMFLxHumRat + thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat +
    5001            0 :                      thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5002              :             } else {
    5003          288 :                 AA = thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa + thisZoneHB.MixingMassFlowZone + thisZoneHB.MDotOA;
    5004          288 :                 BB = (LatentGain / H2OHtOfVap) + ((thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) + thisZoneHB.EAMFLxHumRat +
    5005          288 :                      thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat + thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5006              :             }
    5007              : 
    5008          288 :             Real64 CC = RhoAir * zone.Volume * zone.ZoneVolCapMultpMoist / TimeStepSysSec;
    5009          288 :             Real64 DD = (3.0 * state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum) -
    5010          288 :                          (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum) +
    5011          288 :                          (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat3(ZoneNum));
    5012              : 
    5013          288 :             Real64 delta_HR = (zone.ZoneMeasuredHumidityRatio - state.dataEnvrn->OutHumRat);
    5014              : 
    5015              :             Real64 AirDensity =
    5016          288 :                 Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, zone.OutDryBulbTemp, state.dataEnvrn->OutHumRat, RoutineName);
    5017              : 
    5018          288 :             Real64 M_inf = 0.0;
    5019          288 :             if (std::abs(zone.ZoneMeasuredHumidityRatio - state.dataEnvrn->OutHumRat) > 0.0000001) {
    5020          288 :                 M_inf = (CC * DD + BB - ((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredHumidityRatio) / delta_HR;
    5021              :             }
    5022              : 
    5023              :             // Add threshold for air change rate
    5024          288 :             Real64 ACH_inf = max(0.0, min(10.0, (M_inf / AirDensity) / zone.Volume * Constant::rSecsInHour));
    5025          288 :             M_inf = (ACH_inf / Constant::rSecsInHour) * zone.Volume * AirDensity;
    5026          288 :             zone.MCPIHM = M_inf;
    5027          288 :             zone.InfilOAAirChangeRateHM = ACH_inf;
    5028              :         }
    5029              : 
    5030              :         // Hybrid Model calculate people count
    5031          600 :         if (hmZone.PeopleCountCalc_H && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    5032          288 :             zone.ZonePeopleActivityLevel = hmZone.peopleActivityLevelSched ? hmZone.peopleActivityLevelSched->getCurrentVal() : 0.0;
    5033          288 :             zone.ZonePeopleSensibleHeatFraction = hmZone.peopleSensibleFracSched ? hmZone.peopleSensibleFracSched->getCurrentVal() : 0.0;
    5034          288 :             zone.ZonePeopleRadiantHeatFraction = hmZone.peopleRadiantFracSched ? hmZone.peopleRadiantFracSched->getCurrentVal() : 0.0;
    5035              : 
    5036          288 :             Real64 FractionSensible = zone.ZonePeopleSensibleHeatFraction;
    5037              : 
    5038          288 :             if (FractionSensible <= 0.0) {
    5039            0 :                 FractionSensible = 0.6;
    5040              :             }
    5041              : 
    5042          288 :             if (ActivityLevel <= 0.0) {
    5043          288 :                 ActivityLevel = 130.0;
    5044              :             }
    5045              : 
    5046              :             // Conditionally calculate the humidity-dependent and humidity-independent
    5047              :             // terms.
    5048          288 :             if (hmZone.IncludeSystemSupplyParameters) {
    5049          288 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched->getCurrentVal();
    5050          288 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched->getCurrentVal();
    5051              : 
    5052          288 :                 Real64 SumSysM_HM = zone.ZoneMeasuredSupplyAirFlowRate;
    5053          288 :                 Real64 SumSysMHumRat_HM = zone.ZoneMeasuredSupplyAirFlowRate * zone.ZoneMeasuredSupplyAirHumidityRatio;
    5054              : 
    5055          288 :                 AA = SumSysM_HM + thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa +
    5056          288 :                      thisZoneHB.MixingMassFlowZone + thisZoneHB.MDotOA;
    5057          864 :                 BB = SumSysMHumRat_HM + (LatentGainExceptPeople / H2OHtOfVap) +
    5058          288 :                      ((thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) + thisZoneHB.EAMFLxHumRat +
    5059          288 :                      thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat + thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5060              :             } else {
    5061            0 :                 AA = ZoneMassFlowRate + thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa +
    5062            0 :                      thisZoneHB.MixingMassFlowZone + thisZoneHB.MDotOA;
    5063            0 :                 BB = (LatentGainExceptPeople / H2OHtOfVap) + ((thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) +
    5064            0 :                      thisZoneHB.EAMFLxHumRat + (MoistureMassFlowRate) + thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat +
    5065            0 :                      thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5066              :             }
    5067              : 
    5068          288 :             Real64 CC = RhoAir * zone.Volume * zone.ZoneVolCapMultpMoist / TimeStepSysSec;
    5069          288 :             Real64 DD = (3.0 * state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum) -
    5070          288 :                          (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum) +
    5071          288 :                          (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat3(ZoneNum));
    5072              : 
    5073          288 :             Real64 LatentGainPeople = (((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredHumidityRatio - BB - CC * DD) * H2OHtOfVap;
    5074          288 :             Real64 UpperBound = max(0.0, LatentGain / (ActivityLevel * (1.0 - FractionSensible)));
    5075          288 :             Real64 NumPeople = min(UpperBound, max(0.0, LatentGainPeople / (ActivityLevel * (1.0 - FractionSensible))));
    5076          288 :             NumPeople = floor(NumPeople * 100.00 + 0.5) / 100.00;
    5077          288 :             if (NumPeople < 0.05) {
    5078          235 :                 NumPeople = 0;
    5079              :             }
    5080          288 :             zone.NumOccHM = NumPeople;
    5081              :         }
    5082              :     }
    5083              : 
    5084              :     // Update zone humidity ratio in the previous steps
    5085          600 :     state.dataHeatBalFanSys->PreviousMeasuredHumRat3(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum);
    5086          600 :     state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum);
    5087          600 :     state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum) = zone.ZoneMeasuredHumidityRatio;
    5088          600 : }
    5089              : 
    5090     62806616 : void ZoneSpaceHeatBalanceData::calcZoneOrSpaceSums(EnergyPlusData &state,
    5091              :                                                    bool const CorrectorFlag, // Corrector call flag
    5092              :                                                    int const zoneNum,
    5093              :                                                    int const spaceNum)
    5094              : {
    5095              : 
    5096              :     // SUBROUTINE INFORMATION:
    5097              :     //       AUTHOR         Peter Graham Ellis
    5098              :     //       DATE WRITTEN   July 2003
    5099              :     //       MODIFIED       Aug 2003, FCW: add this->SumHA contributions from window frame and divider
    5100              :     //                      Aug 2003, CC: change how the reference temperatures are used
    5101              : 
    5102              :     // PURPOSE OF THIS SUBROUTINE:
    5103              :     // This subroutine calculates the various sums that go into the zone heat balance
    5104              :     // equation.  This replaces the SUMC, SumHA, and SumHAT calculations that were
    5105              :     // previously done in various places throughout the program.
    5106              :     // The SumHAT portion of the code is reproduced in RadiantSystemHighTemp and
    5107              :     // RadiantSystemLowTemp and should be updated accordingly.
    5108              :     // A reference temperature (Tref) is specified for use with the ceiling diffuser
    5109              :     // convection correlation.  A bogus value of Tref = -999.9 defaults to using
    5110              :     // the zone air (i.e. outlet) temperature for the reference temperature.
    5111              :     // If Tref is applied to all surfaces, SumHA = 0, and SumHATref /= 0.
    5112              :     // If Tref is not used at all, SumHATref = 0, and SumHA /= 0.
    5113              :     // For future implementations, Tref can be easily converted into an array to
    5114              :     // allow a different reference temperature to be specified for each surface.
    5115     62806616 :     assert(zoneNum > 0);
    5116              : 
    5117     62806616 :     this->SumHA = 0.0;
    5118     62806616 :     this->SumHATsurf = 0.0;
    5119     62806616 :     this->SumHATref = 0.0;
    5120     62806616 :     this->SumSysMCp = 0.0;
    5121     62806616 :     this->SumSysMCpT = 0.0;
    5122              :     // Sum all convective internal gains: this->SumIntGain
    5123     62806616 :     if (spaceNum == 0) {
    5124     62691052 :         this->SumIntGain = InternalHeatGains::zoneSumAllInternalConvectionGains(state, zoneNum);
    5125              :     } else {
    5126       115564 :         this->SumIntGain = InternalHeatGains::spaceSumAllInternalConvectionGains(state, spaceNum);
    5127              :     }
    5128     62806616 :     this->SumIntGain += state.dataHeatBalFanSys->SumConvHTRadSys(zoneNum) + state.dataHeatBalFanSys->SumConvPool(zoneNum);
    5129              : 
    5130              :     // Add heat to return air if zonal system (no return air) or cycling system (return air frequently very low or zero)
    5131     62806616 :     assert(zoneNum > 0);
    5132     62806616 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    5133     62806616 :     if (thisZone.NoHeatToReturnAir) {
    5134      7216260 :         if (spaceNum == 0) {
    5135      7216260 :             this->SumIntGain += InternalHeatGains::zoneSumAllReturnAirConvectionGains(state, zoneNum, 0);
    5136              :         } else {
    5137            0 :             this->SumIntGain += InternalHeatGains::spaceSumAllReturnAirConvectionGains(state, spaceNum, 0);
    5138              :         }
    5139              :     }
    5140              : 
    5141              :     // Sum all non-system air flow, i.e. infiltration, simple ventilation, mixing, earth tube: this->SumMCp, this->SumMCpT
    5142     62806616 :     this->SumMCp = this->MCPI + this->MCPV + this->MCPM + this->MCPE + this->MCPC + this->MDotCPOA;
    5143     62806616 :     this->SumMCpT = this->MCPTI + this->MCPTV + this->MCPTM + this->MCPTE + this->MCPTC + this->MDotCPOA * thisZone.OutDryBulbTemp;
    5144              : 
    5145              :     // Sum all multizone air flow calculated from AirflowNetwork by assuming no simple air infiltration model
    5146    124625978 :     if (state.afn->multizone_always_simulated ||
    5147     61819362 :         (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    5148        46516 :          state.afn->AirflowNetworkFanActivated)) {
    5149      1023709 :         auto const &exchangeData = state.afn->exchangeData(zoneNum);
    5150      1023709 :         this->SumMCp = exchangeData.SumMCp + exchangeData.SumMVCp + exchangeData.SumMMCp;
    5151      1023709 :         this->SumMCpT = exchangeData.SumMCpT + exchangeData.SumMVCpT + exchangeData.SumMMCpT;
    5152              :     }
    5153              : 
    5154              :     // Sum all system air flow: this->SumSysMCp, this->SumSysMCpT and check to see if this is a controlled zone
    5155              :     // If the space is controlled, use space supply nodes, otherwise use zone supply nodes and allocate later
    5156     62806616 :     bool isSpaceControlled = (spaceNum > 0 && state.dataZoneEquip->spaceEquipConfig(spaceNum).IsControlled);
    5157     62806616 :     if (CorrectorFlag) {
    5158              :         // Plenum and controlled zones have a different set of inlet nodes which must be calculated.
    5159     31373982 :         if (thisZone.IsControlled) {
    5160     27124722 :             auto const &zsec = (isSpaceControlled ? state.dataZoneEquip->spaceEquipConfig(spaceNum) : state.dataZoneEquip->ZoneEquipConfig(zoneNum));
    5161     56010835 :             for (int NodeNum = 1, NodeNum_end = zsec.NumInletNodes; NodeNum <= NodeNum_end; ++NodeNum) {
    5162              :                 // Get node conditions, this next block is of interest to irratic system loads... maybe nodes are not accurate at time of call?
    5163              :                 //  how can we tell?  predict step must be lagged ?  correct step, systems have run.
    5164     28886113 :                 auto const &node(state.dataLoopNodes->Node(zsec.InletNode(NodeNum)));
    5165     28886113 :                 Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->airHumRat);
    5166     28886113 :                 Real64 const MassFlowRate_CpAir(node.MassFlowRate * CpAir);
    5167     28886113 :                 this->SumSysMCp += MassFlowRate_CpAir;
    5168     28886113 :                 this->SumSysMCpT += MassFlowRate_CpAir * node.Temp;
    5169              :             }
    5170              : 
    5171      4249260 :         } else if (thisZone.IsReturnPlenum) {
    5172      1089639 :             auto const &zrpc(state.dataZonePlenum->ZoneRetPlenCond(thisZone.PlenumCondNum));
    5173      1089639 :             Real64 const air_hum_rat(this->airHumRat);
    5174      6283299 :             for (int NodeNum = 1, NodeNum_end = zrpc.NumInletNodes; NodeNum <= NodeNum_end; ++NodeNum) {
    5175      5193660 :                 auto const &node(state.dataLoopNodes->Node(zrpc.InletNode(NodeNum)));
    5176      5193660 :                 Real64 const MassFlowRate_CpAir(node.MassFlowRate * Psychrometrics::PsyCpAirFnW(air_hum_rat));
    5177      5193660 :                 this->SumSysMCp += MassFlowRate_CpAir;
    5178      5193660 :                 this->SumSysMCpT += MassFlowRate_CpAir * node.Temp;
    5179              :             }
    5180              :             // add in the leaks
    5181      6301939 :             for (int ADUListIndex = 1, ADUListIndex_end = zrpc.NumADUs; ADUListIndex <= ADUListIndex_end; ++ADUListIndex) {
    5182      5212300 :                 auto &airDistUnit = state.dataDefineEquipment->AirDistUnit(zrpc.ADUIndex(ADUListIndex));
    5183      5212300 :                 if (airDistUnit.UpStreamLeak) {
    5184       106595 :                     Real64 const MassFlowRate_CpAir(airDistUnit.MassFlowRateUpStrLk * Psychrometrics::PsyCpAirFnW(air_hum_rat));
    5185       106595 :                     this->SumSysMCp += MassFlowRate_CpAir;
    5186       106595 :                     this->SumSysMCpT += MassFlowRate_CpAir * state.dataLoopNodes->Node(airDistUnit.InletNodeNum).Temp;
    5187              :                 }
    5188      5212300 :                 if (airDistUnit.DownStreamLeak) {
    5189       106595 :                     Real64 const MassFlowRate_CpAir(airDistUnit.MassFlowRateDnStrLk * Psychrometrics::PsyCpAirFnW(air_hum_rat));
    5190       106595 :                     this->SumSysMCp += MassFlowRate_CpAir;
    5191       106595 :                     this->SumSysMCpT += MassFlowRate_CpAir * state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).Temp;
    5192              :                 }
    5193              :             }
    5194              : 
    5195      3159621 :         } else if (thisZone.IsSupplyPlenum) {
    5196        19526 :             Real64 MassFlowRate = state.dataLoopNodes->Node(state.dataZonePlenum->ZoneSupPlenCond(thisZone.PlenumCondNum).InletNode).MassFlowRate;
    5197        19526 :             Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->airHumRat);
    5198        19526 :             this->SumSysMCp += MassFlowRate * CpAir;
    5199        19526 :             this->SumSysMCpT +=
    5200        19526 :                 MassFlowRate * CpAir * state.dataLoopNodes->Node(state.dataZonePlenum->ZoneSupPlenCond(thisZone.PlenumCondNum).InletNode).Temp;
    5201              :         }
    5202              : 
    5203     31373982 :         int ZoneMult = thisZone.Multiplier * thisZone.ListMultiplier;
    5204              : 
    5205     31373982 :         this->SumSysMCp /= ZoneMult;
    5206     31373982 :         this->SumSysMCpT /= ZoneMult;
    5207              :     }
    5208              : 
    5209     62806616 :     if (spaceNum > 0 && !isSpaceControlled) {
    5210              :         // If space is not controlled, allocate zone-level airflow by volume
    5211        93034 :         Real64 spaceFrac = state.dataHeatBal->space(spaceNum).fracZoneVolume;
    5212        93034 :         this->SumSysMCp *= spaceFrac;
    5213        93034 :         this->SumSysMCpT *= spaceFrac;
    5214              :     }
    5215              : 
    5216              :     // Sum all surface convection: this->SumHA, this->SumHATsurf, this->SumHATref (and additional contributions to this->SumIntGain)
    5217     62806616 :     SumHATOutput sumHATResults; // space or zone return values
    5218     62806616 :     sumHATResults = this->calcSumHAT(state, zoneNum, spaceNum);
    5219     62806616 :     this->SumIntGain += sumHATResults.sumIntGain;
    5220     62806616 :     this->SumHA = sumHATResults.sumHA;
    5221     62806616 :     this->SumHATsurf = sumHATResults.sumHATsurf;
    5222     62806616 :     this->SumHATref = sumHATResults.sumHATref;
    5223     62806616 : }
    5224              : 
    5225     62691052 : SumHATOutput ZoneHeatBalanceData::calcSumHAT(EnergyPlusData &state, int const zoneNum, [[maybe_unused]] int const spaceNum)
    5226              : {
    5227     62691052 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5228     62691052 :     assert(zoneNum > 0);
    5229     62691052 :     assert(spaceNum == 0);
    5230     62691052 :     SumHATOutput zoneResults; // zone-level return values
    5231    125509832 :     for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    5232     62818780 :         SumHATOutput spaceResults; // temporary return value from space-level calcSumHAT
    5233     62818780 :         spaceResults = s_ztpc->spaceHeatBalance(zoneSpaceNum).calcSumHAT(state, zoneNum, zoneSpaceNum);
    5234     62818780 :         zoneResults.sumIntGain += spaceResults.sumIntGain;
    5235     62818780 :         zoneResults.sumHA += spaceResults.sumHA;
    5236     62818780 :         zoneResults.sumHATsurf += spaceResults.sumHATsurf;
    5237     62818780 :         zoneResults.sumHATref += spaceResults.sumHATref;
    5238     62691052 :     }
    5239     62691052 :     return zoneResults;
    5240              : }
    5241              : 
    5242     62934344 : SumHATOutput SpaceHeatBalanceData::calcSumHAT(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    5243              : {
    5244     62934344 :     assert(zoneNum > 0);
    5245     62934344 :     assert(spaceNum > 0);
    5246     62934344 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5247     62934344 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    5248     62934344 :     auto &thisSpace = state.dataHeatBal->space(spaceNum);
    5249     62934344 :     SumHATOutput results; // space-level return values
    5250              : 
    5251    609545632 :     for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
    5252    546611288 :         Real64 HA = 0.0;
    5253    546611288 :         Real64 Area = state.dataSurface->Surface(SurfNum).Area; // For windows, this is the glazing area
    5254              : 
    5255    546611288 :         if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
    5256     74769762 :             DataSurfaces::WinShadingType const shading_flag = state.dataSurface->SurfWinShadingFlag(SurfNum);
    5257              : 
    5258              :             // Add to the convective internal gains
    5259     74769762 :             if (ANY_INTERIOR_SHADE_BLIND(shading_flag)) {
    5260              :                 // The shade area covers the area of the glazing plus the area of the dividers.
    5261       555262 :                 Area += state.dataSurface->SurfWinDividerArea(SurfNum);
    5262              :                 // If interior shade or blind is present it is assumed that both the convective and IR radiative gain
    5263              :                 // from the inside surface of the divider goes directly into the zone air -- i.e., the IR radiative
    5264              :                 // interaction between divider and shade or blind is ignored due to the difficulty of calculating this interaction
    5265              :                 // at the same time that the interaction between glass and shade is calculated.
    5266       555262 :                 results.sumIntGain += state.dataSurface->SurfWinDividerHeatGain(SurfNum);
    5267              :             }
    5268              : 
    5269              :             // Other convection term is applicable to equivalent layer window (ASHWAT) model
    5270     74769762 :             if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
    5271        16326 :                 results.sumIntGain += state.dataSurface->SurfWinOtherConvHeatGain(SurfNum);
    5272              :             }
    5273              : 
    5274              :             // Convective heat gain from natural convection in gap between glass and interior shade or blind
    5275     74769762 :             if (ANY_INTERIOR_SHADE_BLIND(shading_flag)) {
    5276       555262 :                 results.sumIntGain += state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum);
    5277              :             }
    5278              : 
    5279              :             // Convective heat gain from airflow window
    5280     74769762 :             if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
    5281        40144 :                 results.sumIntGain += state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum);
    5282        40144 :                 if (thisZone.NoHeatToReturnAir) {
    5283        40048 :                     results.sumIntGain += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
    5284        40048 :                     state.dataSurface->SurfWinHeatGain(SurfNum) += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
    5285        40048 :                     if (state.dataSurface->SurfWinHeatGain(SurfNum) >= 0.0) {
    5286        11000 :                         state.dataSurface->SurfWinHeatGainRep(SurfNum) = state.dataSurface->SurfWinHeatGain(SurfNum);
    5287        11000 :                         state.dataSurface->SurfWinHeatGainRepEnergy(SurfNum) =
    5288        11000 :                             state.dataSurface->SurfWinHeatGainRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    5289              :                     } else {
    5290        29048 :                         state.dataSurface->SurfWinHeatLossRep(SurfNum) = -state.dataSurface->SurfWinHeatGain(SurfNum);
    5291        29048 :                         state.dataSurface->SurfWinHeatLossRepEnergy(SurfNum) =
    5292        29048 :                             state.dataSurface->SurfWinHeatLossRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    5293              :                     }
    5294        40048 :                     state.dataSurface->SurfWinHeatTransferRepEnergy(SurfNum) =
    5295        40048 :                         state.dataSurface->SurfWinHeatGain(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    5296              :                 }
    5297              :             }
    5298              : 
    5299              :             // Add to the surface convection sums
    5300     74769762 :             if (state.dataSurface->SurfWinFrameArea(SurfNum) > 0.0) {
    5301              :                 // Window frame contribution
    5302      2874560 :                 Real64 const HA_surf(state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
    5303      2874560 :                                      (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum)));
    5304      2874560 :                 results.sumHATsurf += HA_surf * state.dataSurface->SurfWinFrameTempIn(SurfNum);
    5305      2874560 :                 HA += HA_surf;
    5306              :             }
    5307              : 
    5308     74769762 :             if (state.dataSurface->SurfWinDividerArea(SurfNum) > 0.0 && !ANY_INTERIOR_SHADE_BLIND(shading_flag)) {
    5309              :                 // Window divider contribution (only from shade or blind for window with divider and interior shade or blind)
    5310       992870 :                 Real64 const HA_surf(state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
    5311       992870 :                                      (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum)));
    5312       992870 :                 results.sumHATsurf += HA_surf * state.dataSurface->SurfWinDividerTempIn(SurfNum);
    5313       992870 :                 HA += HA_surf;
    5314              :             }
    5315              : 
    5316              :         } // End of check if window
    5317              : 
    5318    546611288 :         HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area;
    5319    546611288 :         results.sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(SurfNum);
    5320              : 
    5321              :         // determine reference air temperature for this surface
    5322    546611288 :         switch (state.dataSurface->SurfTAirRef(SurfNum)) {
    5323      3134082 :         case DataSurfaces::RefAirTemp::ZoneMeanAirTemp:
    5324              :             // The zone air is the reference temperature (which is to be solved for in CorrectZoneAirTemp).
    5325      3134082 :             results.sumHA += HA;
    5326      3134082 :             break;
    5327      4502853 :         case DataSurfaces::RefAirTemp::AdjacentAirTemp:
    5328      4502853 :             results.sumHATref += HA * state.dataHeatBal->SurfTempEffBulkAir(SurfNum);
    5329      4502853 :             break;
    5330            0 :         case DataSurfaces::RefAirTemp::ZoneSupplyAirTemp:
    5331              :             // check whether this zone is a controlled zone or not
    5332            0 :             if (!thisZone.IsControlled) {
    5333            0 :                 ShowFatalError(state,
    5334            0 :                                format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}", thisZone.Name));
    5335            0 :                 return results;
    5336              :             }
    5337              :             // determine supply air temperature as a weighted average of the inlet temperatures.
    5338              :             // TODO: For now, use zone-level values for system flow
    5339            0 :             if (s_ztpc->zoneHeatBalance(zoneNum).SumSysMCp > 0.0) {
    5340            0 :                 results.sumHATref += HA * s_ztpc->zoneHeatBalance(zoneNum).SumSysMCpT / s_ztpc->zoneHeatBalance(zoneNum).SumSysMCp;
    5341              :             } else {
    5342              :                 // no system flow (yet) so just use zone air temperature #5906
    5343            0 :                 results.sumHA += HA;
    5344              :             }
    5345            0 :             break;
    5346    538974353 :         default:
    5347              :             // currently set to mean air temp but should add error warning here
    5348    538974353 :             results.sumHA += HA;
    5349    538974353 :             break;
    5350              :         }
    5351              : 
    5352              :     } // SurfNum
    5353     62934344 :     return results;
    5354              : }
    5355     31431206 : void CalcZoneComponentLoadSums(EnergyPlusData &state,
    5356              :                                int ZoneNum, // Zone number
    5357              :                                ZoneTempPredictorCorrector::ZoneSpaceHeatBalanceData *thisHB,
    5358              :                                DataHeatBalance::AirReportVars &thisAirRpt)
    5359              : {
    5360              : 
    5361              :     // SUBROUTINE INFORMATION:
    5362              :     //       AUTHOR         Brent Griffith
    5363              :     //       DATE WRITTEN   Feb 2008
    5364              : 
    5365              :     // PURPOSE OF THIS SUBROUTINE:
    5366              :     // This subroutine calculates the various sums that go into the zone heat balance
    5367              :     // equation for reporting (and diagnostic) purposes only.
    5368              :     // It was derived from CalcZonethisAirRpt.Sums but differs in that that routine
    5369              :     // breaks up the component's dependence on zone air temp in order to *solve* for zone air temp,
    5370              :     // but here we *use* the result for zone air temp and calculate the terms of the heat balance
    5371              :     // Go back and calculate each of the 6 terms in Equation 5 and fill report variables.
    5372              :     // notes on these raw terms for zone air heat balance model :
    5373              :     //  these are state variables at the end of the last system timestep.
    5374              :     //  they are not necessarily proper averages for what happened over entire zone time step
    5375              :     //  these are not multiplied by zone multipliers.
    5376              :     //  The values are all Watts.
    5377              : 
    5378              :     // REFERENCES:
    5379              :     // Equation 5 in Engineering Reference.
    5380              : 
    5381     31431206 :     thisAirRpt.SumIntGains = 0.0;    // Zone sum of convective internal gains
    5382     31431206 :     thisAirRpt.SumHADTsurfs = 0.0;   // Zone sum of Hc*Area*(Tsurf - Tz)
    5383     31431206 :     thisAirRpt.SumMCpDTzones = 0.0;  // zone sum of MassFlowRate*cp*(TremotZone - Tz) transfer air from other zone, Mixing
    5384     31431206 :     thisAirRpt.SumMCpDtInfil = 0.0;  // Zone sum of MassFlowRate*Cp*(Tout - Tz)
    5385     31431206 :     thisAirRpt.SumMCpDTsystem = 0.0; // Zone sum of air system MassFlowRate*Cp*(Tsup - Tz)
    5386     31431206 :     thisAirRpt.SumNonAirSystem = 0.0;
    5387     31431206 :     thisAirRpt.CzdTdt = 0.0;
    5388     31431206 :     thisAirRpt.imBalance = 0.0;
    5389     31431206 :     thisAirRpt.SumEnthalpyM = 0.0;
    5390     31431206 :     thisAirRpt.SumEnthalpyH = 0.0;
    5391              : 
    5392     31431206 :     auto &thisZone = state.dataHeatBal->Zone(ZoneNum);
    5393              : 
    5394     31431206 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    5395              : 
    5396              :     // Sum all convective internal gains: SumIntGain
    5397     31431206 :     thisAirRpt.SumIntGains = InternalHeatGains::zoneSumAllInternalConvectionGains(state, ZoneNum);
    5398              : 
    5399              :     // Add heat to return air if zonal system (no return air) or cycling system (return air frequently very
    5400              :     // low or zero)
    5401     31431206 :     if (thisZone.NoHeatToReturnAir) {
    5402      3608130 :         thisAirRpt.SumIntGains += InternalHeatGains::zoneSumAllReturnAirConvectionGains(state, ZoneNum, 0);
    5403              :     }
    5404              : 
    5405              :     // sum non-system air flow transfers between zones
    5406     31431206 :     thisAirRpt.SumMCpDTzones = thisHB->MCPTM - thisHB->MCPM * thisHB->MAT; // but maybe it should be ZTAV(ZoneNum)
    5407              : 
    5408              :     // Sum non-system air flow, i.e. infiltration, simple ventilation, earth tube
    5409              :     //  reuse SumMCp, SumMCpT from CalcZoneSum but use MAT (or maybe ZTAV?) to complete
    5410     31431206 :     thisAirRpt.SumMCpDtInfil = (thisHB->MCPTI - thisHB->MCPI * thisHB->MAT) + (thisHB->MCPTV - thisHB->MCPV * thisHB->MAT) +
    5411     31431206 :                                (thisHB->MCPTE - thisHB->MCPE * thisHB->MAT) + (thisHB->MCPTC - thisHB->MCPC * thisHB->MAT) +
    5412     31431206 :                                (thisHB->MDotCPOA * thisZone.OutDryBulbTemp -
    5413     31431206 :                                 thisHB->MDotCPOA * thisHB->MAT); // infiltration | Ventilation (simple) | Earth tube. | Cooltower | combined OA flow
    5414              : 
    5415              :     // Sum all multizone air flow calculated from AirflowNetwork by assuming no simple air infiltration model (if used)
    5416     62368785 :     if (state.afn->multizone_always_simulated ||
    5417     30937579 :         (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    5418        23258 :          state.afn->AirflowNetworkFanActivated)) {
    5419              :         // Multizone airflow calculated in AirflowNetwork
    5420       511861 :         thisAirRpt.SumMCpDtInfil = state.afn->exchangeData(ZoneNum).SumMCpT + state.afn->exchangeData(ZoneNum).SumMVCpT -
    5421       511861 :                                    (state.afn->exchangeData(ZoneNum).SumMCp + state.afn->exchangeData(ZoneNum).SumMVCp) * thisHB->MAT;
    5422       511861 :         thisAirRpt.SumMCpDTzones = state.afn->exchangeData(ZoneNum).SumMMCpT - state.afn->exchangeData(ZoneNum).SumMMCp * thisHB->MAT;
    5423              :     }
    5424              : 
    5425              :     // Sum all system air flow: reusing how SumSysMCp, SumSysMCpT are calculated in CalcZoneSums
    5426              :     // Plenum and controlled zones have a different set of inlet nodes which must be calculated.
    5427     31431206 :     Real64 QSensRate = 0.0;
    5428     31431206 :     if (thisZone.IsControlled) {
    5429     27174934 :         auto &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(ZoneNum);
    5430     56111259 :         for (int NodeNum = 1; NodeNum <= zoneEquipConfig.NumInletNodes; ++NodeNum) {
    5431              :             // Get node conditions
    5432     28936325 :             Real64 const NodeTemp = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(NodeNum)).Temp;
    5433     28936325 :             Real64 const MassFlowRate = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(NodeNum)).MassFlowRate;
    5434     28936325 :             QSensRate = calcZoneSensibleOutput(MassFlowRate, NodeTemp, thisHB->MAT, thisHB->airHumRat);
    5435     28936325 :             thisAirRpt.SumMCpDTsystem += QSensRate;
    5436              : 
    5437     28936325 :             if (zoneEquipConfig.InletNodeADUNum(NodeNum) > 0) {
    5438     14854137 :                 auto &airDistUnit = state.dataDefineEquipment->AirDistUnit(zoneEquipConfig.InletNodeADUNum(NodeNum));
    5439     14854137 :                 Real64 ADUHeatAddRate = calcZoneSensibleOutput(state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).MassFlowRate,
    5440     14854137 :                                                                state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).Temp,
    5441              :                                                                thisHB->MAT,
    5442              :                                                                thisHB->airHumRat);
    5443     14854137 :                 airDistUnit.HeatRate = max(0.0, ADUHeatAddRate);
    5444     14854137 :                 airDistUnit.CoolRate = std::abs(min(0.0, ADUHeatAddRate));
    5445     14854137 :                 airDistUnit.HeatGain = airDistUnit.HeatRate * TimeStepSysSec;
    5446     14854137 :                 airDistUnit.CoolGain = airDistUnit.CoolRate * TimeStepSysSec;
    5447              :             }
    5448              :         }
    5449              : 
    5450      4256272 :     } else if (thisZone.IsReturnPlenum) {
    5451      1089639 :         auto &zoneRetPlenCond = state.dataZonePlenum->ZoneRetPlenCond(thisZone.PlenumCondNum);
    5452      6283299 :         for (int NodeNum = 1; NodeNum <= zoneRetPlenCond.NumInletNodes; ++NodeNum) {
    5453      5193660 :             QSensRate = calcZoneSensibleOutput(state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(NodeNum)).MassFlowRate,
    5454      5193660 :                                                state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(NodeNum)).Temp,
    5455              :                                                thisHB->MAT,
    5456              :                                                thisHB->airHumRat);
    5457      5193660 :             thisAirRpt.SumMCpDTsystem += QSensRate;
    5458              :         }
    5459              :         // add in the leaks
    5460      6301939 :         for (int ADUListIndex = 1; ADUListIndex <= zoneRetPlenCond.NumADUs; ++ADUListIndex) {
    5461      5212300 :             auto &airDistUnit = state.dataDefineEquipment->AirDistUnit(zoneRetPlenCond.ADUIndex(ADUListIndex));
    5462      5212300 :             if (airDistUnit.UpStreamLeak) {
    5463       319785 :                 QSensRate = calcZoneSensibleOutput(
    5464       106595 :                     airDistUnit.MassFlowRateUpStrLk, state.dataLoopNodes->Node(airDistUnit.InletNodeNum).Temp, thisHB->MAT, thisHB->airHumRat);
    5465       106595 :                 thisAirRpt.SumMCpDTsystem += QSensRate;
    5466              :             }
    5467      5212300 :             if (airDistUnit.DownStreamLeak) {
    5468       319785 :                 QSensRate = calcZoneSensibleOutput(
    5469       106595 :                     airDistUnit.MassFlowRateDnStrLk, state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).Temp, thisHB->MAT, thisHB->airHumRat);
    5470       106595 :                 thisAirRpt.SumMCpDTsystem += QSensRate;
    5471              :             }
    5472              :         }
    5473              : 
    5474      3166633 :     } else if (thisZone.IsSupplyPlenum) {
    5475        19526 :         auto &zoneSupPlenCond = state.dataZonePlenum->ZoneSupPlenCond(thisZone.PlenumCondNum);
    5476        19526 :         QSensRate = calcZoneSensibleOutput(state.dataLoopNodes->Node(zoneSupPlenCond.InletNode).MassFlowRate,
    5477        19526 :                                            state.dataLoopNodes->Node(zoneSupPlenCond.InletNode).Temp,
    5478              :                                            thisHB->MAT,
    5479              :                                            thisHB->airHumRat);
    5480        19526 :         thisAirRpt.SumMCpDTsystem += QSensRate;
    5481              :     }
    5482              : 
    5483              :     // non air system response.
    5484     31431206 :     thisAirRpt.SumNonAirSystem =
    5485     31431206 :         thisHB->NonAirSystemResponse + state.dataHeatBalFanSys->SumConvHTRadSys(ZoneNum) + state.dataHeatBalFanSys->SumConvPool(ZoneNum);
    5486              : 
    5487              :     // Sum all surface convection: SumHA, SumHATsurf, SumHATref (and additional contributions to SumIntGain)
    5488     62994064 :     for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
    5489     31562858 :         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    5490    305331376 :         for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
    5491              : 
    5492    273768518 :             Real64 Area = state.dataSurface->Surface(SurfNum).Area; // For windows, this is the glazing area
    5493    273768518 :             Real64 RefAirTemp = state.dataSurface->Surface(SurfNum).getInsideAirTemperature(state, SurfNum);
    5494              : 
    5495    273768518 :             if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
    5496              : 
    5497              :                 // Add to the convective internal gains
    5498     37427799 :                 if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
    5499              :                     // The shade area covers the area of the glazing plus the area of the dividers.
    5500       277631 :                     Area += state.dataSurface->SurfWinDividerArea(SurfNum);
    5501              :                     // If interior shade or blind is present it is assumed that both the convective and IR radiative gain
    5502              :                     // from the inside surface of the divider goes directly into the zone air -- i.e., the IR radiative
    5503              :                     // interaction between divider and shade or blind is ignored due to the difficulty of calculating this interaction
    5504              :                     // at the same time that the interaction between glass and shade is calculated.
    5505       277631 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinDividerHeatGain(SurfNum);
    5506              :                 }
    5507              : 
    5508              :                 // Other convection term is applicable to equivalent layer window (ASHWAT) model
    5509     37427799 :                 if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
    5510         8163 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinOtherConvHeatGain(SurfNum);
    5511              :                 }
    5512              : 
    5513              :                 // Convective heat gain from natural convection in gap between glass and interior shade or blind
    5514     37427799 :                 if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
    5515       277631 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum);
    5516              :                 }
    5517              : 
    5518              :                 // Convective heat gain from airflow window
    5519     37427799 :                 if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
    5520        20072 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum);
    5521        20072 :                     if (thisZone.NoHeatToReturnAir) {
    5522        20024 :                         thisAirRpt.SumIntGains += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
    5523              :                     }
    5524              :                 }
    5525              : 
    5526              :                 // Add to the surface convection sums
    5527     37427799 :                 if (state.dataSurface->SurfWinFrameArea(SurfNum) > 0.0) {
    5528              :                     // Window frame contribution
    5529      1437280 :                     thisAirRpt.SumHADTsurfs += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
    5530      1437280 :                                                (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum)) *
    5531      1437280 :                                                (state.dataSurface->SurfWinFrameTempIn(SurfNum) - RefAirTemp);
    5532              :                 }
    5533              : 
    5534     37927223 :                 if (state.dataSurface->SurfWinDividerArea(SurfNum) > 0.0 &&
    5535       499424 :                     !ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
    5536              :                     // Window divider contribution (only from shade or blind for window with divider and interior shade or blind)
    5537       496435 :                     thisAirRpt.SumHADTsurfs += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
    5538       496435 :                                                (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum)) *
    5539       496435 :                                                (state.dataSurface->SurfWinDividerTempIn(SurfNum) - RefAirTemp);
    5540              :                 }
    5541              : 
    5542              :             } // End of check if window
    5543              : 
    5544    273768518 :             thisAirRpt.SumHADTsurfs +=
    5545    273768518 :                 state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area * (state.dataHeatBalSurf->SurfTempInTmp(SurfNum) - RefAirTemp);
    5546              : 
    5547              :             // Accumulate Zone Phase Change Material Melting/Freezing Enthalpy output variables
    5548    273768518 :             if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
    5549      1710066 :                 thisAirRpt.SumEnthalpyM += state.dataHeatBalFiniteDiffMgr->SurfaceFD(SurfNum).EnthalpyM;
    5550      1710066 :                 thisAirRpt.SumEnthalpyH += state.dataHeatBalFiniteDiffMgr->SurfaceFD(SurfNum).EnthalpyF;
    5551              :             }
    5552              :         }
    5553     31431206 :     }
    5554              :     // now calculate air energy storage source term.
    5555              :     // capacitance is volume * density * heat capacity
    5556     31431206 :     Real64 CpAir = Psychrometrics::PsyCpAirFnW(thisHB->airHumRat);
    5557     31431206 :     Real64 RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, thisHB->MAT, thisHB->airHumRat);
    5558              : 
    5559     31431206 :     switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    5560     17089871 :     case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    5561     17089871 :         thisAirRpt.CzdTdt = RhoAir * CpAir * thisZone.Volume * thisZone.ZoneVolCapMultpSens * (thisHB->MAT - thisHB->ZTM[0]) / TimeStepSysSec;
    5562              :         // Exact solution
    5563     17089871 :     } break;
    5564     14303125 :     case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    5565     14303125 :         thisAirRpt.CzdTdt = thisHB->TempIndCoef - thisHB->TempDepCoef * thisHB->MAT;
    5566     14303125 :     } break;
    5567        38210 :     case DataHeatBalance::SolutionAlgo::EulerMethod: {
    5568        38210 :         thisAirRpt.CzdTdt = thisHB->AirPowerCap * (thisHB->MAT - thisHB->T1);
    5569        38210 :     } break;
    5570            0 :     default:
    5571            0 :         break;
    5572              :     }
    5573              : 
    5574     31431206 :     if (state.dataGlobal->DisplayZoneAirHeatBalanceOffBalance) {
    5575       263377 :         thisAirRpt.imBalance = thisAirRpt.SumIntGains + thisAirRpt.SumHADTsurfs + thisAirRpt.SumMCpDTzones + thisAirRpt.SumMCpDtInfil +
    5576       263377 :                                thisAirRpt.SumMCpDTsystem + thisAirRpt.SumNonAirSystem - thisAirRpt.CzdTdt;
    5577              : 
    5578              :         // throw warning if seriously out of balance (this may need to be removed if too noisy... )
    5579              :         // formulate dynamic threshold value based on 20% of quadrature sum of components
    5580       263377 :         Real64 Threshold = 0.2 * std::sqrt(pow_2(thisAirRpt.SumIntGains) + pow_2(thisAirRpt.SumHADTsurfs) + pow_2(thisAirRpt.SumMCpDTzones) +
    5581       263377 :                                            pow_2(thisAirRpt.SumMCpDtInfil) + pow_2(thisAirRpt.SumMCpDTsystem) + pow_2(thisAirRpt.SumNonAirSystem) +
    5582       263377 :                                            pow_2(thisAirRpt.CzdTdt));
    5583       264173 :         if ((std::abs(thisAirRpt.imBalance) > Threshold) && (!state.dataGlobal->WarmupFlag) &&
    5584          796 :             (!state.dataGlobal->DoingSizing)) { // air balance is out by more than threshold
    5585          796 :             if (thisZone.AirHBimBalanceErrIndex == 0) {
    5586           14 :                 ShowWarningMessage(state, format("Zone Air Heat Balance is out of balance for zone named {}", thisZone.Name));
    5587           14 :                 ShowContinueError(state, format("Zone Air Heat Balance Deviation Rate is more than {:.1R} {{W}}", Threshold));
    5588           14 :                 if (state.dataHVACGlobal->TurnFansOn) {
    5589            0 :                     ShowContinueError(state, "Night cycle fan operation may be causing above error");
    5590              :                 }
    5591              : 
    5592           42 :                 ShowContinueErrorTimeStamp(state, " Occurrence info:");
    5593              :             }
    5594         6368 :             ShowRecurringWarningErrorAtEnd(state,
    5595         1592 :                                            format("Zone Air Heat Balance is out of balance ... zone named {}", thisZone.Name),
    5596          796 :                                            thisZone.AirHBimBalanceErrIndex,
    5597         1592 :                                            std::abs(thisAirRpt.imBalance) - Threshold,
    5598         1592 :                                            std::abs(thisAirRpt.imBalance) - Threshold,
    5599              :                                            _,
    5600              :                                            "{W}",
    5601              :                                            "{W}");
    5602              :         }
    5603              :     }
    5604     31431206 : }
    5605              : 
    5606         3642 : bool VerifyThermostatInZone(EnergyPlusData &state, std::string const &ZoneName) // Zone to verify
    5607              : {
    5608              : 
    5609              :     // FUNCTION INFORMATION:
    5610              :     //       AUTHOR         Linda Lawrie
    5611              :     //       DATE WRITTEN   Feb 2005
    5612              : 
    5613              :     // PURPOSE OF THIS FUNCTION:
    5614              :     // This function verifies that a zone (by name) has a Zone Control:Thermostatic object entered.
    5615              : 
    5616         3642 :     if (state.dataZoneCtrls->GetZoneAirStatsInputFlag) {
    5617            0 :         GetZoneAirSetPoints(state);
    5618            0 :         state.dataZoneCtrls->GetZoneAirStatsInputFlag = false;
    5619              :     }
    5620         3642 :     if (state.dataZoneCtrls->NumTempControlledZones > 0) {
    5621         3639 :         if (Util::FindItemInList(ZoneName, state.dataZoneCtrls->TempControlledZone, &DataZoneControls::ZoneTempControls::ZoneName) > 0) {
    5622         3638 :             return true;
    5623              :         } else {
    5624            1 :             return false;
    5625              :         }
    5626              :     }
    5627            3 :     return false;
    5628              : }
    5629              : 
    5630         4334 : bool VerifyControlledZoneForThermostat(EnergyPlusData &state, std::string const &ZoneName) // Zone to verify
    5631              : {
    5632              : 
    5633              :     // FUNCTION INFORMATION:
    5634              :     //       AUTHOR         Linda Lawrie
    5635              :     //       DATE WRITTEN   Mar 2007
    5636              : 
    5637              :     // PURPOSE OF THIS FUNCTION:
    5638              :     // This function verifies that a zone (by name) has a ZoneHVAC:EquipmentConnections object entered.
    5639              : 
    5640         4334 :     return (Util::FindItemInList(ZoneName, state.dataZoneEquip->ZoneEquipConfig, &DataZoneEquipment::EquipConfiguration::ZoneName) > 0);
    5641              : }
    5642              : 
    5643      3588830 : void DetectOscillatingZoneTemp(EnergyPlusData &state)
    5644              : {
    5645              :     // SUBROUTINE INFORMATION:
    5646              :     //       AUTHOR         Jason Glazer
    5647              :     //       DATE WRITTEN   August 2005
    5648              : 
    5649              :     // PURPOSE OF THIS SUBROUTINE:
    5650              :     // Oscillating temperatures between HVAC timesteps indicate that the
    5651              :     // simulation may be poor. Code is trying to be fast since the purpose
    5652              :     // is to see the impact on oscillating by trying longer time steps in
    5653              :     // an attempt to speed up the simulation.
    5654              :     // Note that the OscillateMagnitude threshold must be less than
    5655              :     // MaxZoneTempDiff since ManageHVAC keeps shortening the timestep
    5656              :     // until that is reached unless it goes to less than the
    5657              :     // MinTimeStepSys.
    5658      3588830 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5659              : 
    5660              :     // first time run allocate arrays and setup output variable
    5661      3588830 :     if (s_ztpc->SetupOscillationOutputFlag) {
    5662          801 :         s_ztpc->ZoneTempHist.allocate(4, state.dataGlobal->NumOfZones);
    5663          801 :         s_ztpc->ZoneTempHist = 0.0;
    5664          801 :         s_ztpc->ZoneTempOscillate.dimension(state.dataGlobal->NumOfZones, 0.0);
    5665          801 :         s_ztpc->ZoneTempOscillateDuringOccupancy.dimension(state.dataGlobal->NumOfZones, 0.0);
    5666          801 :         s_ztpc->ZoneTempOscillateInDeadband.dimension(state.dataGlobal->NumOfZones, 0.0);
    5667              :         // set up zone by zone variables, CurrentModuleObject='Zone'
    5668         6002 :         for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    5669         5201 :             auto &zone = state.dataHeatBal->Zone(iZone);
    5670        10402 :             SetupOutputVariable(state,
    5671              :                                 "Zone Oscillating Temperatures Time",
    5672              :                                 Constant::Units::hr,
    5673         5201 :                                 s_ztpc->ZoneTempOscillate(iZone),
    5674              :                                 OutputProcessor::TimeStepType::System,
    5675              :                                 OutputProcessor::StoreType::Sum,
    5676         5201 :                                 zone.Name);
    5677        10402 :             SetupOutputVariable(state,
    5678              :                                 "Zone Oscillating Temperatures During Occupancy Time",
    5679              :                                 Constant::Units::hr,
    5680         5201 :                                 s_ztpc->ZoneTempOscillateDuringOccupancy(iZone),
    5681              :                                 OutputProcessor::TimeStepType::System,
    5682              :                                 OutputProcessor::StoreType::Sum,
    5683         5201 :                                 zone.Name);
    5684        10402 :             SetupOutputVariable(state,
    5685              :                                 "Zone Oscillating Temperatures in Deadband Time",
    5686              :                                 Constant::Units::hr,
    5687         5201 :                                 s_ztpc->ZoneTempOscillateInDeadband(iZone),
    5688              :                                 OutputProcessor::TimeStepType::System,
    5689              :                                 OutputProcessor::StoreType::Sum,
    5690         5201 :                                 zone.Name);
    5691              :         }
    5692              :         // set up a variable covering all zones
    5693         3204 :         SetupOutputVariable(state,
    5694              :                             "Facility Any Zone Oscillating Temperatures Time",
    5695              :                             Constant::Units::hr,
    5696          801 :                             s_ztpc->AnyZoneTempOscillate,
    5697              :                             OutputProcessor::TimeStepType::System,
    5698              :                             OutputProcessor::StoreType::Sum,
    5699              :                             "Facility");
    5700         3204 :         SetupOutputVariable(state,
    5701              :                             "Facility Any Zone Oscillating Temperatures During Occupancy Time",
    5702              :                             Constant::Units::hr,
    5703          801 :                             s_ztpc->AnyZoneTempOscillateDuringOccupancy,
    5704              :                             OutputProcessor::TimeStepType::System,
    5705              :                             OutputProcessor::StoreType::Sum,
    5706              :                             "Facility");
    5707         3204 :         SetupOutputVariable(state,
    5708              :                             "Facility Any Zone Oscillating Temperatures in Deadband Time",
    5709              :                             Constant::Units::hr,
    5710          801 :                             s_ztpc->AnyZoneTempOscillateInDeadband,
    5711              :                             OutputProcessor::TimeStepType::System,
    5712              :                             OutputProcessor::StoreType::Sum,
    5713              :                             "Facility");
    5714              :         // test if the oscillation variables are even used
    5715         2403 :         if (ReportingThisVariable(state, "Zone Oscillating Temperatures Time") ||
    5716         2403 :             ReportingThisVariable(state, "Zone Oscillating Temperatures During Occupancy Time") ||
    5717         2403 :             ReportingThisVariable(state, "Zone Oscillating Temperatures in Deadband Time") ||
    5718         2403 :             ReportingThisVariable(state, "Facility Any Zone Oscillating Temperatures Time") ||
    5719         4806 :             ReportingThisVariable(state, "Facility Any Zone Oscillating Temperatures During Occupancy Time") ||
    5720         2403 :             ReportingThisVariable(state, "Facility Any Zone Oscillating Temperatures in Deadband Time")) {
    5721            0 :             s_ztpc->OscillationVariablesNeeded = true;
    5722              :         }
    5723          801 :         s_ztpc->SetupOscillationOutputFlag = false;
    5724              :     }
    5725              : 
    5726      3588830 :     Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    5727      3588830 :     if (s_ztpc->OscillationVariablesNeeded) {
    5728              :         // precalc the negative value for performance
    5729        27408 :         Real64 NegOscillateMagnitude = -HVAC::OscillateMagnitude;
    5730              :         // assume no zone is oscillating
    5731        27408 :         bool isAnyZoneOscillating = false;
    5732        27408 :         bool isAnyZoneOscillatingDuringOccupancy = false;
    5733        27408 :         bool isAnyZoneOscillatingInDeadband = false;
    5734              : 
    5735       378643 :         for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    5736       351235 :             bool isOscillate = false;
    5737       351235 :             s_ztpc->ZoneTempHist(4, iZone) = s_ztpc->ZoneTempHist(3, iZone);
    5738       351235 :             s_ztpc->ZoneTempHist(3, iZone) = s_ztpc->ZoneTempHist(2, iZone);
    5739       351235 :             s_ztpc->ZoneTempHist(2, iZone) = s_ztpc->ZoneTempHist(1, iZone);
    5740       351235 :             s_ztpc->ZoneTempHist(1, iZone) = s_ztpc->zoneHeatBalance(iZone).ZT;
    5741       351235 :             Real64 Diff34 = s_ztpc->ZoneTempHist(3, iZone) - s_ztpc->ZoneTempHist(4, iZone);
    5742       351235 :             Real64 Diff23 = s_ztpc->ZoneTempHist(2, iZone) - s_ztpc->ZoneTempHist(3, iZone);
    5743       351235 :             Real64 Diff12 = s_ztpc->ZoneTempHist(1, iZone) - s_ztpc->ZoneTempHist(2, iZone);
    5744              :             // roll out the conditionals for increased performance
    5745       351235 :             if (Diff12 > HVAC::OscillateMagnitude) {
    5746        62909 :                 if (Diff23 < NegOscillateMagnitude) {
    5747        11102 :                     if (Diff34 > HVAC::OscillateMagnitude) {
    5748         2750 :                         isOscillate = true;
    5749              :                     }
    5750              :                 }
    5751              :             }
    5752              :             // now try the opposite sequence of swings
    5753       351235 :             if (Diff12 < NegOscillateMagnitude) {
    5754        59455 :                 if (Diff23 > HVAC::OscillateMagnitude) {
    5755         7703 :                     if (Diff34 < NegOscillateMagnitude) {
    5756         2397 :                         isOscillate = true;
    5757              :                     }
    5758              :                 }
    5759              :             }
    5760       351235 :             s_ztpc->ZoneTempOscillateDuringOccupancy(iZone) = 0.0;
    5761       351235 :             s_ztpc->ZoneTempOscillateInDeadband(iZone) = 0.0;
    5762       351235 :             if (isOscillate) {
    5763         5147 :                 s_ztpc->ZoneTempOscillate(iZone) = TimeStepSys;
    5764         5147 :                 isAnyZoneOscillating = true;
    5765         5147 :                 if (allocated(state.dataThermalComforts->ThermalComfortInASH55)) {
    5766         5147 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    5767          372 :                         s_ztpc->ZoneTempOscillateDuringOccupancy(iZone) = TimeStepSys;
    5768          372 :                         isAnyZoneOscillatingDuringOccupancy = true;
    5769              :                     }
    5770              :                 }
    5771         5147 :                 if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(iZone)) {
    5772          884 :                     s_ztpc->ZoneTempOscillateInDeadband(iZone) = TimeStepSys;
    5773          884 :                     isAnyZoneOscillatingInDeadband = true;
    5774              :                 }
    5775              :             } else {
    5776       346088 :                 s_ztpc->ZoneTempOscillate(iZone) = 0.0;
    5777              :             }
    5778              :         }
    5779              :         // any zone variable
    5780        27408 :         s_ztpc->AnyZoneTempOscillate = (isAnyZoneOscillating) ? TimeStepSys : 0.0;
    5781        27408 :         s_ztpc->AnyZoneTempOscillateDuringOccupancy = (isAnyZoneOscillatingDuringOccupancy) ? TimeStepSys : 0.0;
    5782        27408 :         s_ztpc->AnyZoneTempOscillateInDeadband = (isAnyZoneOscillatingInDeadband) ? TimeStepSys : 0.0;
    5783              : 
    5784              :         // annual/runperiod sum for _perflog.csv file
    5785        27408 :         s_ztpc->AnnualAnyZoneTempOscillate += s_ztpc->AnyZoneTempOscillate;
    5786        27408 :         s_ztpc->AnnualAnyZoneTempOscillateDuringOccupancy += s_ztpc->AnyZoneTempOscillateDuringOccupancy;
    5787        27408 :         s_ztpc->AnnualAnyZoneTempOscillateInDeadband += s_ztpc->AnyZoneTempOscillateInDeadband;
    5788              :     }
    5789      3588830 : }
    5790              : 
    5791     31204152 : void AdjustAirSetPointsforOpTempCntrl(EnergyPlusData &state, int const TempControlledZoneID, int const ActualZoneNum, Real64 &ZoneAirSetPoint)
    5792              : {
    5793              : 
    5794              :     // SUBROUTINE INFORMATION:
    5795              :     //       AUTHOR         B. Griffith
    5796              :     //       DATE WRITTEN   June 2006
    5797              : 
    5798              :     // PURPOSE OF THIS SUBROUTINE:
    5799              :     // This subroutine modifies the air temperature setpoint to effect operative temperature control
    5800              : 
    5801              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5802     31204152 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5803              :     Real64 thisMRTFraction; // local variable for fraction that MRT is in Op Temp definition
    5804              : 
    5805     31204152 :     if (!(state.dataZoneCtrls->AnyOpTempControl)) {
    5806     31171008 :         return; // do nothing to setpoint
    5807              :     }
    5808              : 
    5809        33144 :     auto &tempControlledZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneID);
    5810        33144 :     if (tempControlledZone.OpTempCtrl == DataZoneControls::TempCtrl::None) {
    5811            0 :         return; // do nothing to setpoint
    5812              :     }
    5813              : 
    5814              :     // is operative temp radiative fraction scheduled or fixed?
    5815        66288 :     thisMRTFraction = (tempControlledZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled)
    5816        33144 :                           ? tempControlledZone.opTempRadiativeFractionSched->getCurrentVal()
    5817              :                           : tempControlledZone.FixedRadiativeFraction;
    5818              : 
    5819              :     // get mean radiant temperature for zone
    5820        33144 :     Real64 thisMRT = s_ztpc->zoneHeatBalance(ActualZoneNum).MRT;
    5821              : 
    5822              :     // modify setpoint for operative temperature control
    5823              :     //  trapping for MRT fractions between 0.0 and 0.9 during get input, so shouldn't be able to divide by zero here.
    5824        33144 :     ZoneAirSetPoint = (ZoneAirSetPoint - thisMRTFraction * thisMRT) / (1.0 - thisMRTFraction);
    5825              : }
    5826              : 
    5827         6765 : void AdjustOperativeSetPointsforAdapComfort(EnergyPlusData &state, int const TempControlledZoneID, Real64 &ZoneAirSetPoint)
    5828              : {
    5829              :     // SUBROUTINE INFORMATION:
    5830              :     //       AUTHOR         Xuan Luo
    5831              :     //       DATE WRITTEN   Jan 2017
    5832              : 
    5833              :     // PURPOSE OF THIS SUBROUTINE:
    5834              :     // This routine adjust the operative setpoints for each controlled adaptive thermal comfort models.
    5835              : 
    5836         6765 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5837         6765 :     auto const &tempControlledZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneID);
    5838         6765 :     auto const &AdapComfortDailySetPointSchedule = s_ztpc->AdapComfortDailySetPointSchedule;
    5839              : 
    5840              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5841         6765 :     int originZoneAirSetPoint = ZoneAirSetPoint;
    5842         6765 :     int AdaptiveComfortModelTypeIndex = tempControlledZone.AdaptiveComfortModelTypeIndex;
    5843              : 
    5844              :     // adjust zone operative setpoint
    5845         6765 :     if (!(tempControlledZone.AdaptiveComfortTempControl)) {
    5846            0 :         return; // do nothing to setpoint
    5847              :     }
    5848         6780 :     if ((state.dataWeather->Environment(state.dataWeather->Envrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
    5849           15 :         (state.dataWeather->Environment(state.dataWeather->Envrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
    5850              :         // Adjust run period cooling set point
    5851           15 :         switch (AdaptiveComfortModelTypeIndex) {
    5852           15 :         case static_cast<int>(AdaptiveComfortModel::ASH55_CENTRAL):
    5853           15 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central(state.dataEnvrn->DayOfYear);
    5854           15 :             break;
    5855            0 :         case static_cast<int>(AdaptiveComfortModel::ASH55_UPPER_90):
    5856            0 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_90(state.dataEnvrn->DayOfYear);
    5857            0 :             break;
    5858            0 :         case static_cast<int>(AdaptiveComfortModel::ASH55_UPPER_80):
    5859            0 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_80(state.dataEnvrn->DayOfYear);
    5860            0 :             break;
    5861            0 :         case static_cast<int>(AdaptiveComfortModel::CEN15251_CENTRAL):
    5862            0 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central(state.dataEnvrn->DayOfYear);
    5863            0 :             break;
    5864            0 :         case static_cast<int>(AdaptiveComfortModel::CEN15251_UPPER_I):
    5865            0 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I(state.dataEnvrn->DayOfYear);
    5866            0 :             break;
    5867            0 :         case static_cast<int>(AdaptiveComfortModel::CEN15251_UPPER_II):
    5868            0 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II(state.dataEnvrn->DayOfYear);
    5869            0 :             break;
    5870            0 :         case static_cast<int>(AdaptiveComfortModel::CEN15251_UPPER_III):
    5871            0 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_III(state.dataEnvrn->DayOfYear);
    5872            0 :             break;
    5873            0 :         default:
    5874            0 :             break;
    5875              :         }
    5876              :     } else {
    5877         6750 :         int const envrnDayNum(state.dataWeather->Environment(state.dataWeather->Envrn).DesignDayNum);
    5878         6750 :         int constexpr summerDesignDayTypeIndex(9);
    5879              :         // Adjust summer design day set point
    5880         6750 :         if (state.dataWeather->DesDayInput(envrnDayNum).DayType == summerDesignDayTypeIndex) {
    5881         6750 :             ZoneAirSetPoint = s_ztpc->AdapComfortSetPointSummerDesDay[AdaptiveComfortModelTypeIndex - 2];
    5882              :         }
    5883              :     }
    5884              :     // If adaptive operative temperature not applicable, set back
    5885         6765 :     if (ZoneAirSetPoint < originZoneAirSetPoint) {
    5886           15 :         ZoneAirSetPoint = originZoneAirSetPoint;
    5887              :     }
    5888              :     // If meet fault flag, set back
    5889         6765 :     if (ZoneAirSetPoint == -1) {
    5890            0 :         ZoneAirSetPoint = originZoneAirSetPoint;
    5891              :     }
    5892              : }
    5893              : 
    5894         2025 : void CalcZoneAirComfortSetPoints(EnergyPlusData &state)
    5895              : {
    5896              : 
    5897              :     // SUBROUTINE INFORMATION:
    5898              :     //       AUTHOR         Lixing Gu
    5899              :     //       DATE WRITTEN   May 2006
    5900              : 
    5901              :     // PURPOSE OF THIS SUBROUTINE:
    5902              :     // This routine sets the thermal comfort setpoints for each controlled zone based on air temperature obtained from thermal comfort models.
    5903              : 
    5904              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5905         2025 :     Real64 SetPointLo = 0.0;
    5906         2025 :     Real64 SetPointHi = 0.0;
    5907         2025 :     Real64 Tset = 0.0;
    5908         2025 :     int ObjectCount = 0;
    5909         2025 :     Real64 PeopleCount = 0.0;
    5910              : 
    5911         2025 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5912         2025 :     auto &s_hbfs = state.dataHeatBalFanSys;
    5913              :     // Call thermal comfort module to read zone control comfort object
    5914         2025 :     if (s_ztpc->CalcZoneAirComfortSetPointsFirstTimeFlag) {
    5915            1 :         ThermalComfort::ManageThermalComfort(state, true);
    5916            1 :         s_ztpc->CalcZoneAirComfortSetPointsFirstTimeFlag = false;
    5917              :     }
    5918              : 
    5919         2025 :     s_hbfs->ComfortControlType = HVAC::SetptType::Uncontrolled; // Default
    5920              : 
    5921         4050 :     for (int RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++RelativeZoneNum) {
    5922              : 
    5923         2025 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(RelativeZoneNum);
    5924         2025 :         int ActualZoneNum = comfortZone.ActualZoneNum;
    5925         2025 :         auto &zone = state.dataHeatBal->Zone(ActualZoneNum);
    5926         2025 :         auto &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ActualZoneNum);
    5927         2025 :         auto &zoneComfortControlsFanger = state.dataHeatBalFanSys->ZoneComfortControlsFanger(ActualZoneNum);
    5928         2025 :         s_hbfs->ComfortControlType(ActualZoneNum) = static_cast<HVAC::SetptType>(comfortZone.setptTypeSched->getCurrentVal());
    5929         2025 :         s_hbfs->ComfortControlTypeRpt(ActualZoneNum) = (int)s_hbfs->ComfortControlType(ActualZoneNum);
    5930              : 
    5931              :         // Get PMV values
    5932         2025 :         switch (s_hbfs->ComfortControlType(ActualZoneNum)) {
    5933         1185 :         case HVAC::SetptType::Uncontrolled: {
    5934         1185 :             zoneComfortControlsFanger.LowPMV = -999.0;
    5935         1185 :             zoneComfortControlsFanger.HighPMV = -999.0;
    5936         1185 :         } break;
    5937              : 
    5938          210 :         case HVAC::SetptType::SingleHeat: {
    5939          210 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::SingleHeat;
    5940          210 :             zoneComfortControlsFanger.LowPMV = comfortZone.setpts[(int)HVAC::SetptType::SingleHeat].heatSetptSched->getCurrentVal();
    5941          210 :             zoneComfortControlsFanger.HighPMV = -999.0;
    5942          210 :         } break;
    5943              : 
    5944          210 :         case HVAC::SetptType::SingleCool: {
    5945          210 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::SingleCool;
    5946          210 :             zoneComfortControlsFanger.LowPMV = -999.0;
    5947          210 :             zoneComfortControlsFanger.HighPMV = comfortZone.setpts[(int)HVAC::SetptType::SingleCool].coolSetptSched->getCurrentVal();
    5948          210 :         } break;
    5949              : 
    5950          210 :         case HVAC::SetptType::SingleHeatCool: {
    5951          210 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::SingleHeatCool;
    5952          210 :             zoneComfortControlsFanger.LowPMV = zoneComfortControlsFanger.HighPMV =
    5953          210 :                 comfortZone.setpts[(int)HVAC::SetptType::SingleHeatCool].coolSetptSched->getCurrentVal();
    5954          210 :         } break;
    5955              : 
    5956          210 :         case HVAC::SetptType::DualHeatCool: {
    5957          210 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::DualHeatCool;
    5958          210 :             zoneComfortControlsFanger.LowPMV = comfortZone.setpts[(int)HVAC::SetptType::DualHeatCool].heatSetptSched->getCurrentVal();
    5959          210 :             zoneComfortControlsFanger.HighPMV = comfortZone.setpts[(int)HVAC::SetptType::DualHeatCool].coolSetptSched->getCurrentVal();
    5960          210 :             if (zoneComfortControlsFanger.LowPMV > zoneComfortControlsFanger.HighPMV) {
    5961            0 :                 ++zoneComfortControlsFanger.DualPMVErrCount;
    5962            0 :                 if (zoneComfortControlsFanger.DualPMVErrCount < 2) {
    5963            0 :                     ShowWarningError(state,
    5964            0 :                                      format("ThermostatSetpoint:ThermalComfort:Fanger:DualSetpoint: The heating PMV setpoint is above the "
    5965              :                                             "cooling PMV setpoint in {}",
    5966            0 :                                             comfortZone.setpts[(int)HVAC::SetptType::DualHeatCool].Name));
    5967            0 :                     ShowContinueError(state, "The zone dual heating PMV setpoint is set to the dual cooling PMV setpoint.");
    5968            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    5969              :                 } else {
    5970            0 :                     ShowRecurringWarningErrorAtEnd(state,
    5971              :                                                    "The heating PMV setpoint is still above the cooling PMV setpoint",
    5972            0 :                                                    zoneComfortControlsFanger.DualPMVErrIndex,
    5973            0 :                                                    zoneComfortControlsFanger.LowPMV,
    5974            0 :                                                    zoneComfortControlsFanger.LowPMV);
    5975              :                 }
    5976            0 :                 zoneComfortControlsFanger.LowPMV = zoneComfortControlsFanger.HighPMV;
    5977              :             }
    5978          210 :         } break;
    5979              : 
    5980            0 :         default: {
    5981            0 :             ShowSevereError(state,
    5982            0 :                             format("CalcZoneAirTempSetpoints: Illegal thermal control control type for Zone={}, Found value={}, in Schedule={}",
    5983            0 :                                    zone.Name,
    5984            0 :                                    s_hbfs->ComfortControlTypeRpt(ActualZoneNum),
    5985            0 :                                    comfortZone.setptTypeSched->Name));
    5986            0 :         } break;
    5987              :         } // switch
    5988              : 
    5989              :         // Check Average method
    5990         2025 :         switch (comfortZone.averageMethod) {
    5991         2025 :         case DataZoneControls::AverageMethod::NO: {
    5992         2025 :             int PeopleNum = comfortZone.SpecificObjectNum;
    5993         2025 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::SingleCool) {
    5994          210 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, SetPointLo);
    5995              :             } else {
    5996         1815 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.LowPMV, SetPointLo);
    5997              :             }
    5998         2025 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    5999          210 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, SetPointHi);
    6000              :             }
    6001         2025 :         } break;
    6002              : 
    6003            0 :         case DataZoneControls::AverageMethod::SPE: {
    6004            0 :             int PeopleNum = comfortZone.SpecificObjectNum;
    6005            0 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::SingleCool) {
    6006            0 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, SetPointLo);
    6007              :             } else {
    6008            0 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.LowPMV, SetPointLo);
    6009              :             }
    6010            0 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6011            0 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, SetPointHi);
    6012              :             }
    6013            0 :         } break;
    6014              : 
    6015            0 :         case DataZoneControls::AverageMethod::OBJ: {
    6016            0 :             SetPointLo = 0.0;
    6017            0 :             SetPointHi = 0.0;
    6018            0 :             for (int peopleNum = 1; peopleNum <= state.dataHeatBal->TotPeople; ++peopleNum) {
    6019            0 :                 if (ActualZoneNum == state.dataHeatBal->People(peopleNum).ZonePtr) {
    6020            0 :                     ++ObjectCount;
    6021            0 :                     GetComfortSetPoints(state, peopleNum, RelativeZoneNum, zoneComfortControlsFanger.LowPMV, Tset);
    6022            0 :                     SetPointLo += Tset;
    6023              : 
    6024            0 :                     if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6025            0 :                         GetComfortSetPoints(state, peopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, Tset);
    6026            0 :                         SetPointHi += Tset;
    6027              :                     }
    6028              :                 }
    6029              :             }
    6030            0 :             SetPointLo /= ObjectCount;
    6031            0 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6032            0 :                 SetPointHi /= ObjectCount;
    6033              :             }
    6034            0 :         } break;
    6035              : 
    6036            0 :         case DataZoneControls::AverageMethod::PEO: {
    6037            0 :             SetPointLo = 0.0;
    6038            0 :             SetPointHi = 0.0;
    6039              : 
    6040            0 :             for (int PeopleNum = 1; PeopleNum <= state.dataHeatBal->TotPeople; ++PeopleNum) {
    6041            0 :                 auto &people = state.dataHeatBal->People(PeopleNum);
    6042            0 :                 if (ActualZoneNum == people.ZonePtr) {
    6043            0 :                     int NumberOccupants = people.NumberOfPeople * people.sched->getCurrentVal();
    6044            0 :                     PeopleCount += NumberOccupants;
    6045            0 :                     GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.LowPMV, Tset);
    6046            0 :                     SetPointLo += Tset * NumberOccupants;
    6047            0 :                     if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6048            0 :                         GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, Tset);
    6049            0 :                         SetPointHi += Tset * NumberOccupants;
    6050              :                     }
    6051              :                 }
    6052              :             }
    6053            0 :             if (PeopleCount > 0) {
    6054            0 :                 SetPointLo /= PeopleCount;
    6055            0 :                 if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6056            0 :                     SetPointHi /= PeopleCount;
    6057              :                 }
    6058              :             } else {
    6059            0 :                 if (comfortZone.PeopleAverageErrIndex == 0) {
    6060            0 :                     ShowWarningMessage(state,
    6061            0 :                                        format("ZoneControl:Thermostat:ThermalComfort: The total number of people in Zone = {} is zero. The People "
    6062              :                                               "Average option is not used.",
    6063            0 :                                               zone.Name));
    6064            0 :                     ShowContinueError(state, "The Object Average option is used instead. Simulation continues .....");
    6065            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    6066              :                 }
    6067            0 :                 ShowRecurringWarningErrorAtEnd(state,
    6068            0 :                                                "ZoneControl:Thermostat:ThermalComfort: The total number of people in Zone = " + zone.Name +
    6069              :                                                    " is still zero. The People Average option is not used",
    6070            0 :                                                comfortZone.PeopleAverageErrIndex,
    6071              :                                                PeopleCount,
    6072              :                                                PeopleCount);
    6073            0 :                 SetPointLo = 0.0;
    6074            0 :                 SetPointHi = 0.0;
    6075            0 :                 for (int peopleNum = 1; peopleNum <= state.dataHeatBal->TotPeople; ++peopleNum) {
    6076            0 :                     if (ActualZoneNum == state.dataHeatBal->People(peopleNum).ZonePtr) {
    6077            0 :                         ++ObjectCount;
    6078            0 :                         GetComfortSetPoints(state, peopleNum, RelativeZoneNum, zoneComfortControlsFanger.LowPMV, Tset);
    6079            0 :                         SetPointLo += Tset;
    6080            0 :                         if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6081            0 :                             GetComfortSetPoints(state, peopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, Tset);
    6082            0 :                             SetPointHi += Tset;
    6083              :                         }
    6084              :                     }
    6085              :                 }
    6086            0 :                 SetPointLo /= ObjectCount;
    6087            0 :                 if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    6088            0 :                     SetPointHi /= ObjectCount;
    6089              :                 }
    6090              :             }
    6091            0 :         } break;
    6092              : 
    6093            0 :         default: {
    6094            0 :         } break;
    6095              :         } // switch
    6096              : 
    6097              :         // Assign setpoint
    6098         2025 :         switch (s_hbfs->ComfortControlType(ActualZoneNum)) {
    6099              : 
    6100         1185 :         case HVAC::SetptType::Uncontrolled: {
    6101         1185 :             switch (state.dataHeatBalFanSys->TempControlType(ActualZoneNum)) {
    6102          594 :             case HVAC::SetptType::SingleHeat:
    6103          594 :                 zoneTstatSetpt.setptHi = 0.0;
    6104          594 :                 break;
    6105          591 :             case HVAC::SetptType::SingleCool:
    6106          591 :                 zoneTstatSetpt.setptLo = 0.0;
    6107          591 :                 break;
    6108            0 :             default:
    6109            0 :                 break;
    6110              :             }
    6111         1185 :         } break;
    6112              : 
    6113          210 :         case HVAC::SetptType::SingleHeat: {
    6114          210 :             if (SetPointLo < comfortZone.TdbMinSetPoint) {
    6115            0 :                 SetPointLo = comfortZone.TdbMinSetPoint;
    6116            0 :                 if (comfortZone.TdbMinErrIndex < 2) {
    6117            0 :                     ShowWarningMessage(state,
    6118            0 :                                        format("ThermostatSetpoint:ThermalComfort:Fanger:SingleHeating temperature is below the Minimum dry-bulb "
    6119              :                                               "temperature setpoint {}",
    6120            0 :                                               comfortZone.Name));
    6121            0 :                     ShowContinueError(state, "The zone heating setpoint is set to the Minimum dry-bulb temperature setpoint");
    6122            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    6123              :                 }
    6124            0 :                 ShowRecurringWarningErrorAtEnd(state,
    6125              :                                                "ThermostatSetpoint:ThermalComfort:Fanger:SingleHeating temperature is still below the "
    6126              :                                                "Minimum dry-bulb temperature setpoint ...",
    6127            0 :                                                comfortZone.TdbMinErrIndex,
    6128              :                                                SetPointLo,
    6129              :                                                SetPointLo);
    6130              :             }
    6131          210 :             zoneTstatSetpt.setpt = SetPointLo;
    6132          210 :             zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    6133          210 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::SingleHeat;
    6134          210 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6135          210 :         } break;
    6136              : 
    6137          210 :         case HVAC::SetptType::SingleCool: {
    6138          210 :             if (SetPointLo > comfortZone.TdbMaxSetPoint) {
    6139            0 :                 SetPointLo = comfortZone.TdbMaxSetPoint;
    6140            0 :                 if (comfortZone.TdbMaxErrIndex == 0) {
    6141            0 :                     ShowWarningMessage(state,
    6142            0 :                                        format("ThermostatSetpoint:ThermalComfort:Fanger:SingleCooling temperature is above the Maximum dry-bulb "
    6143              :                                               "temperature setpoint {}",
    6144            0 :                                               comfortZone.Name));
    6145            0 :                     ShowContinueError(state, "The zone cooling setpoint is set to the Maximum dry-bulb temperature setpoint");
    6146            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    6147              :                 }
    6148            0 :                 ShowRecurringWarningErrorAtEnd(state,
    6149              :                                                "ThermostatSetpoint:ThermalComfort:Fanger:SingleCooling temperature is still above the "
    6150              :                                                "Maximum dry-bulb temperature setpoint ...",
    6151            0 :                                                comfortZone.TdbMaxErrIndex,
    6152              :                                                SetPointLo,
    6153              :                                                SetPointLo);
    6154              :             }
    6155          210 :             zoneTstatSetpt.setpt = SetPointLo;
    6156          210 :             zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    6157          210 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::SingleCool;
    6158          210 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6159          210 :         } break;
    6160              : 
    6161          210 :         case HVAC::SetptType::SingleHeatCool: {
    6162          210 :             if (comfortZone.TdbMaxSetPoint == comfortZone.TdbMinSetPoint) {
    6163            0 :                 SetPointLo = comfortZone.TdbMaxSetPoint;
    6164              :             }
    6165          210 :             if (SetPointLo > comfortZone.TdbMaxSetPoint) {
    6166            0 :                 SetPointLo = comfortZone.TdbMaxSetPoint;
    6167              :             }
    6168          210 :             if (SetPointLo < comfortZone.TdbMinSetPoint) {
    6169            0 :                 SetPointLo = comfortZone.TdbMinSetPoint;
    6170              :             }
    6171          210 :             if (SetPointLo < comfortZone.TdbMinSetPoint || SetPointLo > comfortZone.TdbMaxSetPoint) {
    6172            0 :                 if (comfortZone.TdbHCErrIndex == 0) {
    6173            0 :                     ShowWarningMessage(state,
    6174            0 :                                        format("ThermostatSetpoint:ThermalComfort:Fanger:SingleHeatingOrCooling temperature is above the Maximum or "
    6175              :                                               "below the Minimum dry-bulb temperature setpoint {}",
    6176            0 :                                               comfortZone.Name));
    6177            0 :                     ShowContinueError(state,
    6178              :                                       "The zone setpoint is set to the Maximum dry-bulb temperature setpoint if above or the Minimum "
    6179              :                                       "dry-bulb temperature setpoint if below");
    6180            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    6181              :                 }
    6182            0 :                 ShowRecurringWarningErrorAtEnd(state,
    6183              :                                                "ThermostatSetpoint:ThermalComfort:Fanger:SingleHeatingOrCooling temperature is still beyond "
    6184              :                                                "the range between Maximum and Minimum dry-bulb temperature setpoint ...",
    6185            0 :                                                comfortZone.TdbHCErrIndex,
    6186              :                                                SetPointLo,
    6187              :                                                SetPointLo);
    6188              :             }
    6189              : 
    6190          210 :             zoneTstatSetpt.setpt = SetPointLo;
    6191          210 :             zoneTstatSetpt.setptHi = zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    6192          210 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::SingleHeatCool;
    6193          210 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6194          210 :         } break;
    6195              : 
    6196          210 :         case HVAC::SetptType::DualHeatCool: {
    6197          210 :             if (SetPointLo < comfortZone.TdbMinSetPoint) {
    6198            0 :                 SetPointLo = comfortZone.TdbMinSetPoint;
    6199              : 
    6200            0 :                 if (comfortZone.TdbDualMinErrIndex == 0) {
    6201            0 :                     ShowWarningMessage(state,
    6202            0 :                                        format("ThermostatSetpoint:ThermalComfort:Fanger:DualSetpoint temperature is below the Minimum dry-bulb "
    6203              :                                               "temperature setpoint {}",
    6204            0 :                                               comfortZone.Name));
    6205            0 :                     ShowContinueError(state, "The zone dual heating setpoint is set to the Minimum dry-bulb temperature setpoint");
    6206            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    6207              :                 }
    6208            0 :                 ShowRecurringWarningErrorAtEnd(state,
    6209              :                                                "ThermostatSetpoint:ThermalComfort:Fanger:DualSetpoint temperature is still below the Minimum "
    6210              :                                                "dry-bulb temperature setpoint ...",
    6211            0 :                                                comfortZone.TdbDualMinErrIndex,
    6212              :                                                SetPointLo,
    6213              :                                                SetPointLo);
    6214              :             }
    6215          210 :             if (SetPointHi > comfortZone.TdbMaxSetPoint) {
    6216            0 :                 SetPointHi = comfortZone.TdbMaxSetPoint;
    6217            0 :                 if (comfortZone.TdbDualMaxErrIndex == 0) {
    6218            0 :                     ShowWarningMessage(state,
    6219            0 :                                        format("ThermostatSetpoint:ThermalComfort:Fanger:DualSetpoint temperature is above the Maximum dry-bulb "
    6220              :                                               "temperature setpoint in zone = {}",
    6221            0 :                                               comfortZone.Name));
    6222            0 :                     ShowContinueError(state, "The zone dual cooling setpoint is set to the Maximum dry-bulb temperature setpoint");
    6223            0 :                     ShowContinueErrorTimeStamp(state, "Occurrence info:");
    6224              :                 }
    6225            0 :                 ShowRecurringWarningErrorAtEnd(state,
    6226              :                                                "ThermostatSetpoint:ThermalComfort:Fanger:DualSetpoint temperature is still above the Maximum "
    6227              :                                                "dry-bulb temperature setpoint ...",
    6228            0 :                                                comfortZone.TdbDualMaxErrIndex,
    6229              :                                                SetPointLo,
    6230              :                                                SetPointLo);
    6231              :             }
    6232              : 
    6233          210 :             zoneTstatSetpt.setptLo = SetPointLo;
    6234          210 :             zoneTstatSetpt.setptHi = SetPointHi;
    6235          210 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::DualHeatCool;
    6236          210 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6237          210 :         } break;
    6238              : 
    6239            0 :         default: {
    6240            0 :             ShowSevereError(state,
    6241            0 :                             format("CalcZoneAirComfortSetpoints: Illegal thermal control control type for Zone={}, Found value={}, in Schedule={}",
    6242            0 :                                    zone.Name,
    6243            0 :                                    s_hbfs->ComfortControlTypeRpt(ActualZoneNum),
    6244            0 :                                    comfortZone.setptTypeSched->Name));
    6245            0 :         } break;
    6246              :         } // switch ()
    6247              :     }
    6248         2025 : } // CalcZoneAirComfortSetpoints()
    6249              : 
    6250         2235 : void GetComfortSetPoints(EnergyPlusData &state,
    6251              :                          int const PeopleNum,
    6252              :                          int const ComfortControlNum,
    6253              :                          Real64 const PMVSet,
    6254              :                          Real64 &Tset // drybulb setpoint temperature for a given PMV value
    6255              : )
    6256              : {
    6257              : 
    6258              :     // SUBROUTINE INFORMATION:
    6259              :     //       AUTHOR         Lixing Gu
    6260              :     //       DATE WRITTEN   May, 2006
    6261              :     // PURPOSE OF THIS SUBROUTINE:
    6262              :     // This routine sets what the thermal comfort setpoints for each controlled zone should be based on air temperature
    6263              :     // obtained from thermal comfort models. This is called each time step.
    6264              : 
    6265              :     // SUBROUTINE ARGUMENT DEFINITIONS:
    6266              :     // 0 = Solution; 1 = Set to Min; 2 Set to Max
    6267              : 
    6268              :     // SUBROUTINE PARAMETER DEFINITIONS:
    6269         2235 :     Real64 constexpr Acc(0.001); // accuracy control for SolveRoot
    6270         2235 :     int constexpr MaxIter(500);  // iteration control for SolveRoot
    6271              : 
    6272              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    6273         2235 :     Real64 PMVResult = 0.0; // Calculated PMV value
    6274              : 
    6275         2235 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    6276              : 
    6277         2235 :     auto &comfortControlledZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlNum);
    6278         2235 :     Real64 Tmin = comfortControlledZone.TdbMinSetPoint;
    6279         2235 :     Real64 Tmax = comfortControlledZone.TdbMaxSetPoint;
    6280              : 
    6281         2235 :     ThermalComfort::CalcThermalComfortFanger(state, PeopleNum, Tmin, PMVResult);
    6282         2235 :     Real64 PMVMin = PMVResult;
    6283         2235 :     ThermalComfort::CalcThermalComfortFanger(state, PeopleNum, Tmax, PMVResult);
    6284         2235 :     Real64 PMVMax = PMVResult;
    6285         2235 :     if (PMVSet > PMVMin && PMVSet < PMVMax) {
    6286              : 
    6287         5281 :         auto f = [&state, PMVSet, PeopleNum](Real64 Tset) {
    6288         5281 :             Real64 PMVresult = 0.0; // resulting PMV values
    6289         5281 :             ThermalComfort::CalcThermalComfortFanger(state, PeopleNum, Tset, PMVresult);
    6290         5281 :             return (PMVSet - PMVresult);
    6291         1050 :         };
    6292              : 
    6293         1050 :         int SolFla = 0; // feed back flag from SolveRoot
    6294         1050 :         General::SolveRoot(state, Acc, MaxIter, SolFla, Tset, f, Tmin, Tmax);
    6295         1050 :         if (SolFla == -1) {
    6296            0 :             if (!state.dataGlobal->WarmupFlag) {
    6297            0 :                 ++s_ztpc->IterLimitExceededNum1;
    6298            0 :                 if (s_ztpc->IterLimitExceededNum1 == 1) {
    6299            0 :                     ShowWarningError(
    6300              :                         state,
    6301            0 :                         format("{}: Iteration limit exceeded calculating thermal comfort Fanger setpoint and non-converged setpoint is used",
    6302            0 :                                comfortControlledZone.Name));
    6303              :                 } else {
    6304            0 :                     ShowRecurringWarningErrorAtEnd(state,
    6305            0 :                                                    comfortControlledZone.Name + ":  Iteration limit exceeded calculating thermal comfort setpoint.",
    6306            0 :                                                    s_ztpc->IterLimitErrIndex1,
    6307              :                                                    Tset,
    6308              :                                                    Tset);
    6309              :                 }
    6310              :             }
    6311         1050 :         } else if (SolFla == -2) {
    6312            0 :             if (!state.dataGlobal->WarmupFlag) {
    6313            0 :                 ++s_ztpc->IterLimitExceededNum2;
    6314            0 :                 if (s_ztpc->IterLimitExceededNum2 == 1) {
    6315            0 :                     ShowWarningError(
    6316              :                         state,
    6317            0 :                         format("{}: Solution is not found in calculating thermal comfort Fanger setpoint and the minimum setpoint is used",
    6318            0 :                                comfortControlledZone.Name));
    6319              :                 } else {
    6320            0 :                     ShowRecurringWarningErrorAtEnd(
    6321              :                         state,
    6322            0 :                         format("{}:  Solution is not found in  calculating thermal comfort Fanger setpoint.", comfortControlledZone.Name),
    6323            0 :                         s_ztpc->IterLimitErrIndex2,
    6324              :                         Tset,
    6325              :                         Tset);
    6326              :                 }
    6327              :             }
    6328              :         }
    6329         2235 :     } else if (PMVSet < PMVMin) {
    6330         1185 :         Tset = Tmin;
    6331            0 :     } else if (PMVSet > PMVMax) {
    6332            0 :         Tset = Tmax;
    6333              :     }
    6334         2235 : }
    6335              : 
    6336     15569599 : void AdjustCoolingSetPointforTempAndHumidityControl(EnergyPlusData &state,
    6337              :                                                     int const TempControlledZoneID,
    6338              :                                                     int const ActualZoneNum // controlled zone actual zone number
    6339              : )
    6340              : {
    6341              :     // SUBROUTINE INFORMATION:
    6342              :     //       AUTHOR         Bereket A Nigusse, FSEC/UCF
    6343              :     //       DATE WRITTEN   Nov 2010
    6344              : 
    6345              :     // PURPOSE OF THIS SUBROUTINE:
    6346              :     //  This subroutine modifies the air cooling setpoint temperature to effect zone air Temperature and humidity control
    6347              :     //  Alter the zone air cooling setpoint if the zone air relative humidity value exceeds the the zone dehumidifying relative humidity setpoint.
    6348              : 
    6349     15569599 :     auto const &s_ztpc = state.dataZoneTempPredictorCorrector;
    6350     15569599 :     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneID);
    6351     15569599 :     auto &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ActualZoneNum);
    6352              : 
    6353     15569599 :     if (!state.dataZoneCtrls->AnyZoneTempAndHumidityControl) {
    6354     15562960 :         return; // do nothing to setpoint
    6355              :     }
    6356         6639 :     if (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::None) {
    6357            0 :         return; // do nothing to setpoint
    6358              :     }
    6359              : 
    6360         6639 :     Real64 ZoneOvercoolRange = (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::Scheduled) ? tempZone.zoneOvercoolRangeSched->getCurrentVal()
    6361         6639 :                                                                                                 : tempZone.ZoneOvercoolConstRange;
    6362              : 
    6363         6639 :     Real64 ZoneOvercoolControlRatio = tempZone.ZoneOvercoolControlRatio;
    6364              : 
    6365              :     // For Dual Setpoint thermostat the overcool range is limited by the temperature difference between cooling and heating setpoints
    6366         6639 :     Real64 MaxAllowedOvercoolRange = zoneTstatSetpt.setptHi - zoneTstatSetpt.setptLo;
    6367         6639 :     if (MaxAllowedOvercoolRange > 0.0) {
    6368         6639 :         ZoneOvercoolRange = min(ZoneOvercoolRange, MaxAllowedOvercoolRange);
    6369              :     }
    6370              :     // Calculate difference between zone air relative humidity and the dehumidifying setpoint
    6371         6639 :     Real64 RelativeHumidityDiff = s_ztpc->zoneHeatBalance(ActualZoneNum).airRelHum - tempZone.dehumidifyingSched->getCurrentVal();
    6372         6639 :     if (RelativeHumidityDiff > 0.0 && ZoneOvercoolControlRatio > 0.0) {
    6373              :         // proportionally reset the cooling setpoint temperature downward (zone Overcool)
    6374         4906 :         ZoneOvercoolRange = min(ZoneOvercoolRange, RelativeHumidityDiff / ZoneOvercoolControlRatio);
    6375         4906 :         zoneTstatSetpt.setptHi -= ZoneOvercoolRange;
    6376              :     }
    6377              : }
    6378              : 
    6379      2828450 : void OverrideAirSetPointsforEMSCntrl(EnergyPlusData &state)
    6380              : {
    6381              : 
    6382              :     // SUBROUTINE INFORMATION:
    6383              :     //       AUTHOR         L. Gu
    6384              :     //       DATE WRITTEN   June 2017
    6385              : 
    6386              :     // PURPOSE OF THIS SUBROUTINE:
    6387              :     // This subroutine overrides the air temperature setpoint based on EMS
    6388      2828450 :     auto const &s_hbfs = state.dataHeatBalFanSys;
    6389              : 
    6390     20391270 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumTempControlledZones; ++Loop) {
    6391     17562820 :         auto const &tempZone = state.dataZoneCtrls->TempControlledZone(Loop);
    6392     17562820 :         if (tempZone.EMSOverrideHeatingSetPointOn) {
    6393            0 :             int ZoneNum = tempZone.ActualZoneNum;
    6394            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6395              : 
    6396            0 :             switch (state.dataHeatBalFanSys->TempControlType(ZoneNum)) {
    6397            0 :             case HVAC::SetptType::SingleHeat:
    6398              :             case HVAC::SetptType::SingleHeatCool: {
    6399            0 :                 zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt = tempZone.EMSOverrideHeatingSetPointValue;
    6400            0 :             } break;
    6401              : 
    6402            0 :             case HVAC::SetptType::DualHeatCool: {
    6403            0 :                 zoneTstatSetpt.setptLo = tempZone.EMSOverrideHeatingSetPointValue;
    6404            0 :             } break;
    6405              : 
    6406            0 :             default: {
    6407            0 :             } break;
    6408              :             } // switch ()
    6409              :         }
    6410              : 
    6411     17562820 :         if (tempZone.EMSOverrideCoolingSetPointOn) {
    6412            0 :             int ZoneNum = tempZone.ActualZoneNum;
    6413            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6414              : 
    6415            0 :             switch (s_hbfs->TempControlType(ZoneNum)) {
    6416            0 :             case HVAC::SetptType::SingleCool:
    6417              :             case HVAC::SetptType::SingleHeatCool: {
    6418            0 :                 zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt = tempZone.EMSOverrideCoolingSetPointValue;
    6419            0 :             } break;
    6420              : 
    6421            0 :             case HVAC::SetptType::DualHeatCool: {
    6422            0 :                 zoneTstatSetpt.setptHi = tempZone.EMSOverrideCoolingSetPointValue;
    6423            0 :             } break;
    6424              : 
    6425            0 :             default: {
    6426            0 :             } break;
    6427              :             } // switch ()
    6428              :         }
    6429              :     }
    6430              : 
    6431      2830475 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumComfortControlledZones; ++Loop) {
    6432         2025 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(Loop);
    6433         2025 :         if (comfortZone.EMSOverrideHeatingSetPointOn) {
    6434            0 :             int ZoneNum = comfortZone.ActualZoneNum;
    6435            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6436              : 
    6437            0 :             switch (s_hbfs->ComfortControlType(ZoneNum)) {
    6438            0 :             case HVAC::SetptType::SingleHeat:
    6439              :             case HVAC::SetptType::SingleHeatCool: {
    6440            0 :                 zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt = comfortZone.EMSOverrideHeatingSetPointValue;
    6441            0 :             } break;
    6442              : 
    6443            0 :             case HVAC::SetptType::DualHeatCool: {
    6444            0 :                 zoneTstatSetpt.setptLo = comfortZone.EMSOverrideHeatingSetPointValue;
    6445            0 :             } break;
    6446              : 
    6447            0 :             default: {
    6448            0 :             } break;
    6449              : 
    6450              :             } // switch ()
    6451              :         }
    6452              : 
    6453         2025 :         if (comfortZone.EMSOverrideCoolingSetPointOn) {
    6454              : 
    6455            0 :             int ZoneNum = comfortZone.ActualZoneNum;
    6456            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6457              : 
    6458            0 :             switch (static_cast<HVAC::SetptType>(s_hbfs->ComfortControlType(ZoneNum))) {
    6459            0 :             case HVAC::SetptType::SingleCool:
    6460              :             case HVAC::SetptType::SingleHeatCool: {
    6461            0 :                 zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt = comfortZone.EMSOverrideCoolingSetPointValue;
    6462            0 :             } break;
    6463              : 
    6464            0 :             case HVAC::SetptType::DualHeatCool: {
    6465            0 :                 zoneTstatSetpt.setptHi = comfortZone.EMSOverrideCoolingSetPointValue;
    6466            0 :             } break;
    6467              : 
    6468            0 :             default: {
    6469            0 :             } break;
    6470              : 
    6471              :             } // switch ()
    6472              :         }
    6473              :     }
    6474      2828450 : }
    6475              : 
    6476              : // add values to the LEED tabular report related to schedules used by the thermostat objects
    6477          799 : void FillPredefinedTableOnThermostatSetpoints(EnergyPlusData &state)
    6478              : {
    6479              :     // J.Glazer - Aug 2017
    6480              :     using namespace OutputReportPredefined;
    6481          799 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    6482              : 
    6483          799 :     std::vector<int> uniqSch;
    6484          799 :     uniqSch.reserve(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat] + s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool] +
    6485          799 :                     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool] + s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool] * 2);
    6486              : 
    6487              :     Real64 setPointAt11;
    6488              :     Real64 setPointAt23;
    6489              :     int numDays;
    6490          799 :     std::string monthAssumed;
    6491          799 :     std::string monthAssumed2;
    6492              : 
    6493         1253 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeat]) {
    6494          454 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.heatSched->Num) != uniqSch.end()) {
    6495            9 :             continue;
    6496              :         }
    6497              : 
    6498          445 :         uniqSch.emplace_back(setpt.heatSched->Num);
    6499          445 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.heatSched->Name, setpt.Name);
    6500              : 
    6501          445 :         std::tie(setPointAt11, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 11);
    6502          445 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.heatSched->Name, setPointAt11);
    6503          445 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.heatSched->Name, numDays);
    6504              : 
    6505          445 :         std::tie(setPointAt23, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 23);
    6506          445 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.heatSched->Name, setPointAt23);
    6507          445 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.heatSched->Name, numDays);
    6508              : 
    6509          445 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.heatSched->Name, monthAssumed);
    6510              :     }
    6511              : 
    6512         1247 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleCool]) {
    6513          448 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.coolSched->Num) != uniqSch.end()) {
    6514           14 :             continue;
    6515              :         }
    6516              : 
    6517          434 :         uniqSch.emplace_back(setpt.coolSched->Num);
    6518          434 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.coolSched->Name, setpt.Name);
    6519              : 
    6520          434 :         std::tie(setPointAt11, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 11);
    6521          434 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.coolSched->Name, setPointAt11);
    6522          434 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.coolSched->Name, numDays);
    6523              : 
    6524          434 :         std::tie(setPointAt23, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 23);
    6525          434 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.coolSched->Name, setPointAt23);
    6526          434 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.coolSched->Name, numDays);
    6527              : 
    6528          434 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.coolSched->Name, monthAssumed);
    6529              :     }
    6530              : 
    6531          810 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeatCool]) {
    6532           11 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.heatSched->Num) != uniqSch.end()) {
    6533            0 :             continue;
    6534              :         }
    6535              : 
    6536           11 :         uniqSch.emplace_back(setpt.heatSched->Num);
    6537           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.heatSched->Name, setpt.Name);
    6538              : 
    6539           11 :         std::string schNm = setpt.heatSched->Name + " (summer)";
    6540           11 :         std::tie(setPointAt11, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 11);
    6541           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, schNm, setPointAt11);
    6542           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, schNm, numDays);
    6543              : 
    6544           11 :         std::tie(setPointAt23, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 23);
    6545           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, schNm, setPointAt23);
    6546           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, schNm, numDays);
    6547              : 
    6548           11 :         schNm = setpt.heatSched->Name + " (winter)";
    6549           11 :         std::tie(setPointAt11, numDays, monthAssumed2) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 11);
    6550           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, schNm, setPointAt11);
    6551           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, schNm, numDays);
    6552              : 
    6553           11 :         std::tie(setPointAt23, numDays, monthAssumed2) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 23);
    6554           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, schNm, setPointAt23);
    6555           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, schNm, numDays);
    6556              : 
    6557           11 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.heatSched->Name, monthAssumed + " and " + monthAssumed2);
    6558           11 :     }
    6559              : 
    6560         3390 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::DualHeatCool]) {
    6561         2591 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.heatSched->Num) == uniqSch.end()) {
    6562          491 :             uniqSch.emplace_back(setpt.heatSched->Num);
    6563          491 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.heatSched->Name, setpt.Name);
    6564              : 
    6565          491 :             std::tie(setPointAt11, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 11);
    6566          491 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.heatSched->Name, setPointAt11);
    6567          491 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.heatSched->Name, numDays);
    6568              : 
    6569          491 :             std::tie(setPointAt23, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 23);
    6570          491 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.heatSched->Name, setPointAt23);
    6571          491 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.heatSched->Name, numDays);
    6572              : 
    6573          491 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.heatSched->Name, monthAssumed);
    6574              :         }
    6575              : 
    6576         2591 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.coolSched->Num) == uniqSch.end()) {
    6577          485 :             uniqSch.emplace_back(setpt.coolSched->Num);
    6578          485 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.coolSched->Name, setpt.Name);
    6579              : 
    6580          485 :             std::tie(setPointAt11, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 11);
    6581          485 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.coolSched->Name, setPointAt11);
    6582          485 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.coolSched->Name, numDays);
    6583              : 
    6584          485 :             std::tie(setPointAt23, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 23);
    6585          485 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.coolSched->Name, setPointAt23);
    6586          485 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.coolSched->Name, numDays);
    6587              : 
    6588          485 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.coolSched->Name, monthAssumed);
    6589              :         }
    6590              :     }
    6591          799 : }
    6592              : 
    6593          799 : void FillPredefinedTableOnThermostatSchedules(EnergyPlusData &state)
    6594              : {
    6595              :     // add values to the System Summary tabular report related to schedules used by the thermostat objects
    6596              :     // J.Glazer - March 2024
    6597              :     using OutputReportPredefined::PreDefTableEntry;
    6598          799 :     auto &orp = state.dataOutRptPredefined;
    6599              : 
    6600              :     // Helper struct so we can sort to ensure a consistent order.
    6601              :     // No matter the order in which the multiple Field Sets (Control Object Type, Control Name), the same thing is reported to the tabular outputs
    6602              :     // You don't actually need this anymore
    6603              :     struct ControlTypeInfo
    6604              :     {
    6605              :         // HVAC::ThermostatType tType = HVAC::ThermostatType::Invalid;
    6606              :         std::string thermostatType;
    6607              :         std::string controlTypeName;
    6608              :         std::string heatSchName;
    6609              :         std::string coolSchName;
    6610              : 
    6611              :         // Only need the operator<, and we use C++17 so I can't use a defaulted 3-way operator<=>
    6612        53644 :         bool operator<(const ControlTypeInfo &other) const
    6613              :         {
    6614        53644 :             return std::tie(this->thermostatType, this->controlTypeName, this->heatSchName, this->coolSchName) <
    6615       107288 :                    std::tie(other.thermostatType, other.controlTypeName, other.heatSchName, other.coolSchName);
    6616              :         }
    6617              :     };
    6618              :     using ControlTypeInfoMemPtr = std::string ControlTypeInfo::*;
    6619              : 
    6620              :     // How many people on the EnergyPlus team understand this code?
    6621        17292 :     auto joinStrings = [](const std::vector<ControlTypeInfo> &infos, ControlTypeInfoMemPtr memPtr) -> std::string {
    6622        17292 :         std::vector<std::string> result;
    6623        17292 :         result.reserve(infos.size());
    6624       128276 :         for (const auto &info : infos) {
    6625       110984 :             std::string val = info.*memPtr;
    6626       110984 :             if (val.empty()) {
    6627        88762 :                 continue;
    6628              :             }
    6629        22222 :             result.emplace_back(std::move(val));
    6630       128276 :         }
    6631        51876 :         return fmt::format("{}", fmt::join(result, ", "));
    6632        17292 :     };
    6633              : 
    6634         5122 :     for (int idx = 1; idx <= state.dataZoneCtrls->NumTempControlledZones; ++idx) {
    6635         4323 :         auto &tcz = state.dataZoneCtrls->TempControlledZone(idx);
    6636         4323 :         PreDefTableEntry(state, orp->pdchStatName, tcz.ZoneName, tcz.Name);
    6637         4323 :         PreDefTableEntry(state, orp->pdchStatCtrlTypeSchd, tcz.ZoneName, tcz.setptTypeSched->Name);
    6638              : 
    6639         4323 :         std::vector<ControlTypeInfo> infos;
    6640         4323 :         infos.resize((int)HVAC::SetptType::Num);
    6641        21615 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    6642        17292 :             auto &setpt = tcz.setpts[(int)setptType];
    6643              : 
    6644        17292 :             auto &info = infos[(int)setptType];
    6645              : 
    6646        17292 :             if (setpt.Name.empty()) {
    6647        11161 :                 continue;
    6648              :             }
    6649              : 
    6650         6131 :             info.thermostatType = HVAC::setptTypeNames[(int)setptType];
    6651         6131 :             info.controlTypeName = setpt.Name;
    6652         6131 :             switch (setptType) {
    6653         3829 :             case HVAC::SetptType::DualHeatCool:
    6654              :             case HVAC::SetptType::SingleHeatCool: {
    6655         3829 :                 info.coolSchName = setpt.coolSetptSched->Name;
    6656         3829 :                 info.heatSchName = setpt.heatSetptSched->Name;
    6657         3829 :             } break;
    6658              : 
    6659         1144 :             case HVAC::SetptType::SingleCool: {
    6660         1144 :                 info.coolSchName = setpt.coolSetptSched->Name;
    6661         1144 :             } break;
    6662              : 
    6663         1158 :             case HVAC::SetptType::SingleHeat: {
    6664         1158 :                 info.heatSchName = setpt.heatSetptSched->Name;
    6665         1158 :             } break;
    6666              :             }
    6667         6131 :             infos.emplace_back(std::move(info));
    6668              :         }
    6669         4323 :         std::sort(infos.begin(), infos.end());
    6670              : 
    6671         4323 :         PreDefTableEntry(state, orp->pdchStatSchdType1, tcz.ZoneName, joinStrings(infos, &ControlTypeInfo::thermostatType));
    6672         4323 :         PreDefTableEntry(state, orp->pdchStatSchdTypeName1, tcz.ZoneName, joinStrings(infos, &ControlTypeInfo::controlTypeName));
    6673         4323 :         if (auto heatSchNames = joinStrings(infos, &ControlTypeInfo::heatSchName); !heatSchNames.empty()) {
    6674         4320 :             PreDefTableEntry(state, orp->pdchStatSchdHeatName, tcz.ZoneName, heatSchNames);
    6675         4323 :         }
    6676         4323 :         if (auto coolSchNames = joinStrings(infos, &ControlTypeInfo::coolSchName); !coolSchNames.empty()) {
    6677         4306 :             PreDefTableEntry(state, orp->pdchStatSchdCoolName, tcz.ZoneName, coolSchNames);
    6678         4323 :         }
    6679         4323 :     }
    6680          799 : }
    6681              : 
    6682     31432634 : void ZoneSpaceHeatBalanceData::updateTemperatures(EnergyPlusData &state,
    6683              :                                                   bool const ShortenTimeStepSys,
    6684              :                                                   bool const UseZoneTimeStepHistory,
    6685              :                                                   Real64 const PriorTimeStep,
    6686              :                                                   int const zoneNum,
    6687              :                                                   int const spaceNum)
    6688              : {
    6689     31432634 :     assert(zoneNum > 0);
    6690     31432634 :     if (ShortenTimeStepSys) {
    6691              :         // timestep has just shifted from full zone timestep to a new shorter system timestep
    6692              :         // throw away last updates in corrector and rewind for resimulating smaller timestep
    6693      3173070 :         if (spaceNum == 0) {
    6694      3164644 :             if (state.dataHeatBal->Zone(zoneNum).SystemZoneNodeNumber > 0) { // roll back result for zone air node,
    6695      2799459 :                 auto &zoneNode = state.dataLoopNodes->Node(state.dataHeatBal->Zone(zoneNum).SystemZoneNodeNumber);
    6696      2799459 :                 zoneNode.Temp = this->XMAT[0];
    6697      2799459 :                 state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->XMAT[0];
    6698      2799459 :                 zoneNode.HumRat = this->WPrevZoneTS[0];
    6699      2799459 :                 zoneNode.Enthalpy = Psychrometrics::PsyHFnTdbW(this->XMAT[0], this->WPrevZoneTS[0]);
    6700              :             }
    6701              :         } else {
    6702         8426 :             if (state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber > 0) { // roll back result for space air node,
    6703         7776 :                 auto &spaceNode = state.dataLoopNodes->Node(state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber);
    6704         7776 :                 spaceNode.Temp = this->XMAT[0];
    6705         7776 :                 state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->XMAT[0];
    6706         7776 :                 spaceNode.HumRat = this->WPrevZoneTS[0];
    6707         7776 :                 spaceNode.Enthalpy = Psychrometrics::PsyHFnTdbW(this->XMAT[0], this->WPrevZoneTS[0]);
    6708              :             }
    6709              :         }
    6710              : 
    6711      3173070 :         if (state.dataHVACGlobal->NumOfSysTimeSteps !=
    6712      3173070 :             state.dataHVACGlobal->NumOfSysTimeStepsLastZoneTimeStep) { // cannot reuse existing DS data, interpolate from zone time
    6713      1436337 :             Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    6714      1436337 :             this->MAT = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, this->XMAT, this->DSXMAT);
    6715      1436337 :             this->airHumRat = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, this->WPrevZoneTS, this->DSWPrevZoneTS);
    6716              : 
    6717      1436337 :             if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    6718         9127 :                 if (state.dataRoomAir->IsZoneDispVent3Node(zoneNum) || state.dataRoomAir->IsZoneUFAD(zoneNum)) {
    6719              : 
    6720         2788 :                     state.dataRoomAir->MATFloor(zoneNum) = DownInterpolate4HistoryValues(
    6721         2788 :                         PriorTimeStep, TimeStepSys, state.dataRoomAir->XMATFloor(zoneNum), state.dataRoomAir->DSXMATFloor(zoneNum));
    6722         2788 :                     state.dataRoomAir->MATOC(zoneNum) = DownInterpolate4HistoryValues(
    6723         2788 :                         PriorTimeStep, TimeStepSys, state.dataRoomAir->XMATOC(zoneNum), state.dataRoomAir->DSXMATOC(zoneNum));
    6724         2788 :                     state.dataRoomAir->MATMX(zoneNum) = DownInterpolate4HistoryValues(
    6725         2788 :                         PriorTimeStep, TimeStepSys, state.dataRoomAir->XMATMX(zoneNum), state.dataRoomAir->DSXMATMX(zoneNum));
    6726              :                 }
    6727         9127 :                 if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    6728         2219 :                     for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    6729         1902 :                         afnNode.AirTemp = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, afnNode.AirTempX, afnNode.AirTempDSX);
    6730              : 
    6731         1902 :                         afnNode.HumRat = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, afnNode.HumRatX, afnNode.HumRatDSX);
    6732              :                     }
    6733              :                 }
    6734              :             }
    6735              :         } else { // reuse history data in DS terms from last zone time step to preserve information that would be lost
    6736              :                  // do nothing because DS history would have been pushed prior and should be ready
    6737              :         }
    6738              :     }
    6739              :     // now update the variables actually used in the balance equations.
    6740     31432634 :     if (UseZoneTimeStepHistory) {
    6741     20600445 :         this->ZTM = this->XMAT;
    6742     20600445 :         this->WPrevZoneTSTemp = this->WPrevZoneTS;
    6743              :     } else { // use down-stepped history
    6744     10832189 :         this->ZTM = this->DSXMAT;
    6745     10832189 :         this->WPrevZoneTSTemp = this->DSWPrevZoneTS;
    6746              :     }
    6747     31432634 : }
    6748              : 
    6749     31432634 : void ZoneSpaceHeatBalanceData::calcPredictedSystemLoad(EnergyPlusData &state, Real64 const RAFNFrac, int const zoneNum, int const spaceNum)
    6750              : {
    6751              :     // Calculate the predicted system load for a time step.
    6752              : 
    6753     31432634 :     assert(zoneNum > 0);
    6754     31432634 :     auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
    6755     31432634 :     auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(zoneNum);
    6756              : 
    6757     31432634 :     bool thisDeadBandOrSetBack = false;
    6758     31432634 :     Real64 ZoneSetPoint = 0.0;
    6759     31432634 :     Real64 totalLoad = 0.0;
    6760     31432634 :     Real64 LoadToHeatingSetPoint = 0.0;
    6761     31432634 :     Real64 LoadToCoolingSetPoint = 0.0;
    6762              : 
    6763     31432634 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    6764              : 
    6765     31432634 :     int zoneNodeNum = thisZone.SystemZoneNodeNumber;
    6766     31432634 :     if (spaceNum > 0) {
    6767        86394 :         zoneNodeNum = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    6768              :     }
    6769              : 
    6770     31432634 :     switch (state.dataHeatBalFanSys->TempControlType(zoneNum)) {
    6771      4727336 :     case HVAC::SetptType::Uncontrolled: {
    6772              :         // Uncontrolled Zone
    6773      4727336 :         LoadToHeatingSetPoint = 0.0;
    6774      4727336 :         LoadToCoolingSetPoint = 0.0;
    6775      4727336 :         totalLoad = 0.0;
    6776      4727336 :     } break;
    6777              : 
    6778      2284514 :     case HVAC::SetptType::SingleHeat: {
    6779      2284514 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6780      2284514 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6781      2284514 :             LoadToHeatingSetPoint = (this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad);
    6782      2284514 :             break;
    6783              :         }
    6784            0 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    6785            0 :             if (this->tempDepLoad == 0.0) { // B=0
    6786            0 :                 LoadToHeatingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) - this->tempIndLoad;
    6787              :             } else {
    6788            0 :                 Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    6789            0 :                 LoadToHeatingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setpt - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6790              :             }
    6791            0 :         } break;
    6792              : 
    6793            0 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    6794            0 :             LoadToHeatingSetPoint =
    6795            0 :                 this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) + this->tempDepLoad * (zoneTstatSetpt.setpt) - this->tempIndLoad;
    6796            0 :         } break;
    6797              : 
    6798            0 :         default: {
    6799            0 :             assert(false);
    6800              :         } break;
    6801              :         } // switch (Algo)
    6802              : 
    6803      2284514 :         if (RAFNFrac > 0.0) {
    6804            0 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6805              :         }
    6806      2284514 :         totalLoad = LoadToHeatingSetPoint;
    6807      2284514 :         ZoneSetPoint = zoneTstatSetpt.setpt;
    6808      2284514 :         LoadToCoolingSetPoint = LoadToHeatingSetPoint;
    6809              :         // for consistency with the other cases, use LE instead of LT and don't subtract 1.0 Watt as a way of pushing the zero load
    6810              :         // case over the threshold
    6811      2284514 :         if ((totalLoad) <= 0.0) {
    6812       375098 :             thisDeadBandOrSetBack = true;
    6813              :         }
    6814      2284514 :     } break;
    6815              : 
    6816      2742642 :     case HVAC::SetptType::SingleCool: {
    6817      2742642 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6818      2742642 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6819      2742642 :             LoadToCoolingSetPoint = this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad;
    6820      2742642 :         } break;
    6821              : 
    6822            0 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    6823            0 :             if (this->tempDepLoad == 0.0) { // B=0
    6824            0 :                 LoadToCoolingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) - this->tempIndLoad;
    6825              :             } else {
    6826            0 :                 Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    6827            0 :                 LoadToCoolingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setpt - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6828              :             }
    6829            0 :         } break;
    6830              : 
    6831            0 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    6832            0 :             LoadToCoolingSetPoint =
    6833            0 :                 this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) + this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad;
    6834            0 :         } break;
    6835            0 :         default: {
    6836            0 :             assert(false);
    6837              :         } break;
    6838              :         } // swtich (Algo)
    6839              : 
    6840      2742642 :         if (RAFNFrac > 0.0) {
    6841            0 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6842              :         }
    6843      2742642 :         if (thisZone.HasAdjustedReturnTempByITE && !(state.dataGlobal->BeginSimFlag)) {
    6844            0 :             LoadToCoolingSetPoint = this->tempDepLoad * thisZone.AdjustedReturnTempByITE - this->tempIndLoad;
    6845              :         }
    6846      2742642 :         totalLoad = LoadToCoolingSetPoint;
    6847      2742642 :         ZoneSetPoint = zoneTstatSetpt.setpt;
    6848      2742642 :         LoadToHeatingSetPoint = LoadToCoolingSetPoint;
    6849              :         // for consistency with the other cases, use GE instead of GT and don't add 1.0 Watt as a way of pushing the zero load
    6850              :         // case over the threshold
    6851      2742642 :         if ((totalLoad) >= 0.0) {
    6852      1147384 :             thisDeadBandOrSetBack = true;
    6853              :         }
    6854      2742642 :     } break;
    6855              : 
    6856        94330 :     case HVAC::SetptType::SingleHeatCool: {
    6857        94330 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6858        94330 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6859        94330 :             LoadToHeatingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setpt) - this->tempIndLoad);
    6860        94330 :             LoadToCoolingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setpt) - this->tempIndLoad);
    6861        94330 :         } break;
    6862              : 
    6863            0 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    6864            0 :             if (this->tempDepLoad == 0.0) { // B=0
    6865            0 :                 LoadToHeatingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) - this->tempIndLoad;
    6866            0 :                 LoadToCoolingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) - this->tempIndLoad;
    6867              :             } else {
    6868            0 :                 Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    6869            0 :                 LoadToHeatingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setpt - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6870            0 :                 LoadToCoolingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setpt - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6871              :             }
    6872            0 :         } break;
    6873              : 
    6874            0 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    6875            0 :             LoadToHeatingSetPoint =
    6876            0 :                 this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) + this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad;
    6877            0 :             LoadToCoolingSetPoint =
    6878            0 :                 this->AirPowerCap * (zoneTstatSetpt.setpt - this->T1) + this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad;
    6879            0 :         } break;
    6880            0 :         default: {
    6881            0 :             assert(false);
    6882              :         } break;
    6883              :         } // swtich (Algo)
    6884              : 
    6885        94330 :         ZoneSetPoint = zoneTstatSetpt.setpt;
    6886        94330 :         if (RAFNFrac > 0.0) {
    6887            0 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6888              :         }
    6889        94330 :         if (RAFNFrac > 0.0) {
    6890            0 :             LoadToCoolingSetPoint = LoadToCoolingSetPoint / RAFNFrac;
    6891              :         }
    6892              : 
    6893        94330 :         if (thisZone.HasAdjustedReturnTempByITE && !(state.dataGlobal->BeginSimFlag)) {
    6894            0 :             LoadToCoolingSetPoint = this->tempDepLoad * thisZone.AdjustedReturnTempByITE - this->tempIndLoad;
    6895              :         }
    6896              : 
    6897              :         // Note that LoadToHeatingSetPoint is generally not equal to LoadToCoolingSetPoint
    6898              :         // when the heating and cooling set-points are equal if the zone is unmixed,
    6899              :         // e.g. displacement ventilation or UFAD, since the stratification is generally not the same in heating and cooling modes
    6900              : 
    6901              :         // Possible combinations:
    6902              :         // 1/  LoadToHeatingSetPoint > 0 & LoadToCoolingSetPoint > 0 -->  Heating required
    6903              :         // 2/  LoadToHeatingSetPoint  >  LoadToCoolingSetPoint       -->  Possible in the unmixed case but should be trapped
    6904              :         //                                                                 as a poor choice of set-points
    6905              :         // 3/  LoadToHeatingSetPoint < 0 & LoadToCoolingSetPoint < 0 -->  Cooling Required
    6906              :         // 4/  LoadToHeatingSetPoint <=0 & LoadToCoolingSetPoint >=0 -->  Dead Band Operation ! includes zero load cases
    6907              :         // First trap bad set-points
    6908        94330 :         if (LoadToHeatingSetPoint > LoadToCoolingSetPoint) {
    6909            0 :             ShowSevereError(state,
    6910              :                             "HVAC::SetptType::SingleHeatCool: Effective heating set-point higher than effective cooling set-point - use "
    6911              :                             "DualSetPointWithDeadBand if using unmixed air model");
    6912            0 :             ShowContinueErrorTimeStamp(state, format("occurs in Zone={}", thisZone.Name));
    6913            0 :             ShowContinueError(state,
    6914            0 :                               format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
    6915            0 :             ShowContinueError(state, format("Zone TempDepZnLd={:.2R}", this->tempDepLoad));
    6916            0 :             ShowContinueError(state, format("Zone TempIndZnLd={:.2R}", this->tempIndLoad));
    6917            0 :             ShowContinueError(state, format("Zone ThermostatSetPoint={:.2R}", zoneTstatSetpt.setpt));
    6918            0 :             ShowFatalError(state, "Program terminates due to above conditions.");
    6919              :         }
    6920              : 
    6921        94330 :         if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
    6922        47377 :             totalLoad = LoadToHeatingSetPoint;
    6923        46953 :         } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
    6924        46953 :             totalLoad = LoadToCoolingSetPoint;
    6925            0 :         } else if (LoadToHeatingSetPoint <= 0.0 && LoadToCoolingSetPoint >= 0.0) { // deadband includes zero loads
    6926            0 :             totalLoad = 0.0;
    6927            0 :             if (zoneNodeNum > 0) {
    6928            0 :                 ZoneSetPoint = state.dataLoopNodes->Node(zoneNodeNum).Temp;
    6929            0 :                 ZoneSetPoint = max(ZoneSetPoint, zoneTstatSetpt.setptLo); // trap out of deadband
    6930            0 :                 ZoneSetPoint = min(ZoneSetPoint, zoneTstatSetpt.setptHi); // trap out of deadband
    6931              :             }
    6932            0 :             thisDeadBandOrSetBack = true;
    6933              :         } else { // this should never occur!
    6934            0 :             ShowSevereError(state,
    6935              :                             "SingleHeatCoolSetPoint: Unanticipated combination of heating and cooling loads - report to EnergyPlus Development Team");
    6936            0 :             ShowContinueErrorTimeStamp(state, format("occurs in Zone={}", thisZone.Name));
    6937            0 :             ShowContinueError(state,
    6938            0 :                               format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
    6939            0 :             ShowContinueError(state, format("Zone TempDepZnLd={:.2R}", this->tempDepLoad));
    6940            0 :             ShowContinueError(state, format("Zone TempIndZnLd={:.2R}", this->tempIndLoad));
    6941            0 :             ShowContinueError(state, format("Zone ThermostatSetPoint={:.2R}", zoneTstatSetpt.setpt));
    6942            0 :             ShowFatalError(state, "Program terminates due to above conditions.");
    6943              :         }
    6944        94330 :     } break;
    6945              : 
    6946     21583812 :     case HVAC::SetptType::DualHeatCool: {
    6947     21583812 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6948      8742818 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6949      8742818 :             LoadToHeatingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setptLo) - this->tempIndLoad);
    6950      8742818 :             LoadToCoolingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setptHi) - this->tempIndLoad);
    6951      8742818 :         } break;
    6952              : 
    6953     12809392 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    6954     12809392 :             if (this->tempDepLoad == 0.0) { // B=0
    6955            0 :                 LoadToHeatingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setptLo - this->T1) - this->tempIndLoad;
    6956            0 :                 LoadToCoolingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setptHi - this->T1) - this->tempIndLoad;
    6957              :             } else {
    6958     12809392 :                 Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    6959     12809392 :                 LoadToHeatingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setptLo - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6960     12809392 :                 LoadToCoolingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setptHi - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6961              :             }
    6962     12809392 :         } break;
    6963              : 
    6964        31602 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    6965        31602 :             LoadToHeatingSetPoint =
    6966        31602 :                 this->AirPowerCap * (zoneTstatSetpt.setptLo - this->T1) + this->tempDepLoad * zoneTstatSetpt.setptLo - this->tempIndLoad;
    6967        31602 :             LoadToCoolingSetPoint =
    6968        31602 :                 this->AirPowerCap * (zoneTstatSetpt.setptHi - this->T1) + this->tempDepLoad * zoneTstatSetpt.setptHi - this->tempIndLoad;
    6969        31602 :         } break;
    6970            0 :         default: {
    6971            0 :             assert(false);
    6972              : 
    6973              :         } break;
    6974              :         } // switch (Algo)
    6975              : 
    6976     21583812 :         if (RAFNFrac > 0.0) {
    6977         7413 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6978              :         }
    6979     21583812 :         if (RAFNFrac > 0.0) {
    6980         7413 :             LoadToCoolingSetPoint = LoadToCoolingSetPoint / RAFNFrac;
    6981              :         }
    6982              : 
    6983     21583812 :         if (thisZone.HasAdjustedReturnTempByITE && !(state.dataGlobal->BeginSimFlag)) {
    6984        11058 :             LoadToCoolingSetPoint = this->tempDepLoad * thisZone.AdjustedReturnTempByITE - this->tempIndLoad;
    6985              :         }
    6986              : 
    6987              :         // Possible combinations:
    6988              :         // 1/  LoadToHeatingSetPoint > 0 & LoadToCoolingSetPoint > 0 -->  Heating required
    6989              :         // 2/  LoadToHeatingSetPoint  >  LoadToCoolingSetPoint       -->  Possible in the unmixed case but should be trapped
    6990              :         //                                                                  as a poor choice of set-points
    6991              :         // 3/  LoadToHeatingSetPoint < 0 & LoadToCoolingSetPoint < 0 -->  Cooling Required
    6992              :         // 4/  LoadToHeatingSetPoint <=0 & LoadToCoolingSetPoint >=0 -->  Dead Band Operation - includes zero load cases
    6993              :         // First trap bad set-points
    6994     21583812 :         if (LoadToHeatingSetPoint > LoadToCoolingSetPoint) {
    6995            0 :             ShowSevereError(state,
    6996              :                             "DualSetPointWithDeadBand: Effective heating set-point higher than effective cooling set-point - increase "
    6997              :                             "deadband if using unmixed air model");
    6998            0 :             ShowContinueErrorTimeStamp(state, format("occurs in Zone={}", thisZone.Name));
    6999            0 :             ShowContinueError(state,
    7000            0 :                               format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
    7001            0 :             ShowContinueError(state, format("Zone TempDepZnLd={:.2R}", this->tempDepLoad));
    7002            0 :             ShowContinueError(state, format("Zone TempIndZnLd={:.2R}", this->tempIndLoad));
    7003            0 :             ShowContinueError(state, format("Zone Heating ThermostatSetPoint={:.2R}", zoneTstatSetpt.setptLo));
    7004            0 :             ShowContinueError(state, format("Zone Cooling ThermostatSetPoint={:.2R}", zoneTstatSetpt.setptHi));
    7005            0 :             ShowFatalError(state, "Program terminates due to above conditions.");
    7006              :         }
    7007              : 
    7008     21583812 :         if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
    7009      8913301 :             totalLoad = LoadToHeatingSetPoint;
    7010      8913301 :             ZoneSetPoint = zoneTstatSetpt.setptLo;
    7011     12670511 :         } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
    7012      7822158 :             totalLoad = LoadToCoolingSetPoint;
    7013      7822158 :             ZoneSetPoint = zoneTstatSetpt.setptHi;
    7014      4848353 :         } else if (LoadToHeatingSetPoint <= 0.0 && LoadToCoolingSetPoint >= 0.0) { // deadband includes zero loads
    7015              :             // this turns out to cause instabilities sometimes? that lead to setpoint errors if predictor is off.
    7016      4848353 :             totalLoad = 0.0;
    7017      4848353 :             if (zoneNodeNum > 0) {
    7018      4848353 :                 ZoneSetPoint = state.dataLoopNodes->Node(zoneNodeNum).Temp;
    7019      4848353 :                 ZoneSetPoint = max(ZoneSetPoint, zoneTstatSetpt.setptLo); // trap out of deadband
    7020      4848353 :                 ZoneSetPoint = min(ZoneSetPoint, zoneTstatSetpt.setptHi); // trap out of deadband
    7021              :             }
    7022      4848353 :             thisDeadBandOrSetBack = true;
    7023              :         } else { // this should never occur!
    7024            0 :             ShowSevereError(
    7025              :                 state, "DualSetPointWithDeadBand: Unanticipated combination of heating and cooling loads - report to EnergyPlus Development Team");
    7026            0 :             ShowContinueErrorTimeStamp(state, format("occurs in Zone={}", thisZone.Name));
    7027            0 :             ShowContinueError(state,
    7028            0 :                               format("LoadToHeatingSetPoint={:.3R}, LoadToCoolingSetPoint={:.3R}", LoadToHeatingSetPoint, LoadToCoolingSetPoint));
    7029            0 :             ShowContinueError(state, format("Zone Heating Set-point={:.2R}", zoneTstatSetpt.setptLo));
    7030            0 :             ShowContinueError(state, format("Zone Cooling Set-point={:.2R}", zoneTstatSetpt.setptHi));
    7031            0 :             ShowContinueError(state, format("Zone TempDepZnLd={:.2R}", this->tempDepLoad));
    7032            0 :             ShowContinueError(state, format("Zone TempIndZnLd={:.2R}", this->tempIndLoad));
    7033            0 :             ShowContinueError(state, format("Zone ThermostatSetPoint={:.2R}", zoneTstatSetpt.setpt));
    7034              : 
    7035            0 :             ShowFatalError(state, "Program terminates due to above conditions.");
    7036              :         }
    7037     21583812 :     } break;
    7038              : 
    7039            0 :     default: {
    7040            0 :     } break;
    7041              :     } // swtich (setptType)
    7042              : 
    7043     31432634 :     int systemNodeNumber = 0;
    7044     31432634 :     int stageNum = 0;
    7045     31432634 :     if (spaceNum > 0) {
    7046        86394 :         systemNodeNumber = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    7047        86394 :         stageNum = state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).StageNum;
    7048        86394 :         assert(stageNum == state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).StageNum);
    7049              :     } else {
    7050     31346240 :         systemNodeNumber = thisZone.SystemZoneNodeNumber;
    7051     31346240 :         stageNum = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).StageNum;
    7052              :     }
    7053              :     // Staged control zone
    7054     31432634 :     if (s_ztpc->NumStageCtrZone > 0) {
    7055       122502 :         if (state.dataZoneCtrls->StageZoneLogic(zoneNum)) {
    7056       100893 :             if (stageNum == 0) { // No load
    7057        44373 :                 LoadToHeatingSetPoint = 0.0;
    7058        44373 :                 LoadToCoolingSetPoint = 0.0;
    7059        44373 :                 totalLoad = 0.0;
    7060        44373 :                 if (systemNodeNumber > 0) {
    7061        44373 :                     ZoneSetPoint = state.dataLoopNodes->Node(systemNodeNumber).Temp;
    7062        44373 :                     ZoneSetPoint = max(ZoneSetPoint, zoneTstatSetpt.setptLo); // trap out of deadband
    7063        44373 :                     ZoneSetPoint = min(ZoneSetPoint, zoneTstatSetpt.setptHi); // trap out of deadband
    7064              :                 }
    7065        44373 :                 thisDeadBandOrSetBack = true;
    7066              : 
    7067        56520 :             } else if (stageNum < 0) { // Cooling load
    7068        19407 :                 switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    7069        19407 :                 case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    7070        19407 :                     LoadToCoolingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setptHi) - this->tempIndLoad);
    7071        19407 :                 } break;
    7072              : 
    7073            0 :                 case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    7074            0 :                     if (this->tempDepLoad == 0.0) { // B=0
    7075            0 :                         LoadToCoolingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setptHi - this->T1) - this->tempIndLoad;
    7076              :                     } else {
    7077            0 :                         Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    7078            0 :                         LoadToCoolingSetPoint =
    7079            0 :                             this->tempDepLoad * (zoneTstatSetpt.setptHi - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    7080              :                     }
    7081            0 :                 } break;
    7082              : 
    7083            0 :                 case DataHeatBalance::SolutionAlgo::EulerMethod: {
    7084            0 :                     LoadToCoolingSetPoint =
    7085            0 :                         this->AirPowerCap * (zoneTstatSetpt.setptHi - this->T1) + this->tempDepLoad * zoneTstatSetpt.setptHi - this->tempIndLoad;
    7086            0 :                 } break;
    7087            0 :                 default: {
    7088            0 :                     assert(false);
    7089              :                 } break;
    7090              :                 } // switch (Algo)
    7091              : 
    7092        19407 :                 totalLoad = LoadToCoolingSetPoint;
    7093        19407 :                 ZoneSetPoint = zoneTstatSetpt.setptHi;
    7094        19407 :                 LoadToHeatingSetPoint = LoadToCoolingSetPoint;
    7095        19407 :                 if ((totalLoad) >= 0.0) {
    7096         5839 :                     thisDeadBandOrSetBack = true;
    7097              :                 }
    7098              :             } else { // Heating load
    7099        37113 :                 switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    7100        37113 :                 case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    7101        37113 :                     LoadToHeatingSetPoint = (this->tempDepLoad * zoneTstatSetpt.setptLo - this->tempIndLoad);
    7102        37113 :                 } break;
    7103              : 
    7104            0 :                 case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    7105            0 :                     if (this->tempDepLoad == 0.0) { // B=0
    7106            0 :                         LoadToHeatingSetPoint = this->AirPowerCap * (zoneTstatSetpt.setptLo - this->T1) - this->tempIndLoad;
    7107              :                     } else {
    7108            0 :                         Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    7109            0 :                         LoadToHeatingSetPoint =
    7110            0 :                             this->tempDepLoad * (zoneTstatSetpt.setptLo - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    7111              :                     }
    7112            0 :                 } break;
    7113              : 
    7114            0 :                 case DataHeatBalance::SolutionAlgo::EulerMethod: {
    7115            0 :                     LoadToHeatingSetPoint =
    7116            0 :                         this->AirPowerCap * (zoneTstatSetpt.setptLo - this->T1) + this->tempDepLoad * (zoneTstatSetpt.setptLo) - this->tempIndLoad;
    7117            0 :                 } break;
    7118            0 :                 default: {
    7119            0 :                     assert(false);
    7120              :                 } break;
    7121              :                 } // switch (Algo)
    7122              : 
    7123        37113 :                 totalLoad = LoadToHeatingSetPoint;
    7124        37113 :                 ZoneSetPoint = zoneTstatSetpt.setptLo;
    7125        37113 :                 LoadToCoolingSetPoint = LoadToHeatingSetPoint;
    7126        37113 :                 if ((totalLoad) <= 0.0) {
    7127         2164 :                     thisDeadBandOrSetBack = true;
    7128              :                 }
    7129              :             }
    7130              :         }
    7131              :     }
    7132              : 
    7133              :     // If the ZoneNodeNum has been set for a Controlled Zone, then the zone setpoint is placed on the node.
    7134     31432634 :     if (zoneNodeNum > 0) {
    7135     28285514 :         state.dataLoopNodes->Node(zoneNodeNum).TempSetPoint = ZoneSetPoint;
    7136              :     }
    7137              : 
    7138     31432634 :     state.dataZoneEnergyDemand->Setback(zoneNum) = (ZoneSetPoint > this->setPointLast);
    7139              : 
    7140     31432634 :     this->setPointLast = ZoneSetPoint;
    7141     31432634 :     state.dataHeatBalFanSys->zoneTstatSetpts(zoneNum).setpt = ZoneSetPoint; // needed to fix Issue # 5048
    7142     31432634 :     state.dataZoneEnergyDemand->DeadBandOrSetback(zoneNum) = thisDeadBandOrSetBack;
    7143     31432634 :     state.dataZoneEnergyDemand->CurDeadBandOrSetback(zoneNum) = thisDeadBandOrSetBack;
    7144              : 
    7145              :     // Apply the Zone Multiplier and Load Correction factor as needed
    7146     31432634 :     if (spaceNum > 0) {
    7147        86394 :         state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).reportSensibleLoadsZoneMultiplier(
    7148              :             state, zoneNum, totalLoad, LoadToHeatingSetPoint, LoadToCoolingSetPoint);
    7149              :     } else {
    7150     31346240 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).reportSensibleLoadsZoneMultiplier(
    7151              :             state, zoneNum, totalLoad, LoadToHeatingSetPoint, LoadToCoolingSetPoint);
    7152              :     }
    7153     31432634 : }
    7154              : } // namespace EnergyPlus::ZoneTempPredictorCorrector
        

Generated by: LCOV version 2.0-1