LCOV - code coverage report
Current view: top level - EnergyPlus - ZoneTempPredictorCorrector.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 47.4 % 4084 1936
Test Date: 2025-06-02 12:03:30 Functions: 86.7 % 45 39

            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      1140359 : 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      1140359 :     if (state.dataZoneCtrls->GetZoneAirStatsInputFlag) {
     214          102 :         GetZoneAirSetPoints(state);
     215          102 :         state.dataZoneCtrls->GetZoneAirStatsInputFlag = false;
     216              :     }
     217              : 
     218      1140359 :     InitZoneAirSetPoints(state);
     219              : 
     220      1140359 :     switch (UpdateType) {
     221       248593 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::GetZoneSetPoints: {
     222       248593 :         CalcZoneAirTempSetPoints(state);
     223       248593 :     } break;
     224       297256 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::PredictStep: {
     225       297256 :         PredictSystemLoads(state, ShortenTimeStepSys, UseZoneTimeStepHistory, PriorTimeStep);
     226       297256 :     } break;
     227       297255 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::CorrectStep: {
     228       297255 :         ZoneTempChange = correctZoneAirTemps(state, UseZoneTimeStepHistory);
     229       297255 :     } break;
     230            0 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::RevertZoneTimestepHistories: {
     231            0 :         RevertZoneTimestepHistories(state);
     232            0 :     } break;
     233       248592 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::PushZoneTimestepHistories: {
     234       248592 :         PushZoneTimestepHistories(state);
     235       248592 :     } break;
     236        48663 :     case DataHeatBalFanSys::PredictorCorrectorCtrl::PushSystemTimestepHistories: {
     237        48663 :         PushSystemTimestepHistories(state);
     238        48663 :     } break;
     239            0 :     default:
     240            0 :         break;
     241              :     }
     242      1140359 : }
     243              : 
     244          111 : 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          111 :     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          111 :     Array1D_int CTSchedMapToControlledZone;
     283          111 :     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          111 :     Array1D<NeededControlTypes> TStatControlTypes;
     304          111 :     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          111 :     auto &s_ipsc = state.dataIPShortCut;
     313          111 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
     314              : 
     315          111 :     auto &TStatObjects = state.dataZoneCtrls->TStatObjects;
     316          111 :     auto &Zone = state.dataHeatBal->Zone;
     317          111 :     auto &ZoneList = state.dataHeatBal->ZoneList;
     318          111 :     auto &ComfortTStatObjects = state.dataZoneCtrls->ComfortTStatObjects;
     319          111 :     int NumOfZones = state.dataGlobal->NumOfZones;
     320          111 :     auto &s_ip = state.dataInputProcessing->inputProcessor;
     321              : 
     322          111 :     s_ipsc->cCurrentModuleObject = cZControlTypes(static_cast<int>(ZoneControlTypes::TStat));
     323              :     // Update Num in state and make local convenience copy
     324          111 :     int NumTStatStatements = state.dataZoneCtrls->NumTStatStatements = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     325          111 :     TStatObjects.allocate(NumTStatStatements);
     326              : 
     327              :     // Pre-scan for use of Zone lists in TStat statements (i.e. Global application of TStat)
     328          111 :     state.dataZoneCtrls->NumTempControlledZones = 0;
     329          200 :     for (Item = 1; Item <= NumTStatStatements; ++Item) {
     330          178 :         s_ip->getObjectItem(state,
     331           89 :                             s_ipsc->cCurrentModuleObject,
     332              :                             Item,
     333           89 :                             s_ipsc->cAlphaArgs,
     334              :                             NumAlphas,
     335           89 :                             s_ipsc->rNumericArgs,
     336              :                             NumNums,
     337              :                             IOStat,
     338           89 :                             s_ipsc->lNumericFieldBlanks,
     339           89 :                             s_ipsc->lAlphaFieldBlanks,
     340           89 :                             s_ipsc->cAlphaFieldNames,
     341           89 :                             s_ipsc->cNumericFieldNames);
     342              : 
     343           89 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     344           89 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     345              : 
     346           89 :         TStatObjects(Item).Name = s_ipsc->cAlphaArgs(1);
     347           89 :         Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     348           89 :         ZLItem = 0;
     349           89 :         if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
     350            1 :             ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
     351              :         }
     352           89 :         if (Item1 > 0) {
     353           88 :             TStatObjects(Item).TempControlledZoneStartPtr = state.dataZoneCtrls->NumTempControlledZones + 1;
     354           88 :             ++state.dataZoneCtrls->NumTempControlledZones;
     355           88 :             TStatObjects(Item).NumOfZones = 1;
     356           88 :             TStatObjects(Item).ZoneListActive = false;
     357           88 :             TStatObjects(Item).ZoneOrZoneListPtr = Item1;
     358            1 :         } else if (ZLItem > 0) {
     359            1 :             auto const &ZoneList = state.dataHeatBal->ZoneList(ZLItem);
     360            1 :             TStatObjects(Item).TempControlledZoneStartPtr = state.dataZoneCtrls->NumTempControlledZones + 1;
     361            1 :             state.dataZoneCtrls->NumTempControlledZones += ZoneList.NumOfZones;
     362            1 :             TStatObjects(Item).NumOfZones = ZoneList.NumOfZones;
     363            1 :             TStatObjects(Item).ZoneListActive = true;
     364            1 :             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          111 :     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          111 :     if (state.dataZoneCtrls->NumTempControlledZones > 0) {
     378           60 :         state.dataZoneCtrls->TempControlledZone.allocate(state.dataZoneCtrls->NumTempControlledZones);
     379           60 :         TStatControlTypes.allocate(state.dataZoneCtrls->NumTempControlledZones); // Number of set point types
     380           60 :         CTSchedMapToControlledZone.dimension(state.dataZoneCtrls->NumTempControlledZones, 0);
     381              : 
     382           60 :         TempControlledZoneNum = 0;
     383           60 :         s_ztpc->NumOnOffCtrZone = 0;
     384          149 :         for (Item = 1; Item <= NumTStatStatements; ++Item) {
     385          178 :             s_ip->getObjectItem(state,
     386           89 :                                 s_ipsc->cCurrentModuleObject,
     387              :                                 Item,
     388           89 :                                 s_ipsc->cAlphaArgs,
     389              :                                 NumAlphas,
     390           89 :                                 s_ipsc->rNumericArgs,
     391              :                                 NumNums,
     392              :                                 IOStat,
     393           89 :                                 s_ipsc->lNumericFieldBlanks,
     394           89 :                                 s_ipsc->lAlphaFieldBlanks,
     395           89 :                                 s_ipsc->cAlphaFieldNames,
     396           89 :                                 s_ipsc->cNumericFieldNames);
     397              : 
     398           89 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     399              : 
     400          180 :             for (Item1 = 1; Item1 <= TStatObjects(Item).NumOfZones; ++Item1) {
     401           91 :                 ++TempControlledZoneNum;
     402           91 :                 auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     403              : 
     404           91 :                 if (TStatObjects(Item).ZoneListActive) {
     405            3 :                     s_ipsc->cAlphaArgs(2) = Zone(ZoneList(TStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name;
     406              :                 }
     407           91 :                 int ZoneAssigned = Util::FindItemInList(s_ipsc->cAlphaArgs(2),
     408           91 :                                                         state.dataZoneCtrls->TempControlledZone,
     409              :                                                         &DataZoneControls::ZoneTempControls::ZoneName,
     410              :                                                         TempControlledZoneNum - 1);
     411           91 :                 if (ZoneAssigned == 0) {
     412           91 :                     tempZone.ZoneName = s_ipsc->cAlphaArgs(2);
     413           91 :                     tempZone.ActualZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     414           91 :                     if (tempZone.ActualZoneNum == 0) {
     415            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     416            0 :                         ErrorsFound = true;
     417              :                     } else {
     418           91 :                         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           91 :                 if (!TStatObjects(Item).ZoneListActive) {
     429           88 :                     tempZone.Name = s_ipsc->cAlphaArgs(1);
     430              :                 } else {
     431            3 :                     auto &ZoneList = state.dataHeatBal->ZoneList(TStatObjects(Item).ZoneOrZoneListPtr);
     432            9 :                     CheckCreatedZoneItemName(state,
     433              :                                              RoutineName,
     434            3 :                                              s_ipsc->cCurrentModuleObject,
     435            3 :                                              Zone(ZoneList.Zone(Item1)).Name,
     436              :                                              ZoneList.MaxZoneNameLength,
     437            3 :                                              TStatObjects(Item).Name,
     438            3 :                                              state.dataZoneCtrls->TempControlledZone,
     439              :                                              TempControlledZoneNum - 1,
     440            3 :                                              tempZone.Name,
     441              :                                              errFlag);
     442            3 :                     if (errFlag) {
     443            0 :                         ErrorsFound = true;
     444              :                     }
     445              :                 }
     446              : 
     447           91 :                 tempZone.setptTypeSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3));
     448           91 :                 if (Item1 == 1) { // only show error on first of several if zone list
     449           89 :                     if (tempZone.setptTypeSched == nullptr) {
     450            0 :                         ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     451            0 :                         ErrorsFound = true;
     452           89 :                     } 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           91 :                 if (s_ipsc->lAlphaFieldBlanks(7)) {
     460           79 :                     NumAlphas = 5;
     461           12 :                 } else if (s_ipsc->lAlphaFieldBlanks(9)) {
     462            8 :                     NumAlphas = 7;
     463            4 :                 } else if (s_ipsc->lAlphaFieldBlanks(11)) {
     464            4 :                     NumAlphas = 9;
     465              :                 }
     466              : 
     467           91 :                 int NumSetptTypes = nint((NumAlphas - 3.0) / 2.0);
     468              : 
     469          198 :                 for (int iSetpt = 1; iSetpt <= NumSetptTypes; ++iSetpt) {
     470              : 
     471          107 :                     HVAC::SetptType setptType = HVAC::SetptType::Invalid;
     472          107 :                     int spIdx = 2 * iSetpt - 1 + 3;
     473              : 
     474          107 :                     if (s_ipsc->lAlphaFieldBlanks(spIdx)) {
     475            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(spIdx));
     476            0 :                         ErrorsFound = true;
     477            0 :                         continue;
     478          107 :                     } 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          107 :                     tempZone.setpts[(int)setptType].Name = s_ipsc->cAlphaArgs(2 * iSetpt + 3);
     486          107 :                     tempZone.setpts[(int)setptType].isUsed = true;
     487              :                 }
     488              : 
     489           91 :                 if (NumNums > 0) {
     490            3 :                     if (s_ipsc->rNumericArgs(1) >= 0.0) {
     491            3 :                         tempZone.DeltaTCutSet = s_ipsc->rNumericArgs(1);
     492            3 :                         if (s_ipsc->rNumericArgs(1) > 0.0) {
     493            0 :                             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           91 :                 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          111 :     s_ipsc->cCurrentModuleObject = setptTypeNamesUC[(int)HVAC::SetptType::SingleHeat];
     519          111 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     520              : 
     521          111 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat] > 0) {
     522           14 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeat].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat]);
     523              :     }
     524              : 
     525          129 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat]; ++idx) {
     526           36 :         s_ip->getObjectItem(state,
     527           18 :                             s_ipsc->cCurrentModuleObject,
     528              :                             idx,
     529           18 :                             s_ipsc->cAlphaArgs,
     530              :                             NumAlphas,
     531           18 :                             s_ipsc->rNumericArgs,
     532              :                             NumNums,
     533              :                             IOStat,
     534           18 :                             s_ipsc->lNumericFieldBlanks,
     535           18 :                             s_ipsc->lAlphaFieldBlanks,
     536           18 :                             s_ipsc->cAlphaFieldNames,
     537           18 :                             s_ipsc->cNumericFieldNames);
     538              : 
     539           18 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     540              : 
     541           18 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     542           18 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeat](idx);
     543           18 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     544              : 
     545           18 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     546            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     547            0 :             ErrorsFound = true;
     548           18 :         } 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          111 :     s_ipsc->cCurrentModuleObject = setptTypeNamesUC[(int)HVAC::SetptType::SingleCool];
     556          111 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     557              : 
     558          111 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool] > 0) {
     559           15 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleCool].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool]);
     560              :     }
     561              : 
     562          130 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool]; ++idx) {
     563           38 :         s_ip->getObjectItem(state,
     564           19 :                             s_ipsc->cCurrentModuleObject,
     565              :                             idx,
     566           19 :                             s_ipsc->cAlphaArgs,
     567              :                             NumAlphas,
     568           19 :                             s_ipsc->rNumericArgs,
     569              :                             NumNums,
     570              :                             IOStat,
     571           19 :                             s_ipsc->lNumericFieldBlanks,
     572           19 :                             s_ipsc->lAlphaFieldBlanks,
     573           19 :                             s_ipsc->cAlphaFieldNames,
     574           19 :                             s_ipsc->cNumericFieldNames);
     575              : 
     576           19 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     577              : 
     578           19 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     579           19 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleCool](idx);
     580           19 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     581              : 
     582           19 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     583            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     584            0 :             ErrorsFound = true;
     585           19 :         } 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          111 :     s_ipsc->cCurrentModuleObject = setptTypeNames[(int)HVAC::SetptType::SingleHeatCool];
     593          111 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     594              : 
     595          111 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool] > 0) {
     596            1 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeatCool].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool]);
     597              :     }
     598              : 
     599          112 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeatCool]; ++idx) {
     600            2 :         s_ip->getObjectItem(state,
     601            1 :                             s_ipsc->cCurrentModuleObject,
     602              :                             idx,
     603            1 :                             s_ipsc->cAlphaArgs,
     604              :                             NumAlphas,
     605            1 :                             s_ipsc->rNumericArgs,
     606              :                             NumNums,
     607              :                             IOStat,
     608            1 :                             s_ipsc->lNumericFieldBlanks,
     609            1 :                             s_ipsc->lAlphaFieldBlanks,
     610            1 :                             s_ipsc->cAlphaFieldNames,
     611            1 :                             s_ipsc->cNumericFieldNames);
     612              : 
     613            1 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     614              : 
     615            1 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeatCool](idx);
     616            1 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     617              : 
     618            1 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     619            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     620            0 :             ErrorsFound = true;
     621            1 :         } 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          111 :     s_ipsc->cCurrentModuleObject = setptTypeNames[(int)HVAC::SetptType::DualHeatCool];
     629          111 :     s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     630              : 
     631          111 :     if (s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool] > 0) {
     632           53 :         s_ztpc->tempSetptScheds[(int)HVAC::SetptType::DualHeatCool].allocate(s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool]);
     633              :     }
     634              : 
     635          165 :     for (int idx = 1; idx <= s_ztpc->NumTempControls[(int)HVAC::SetptType::DualHeatCool]; ++idx) {
     636          108 :         s_ip->getObjectItem(state,
     637           54 :                             s_ipsc->cCurrentModuleObject,
     638              :                             idx,
     639           54 :                             s_ipsc->cAlphaArgs,
     640              :                             NumAlphas,
     641           54 :                             s_ipsc->rNumericArgs,
     642              :                             NumNums,
     643              :                             IOStat,
     644           54 :                             s_ipsc->lNumericFieldBlanks,
     645           54 :                             s_ipsc->lAlphaFieldBlanks,
     646           54 :                             s_ipsc->cAlphaFieldNames,
     647           54 :                             s_ipsc->cNumericFieldNames);
     648              : 
     649           54 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     650           54 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     651           54 :         auto &setpt = s_ztpc->tempSetptScheds[(int)HVAC::SetptType::DualHeatCool](idx);
     652           54 :         setpt.Name = s_ipsc->cAlphaArgs(1);
     653              : 
     654           54 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
     655            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
     656            0 :             ErrorsFound = true;
     657           54 :         } 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           54 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
     663            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
     664            0 :             ErrorsFound = true;
     665           54 :         } 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          202 :     for (TempControlledZoneNum = 1; TempControlledZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++TempControlledZoneNum) {
     674           91 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     675              : 
     676          455 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
     677          364 :             auto &setpt = tempZone.setpts[(int)setptType];
     678          364 :             if (!setpt.isUsed) {
     679          257 :                 continue;
     680              :             }
     681              : 
     682          107 :             int setptIdx = Util::FindItem(setpt.Name, s_ztpc->tempSetptScheds[(int)setptType]);
     683              : 
     684          107 :             if (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
     685              :                 setptType == HVAC::SetptType::DualHeatCool) {
     686           93 :                 setpt.heatSetptSched = s_ztpc->tempSetptScheds[(int)setptType](setptIdx).heatSched;
     687              :             }
     688              : 
     689          107 :             if (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
     690              :                 setptType == HVAC::SetptType::DualHeatCool) {
     691           94 :                 setpt.coolSetptSched = s_ztpc->tempSetptScheds[(int)setptType](setptIdx).coolSched;
     692              :             }
     693              :         }
     694              :     }
     695              : 
     696              :     // Now, Check the schedule values/indices for validity
     697              : 
     698          202 :     for (int TempControlledZoneNum = 1; TempControlledZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++TempControlledZoneNum) {
     699           91 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     700              : 
     701           91 :         if (tempZone.setptTypeSched == nullptr) {
     702            0 :             continue; // error will be caught elsewhere
     703              :         }
     704              : 
     705           91 :         int SchedMin = tempZone.setptTypeSched->getMinVal(state);
     706           91 :         int SchedMax = tempZone.setptTypeSched->getMaxVal(state);
     707              : 
     708           91 :         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          455 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
     718          364 :             auto const &setpt = tempZone.setpts[(int)setptType];
     719              : 
     720          364 :             if (!setpt.isUsed) {
     721              :                 // Catch early issues
     722          257 :                 if (tempZone.setptTypeSched->hasVal(state, (int)setptType)) {
     723            1 :                     ShowSevereError(state, format("Control Type Schedule={}", tempZone.setptTypeSched->Name));
     724            2 :                     ShowContinueError(
     725              :                         state,
     726            2 :                         format("..specifies {} ({}) as the control type. Not valid for this zone.", (int)setptType, setptTypeNames[(int)setptType]));
     727            1 :                     ShowContinueError(state, format("..reference {}={}", cZControlTypes((int)ZoneControlTypes::TStat), tempZone.Name));
     728            1 :                     ShowContinueError(state, format("..reference ZONE={}", tempZone.ZoneName));
     729            1 :                     ErrorsFound = true;
     730              :                 }
     731          257 :                 continue;
     732              :             }
     733              : 
     734           14 :             if (setpt.heatSetptSched == nullptr &&
     735           14 :                 (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
     736          121 :                  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           13 :             if (setpt.coolSetptSched == nullptr &&
     748           13 :                 (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
     749          120 :                  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          202 :     for (TempControlledZoneNum = 1; TempControlledZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++TempControlledZoneNum) {
     763           91 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
     764           91 :         ActualZoneNum = tempZone.ActualZoneNum;
     765              : 
     766           91 :         if (tempZone.setptTypeSched == nullptr) {
     767            0 :             continue; // error caught elsewhere -- would just be confusing here
     768              :         }
     769              : 
     770          455 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
     771          364 :             if (TStatControlTypes(TempControlledZoneNum).MustHave[(int)setptType] &&
     772            0 :                 TStatControlTypes(TempControlledZoneNum).DidHave[(int)setptType]) {
     773            0 :                 continue;
     774              :             }
     775              : 
     776          364 :             if (!TStatControlTypes(TempControlledZoneNum).MustHave[(int)setptType]) {
     777          364 :                 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          111 :     if (allocated(TStatControlTypes)) {
     787           60 :         TStatControlTypes.deallocate();
     788              :     }
     789              :     // This starts the Humidity Control Get Input section
     790          111 :     s_ipsc->cCurrentModuleObject = cZControlTypes(static_cast<int>(ZoneControlTypes::HStat));
     791          111 :     state.dataZoneCtrls->NumHumidityControlZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     792              : 
     793          111 :     if (state.dataZoneCtrls->NumHumidityControlZones > 0) {
     794            5 :         state.dataZoneCtrls->HumidityControlZone.allocate(state.dataZoneCtrls->NumHumidityControlZones);
     795            5 :         s_ztpc->HumidityControlZoneUniqueNames.reserve(static_cast<unsigned>(state.dataZoneCtrls->NumHumidityControlZones));
     796              :     }
     797              : 
     798          116 :     for (HumidControlledZoneNum = 1; HumidControlledZoneNum <= state.dataZoneCtrls->NumHumidityControlZones; ++HumidControlledZoneNum) {
     799           10 :         s_ip->getObjectItem(state,
     800            5 :                             s_ipsc->cCurrentModuleObject,
     801              :                             HumidControlledZoneNum,
     802            5 :                             s_ipsc->cAlphaArgs,
     803              :                             NumAlphas,
     804            5 :                             s_ipsc->rNumericArgs,
     805              :                             NumNums,
     806              :                             IOStat,
     807            5 :                             s_ipsc->lNumericFieldBlanks,
     808            5 :                             s_ipsc->lAlphaFieldBlanks,
     809            5 :                             s_ipsc->cAlphaFieldNames,
     810            5 :                             s_ipsc->cNumericFieldNames);
     811              : 
     812            5 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     813              : 
     814            5 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     815              : 
     816            5 :         auto &humidControlledZone = state.dataZoneCtrls->HumidityControlZone(HumidControlledZoneNum);
     817            5 :         humidControlledZone.ControlName = s_ipsc->cAlphaArgs(1);
     818            5 :         GlobalNames::IntraObjUniquenessCheck(state,
     819            5 :                                              s_ipsc->cAlphaArgs(2),
     820            5 :                                              s_ipsc->cCurrentModuleObject,
     821            5 :                                              s_ipsc->cAlphaFieldNames(2),
     822            5 :                                              s_ztpc->HumidityControlZoneUniqueNames,
     823              :                                              ErrorsFound);
     824              : 
     825            5 :         humidControlledZone.ZoneName = s_ipsc->cAlphaArgs(2);
     826            5 :         humidControlledZone.ActualZoneNum = Util::FindItem(s_ipsc->cAlphaArgs(2), Zone);
     827            5 :         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            5 :             state.dataHeatBal->Zone(humidControlledZone.ActualZoneNum).humidityControlZoneIndex = HumidControlledZoneNum;
     837              :         }
     838              : 
     839            5 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
     840            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
     841            0 :             ErrorsFound = true;
     842            5 :         } 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            5 :         if (NumAlphas < 4 || s_ipsc->lAlphaFieldBlanks(4)) {
     848            4 :             humidControlledZone.dehumidifyingSched = humidControlledZone.humidifyingSched;
     849            1 :         } 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          111 :     s_ipsc->cCurrentModuleObject = cZControlTypes(static_cast<int>(ZoneControlTypes::TCTStat));
     858          111 :     state.dataZoneCtrls->NumComfortTStatStatements = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     859          111 :     ComfortTStatObjects.allocate(state.dataZoneCtrls->NumComfortTStatStatements);
     860              : 
     861              :     // Pre-scan for use of Zone lists in TStat statements (i.e. Global application of TStat)
     862          111 :     state.dataZoneCtrls->NumComfortControlledZones = 0;
     863          111 :     errFlag = false;
     864          111 :     for (Item = 1; Item <= state.dataZoneCtrls->NumComfortTStatStatements; ++Item) {
     865            0 :         s_ip->getObjectItem(state,
     866            0 :                             s_ipsc->cCurrentModuleObject,
     867              :                             Item,
     868            0 :                             s_ipsc->cAlphaArgs,
     869              :                             NumAlphas,
     870            0 :                             s_ipsc->rNumericArgs,
     871              :                             NumNums,
     872              :                             IOStat,
     873            0 :                             s_ipsc->lNumericFieldBlanks,
     874            0 :                             s_ipsc->lAlphaFieldBlanks,
     875            0 :                             s_ipsc->cAlphaFieldNames,
     876            0 :                             s_ipsc->cNumericFieldNames);
     877            0 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
     878              : 
     879            0 :         Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     880            0 :         ZLItem = 0;
     881            0 :         if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
     882            0 :             ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
     883              :         }
     884            0 :         ComfortTStatObjects(Item).Name = s_ipsc->cAlphaArgs(1);
     885            0 :         if (Item1 > 0) {
     886            0 :             ComfortTStatObjects(Item).ComfortControlledZoneStartPtr = state.dataZoneCtrls->NumComfortControlledZones + 1;
     887            0 :             ++state.dataZoneCtrls->NumComfortControlledZones;
     888            0 :             ComfortTStatObjects(Item).NumOfZones = 1;
     889            0 :             ComfortTStatObjects(Item).ZoneListActive = false;
     890            0 :             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          111 :     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          111 :     if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
     917            0 :         state.dataZoneCtrls->ComfortControlledZone.allocate(state.dataZoneCtrls->NumComfortControlledZones);
     918            0 :         TComfortControlTypes.allocate(state.dataZoneCtrls->NumComfortControlledZones); // Number of set point types
     919            0 :         CCmSchedMapToControlledZone.dimension(state.dataZoneCtrls->NumComfortControlledZones, 0);
     920              : 
     921            0 :         ComfortControlledZoneNum = 0;
     922            0 :         for (Item = 1; Item <= state.dataZoneCtrls->NumComfortTStatStatements; ++Item) {
     923            0 :             s_ip->getObjectItem(state,
     924            0 :                                 s_ipsc->cCurrentModuleObject,
     925              :                                 Item,
     926            0 :                                 s_ipsc->cAlphaArgs,
     927              :                                 NumAlphas,
     928            0 :                                 s_ipsc->rNumericArgs,
     929              :                                 NumNums,
     930              :                                 IOStat,
     931            0 :                                 s_ipsc->lNumericFieldBlanks,
     932            0 :                                 s_ipsc->lAlphaFieldBlanks,
     933            0 :                                 s_ipsc->cAlphaFieldNames,
     934            0 :                                 s_ipsc->cNumericFieldNames);
     935              : 
     936            0 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     937              : 
     938            0 :             for (Item1 = 1; Item1 <= ComfortTStatObjects(Item).NumOfZones; ++Item1) {
     939            0 :                 ++ComfortControlledZoneNum;
     940              : 
     941            0 :                 auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
     942              : 
     943            0 :                 if (ComfortTStatObjects(Item).ZoneListActive) {
     944            0 :                     s_ipsc->cAlphaArgs(2) = state.dataHeatBal->Zone(ZoneList(ComfortTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name;
     945              :                 }
     946            0 :                 int ZoneAssigned = Util::FindItemInList(s_ipsc->cAlphaArgs(2),
     947            0 :                                                         state.dataZoneCtrls->ComfortControlledZone,
     948              :                                                         &DataZoneControls::ZoneComfortControls::ZoneName,
     949              :                                                         ComfortControlledZoneNum - 1);
     950            0 :                 if (ZoneAssigned == 0) {
     951            0 :                     comfortZone.ZoneName = s_ipsc->cAlphaArgs(2);
     952            0 :                     comfortZone.ActualZoneNum = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
     953            0 :                     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            0 :                 if (!ComfortTStatObjects(Item).ZoneListActive) {
     969            0 :                     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            0 :                 int IZoneCount = 0;
     977            0 :                 for (i = 1; i <= state.dataHeatBal->TotPeople; ++i) {
     978            0 :                     if (comfortZone.ActualZoneNum == state.dataHeatBal->People(i).ZonePtr) {
     979            0 :                         ++IZoneCount;
     980              :                     }
     981              :                 }
     982              :                 // Could not find a people object for this particular zone
     983            0 :                 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            0 :                 comfortZone.averageMethod = DataZoneControls::AverageMethod::NO;
     994            0 :                 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            0 :                     for (i = 1; i <= state.dataHeatBal->TotPeople; ++i) {
    1011            0 :                         if (comfortZone.ActualZoneNum == state.dataHeatBal->People(i).ZonePtr) {
    1012            0 :                             break;
    1013              :                         }
    1014              :                     }
    1015            0 :                     comfortZone.SpecificObjectNum = i;
    1016              :                 }
    1017              :                 // Check values used for thermal comfort calculation
    1018            0 :                 for (i = 1; i <= state.dataHeatBal->TotPeople; ++i) {
    1019            0 :                     auto &people = state.dataHeatBal->People(i);
    1020              : 
    1021            0 :                     if (comfortZone.ActualZoneNum != people.ZonePtr) {
    1022            0 :                         continue;
    1023              :                     }
    1024              : 
    1025              :                     // Check activity level
    1026            0 :                     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            0 :                     } 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            0 :                     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            0 :                     } 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            0 :                     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            0 :                     } 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            0 :                     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            0 :                 if (NumNums > 0) {
    1068            0 :                     comfortZone.TdbMinSetPoint = s_ipsc->rNumericArgs(1);
    1069            0 :                     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            0 :                 if (NumNums > 1) {
    1081            0 :                     comfortZone.TdbMaxSetPoint = s_ipsc->rNumericArgs(2);
    1082            0 :                     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            0 :                 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            0 :                 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            0 :                 if (s_ipsc->lAlphaFieldBlanks(5)) {
    1109            0 :                     ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
    1110            0 :                     ErrorsFound = true;
    1111            0 :                 } 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            0 :                 } 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            0 :                 int NumSetptTypes = nint((NumAlphas - 5.0) / 2.0);
    1120              : 
    1121            0 :                 for (int iSetptType = 1; iSetptType <= NumSetptTypes; ++iSetptType) {
    1122              : 
    1123            0 :                     int ctIdx = 2 * iSetptType - 1 + 5;
    1124              : 
    1125            0 :                     HVAC::SetptType setptType = HVAC::SetptType::Invalid;
    1126            0 :                     if (s_ipsc->lAlphaFieldBlanks(ctIdx)) {
    1127            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(ctIdx));
    1128            0 :                         ErrorsFound = true;
    1129            0 :                         continue;
    1130            0 :                     } 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            0 :                     auto &setpt = comfortZone.setpts[(int)setptType];
    1138            0 :                     setpt.Name = s_ipsc->cAlphaArgs(nint(2.0 * iSetptType + 5));
    1139            0 :                     setpt.isUsed = true;
    1140              :                 }
    1141              :             }
    1142              :         } // NumComfortTStatStatements
    1143              :     }
    1144              :     // End of Thermal comfort control reading and checking
    1145              : 
    1146          111 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::SingleHeat];
    1147          111 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1148              : 
    1149          111 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat] > 0) {
    1150            0 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeat].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat]);
    1151              :     }
    1152              : 
    1153          111 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeat]; ++idx) {
    1154            0 :         s_ip->getObjectItem(state,
    1155            0 :                             s_ipsc->cCurrentModuleObject,
    1156              :                             idx,
    1157            0 :                             s_ipsc->cAlphaArgs,
    1158              :                             NumAlphas,
    1159            0 :                             s_ipsc->rNumericArgs,
    1160              :                             NumNums,
    1161              :                             IOStat,
    1162            0 :                             s_ipsc->lNumericFieldBlanks,
    1163            0 :                             s_ipsc->lAlphaFieldBlanks,
    1164            0 :                             s_ipsc->cAlphaFieldNames,
    1165            0 :                             s_ipsc->cNumericFieldNames);
    1166              : 
    1167            0 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1168              : 
    1169            0 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1170            0 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeat](idx);
    1171            0 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1172              : 
    1173            0 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1174            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1175            0 :             ErrorsFound = true;
    1176            0 :         } 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            0 :         } 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          111 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::SingleCool];
    1186          111 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1187              : 
    1188          111 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool] > 0) {
    1189            0 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleCool].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool]);
    1190              :     }
    1191              : 
    1192          111 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleCool]; ++idx) {
    1193            0 :         s_ip->getObjectItem(state,
    1194            0 :                             s_ipsc->cCurrentModuleObject,
    1195              :                             idx,
    1196            0 :                             s_ipsc->cAlphaArgs,
    1197              :                             NumAlphas,
    1198            0 :                             s_ipsc->rNumericArgs,
    1199              :                             NumNums,
    1200              :                             IOStat,
    1201            0 :                             s_ipsc->lNumericFieldBlanks,
    1202            0 :                             s_ipsc->lAlphaFieldBlanks,
    1203            0 :                             s_ipsc->cAlphaFieldNames,
    1204            0 :                             s_ipsc->cNumericFieldNames);
    1205              : 
    1206            0 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1207              : 
    1208            0 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1209            0 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleCool](idx);
    1210            0 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1211              : 
    1212            0 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1213            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1214            0 :             ErrorsFound = true;
    1215            0 :         } 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            0 :         } 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          111 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::SingleHeatCool];
    1226          111 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1227              : 
    1228          111 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool] > 0) {
    1229            0 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeatCool].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool]);
    1230              :     }
    1231              : 
    1232          111 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::SingleHeatCool]; ++idx) {
    1233            0 :         s_ip->getObjectItem(state,
    1234            0 :                             s_ipsc->cCurrentModuleObject,
    1235              :                             idx,
    1236            0 :                             s_ipsc->cAlphaArgs,
    1237              :                             NumAlphas,
    1238            0 :                             s_ipsc->rNumericArgs,
    1239              :                             NumNums,
    1240              :                             IOStat,
    1241            0 :                             s_ipsc->lNumericFieldBlanks,
    1242            0 :                             s_ipsc->lAlphaFieldBlanks,
    1243            0 :                             s_ipsc->cAlphaFieldNames,
    1244            0 :                             s_ipsc->cNumericFieldNames);
    1245              : 
    1246            0 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1247              : 
    1248            0 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1249            0 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::SingleHeatCool](idx);
    1250            0 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1251              : 
    1252            0 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1253            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1254            0 :             ErrorsFound = true;
    1255            0 :         } 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            0 :         } 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          111 :     s_ipsc->cCurrentModuleObject = comfortSetptTypeNames[(int)HVAC::SetptType::DualHeatCool];
    1266          111 :     s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool] = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1267              : 
    1268          111 :     if (s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool] > 0) {
    1269            0 :         s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::DualHeatCool].allocate(s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool]);
    1270              :     }
    1271              : 
    1272          111 :     for (int idx = 1; idx <= s_ztpc->NumComfortControls[(int)HVAC::SetptType::DualHeatCool]; ++idx) {
    1273            0 :         s_ip->getObjectItem(state,
    1274            0 :                             s_ipsc->cCurrentModuleObject,
    1275              :                             idx,
    1276            0 :                             s_ipsc->cAlphaArgs,
    1277              :                             NumAlphas,
    1278            0 :                             s_ipsc->rNumericArgs,
    1279              :                             NumNums,
    1280              :                             IOStat,
    1281            0 :                             s_ipsc->lNumericFieldBlanks,
    1282            0 :                             s_ipsc->lAlphaFieldBlanks,
    1283            0 :                             s_ipsc->cAlphaFieldNames,
    1284            0 :                             s_ipsc->cNumericFieldNames);
    1285              : 
    1286            0 :         ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1287              : 
    1288            0 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1289            0 :         auto &setpt = s_ztpc->comfortSetptScheds[(int)HVAC::SetptType::DualHeatCool](idx);
    1290            0 :         setpt.Name = s_ipsc->cAlphaArgs(1);
    1291              : 
    1292            0 :         if (s_ipsc->lAlphaFieldBlanks(2)) {
    1293            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1294            0 :             ErrorsFound = true;
    1295            0 :         } 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            0 :         } 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            0 :         if (s_ipsc->lAlphaFieldBlanks(3)) {
    1304            0 :             ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
    1305            0 :             ErrorsFound = true;
    1306            0 :         } 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            0 :         } 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          111 :     for (ComfortControlledZoneNum = 1; ComfortControlledZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++ComfortControlledZoneNum) {
    1318              : 
    1319            0 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
    1320              : 
    1321            0 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    1322            0 :             auto &setpt = comfortZone.setpts[(int)setptType];
    1323            0 :             if (!setpt.isUsed) {
    1324            0 :                 continue;
    1325              :             }
    1326              : 
    1327            0 :             int setptIdx = Util::FindItem(setpt.Name, s_ztpc->comfortSetptScheds[(int)setptType]);
    1328              : 
    1329            0 :             if (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
    1330              :                 setptType == HVAC::SetptType::DualHeatCool) {
    1331            0 :                 setpt.heatSetptSched = s_ztpc->comfortSetptScheds[(int)setptType](setptIdx).heatSched;
    1332              :             }
    1333              : 
    1334            0 :             if (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
    1335              :                 setptType == HVAC::SetptType::DualHeatCool) {
    1336            0 :                 setpt.coolSetptSched = s_ztpc->comfortSetptScheds[(int)setptType](setptIdx).coolSched;
    1337              :             }
    1338              : 
    1339            0 :             TComfortControlTypes(ComfortControlledZoneNum).MustHave[(int)setptType] = true;
    1340              :         }
    1341              :     }
    1342              : 
    1343              :     // Now, Check the schedule values/indices for validity for Thermal Comfort Control
    1344              : 
    1345          111 :     for (ComfortControlledZoneNum = 1; ComfortControlledZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++ComfortControlledZoneNum) {
    1346            0 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
    1347            0 :         ActualZoneNum = comfortZone.ActualZoneNum;
    1348            0 :         if (comfortZone.setptTypeSched == nullptr) {
    1349            0 :             continue;
    1350              :         }
    1351              : 
    1352            0 :         int SchedMin = comfortZone.setptTypeSched->getMinVal(state);
    1353            0 :         int SchedMax = comfortZone.setptTypeSched->getMaxVal(state);
    1354              : 
    1355            0 :         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            0 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    1365            0 :             auto const &setpt = comfortZone.setpts[(int)setptType];
    1366            0 :             if (!setpt.isUsed) {
    1367            0 :                 continue;
    1368              :             }
    1369              : 
    1370            0 :             TComfortControlTypes(ComfortControlledZoneNum).DidHave[(int)setptType] = true;
    1371              : 
    1372            0 :             if (setpt.heatSetptSched == nullptr &&
    1373            0 :                 (setptType == HVAC::SetptType::SingleHeat || setptType == HVAC::SetptType::SingleHeatCool ||
    1374            0 :                  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            0 :             if (setpt.coolSetptSched == nullptr &&
    1386            0 :                 (setptType == HVAC::SetptType::SingleCool || setptType == HVAC::SetptType::SingleHeatCool ||
    1387            0 :                  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          111 :     for (int ComfortControlledZoneNum = 1; ComfortControlledZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++ComfortControlledZoneNum) {
    1401              : 
    1402            0 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlledZoneNum);
    1403            0 :         ActualZoneNum = comfortZone.ActualZoneNum;
    1404            0 :         if (comfortZone.setptTypeSched == nullptr) {
    1405            0 :             continue;
    1406              :         }
    1407              : 
    1408            0 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    1409            0 :             if (TComfortControlTypes(ComfortControlledZoneNum).MustHave[(int)setptType] &&
    1410            0 :                 TComfortControlTypes(ComfortControlledZoneNum).DidHave[(int)setptType]) {
    1411            0 :                 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          111 :     if (allocated(TComfortControlTypes)) {
    1427            0 :         TComfortControlTypes.deallocate();
    1428              :     }
    1429              : 
    1430              :     // Get the Hybrid Model setting inputs
    1431          111 :     HybridModel::GetHybridModelZone(state);
    1432              : 
    1433              :     // Default multiplier values
    1434          111 :     Real64 ZoneVolCapMultpSens = 1.0;
    1435          111 :     Real64 ZoneVolCapMultpMoist = 1.0;
    1436          111 :     Real64 ZoneVolCapMultpCO2 = 1.0;
    1437          111 :     Real64 ZoneVolCapMultpGenContam = 1.0;
    1438              : 
    1439              :     // Get the Zone Air Capacitance Multiplier for use in the Predictor-Corrector Procedure
    1440          111 :     s_ipsc->cCurrentModuleObject = "ZoneCapacitanceMultiplier:ResearchSpecial";
    1441          111 :     int NumZoneCapaMultiplier = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject); // Number of ZonesCapacityMultiplier object
    1442          111 :     if (NumZoneCapaMultiplier == 0) {
    1443              :         // Assign default multiplier values to all zones
    1444          257 :         for (int ZoneNum = 1; ZoneNum <= NumOfZones; ZoneNum++) {
    1445          146 :             auto &Zone = state.dataHeatBal->Zone(ZoneNum);
    1446          146 :             Zone.ZoneVolCapMultpSens = ZoneVolCapMultpSens;
    1447          146 :             Zone.ZoneVolCapMultpMoist = ZoneVolCapMultpMoist;
    1448          146 :             Zone.ZoneVolCapMultpCO2 = ZoneVolCapMultpCO2;
    1449          146 :             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            0 :         for (int ZoneCapNum = 1; ZoneCapNum <= NumZoneCapaMultiplier; ZoneCapNum++) {
    1458            0 :             s_ip->getObjectItem(state,
    1459            0 :                                 s_ipsc->cCurrentModuleObject,
    1460              :                                 ZoneCapNum,
    1461            0 :                                 s_ipsc->cAlphaArgs,
    1462              :                                 NumAlphas,
    1463            0 :                                 s_ipsc->rNumericArgs,
    1464              :                                 NumNums,
    1465              :                                 IOStat,
    1466            0 :                                 s_ipsc->lNumericFieldBlanks,
    1467            0 :                                 s_ipsc->lAlphaFieldBlanks,
    1468            0 :                                 s_ipsc->cAlphaFieldNames,
    1469            0 :                                 s_ipsc->cNumericFieldNames);
    1470              : 
    1471            0 :             if (s_ipsc->lAlphaFieldBlanks(2)) {
    1472              :                 // default multiplier values for all the zones not specified (zone or zonelist name field is empty)
    1473            0 :                 ZoneVolCapMultpSens = s_ipsc->rNumericArgs(1);
    1474            0 :                 ZoneVolCapMultpMoist = s_ipsc->rNumericArgs(2);
    1475            0 :                 ZoneVolCapMultpCO2 = s_ipsc->rNumericArgs(3);
    1476            0 :                 ZoneVolCapMultpGenContam = s_ipsc->rNumericArgs(4);
    1477              :             } else {
    1478              :                 // multiplier values for the specified zone(s)
    1479            0 :                 ZLItem = 0;
    1480              : 
    1481            0 :                 Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
    1482            0 :                 if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
    1483            0 :                     ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
    1484              :                 }
    1485            0 :                 if (Item1 > 0) {
    1486            0 :                     int ZoneNum = Item1;
    1487            0 :                     Zone(ZoneNum).FlagCustomizedZoneCap = true;
    1488            0 :                     Zone(ZoneNum).ZoneVolCapMultpSens = s_ipsc->rNumericArgs(1);
    1489            0 :                     Zone(ZoneNum).ZoneVolCapMultpMoist = s_ipsc->rNumericArgs(2);
    1490            0 :                     Zone(ZoneNum).ZoneVolCapMultpCO2 = s_ipsc->rNumericArgs(3);
    1491            0 :                     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            0 :         for (int ZoneNum = 1; ZoneNum <= NumOfZones; ZoneNum++) {
    1516            0 :             auto &Zone = state.dataHeatBal->Zone(ZoneNum);
    1517            0 :             if (!Zone.FlagCustomizedZoneCap) {
    1518            0 :                 Zone.ZoneVolCapMultpSens = ZoneVolCapMultpSens;
    1519            0 :                 Zone.ZoneVolCapMultpMoist = ZoneVolCapMultpMoist;
    1520            0 :                 Zone.ZoneVolCapMultpCO2 = ZoneVolCapMultpCO2;
    1521            0 :                 Zone.ZoneVolCapMultpGenContam = ZoneVolCapMultpGenContam;
    1522              :             }
    1523              :         }
    1524              : 
    1525              :         // Calculate the average multiplier value from all zones
    1526              :         {
    1527            0 :             Real64 ZoneVolCapMultpSens_temp = 0.0;
    1528            0 :             Real64 ZoneVolCapMultpMoist_temp = 0.0;
    1529            0 :             Real64 ZoneVolCapMultpCO2_temp = 0.0;
    1530            0 :             Real64 ZoneVolCapMultpGenContam_temp = 0.0;
    1531              : 
    1532            0 :             for (int ZoneNum = 1; ZoneNum <= NumOfZones; ZoneNum++) {
    1533            0 :                 auto const &Zone = state.dataHeatBal->Zone(ZoneNum);
    1534            0 :                 ZoneVolCapMultpSens_temp += Zone.ZoneVolCapMultpSens;
    1535            0 :                 ZoneVolCapMultpMoist_temp += Zone.ZoneVolCapMultpMoist;
    1536            0 :                 ZoneVolCapMultpCO2_temp += Zone.ZoneVolCapMultpCO2;
    1537            0 :                 ZoneVolCapMultpGenContam_temp += Zone.ZoneVolCapMultpGenContam;
    1538              :             }
    1539              : 
    1540            0 :             if (NumOfZones > 0) {
    1541            0 :                 ZoneVolCapMultpSens = ZoneVolCapMultpSens_temp / NumOfZones;
    1542            0 :                 ZoneVolCapMultpMoist = ZoneVolCapMultpMoist_temp / NumOfZones;
    1543            0 :                 ZoneVolCapMultpCO2 = ZoneVolCapMultpCO2_temp / NumOfZones;
    1544            0 :                 ZoneVolCapMultpGenContam = ZoneVolCapMultpGenContam_temp / NumOfZones;
    1545              :             }
    1546              :         }
    1547              :     }
    1548              : 
    1549          111 :     print(state.files.eio, Header);
    1550          111 :     print(state.files.eio, Format_701, ZoneVolCapMultpSens, ZoneVolCapMultpMoist, ZoneVolCapMultpCO2, ZoneVolCapMultpGenContam);
    1551              : 
    1552          111 :     s_ipsc->cCurrentModuleObject = cZControlTypes((int)ZoneControlTypes::OTTStat);
    1553          111 :     state.dataZoneCtrls->NumOpTempControlledZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1554              : 
    1555          111 :     if (state.dataZoneCtrls->NumOpTempControlledZones > 0) {
    1556            0 :         state.dataZoneCtrls->AnyOpTempControl = true;
    1557              : 
    1558            0 :         for (int idx = 1; idx <= state.dataZoneCtrls->NumOpTempControlledZones; ++idx) {
    1559            0 :             s_ip->getObjectItem(state,
    1560            0 :                                 s_ipsc->cCurrentModuleObject,
    1561              :                                 idx,
    1562            0 :                                 s_ipsc->cAlphaArgs,
    1563              :                                 NumAlphas,
    1564            0 :                                 s_ipsc->rNumericArgs,
    1565              :                                 NumNums,
    1566              :                                 IOStat,
    1567            0 :                                 s_ipsc->lNumericFieldBlanks,
    1568            0 :                                 s_ipsc->lAlphaFieldBlanks,
    1569            0 :                                 s_ipsc->cAlphaFieldNames,
    1570            0 :                                 s_ipsc->cNumericFieldNames);
    1571              : 
    1572            0 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1573              : 
    1574              :             // find matching name of  ZONECONTROL:THERMOSTAT object
    1575            0 :             if ((found = Util::FindItem(s_ipsc->cAlphaArgs(1), TStatObjects)) != 0) {
    1576            0 :                 auto const &TStatObjects = state.dataZoneCtrls->TStatObjects(found);
    1577            0 :                 for (Item = 1; Item <= TStatObjects.NumOfZones; ++Item) {
    1578            0 :                     TempControlledZoneNum = TStatObjects.TempControlledZoneStartPtr + Item - 1;
    1579            0 :                     auto &TempControlledZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1580            0 :                     if (state.dataZoneCtrls->NumTempControlledZones == 0) {
    1581            0 :                         continue;
    1582              :                     }
    1583            0 :                     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1584            0 :                     tempZone.OpTempCtrl =
    1585            0 :                         static_cast<DataZoneControls::TempCtrl>(getEnumValue(DataZoneControls::tempCtrlNamesUC, s_ipsc->cAlphaArgs(2)));
    1586              : 
    1587            0 :                     if (Item == 1) {
    1588            0 :                         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            0 :                     tempZone.FixedRadiativeFraction = s_ipsc->rNumericArgs(1);
    1596              : 
    1597            0 :                     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            0 :                         if (Item == 1) {
    1620            0 :                             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            0 :                             } 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            0 :                     if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Constant || tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1633            0 :                         if (NumAlphas >= 4 && !s_ipsc->lAlphaFieldBlanks(4)) {
    1634              :                             int adaptiveComfortModelTypeIndex =
    1635            0 :                                 Util::FindItem(s_ipsc->cAlphaArgs(4), AdaptiveComfortModelTypes, AdaptiveComfortModelTypes.isize());
    1636            0 :                             if (!adaptiveComfortModelTypeIndex) {
    1637            0 :                                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(4), s_ipsc->cAlphaArgs(4));
    1638            0 :                                 ErrorsFound = true;
    1639            0 :                             } else if (adaptiveComfortModelTypeIndex != static_cast<int>(AdaptiveComfortModel::ADAP_NONE)) {
    1640            0 :                                 tempZone.AdaptiveComfortTempControl = true;
    1641            0 :                                 tempZone.AdaptiveComfortModelTypeIndex =
    1642            0 :                                     Util::FindItem(s_ipsc->cAlphaArgs(4), AdaptiveComfortModelTypes, AdaptiveComfortModelTypes.isize());
    1643            0 :                                 if (!s_ztpc->AdapComfortDailySetPointSchedule.initialized) {
    1644            0 :                                     Array1D<Real64> runningAverageASH(state.dataWeather->NumDaysInYear, 0.0);
    1645            0 :                                     Array1D<Real64> runningAverageCEN(state.dataWeather->NumDaysInYear, 0.0);
    1646            0 :                                     CalculateMonthlyRunningAverageDryBulb(state, runningAverageASH, runningAverageCEN);
    1647            0 :                                     CalculateAdaptiveComfortSetPointSchl(state, runningAverageASH, runningAverageCEN);
    1648            0 :                                 }
    1649              :                             }
    1650              :                         }
    1651              :                     }
    1652              : 
    1653              :                     // CurrentModuleObject='ZoneControl:Thermostat:OperativeTemperature'
    1654            0 :                     SetupOutputVariable(state,
    1655              :                                         "Zone Thermostat Operative Temperature",
    1656              :                                         Constant::Units::C,
    1657            0 :                                         state.dataHeatBal->ZnAirRpt(tempZone.ActualZoneNum).ThermOperativeTemp,
    1658              :                                         OutputProcessor::TimeStepType::Zone,
    1659              :                                         OutputProcessor::StoreType::Average,
    1660            0 :                                         Zone(tempZone.ActualZoneNum).Name);
    1661              :                 } // TStat Objects Loop
    1662              : 
    1663              :                 // It might be in the TempControlledZones
    1664            0 :             } else if ((found = Util::FindItem(s_ipsc->cAlphaArgs(1), state.dataZoneCtrls->TempControlledZone)) != 0) {
    1665              : 
    1666            0 :                 TempControlledZoneNum = found;
    1667            0 :                 auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1668            0 :                 tempZone.OpTempCtrl = static_cast<DataZoneControls::TempCtrl>(getEnumValue(DataZoneControls::tempCtrlNamesUC, s_ipsc->cAlphaArgs(2)));
    1669            0 :                 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            0 :                 tempZone.FixedRadiativeFraction = s_ipsc->rNumericArgs(1);
    1675              : 
    1676            0 :                 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            0 :                     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            0 :                     } 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            0 :                 if (tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Constant || tempZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled) {
    1703            0 :                     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            0 :                     SetupOutputVariable(state,
    1724              :                                         "Zone Thermostat Operative Temperature",
    1725              :                                         Constant::Units::C,
    1726            0 :                                         state.dataHeatBal->ZnAirRpt(tempZone.ActualZoneNum).ThermOperativeTemp,
    1727              :                                         OutputProcessor::TimeStepType::Zone,
    1728              :                                         OutputProcessor::StoreType::Average,
    1729            0 :                                         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          111 :     s_ipsc->cCurrentModuleObject = cZControlTypes((int)ZoneControlTypes::TandHStat);
    1745          111 :     state.dataZoneCtrls->NumTempAndHumidityControlledZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1746          111 :     if (state.dataZoneCtrls->NumTempAndHumidityControlledZones > 0) {
    1747            0 :         state.dataZoneCtrls->AnyZoneTempAndHumidityControl = true;
    1748              : 
    1749            0 :         for (int idx = 1; idx <= state.dataZoneCtrls->NumTempAndHumidityControlledZones; ++idx) {
    1750            0 :             s_ip->getObjectItem(state,
    1751            0 :                                 s_ipsc->cCurrentModuleObject,
    1752              :                                 idx,
    1753            0 :                                 s_ipsc->cAlphaArgs,
    1754              :                                 NumAlphas,
    1755            0 :                                 s_ipsc->rNumericArgs,
    1756              :                                 NumNums,
    1757              :                                 IOStat,
    1758            0 :                                 s_ipsc->lNumericFieldBlanks,
    1759            0 :                                 s_ipsc->lAlphaFieldBlanks,
    1760            0 :                                 s_ipsc->cAlphaFieldNames,
    1761            0 :                                 s_ipsc->cNumericFieldNames);
    1762              : 
    1763            0 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1764              : 
    1765              :             // find matching name of  ZONECONTROL:THERMOSTAT object
    1766            0 :             found = Util::FindItem(s_ipsc->cAlphaArgs(1), TStatObjects);
    1767            0 :             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            0 :                 for (int Item = 1; Item <= TStatObjects(found).NumOfZones; ++Item) {
    1831            0 :                     TempControlledZoneNum = TStatObjects(found).TempControlledZoneStartPtr + Item - 1;
    1832              : 
    1833            0 :                     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneNum);
    1834              : 
    1835            0 :                     if (s_ipsc->lAlphaFieldBlanks(2)) {
    1836            0 :                         ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
    1837            0 :                         ErrorsFound = true;
    1838            0 :                     } 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            0 :                     if (s_ipsc->cAlphaArgs(3) == "NONE") {
    1845            0 :                         tempZone.OvercoolCtrl = DataZoneControls::TempCtrl::None;
    1846            0 :                     } 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            0 :                     } else if ((tempZone.OvercoolCtrl = static_cast<DataZoneControls::TempCtrl>(
    1852            0 :                                     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            0 :                     tempZone.ZoneOvercoolConstRange = s_ipsc->rNumericArgs(1);
    1860              : 
    1861            0 :                     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            0 :                         if (Item == 1) {
    1881            0 :                             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            0 :                             } 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            0 :                     tempZone.ZoneOvercoolControlRatio = s_ipsc->rNumericArgs(2);
    1892              :                     // check Overcool Control Ratio limits
    1893            0 :                     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          111 :     s_ipsc->cCurrentModuleObject = cZControlTypes((int)ZoneControlTypes::StagedDual);
    1906          111 :     NumStageControlledZones = s_ip->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
    1907          111 :     if (NumStageControlledZones > 0) {
    1908            0 :         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          111 :     s_ztpc->NumStageCtrZone = 0;
    1913          111 :     for (Item = 1; Item <= NumStageControlledZones; ++Item) {
    1914            0 :         s_ip->getObjectItem(state,
    1915            0 :                             s_ipsc->cCurrentModuleObject,
    1916              :                             Item,
    1917            0 :                             s_ipsc->cAlphaArgs,
    1918              :                             NumAlphas,
    1919            0 :                             s_ipsc->rNumericArgs,
    1920              :                             NumNums,
    1921              :                             IOStat,
    1922            0 :                             s_ipsc->lNumericFieldBlanks,
    1923            0 :                             s_ipsc->lAlphaFieldBlanks,
    1924            0 :                             s_ipsc->cAlphaFieldNames,
    1925            0 :                             s_ipsc->cNumericFieldNames);
    1926            0 :         Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), s_ipsc->cCurrentModuleObject, ErrorsFound);
    1927              : 
    1928            0 :         state.dataZoneCtrls->StagedTStatObjects(Item).Name = s_ipsc->cAlphaArgs(1);
    1929            0 :         Item1 = Util::FindItemInList(s_ipsc->cAlphaArgs(2), Zone);
    1930            0 :         ZLItem = 0;
    1931            0 :         if (Item1 == 0 && state.dataHeatBal->NumOfZoneLists > 0) {
    1932            0 :             ZLItem = Util::FindItemInList(s_ipsc->cAlphaArgs(2), ZoneList);
    1933              :         }
    1934            0 :         if (Item1 > 0) {
    1935            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).StageControlledZoneStartPtr = s_ztpc->NumStageCtrZone + 1;
    1936            0 :             ++s_ztpc->NumStageCtrZone;
    1937            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).NumOfZones = 1;
    1938            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive = false;
    1939            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr = Item1;
    1940            0 :         } else if (ZLItem > 0) {
    1941            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).TempControlledZoneStartPtr = s_ztpc->NumStageCtrZone + 1;
    1942            0 :             s_ztpc->NumStageCtrZone += ZoneList(ZLItem).NumOfZones;
    1943            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).NumOfZones = ZoneList(ZLItem).NumOfZones;
    1944            0 :             state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive = true;
    1945            0 :             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          111 :     if (ErrorsFound) {
    1958            1 :         ShowSevereError(state, format("GetStagedDualSetpoint: Errors with invalid names in {} objects.", s_ipsc->cCurrentModuleObject));
    1959            2 :         ShowContinueError(state, "...These will not be read in.  Other errors may occur.");
    1960            1 :         s_ztpc->NumStageCtrZone = 0;
    1961              :     }
    1962              : 
    1963          111 :     if (s_ztpc->NumStageCtrZone > 0) {
    1964            0 :         state.dataZoneCtrls->StageControlledZone.allocate(s_ztpc->NumStageCtrZone);
    1965            0 :         state.dataZoneCtrls->StageZoneLogic.dimension(NumOfZones, false);
    1966              : 
    1967            0 :         int StageControlledZoneNum = 0;
    1968            0 :         for (Item = 1; Item <= NumStageControlledZones; ++Item) {
    1969            0 :             s_ip->getObjectItem(state,
    1970            0 :                                 s_ipsc->cCurrentModuleObject,
    1971              :                                 Item,
    1972            0 :                                 s_ipsc->cAlphaArgs,
    1973              :                                 NumAlphas,
    1974            0 :                                 s_ipsc->rNumericArgs,
    1975              :                                 NumNums,
    1976              :                                 IOStat,
    1977            0 :                                 s_ipsc->lNumericFieldBlanks,
    1978            0 :                                 s_ipsc->lAlphaFieldBlanks,
    1979            0 :                                 s_ipsc->cAlphaFieldNames,
    1980            0 :                                 s_ipsc->cNumericFieldNames);
    1981              : 
    1982            0 :             ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
    1983              : 
    1984            0 :             for (Item1 = 1; Item1 <= state.dataZoneCtrls->StagedTStatObjects(Item).NumOfZones; ++Item1) {
    1985            0 :                 ++StageControlledZoneNum;
    1986              : 
    1987            0 :                 auto &stageZone = state.dataZoneCtrls->StageControlledZone(StageControlledZoneNum);
    1988              : 
    1989            0 :                 if (state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive) {
    1990            0 :                     s_ipsc->cAlphaArgs(2) =
    1991            0 :                         state.dataHeatBal->Zone(ZoneList(state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name;
    1992              :                 }
    1993            0 :                 int ZoneAssigned = Util::FindItemInList(s_ipsc->cAlphaArgs(2),
    1994            0 :                                                         state.dataZoneCtrls->StageControlledZone,
    1995              :                                                         &DataZoneControls::ZoneStagedControls::ZoneName,
    1996              :                                                         StageControlledZoneNum - 1);
    1997            0 :                 if (ZoneAssigned == 0) {
    1998            0 :                     stageZone.ZoneName = s_ipsc->cAlphaArgs(2);
    1999            0 :                     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            0 :                     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            0 :                 if (!state.dataZoneCtrls->StagedTStatObjects(Item).ZoneListActive) {
    2023            0 :                     stageZone.Name = s_ipsc->cAlphaArgs(1);
    2024              :                 } else {
    2025            0 :                     CheckCreatedZoneItemName(
    2026              :                         state,
    2027              :                         RoutineName,
    2028            0 :                         s_ipsc->cCurrentModuleObject,
    2029            0 :                         state.dataHeatBal->Zone(ZoneList(state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr).Zone(Item1)).Name,
    2030            0 :                         ZoneList(state.dataZoneCtrls->StagedTStatObjects(Item).ZoneOrZoneListPtr).MaxZoneNameLength,
    2031            0 :                         state.dataZoneCtrls->StagedTStatObjects(Item).Name,
    2032            0 :                         state.dataZoneCtrls->StageControlledZone,
    2033              :                         StageControlledZoneNum - 1,
    2034            0 :                         stageZone.Name,
    2035              :                         errFlag);
    2036            0 :                     if (errFlag) {
    2037            0 :                         ErrorsFound = true;
    2038              :                     }
    2039              :                 }
    2040              : 
    2041            0 :                 stageZone.NumOfHeatStages = s_ipsc->rNumericArgs(1);
    2042            0 :                 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            0 :                 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            0 :                 } 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            0 :                 stageZone.HeatThroRange = s_ipsc->rNumericArgs(2);
    2060            0 :                 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            0 :                 if (stageZone.NumOfHeatStages > 0) {
    2066            0 :                     stageZone.HeatTOffset.allocate(stageZone.NumOfHeatStages);
    2067            0 :                     for (i = 1; i <= stageZone.NumOfHeatStages; ++i) {
    2068            0 :                         stageZone.HeatTOffset(i) = s_ipsc->rNumericArgs(2 + i);
    2069            0 :                         if (s_ipsc->lNumericFieldBlanks(2 + i)) {
    2070            0 :                             ShowSevereEmptyField(state, eoh, s_ipsc->cNumericFieldNames(2 + i));
    2071            0 :                             ErrorsFound = true;
    2072            0 :                         } 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            0 :                         if (i > 1) {
    2078            0 :                             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            0 :                 stageZone.NumOfCoolStages = s_ipsc->rNumericArgs(7);
    2093            0 :                 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            0 :                 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            0 :                 } 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            0 :                 stageZone.CoolThroRange = s_ipsc->rNumericArgs(8);
    2111            0 :                 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            0 :                 if (stageZone.NumOfCoolStages > 0) {
    2117            0 :                     stageZone.CoolTOffset.allocate(stageZone.NumOfCoolStages);
    2118            0 :                     for (i = 1; i <= stageZone.NumOfCoolStages; ++i) {
    2119            0 :                         stageZone.CoolTOffset(i) = s_ipsc->rNumericArgs(8 + i);
    2120            0 :                         if (s_ipsc->lNumericFieldBlanks(8 + i)) {
    2121            0 :                             ShowSevereEmptyField(state, eoh, s_ipsc->cNumericFieldNames(8 + i));
    2122            0 :                             ErrorsFound = true;
    2123            0 :                         } 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            0 :                         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            0 :         if ((s_ip->getNumObjectsFound(state, "AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed") == 0) &&
    2144            0 :             (s_ip->getNumObjectsFound(state, "AirLoopHVAC:UnitarySystem") == 0) &&
    2145            0 :             (s_ip->getNumObjectsFound(state, "SetpointManager:SingleZone:OneStageCooling") == 0) &&
    2146            0 :             (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          111 :     if (ErrorsFound) {
    2157            3 :         ShowFatalError(state, "Errors getting Zone Control input data.  Preceding condition(s) cause termination.");
    2158              :     }
    2159          114 : }
    2160              : 
    2161            0 : 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            0 :     Array1D<Real64> adaptiveTemp(state.dataWeather->NumDaysInYear, 0.0);
    2185            0 :     Array1D<Real64> dailyDryTemp(state.dataWeather->NumDaysInYear, 0.0);
    2186              : 
    2187            0 :     if (FileSystem::fileExists(state.files.inputWeatherFilePath.filePath)) {
    2188              :         // Read hourly dry bulb temperature first
    2189            0 :         auto epwFile = state.files.inputWeatherFilePath.open(state, "CalcThermalComfortAdaptive");
    2190            0 :         for (int i = 1; i <= 9; ++i) { // Headers
    2191            0 :             epwFile.readLine();
    2192              :         }
    2193            0 :         for (int i = 1; i <= state.dataWeather->NumDaysInYear; ++i) {
    2194            0 :             avgDryBulb = 0.0;
    2195            0 :             for (int j = 1; j <= 24; ++j) {
    2196            0 :                 std::string epwLine = epwFile.readLine().data;
    2197            0 :                 for (int ind = 1; ind <= 6; ++ind) {
    2198            0 :                     pos = index(epwLine, ',');
    2199            0 :                     epwLine.erase(0, pos + 1);
    2200              :                 }
    2201            0 :                 pos = index(epwLine, ',');
    2202            0 :                 dryBulb = StrToReal(epwLine.substr(0, pos));
    2203            0 :                 avgDryBulb += (dryBulb / 24.0);
    2204            0 :             }
    2205            0 :             dailyDryTemp(i) = avgDryBulb;
    2206              :         }
    2207            0 :         epwFile.close();
    2208              : 
    2209              :         // Calculate monthly running average dry bulb temperature.
    2210            0 :         int dayOfYear = 0;
    2211            0 :         while (dayOfYear < state.dataWeather->NumDaysInYear) {
    2212            0 :             dayOfYear++;
    2213            0 :             int calcEndDay = dayOfYear - 1;
    2214            0 :             int calcStartDayASH = calcEndDay - 30;
    2215            0 :             int calcStartDayCEN = calcEndDay - 7;
    2216              : 
    2217            0 :             if (calcStartDayASH > 0) {
    2218            0 :                 for (int i = calcStartDayASH; i <= calcStartDayASH + 30; i++) {
    2219            0 :                     avgDryBulb = dailyDryTemp(i);
    2220            0 :                     runningAverageASH(dayOfYear) = runningAverageASH(dayOfYear) + avgDryBulb;
    2221              :                 }
    2222            0 :                 runningAverageASH(dayOfYear) /= 30;
    2223              :             } else { // Do special things for wrapping the epw
    2224            0 :                 calcStartDayASH += state.dataWeather->NumDaysInYear;
    2225            0 :                 for (int i = 1; i <= calcEndDay; i++) {
    2226            0 :                     avgDryBulb = dailyDryTemp(i);
    2227            0 :                     runningAverageASH(dayOfYear) = runningAverageASH(dayOfYear) + avgDryBulb;
    2228              :                 }
    2229            0 :                 for (int i = calcStartDayASH; i < state.dataWeather->NumDaysInYear; i++) {
    2230            0 :                     avgDryBulb = dailyDryTemp(i);
    2231            0 :                     runningAverageASH(dayOfYear) = runningAverageASH(dayOfYear) + avgDryBulb;
    2232              :                 }
    2233            0 :                 runningAverageASH(dayOfYear) /= 30;
    2234              :             }
    2235              : 
    2236            0 :             if (calcStartDayCEN > 0) {
    2237            0 :                 for (int i = calcStartDayCEN; i <= calcStartDayCEN + 7; i++) {
    2238            0 :                     avgDryBulb = dailyDryTemp(i);
    2239            0 :                     runningAverageCEN(dayOfYear) = runningAverageCEN(dayOfYear) + avgDryBulb;
    2240              :                 }
    2241            0 :                 runningAverageCEN(dayOfYear) /= 7;
    2242              :             } else { // Do special things for wrapping the epw
    2243            0 :                 calcStartDayCEN += state.dataWeather->NumDaysInYear;
    2244            0 :                 for (int i = 1; i <= calcEndDay; i++) {
    2245            0 :                     avgDryBulb = dailyDryTemp(i);
    2246            0 :                     runningAverageCEN(dayOfYear) = runningAverageCEN(dayOfYear) + avgDryBulb;
    2247              :                 }
    2248            0 :                 for (int i = calcStartDayCEN; i < state.dataWeather->NumDaysInYear; i++) {
    2249            0 :                     avgDryBulb = dailyDryTemp(i);
    2250            0 :                     runningAverageCEN(dayOfYear) = runningAverageCEN(dayOfYear) + avgDryBulb;
    2251              :                 }
    2252            0 :                 runningAverageCEN(dayOfYear) /= 7;
    2253              :             }
    2254              :         }
    2255            0 :     } 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            0 : }
    2261              : 
    2262            3 : 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            3 :     int constexpr summerDesignDayTypeIndex(9);
    2274            3 :     Real64 GrossApproxAvgDryBulbDesignDay(0.0);
    2275              : 
    2276            3 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    2277              : 
    2278            6 :     for (size_t i = 1; i <= state.dataWeather->DesDayInput.size(); i++) {
    2279              :         // Summer design day
    2280            3 :         if (state.dataWeather->DesDayInput(i).DayType == summerDesignDayTypeIndex) {
    2281            3 :             GrossApproxAvgDryBulbDesignDay = (state.dataWeather->DesDayInput(i).MaxDryBulb +
    2282            3 :                                               (state.dataWeather->DesDayInput(i).MaxDryBulb - state.dataWeather->DesDayInput(i).DailyDBRange)) /
    2283              :                                              2.0;
    2284            3 :             if (GrossApproxAvgDryBulbDesignDay > 10 && GrossApproxAvgDryBulbDesignDay < 33.5) {
    2285            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[0] = 0.31 * GrossApproxAvgDryBulbDesignDay + 17.8;
    2286            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[1] = 0.31 * GrossApproxAvgDryBulbDesignDay + 20.3;
    2287            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[2] = 0.31 * GrossApproxAvgDryBulbDesignDay + 21.3;
    2288              :             }
    2289            3 :             if (GrossApproxAvgDryBulbDesignDay > 10 && GrossApproxAvgDryBulbDesignDay < 30) {
    2290            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[3] = 0.33 * GrossApproxAvgDryBulbDesignDay + 18.8;
    2291            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[4] = 0.33 * GrossApproxAvgDryBulbDesignDay + 20.8;
    2292              :                 ; // What is this?
    2293            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[5] = 0.33 * GrossApproxAvgDryBulbDesignDay + 21.8;
    2294              :                 ;
    2295            3 :                 s_ztpc->AdapComfortSetPointSummerDesDay[6] = 0.33 * GrossApproxAvgDryBulbDesignDay + 22.8;
    2296              :                 ;
    2297              :             }
    2298              :         }
    2299              :     }
    2300              : 
    2301            3 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central.allocate(state.dataWeather->NumDaysInYear);
    2302            3 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_90.allocate(state.dataWeather->NumDaysInYear);
    2303            3 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_80.allocate(state.dataWeather->NumDaysInYear);
    2304            3 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central.allocate(state.dataWeather->NumDaysInYear);
    2305            3 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I.allocate(state.dataWeather->NumDaysInYear);
    2306            3 :     s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II.allocate(state.dataWeather->NumDaysInYear);
    2307            3 :     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         1098 :     for (int day = 1; day <= state.dataWeather->NumDaysInYear; day++) {
    2311         1095 :         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          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central(day) = -1;
    2317          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_90(day) = -1;
    2318          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Upper_80(day) = -1;
    2319              :         }
    2320         1095 :         if (runningAverageCEN(day) > 10 && runningAverageCEN(day) < 30) {
    2321          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central(day) = 0.33 * runningAverageCEN(day) + 18.8;
    2322          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I(day) = 0.33 * runningAverageCEN(day) + 20.8;
    2323          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II(day) = 0.33 * runningAverageCEN(day) + 21.8;
    2324          365 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_III(day) = 0.33 * runningAverageCEN(day) + 22.8;
    2325              :         } else {
    2326          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central(day) = -1;
    2327          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_I(day) = -1;
    2328          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_II(day) = -1;
    2329          730 :             s_ztpc->AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Upper_III(day) = -1;
    2330              :         }
    2331              :     }
    2332            3 :     s_ztpc->AdapComfortDailySetPointSchedule.initialized = true;
    2333            3 : }
    2334              : 
    2335      1141716 : 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      1141716 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    2359      1141716 :     auto &s_hbfs = state.dataHeatBalFanSys;
    2360              : 
    2361      1141716 :     auto &ZoneList = state.dataHeatBal->ZoneList;
    2362      1141716 :     auto &TempControlType = state.dataHeatBalFanSys->TempControlType;
    2363      1141716 :     auto &TempControlTypeRpt = state.dataHeatBalFanSys->TempControlTypeRpt;
    2364      1141716 :     int NumOfZones = state.dataGlobal->NumOfZones;
    2365              : 
    2366      1141716 :     if (s_ztpc->InitZoneAirSetPointsOneTimeFlag) {
    2367          107 :         s_hbfs->zoneTstatSetpts.allocate(NumOfZones);
    2368              : 
    2369          107 :         state.dataHeatBalFanSys->LoadCorrectionFactor.dimension(NumOfZones, 0.0);
    2370          107 :         TempControlType.dimension(NumOfZones, HVAC::SetptType::Uncontrolled);
    2371          107 :         TempControlTypeRpt.dimension(NumOfZones, 0);
    2372          107 :         if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
    2373            0 :             state.dataHeatBalFanSys->ComfortControlType.dimension(NumOfZones, HVAC::SetptType::Uncontrolled);
    2374            0 :             state.dataHeatBalFanSys->ComfortControlTypeRpt.dimension(NumOfZones, 0);
    2375            0 :             state.dataHeatBalFanSys->ZoneComfortControlsFanger.allocate(NumOfZones);
    2376              :         }
    2377          107 :         state.dataZoneEnergyDemand->Setback.dimension(NumOfZones, false);
    2378          107 :         state.dataZoneEnergyDemand->DeadBandOrSetback.dimension(NumOfZones, false);
    2379          107 :         state.dataZoneEnergyDemand->CurDeadBandOrSetback.dimension(NumOfZones, false);
    2380              : 
    2381          107 :         state.dataHeatBal->ZoneListSNLoadHeatEnergy.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2382          107 :         state.dataHeatBal->ZoneListSNLoadCoolEnergy.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2383          107 :         state.dataHeatBal->ZoneListSNLoadHeatRate.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2384          107 :         state.dataHeatBal->ZoneListSNLoadCoolRate.dimension(state.dataHeatBal->NumOfZoneLists, 0.0);
    2385              : 
    2386          107 :         state.dataHeatBal->ZoneGroupSNLoadHeatEnergy.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2387          107 :         state.dataHeatBal->ZoneGroupSNLoadCoolEnergy.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2388          107 :         state.dataHeatBal->ZoneGroupSNLoadHeatRate.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2389          107 :         state.dataHeatBal->ZoneGroupSNLoadCoolRate.dimension(state.dataHeatBal->NumOfZoneGroups, 0.0);
    2390              : 
    2391              :         // Hybrid modeling
    2392          107 :         state.dataHeatBalFanSys->PreviousMeasuredZT1.dimension(NumOfZones, 0.0);
    2393          107 :         state.dataHeatBalFanSys->PreviousMeasuredZT2.dimension(NumOfZones, 0.0);
    2394          107 :         state.dataHeatBalFanSys->PreviousMeasuredZT3.dimension(NumOfZones, 0.0);
    2395          107 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat1.dimension(NumOfZones, 0.0);
    2396          107 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat2.dimension(NumOfZones, 0.0);
    2397          107 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat3.dimension(NumOfZones, 0.0);
    2398              : 
    2399              :         // Allocate Derived Types
    2400          107 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand.allocate(NumOfZones);
    2401          107 :         state.dataZoneEnergyDemand->ZoneSysMoistureDemand.allocate(NumOfZones);
    2402          107 :         if (state.dataHeatBal->doSpaceHeatBalanceSimulation || state.dataHeatBal->doSpaceHeatBalanceSizing) {
    2403            8 :             state.dataZoneEnergyDemand->spaceSysEnergyDemand.allocate(state.dataGlobal->numSpaces);
    2404            8 :             state.dataZoneEnergyDemand->spaceSysMoistureDemand.allocate(state.dataGlobal->numSpaces);
    2405              :         }
    2406              : 
    2407          235 :         for (int zoneNum = 1; zoneNum <= NumOfZones; ++zoneNum) {
    2408          128 :             bool FirstSurfFlag = true;
    2409          271 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2410          143 :                 auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    2411          884 :                 for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
    2412          741 :                     if (FirstSurfFlag) {
    2413          126 :                         TRefFlag = state.dataSurface->SurfTAirRef(SurfNum);
    2414          126 :                         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          741 :                     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          128 :             }
    2425              :         }
    2426              : 
    2427              :         // CurrentModuleObject='Zone'
    2428          235 :         for (int zoneNum = 1; zoneNum <= NumOfZones; ++zoneNum) {
    2429          128 :             auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    2430          128 :             s_ztpc->zoneHeatBalance(zoneNum).setUpOutputVars(state, DataStringGlobals::zonePrefix, thisZone.Name);
    2431          128 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    2432            5 :                 for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2433            3 :                     s_ztpc->spaceHeatBalance(spaceNum).setUpOutputVars(
    2434            3 :                         state, DataStringGlobals::spacePrefix, state.dataHeatBal->space(spaceNum).Name);
    2435            2 :                 }
    2436              :             }
    2437          128 :             bool staged = false;
    2438          128 :             if (allocated(state.dataZoneCtrls->StageZoneLogic)) {
    2439            0 :                 staged = state.dataZoneCtrls->StageZoneLogic(zoneNum);
    2440              :             }
    2441              :             // If not doSpaceHeatBalanceSimulation then meter zones, not spaces
    2442          128 :             bool attachMeters = !state.dataHeatBal->doSpaceHeatBalanceSimulation;
    2443          128 :             state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).setUpOutputVars(
    2444          128 :                 state, DataStringGlobals::zonePrefix, thisZone.Name, staged, attachMeters, thisZone.Multiplier, thisZone.ListMultiplier);
    2445          128 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    2446              :                 // If doSpaceHeatBalanceSimulation then meter spaces, not zones
    2447            2 :                 attachMeters = state.dataHeatBal->doSpaceHeatBalanceSimulation;
    2448            5 :                 for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2449            3 :                     state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).setUpOutputVars(state,
    2450              :                                                                                                DataStringGlobals::spacePrefix,
    2451            3 :                                                                                                state.dataHeatBal->space(spaceNum).Name,
    2452              :                                                                                                staged,
    2453              :                                                                                                attachMeters,
    2454              :                                                                                                thisZone.Multiplier,
    2455              :                                                                                                thisZone.ListMultiplier);
    2456            2 :                 }
    2457              :             }
    2458          128 :             state.dataZoneEnergyDemand->ZoneSysMoistureDemand(zoneNum).setUpOutputVars(state, DataStringGlobals::zonePrefix, thisZone.Name);
    2459          128 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    2460            5 :                 for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    2461            3 :                     state.dataZoneEnergyDemand->spaceSysMoistureDemand(spaceNum).setUpOutputVars(
    2462            3 :                         state, DataStringGlobals::spacePrefix, state.dataHeatBal->space(spaceNum).Name);
    2463            2 :                 }
    2464              :             }
    2465          256 :             SetupOutputVariable(state,
    2466              :                                 "Zone Thermostat Air Temperature",
    2467              :                                 Constant::Units::C,
    2468          128 :                                 state.dataHeatBalFanSys->TempTstatAir(zoneNum),
    2469              :                                 OutputProcessor::TimeStepType::System,
    2470              :                                 OutputProcessor::StoreType::Average,
    2471          128 :                                 thisZone.Name);
    2472          128 :             SetupOutputVariable(state,
    2473              :                                 "Zone Thermostat Control Type",
    2474              :                                 Constant::Units::None,
    2475          128 :                                 state.dataHeatBalFanSys->TempControlTypeRpt(zoneNum),
    2476              :                                 OutputProcessor::TimeStepType::Zone,
    2477              :                                 OutputProcessor::StoreType::Average,
    2478          128 :                                 thisZone.Name);
    2479          256 :             SetupOutputVariable(state,
    2480              :                                 "Zone Thermostat Heating Setpoint Temperature",
    2481              :                                 Constant::Units::C,
    2482          128 :                                 s_hbfs->zoneTstatSetpts(zoneNum).setptLo,
    2483              :                                 OutputProcessor::TimeStepType::System,
    2484              :                                 OutputProcessor::StoreType::Average,
    2485          128 :                                 thisZone.Name);
    2486          256 :             SetupOutputVariable(state,
    2487              :                                 "Zone Thermostat Cooling Setpoint Temperature",
    2488              :                                 Constant::Units::C,
    2489          128 :                                 s_hbfs->zoneTstatSetpts(zoneNum).setptHi,
    2490              :                                 OutputProcessor::TimeStepType::System,
    2491              :                                 OutputProcessor::StoreType::Average,
    2492          128 :                                 thisZone.Name);
    2493          256 :             SetupOutputVariable(state,
    2494              :                                 "Zone Adaptive Comfort Operative Temperature Set Point",
    2495              :                                 Constant::Units::C,
    2496          128 :                                 s_hbfs->zoneTstatSetpts(zoneNum).setptAdapComfortCool,
    2497              :                                 OutputProcessor::TimeStepType::Zone,
    2498              :                                 OutputProcessor::StoreType::Average,
    2499          128 :                                 thisZone.Name);
    2500          256 :             SetupOutputVariable(state,
    2501              :                                 "Zone Predicted Sensible Load Room Air Correction Factor",
    2502              :                                 Constant::Units::None,
    2503          128 :                                 state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum),
    2504              :                                 OutputProcessor::TimeStepType::System,
    2505              :                                 OutputProcessor::StoreType::Average,
    2506          128 :                                 thisZone.Name);
    2507              :         } // zoneNum
    2508              : 
    2509              :         // Thermal comfort control output
    2510          107 :         if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
    2511              :             // CurrentModuleObject='ZoneControl:Thermostat:ThermalComfort'
    2512            0 :             for (int Loop = 1; Loop <= state.dataZoneCtrls->NumComfortControlledZones; ++Loop) {
    2513            0 :                 int zoneNum = state.dataZoneCtrls->ComfortControlledZone(Loop).ActualZoneNum;
    2514            0 :                 auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    2515            0 :                 SetupOutputVariable(state,
    2516              :                                     "Zone Thermal Comfort Control Type",
    2517              :                                     Constant::Units::None,
    2518            0 :                                     state.dataHeatBalFanSys->ComfortControlTypeRpt(zoneNum),
    2519              :                                     OutputProcessor::TimeStepType::Zone,
    2520              :                                     OutputProcessor::StoreType::Average,
    2521            0 :                                     thisZone.Name);
    2522            0 :                 SetupOutputVariable(state,
    2523              :                                     "Zone Thermal Comfort Control Fanger Low Setpoint PMV",
    2524              :                                     Constant::Units::None,
    2525            0 :                                     state.dataHeatBalFanSys->ZoneComfortControlsFanger(zoneNum).LowPMV,
    2526              :                                     OutputProcessor::TimeStepType::Zone,
    2527              :                                     OutputProcessor::StoreType::Average,
    2528            0 :                                     thisZone.Name);
    2529            0 :                 SetupOutputVariable(state,
    2530              :                                     "Zone Thermal Comfort Control Fanger High Setpoint PMV",
    2531              :                                     Constant::Units::None,
    2532            0 :                                     state.dataHeatBalFanSys->ZoneComfortControlsFanger(zoneNum).HighPMV,
    2533              :                                     OutputProcessor::TimeStepType::Zone,
    2534              :                                     OutputProcessor::StoreType::Average,
    2535            0 :                                     thisZone.Name);
    2536              :             }
    2537              :         }
    2538              : 
    2539              :         // CurrentModuleObject='ZoneList'
    2540          120 :         for (int Loop = 1; Loop <= state.dataHeatBal->NumOfZoneLists; ++Loop) {
    2541           13 :             auto &zoneList = state.dataHeatBal->ZoneList(Loop);
    2542           26 :             SetupOutputVariable(state,
    2543              :                                 "Zone List Sensible Heating Energy",
    2544              :                                 Constant::Units::J,
    2545           13 :                                 state.dataHeatBal->ZoneListSNLoadHeatEnergy(Loop),
    2546              :                                 OutputProcessor::TimeStepType::System,
    2547              :                                 OutputProcessor::StoreType::Sum,
    2548           13 :                                 zoneList.Name);
    2549           26 :             SetupOutputVariable(state,
    2550              :                                 "Zone List Sensible Cooling Energy",
    2551              :                                 Constant::Units::J,
    2552           13 :                                 state.dataHeatBal->ZoneListSNLoadCoolEnergy(Loop),
    2553              :                                 OutputProcessor::TimeStepType::System,
    2554              :                                 OutputProcessor::StoreType::Sum,
    2555           13 :                                 zoneList.Name);
    2556           26 :             SetupOutputVariable(state,
    2557              :                                 "Zone List Sensible Heating Rate",
    2558              :                                 Constant::Units::W,
    2559           13 :                                 state.dataHeatBal->ZoneListSNLoadHeatRate(Loop),
    2560              :                                 OutputProcessor::TimeStepType::System,
    2561              :                                 OutputProcessor::StoreType::Average,
    2562           13 :                                 zoneList.Name);
    2563           26 :             SetupOutputVariable(state,
    2564              :                                 "Zone List Sensible Cooling Rate",
    2565              :                                 Constant::Units::W,
    2566           13 :                                 state.dataHeatBal->ZoneListSNLoadCoolRate(Loop),
    2567              :                                 OutputProcessor::TimeStepType::System,
    2568              :                                 OutputProcessor::StoreType::Average,
    2569           13 :                                 zoneList.Name);
    2570              :         } // Loop
    2571              : 
    2572              :         // CurrentModuleObject='ZoneGroup'
    2573          118 :         for (int Loop = 1; Loop <= state.dataHeatBal->NumOfZoneGroups; ++Loop) {
    2574           11 :             auto &zoneGroup = state.dataHeatBal->ZoneGroup(Loop);
    2575           22 :             SetupOutputVariable(state,
    2576              :                                 "Zone Group Sensible Heating Energy",
    2577              :                                 Constant::Units::J,
    2578           11 :                                 state.dataHeatBal->ZoneGroupSNLoadHeatEnergy(Loop),
    2579              :                                 OutputProcessor::TimeStepType::System,
    2580              :                                 OutputProcessor::StoreType::Sum,
    2581           11 :                                 zoneGroup.Name);
    2582           22 :             SetupOutputVariable(state,
    2583              :                                 "Zone Group Sensible Cooling Energy",
    2584              :                                 Constant::Units::J,
    2585           11 :                                 state.dataHeatBal->ZoneGroupSNLoadCoolEnergy(Loop),
    2586              :                                 OutputProcessor::TimeStepType::System,
    2587              :                                 OutputProcessor::StoreType::Sum,
    2588           11 :                                 zoneGroup.Name);
    2589           22 :             SetupOutputVariable(state,
    2590              :                                 "Zone Group Sensible Heating Rate",
    2591              :                                 Constant::Units::W,
    2592           11 :                                 state.dataHeatBal->ZoneGroupSNLoadHeatRate(Loop),
    2593              :                                 OutputProcessor::TimeStepType::System,
    2594              :                                 OutputProcessor::StoreType::Average,
    2595           11 :                                 zoneGroup.Name);
    2596           22 :             SetupOutputVariable(state,
    2597              :                                 "Zone Group Sensible Cooling Rate",
    2598              :                                 Constant::Units::W,
    2599           11 :                                 state.dataHeatBal->ZoneGroupSNLoadCoolRate(Loop),
    2600              :                                 OutputProcessor::TimeStepType::System,
    2601              :                                 OutputProcessor::StoreType::Average,
    2602           11 :                                 zoneGroup.Name);
    2603              :         } // Loop
    2604              : 
    2605          107 :         s_ztpc->InitZoneAirSetPointsOneTimeFlag = false;
    2606              :     }
    2607              : 
    2608              :     // Do the Begin Environment initializations
    2609      1141716 :     if (s_ztpc->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag) {
    2610         1157 :         for (auto &thisZoneHB : s_ztpc->zoneHeatBalance) {
    2611          674 :             thisZoneHB.beginEnvironmentInit(state);
    2612          483 :         }
    2613          483 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    2614          112 :             for (auto &thisSpaceHB : s_ztpc->spaceHeatBalance) {
    2615           84 :                 thisSpaceHB.beginEnvironmentInit(state);
    2616           28 :             }
    2617              :         }
    2618              : 
    2619         1157 :         for (auto &zoneTstatSetpt : s_hbfs->zoneTstatSetpts) {
    2620          674 :             zoneTstatSetpt.setpt = zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi = 0.0;
    2621              :         }
    2622              : 
    2623          483 :         state.dataHeatBalFanSys->LoadCorrectionFactor = 1.0;
    2624          483 :         TempControlType = HVAC::SetptType::Uncontrolled;
    2625         1157 :         for (auto &e : state.dataZoneEnergyDemand->ZoneSysEnergyDemand) {
    2626          674 :             e.beginEnvironmentInit();
    2627          483 :         }
    2628         1157 :         for (auto &e : state.dataZoneEnergyDemand->ZoneSysMoistureDemand) {
    2629          674 :             e.beginEnvironmentInit();
    2630          483 :         }
    2631          483 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    2632          112 :             for (auto &e : state.dataZoneEnergyDemand->spaceSysEnergyDemand) {
    2633           84 :                 e.beginEnvironmentInit();
    2634           28 :             }
    2635          112 :             for (auto &e : state.dataZoneEnergyDemand->spaceSysMoistureDemand) {
    2636           84 :                 e.beginEnvironmentInit();
    2637           28 :             }
    2638              :         }
    2639              : 
    2640          483 :         state.dataZoneEnergyDemand->DeadBandOrSetback = false;
    2641              : 
    2642         1157 :         for (auto &e : state.dataHeatBal->Zone) {
    2643          674 :             e.NoHeatToReturnAir = false;
    2644          483 :         }
    2645          483 :         state.dataHeatBalFanSys->PreviousMeasuredZT1 = 0.0;     // Hybrid modeling
    2646          483 :         state.dataHeatBalFanSys->PreviousMeasuredZT2 = 0.0;     // Hybrid modeling
    2647          483 :         state.dataHeatBalFanSys->PreviousMeasuredZT3 = 0.0;     // Hybrid modeling
    2648          483 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat1 = 0.0; // Hybrid modeling
    2649          483 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat2 = 0.0; // Hybrid modeling
    2650          483 :         state.dataHeatBalFanSys->PreviousMeasuredHumRat3 = 0.0; // Hybrid modeling
    2651              : 
    2652          483 :         s_ztpc->MyEnvrnFlag = false;
    2653              :     }
    2654              : 
    2655      1141716 :     if (!state.dataGlobal->BeginEnvrnFlag) {
    2656      1137395 :         s_ztpc->MyEnvrnFlag = true;
    2657              :     }
    2658              : 
    2659              :     // Do the Begin Day initializations
    2660      1141716 :     if (s_ztpc->MyDayFlag && state.dataGlobal->BeginDayFlag) {
    2661         2298 :         s_ztpc->MyDayFlag = false;
    2662              :     }
    2663              : 
    2664      1141716 :     if (!state.dataGlobal->BeginDayFlag) {
    2665      1129094 :         s_ztpc->MyDayFlag = true;
    2666              :     }
    2667              : 
    2668      1917029 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumTempControlledZones; ++Loop) {
    2669       775313 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(Loop);
    2670       775313 :         if (state.dataZoneEquip->ZoneEquipInputsFilled && !s_ztpc->ControlledZonesChecked) {
    2671           73 :             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       775313 :         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      1141716 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumComfortControlledZones; ++Loop) {
    2730            0 :         auto const &comfortZone = state.dataZoneCtrls->ComfortControlledZone(Loop);
    2731            0 :         if (state.dataZoneEquip->ZoneEquipInputsFilled && !s_ztpc->ControlledZonesChecked) {
    2732            0 :             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            0 :         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      1141716 :     if (s_ztpc->ErrorsFound) {
    2794            0 :         ShowFatalError(state, "InitZoneAirSetpoints - program terminates due to previous condition.");
    2795              :     }
    2796              : 
    2797      1141716 :     if (state.dataZoneEquip->ZoneEquipInputsFilled) {
    2798      1141714 :         s_ztpc->ControlledZonesChecked = true;
    2799              :     }
    2800      1141716 : }
    2801              : 
    2802          758 : void ZoneSpaceHeatBalanceData::beginEnvironmentInit(EnergyPlusData &state)
    2803              : {
    2804         3790 :     for (int i = 0; i <= 3; ++i) {
    2805         3032 :         this->ZTM[i] = 0.0;
    2806         3032 :         this->WPrevZoneTS[i] = state.dataEnvrn->OutHumRat;
    2807         3032 :         this->DSWPrevZoneTS[i] = state.dataEnvrn->OutHumRat;
    2808         3032 :         this->WPrevZoneTSTemp[i] = 0.0;
    2809              :     }
    2810          758 :     this->WTimeMinusP = state.dataEnvrn->OutHumRat;
    2811          758 :     this->W1 = state.dataEnvrn->OutHumRat;
    2812          758 :     this->WMX = state.dataEnvrn->OutHumRat;
    2813          758 :     this->WM2 = state.dataEnvrn->OutHumRat;
    2814          758 :     this->airHumRatTemp = 0.0;
    2815          758 :     this->tempIndLoad = 0.0;
    2816          758 :     this->tempDepLoad = 0.0;
    2817          758 :     this->airRelHum = 0.0;
    2818          758 :     this->AirPowerCap = 0.0;
    2819          758 :     this->T1 = 0.0;
    2820          758 : }
    2821              : 
    2822          131 : void ZoneSpaceHeatBalanceData::setUpOutputVars(EnergyPlusData &state, std::string_view prefix, std::string const &name)
    2823              : {
    2824          393 :     SetupOutputVariable(state,
    2825          262 :                         format("{} Air Temperature", prefix),
    2826              :                         Constant::Units::C,
    2827          131 :                         this->ZT,
    2828              :                         OutputProcessor::TimeStepType::System,
    2829              :                         OutputProcessor::StoreType::Average,
    2830              :                         name);
    2831          393 :     SetupOutputVariable(state,
    2832          262 :                         format("{} Air Humidity Ratio", prefix),
    2833              :                         Constant::Units::None,
    2834          131 :                         this->airHumRat,
    2835              :                         OutputProcessor::TimeStepType::System,
    2836              :                         OutputProcessor::StoreType::Average,
    2837              :                         name);
    2838          393 :     SetupOutputVariable(state,
    2839          262 :                         format("{} Air Relative Humidity", prefix),
    2840              :                         Constant::Units::Perc,
    2841          131 :                         this->airRelHum,
    2842              :                         OutputProcessor::TimeStepType::System,
    2843              :                         OutputProcessor::StoreType::Average,
    2844              :                         name);
    2845          393 :     SetupOutputVariable(state,
    2846          262 :                         format("{} Mean Radiant Temperature", prefix),
    2847              :                         Constant::Units::C,
    2848          131 :                         this->MRT,
    2849              :                         OutputProcessor::TimeStepType::Zone,
    2850              :                         OutputProcessor::StoreType::Average,
    2851              :                         name);
    2852          131 : }
    2853              : 
    2854       297272 : 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       297272 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    2895       297272 :     auto const &s_hbfs = state.dataHeatBalFanSys;
    2896              : 
    2897              :     // Staged thermostat setpoint
    2898       297272 :     if (s_ztpc->NumStageCtrZone > 0) {
    2899            0 :         for (int RelativeZoneNum = 1; RelativeZoneNum <= s_ztpc->NumStageCtrZone; ++RelativeZoneNum) {
    2900            0 :             auto &thisStageControlZone = state.dataZoneCtrls->StageControlledZone(RelativeZoneNum);
    2901            0 :             int ActualZoneNum = thisStageControlZone.ActualZoneNum;
    2902              : 
    2903            0 :             auto &thisZoneHB = s_ztpc->zoneHeatBalance(ActualZoneNum);
    2904            0 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ActualZoneNum);
    2905            0 :             Real64 ZoneT = thisZoneHB.MAT; // Zone temperature at previous time step
    2906            0 :             if (ShortenTimeStepSys) {
    2907            0 :                 ZoneT = thisZoneHB.XMPT;
    2908              :             }
    2909            0 :             thisStageControlZone.HeatSetPoint = thisStageControlZone.heatSetptBaseSched->getCurrentVal();
    2910            0 :             thisStageControlZone.CoolSetPoint = thisStageControlZone.coolSetptBaseSched->getCurrentVal();
    2911              : 
    2912            0 :             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            0 :             if (thisStageControlZone.CoolSetPoint < ZoneT) { // Cooling
    2932            0 :                 Real64 SetpointOffset = ZoneT - thisStageControlZone.CoolSetPoint;
    2933            0 :                 int Itemp = 0;
    2934            0 :                 for (int I = 1; I <= thisStageControlZone.NumOfCoolStages; ++I) {
    2935            0 :                     if (SetpointOffset >= thisStageControlZone.CoolTOffset(I)) {
    2936            0 :                         Itemp = -I;
    2937              :                     }
    2938              :                 }
    2939            0 :                 state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ActualZoneNum).StageNum = Itemp;
    2940            0 :                 if (SetpointOffset >= 0.5 * thisStageControlZone.CoolThroRange) {
    2941            0 :                     zoneTstatSetpt.setptHi = thisStageControlZone.CoolSetPoint - 0.5 * thisStageControlZone.CoolThroRange;
    2942              :                 } else {
    2943            0 :                     zoneTstatSetpt.setptHi = thisStageControlZone.CoolSetPoint + 0.5 * thisStageControlZone.CoolThroRange;
    2944              :                 }
    2945            0 :                 zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi;
    2946              : 
    2947            0 :             } else if (thisStageControlZone.HeatSetPoint > ZoneT) { // heating
    2948            0 :                 Real64 SetpointOffset = ZoneT - thisStageControlZone.HeatSetPoint;
    2949            0 :                 int Itemp = 0;
    2950            0 :                 for (int I = 1; I <= thisStageControlZone.NumOfHeatStages; ++I) {
    2951            0 :                     if (std::abs(SetpointOffset) >= std::abs(thisStageControlZone.HeatTOffset(I))) {
    2952            0 :                         Itemp = I;
    2953              :                     }
    2954              :                 }
    2955            0 :                 state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ActualZoneNum).StageNum = Itemp;
    2956            0 :                 if (std::abs(SetpointOffset) >= 0.5 * thisStageControlZone.CoolThroRange) {
    2957            0 :                     zoneTstatSetpt.setptLo = thisStageControlZone.HeatSetPoint + 0.5 * thisStageControlZone.HeatThroRange;
    2958              :                 } else {
    2959            0 :                     zoneTstatSetpt.setptLo = thisStageControlZone.HeatSetPoint - 0.5 * thisStageControlZone.HeatThroRange;
    2960              :                 }
    2961            0 :                 zoneTstatSetpt.setptHi = zoneTstatSetpt.setptLo;
    2962              : 
    2963              :             } else {
    2964            0 :                 zoneTstatSetpt.setptHi = thisStageControlZone.CoolSetPoint + 0.5 * thisStageControlZone.CoolThroRange;
    2965            0 :                 zoneTstatSetpt.setptLo = thisStageControlZone.HeatSetPoint - 0.5 * thisStageControlZone.HeatThroRange;
    2966            0 :                 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            0 :             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       297272 :     if (s_ztpc->NumOnOffCtrZone > 0) {
    2980           16 :         Real64 TempTole = 0.02;
    2981              :         Real64 Tprev;
    2982           32 :         for (int RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++RelativeZoneNum) {
    2983           16 :             auto &thisTempControlledZone = state.dataZoneCtrls->TempControlledZone(RelativeZoneNum);
    2984           16 :             if (thisTempControlledZone.DeltaTCutSet > 0.0) {
    2985           16 :                 if (ShortenTimeStepSys) {
    2986            4 :                     thisTempControlledZone.HeatModeLast = thisTempControlledZone.HeatModeLastSave;
    2987            4 :                     thisTempControlledZone.CoolModeLast = thisTempControlledZone.CoolModeLastSave;
    2988              :                 } else {
    2989           12 :                     thisTempControlledZone.HeatModeLastSave = thisTempControlledZone.HeatModeLast;
    2990           12 :                     thisTempControlledZone.CoolModeLastSave = thisTempControlledZone.CoolModeLast;
    2991              :                 }
    2992              : 
    2993           16 :                 auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(thisTempControlledZone.ActualZoneNum);
    2994           16 :                 auto &thisZoneHB = s_ztpc->zoneHeatBalance(thisTempControlledZone.ActualZoneNum);
    2995           16 :                 thisTempControlledZone.CoolOffFlag = false;
    2996           16 :                 thisTempControlledZone.HeatOffFlag = false;
    2997           16 :                 if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::ThirdOrder) {
    2998            8 :                     Tprev = thisZoneHB.MAT;
    2999            8 :                     if (ShortenTimeStepSys) {
    3000            4 :                         Tprev = thisZoneHB.XMPT;
    3001              :                     }
    3002              :                 } else {
    3003            8 :                     Tprev = thisZoneHB.T1;
    3004              :                 }
    3005              : 
    3006           16 :                 switch (state.dataHeatBalFanSys->TempControlType(thisTempControlledZone.ActualZoneNum)) {
    3007              : 
    3008            4 :                 case HVAC::SetptType::SingleHeat: {
    3009            4 :                     zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo;
    3010            4 :                     zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3011            4 :                     if (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + TempTole) {
    3012            2 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3013            2 :                         zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    3014            2 :                     } else if (Tprev > thisTempControlledZone.ZoneThermostatSetPointLo &&
    3015            2 :                                (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet - TempTole)) {
    3016            2 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3017            2 :                         zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    3018              :                     } else {
    3019            0 :                         thisTempControlledZone.HeatOffFlag = true;
    3020              :                     }
    3021            4 :                     if (thisTempControlledZone.HeatModeLast && Tprev > thisTempControlledZone.ZoneThermostatSetPointLo) {
    3022            2 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointLo;
    3023            2 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3024            2 :                         thisTempControlledZone.HeatOffFlag = true;
    3025              :                     }
    3026            4 :                 } break;
    3027              : 
    3028            4 :                 case HVAC::SetptType::SingleCool: {
    3029            4 :                     zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi;
    3030            4 :                     zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3031            4 :                     if (Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - TempTole) {
    3032            2 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3033            2 :                         zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3034            2 :                     } else if (Tprev < thisTempControlledZone.ZoneThermostatSetPointHi &&
    3035            2 :                                Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet + TempTole) {
    3036            2 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3037            2 :                         zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3038              :                     } else {
    3039            0 :                         thisTempControlledZone.CoolOffFlag = true;
    3040              :                     }
    3041            4 :                     if (thisTempControlledZone.CoolModeLast && Tprev < thisTempControlledZone.ZoneThermostatSetPointHi) {
    3042            2 :                         zoneTstatSetpt.setpt = thisTempControlledZone.ZoneThermostatSetPointHi;
    3043            2 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3044            2 :                         thisTempControlledZone.CoolOffFlag = true;
    3045              :                     }
    3046            4 :                 } break;
    3047              : 
    3048            6 :                 case HVAC::SetptType::DualHeatCool: {
    3049            6 :                     zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3050            6 :                     zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3051            6 :                     if (Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - TempTole) {
    3052            2 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3053            4 :                     } else if (Tprev < thisTempControlledZone.ZoneThermostatSetPointHi &&
    3054            4 :                                Tprev > thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet + TempTole) {
    3055            2 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi - thisTempControlledZone.DeltaTCutSet;
    3056              :                     } else {
    3057            2 :                         thisTempControlledZone.CoolOffFlag = true;
    3058              :                     }
    3059            6 :                     if (thisTempControlledZone.CoolModeLast && Tprev < thisTempControlledZone.ZoneThermostatSetPointHi) {
    3060            4 :                         zoneTstatSetpt.setptHi = thisTempControlledZone.ZoneThermostatSetPointHi;
    3061            4 :                         thisTempControlledZone.CoolOffFlag = true;
    3062              :                     }
    3063              : 
    3064            6 :                     if (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + TempTole) {
    3065            2 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3066            4 :                     } else if (Tprev > thisTempControlledZone.ZoneThermostatSetPointLo &&
    3067            4 :                                (Tprev < thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet - TempTole)) {
    3068            0 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo + thisTempControlledZone.DeltaTCutSet;
    3069              :                     } else {
    3070            4 :                         thisTempControlledZone.HeatOffFlag = true;
    3071              :                     }
    3072            6 :                     if (thisTempControlledZone.HeatModeLast && Tprev > thisTempControlledZone.ZoneThermostatSetPointLo) {
    3073            3 :                         zoneTstatSetpt.setptLo = thisTempControlledZone.ZoneThermostatSetPointLo;
    3074            3 :                         thisTempControlledZone.HeatOffFlag = true;
    3075              :                     }
    3076              : 
    3077              :                     // check setpoint for both and provde an error message
    3078            6 :                     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            6 :                 } break;
    3089              : 
    3090            2 :                 default: {
    3091            2 :                 } break;
    3092              :                 } // switch (setptType)
    3093              :             }
    3094              :         }
    3095              :     }
    3096              : 
    3097       715600 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    3098       418328 :         auto &thisZoneHB = s_ztpc->zoneHeatBalance(zoneNum);
    3099       418328 :         thisZoneHB.predictSystemLoad(state, ShortenTimeStepSys, UseZoneTimeStepHistory, PriorTimeStep, zoneNum);
    3100       888953 :         for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    3101       470625 :             if (state.dataHeatBal->doSpaceHeatBalance) {
    3102        44487 :                 s_ztpc->spaceHeatBalance(spaceNum).predictSystemLoad(
    3103              :                     state, ShortenTimeStepSys, UseZoneTimeStepHistory, PriorTimeStep, zoneNum, spaceNum);
    3104       426138 :             } else if (ShortenTimeStepSys) {
    3105        19072 :                 s_ztpc->spaceHeatBalance(spaceNum).MAT = thisZoneHB.MAT;
    3106        19072 :                 s_ztpc->spaceHeatBalance(spaceNum).airHumRat = thisZoneHB.airHumRat;
    3107              :             }
    3108       418328 :         }
    3109              :     }
    3110       297272 :     if (s_ztpc->NumOnOffCtrZone > 0) {
    3111           32 :         for (int RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++RelativeZoneNum) {
    3112           16 :             auto &thisTempControlledZone = state.dataZoneCtrls->TempControlledZone(RelativeZoneNum);
    3113           16 :             if (thisTempControlledZone.DeltaTCutSet > 0.0) {
    3114           16 :                 int ZoneNum = thisTempControlledZone.ActualZoneNum;
    3115           16 :                 if (thisTempControlledZone.CoolOffFlag && state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).TotalOutputRequired >= 0.0) {
    3116            6 :                     thisTempControlledZone.CoolModeLast = true;
    3117              :                 } else {
    3118           10 :                     thisTempControlledZone.CoolModeLast = false;
    3119              :                 }
    3120           16 :                 if (thisTempControlledZone.HeatOffFlag && state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).TotalOutputRequired <= 0.0) {
    3121            6 :                     thisTempControlledZone.HeatModeLast = true;
    3122              :                 } else {
    3123           10 :                     thisTempControlledZone.HeatModeLast = false;
    3124              :                 }
    3125              :             }
    3126              :         }
    3127              :     }
    3128       297272 : }
    3129       462815 : 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       462815 :     assert(zoneNum > 0);
    3138       462815 :     this->updateTemperatures(state, shortenTimeStepSys, useZoneTimeStepHistory, priorTimeStep, zoneNum, spaceNum);
    3139              : 
    3140       462815 :     Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    3141       462815 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    3142              : 
    3143       462815 :     Real64 volume = 0.0;
    3144       462815 :     if (spaceNum > 0) {
    3145        44487 :         volume = state.dataHeatBal->space(spaceNum).Volume;
    3146              :     } else {
    3147       418328 :         volume = state.dataHeatBal->Zone(zoneNum).Volume;
    3148              :     }
    3149              : 
    3150       462815 :     this->AirPowerCap = volume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpSens *
    3151       462815 :                         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->MAT, this->airHumRat) *
    3152       462815 :                         Psychrometrics::PsyCpAirFnW(this->airHumRat) / TimeStepSysSec;
    3153       462815 :     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       462815 :     this->calcZoneOrSpaceSums(state, false, zoneNum, spaceNum);
    3159              : 
    3160              :     // Sum all convective internal gains except for people: SumIntGainExceptPeople
    3161       462815 :     if (spaceNum == 0 && state.dataHybridModel->FlagHybridModel_PC) {
    3162            0 :         this->SumIntGainExceptPeople = 0.0;
    3163            0 :         this->SumIntGainExceptPeople = InternalHeatGains::SumAllInternalConvectionGainsExceptPeople(state, zoneNum);
    3164              :     }
    3165              : 
    3166       462815 :     this->TempDepCoef = this->SumHA + this->SumMCp;
    3167       462815 :     this->TempIndCoef = this->SumIntGain + this->SumHATsurf - this->SumHATref + this->SumMCpT + this->SysDepZoneLoadsLagged;
    3168       462815 :     this->TempHistoryTerm = this->AirPowerCap * (3.0 * this->ZTM[0] - (3.0 / 2.0) * this->ZTM[1] + (1.0 / 3.0) * this->ZTM[2]);
    3169       462815 :     this->tempDepLoad = (11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef;
    3170       462815 :     this->tempIndLoad = this->TempHistoryTerm + this->TempIndCoef;
    3171       462815 :     if (state.dataRoomAir->anyNonMixingRoomAirModel) {
    3172            0 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    3173              :             // RoomAirflowNetworkModel - make dynamic term independent of TimeStepSys
    3174            0 :             auto &afnZoneInfo = state.dataRoomAir->AFNZoneInfo(zoneNum);
    3175            0 :             if (afnZoneInfo.IsUsed) {
    3176            0 :                 int RoomAirNode = afnZoneInfo.ControlAirNodeID;
    3177            0 :                 RoomAir::LoadPredictionRoomAirModelAFN(state, zoneNum, RoomAirNode);
    3178            0 :                 this->TempDepCoef = afnZoneInfo.Node(RoomAirNode).SumHA + afnZoneInfo.Node(RoomAirNode).SumLinkMCp;
    3179            0 :                 this->TempIndCoef = afnZoneInfo.Node(RoomAirNode).SumIntSensibleGain + afnZoneInfo.Node(RoomAirNode).SumHATsurf -
    3180            0 :                                     afnZoneInfo.Node(RoomAirNode).SumHATref + afnZoneInfo.Node(RoomAirNode).SumLinkMCpT +
    3181            0 :                                     afnZoneInfo.Node(RoomAirNode).SysDepZoneLoadsLagged;
    3182            0 :                 this->AirPowerCap = afnZoneInfo.Node(RoomAirNode).AirVolume * state.dataHeatBal->Zone(zoneNum).ZoneVolCapMultpSens *
    3183            0 :                                     afnZoneInfo.Node(RoomAirNode).RhoAir * afnZoneInfo.Node(RoomAirNode).CpAir / TimeStepSysSec;
    3184            0 :                 this->TempHistoryTerm = this->AirPowerCap * (3.0 * this->ZTM[0] - (3.0 / 2.0) * this->ZTM[1] + (1.0 / 3.0) * this->ZTM[2]);
    3185            0 :                 this->tempDepLoad = (11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef;
    3186            0 :                 this->tempIndLoad = this->TempHistoryTerm + this->TempIndCoef;
    3187            0 :                 if (afnZoneInfo.Node(RoomAirNode).HasHVACAssigned) {
    3188            0 :                     RAFNFrac = afnZoneInfo.Node(RoomAirNode).HVAC(1).SupplyFraction;
    3189              :                 }
    3190              :             }
    3191              :         }
    3192              :     }
    3193              : 
    3194              :     // Exact solution or Euler method
    3195       462815 :     state.dataHVACGlobal->ShortenTimeStepSysRoomAir = false;
    3196       462815 :     if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
    3197        61728 :         if (shortenTimeStepSys && TimeStepSys < state.dataGlobal->TimeStepZone) {
    3198         3207 :             if (state.dataHVACGlobal->PreviousTimeStep < state.dataGlobal->TimeStepZone) {
    3199         2866 :                 this->T1 = this->TM2;
    3200         2866 :                 this->W1 = this->WM2;
    3201         2866 :                 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          341 :                 this->T1 = this->TMX;
    3210          341 :                 this->W1 = this->WMX;
    3211          341 :                 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         3207 :             state.dataHVACGlobal->ShortenTimeStepSysRoomAir = true;
    3220              :         } else {
    3221        58521 :             this->T1 = this->ZT;
    3222        58521 :             this->W1 = this->airHumRat;
    3223        58521 :             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        61728 :         this->tempDepLoad = this->TempDepCoef;
    3232        61728 :         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       462815 :     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       462815 :     this->calcPredictedHumidityRatio(state, RAFNFrac, zoneNum, spaceNum);
    3240       462815 : }
    3241              : 
    3242       248614 : 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       248614 :     auto &s_hbfs = state.dataHeatBalFanSys;
    3263              : 
    3264       248614 :     auto &Zone = state.dataHeatBal->Zone;
    3265       248614 :     auto &TempControlledZone = state.dataZoneCtrls->TempControlledZone;
    3266       248614 :     auto &TempControlType = state.dataHeatBalFanSys->TempControlType;
    3267       248614 :     auto &TempControlTypeRpt = state.dataHeatBalFanSys->TempControlTypeRpt;
    3268       248614 :     int NumOfZones = state.dataGlobal->NumOfZones;
    3269              : 
    3270       248614 :     TempControlType = HVAC::SetptType::Uncontrolled; // Default
    3271              : 
    3272              :     // Place holder for occupied heating and cooling set points - for optimum start
    3273       248614 :     if (!allocated(state.dataZoneCtrls->OccRoomTSetPointHeat)) {
    3274          105 :         state.dataZoneCtrls->OccRoomTSetPointHeat.allocate(NumOfZones);
    3275              :     }
    3276       248614 :     if (!allocated(state.dataZoneCtrls->OccRoomTSetPointCool)) {
    3277          105 :         state.dataZoneCtrls->OccRoomTSetPointCool.allocate(NumOfZones);
    3278              :     }
    3279       248614 :     state.dataZoneCtrls->OccRoomTSetPointHeat = 0.0;
    3280       248614 :     state.dataZoneCtrls->OccRoomTSetPointCool = 100.0;
    3281       248614 :     DeltaT = 0.0;
    3282              : 
    3283       425257 :     for (RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumTempControlledZones; ++RelativeZoneNum) {
    3284       176643 :         auto &tempZone = state.dataZoneCtrls->TempControlledZone(RelativeZoneNum);
    3285              :         // What if this zone not controlled???
    3286              : 
    3287       176643 :         int ActualZoneNum = tempZone.ActualZoneNum;
    3288       176643 :         TempControlType(ActualZoneNum) = static_cast<HVAC::SetptType>(tempZone.setptTypeSched->getCurrentVal());
    3289       176643 :         TempControlTypeRpt(ActualZoneNum) = static_cast<int>(TempControlType(ActualZoneNum));
    3290              :         // Error detection for these values is done in the Get routine
    3291              : 
    3292       176643 :         auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(tempZone.ActualZoneNum);
    3293              : 
    3294       176643 :         switch (TempControlType(ActualZoneNum)) {
    3295            1 :         case HVAC::SetptType::Uncontrolled: {
    3296            1 :         } break;
    3297              : 
    3298        13490 :         case HVAC::SetptType::SingleHeat: {
    3299        13490 :             if (tempZone.setpts[(int)HVAC::SetptType::SingleHeat].isUsed) {
    3300        13490 :                 zoneTstatSetpt.setpt = tempZone.setpts[(int)HVAC::SetptType::SingleHeat].heatSetptSched->getCurrentVal();
    3301        13490 :                 tempZone.ZoneThermostatSetPointLo = zoneTstatSetpt.setpt;
    3302              : 
    3303        13490 :                 AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setpt);
    3304        13490 :                 zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    3305              :             }
    3306        13490 :         } break;
    3307              : 
    3308        13496 :         case HVAC::SetptType::SingleCool: {
    3309        13496 :             zoneTstatSetpt.setpt = tempZone.setpts[(int)HVAC::SetptType::SingleCool].coolSetptSched->getCurrentVal();
    3310        13496 :             tempZone.ZoneThermostatSetPointHi = zoneTstatSetpt.setpt;
    3311              : 
    3312              :             // Added Jan 17 (X. Luo)
    3313              :             // Adjust operative temperature based on adaptive comfort model
    3314        13496 :             if (tempZone.AdaptiveComfortTempControl) {
    3315            0 :                 AdjustOperativeSetPointsforAdapComfort(state, RelativeZoneNum, zoneTstatSetpt.setpt);
    3316            0 :                 zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setpt;
    3317              :             }
    3318              : 
    3319        13496 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setpt);
    3320        13496 :             zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3321              : 
    3322        13496 :             AdjustCoolingSetPointforTempAndHumidityControl(state, RelativeZoneNum, ActualZoneNum);
    3323        13496 :         } break;
    3324              : 
    3325            6 :         case HVAC::SetptType::SingleHeatCool: {
    3326            6 :             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            6 :             if (tempZone.AdaptiveComfortTempControl) {
    3331            0 :                 AdjustOperativeSetPointsforAdapComfort(state, RelativeZoneNum, zoneTstatSetpt.setpt);
    3332            0 :                 zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setpt;
    3333              :             }
    3334              : 
    3335            6 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setpt);
    3336              : 
    3337            6 :             zoneTstatSetpt.setptLo = zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    3338              : 
    3339              :             // Change the room set point to occupied set point during optimum start period--------------
    3340              : 
    3341            6 :             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            6 :         } break;
    3355              : 
    3356       149650 :         case HVAC::SetptType::DualHeatCool: {
    3357       149650 :             zoneTstatSetpt.setptHi = tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].coolSetptSched->getCurrentVal();
    3358       149650 :             TempControlledZone(RelativeZoneNum).ZoneThermostatSetPointHi = zoneTstatSetpt.setptHi;
    3359              : 
    3360              :             // Added Jan 17 (X. Luo)
    3361              :             // Adjust operative temperature based on adaptive comfort model
    3362       149650 :             if ((TempControlledZone(RelativeZoneNum).AdaptiveComfortTempControl)) {
    3363            0 :                 AdjustOperativeSetPointsforAdapComfort(state, RelativeZoneNum, zoneTstatSetpt.setptHi);
    3364            0 :                 zoneTstatSetpt.setptAdapComfortCool = zoneTstatSetpt.setptHi;
    3365              :             }
    3366              : 
    3367       149650 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setptHi);
    3368              : 
    3369       149650 :             zoneTstatSetpt.setptLo = tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].heatSetptSched->getCurrentVal();
    3370       149650 :             TempControlledZone(RelativeZoneNum).ZoneThermostatSetPointLo = zoneTstatSetpt.setptLo;
    3371       149650 :             AdjustAirSetPointsforOpTempCntrl(state, RelativeZoneNum, ActualZoneNum, zoneTstatSetpt.setptLo);
    3372              : 
    3373              :             // Change the room set point to occupied set point during optimum start period--------------
    3374              : 
    3375       149650 :             if (allocated(state.dataAvail->OptStart)) {
    3376            3 :                 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            3 :                     OccStartTime = CEILING(state.dataAvail->OptStart(ActualZoneNum).OccStartTime) + 1;
    3379            3 :                     state.dataZoneCtrls->OccRoomTSetPointCool(ActualZoneNum) =
    3380            3 :                         tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].coolSetptSched->getDayVals(
    3381            3 :                             state)[OccStartTime * state.dataGlobal->TimeStepsInHour];
    3382            3 :                     state.dataZoneCtrls->OccRoomTSetPointHeat(ActualZoneNum) =
    3383            3 :                         tempZone.setpts[(int)HVAC::SetptType::DualHeatCool].heatSetptSched->getDayVals(
    3384            3 :                             state)[OccStartTime * state.dataGlobal->TimeStepsInHour];
    3385              :                 }
    3386              : 
    3387            3 :                 if (state.dataAvail->OptStart(ActualZoneNum).OptStartFlag) {
    3388            0 :                     zoneTstatSetpt.setptHi = state.dataZoneCtrls->OccRoomTSetPointCool(ActualZoneNum);
    3389            0 :                     zoneTstatSetpt.setptLo = state.dataZoneCtrls->OccRoomTSetPointHeat(ActualZoneNum);
    3390              :                 }
    3391              :             }
    3392              :             //--------------------------------------------------------------------------------------------
    3393              : 
    3394       149650 :             AdjustCoolingSetPointforTempAndHumidityControl(state, RelativeZoneNum, ActualZoneNum);
    3395       149650 :         } 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       176643 :         if ((state.dataFaultsMgr->NumFaultyThermostat > 0) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
    3409            0 :             (!state.dataGlobal->KickOffSimulation)) {
    3410              :             //  loop through the FaultsThermostatOffset objects to find the one for the zone
    3411            0 :             for (int iFault = 1; iFault <= state.dataFaultsMgr->NumFaultyThermostat; ++iFault) {
    3412              :                 // Why are we doing this here?
    3413            0 :                 if (Util::SameString(TempControlledZone(RelativeZoneNum).Name,
    3414            0 :                                      state.dataFaultsMgr->FaultsThermostatOffset(iFault).FaultyThermostatName)) {
    3415              : 
    3416              :                     // Check fault availability schedules
    3417            0 :                     if (state.dataFaultsMgr->FaultsThermostatOffset(iFault).availSched->getCurrentVal() > 0.0) {
    3418              : 
    3419              :                         // Check fault severity schedules to update the reference thermostat offset
    3420            0 :                         Real64 rSchVal = 1.0;
    3421              :                         Real64 offsetUpdated;
    3422            0 :                         if (state.dataFaultsMgr->FaultsThermostatOffset(iFault).severitySched != nullptr) {
    3423            0 :                             rSchVal = state.dataFaultsMgr->FaultsThermostatOffset(iFault).severitySched->getCurrentVal();
    3424              :                         }
    3425            0 :                         offsetUpdated = rSchVal * state.dataFaultsMgr->FaultsThermostatOffset(iFault).Offset;
    3426              : 
    3427              :                         // Positive offset means the sensor reading is higher than the actual value
    3428            0 :                         zoneTstatSetpt.setpt -= offsetUpdated;
    3429            0 :                         zoneTstatSetpt.setptLo -= offsetUpdated;
    3430            0 :                         zoneTstatSetpt.setptHi -= offsetUpdated;
    3431              :                     }
    3432              : 
    3433              :                     // Stop searching the FaultsThermostatOffset object for the zone
    3434            0 :                     break;
    3435              :                 }
    3436              :             }
    3437              :         }
    3438              :     }
    3439              : 
    3440       248614 :     if (state.dataZoneCtrls->NumComfortControlledZones > 0) {
    3441            0 :         CalcZoneAirComfortSetPoints(state);
    3442              :     }
    3443       248614 :     OverrideAirSetPointsforEMSCntrl(state);
    3444       248614 : }
    3445              : 
    3446       462815 : 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       462815 :     Real64 ZoneRHHumidifyingSetPoint = 0.0;   // Zone humidifying set point (%)
    3467       462815 :     Real64 ZoneRHDehumidifyingSetPoint = 0.0; // Zone dehumidifying set point (%)
    3468              : 
    3469       462815 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    3470       462815 :     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       462815 :     bool ControlledHumidZoneFlag = false;
    3474              :     // Check all the controlled zones to see if it matches the zone simulated
    3475       462815 :     if (thisZone.humidityControlZoneIndex > 0) {
    3476         8520 :         auto &humidityControlZone = state.dataZoneCtrls->HumidityControlZone(thisZone.humidityControlZoneIndex);
    3477         8520 :         assert(humidityControlZone.ActualZoneNum == zoneNum);
    3478         8520 :         ZoneRHHumidifyingSetPoint = humidityControlZone.humidifyingSched->getCurrentVal();
    3479         8520 :         ZoneRHDehumidifyingSetPoint = humidityControlZone.dehumidifyingSched->getCurrentVal();
    3480              : 
    3481              :         // Apply EMS values to overwrite the humidistat values
    3482         8520 :         if (humidityControlZone.EMSOverrideHumidifySetPointOn) {
    3483            0 :             ZoneRHHumidifyingSetPoint = humidityControlZone.EMSOverrideHumidifySetPointValue;
    3484              :         }
    3485         8520 :         if (humidityControlZone.EMSOverrideDehumidifySetPointOn) {
    3486            0 :             ZoneRHDehumidifyingSetPoint = humidityControlZone.EMSOverrideDehumidifySetPointValue;
    3487              :         }
    3488              : 
    3489              :         // Apply offsets for faulty humidistats
    3490         8520 :         if ((state.dataFaultsMgr->NumFaultyHumidistat > 0) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
    3491            0 :             (!state.dataGlobal->KickOffSimulation)) {
    3492              : 
    3493              :             //  loop through the FaultsHumidistatOffset objects to find the one for the zone
    3494            0 :             for (int iFault = 1; iFault <= state.dataFaultsMgr->NumFaultyHumidistat; ++iFault) {
    3495              : 
    3496            0 :                 if (Util::SameString(humidityControlZone.ControlName, state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyHumidistatName)) {
    3497              : 
    3498            0 :                     if (Util::SameString(state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyHumidistatType, "ThermostatOffsetDependent")) {
    3499              :                         // For Humidistat Offset Type I: ThermostatOffsetDependent
    3500              : 
    3501            0 :                         bool IsThermostatFound = false;
    3502            0 :                         Real64 offsetThermostat = 0.0;
    3503              : 
    3504              :                         // Get the offset value of the corresponding thermostat fault object
    3505            0 :                         if (state.dataFaultsMgr->NumFaultyThermostat > 0) {
    3506              : 
    3507              :                             //  loop through the FaultsThermostatOffset objects to find the one causes the Humidistat Offset
    3508            0 :                             for (int iFaultThermo = 1; iFaultThermo <= state.dataFaultsMgr->NumFaultyThermostat; ++iFaultThermo) {
    3509              : 
    3510            0 :                                 if (Util::SameString(state.dataFaultsMgr->FaultsHumidistatOffset(iFault).FaultyThermostatName,
    3511            0 :                                                      state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).Name)) {
    3512            0 :                                     IsThermostatFound = true;
    3513              : 
    3514              :                                     // Check fault availability schedules
    3515            0 :                                     if (state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).availSched->getCurrentVal() > 0.0) {
    3516              : 
    3517              :                                         // Check fault severity schedules to update the reference thermostat offset
    3518            0 :                                         Real64 rSchVal = 1.0;
    3519            0 :                                         if (state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).severitySched != nullptr) {
    3520            0 :                                             rSchVal = state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).severitySched->getCurrentVal();
    3521              :                                         }
    3522            0 :                                         offsetThermostat = rSchVal * state.dataFaultsMgr->FaultsThermostatOffset(iFaultThermo).Offset;
    3523              :                                     }
    3524              : 
    3525              :                                     // Stop searching the FaultsThermostatOffset object for the Humidistat Offset
    3526            0 :                                     break;
    3527              :                                 }
    3528              :                             }
    3529              :                         }
    3530              : 
    3531              :                         // The FaultsThermostatOffset specified in the FaultHumidistatOffset is not found
    3532            0 :                         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            0 :                         if (offsetThermostat != 0.0) {
    3542              :                             // Calculate the humidistat offset value from the thermostat offset value
    3543            0 :                             Real64 faultZoneWHumidifyingSetPoint = Psychrometrics::PsyWFnTdbRhPb(
    3544            0 :                                 state, (this->MAT + offsetThermostat), (ZoneRHHumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress);
    3545            0 :                             Real64 faultZoneWDehumidifyingSetPoint = Psychrometrics::PsyWFnTdbRhPb(
    3546            0 :                                 state, (this->MAT + offsetThermostat), (ZoneRHDehumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress);
    3547              :                             Real64 offsetZoneRHHumidifyingSetPoint =
    3548            0 :                                 ZoneRHHumidifyingSetPoint -
    3549            0 :                                 Psychrometrics::PsyRhFnTdbWPb(state, this->MAT, faultZoneWHumidifyingSetPoint, state.dataEnvrn->OutBaroPress) * 100.0;
    3550              :                             Real64 offsetZoneRHDehumidifyingSetPoint =
    3551            0 :                                 ZoneRHDehumidifyingSetPoint -
    3552            0 :                                 Psychrometrics::PsyRhFnTdbWPb(state, this->MAT, faultZoneWDehumidifyingSetPoint, state.dataEnvrn->OutBaroPress) *
    3553            0 :                                     100.0;
    3554              : 
    3555              :                             // Apply the calculated humidistat offset value
    3556              :                             // Positive offset means the sensor reading is higher than the actual value
    3557            0 :                             ZoneRHHumidifyingSetPoint -= offsetZoneRHHumidifyingSetPoint;
    3558            0 :                             ZoneRHDehumidifyingSetPoint -= offsetZoneRHDehumidifyingSetPoint;
    3559              : 
    3560              :                             // constrain value to something reasonable
    3561            0 :                             ZoneRHHumidifyingSetPoint = min(100.0, max(0.0, ZoneRHHumidifyingSetPoint));
    3562            0 :                             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            0 :                         if (state.dataFaultsMgr->FaultsHumidistatOffset(iFault).availSched->getCurrentVal() > 0.0) {
    3570              : 
    3571              :                             // Check fault severity schedules to update the reference humidistat offset
    3572            0 :                             Real64 rSchVal = 1.0;
    3573              :                             Real64 offsetUpdated;
    3574            0 :                             if (state.dataFaultsMgr->FaultsHumidistatOffset(iFault).severitySched != nullptr) {
    3575            0 :                                 rSchVal = state.dataFaultsMgr->FaultsHumidistatOffset(iFault).severitySched->getCurrentVal();
    3576              :                             }
    3577            0 :                             offsetUpdated = rSchVal * state.dataFaultsMgr->FaultsHumidistatOffset(iFault).Offset;
    3578              : 
    3579              :                             // Positive offset means the sensor reading is higher than the actual value
    3580            0 :                             ZoneRHHumidifyingSetPoint -= offsetUpdated;
    3581            0 :                             ZoneRHDehumidifyingSetPoint -= offsetUpdated;
    3582              : 
    3583              :                             // constrain value to something reasonable
    3584            0 :                             ZoneRHHumidifyingSetPoint = min(100.0, max(0.0, ZoneRHHumidifyingSetPoint));
    3585            0 :                             ZoneRHDehumidifyingSetPoint = min(100.0, max(0.0, ZoneRHDehumidifyingSetPoint));
    3586              :                         }
    3587              :                     }
    3588            0 :                     break;
    3589              :                 }
    3590              :             }
    3591              :         }
    3592              : 
    3593              :         // Run-time error check
    3594         8520 :         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         8520 :         if (ZoneRHHumidifyingSetPoint == ZoneRHDehumidifyingSetPoint) {
    3609         8520 :             SingleSetPoint = true;
    3610              :         }
    3611         8520 :         ControlledHumidZoneFlag = true;
    3612              : 
    3613              :     } // HumidControlledZoneNum
    3614              : 
    3615              :     // if zone latent sizing is requested but no humidistat exists
    3616       462815 :     if (state.dataGlobal->DoingSizing && !ControlledHumidZoneFlag && state.dataHeatBal->DoLatentSizing) {
    3617        27494 :         for (size_t zoneEqConfigNum = 1; zoneEqConfigNum <= state.dataZoneEquip->ZoneEquipConfig.size(); ++zoneEqConfigNum) {
    3618        27494 :             auto &zoneEqConfig = state.dataZoneEquip->ZoneEquipConfig(zoneEqConfigNum);
    3619        27494 :             if (!zoneEqConfig.IsControlled) {
    3620            0 :                 continue;
    3621              :             }
    3622        27494 :             int ZoneSizNum = Util::FindItemInList(zoneEqConfig.ZoneName, state.dataSize->ZoneSizingInput, &DataSizing::ZoneSizingInputData::ZoneName);
    3623              :             // should use the first Sizing:Zone object if not found
    3624        27494 :             if (ZoneSizNum == 0 && !state.dataSize->ZoneSizingInput.empty()) {
    3625            0 :                 ZoneSizNum = 1;
    3626              :             }
    3627        27494 :             if (ZoneSizNum > 0) {
    3628        27494 :                 auto &zoneSizingInput = state.dataSize->ZoneSizingInput(ZoneSizNum);
    3629        27494 :                 if (zoneSizingInput.zoneLatentSizing) {
    3630        54988 :                     ZoneRHDehumidifyingSetPoint = (zoneSizingInput.zoneRHDehumidifySched != nullptr)
    3631        27494 :                                                       ? zoneSizingInput.zoneRHDehumidifySched->getCurrentVal()
    3632              :                                                       : zoneSizingInput.zoneRHDehumidifySetPoint;
    3633        54988 :                     ZoneRHHumidifyingSetPoint = (zoneSizingInput.zoneRHHumidifySched != nullptr)
    3634        27494 :                                                     ? zoneSizingInput.zoneRHHumidifySched->getCurrentVal()
    3635              :                                                     : zoneSizingInput.zoneRHHumidifySetPoint;
    3636        27494 :                     if (ZoneRHHumidifyingSetPoint > ZoneRHDehumidifyingSetPoint) {
    3637            0 :                         ZoneRHHumidifyingSetPoint = ZoneRHDehumidifyingSetPoint;
    3638              :                     }
    3639        27494 :                     if (ZoneRHHumidifyingSetPoint == ZoneRHDehumidifyingSetPoint) {
    3640        27494 :                         SingleSetPoint = true;
    3641              :                     }
    3642        27494 :                     ControlledHumidZoneFlag = true;
    3643              :                 }
    3644              :             }
    3645        27494 :             break;
    3646              :         }
    3647              :     }
    3648              : 
    3649       462815 :     Real64 LoadToHumidifySetPoint = 0.0;   // Moisture load at humidifying set point
    3650       462815 :     Real64 LoadToDehumidifySetPoint = 0.0; // Moisture load at dehumidifying set point
    3651       462815 :     Real64 totalOutputRequired = 0.0;
    3652       462815 :     if (ControlledHumidZoneFlag) {
    3653              : 
    3654              :         // Calculate hourly humidity ratio from infiltration + humidity added from latent load
    3655              :         // to determine system added/subtracted moisture.
    3656        36014 :         Real64 LatentGain = this->latentGain + state.dataHeatBalFanSys->SumLatentHTRadSys(zoneNum) + state.dataHeatBalFanSys->SumLatentPool(zoneNum);
    3657              : 
    3658        36014 :         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        36014 :         Real64 RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->ZT, this->airHumRat, RoutineName);
    3667        36014 :         Real64 H2OHtOfVap = Psychrometrics::PsyHgAirFnWTdb(this->airHumRat, this->ZT);
    3668              : 
    3669              :         // Assume that the system will have flow
    3670        36014 :         Real64 A = 0.0;
    3671        36014 :         Real64 B = 0.0;
    3672        36014 :         Real64 C = 0.0;
    3673        72028 :         if (state.afn->multizone_always_simulated ||
    3674        36014 :             (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    3675            0 :              state.afn->AirflowNetworkFanActivated)) {
    3676              :             // Multizone airflow calculated in AirflowNetwork
    3677            0 :             B = (LatentGain / H2OHtOfVap) + state.afn->exchangeData(zoneNum).SumMHrW + state.afn->exchangeData(zoneNum).SumMMHrW + this->SumHmARaW;
    3678            0 :             A = state.afn->exchangeData(zoneNum).SumMHr + state.afn->exchangeData(zoneNum).SumMMHr + this->SumHmARa;
    3679              :         } else {
    3680        36014 :             B = (LatentGain / H2OHtOfVap) + ((this->OAMFL + this->VAMFL + this->CTMFL) * state.dataEnvrn->OutHumRat) + this->EAMFLxHumRat +
    3681        36014 :                 this->SumHmARaW + this->MixingMassFlowXHumRat + this->MDotOA * state.dataEnvrn->OutHumRat;
    3682        36014 :             A = this->OAMFL + this->VAMFL + this->EAMFL + this->CTMFL + this->SumHmARa + this->MixingMassFlowZone + this->MDotOA;
    3683              :         }
    3684        36014 :         Real64 volume = 0.0;
    3685        36014 :         if (spaceNum > 0) {
    3686        19080 :             volume = state.dataHeatBal->space(spaceNum).Volume;
    3687              :         } else {
    3688        16934 :             volume = thisZone.Volume;
    3689              :         }
    3690        36014 :         C = RhoAir * volume * thisZone.ZoneVolCapMultpMoist / TimeStepSysSec;
    3691              : 
    3692        36014 :         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        36014 :             Psychrometrics::PsyWFnTdbRhPb(state, this->ZT, (ZoneRHHumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress, RoutineName);
    3709        36014 :         Real64 exp_700_A_C(0.0);
    3710        36014 :         if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::ThirdOrder) {
    3711        36014 :             LoadToHumidifySetPoint =
    3712        72028 :                 ((11.0 / 6.0) * C + A) * WZoneSetPoint -
    3713        36014 :                 (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            0 :         } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
    3716            0 :             if (A == 0.0) { // B=0
    3717            0 :                 LoadToHumidifySetPoint = C * (WZoneSetPoint - this->W1) - B;
    3718              :             } else {
    3719            0 :                 exp_700_A_C = std::exp(min(700.0, -A / C)); // Tuned Save expensive value
    3720            0 :                 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        36014 :         if (RAFNFrac > 0.0) {
    3726            0 :             LoadToHumidifySetPoint = LoadToHumidifySetPoint / RAFNFrac;
    3727              :         }
    3728              :         WZoneSetPoint =
    3729        36014 :             Psychrometrics::PsyWFnTdbRhPb(state, this->ZT, (ZoneRHDehumidifyingSetPoint / 100.0), state.dataEnvrn->OutBaroPress, RoutineName);
    3730        36014 :         if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::ThirdOrder) {
    3731        36014 :             LoadToDehumidifySetPoint =
    3732        72028 :                 ((11.0 / 6.0) * C + A) * WZoneSetPoint -
    3733        36014 :                 (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            0 :         } else if (state.dataHeatBal->ZoneAirSolutionAlgo == DataHeatBalance::SolutionAlgo::AnalyticalSolution) {
    3736            0 :             if (A == 0.0) { // B=0
    3737            0 :                 LoadToDehumidifySetPoint = C * (WZoneSetPoint - this->W1) - B;
    3738              :             } else {
    3739            0 :                 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        36014 :         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        36014 :         if (SingleSetPoint) {
    3753        36014 :             totalOutputRequired = LoadToHumidifySetPoint;
    3754              :         } else {
    3755            0 :             if (LoadToHumidifySetPoint > 0.0 && LoadToDehumidifySetPoint > 0.0) {
    3756            0 :                 totalOutputRequired = LoadToHumidifySetPoint;
    3757            0 :             } else if (LoadToHumidifySetPoint < 0.0 && LoadToDehumidifySetPoint < 0.0) {
    3758            0 :                 totalOutputRequired = LoadToDehumidifySetPoint;
    3759            0 :             } else if (LoadToHumidifySetPoint <= 0.0 && LoadToDehumidifySetPoint >= 0.0) { // deadband includes zero loads
    3760            0 :                 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       462815 :     if (spaceNum > 0) {
    3777        44487 :         auto &thisspaceSysMoistureDemand = state.dataZoneEnergyDemand->spaceSysMoistureDemand(spaceNum);
    3778        44487 :         if (ControlledHumidZoneFlag) {
    3779        19080 :             thisspaceSysMoistureDemand.reportMoistLoadsZoneMultiplier(
    3780              :                 state, zoneNum, totalOutputRequired, LoadToHumidifySetPoint, LoadToDehumidifySetPoint);
    3781              :         } else {
    3782        25407 :             thisspaceSysMoistureDemand.TotalOutputRequired = 0.0;
    3783        25407 :             thisspaceSysMoistureDemand.OutputRequiredToDehumidifyingSP = 0.0;
    3784        25407 :             thisspaceSysMoistureDemand.OutputRequiredToHumidifyingSP = 0.0;
    3785              :         }
    3786              :     } else {
    3787       418328 :         auto &thisZoneSysMoistureDemand = state.dataZoneEnergyDemand->ZoneSysMoistureDemand(zoneNum);
    3788       418328 :         if (ControlledHumidZoneFlag) {
    3789        16934 :             thisZoneSysMoistureDemand.reportMoistLoadsZoneMultiplier(
    3790              :                 state, zoneNum, totalOutputRequired, LoadToHumidifySetPoint, LoadToDehumidifySetPoint);
    3791              :         } else {
    3792       401394 :             thisZoneSysMoistureDemand.TotalOutputRequired = 0.0;
    3793       401394 :             thisZoneSysMoistureDemand.OutputRequiredToDehumidifyingSP = 0.0;
    3794       401394 :             thisZoneSysMoistureDemand.OutputRequiredToHumidifyingSP = 0.0;
    3795              :         }
    3796              :     }
    3797       462815 : }
    3798              : 
    3799       297260 : Real64 correctZoneAirTemps(EnergyPlusData &state,
    3800              :                            bool useZoneTimeStepHistory // if true then use zone timestep history, if false use system time step history
    3801              : )
    3802              : {
    3803       297260 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    3804       297260 :     Real64 maxTempChange = DataPrecisionGlobals::constant_zero; // Max absolute air temperature change between previous and current timestep
    3805       715592 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    3806       418332 :         auto &thisZoneHB = s_ztpc->zoneHeatBalance(zoneNum);
    3807       418332 :         Real64 zoneTempChange = thisZoneHB.correctAirTemp(state, useZoneTimeStepHistory, zoneNum);
    3808       418332 :         auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    3809       888961 :         for (int spaceNum : thisZone.spaceIndexes) {
    3810       470629 :             auto &thisSpaceHB = s_ztpc->spaceHeatBalance(spaceNum);
    3811       525934 :             if (state.dataHeatBal->doSpaceHeatBalanceSimulation &&
    3812        55305 :                 !state.dataGlobal->DoingSizing) { // Need space air temps to match zone temps for sizing
    3813        55305 :                 Real64 spaceTempChange = thisSpaceHB.correctAirTemp(state, useZoneTimeStepHistory, zoneNum, spaceNum);
    3814        55305 :                 maxTempChange = max(maxTempChange, spaceTempChange);
    3815              :             } else {
    3816              :                 // If doing sizing and zone is controlled, then set space node to match zone node
    3817       415324 :                 if (state.dataHeatBal->doSpaceHeatBalanceSizing && thisZone.IsControlled) {
    3818        44613 :                     auto const &thisZoneNode = state.dataLoopNodes->Node(thisZone.SystemZoneNodeNumber);
    3819        44613 :                     auto &thisSpaceNode = state.dataLoopNodes->Node(state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber);
    3820        44613 :                     thisSpaceNode.Temp = thisZoneNode.Temp;
    3821        44613 :                     thisSpaceNode.HumRat = thisZoneNode.HumRat;
    3822        44613 :                     thisSpaceNode.Enthalpy = thisZoneNode.Enthalpy;
    3823              :                 }
    3824              :                 // If no SpaceHB or doing sizing, then set space temps and humrat to match zone
    3825       415324 :                 thisSpaceHB.ZT = thisZoneHB.ZT;
    3826       415324 :                 thisSpaceHB.ZTM = thisZoneHB.ZTM;
    3827       415324 :                 thisSpaceHB.MAT = thisZoneHB.MAT;
    3828       415324 :                 thisSpaceHB.airHumRat = thisZoneHB.airHumRat;
    3829       415324 :                 thisSpaceHB.airRelHum = thisZoneHB.airRelHum;
    3830              :                 // thisSpaceHB.ZTAVComf = thisZoneHB.ZTAVComf;
    3831              :             }
    3832       418332 :         }
    3833       418332 :         maxTempChange = max(maxTempChange, zoneTempChange);
    3834              : 
    3835       418332 :         CalcZoneComponentLoadSums(state, zoneNum, &s_ztpc->zoneHeatBalance(zoneNum), state.dataHeatBal->ZnAirRpt(zoneNum));
    3836       418332 :         if (state.dataHeatBal->doSpaceHeatBalanceSimulation) {
    3837        92175 :             for (int spaceNum : thisZone.spaceIndexes) {
    3838        55305 :                 CalcZoneComponentLoadSums(state, zoneNum, &s_ztpc->spaceHeatBalance(spaceNum), state.dataHeatBal->spaceAirRpt(spaceNum));
    3839        36870 :             }
    3840              :         }
    3841              :     }
    3842       297260 :     return maxTempChange;
    3843              : }
    3844              : 
    3845       473637 : 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       473637 :     Real64 tempChange = DataPrecisionGlobals::constant_zero; // Zone or space air temperature change between previous and current timestep
    3865              : 
    3866       473637 :     assert(zoneNum > 0);
    3867       473637 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    3868              : 
    3869              :     // Update zone temperatures
    3870              : 
    3871       473637 :     Real64 ZoneMult = thisZone.Multiplier * thisZone.ListMultiplier;
    3872              : 
    3873       473637 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    3874              : 
    3875              :     // update the variables actually used in the balance equations.
    3876       473637 :     if (!useZoneTimeStepHistory) {
    3877       134881 :         this->ZTM = this->DSXMAT;
    3878       134881 :         this->WPrevZoneTSTemp = this->DSWPrevZoneTS;
    3879              :     } else {
    3880       338756 :         this->ZTM = this->XMAT;
    3881       338756 :         this->WPrevZoneTSTemp = this->WPrevZoneTS;
    3882              :     }
    3883              : 
    3884       473637 :     Real64 volume = 0.0;
    3885       473637 :     if (spaceNum > 0) {
    3886        55305 :         volume = state.dataHeatBal->space(spaceNum).Volume;
    3887              :     } else {
    3888       418332 :         volume = thisZone.Volume;
    3889              :     }
    3890      1420911 :     this->AirPowerCap = volume * thisZone.ZoneVolCapMultpSens *
    3891       473637 :                         Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->MAT, this->airHumRat, RoutineName) *
    3892       473637 :                         Psychrometrics::PsyCpAirFnW(this->airHumRat) / TimeStepSysSec;
    3893              : 
    3894              :     // SpaceHB TODO: For now, room air model is only for zones
    3895       473637 :     if (spaceNum == 0) {
    3896       418332 :         RoomAir::ManageAirModel(state, zoneNum);
    3897              :     }
    3898              : 
    3899              :     // Calculate the various heat balance sums
    3900       473637 :     this->calcZoneOrSpaceSums(state, true, zoneNum, spaceNum);
    3901              : 
    3902              :     // Sum all convective internal gains except for people: SumIntGainExceptPeople
    3903       473637 :     if (state.dataHybridModel->FlagHybridModel_PC) {
    3904              :         // TODO: For now, don't do space heat balance with hybrid model
    3905            0 :         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       473637 :     int ZoneNodeNum = thisZone.SystemZoneNodeNumber;
    3910       473637 :     if (spaceNum > 0) {
    3911        55305 :         ZoneNodeNum = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    3912              :     }
    3913              : 
    3914       473637 :     Real64 SNLoad = 0.0;
    3915              : 
    3916       473637 :     if (ZoneNodeNum > 0) { // This zone is controlled by a zone equipment configuration or zone plenum
    3917       199573 :         auto &thisSystemNode = state.dataLoopNodes->Node(ZoneNodeNum);
    3918              : 
    3919              :         // Heat balance coefficients for controlled zone, i.e. with system air flow
    3920       199573 :         this->TempDepCoef = this->SumHA + this->SumMCp + this->SumSysMCp;
    3921       199573 :         this->TempIndCoef = this->SumIntGain + this->SumHATsurf - this->SumHATref + this->SumMCpT + this->SumSysMCpT +
    3922       199573 :                             (this->NonAirSystemResponse / ZoneMult + this->SysDepZoneLoadsLagged);
    3923              : 
    3924       199573 :         if (state.afn->distribution_simulated) {
    3925         5684 :             this->TempIndCoef += state.afn->exchangeData(zoneNum).TotalSen;
    3926              :         }
    3927              : 
    3928              :         // Solve for zone air temperature
    3929       199573 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    3930       174716 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    3931       174716 :             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       174716 :                        ((11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef);
    3933       174716 :         } break;
    3934        24857 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    3935        24857 :             if (this->TempDepCoef == 0.0) { // B=0
    3936            0 :                 this->ZT = this->T1 + this->TempIndCoef / this->AirPowerCap;
    3937              :             } else {
    3938        24857 :                 this->ZT = (this->T1 - this->TempIndCoef / this->TempDepCoef) * std::exp(min(700.0, -this->TempDepCoef / this->AirPowerCap)) +
    3939        24857 :                            this->TempIndCoef / this->TempDepCoef;
    3940              :             }
    3941        24857 :         } break;
    3942            0 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    3943            0 :             this->ZT = (this->AirPowerCap * this->T1 + this->TempIndCoef) / (this->AirPowerCap + this->TempDepCoef);
    3944            0 :         } 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       199573 :         if (!state.dataRoomAir->anyNonMixingRoomAirModel) {
    3951              :             // Fully mixed
    3952       199573 :             thisSystemNode.Temp = this->ZT;
    3953              :             // SpaceHB TODO: What to do here if this is for space
    3954       199573 :             if (spaceNum == 0) {
    3955       199573 :                 state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    3956              :             }
    3957       199573 :             state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    3958              :         } else {
    3959            0 :             auto const &thisAirModel = state.dataRoomAir->AirModel(zoneNum);
    3960            0 :             if ((thisAirModel.AirModel == RoomAir::RoomAirModel::Mixing) || (!thisAirModel.SimAirModel)) {
    3961              :                 // Fully mixed
    3962            0 :                 thisSystemNode.Temp = this->ZT;
    3963              :                 // SpaceHB TODO: What to do here if this is for space
    3964            0 :                 if (spaceNum == 0) {
    3965            0 :                     state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    3966              :                 }
    3967            0 :                 state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    3968            0 :             } 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            0 :                 if (this->SumSysMCp > HVAC::SmallMassFlow) {
    3972            0 :                     Real64 TempSupplyAir = this->SumSysMCpT / this->SumSysMCp; // Non-negligible flow, calculate supply air temperature
    3973            0 :                     if (std::abs(TempSupplyAir - this->ZT) > state.dataHeatBal->TempConvergTol) {
    3974            0 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = (TempSupplyAir - thisSystemNode.Temp) / (TempSupplyAir - this->ZT);
    3975              :                         // constrain value to something reasonable
    3976            0 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = max(-3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3977            0 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = min(3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3978              : 
    3979              :                     } else {
    3980            0 :                         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            0 :                     state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    3985              :                 }
    3986            0 :             } else if (thisAirModel.SimAirModel && ((thisAirModel.AirModel == RoomAir::RoomAirModel::UserDefined) ||
    3987            0 :                                                     (thisAirModel.AirModel == RoomAir::RoomAirModel::DispVent1Node))) {
    3988            0 :                 if (this->SumSysMCp > HVAC::SmallMassFlow) {
    3989            0 :                     Real64 TempSupplyAir = this->SumSysMCpT / this->SumSysMCp; // Non-negligible flow, calculate supply air temperature
    3990            0 :                     if (std::abs(TempSupplyAir - this->ZT) > state.dataHeatBal->TempConvergTol) {
    3991            0 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = (TempSupplyAir - thisSystemNode.Temp) / (TempSupplyAir - this->ZT);
    3992              :                         // constrain value
    3993            0 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = max(-3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3994            0 :                         state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = min(3.0, state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum));
    3995              : 
    3996              :                     } else {
    3997            0 :                         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            0 :                     state.dataHeatBalFanSys->LoadCorrectionFactor(zoneNum) = 1.0;
    4002              :                 }
    4003            0 :             } else if (thisAirModel.AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4004              :                 // Zone node used in the RoomAirflowNetwork model
    4005            0 :                 this->ZT = state.dataRoomAir->AFNZoneInfo(zoneNum).Node(state.dataRoomAir->AFNZoneInfo(zoneNum).ControlAirNodeID).AirTemp;
    4006            0 :                 thisSystemNode.Temp = this->ZT;
    4007              :                 // SpaceHB TODO: What to do here if this is for space
    4008            0 :                 if (spaceNum == 0) {
    4009            0 :                     state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->ZT;
    4010              :                 }
    4011            0 :                 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       199573 :         Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->airHumRat);
    4024       199573 :         Real64 ZoneEnthalpyIn = this->SumSysMCpT;
    4025              : 
    4026              :         // SNLOAD is the single zone load, without Zone Multiplier or Zone List Multiplier
    4027       199573 :         SNLoad = ZoneEnthalpyIn - (thisSystemNode.MassFlowRate / ZoneMult) * CpAir * thisSystemNode.Temp + this->NonAirSystemResponse / ZoneMult +
    4028       199573 :                  this->SysDepZoneLoadsLagged;
    4029              : 
    4030              :     } else {
    4031              : 
    4032              :         // Heat balance coefficients for uncontrolled zone, i.e. without system air flow
    4033       274064 :         this->TempDepCoef = this->SumHA + this->SumMCp;
    4034       274064 :         this->TempIndCoef = this->SumIntGain + this->SumHATsurf - this->SumHATref + this->SumMCpT;
    4035              : 
    4036       274064 :         if (state.afn->distribution_simulated) {
    4037        11368 :             this->TempIndCoef += state.afn->exchangeData(zoneNum).TotalSen;
    4038              :         }
    4039              : 
    4040              :         // Solve for zone air temperature
    4041       274064 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    4042       181889 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    4043       181889 :             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       181889 :                        ((11.0 / 6.0) * this->AirPowerCap + this->TempDepCoef);
    4045              :             // Exact solution
    4046       181889 :         } break;
    4047        92175 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    4048        92175 :             if (this->TempDepCoef == 0.0) { // B=0
    4049            0 :                 this->ZT = this->T1 + this->TempIndCoef / this->AirPowerCap;
    4050              :             } else {
    4051        92175 :                 this->ZT = (this->T1 - this->TempIndCoef / this->TempDepCoef) * std::exp(min(700.0, -this->TempDepCoef / this->AirPowerCap)) +
    4052        92175 :                            this->TempIndCoef / this->TempDepCoef;
    4053              :             }
    4054        92175 :         } break;
    4055            0 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    4056            0 :             this->ZT = (this->AirPowerCap * this->T1 + this->TempIndCoef) / (this->AirPowerCap + this->TempDepCoef);
    4057            0 :         } break;
    4058            0 :         default:
    4059            0 :             break;
    4060              :         }
    4061              : 
    4062              :         // SpaceHB TODO: For now, room air model is only for zones
    4063       274064 :         if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    4064            0 :             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       274064 :         SNLoad = 0.0;
    4071              :     }
    4072              : 
    4073              :     // Hybrid modeling start
    4074              :     // SpaceHB TODO: For now, hybrid model is only for zones
    4075       473637 :     if (spaceNum == 0 && state.dataHybridModel->FlagHybridModel) {
    4076            5 :         auto &hmZone = state.dataHybridModel->hybridModelZones(zoneNum);
    4077           10 :         if ((hmZone.InfiltrationCalc_T || hmZone.InternalThermalMassCalc_T || hmZone.PeopleCountCalc_T) && (!state.dataGlobal->WarmupFlag) &&
    4078            5 :             (!state.dataGlobal->DoingSizing)) {
    4079            5 :             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       473637 :     this->MAT = this->ZT;
    4095              : 
    4096              :     // Determine sensible load heating/cooling rate and energy
    4097       473637 :     if (spaceNum > 0) {
    4098        55305 :         state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).reportZoneAirSystemSensibleLoads(state, SNLoad);
    4099              :     } else {
    4100       418332 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).reportZoneAirSystemSensibleLoads(state, SNLoad);
    4101              :     }
    4102              : 
    4103              :     // Final humidity calcs
    4104       473637 :     this->correctHumRat(state, zoneNum, spaceNum);
    4105              : 
    4106       473637 :     this->airHumRat = this->airHumRatTemp;
    4107       473637 :     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       473637 :     bool isMixed = true;
    4111              :     // SpaceHB TODO: For now, room air model is only for zones
    4112       473637 :     if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    4113            0 :         isMixed = !((state.dataRoomAir->IsZoneDispVent3Node(zoneNum) && !state.dataRoomAir->ZoneDispVent3NodeMixedFlag(zoneNum)) ||
    4114            0 :                     (state.dataRoomAir->IsZoneUFAD(zoneNum) && !state.dataRoomAir->ZoneUFADMixedFlag(zoneNum)));
    4115              :     }
    4116       473637 :     switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    4117       356605 :     case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    4118       356605 :         if (isMixed) {
    4119       356605 :             tempChange = max(tempChange, std::abs(this->ZT - this->ZTM[0]));
    4120              :         } else {
    4121            0 :             tempChange = max(tempChange,
    4122            0 :                              max(std::abs(state.dataRoomAir->ZTOC(zoneNum) - state.dataRoomAir->ZTMOC(zoneNum)[0]),
    4123            0 :                                  std::abs(state.dataRoomAir->ZTMX(zoneNum) - state.dataRoomAir->ZTMMX(zoneNum)[0])));
    4124              :         }
    4125       356605 :     } break;
    4126       117032 :     case DataHeatBalance::SolutionAlgo::AnalyticalSolution:
    4127              :     case DataHeatBalance::SolutionAlgo::EulerMethod: {
    4128       117032 :         if (isMixed) {
    4129       117032 :             tempChange = max(tempChange, std::abs(this->ZT - this->T1));
    4130              :         } else {
    4131            0 :             tempChange = max(tempChange,
    4132            0 :                              max(std::abs(state.dataRoomAir->ZTOC(zoneNum) - state.dataRoomAir->Zone1OC(zoneNum)),
    4133            0 :                                  std::abs(state.dataRoomAir->ZTMX(zoneNum) - state.dataRoomAir->Zone1MX(zoneNum))));
    4134              :         }
    4135       117032 :     } break;
    4136            0 :     default:
    4137            0 :         break;
    4138              :     }
    4139              : 
    4140       473637 :     return tempChange;
    4141              : }
    4142              : 
    4143       248592 : 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       248592 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4153       583878 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    4154       335286 :         s_ztpc->zoneHeatBalance(zoneNum).pushZoneTimestepHistory(state, zoneNum);
    4155       335286 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    4156        56616 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4157        42462 :                 s_ztpc->spaceHeatBalance(spaceNum).pushZoneTimestepHistory(state, zoneNum, spaceNum);
    4158        14154 :             }
    4159              :         }
    4160              :     }
    4161       248592 : }
    4162              : 
    4163       377748 : void ZoneSpaceHeatBalanceData::pushZoneTimestepHistory(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    4164              : {
    4165              : 
    4166       377748 :     constexpr std::string_view routineName("pushTimestepHistories");
    4167       377748 :     assert(zoneNum > 0);
    4168              : 
    4169       377748 :     auto &thisAirModel = state.dataRoomAir->AirModel(zoneNum);
    4170              : 
    4171              :     // Push the temperature and humidity ratio histories
    4172              : 
    4173      1510992 :     for (int iHistory = 3; iHistory >= 1; --iHistory) {
    4174      1133244 :         this->XMAT[iHistory] = this->XMAT[iHistory - 1];
    4175      1133244 :         this->WPrevZoneTS[iHistory] = this->WPrevZoneTS[iHistory - 1];
    4176              :     }
    4177       377748 :     this->XMAT[0] = this->ZTAV; // using average for whole zone time step.
    4178       377748 :     this->XMPT = this->ZT;
    4179       377748 :     this->WPrevZoneTS[0] = this->airHumRatAvg; // using average for whole zone time step.
    4180       377748 :     this->airHumRat = this->airHumRatTemp;
    4181       377748 :     this->WTimeMinusP = this->airHumRatTemp;
    4182       377748 :     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       377748 :     if (spaceNum == 0) {
    4186       335286 :         if (thisAirModel.AirModel == RoomAir::RoomAirModel::DispVent3Node || thisAirModel.AirModel == RoomAir::RoomAirModel::UFADInt ||
    4187       335286 :             thisAirModel.AirModel == RoomAir::RoomAirModel::UFADExt) {
    4188            0 :             state.dataRoomAir->XMATFloor(zoneNum)[3] = state.dataRoomAir->XMATFloor(zoneNum)[2];
    4189            0 :             state.dataRoomAir->XMATFloor(zoneNum)[2] = state.dataRoomAir->XMATFloor(zoneNum)[1];
    4190            0 :             state.dataRoomAir->XMATFloor(zoneNum)[1] = state.dataRoomAir->XMATFloor(zoneNum)[0];
    4191            0 :             state.dataRoomAir->XMATFloor(zoneNum)[0] = state.dataRoomAir->ZTFloor(zoneNum);
    4192            0 :             state.dataRoomAir->MATFloor(zoneNum) = state.dataRoomAir->ZTFloor(zoneNum);
    4193              : 
    4194            0 :             state.dataRoomAir->XMATOC(zoneNum)[3] = state.dataRoomAir->XMATOC(zoneNum)[2];
    4195            0 :             state.dataRoomAir->XMATOC(zoneNum)[2] = state.dataRoomAir->XMATOC(zoneNum)[1];
    4196            0 :             state.dataRoomAir->XMATOC(zoneNum)[1] = state.dataRoomAir->XMATOC(zoneNum)[0];
    4197            0 :             state.dataRoomAir->XMATOC(zoneNum)[0] = state.dataRoomAir->ZTOC(zoneNum);
    4198            0 :             state.dataRoomAir->MATOC(zoneNum) = state.dataRoomAir->ZTOC(zoneNum);
    4199              : 
    4200            0 :             state.dataRoomAir->XMATMX(zoneNum)[3] = state.dataRoomAir->XMATMX(zoneNum)[2];
    4201            0 :             state.dataRoomAir->XMATMX(zoneNum)[2] = state.dataRoomAir->XMATMX(zoneNum)[1];
    4202            0 :             state.dataRoomAir->XMATMX(zoneNum)[1] = state.dataRoomAir->XMATMX(zoneNum)[0];
    4203            0 :             state.dataRoomAir->XMATMX(zoneNum)[0] = state.dataRoomAir->ZTMX(zoneNum);
    4204            0 :             state.dataRoomAir->MATMX(zoneNum) = state.dataRoomAir->ZTMX(zoneNum);
    4205              :         }
    4206              : 
    4207              :         // for RoomAirflowNetwork model
    4208       335286 :         if (thisAirModel.AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4209            0 :             for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    4210            0 :                 afnNode.AirTempX[3] = afnNode.AirTempX[2];
    4211            0 :                 afnNode.AirTempX[2] = afnNode.AirTempX[1];
    4212            0 :                 afnNode.AirTempX[1] = afnNode.AirTempX[0];
    4213            0 :                 afnNode.AirTempX[0] = afnNode.AirTemp;
    4214              : 
    4215            0 :                 afnNode.HumRatX[3] = afnNode.HumRatX[2];
    4216            0 :                 afnNode.HumRatX[2] = afnNode.HumRatX[1];
    4217            0 :                 afnNode.HumRatX[1] = afnNode.HumRatX[0];
    4218            0 :                 afnNode.HumRatX[0] = afnNode.HumRat;
    4219              :             }
    4220              :         }
    4221              :     }
    4222              : 
    4223       377748 :     if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
    4224        23424 :         this->TM2 = this->TMX;
    4225        23424 :         this->TMX = this->ZTAV; // using average for whole zone time step.
    4226        23424 :         this->WM2 = this->WMX;
    4227        23424 :         this->WMX = this->airHumRatAvg; // using average for whole zone time step.
    4228              :         // SpaceHB TODO: For now, room air model is only for zones
    4229        23424 :         if (spaceNum == 0) {
    4230        23424 :             if (thisAirModel.AirModel == RoomAir::RoomAirModel::DispVent3Node || thisAirModel.AirModel == RoomAir::RoomAirModel::UFADInt ||
    4231        23424 :                 thisAirModel.AirModel == RoomAir::RoomAirModel::UFADExt) {
    4232            0 :                 state.dataRoomAir->ZoneM2Floor(zoneNum) = state.dataRoomAir->ZoneMXFloor(zoneNum);
    4233            0 :                 state.dataRoomAir->ZoneMXFloor(zoneNum) = state.dataRoomAir->ZTFloor(zoneNum); // using average for whole zone time step.
    4234            0 :                 state.dataRoomAir->ZoneM2OC(zoneNum) = state.dataRoomAir->ZoneMXOC(zoneNum);
    4235            0 :                 state.dataRoomAir->ZoneMXOC(zoneNum) = state.dataRoomAir->ZTOC(zoneNum); // using average for whole zone time step.
    4236            0 :                 state.dataRoomAir->ZoneM2MX(zoneNum) = state.dataRoomAir->ZoneMXMX(zoneNum);
    4237            0 :                 state.dataRoomAir->ZoneMXMX(zoneNum) = state.dataRoomAir->ZTMX(zoneNum); // using average for whole zone time step.
    4238              :             }
    4239              : 
    4240        23424 :             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       377748 : }
    4252              : 
    4253        48663 : 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        48663 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4263       131704 :     for (int zoneNum = 1; zoneNum <= state.dataGlobal->NumOfZones; ++zoneNum) {
    4264        83041 :         s_ztpc->zoneHeatBalance(zoneNum).pushSystemTimestepHistory(state, zoneNum);
    4265        83041 :         if (state.dataHeatBal->doSpaceHeatBalance) {
    4266         2700 :             for (int spaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    4267         2025 :                 s_ztpc->spaceHeatBalance(spaceNum).pushSystemTimestepHistory(state, zoneNum, spaceNum);
    4268          675 :             }
    4269              :         }
    4270              :     }
    4271        48663 : }
    4272              : 
    4273        85066 : void ZoneSpaceHeatBalanceData::pushSystemTimestepHistory(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    4274              : {
    4275        85066 :     assert(zoneNum > 0);
    4276       340264 :     for (int iHistory = 3; iHistory >= 1; --iHistory) {
    4277       255198 :         this->DSXMAT[iHistory] = this->DSXMAT[iHistory - 1];
    4278       255198 :         this->DSWPrevZoneTS[iHistory] = this->DSWPrevZoneTS[iHistory - 1];
    4279              :     }
    4280        85066 :     this->DSXMAT[0] = this->MAT;
    4281        85066 :     this->DSWPrevZoneTS[0] = this->airHumRat;
    4282              : 
    4283              :     // SpaceHB TODO: For now, room air model is only for zones
    4284        85066 :     if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    4285            0 :         if (state.dataRoomAir->IsZoneDispVent3Node(zoneNum) || state.dataRoomAir->IsZoneUFAD(zoneNum)) {
    4286            0 :             state.dataRoomAir->DSXMATFloor(zoneNum)[3] = state.dataRoomAir->DSXMATFloor(zoneNum)[2];
    4287            0 :             state.dataRoomAir->DSXMATFloor(zoneNum)[2] = state.dataRoomAir->DSXMATFloor(zoneNum)[1];
    4288            0 :             state.dataRoomAir->DSXMATFloor(zoneNum)[1] = state.dataRoomAir->DSXMATFloor(zoneNum)[0];
    4289            0 :             state.dataRoomAir->DSXMATFloor(zoneNum)[0] = state.dataRoomAir->MATFloor(zoneNum);
    4290              : 
    4291            0 :             state.dataRoomAir->DSXMATOC(zoneNum)[3] = state.dataRoomAir->DSXMATOC(zoneNum)[2];
    4292            0 :             state.dataRoomAir->DSXMATOC(zoneNum)[2] = state.dataRoomAir->DSXMATOC(zoneNum)[1];
    4293            0 :             state.dataRoomAir->DSXMATOC(zoneNum)[1] = state.dataRoomAir->DSXMATOC(zoneNum)[0];
    4294            0 :             state.dataRoomAir->DSXMATOC(zoneNum)[0] = state.dataRoomAir->MATOC(zoneNum);
    4295              : 
    4296            0 :             state.dataRoomAir->DSXMATMX(zoneNum)[3] = state.dataRoomAir->DSXMATMX(zoneNum)[2];
    4297            0 :             state.dataRoomAir->DSXMATMX(zoneNum)[2] = state.dataRoomAir->DSXMATMX(zoneNum)[1];
    4298            0 :             state.dataRoomAir->DSXMATMX(zoneNum)[1] = state.dataRoomAir->DSXMATMX(zoneNum)[0];
    4299            0 :             state.dataRoomAir->DSXMATMX(zoneNum)[0] = state.dataRoomAir->MATMX(zoneNum);
    4300              :         }
    4301            0 :         if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4302            0 :             for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    4303            0 :                 afnNode.AirTempDSX[3] = afnNode.AirTempDSX[2];
    4304            0 :                 afnNode.AirTempDSX[2] = afnNode.AirTempDSX[1];
    4305            0 :                 afnNode.AirTempDSX[1] = afnNode.AirTempDSX[0];
    4306            0 :                 afnNode.AirTempDSX[0] = afnNode.AirTemp;
    4307              : 
    4308            0 :                 afnNode.HumRatDSX[3] = afnNode.HumRatDSX[2];
    4309            0 :                 afnNode.HumRatDSX[2] = afnNode.HumRatDSX[1];
    4310            0 :                 afnNode.HumRatDSX[1] = afnNode.HumRatDSX[0];
    4311            0 :                 afnNode.HumRatDSX[0] = afnNode.HumRat;
    4312              :             }
    4313              :         }
    4314              :     }
    4315              : 
    4316        85066 :     if (state.dataHeatBal->ZoneAirSolutionAlgo != DataHeatBalance::SolutionAlgo::ThirdOrder) {
    4317        38303 :         this->TM2 = this->TMX;
    4318        38303 :         this->TMX = this->MAT; // using average for whole zone time step.
    4319        38303 :         this->WM2 = this->WMX;
    4320        38303 :         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        38303 :         if (spaceNum == 0) {
    4324        38303 :             if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::DispVent3Node ||
    4325        76606 :                 state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::UFADInt ||
    4326        38303 :                 state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::UFADExt) {
    4327            0 :                 state.dataRoomAir->ZoneM2Floor(zoneNum) = state.dataRoomAir->ZoneMXFloor(zoneNum);
    4328            0 :                 state.dataRoomAir->ZoneMXFloor(zoneNum) = state.dataRoomAir->ZTFloor(zoneNum); // using average for whole zone time step.
    4329            0 :                 state.dataRoomAir->ZoneM2OC(zoneNum) = state.dataRoomAir->ZoneMXOC(zoneNum);
    4330            0 :                 state.dataRoomAir->ZoneMXOC(zoneNum) = state.dataRoomAir->ZTOC(zoneNum); // using average for whole zone time step.
    4331            0 :                 state.dataRoomAir->ZoneM2MX(zoneNum) = state.dataRoomAir->ZoneMXMX(zoneNum);
    4332            0 :                 state.dataRoomAir->ZoneMXMX(zoneNum) = state.dataRoomAir->ZTMX(zoneNum); // using average for whole zone time step.
    4333              :             }
    4334        38303 :             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        85066 : }
    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       473647 : 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       473647 :     assert(zoneNum > 0);
    4418              :     static constexpr std::string_view RoutineName("correctHumRat");
    4419              : 
    4420       473647 :     Real64 MoistureMassFlowRate = 0.0;
    4421       473647 :     Real64 ZoneMassFlowRate = 0.0;
    4422       473647 :     auto &zone = state.dataHeatBal->Zone(zoneNum);
    4423       473647 :     int ZoneMult = zone.Multiplier * zone.ListMultiplier;
    4424       473647 :     bool ControlledZoneAirFlag = zone.IsControlled;
    4425       473647 :     bool ZoneRetPlenumAirFlag = zone.IsReturnPlenum;
    4426       473647 :     bool ZoneSupPlenumAirFlag = zone.IsSupplyPlenum;
    4427              : 
    4428       473647 :     if (ControlledZoneAirFlag) { // If there is system flow then calculate the flow rates
    4429       199578 :         auto &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(zoneNum);
    4430              :         // Calculate moisture flow rate into each zone
    4431       418187 :         for (int NodeNum = 1; NodeNum <= zoneEquipConfig.NumInletNodes; ++NodeNum) {
    4432       218609 :             auto const &inletNode = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(NodeNum));
    4433       218609 :             MoistureMassFlowRate += (inletNode.MassFlowRate * inletNode.HumRat) / ZoneMult;
    4434       218609 :             ZoneMassFlowRate += inletNode.MassFlowRate / ZoneMult;
    4435              :         }
    4436              : 
    4437              :         // Do the calculations for the plenum zone
    4438       274069 :     } else if (ZoneRetPlenumAirFlag) {
    4439            0 :         int ZoneRetPlenumNum = zone.PlenumCondNum;
    4440            0 :         auto &zoneRetPlenCond = state.dataZonePlenum->ZoneRetPlenCond(ZoneRetPlenumNum);
    4441            0 :         for (int NodeNum = 1; NodeNum <= zoneRetPlenCond.NumInletNodes; ++NodeNum) {
    4442            0 :             auto const &inletNode = state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(NodeNum));
    4443            0 :             MoistureMassFlowRate += (inletNode.MassFlowRate * inletNode.HumRat) / ZoneMult;
    4444            0 :             ZoneMassFlowRate += inletNode.MassFlowRate / ZoneMult;
    4445              :         }
    4446              :         // add in the leak flow
    4447            0 :         for (int ADUListIndex = 1; ADUListIndex <= zoneRetPlenCond.NumADUs; ++ADUListIndex) {
    4448            0 :             int ADUNum = zoneRetPlenCond.ADUIndex(ADUListIndex);
    4449            0 :             auto const &airDistUnit = state.dataDefineEquipment->AirDistUnit(ADUNum);
    4450            0 :             if (airDistUnit.UpStreamLeak) {
    4451            0 :                 int ADUInNode = airDistUnit.InletNodeNum;
    4452            0 :                 MoistureMassFlowRate += (airDistUnit.MassFlowRateUpStrLk * state.dataLoopNodes->Node(ADUInNode).HumRat) / ZoneMult;
    4453            0 :                 ZoneMassFlowRate += airDistUnit.MassFlowRateUpStrLk / ZoneMult;
    4454              :             }
    4455            0 :             if (airDistUnit.DownStreamLeak) {
    4456            0 :                 int ADUOutNode = airDistUnit.OutletNodeNum;
    4457            0 :                 MoistureMassFlowRate += (airDistUnit.MassFlowRateDnStrLk * state.dataLoopNodes->Node(ADUOutNode).HumRat) / ZoneMult;
    4458            0 :                 ZoneMassFlowRate += airDistUnit.MassFlowRateDnStrLk / ZoneMult;
    4459              :             }
    4460              :         }
    4461              : 
    4462       274069 :     } else if (ZoneSupPlenumAirFlag) {
    4463            0 :         int ZoneSupPlenumNum = zone.PlenumCondNum;
    4464            0 :         auto const &inletNode = state.dataLoopNodes->Node(state.dataZonePlenum->ZoneSupPlenCond(ZoneSupPlenumNum).InletNode);
    4465            0 :         MoistureMassFlowRate += (inletNode.MassFlowRate * inletNode.HumRat) / ZoneMult;
    4466            0 :         ZoneMassFlowRate += inletNode.MassFlowRate / ZoneMult;
    4467              :     }
    4468              : 
    4469              :     // Calculate hourly humidity ratio from infiltration + humidity added from latent load + system added moisture
    4470       473647 :     Real64 LatentGain = this->latentGain + state.dataHeatBalFanSys->SumLatentHTRadSys(zoneNum) + state.dataHeatBalFanSys->SumLatentPool(zoneNum);
    4471              : 
    4472       473647 :     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       473647 :     Real64 const RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->ZT, this->airHumRat, RoutineName);
    4480       473647 :     Real64 const H2OHtOfVap = Psychrometrics::PsyHgAirFnWTdb(this->airHumRat, this->ZT);
    4481              : 
    4482       473647 :     Real64 B = (LatentGain / H2OHtOfVap) + ((this->OAMFL + this->VAMFL + this->CTMFL) * state.dataEnvrn->OutHumRat) + this->EAMFLxHumRat +
    4483       473647 :                (MoistureMassFlowRate) + this->SumHmARaW + this->MixingMassFlowXHumRat + this->MDotOA * state.dataEnvrn->OutHumRat;
    4484       473647 :     Real64 A = ZoneMassFlowRate + this->OAMFL + this->VAMFL + this->EAMFL + this->CTMFL + this->SumHmARa + this->MixingMassFlowZone + this->MDotOA;
    4485              : 
    4486       930242 :     if (state.afn->multizone_always_simulated ||
    4487       456595 :         (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    4488            0 :          state.afn->AirflowNetworkFanActivated)) {
    4489        17052 :         auto const &exchangeData = state.afn->exchangeData(zoneNum);
    4490              :         // Multizone airflow calculated in AirflowNetwork
    4491        17052 :         B = (LatentGain / H2OHtOfVap) + (exchangeData.SumMHrW + exchangeData.SumMMHrW) + (MoistureMassFlowRate) + this->SumHmARaW;
    4492        17052 :         A = ZoneMassFlowRate + exchangeData.SumMHr + exchangeData.SumMMHr + this->SumHmARa;
    4493              :     }
    4494       473647 :     Real64 C = RhoAir * zone.Volume * zone.ZoneVolCapMultpMoist / TimeStepSysSec;
    4495              : 
    4496       473647 :     if (state.afn->distribution_simulated) {
    4497        17052 :         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       473647 :     switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    4505       356609 :     case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    4506       356609 :         this->airHumRatTemp =
    4507       356609 :             (B + C * (3.0 * this->WPrevZoneTSTemp[0] - (3.0 / 2.0) * this->WPrevZoneTSTemp[1] + (1.0 / 3.0) * this->WPrevZoneTSTemp[2])) /
    4508       356609 :             ((11.0 / 6.0) * C + A);
    4509              :         // Exact solution
    4510       356609 :     } break;
    4511       117032 :     case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    4512       117032 :         if (A == 0.0) { // B=0
    4513        55694 :             this->airHumRatTemp = this->W1 + B / C;
    4514              :         } else {
    4515        61338 :             this->airHumRatTemp = (this->W1 - B / A) * std::exp(min(700.0, -A / C)) + B / A;
    4516              :         }
    4517       117032 :     } break;
    4518            6 :     case DataHeatBalance::SolutionAlgo::EulerMethod: {
    4519            6 :         this->airHumRatTemp = (C * this->W1 + B) / (C + A);
    4520            6 :     } break;
    4521            0 :     default:
    4522            0 :         break;
    4523              :     }
    4524              : 
    4525              :     // Set the humidity ratio to zero if the zone has been dried out
    4526       473647 :     if (this->airHumRatTemp < 0.0) {
    4527            0 :         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       473647 :     Real64 const WZSat = Psychrometrics::PsyWFnTdbRhPb(state, this->ZT, 1.0, state.dataEnvrn->OutBaroPress, RoutineName);
    4533              : 
    4534       473647 :     if (this->airHumRatTemp > WZSat) {
    4535       107841 :         this->airHumRatTemp = WZSat;
    4536              :     }
    4537              : 
    4538       473647 :     if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    4539            0 :         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       473647 :     if (spaceNum == 0 && state.dataHybridModel->FlagHybridModel) {
    4545            9 :         auto &hmZone = state.dataHybridModel->hybridModelZones(zoneNum);
    4546            9 :         if ((hmZone.InfiltrationCalc_H || hmZone.PeopleCountCalc_H) && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing)) {
    4547            4 :             Real64 LatentGainExceptPeople = 0.0;
    4548            4 :             if (hmZone.PeopleCountCalc_H) {
    4549            2 :                 LatentGainExceptPeople = this->latentGainExceptPeople + state.dataHeatBalFanSys->SumLatentHTRadSys(zoneNum) +
    4550            2 :                                          state.dataHeatBalFanSys->SumLatentPool(zoneNum);
    4551              :             }
    4552              : 
    4553            4 :             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       473647 :     int ZoneNodeNum = zone.SystemZoneNodeNumber;
    4559       473647 :     if (spaceNum > 0) {
    4560        55305 :         ZoneNodeNum = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    4561              :     }
    4562       473647 :     if (ZoneNodeNum > 0) {
    4563       199583 :         state.dataLoopNodes->Node(ZoneNodeNum).HumRat = this->airHumRatTemp;
    4564       199583 :         state.dataLoopNodes->Node(ZoneNodeNum).Enthalpy = Psychrometrics::PsyHFnTdbW(this->ZT, this->airHumRatTemp);
    4565              :     }
    4566       473647 :     if (state.dataHeatBal->DoLatentSizing) {
    4567         8438 :         Real64 sensibleLoad = 0.0;
    4568         8438 :         Real64 pSat = Psychrometrics::PsyPsatFnTemp(state, this->ZT, RoutineName);
    4569         8438 :         Real64 Tdp = Psychrometrics::PsyTdpFnWPb(state, this->airHumRatTemp, state.dataEnvrn->StdBaroPress);
    4570         8438 :         Real64 vaporPressureDiff = pSat - Psychrometrics::PsyPsatFnTemp(state, Tdp, RoutineName);
    4571         8438 :         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         8438 :             sensibleLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).airSysHeatRate +
    4578         8438 :                            state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).airSysCoolRate;
    4579         8438 :             state.dataZoneEnergyDemand->ZoneSysMoistureDemand(zoneNum).reportZoneAirSystemMoistureLoads(
    4580              :                 state, LatentGain, sensibleLoad, vaporPressureDiff);
    4581              :         }
    4582              :     }
    4583       473647 : }
    4584              : 
    4585            1 : 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            1 :     Real64 constexpr realTWO = 2.0;
    4620            1 :     Real64 constexpr realTHREE = 3.0;
    4621              :     // first determine the ratio of system time step to zone time step
    4622            1 :     Real64 const DSRatio = OldTimeStep / NewTimeStep; // should pretty much be an integer value 2, 3, 4, etc.
    4623              : 
    4624            1 :     newVal0 = oldVal0;
    4625              : 
    4626            1 :     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            1 :         newVal1 = (oldVal0 + oldVal1) / realTWO;
    4633            1 :         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            1 :         newVal3 = (oldVal1 + oldVal2) / realTWO;
    4639            1 :         newVal4 = oldVal2;
    4640            0 :     } 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            0 :         Real64 delta10 = (oldVal1 - oldVal0) / realTHREE;
    4647            0 :         newVal1 = oldVal0 + delta10;
    4648            0 :         newVal2 = newVal1 + delta10;
    4649            0 :         newVal3 = oldVal1;
    4650              :         // last point lies 1/3 way between oldVal1 and oldVal2
    4651              :         // newVal4 = oldVal1 + (oldVal2 - oldVal1) * ((oldTime1 - newTime4) / (OldTimeStep));
    4652            0 :         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            0 :         Real64 delta10 = (oldVal1 - oldVal0) / DSRatio;
    4661            0 :         newVal1 = oldVal0 + delta10;
    4662            0 :         newVal2 = newVal1 + delta10;
    4663            0 :         newVal3 = newVal2 + delta10;
    4664            0 :         newVal4 = newVal3 + delta10;
    4665              :     }
    4666            1 : }
    4667              : 
    4668        10325 : Real64 DownInterpolate4HistoryValues(Real64 OldTimeStep, Real64 NewTimeStep, std::array<Real64, 4> const &oldVals, std::array<Real64, 4> &newVals)
    4669              : {
    4670        10325 :     Real64 constexpr realTWO = 2.0;
    4671        10325 :     Real64 constexpr realTHREE = 3.0;
    4672              :     // first determine the ratio of system time step to zone time step
    4673        10325 :     Real64 const DSRatio = OldTimeStep / NewTimeStep; // should pretty much be an integer value 2, 3, 4, etc.
    4674              : 
    4675        10325 :     newVals[0] = oldVals[0];
    4676              : 
    4677        10325 :     if (std::abs(DSRatio - realTWO) < 0.01) { // DSRatio = 2
    4678              :         // first point lies exactly between (oldVals[0] and oldVals[1])
    4679         3463 :         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         3463 :         newVals[2] = oldVals[1];
    4682         3463 :         newVals[3] = (oldVals[1] + oldVals[2]) / realTWO;
    4683              : 
    4684         6862 :     } else if (std::abs(DSRatio - realTHREE) < 0.01) { // DSRatio = 3
    4685              :         // first two points lie between (oldVals[0] and oldVals[1])
    4686         2282 :         Real64 delta10 = (oldVals[1] - oldVals[0]) / realTHREE;
    4687         2282 :         newVals[1] = oldVals[0] + delta10;
    4688         2282 :         newVals[2] = newVals[1] + delta10;
    4689              :         // last point is oldVals[1]
    4690         2282 :         newVals[3] = oldVals[1];
    4691              : 
    4692              :     } else { // DSRatio = 4 or more
    4693              :         // all new points lie between (oldVals[0] and oldVals[1])
    4694         4580 :         Real64 delta10 = (oldVals[1] - oldVals[0]) / DSRatio;
    4695         4580 :         newVals[1] = oldVals[0] + delta10;
    4696         4580 :         newVals[2] = newVals[1] + delta10;
    4697         4580 :         newVals[3] = newVals[2] + delta10;
    4698              :     }
    4699        10325 :     return oldVals[0];
    4700              : }
    4701            5 : 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            5 :     Real64 AirCapHM(0.0); // Air power capacity for hybrid modeling
    4724            5 :     Real64 AA(0.0);
    4725            5 :     Real64 BB(0.0);
    4726            5 :     Real64 FractionConvection(0.0); // Default convection portion of the sensible heat from people
    4727              : 
    4728            5 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4729            5 :     auto &zone = state.dataHeatBal->Zone(ZoneNum);
    4730            5 :     auto &hmZone = state.dataHybridModel->hybridModelZones(ZoneNum);
    4731            5 :     auto &thisZoneHB = s_ztpc->zoneHeatBalance(ZoneNum);
    4732              : 
    4733            5 :     int ZoneMult = zone.Multiplier * zone.ListMultiplier;
    4734            5 :     zone.ZoneMeasuredTemperature = (hmZone.measuredTempSched != nullptr) ? hmZone.measuredTempSched->getCurrentVal() : 0.0;
    4735            5 :     zone.ZoneVolCapMultpSensHM = 1.0; // Initialize to 1.0 in case hybrid not active
    4736              : 
    4737              :     // HM calculation only HM calculation period start
    4738            5 :     if (state.dataEnvrn->DayOfYear >= hmZone.HybridStartDayOfYear && state.dataEnvrn->DayOfYear <= hmZone.HybridEndDayOfYear) {
    4739            5 :         Real64 MultpHM(1.0);
    4740              : 
    4741            5 :         thisZoneHB.ZT = zone.ZoneMeasuredTemperature; // Array1D<Real64> ZT -- Zone
    4742              :                                                       // Air Temperature Averaged over
    4743              :                                                       // the System Time Increment
    4744            5 :         if (hmZone.InfiltrationCalc_T && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    4745              :             static constexpr std::string_view RoutineNameInfiltration("CalcAirFlowSimple:Infiltration");
    4746              : 
    4747            2 :             if (hmZone.IncludeSystemSupplyParameters) {
    4748            1 :                 zone.ZoneMeasuredSupplyAirTemperature = hmZone.supplyAirTempSched->getCurrentVal();
    4749            1 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched ? hmZone.supplyAirMassFlowRateSched->getCurrentVal() : 0.0;
    4750            1 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched ? hmZone.supplyAirHumRatSched->getCurrentVal() : 0.0;
    4751              :                 // Calculate the air humidity ratio at supply air inlet.
    4752            1 :                 Real64 CpAirInlet(0.0);
    4753            1 :                 CpAirInlet = Psychrometrics::PsyCpAirFnW(zone.ZoneMeasuredSupplyAirHumidityRatio);
    4754              : 
    4755            1 :                 Real64 SumSysMCp_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet;
    4756            1 :                 Real64 SumSysMCpT_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet * zone.ZoneMeasuredSupplyAirTemperature;
    4757              : 
    4758            1 :                 AA = SumSysMCp_HM + SumHA + thisZoneHB.MCPV + thisZoneHB.MCPM + thisZoneHB.MCPE + thisZoneHB.MCPC + thisZoneHB.MDotCPOA;
    4759            1 :                 BB = SumSysMCpT_HM + SumIntGain + SumHATsurf - SumHATref + thisZoneHB.MCPTV + thisZoneHB.MCPTM + thisZoneHB.MCPTE + thisZoneHB.MCPTC +
    4760            1 :                      thisZoneHB.MDotCPOA * zone.OutDryBulbTemp + (thisZoneHB.NonAirSystemResponse / ZoneMult + thisZoneHB.SysDepZoneLoadsLagged);
    4761              :             } else {
    4762            1 :                 AA = SumHA + thisZoneHB.MCPV + thisZoneHB.MCPM + thisZoneHB.MCPE + thisZoneHB.MCPC + thisZoneHB.MDotCPOA;
    4763            1 :                 BB = SumIntGain + SumHATsurf - SumHATref + thisZoneHB.MCPTV + thisZoneHB.MCPTM + thisZoneHB.MCPTE + thisZoneHB.MCPTC +
    4764            1 :                      thisZoneHB.MDotCPOA * zone.OutDryBulbTemp;
    4765              :             }
    4766            2 :             Real64 CC = AirCap;
    4767              :             Real64 DD =
    4768            2 :                 (3.0 * state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) - (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum) +
    4769            2 :                  (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredZT3(ZoneNum));
    4770              : 
    4771            2 :             Real64 delta_T = (zone.ZoneMeasuredTemperature - zone.OutDryBulbTemp);
    4772            2 :             Real64 CpAir = Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat);
    4773            2 :             Real64 AirDensity = Psychrometrics::PsyRhoAirFnPbTdbW(
    4774            2 :                 state, state.dataEnvrn->OutBaroPress, zone.OutDryBulbTemp, state.dataEnvrn->OutHumRat, RoutineNameInfiltration);
    4775            2 :             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            2 :             Real64 M_inf = 0.0;
    4779            2 :             if (std::abs(delta_T) > 0.5) {
    4780            2 :                 M_inf = (BB + CC * DD - ((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredTemperature) / (CpAir * delta_T);
    4781              :             }
    4782            2 :             Real64 ACH_inf = max(0.0, min(10.0, (M_inf / AirDensity) / zone.Volume * Constant::rSecsInHour));
    4783            2 :             M_inf = (ACH_inf / Constant::rSecsInHour) * zone.Volume * AirDensity;
    4784              : 
    4785              :             // Overwrite variable with inverse solution
    4786            2 :             zone.MCPIHM = M_inf;
    4787            2 :             zone.InfilOAAirChangeRateHM = ACH_inf;
    4788              : 
    4789              :         } // Hybrid model infiltration calculation end
    4790              : 
    4791              :         // Hybrid modeling internal thermal mass calculation start
    4792            6 :         if (hmZone.InternalThermalMassCalc_T && SumSysMCpT == 0 && thisZoneHB.ZT != state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) &&
    4793            1 :             state.dataHVACGlobal->UseZoneTimeStepHistory) { // HM calculation only when SumSysMCpT =0,
    4794              :                                                             // TimeStepZone (not @ TimeStepSys)
    4795            1 :             Real64 TempDepCoef = SumHA + SumMCp + SumSysMCp;
    4796            1 :             Real64 TempIndCoef = SumIntGain + SumHATsurf - SumHATref + SumMCpT + SumSysMCpT +
    4797            1 :                                  (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            1 :             if (state.afn->distribution_simulated) {
    4801            0 :                 TempIndCoef += state.afn->exchangeData(ZoneNum).TotalSen;
    4802              :             }
    4803              :             // Calculate air capacity using DataHeatBalance::SolutionAlgo::AnalyticalSolution
    4804            1 :             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            1 :                 Real64 AirCapHM_temp = 0.0;
    4810            1 :                 if (TempIndCoef == TempDepCoef * thisZoneHB.ZT) {
    4811            0 :                     AirCapHM_temp = 0.0; //  This is the denominator.
    4812              :                 } else {
    4813            1 :                     AirCapHM_temp = (TempIndCoef - TempDepCoef * state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum)) /
    4814            1 :                                     (TempIndCoef - TempDepCoef * thisZoneHB.ZT);
    4815              :                 }
    4816              : 
    4817            1 :                 if ((AirCapHM_temp > 0) && (AirCapHM_temp != 1)) {    // Avoid IND
    4818            1 :                     AirCapHM = TempDepCoef / std::log(AirCapHM_temp); // Inverse equation
    4819              :                 } else {
    4820            0 :                     AirCapHM = TempIndCoef / (thisZoneHB.ZT - state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum));
    4821              :                 }
    4822              :             }
    4823              : 
    4824              :             // Calculate multiplier
    4825            1 :             if (std::abs(thisZoneHB.ZT - state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum)) > 0.05) { // Filter
    4826            2 :                 MultpHM = AirCapHM /
    4827            2 :                           (zone.Volume *
    4828            2 :                            Psychrometrics::PsyRhoAirFnPbTdbW(state,
    4829            1 :                                                              state.dataEnvrn->OutBaroPress,
    4830              :                                                              thisZoneHB.ZT,
    4831            1 :                                                              thisZoneHB.airHumRat) *
    4832            1 :                            Psychrometrics::PsyCpAirFnW(thisZoneHB.airHumRat)) *
    4833            1 :                           (state.dataGlobal->TimeStepZone * Constant::rSecsInHour); // Inverse equation
    4834              :             } else {
    4835            0 :                 MultpHM = 1.0; // Default value 1.0
    4836              :             }
    4837              : 
    4838            1 :             processInverseModelMultpHM(
    4839            1 :                 state, MultpHM, zone.ZoneVolCapMultpSensHMSum, zone.ZoneVolCapMultpSensHMCountSum, zone.ZoneVolCapMultpSensHMAverage, ZoneNum);
    4840            1 :             zone.ZoneVolCapMultpSensHM = MultpHM;
    4841              : 
    4842              :         } // Hybrid model internal thermal mass calculation end
    4843              : 
    4844              :         // Hybrid model people count calculation
    4845            5 :         if (hmZone.PeopleCountCalc_T && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    4846            2 :             zone.ZoneMeasuredTemperature = hmZone.measuredTempSched->getCurrentVal();
    4847            2 :             zone.ZonePeopleActivityLevel = hmZone.peopleActivityLevelSched ? hmZone.peopleActivityLevelSched->getCurrentVal() : 0.0;
    4848            2 :             zone.ZonePeopleSensibleHeatFraction = hmZone.peopleSensibleFracSched ? hmZone.peopleSensibleFracSched->getCurrentVal() : 0.0;
    4849            2 :             zone.ZonePeopleRadiantHeatFraction = hmZone.peopleRadiantFracSched ? hmZone.peopleRadiantFracSched->getCurrentVal() : 0.0;
    4850              : 
    4851            2 :             Real64 FractionSensible = zone.ZonePeopleSensibleHeatFraction;
    4852            2 :             Real64 FractionRadiation = zone.ZonePeopleRadiantHeatFraction;
    4853            2 :             Real64 ActivityLevel = hmZone.peopleActivityLevelSched ? hmZone.peopleActivityLevelSched->getCurrentVal() : 0.0;
    4854              : 
    4855            2 :             if (FractionSensible <= 0.0) {
    4856            1 :                 FractionSensible = 0.6;
    4857              :             }
    4858              : 
    4859            2 :             if (FractionRadiation <= 0.0) {
    4860            1 :                 FractionConvection = 0.7;
    4861              :             } else {
    4862            1 :                 FractionConvection = 1.0 - FractionRadiation;
    4863              :             }
    4864              : 
    4865            2 :             if (ActivityLevel <= 0.0) {
    4866            1 :                 ActivityLevel = 130.0;
    4867              :             }
    4868              : 
    4869            2 :             if (hmZone.IncludeSystemSupplyParameters) {
    4870            1 :                 zone.ZoneMeasuredSupplyAirTemperature = hmZone.supplyAirTempSched->getCurrentVal();
    4871            1 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched ? hmZone.supplyAirMassFlowRateSched->getCurrentVal() : 0.0;
    4872            1 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched ? hmZone.supplyAirHumRatSched->getCurrentVal() : 0.0;
    4873              : 
    4874              :                 // Calculate the air humidity ratio at supply air inlet.
    4875            1 :                 Real64 CpAirInlet = Psychrometrics::PsyCpAirFnW(zone.ZoneMeasuredSupplyAirHumidityRatio);
    4876              : 
    4877            1 :                 Real64 SumSysMCp_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet;
    4878            1 :                 Real64 SumSysMCpT_HM = zone.ZoneMeasuredSupplyAirFlowRate * CpAirInlet * zone.ZoneMeasuredSupplyAirTemperature;
    4879              : 
    4880            1 :                 AA = SumSysMCp_HM + SumHA + SumMCp;
    4881            1 :                 BB = SumSysMCpT_HM + SumIntGainExceptPeople + SumHATsurf - SumHATref + SumMCpT +
    4882            1 :                      (thisZoneHB.NonAirSystemResponse / ZoneMult + thisZoneHB.SysDepZoneLoadsLagged);
    4883              :             } else {
    4884            1 :                 AA = SumHA + SumMCp;
    4885            1 :                 BB = SumIntGainExceptPeople + SumHATsurf - SumHATref + SumMCpT;
    4886              :             }
    4887              : 
    4888            2 :             Real64 CC = AirCap;
    4889              :             Real64 DD =
    4890            2 :                 (3.0 * state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) - (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum) +
    4891            2 :                  (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredZT3(ZoneNum));
    4892              : 
    4893            2 :             Real64 SumIntGainPeople = ((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredTemperature - BB - CC * DD;
    4894            2 :             Real64 UpperBound = max(0.0, SumIntGain / (ActivityLevel * FractionSensible * FractionConvection));
    4895            2 :             Real64 NumPeople = min(UpperBound, max(0.0, SumIntGainPeople / (ActivityLevel * FractionSensible * FractionConvection)));
    4896              : 
    4897            2 :             if (NumPeople < 0.05) {
    4898            2 :                 NumPeople = 0;
    4899              :             }
    4900            2 :             zone.NumOccHM = NumPeople;
    4901              :         }
    4902              :     }
    4903              : 
    4904              :     // Update zone temperatures in the previous steps
    4905            5 :     state.dataHeatBalFanSys->PreviousMeasuredZT3(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum);
    4906            5 :     state.dataHeatBalFanSys->PreviousMeasuredZT2(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum);
    4907            5 :     state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) = thisZoneHB.ZT;
    4908            5 : }
    4909              : 
    4910            6 : 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            6 :     Real64 constexpr minHMMultValue = 1.0;
    4919            6 :     Real64 constexpr maxHMMultValue = 30.0;
    4920              : 
    4921            6 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4922            6 :     auto &zone = state.dataHeatBal->Zone(zoneNum);
    4923            6 :     auto &thisZoneHB = s_ztpc->zoneHeatBalance(zoneNum);
    4924              : 
    4925              :     // Apply limits and generate warnings as needed
    4926            6 :     if (multiplierHM < minHMMultValue) { // don't allow this to be less than minimum (potential for instability)
    4927            2 :         multiplierHM = minHMMultValue;
    4928            4 :     } else if (multiplierHM > maxHMMultValue) { // as per suggestions in Defect #10508, only warn if greater than the max
    4929            1 :         if (thisZoneHB.hmThermalMassMultErrIndex == 0) {
    4930            1 :             ShowWarningMessage(state, format("Hybrid model thermal mass multiplier higher than the limit for {}", zone.Name));
    4931            2 :             ShowContinueError(state, "This means that the ratio of the zone air heat capacity for the current time step to the");
    4932            1 :             ShowContinueError(state, format("zone air heat storage is higher than the maximum limit of {:.1R}.", maxHMMultValue));
    4933              :         }
    4934            8 :         ShowRecurringWarningErrorAtEnd(
    4935            2 :             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            6 :     if (multiplierHM > minHMMultValue) {
    4940            3 :         multSumHM += multiplierHM;
    4941            3 :         countSumHM++;
    4942              :     }
    4943              : 
    4944              :     // Calculate average (always so that it does get calculated)
    4945            6 :     if (countSumHM >= 1) {
    4946            4 :         multAvgHM = multSumHM / countSumHM;
    4947              :     }
    4948            6 : }
    4949              : 
    4950            4 : 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            4 :     Real64 AA(0.0);
    4972            4 :     Real64 BB(0.0);
    4973            4 :     Real64 ActivityLevel(0.0);
    4974            4 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    4975              : 
    4976            4 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    4977            4 :     auto &zone = state.dataHeatBal->Zone(ZoneNum);
    4978            4 :     auto &hmZone = state.dataHybridModel->hybridModelZones(ZoneNum);
    4979            4 :     auto &thisZoneHB = s_ztpc->zoneHeatBalance(ZoneNum);
    4980              : 
    4981              :     // Get measured zone humidity ratio
    4982            4 :     zone.ZoneMeasuredHumidityRatio = hmZone.measuredHumRatSched->getCurrentVal();
    4983              : 
    4984            4 :     if (state.dataEnvrn->DayOfYear >= hmZone.HybridStartDayOfYear && state.dataEnvrn->DayOfYear <= hmZone.HybridEndDayOfYear) {
    4985            4 :         thisZoneHB.airHumRat = zone.ZoneMeasuredHumidityRatio;
    4986              : 
    4987              :         // Hybrid Model calculate air infiltration rate
    4988            4 :         if (hmZone.InfiltrationCalc_H && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    4989              :             // Conditionally calculate the time dependent and time independent terms
    4990            2 :             if (hmZone.IncludeSystemSupplyParameters) {
    4991            1 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched->getCurrentVal();
    4992            1 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched->getCurrentVal();
    4993              : 
    4994            1 :                 Real64 SumSysM_HM = zone.ZoneMeasuredSupplyAirFlowRate;
    4995            1 :                 Real64 SumSysMHumRat_HM = zone.ZoneMeasuredSupplyAirFlowRate * zone.ZoneMeasuredSupplyAirHumidityRatio;
    4996              : 
    4997            1 :                 AA = SumSysM_HM + thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa + thisZoneHB.MixingMassFlowZone +
    4998            1 :                      thisZoneHB.MDotOA;
    4999            1 :                 BB = SumSysMHumRat_HM + (LatentGain / H2OHtOfVap) + ((thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) +
    5000            1 :                      thisZoneHB.EAMFLxHumRat + thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat +
    5001            1 :                      thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5002              :             } else {
    5003            1 :                 AA = thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa + thisZoneHB.MixingMassFlowZone + thisZoneHB.MDotOA;
    5004            1 :                 BB = (LatentGain / H2OHtOfVap) + ((thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) + thisZoneHB.EAMFLxHumRat +
    5005            1 :                      thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat + thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5006              :             }
    5007              : 
    5008            2 :             Real64 CC = RhoAir * zone.Volume * zone.ZoneVolCapMultpMoist / TimeStepSysSec;
    5009            2 :             Real64 DD = (3.0 * state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum) -
    5010            2 :                          (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum) +
    5011            2 :                          (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat3(ZoneNum));
    5012              : 
    5013            2 :             Real64 delta_HR = (zone.ZoneMeasuredHumidityRatio - state.dataEnvrn->OutHumRat);
    5014              : 
    5015              :             Real64 AirDensity =
    5016            2 :                 Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, zone.OutDryBulbTemp, state.dataEnvrn->OutHumRat, RoutineName);
    5017              : 
    5018            2 :             Real64 M_inf = 0.0;
    5019            2 :             if (std::abs(zone.ZoneMeasuredHumidityRatio - state.dataEnvrn->OutHumRat) > 0.0000001) {
    5020            2 :                 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            2 :             Real64 ACH_inf = max(0.0, min(10.0, (M_inf / AirDensity) / zone.Volume * Constant::rSecsInHour));
    5025            2 :             M_inf = (ACH_inf / Constant::rSecsInHour) * zone.Volume * AirDensity;
    5026            2 :             zone.MCPIHM = M_inf;
    5027            2 :             zone.InfilOAAirChangeRateHM = ACH_inf;
    5028              :         }
    5029              : 
    5030              :         // Hybrid Model calculate people count
    5031            4 :         if (hmZone.PeopleCountCalc_H && state.dataHVACGlobal->UseZoneTimeStepHistory) {
    5032            2 :             zone.ZonePeopleActivityLevel = hmZone.peopleActivityLevelSched ? hmZone.peopleActivityLevelSched->getCurrentVal() : 0.0;
    5033            2 :             zone.ZonePeopleSensibleHeatFraction = hmZone.peopleSensibleFracSched ? hmZone.peopleSensibleFracSched->getCurrentVal() : 0.0;
    5034            2 :             zone.ZonePeopleRadiantHeatFraction = hmZone.peopleRadiantFracSched ? hmZone.peopleRadiantFracSched->getCurrentVal() : 0.0;
    5035              : 
    5036            2 :             Real64 FractionSensible = zone.ZonePeopleSensibleHeatFraction;
    5037              : 
    5038            2 :             if (FractionSensible <= 0.0) {
    5039            1 :                 FractionSensible = 0.6;
    5040              :             }
    5041              : 
    5042            2 :             if (ActivityLevel <= 0.0) {
    5043            2 :                 ActivityLevel = 130.0;
    5044              :             }
    5045              : 
    5046              :             // Conditionally calculate the humidity-dependent and humidity-independent
    5047              :             // terms.
    5048            2 :             if (hmZone.IncludeSystemSupplyParameters) {
    5049            1 :                 zone.ZoneMeasuredSupplyAirFlowRate = hmZone.supplyAirMassFlowRateSched->getCurrentVal();
    5050            1 :                 zone.ZoneMeasuredSupplyAirHumidityRatio = hmZone.supplyAirHumRatSched->getCurrentVal();
    5051              : 
    5052            1 :                 Real64 SumSysM_HM = zone.ZoneMeasuredSupplyAirFlowRate;
    5053            1 :                 Real64 SumSysMHumRat_HM = zone.ZoneMeasuredSupplyAirFlowRate * zone.ZoneMeasuredSupplyAirHumidityRatio;
    5054              : 
    5055            1 :                 AA = SumSysM_HM + thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa +
    5056            1 :                      thisZoneHB.MixingMassFlowZone + thisZoneHB.MDotOA;
    5057            3 :                 BB = SumSysMHumRat_HM + (LatentGainExceptPeople / H2OHtOfVap) +
    5058            1 :                      ((thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) + thisZoneHB.EAMFLxHumRat +
    5059            1 :                      thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat + thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5060              :             } else {
    5061            1 :                 AA = ZoneMassFlowRate + thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.EAMFL + thisZoneHB.CTMFL + thisZoneHB.SumHmARa +
    5062            1 :                      thisZoneHB.MixingMassFlowZone + thisZoneHB.MDotOA;
    5063            1 :                 BB = (LatentGainExceptPeople / H2OHtOfVap) + ((thisZoneHB.OAMFL + thisZoneHB.VAMFL + thisZoneHB.CTMFL) * state.dataEnvrn->OutHumRat) +
    5064            1 :                      thisZoneHB.EAMFLxHumRat + (MoistureMassFlowRate) + thisZoneHB.SumHmARaW + thisZoneHB.MixingMassFlowXHumRat +
    5065            1 :                      thisZoneHB.MDotOA * state.dataEnvrn->OutHumRat;
    5066              :             }
    5067              : 
    5068            2 :             Real64 CC = RhoAir * zone.Volume * zone.ZoneVolCapMultpMoist / TimeStepSysSec;
    5069            2 :             Real64 DD = (3.0 * state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum) -
    5070            2 :                          (3.0 / 2.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum) +
    5071            2 :                          (1.0 / 3.0) * state.dataHeatBalFanSys->PreviousMeasuredHumRat3(ZoneNum));
    5072              : 
    5073            2 :             Real64 LatentGainPeople = (((11.0 / 6.0) * CC + AA) * zone.ZoneMeasuredHumidityRatio - BB - CC * DD) * H2OHtOfVap;
    5074            2 :             Real64 UpperBound = max(0.0, LatentGain / (ActivityLevel * (1.0 - FractionSensible)));
    5075            2 :             Real64 NumPeople = min(UpperBound, max(0.0, LatentGainPeople / (ActivityLevel * (1.0 - FractionSensible))));
    5076            2 :             NumPeople = floor(NumPeople * 100.00 + 0.5) / 100.00;
    5077            2 :             if (NumPeople < 0.05) {
    5078            0 :                 NumPeople = 0;
    5079              :             }
    5080            2 :             zone.NumOccHM = NumPeople;
    5081              :         }
    5082              :     }
    5083              : 
    5084              :     // Update zone humidity ratio in the previous steps
    5085            4 :     state.dataHeatBalFanSys->PreviousMeasuredHumRat3(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum);
    5086            4 :     state.dataHeatBalFanSys->PreviousMeasuredHumRat2(ZoneNum) = state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum);
    5087            4 :     state.dataHeatBalFanSys->PreviousMeasuredHumRat1(ZoneNum) = zone.ZoneMeasuredHumidityRatio;
    5088            4 : }
    5089              : 
    5090       936456 : 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       936456 :     assert(zoneNum > 0);
    5116              : 
    5117       936456 :     this->SumHA = 0.0;
    5118       936456 :     this->SumHATsurf = 0.0;
    5119       936456 :     this->SumHATref = 0.0;
    5120       936456 :     this->SumSysMCp = 0.0;
    5121       936456 :     this->SumSysMCpT = 0.0;
    5122              :     // Sum all convective internal gains: this->SumIntGain
    5123       936456 :     if (spaceNum == 0) {
    5124       836664 :         this->SumIntGain = InternalHeatGains::zoneSumAllInternalConvectionGains(state, zoneNum);
    5125              :     } else {
    5126        99792 :         this->SumIntGain = InternalHeatGains::spaceSumAllInternalConvectionGains(state, spaceNum);
    5127              :     }
    5128       936456 :     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       936456 :     assert(zoneNum > 0);
    5132       936456 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    5133       936456 :     if (thisZone.NoHeatToReturnAir) {
    5134        33084 :         if (spaceNum == 0) {
    5135        33084 :             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       936456 :     this->SumMCp = this->MCPI + this->MCPV + this->MCPM + this->MCPE + this->MCPC + this->MDotCPOA;
    5143       936456 :     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      1838808 :     if (state.afn->multizone_always_simulated ||
    5147       902352 :         (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    5148            0 :          state.afn->AirflowNetworkFanActivated)) {
    5149        34104 :         auto const &exchangeData = state.afn->exchangeData(zoneNum);
    5150        34104 :         this->SumMCp = exchangeData.SumMCp + exchangeData.SumMVCp + exchangeData.SumMMCp;
    5151        34104 :         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       936456 :     bool isSpaceControlled = (spaceNum > 0 && state.dataZoneEquip->spaceEquipConfig(spaceNum).IsControlled);
    5157       936456 :     if (CorrectorFlag) {
    5158              :         // Plenum and controlled zones have a different set of inlet nodes which must be calculated.
    5159       473640 :         if (thisZone.IsControlled) {
    5160       199576 :             auto const &zsec = (isSpaceControlled ? state.dataZoneEquip->spaceEquipConfig(spaceNum) : state.dataZoneEquip->ZoneEquipConfig(zoneNum));
    5161       418189 :             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       218613 :                 auto const &node(state.dataLoopNodes->Node(zsec.InletNode(NodeNum)));
    5165       218613 :                 Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->airHumRat);
    5166       218613 :                 Real64 const MassFlowRate_CpAir(node.MassFlowRate * CpAir);
    5167       218613 :                 this->SumSysMCp += MassFlowRate_CpAir;
    5168       218613 :                 this->SumSysMCpT += MassFlowRate_CpAir * node.Temp;
    5169              :             }
    5170              : 
    5171       274064 :         } else if (thisZone.IsReturnPlenum) {
    5172            0 :             auto const &zrpc(state.dataZonePlenum->ZoneRetPlenCond(thisZone.PlenumCondNum));
    5173            0 :             Real64 const air_hum_rat(this->airHumRat);
    5174            0 :             for (int NodeNum = 1, NodeNum_end = zrpc.NumInletNodes; NodeNum <= NodeNum_end; ++NodeNum) {
    5175            0 :                 auto const &node(state.dataLoopNodes->Node(zrpc.InletNode(NodeNum)));
    5176            0 :                 Real64 const MassFlowRate_CpAir(node.MassFlowRate * Psychrometrics::PsyCpAirFnW(air_hum_rat));
    5177            0 :                 this->SumSysMCp += MassFlowRate_CpAir;
    5178            0 :                 this->SumSysMCpT += MassFlowRate_CpAir * node.Temp;
    5179              :             }
    5180              :             // add in the leaks
    5181            0 :             for (int ADUListIndex = 1, ADUListIndex_end = zrpc.NumADUs; ADUListIndex <= ADUListIndex_end; ++ADUListIndex) {
    5182            0 :                 auto &airDistUnit = state.dataDefineEquipment->AirDistUnit(zrpc.ADUIndex(ADUListIndex));
    5183            0 :                 if (airDistUnit.UpStreamLeak) {
    5184            0 :                     Real64 const MassFlowRate_CpAir(airDistUnit.MassFlowRateUpStrLk * Psychrometrics::PsyCpAirFnW(air_hum_rat));
    5185            0 :                     this->SumSysMCp += MassFlowRate_CpAir;
    5186            0 :                     this->SumSysMCpT += MassFlowRate_CpAir * state.dataLoopNodes->Node(airDistUnit.InletNodeNum).Temp;
    5187              :                 }
    5188            0 :                 if (airDistUnit.DownStreamLeak) {
    5189            0 :                     Real64 const MassFlowRate_CpAir(airDistUnit.MassFlowRateDnStrLk * Psychrometrics::PsyCpAirFnW(air_hum_rat));
    5190            0 :                     this->SumSysMCp += MassFlowRate_CpAir;
    5191            0 :                     this->SumSysMCpT += MassFlowRate_CpAir * state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).Temp;
    5192              :                 }
    5193              :             }
    5194              : 
    5195       274064 :         } else if (thisZone.IsSupplyPlenum) {
    5196            0 :             Real64 MassFlowRate = state.dataLoopNodes->Node(state.dataZonePlenum->ZoneSupPlenCond(thisZone.PlenumCondNum).InletNode).MassFlowRate;
    5197            0 :             Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->airHumRat);
    5198            0 :             this->SumSysMCp += MassFlowRate * CpAir;
    5199            0 :             this->SumSysMCpT +=
    5200            0 :                 MassFlowRate * CpAir * state.dataLoopNodes->Node(state.dataZonePlenum->ZoneSupPlenCond(thisZone.PlenumCondNum).InletNode).Temp;
    5201              :         }
    5202              : 
    5203       473640 :         int ZoneMult = thisZone.Multiplier * thisZone.ListMultiplier;
    5204              : 
    5205       473640 :         this->SumSysMCp /= ZoneMult;
    5206       473640 :         this->SumSysMCpT /= ZoneMult;
    5207              :     }
    5208              : 
    5209       936456 :     if (spaceNum > 0 && !isSpaceControlled) {
    5210              :         // If space is not controlled, allocate zone-level airflow by volume
    5211        99792 :         Real64 spaceFrac = state.dataHeatBal->space(spaceNum).fracZoneVolume;
    5212        99792 :         this->SumSysMCp *= spaceFrac;
    5213        99792 :         this->SumSysMCpT *= spaceFrac;
    5214              :     }
    5215              : 
    5216              :     // Sum all surface convection: this->SumHA, this->SumHATsurf, this->SumHATref (and additional contributions to this->SumIntGain)
    5217       936456 :     SumHATOutput sumHATResults; // space or zone return values
    5218       936456 :     sumHATResults = this->calcSumHAT(state, zoneNum, spaceNum);
    5219       936456 :     this->SumIntGain += sumHATResults.sumIntGain;
    5220       936456 :     this->SumHA = sumHATResults.sumHA;
    5221       936456 :     this->SumHATsurf = sumHATResults.sumHATsurf;
    5222       936456 :     this->SumHATref = sumHATResults.sumHATref;
    5223       936456 : }
    5224              : 
    5225       836664 : SumHATOutput ZoneHeatBalanceData::calcSumHAT(EnergyPlusData &state, int const zoneNum, [[maybe_unused]] int const spaceNum)
    5226              : {
    5227       836664 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5228       836664 :     assert(zoneNum > 0);
    5229       836664 :     assert(spaceNum == 0);
    5230       836664 :     SumHATOutput zoneResults; // zone-level return values
    5231      1777922 :     for (int zoneSpaceNum : state.dataHeatBal->Zone(zoneNum).spaceIndexes) {
    5232       941258 :         SumHATOutput spaceResults; // temporary return value from space-level calcSumHAT
    5233       941258 :         spaceResults = s_ztpc->spaceHeatBalance(zoneSpaceNum).calcSumHAT(state, zoneNum, zoneSpaceNum);
    5234       941258 :         zoneResults.sumIntGain += spaceResults.sumIntGain;
    5235       941258 :         zoneResults.sumHA += spaceResults.sumHA;
    5236       941258 :         zoneResults.sumHATsurf += spaceResults.sumHATsurf;
    5237       941258 :         zoneResults.sumHATref += spaceResults.sumHATref;
    5238       836664 :     }
    5239       836664 :     return zoneResults;
    5240              : }
    5241              : 
    5242      1041050 : SumHATOutput SpaceHeatBalanceData::calcSumHAT(EnergyPlusData &state, int const zoneNum, int const spaceNum)
    5243              : {
    5244      1041050 :     assert(zoneNum > 0);
    5245      1041050 :     assert(spaceNum > 0);
    5246      1041050 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5247      1041050 :     auto &thisZone = state.dataHeatBal->Zone(zoneNum);
    5248      1041050 :     auto &thisSpace = state.dataHeatBal->space(spaceNum);
    5249      1041050 :     SumHATOutput results; // space-level return values
    5250              : 
    5251      5977375 :     for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
    5252      4936325 :         Real64 HA = 0.0;
    5253      4936325 :         Real64 Area = state.dataSurface->Surface(SurfNum).Area; // For windows, this is the glazing area
    5254              : 
    5255      4936325 :         if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
    5256       258198 :             DataSurfaces::WinShadingType const shading_flag = state.dataSurface->SurfWinShadingFlag(SurfNum);
    5257              : 
    5258              :             // Add to the convective internal gains
    5259       258198 :             if (ANY_INTERIOR_SHADE_BLIND(shading_flag)) {
    5260              :                 // The shade area covers the area of the glazing plus the area of the dividers.
    5261            0 :                 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            0 :                 results.sumIntGain += state.dataSurface->SurfWinDividerHeatGain(SurfNum);
    5267              :             }
    5268              : 
    5269              :             // Other convection term is applicable to equivalent layer window (ASHWAT) model
    5270       258198 :             if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
    5271        11458 :                 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       258198 :             if (ANY_INTERIOR_SHADE_BLIND(shading_flag)) {
    5276            0 :                 results.sumIntGain += state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum);
    5277              :             }
    5278              : 
    5279              :             // Convective heat gain from airflow window
    5280       258198 :             if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
    5281            0 :                 results.sumIntGain += state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum);
    5282            0 :                 if (thisZone.NoHeatToReturnAir) {
    5283            0 :                     results.sumIntGain += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
    5284            0 :                     state.dataSurface->SurfWinHeatGain(SurfNum) += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
    5285            0 :                     if (state.dataSurface->SurfWinHeatGain(SurfNum) >= 0.0) {
    5286            0 :                         state.dataSurface->SurfWinHeatGainRep(SurfNum) = state.dataSurface->SurfWinHeatGain(SurfNum);
    5287            0 :                         state.dataSurface->SurfWinHeatGainRepEnergy(SurfNum) =
    5288            0 :                             state.dataSurface->SurfWinHeatGainRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    5289              :                     } else {
    5290            0 :                         state.dataSurface->SurfWinHeatLossRep(SurfNum) = -state.dataSurface->SurfWinHeatGain(SurfNum);
    5291            0 :                         state.dataSurface->SurfWinHeatLossRepEnergy(SurfNum) =
    5292            0 :                             state.dataSurface->SurfWinHeatLossRep(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    5293              :                     }
    5294            0 :                     state.dataSurface->SurfWinHeatTransferRepEnergy(SurfNum) =
    5295            0 :                         state.dataSurface->SurfWinHeatGain(SurfNum) * state.dataGlobal->TimeStepZoneSec;
    5296              :                 }
    5297              :             }
    5298              : 
    5299              :             // Add to the surface convection sums
    5300       258198 :             if (state.dataSurface->SurfWinFrameArea(SurfNum) > 0.0) {
    5301              :                 // Window frame contribution
    5302           32 :                 Real64 const HA_surf(state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
    5303           32 :                                      (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum)));
    5304           32 :                 results.sumHATsurf += HA_surf * state.dataSurface->SurfWinFrameTempIn(SurfNum);
    5305           32 :                 HA += HA_surf;
    5306              :             }
    5307              : 
    5308       258198 :             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           32 :                 Real64 const HA_surf(state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
    5311           32 :                                      (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum)));
    5312           32 :                 results.sumHATsurf += HA_surf * state.dataSurface->SurfWinDividerTempIn(SurfNum);
    5313           32 :                 HA += HA_surf;
    5314              :             }
    5315              : 
    5316              :         } // End of check if window
    5317              : 
    5318      4936325 :         HA += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area;
    5319      4936325 :         results.sumHATsurf += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area * state.dataHeatBalSurf->SurfTempInTmp(SurfNum);
    5320              : 
    5321              :         // determine reference air temperature for this surface
    5322      4936325 :         switch (state.dataSurface->SurfTAirRef(SurfNum)) {
    5323            4 :         case DataSurfaces::RefAirTemp::ZoneMeanAirTemp:
    5324              :             // The zone air is the reference temperature (which is to be solved for in CorrectZoneAirTemp).
    5325            4 :             results.sumHA += HA;
    5326            4 :             break;
    5327            4 :         case DataSurfaces::RefAirTemp::AdjacentAirTemp:
    5328            4 :             results.sumHATref += HA * state.dataHeatBal->SurfTempEffBulkAir(SurfNum);
    5329            4 :             break;
    5330            4 :         case DataSurfaces::RefAirTemp::ZoneSupplyAirTemp:
    5331              :             // check whether this zone is a controlled zone or not
    5332            4 :             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            4 :             if (s_ztpc->zoneHeatBalance(zoneNum).SumSysMCp > 0.0) {
    5340            2 :                 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            2 :                 results.sumHA += HA;
    5344              :             }
    5345            4 :             break;
    5346      4936313 :         default:
    5347              :             // currently set to mean air temp but should add error warning here
    5348      4936313 :             results.sumHA += HA;
    5349      4936313 :             break;
    5350              :         }
    5351              : 
    5352              :     } // SurfNum
    5353      1041050 :     return results;
    5354              : }
    5355       473637 : 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       473637 :     thisAirRpt.SumIntGains = 0.0;    // Zone sum of convective internal gains
    5382       473637 :     thisAirRpt.SumHADTsurfs = 0.0;   // Zone sum of Hc*Area*(Tsurf - Tz)
    5383       473637 :     thisAirRpt.SumMCpDTzones = 0.0;  // zone sum of MassFlowRate*cp*(TremotZone - Tz) transfer air from other zone, Mixing
    5384       473637 :     thisAirRpt.SumMCpDtInfil = 0.0;  // Zone sum of MassFlowRate*Cp*(Tout - Tz)
    5385       473637 :     thisAirRpt.SumMCpDTsystem = 0.0; // Zone sum of air system MassFlowRate*Cp*(Tsup - Tz)
    5386       473637 :     thisAirRpt.SumNonAirSystem = 0.0;
    5387       473637 :     thisAirRpt.CzdTdt = 0.0;
    5388       473637 :     thisAirRpt.imBalance = 0.0;
    5389       473637 :     thisAirRpt.SumEnthalpyM = 0.0;
    5390       473637 :     thisAirRpt.SumEnthalpyH = 0.0;
    5391              : 
    5392       473637 :     auto &thisZone = state.dataHeatBal->Zone(ZoneNum);
    5393              : 
    5394       473637 :     Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
    5395              : 
    5396              :     // Sum all convective internal gains: SumIntGain
    5397       473637 :     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       473637 :     if (thisZone.NoHeatToReturnAir) {
    5402        16542 :         thisAirRpt.SumIntGains += InternalHeatGains::zoneSumAllReturnAirConvectionGains(state, ZoneNum, 0);
    5403              :     }
    5404              : 
    5405              :     // sum non-system air flow transfers between zones
    5406       473637 :     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       473637 :     thisAirRpt.SumMCpDtInfil = (thisHB->MCPTI - thisHB->MCPI * thisHB->MAT) + (thisHB->MCPTV - thisHB->MCPV * thisHB->MAT) +
    5411       473637 :                                (thisHB->MCPTE - thisHB->MCPE * thisHB->MAT) + (thisHB->MCPTC - thisHB->MCPC * thisHB->MAT) +
    5412       473637 :                                (thisHB->MDotCPOA * thisZone.OutDryBulbTemp -
    5413       473637 :                                 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       930222 :     if (state.afn->multizone_always_simulated ||
    5417       456585 :         (state.afn->simulation_control.type == AirflowNetwork::ControlType::MultizoneWithDistributionOnlyDuringFanOperation &&
    5418            0 :          state.afn->AirflowNetworkFanActivated)) {
    5419              :         // Multizone airflow calculated in AirflowNetwork
    5420        17052 :         thisAirRpt.SumMCpDtInfil = state.afn->exchangeData(ZoneNum).SumMCpT + state.afn->exchangeData(ZoneNum).SumMVCpT -
    5421        17052 :                                    (state.afn->exchangeData(ZoneNum).SumMCp + state.afn->exchangeData(ZoneNum).SumMVCp) * thisHB->MAT;
    5422        17052 :         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       473637 :     Real64 QSensRate = 0.0;
    5428       473637 :     if (thisZone.IsControlled) {
    5429       199573 :         auto &zoneEquipConfig = state.dataZoneEquip->ZoneEquipConfig(ZoneNum);
    5430       418180 :         for (int NodeNum = 1; NodeNum <= zoneEquipConfig.NumInletNodes; ++NodeNum) {
    5431              :             // Get node conditions
    5432       218607 :             Real64 const NodeTemp = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(NodeNum)).Temp;
    5433       218607 :             Real64 const MassFlowRate = state.dataLoopNodes->Node(zoneEquipConfig.InletNode(NodeNum)).MassFlowRate;
    5434       218607 :             QSensRate = calcZoneSensibleOutput(MassFlowRate, NodeTemp, thisHB->MAT, thisHB->airHumRat);
    5435       218607 :             thisAirRpt.SumMCpDTsystem += QSensRate;
    5436              : 
    5437       218607 :             if (zoneEquipConfig.InletNodeADUNum(NodeNum) > 0) {
    5438        52394 :                 auto &airDistUnit = state.dataDefineEquipment->AirDistUnit(zoneEquipConfig.InletNodeADUNum(NodeNum));
    5439        52394 :                 Real64 ADUHeatAddRate = calcZoneSensibleOutput(state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).MassFlowRate,
    5440        52394 :                                                                state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).Temp,
    5441              :                                                                thisHB->MAT,
    5442              :                                                                thisHB->airHumRat);
    5443        52394 :                 airDistUnit.HeatRate = max(0.0, ADUHeatAddRate);
    5444        52394 :                 airDistUnit.CoolRate = std::abs(min(0.0, ADUHeatAddRate));
    5445        52394 :                 airDistUnit.HeatGain = airDistUnit.HeatRate * TimeStepSysSec;
    5446        52394 :                 airDistUnit.CoolGain = airDistUnit.CoolRate * TimeStepSysSec;
    5447              :             }
    5448              :         }
    5449              : 
    5450       274064 :     } else if (thisZone.IsReturnPlenum) {
    5451            0 :         auto &zoneRetPlenCond = state.dataZonePlenum->ZoneRetPlenCond(thisZone.PlenumCondNum);
    5452            0 :         for (int NodeNum = 1; NodeNum <= zoneRetPlenCond.NumInletNodes; ++NodeNum) {
    5453            0 :             QSensRate = calcZoneSensibleOutput(state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(NodeNum)).MassFlowRate,
    5454            0 :                                                state.dataLoopNodes->Node(zoneRetPlenCond.InletNode(NodeNum)).Temp,
    5455              :                                                thisHB->MAT,
    5456              :                                                thisHB->airHumRat);
    5457            0 :             thisAirRpt.SumMCpDTsystem += QSensRate;
    5458              :         }
    5459              :         // add in the leaks
    5460            0 :         for (int ADUListIndex = 1; ADUListIndex <= zoneRetPlenCond.NumADUs; ++ADUListIndex) {
    5461            0 :             auto &airDistUnit = state.dataDefineEquipment->AirDistUnit(zoneRetPlenCond.ADUIndex(ADUListIndex));
    5462            0 :             if (airDistUnit.UpStreamLeak) {
    5463            0 :                 QSensRate = calcZoneSensibleOutput(
    5464            0 :                     airDistUnit.MassFlowRateUpStrLk, state.dataLoopNodes->Node(airDistUnit.InletNodeNum).Temp, thisHB->MAT, thisHB->airHumRat);
    5465            0 :                 thisAirRpt.SumMCpDTsystem += QSensRate;
    5466              :             }
    5467            0 :             if (airDistUnit.DownStreamLeak) {
    5468            0 :                 QSensRate = calcZoneSensibleOutput(
    5469            0 :                     airDistUnit.MassFlowRateDnStrLk, state.dataLoopNodes->Node(airDistUnit.OutletNodeNum).Temp, thisHB->MAT, thisHB->airHumRat);
    5470            0 :                 thisAirRpt.SumMCpDTsystem += QSensRate;
    5471              :             }
    5472              :         }
    5473              : 
    5474       274064 :     } else if (thisZone.IsSupplyPlenum) {
    5475            0 :         auto &zoneSupPlenCond = state.dataZonePlenum->ZoneSupPlenCond(thisZone.PlenumCondNum);
    5476            0 :         QSensRate = calcZoneSensibleOutput(state.dataLoopNodes->Node(zoneSupPlenCond.InletNode).MassFlowRate,
    5477            0 :                                            state.dataLoopNodes->Node(zoneSupPlenCond.InletNode).Temp,
    5478              :                                            thisHB->MAT,
    5479              :                                            thisHB->airHumRat);
    5480            0 :         thisAirRpt.SumMCpDTsystem += QSensRate;
    5481              :     }
    5482              : 
    5483              :     // non air system response.
    5484       473637 :     thisAirRpt.SumNonAirSystem =
    5485       473637 :         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      1036441 :     for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
    5489       562804 :         auto const &thisSpace = state.dataHeatBal->space(spaceNum);
    5490      3065822 :         for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
    5491              : 
    5492      2503018 :             Real64 Area = state.dataSurface->Surface(SurfNum).Area; // For windows, this is the glazing area
    5493      2503018 :             Real64 RefAirTemp = state.dataSurface->Surface(SurfNum).getInsideAirTemperature(state, SurfNum);
    5494              : 
    5495      2503018 :             if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Window) {
    5496              : 
    5497              :                 // Add to the convective internal gains
    5498       129099 :                 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            0 :                     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            0 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinDividerHeatGain(SurfNum);
    5506              :                 }
    5507              : 
    5508              :                 // Other convection term is applicable to equivalent layer window (ASHWAT) model
    5509       129099 :                 if (state.dataConstruction->Construct(state.dataSurface->Surface(SurfNum).Construction).WindowTypeEQL) {
    5510         5729 :                     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       129099 :                 if (ANY_INTERIOR_SHADE_BLIND(state.dataSurface->SurfWinShadingFlag(SurfNum))) {
    5515            0 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinConvHeatFlowNatural(SurfNum);
    5516              :                 }
    5517              : 
    5518              :                 // Convective heat gain from airflow window
    5519       129099 :                 if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0) {
    5520            0 :                     thisAirRpt.SumIntGains += state.dataSurface->SurfWinConvHeatGainToZoneAir(SurfNum);
    5521            0 :                     if (thisZone.NoHeatToReturnAir) {
    5522            0 :                         thisAirRpt.SumIntGains += state.dataSurface->SurfWinRetHeatGainToZoneAir(SurfNum);
    5523              :                     }
    5524              :                 }
    5525              : 
    5526              :                 // Add to the surface convection sums
    5527       129099 :                 if (state.dataSurface->SurfWinFrameArea(SurfNum) > 0.0) {
    5528              :                     // Window frame contribution
    5529           16 :                     thisAirRpt.SumHADTsurfs += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinFrameArea(SurfNum) *
    5530           16 :                                                (1.0 + state.dataSurface->SurfWinProjCorrFrIn(SurfNum)) *
    5531           16 :                                                (state.dataSurface->SurfWinFrameTempIn(SurfNum) - RefAirTemp);
    5532              :                 }
    5533              : 
    5534       129115 :                 if (state.dataSurface->SurfWinDividerArea(SurfNum) > 0.0 &&
    5535           16 :                     !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           16 :                     thisAirRpt.SumHADTsurfs += state.dataHeatBalSurf->SurfHConvInt(SurfNum) * state.dataSurface->SurfWinDividerArea(SurfNum) *
    5538           16 :                                                (1.0 + 2.0 * state.dataSurface->SurfWinProjCorrDivIn(SurfNum)) *
    5539           16 :                                                (state.dataSurface->SurfWinDividerTempIn(SurfNum) - RefAirTemp);
    5540              :                 }
    5541              : 
    5542              :             } // End of check if window
    5543              : 
    5544      2503018 :             thisAirRpt.SumHADTsurfs +=
    5545      2503018 :                 state.dataHeatBalSurf->SurfHConvInt(SurfNum) * Area * (state.dataHeatBalSurf->SurfTempInTmp(SurfNum) - RefAirTemp);
    5546              : 
    5547              :             // Accumulate Zone Phase Change Material Melting/Freezing Enthalpy output variables
    5548      2503018 :             if (state.dataSurface->Surface(SurfNum).HeatTransferAlgorithm == DataSurfaces::HeatTransferModel::CondFD) {
    5549       293832 :                 thisAirRpt.SumEnthalpyM += state.dataHeatBalFiniteDiffMgr->SurfaceFD(SurfNum).EnthalpyM;
    5550       293832 :                 thisAirRpt.SumEnthalpyH += state.dataHeatBalFiniteDiffMgr->SurfaceFD(SurfNum).EnthalpyF;
    5551              :             }
    5552              :         }
    5553       473637 :     }
    5554              :     // now calculate air energy storage source term.
    5555              :     // capacitance is volume * density * heat capacity
    5556       473637 :     Real64 CpAir = Psychrometrics::PsyCpAirFnW(thisHB->airHumRat);
    5557       473637 :     Real64 RhoAir = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, thisHB->MAT, thisHB->airHumRat);
    5558              : 
    5559       473637 :     switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    5560       356605 :     case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    5561       356605 :         thisAirRpt.CzdTdt = RhoAir * CpAir * thisZone.Volume * thisZone.ZoneVolCapMultpSens * (thisHB->MAT - thisHB->ZTM[0]) / TimeStepSysSec;
    5562              :         // Exact solution
    5563       356605 :     } break;
    5564       117032 :     case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    5565       117032 :         thisAirRpt.CzdTdt = thisHB->TempIndCoef - thisHB->TempDepCoef * thisHB->MAT;
    5566       117032 :     } break;
    5567            0 :     case DataHeatBalance::SolutionAlgo::EulerMethod: {
    5568            0 :         thisAirRpt.CzdTdt = thisHB->AirPowerCap * (thisHB->MAT - thisHB->T1);
    5569            0 :     } break;
    5570            0 :     default:
    5571            0 :         break;
    5572              :     }
    5573              : 
    5574       473637 :     if (state.dataGlobal->DisplayZoneAirHeatBalanceOffBalance) {
    5575            0 :         thisAirRpt.imBalance = thisAirRpt.SumIntGains + thisAirRpt.SumHADTsurfs + thisAirRpt.SumMCpDTzones + thisAirRpt.SumMCpDtInfil +
    5576            0 :                                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            0 :         Real64 Threshold = 0.2 * std::sqrt(pow_2(thisAirRpt.SumIntGains) + pow_2(thisAirRpt.SumHADTsurfs) + pow_2(thisAirRpt.SumMCpDTzones) +
    5581            0 :                                            pow_2(thisAirRpt.SumMCpDtInfil) + pow_2(thisAirRpt.SumMCpDTsystem) + pow_2(thisAirRpt.SumNonAirSystem) +
    5582            0 :                                            pow_2(thisAirRpt.CzdTdt));
    5583            0 :         if ((std::abs(thisAirRpt.imBalance) > Threshold) && (!state.dataGlobal->WarmupFlag) &&
    5584            0 :             (!state.dataGlobal->DoingSizing)) { // air balance is out by more than threshold
    5585            0 :             if (thisZone.AirHBimBalanceErrIndex == 0) {
    5586            0 :                 ShowWarningMessage(state, format("Zone Air Heat Balance is out of balance for zone named {}", thisZone.Name));
    5587            0 :                 ShowContinueError(state, format("Zone Air Heat Balance Deviation Rate is more than {:.1R} {{W}}", Threshold));
    5588            0 :                 if (state.dataHVACGlobal->TurnFansOn) {
    5589            0 :                     ShowContinueError(state, "Night cycle fan operation may be causing above error");
    5590              :                 }
    5591              : 
    5592            0 :                 ShowContinueErrorTimeStamp(state, " Occurrence info:");
    5593              :             }
    5594            0 :             ShowRecurringWarningErrorAtEnd(state,
    5595            0 :                                            format("Zone Air Heat Balance is out of balance ... zone named {}", thisZone.Name),
    5596            0 :                                            thisZone.AirHBimBalanceErrIndex,
    5597            0 :                                            std::abs(thisAirRpt.imBalance) - Threshold,
    5598            0 :                                            std::abs(thisAirRpt.imBalance) - Threshold,
    5599              :                                            _,
    5600              :                                            "{W}",
    5601              :                                            "{W}");
    5602              :         }
    5603              :     }
    5604       473637 : }
    5605              : 
    5606           72 : 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           72 :     if (state.dataZoneCtrls->GetZoneAirStatsInputFlag) {
    5617            1 :         GetZoneAirSetPoints(state);
    5618            1 :         state.dataZoneCtrls->GetZoneAirStatsInputFlag = false;
    5619              :     }
    5620           72 :     if (state.dataZoneCtrls->NumTempControlledZones > 0) {
    5621           70 :         if (Util::FindItemInList(ZoneName, state.dataZoneCtrls->TempControlledZone, &DataZoneControls::ZoneTempControls::ZoneName) > 0) {
    5622           70 :             return true;
    5623              :         } else {
    5624            0 :             return false;
    5625              :         }
    5626              :     }
    5627            2 :     return false;
    5628              : }
    5629              : 
    5630           73 : 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           73 :     return (Util::FindItemInList(ZoneName, state.dataZoneEquip->ZoneEquipConfig, &DataZoneEquipment::EquipConfiguration::ZoneName) > 0);
    5641              : }
    5642              : 
    5643       287262 : 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       287262 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5659              : 
    5660              :     // first time run allocate arrays and setup output variable
    5661       287262 :     if (s_ztpc->SetupOscillationOutputFlag) {
    5662          102 :         s_ztpc->ZoneTempHist.allocate(4, state.dataGlobal->NumOfZones);
    5663          102 :         s_ztpc->ZoneTempHist = 0.0;
    5664          102 :         s_ztpc->ZoneTempOscillate.dimension(state.dataGlobal->NumOfZones, 0.0);
    5665          102 :         s_ztpc->ZoneTempOscillateDuringOccupancy.dimension(state.dataGlobal->NumOfZones, 0.0);
    5666          102 :         s_ztpc->ZoneTempOscillateInDeadband.dimension(state.dataGlobal->NumOfZones, 0.0);
    5667              :         // set up zone by zone variables, CurrentModuleObject='Zone'
    5668          224 :         for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    5669          122 :             auto &zone = state.dataHeatBal->Zone(iZone);
    5670          244 :             SetupOutputVariable(state,
    5671              :                                 "Zone Oscillating Temperatures Time",
    5672              :                                 Constant::Units::hr,
    5673          122 :                                 s_ztpc->ZoneTempOscillate(iZone),
    5674              :                                 OutputProcessor::TimeStepType::System,
    5675              :                                 OutputProcessor::StoreType::Sum,
    5676          122 :                                 zone.Name);
    5677          244 :             SetupOutputVariable(state,
    5678              :                                 "Zone Oscillating Temperatures During Occupancy Time",
    5679              :                                 Constant::Units::hr,
    5680          122 :                                 s_ztpc->ZoneTempOscillateDuringOccupancy(iZone),
    5681              :                                 OutputProcessor::TimeStepType::System,
    5682              :                                 OutputProcessor::StoreType::Sum,
    5683          122 :                                 zone.Name);
    5684          244 :             SetupOutputVariable(state,
    5685              :                                 "Zone Oscillating Temperatures in Deadband Time",
    5686              :                                 Constant::Units::hr,
    5687          122 :                                 s_ztpc->ZoneTempOscillateInDeadband(iZone),
    5688              :                                 OutputProcessor::TimeStepType::System,
    5689              :                                 OutputProcessor::StoreType::Sum,
    5690          122 :                                 zone.Name);
    5691              :         }
    5692              :         // set up a variable covering all zones
    5693          408 :         SetupOutputVariable(state,
    5694              :                             "Facility Any Zone Oscillating Temperatures Time",
    5695              :                             Constant::Units::hr,
    5696          102 :                             s_ztpc->AnyZoneTempOscillate,
    5697              :                             OutputProcessor::TimeStepType::System,
    5698              :                             OutputProcessor::StoreType::Sum,
    5699              :                             "Facility");
    5700          408 :         SetupOutputVariable(state,
    5701              :                             "Facility Any Zone Oscillating Temperatures During Occupancy Time",
    5702              :                             Constant::Units::hr,
    5703          102 :                             s_ztpc->AnyZoneTempOscillateDuringOccupancy,
    5704              :                             OutputProcessor::TimeStepType::System,
    5705              :                             OutputProcessor::StoreType::Sum,
    5706              :                             "Facility");
    5707          408 :         SetupOutputVariable(state,
    5708              :                             "Facility Any Zone Oscillating Temperatures in Deadband Time",
    5709              :                             Constant::Units::hr,
    5710          102 :                             s_ztpc->AnyZoneTempOscillateInDeadband,
    5711              :                             OutputProcessor::TimeStepType::System,
    5712              :                             OutputProcessor::StoreType::Sum,
    5713              :                             "Facility");
    5714              :         // test if the oscillation variables are even used
    5715          306 :         if (ReportingThisVariable(state, "Zone Oscillating Temperatures Time") ||
    5716          306 :             ReportingThisVariable(state, "Zone Oscillating Temperatures During Occupancy Time") ||
    5717          306 :             ReportingThisVariable(state, "Zone Oscillating Temperatures in Deadband Time") ||
    5718          306 :             ReportingThisVariable(state, "Facility Any Zone Oscillating Temperatures Time") ||
    5719          612 :             ReportingThisVariable(state, "Facility Any Zone Oscillating Temperatures During Occupancy Time") ||
    5720          306 :             ReportingThisVariable(state, "Facility Any Zone Oscillating Temperatures in Deadband Time")) {
    5721            0 :             s_ztpc->OscillationVariablesNeeded = true;
    5722              :         }
    5723          102 :         s_ztpc->SetupOscillationOutputFlag = false;
    5724              :     }
    5725              : 
    5726       287262 :     Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    5727       287262 :     if (s_ztpc->OscillationVariablesNeeded) {
    5728              :         // precalc the negative value for performance
    5729            0 :         Real64 NegOscillateMagnitude = -HVAC::OscillateMagnitude;
    5730              :         // assume no zone is oscillating
    5731            0 :         bool isAnyZoneOscillating = false;
    5732            0 :         bool isAnyZoneOscillatingDuringOccupancy = false;
    5733            0 :         bool isAnyZoneOscillatingInDeadband = false;
    5734              : 
    5735            0 :         for (int iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
    5736            0 :             bool isOscillate = false;
    5737            0 :             s_ztpc->ZoneTempHist(4, iZone) = s_ztpc->ZoneTempHist(3, iZone);
    5738            0 :             s_ztpc->ZoneTempHist(3, iZone) = s_ztpc->ZoneTempHist(2, iZone);
    5739            0 :             s_ztpc->ZoneTempHist(2, iZone) = s_ztpc->ZoneTempHist(1, iZone);
    5740            0 :             s_ztpc->ZoneTempHist(1, iZone) = s_ztpc->zoneHeatBalance(iZone).ZT;
    5741            0 :             Real64 Diff34 = s_ztpc->ZoneTempHist(3, iZone) - s_ztpc->ZoneTempHist(4, iZone);
    5742            0 :             Real64 Diff23 = s_ztpc->ZoneTempHist(2, iZone) - s_ztpc->ZoneTempHist(3, iZone);
    5743            0 :             Real64 Diff12 = s_ztpc->ZoneTempHist(1, iZone) - s_ztpc->ZoneTempHist(2, iZone);
    5744              :             // roll out the conditionals for increased performance
    5745            0 :             if (Diff12 > HVAC::OscillateMagnitude) {
    5746            0 :                 if (Diff23 < NegOscillateMagnitude) {
    5747            0 :                     if (Diff34 > HVAC::OscillateMagnitude) {
    5748            0 :                         isOscillate = true;
    5749              :                     }
    5750              :                 }
    5751              :             }
    5752              :             // now try the opposite sequence of swings
    5753            0 :             if (Diff12 < NegOscillateMagnitude) {
    5754            0 :                 if (Diff23 > HVAC::OscillateMagnitude) {
    5755            0 :                     if (Diff34 < NegOscillateMagnitude) {
    5756            0 :                         isOscillate = true;
    5757              :                     }
    5758              :                 }
    5759              :             }
    5760            0 :             s_ztpc->ZoneTempOscillateDuringOccupancy(iZone) = 0.0;
    5761            0 :             s_ztpc->ZoneTempOscillateInDeadband(iZone) = 0.0;
    5762            0 :             if (isOscillate) {
    5763            0 :                 s_ztpc->ZoneTempOscillate(iZone) = TimeStepSys;
    5764            0 :                 isAnyZoneOscillating = true;
    5765            0 :                 if (allocated(state.dataThermalComforts->ThermalComfortInASH55)) {
    5766            0 :                     if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
    5767            0 :                         s_ztpc->ZoneTempOscillateDuringOccupancy(iZone) = TimeStepSys;
    5768            0 :                         isAnyZoneOscillatingDuringOccupancy = true;
    5769              :                     }
    5770              :                 }
    5771            0 :                 if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(iZone)) {
    5772            0 :                     s_ztpc->ZoneTempOscillateInDeadband(iZone) = TimeStepSys;
    5773            0 :                     isAnyZoneOscillatingInDeadband = true;
    5774              :                 }
    5775              :             } else {
    5776            0 :                 s_ztpc->ZoneTempOscillate(iZone) = 0.0;
    5777              :             }
    5778              :         }
    5779              :         // any zone variable
    5780            0 :         s_ztpc->AnyZoneTempOscillate = (isAnyZoneOscillating) ? TimeStepSys : 0.0;
    5781            0 :         s_ztpc->AnyZoneTempOscillateDuringOccupancy = (isAnyZoneOscillatingDuringOccupancy) ? TimeStepSys : 0.0;
    5782            0 :         s_ztpc->AnyZoneTempOscillateInDeadband = (isAnyZoneOscillatingInDeadband) ? TimeStepSys : 0.0;
    5783              : 
    5784              :         // annual/runperiod sum for _perflog.csv file
    5785            0 :         s_ztpc->AnnualAnyZoneTempOscillate += s_ztpc->AnyZoneTempOscillate;
    5786            0 :         s_ztpc->AnnualAnyZoneTempOscillateDuringOccupancy += s_ztpc->AnyZoneTempOscillateDuringOccupancy;
    5787            0 :         s_ztpc->AnnualAnyZoneTempOscillateInDeadband += s_ztpc->AnyZoneTempOscillateInDeadband;
    5788              :     }
    5789       287262 : }
    5790              : 
    5791       326292 : 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       326292 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5803              :     Real64 thisMRTFraction; // local variable for fraction that MRT is in Op Temp definition
    5804              : 
    5805       326292 :     if (!(state.dataZoneCtrls->AnyOpTempControl)) {
    5806       326292 :         return; // do nothing to setpoint
    5807              :     }
    5808              : 
    5809            0 :     auto &tempControlledZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneID);
    5810            0 :     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            0 :     thisMRTFraction = (tempControlledZone.OpTempCtrl == DataZoneControls::TempCtrl::Scheduled)
    5816            0 :                           ? tempControlledZone.opTempRadiativeFractionSched->getCurrentVal()
    5817              :                           : tempControlledZone.FixedRadiativeFraction;
    5818              : 
    5819              :     // get mean radiant temperature for zone
    5820            0 :     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            0 :     ZoneAirSetPoint = (ZoneAirSetPoint - thisMRTFraction * thisMRT) / (1.0 - thisMRTFraction);
    5825              : }
    5826              : 
    5827            4 : 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            4 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5837            4 :     auto const &tempControlledZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneID);
    5838            4 :     auto const &AdapComfortDailySetPointSchedule = s_ztpc->AdapComfortDailySetPointSchedule;
    5839              : 
    5840              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    5841            4 :     int originZoneAirSetPoint = ZoneAirSetPoint;
    5842            4 :     int AdaptiveComfortModelTypeIndex = tempControlledZone.AdaptiveComfortModelTypeIndex;
    5843              : 
    5844              :     // adjust zone operative setpoint
    5845            4 :     if (!(tempControlledZone.AdaptiveComfortTempControl)) {
    5846            0 :         return; // do nothing to setpoint
    5847              :     }
    5848            8 :     if ((state.dataWeather->Environment(state.dataWeather->Envrn).KindOfEnvrn != Constant::KindOfSim::DesignDay) &&
    5849            4 :         (state.dataWeather->Environment(state.dataWeather->Envrn).KindOfEnvrn != Constant::KindOfSim::HVACSizeDesignDay)) {
    5850              :         // Adjust run period cooling set point
    5851            4 :         switch (AdaptiveComfortModelTypeIndex) {
    5852            3 :         case static_cast<int>(AdaptiveComfortModel::ASH55_CENTRAL):
    5853            3 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveASH55_Central(state.dataEnvrn->DayOfYear);
    5854            3 :             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            1 :         case static_cast<int>(AdaptiveComfortModel::CEN15251_CENTRAL):
    5862            1 :             ZoneAirSetPoint = AdapComfortDailySetPointSchedule.ThermalComfortAdaptiveCEN15251_Central(state.dataEnvrn->DayOfYear);
    5863            1 :             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            0 :         int const envrnDayNum(state.dataWeather->Environment(state.dataWeather->Envrn).DesignDayNum);
    5878            0 :         int constexpr summerDesignDayTypeIndex(9);
    5879              :         // Adjust summer design day set point
    5880            0 :         if (state.dataWeather->DesDayInput(envrnDayNum).DayType == summerDesignDayTypeIndex) {
    5881            0 :             ZoneAirSetPoint = s_ztpc->AdapComfortSetPointSummerDesDay[AdaptiveComfortModelTypeIndex - 2];
    5882              :         }
    5883              :     }
    5884              :     // If adaptive operative temperature not applicable, set back
    5885            4 :     if (ZoneAirSetPoint < originZoneAirSetPoint) {
    5886            2 :         ZoneAirSetPoint = originZoneAirSetPoint;
    5887              :     }
    5888              :     // If meet fault flag, set back
    5889            4 :     if (ZoneAirSetPoint == -1) {
    5890            0 :         ZoneAirSetPoint = originZoneAirSetPoint;
    5891              :     }
    5892              : }
    5893              : 
    5894            0 : 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            0 :     Real64 SetPointLo = 0.0;
    5906            0 :     Real64 SetPointHi = 0.0;
    5907            0 :     Real64 Tset = 0.0;
    5908            0 :     int ObjectCount = 0;
    5909            0 :     Real64 PeopleCount = 0.0;
    5910              : 
    5911            0 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    5912            0 :     auto &s_hbfs = state.dataHeatBalFanSys;
    5913              :     // Call thermal comfort module to read zone control comfort object
    5914            0 :     if (s_ztpc->CalcZoneAirComfortSetPointsFirstTimeFlag) {
    5915            0 :         ThermalComfort::ManageThermalComfort(state, true);
    5916            0 :         s_ztpc->CalcZoneAirComfortSetPointsFirstTimeFlag = false;
    5917              :     }
    5918              : 
    5919            0 :     s_hbfs->ComfortControlType = HVAC::SetptType::Uncontrolled; // Default
    5920              : 
    5921            0 :     for (int RelativeZoneNum = 1; RelativeZoneNum <= state.dataZoneCtrls->NumComfortControlledZones; ++RelativeZoneNum) {
    5922              : 
    5923            0 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(RelativeZoneNum);
    5924            0 :         int ActualZoneNum = comfortZone.ActualZoneNum;
    5925            0 :         auto &zone = state.dataHeatBal->Zone(ActualZoneNum);
    5926            0 :         auto &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ActualZoneNum);
    5927            0 :         auto &zoneComfortControlsFanger = state.dataHeatBalFanSys->ZoneComfortControlsFanger(ActualZoneNum);
    5928            0 :         s_hbfs->ComfortControlType(ActualZoneNum) = static_cast<HVAC::SetptType>(comfortZone.setptTypeSched->getCurrentVal());
    5929            0 :         s_hbfs->ComfortControlTypeRpt(ActualZoneNum) = (int)s_hbfs->ComfortControlType(ActualZoneNum);
    5930              : 
    5931              :         // Get PMV values
    5932            0 :         switch (s_hbfs->ComfortControlType(ActualZoneNum)) {
    5933            0 :         case HVAC::SetptType::Uncontrolled: {
    5934            0 :             zoneComfortControlsFanger.LowPMV = -999.0;
    5935            0 :             zoneComfortControlsFanger.HighPMV = -999.0;
    5936            0 :         } break;
    5937              : 
    5938            0 :         case HVAC::SetptType::SingleHeat: {
    5939            0 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::SingleHeat;
    5940            0 :             zoneComfortControlsFanger.LowPMV = comfortZone.setpts[(int)HVAC::SetptType::SingleHeat].heatSetptSched->getCurrentVal();
    5941            0 :             zoneComfortControlsFanger.HighPMV = -999.0;
    5942            0 :         } break;
    5943              : 
    5944            0 :         case HVAC::SetptType::SingleCool: {
    5945            0 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::SingleCool;
    5946            0 :             zoneComfortControlsFanger.LowPMV = -999.0;
    5947            0 :             zoneComfortControlsFanger.HighPMV = comfortZone.setpts[(int)HVAC::SetptType::SingleCool].coolSetptSched->getCurrentVal();
    5948            0 :         } break;
    5949              : 
    5950            0 :         case HVAC::SetptType::SingleHeatCool: {
    5951            0 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::SingleHeatCool;
    5952            0 :             zoneComfortControlsFanger.LowPMV = zoneComfortControlsFanger.HighPMV =
    5953            0 :                 comfortZone.setpts[(int)HVAC::SetptType::SingleHeatCool].coolSetptSched->getCurrentVal();
    5954            0 :         } break;
    5955              : 
    5956            0 :         case HVAC::SetptType::DualHeatCool: {
    5957            0 :             zoneComfortControlsFanger.FangerType = (int)HVAC::SetptType::DualHeatCool;
    5958            0 :             zoneComfortControlsFanger.LowPMV = comfortZone.setpts[(int)HVAC::SetptType::DualHeatCool].heatSetptSched->getCurrentVal();
    5959            0 :             zoneComfortControlsFanger.HighPMV = comfortZone.setpts[(int)HVAC::SetptType::DualHeatCool].coolSetptSched->getCurrentVal();
    5960            0 :             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            0 :         } 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            0 :         switch (comfortZone.averageMethod) {
    5991            0 :         case DataZoneControls::AverageMethod::NO: {
    5992            0 :             int PeopleNum = comfortZone.SpecificObjectNum;
    5993            0 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::SingleCool) {
    5994            0 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, SetPointLo);
    5995              :             } else {
    5996            0 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.LowPMV, SetPointLo);
    5997              :             }
    5998            0 :             if (s_hbfs->ComfortControlType(ActualZoneNum) == HVAC::SetptType::DualHeatCool) {
    5999            0 :                 GetComfortSetPoints(state, PeopleNum, RelativeZoneNum, zoneComfortControlsFanger.HighPMV, SetPointHi);
    6000              :             }
    6001            0 :         } 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            0 :         switch (s_hbfs->ComfortControlType(ActualZoneNum)) {
    6099              : 
    6100            0 :         case HVAC::SetptType::Uncontrolled: {
    6101            0 :             switch (state.dataHeatBalFanSys->TempControlType(ActualZoneNum)) {
    6102            0 :             case HVAC::SetptType::SingleHeat:
    6103            0 :                 zoneTstatSetpt.setptHi = 0.0;
    6104            0 :                 break;
    6105            0 :             case HVAC::SetptType::SingleCool:
    6106            0 :                 zoneTstatSetpt.setptLo = 0.0;
    6107            0 :                 break;
    6108            0 :             default:
    6109            0 :                 break;
    6110              :             }
    6111            0 :         } break;
    6112              : 
    6113            0 :         case HVAC::SetptType::SingleHeat: {
    6114            0 :             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            0 :             zoneTstatSetpt.setpt = SetPointLo;
    6132            0 :             zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    6133            0 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::SingleHeat;
    6134            0 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6135            0 :         } break;
    6136              : 
    6137            0 :         case HVAC::SetptType::SingleCool: {
    6138            0 :             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            0 :             zoneTstatSetpt.setpt = SetPointLo;
    6156            0 :             zoneTstatSetpt.setptHi = zoneTstatSetpt.setpt;
    6157            0 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::SingleCool;
    6158            0 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6159            0 :         } break;
    6160              : 
    6161            0 :         case HVAC::SetptType::SingleHeatCool: {
    6162            0 :             if (comfortZone.TdbMaxSetPoint == comfortZone.TdbMinSetPoint) {
    6163            0 :                 SetPointLo = comfortZone.TdbMaxSetPoint;
    6164              :             }
    6165            0 :             if (SetPointLo > comfortZone.TdbMaxSetPoint) {
    6166            0 :                 SetPointLo = comfortZone.TdbMaxSetPoint;
    6167              :             }
    6168            0 :             if (SetPointLo < comfortZone.TdbMinSetPoint) {
    6169            0 :                 SetPointLo = comfortZone.TdbMinSetPoint;
    6170              :             }
    6171            0 :             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            0 :             zoneTstatSetpt.setpt = SetPointLo;
    6191            0 :             zoneTstatSetpt.setptHi = zoneTstatSetpt.setptLo = zoneTstatSetpt.setpt;
    6192            0 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::SingleHeatCool;
    6193            0 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6194            0 :         } break;
    6195              : 
    6196            0 :         case HVAC::SetptType::DualHeatCool: {
    6197            0 :             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            0 :             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            0 :             zoneTstatSetpt.setptLo = SetPointLo;
    6234            0 :             zoneTstatSetpt.setptHi = SetPointHi;
    6235            0 :             state.dataHeatBalFanSys->TempControlType(ActualZoneNum) = HVAC::SetptType::DualHeatCool;
    6236            0 :             state.dataHeatBalFanSys->TempControlTypeRpt(ActualZoneNum) = static_cast<int>(state.dataHeatBalFanSys->TempControlType(ActualZoneNum));
    6237            0 :         } 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            0 : } // CalcZoneAirComfortSetpoints()
    6249              : 
    6250            0 : 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            0 :     Real64 constexpr Acc(0.001); // accuracy control for SolveRoot
    6270            0 :     int constexpr MaxIter(500);  // iteration control for SolveRoot
    6271              : 
    6272              :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    6273            0 :     Real64 PMVResult = 0.0; // Calculated PMV value
    6274              : 
    6275            0 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    6276              : 
    6277            0 :     auto &comfortControlledZone = state.dataZoneCtrls->ComfortControlledZone(ComfortControlNum);
    6278            0 :     Real64 Tmin = comfortControlledZone.TdbMinSetPoint;
    6279            0 :     Real64 Tmax = comfortControlledZone.TdbMaxSetPoint;
    6280              : 
    6281            0 :     ThermalComfort::CalcThermalComfortFanger(state, PeopleNum, Tmin, PMVResult);
    6282            0 :     Real64 PMVMin = PMVResult;
    6283            0 :     ThermalComfort::CalcThermalComfortFanger(state, PeopleNum, Tmax, PMVResult);
    6284            0 :     Real64 PMVMax = PMVResult;
    6285            0 :     if (PMVSet > PMVMin && PMVSet < PMVMax) {
    6286              : 
    6287            0 :         auto f = [&state, PMVSet, PeopleNum](Real64 Tset) {
    6288            0 :             Real64 PMVresult = 0.0; // resulting PMV values
    6289            0 :             ThermalComfort::CalcThermalComfortFanger(state, PeopleNum, Tset, PMVresult);
    6290            0 :             return (PMVSet - PMVresult);
    6291            0 :         };
    6292              : 
    6293            0 :         int SolFla = 0; // feed back flag from SolveRoot
    6294            0 :         General::SolveRoot(state, Acc, MaxIter, SolFla, Tset, f, Tmin, Tmax);
    6295            0 :         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            0 :         } 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            0 :     } else if (PMVSet < PMVMin) {
    6330            0 :         Tset = Tmin;
    6331            0 :     } else if (PMVSet > PMVMax) {
    6332            0 :         Tset = Tmax;
    6333              :     }
    6334            0 : }
    6335              : 
    6336       163146 : 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       163146 :     auto const &s_ztpc = state.dataZoneTempPredictorCorrector;
    6350       163146 :     auto &tempZone = state.dataZoneCtrls->TempControlledZone(TempControlledZoneID);
    6351       163146 :     auto &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(ActualZoneNum);
    6352              : 
    6353       163146 :     if (!state.dataZoneCtrls->AnyZoneTempAndHumidityControl) {
    6354       163146 :         return; // do nothing to setpoint
    6355              :     }
    6356            0 :     if (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::None) {
    6357            0 :         return; // do nothing to setpoint
    6358              :     }
    6359              : 
    6360            0 :     Real64 ZoneOvercoolRange = (tempZone.OvercoolCtrl == DataZoneControls::TempCtrl::Scheduled) ? tempZone.zoneOvercoolRangeSched->getCurrentVal()
    6361            0 :                                                                                                 : tempZone.ZoneOvercoolConstRange;
    6362              : 
    6363            0 :     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            0 :     Real64 MaxAllowedOvercoolRange = zoneTstatSetpt.setptHi - zoneTstatSetpt.setptLo;
    6367            0 :     if (MaxAllowedOvercoolRange > 0.0) {
    6368            0 :         ZoneOvercoolRange = min(ZoneOvercoolRange, MaxAllowedOvercoolRange);
    6369              :     }
    6370              :     // Calculate difference between zone air relative humidity and the dehumidifying setpoint
    6371            0 :     Real64 RelativeHumidityDiff = s_ztpc->zoneHeatBalance(ActualZoneNum).airRelHum - tempZone.dehumidifyingSched->getCurrentVal();
    6372            0 :     if (RelativeHumidityDiff > 0.0 && ZoneOvercoolControlRatio > 0.0) {
    6373              :         // proportionally reset the cooling setpoint temperature downward (zone Overcool)
    6374            0 :         ZoneOvercoolRange = min(ZoneOvercoolRange, RelativeHumidityDiff / ZoneOvercoolControlRatio);
    6375            0 :         zoneTstatSetpt.setptHi -= ZoneOvercoolRange;
    6376              :     }
    6377              : }
    6378              : 
    6379       248616 : 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       248616 :     auto const &s_hbfs = state.dataHeatBalFanSys;
    6389              : 
    6390       425260 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumTempControlledZones; ++Loop) {
    6391       176644 :         auto const &tempZone = state.dataZoneCtrls->TempControlledZone(Loop);
    6392       176644 :         if (tempZone.EMSOverrideHeatingSetPointOn) {
    6393            1 :             int ZoneNum = tempZone.ActualZoneNum;
    6394            1 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6395              : 
    6396            1 :             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            1 :             case HVAC::SetptType::DualHeatCool: {
    6403            1 :                 zoneTstatSetpt.setptLo = tempZone.EMSOverrideHeatingSetPointValue;
    6404            1 :             } break;
    6405              : 
    6406            0 :             default: {
    6407            0 :             } break;
    6408              :             } // switch ()
    6409              :         }
    6410              : 
    6411       176644 :         if (tempZone.EMSOverrideCoolingSetPointOn) {
    6412            1 :             int ZoneNum = tempZone.ActualZoneNum;
    6413            1 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6414              : 
    6415            1 :             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            1 :             case HVAC::SetptType::DualHeatCool: {
    6422            1 :                 zoneTstatSetpt.setptHi = tempZone.EMSOverrideCoolingSetPointValue;
    6423            1 :             } break;
    6424              : 
    6425            0 :             default: {
    6426            0 :             } break;
    6427              :             } // switch ()
    6428              :         }
    6429              :     }
    6430              : 
    6431       248617 :     for (int Loop = 1; Loop <= state.dataZoneCtrls->NumComfortControlledZones; ++Loop) {
    6432            1 :         auto &comfortZone = state.dataZoneCtrls->ComfortControlledZone(Loop);
    6433            1 :         if (comfortZone.EMSOverrideHeatingSetPointOn) {
    6434            1 :             int ZoneNum = comfortZone.ActualZoneNum;
    6435            1 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6436              : 
    6437            1 :             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            1 :             case HVAC::SetptType::DualHeatCool: {
    6444            1 :                 zoneTstatSetpt.setptLo = comfortZone.EMSOverrideHeatingSetPointValue;
    6445            1 :             } break;
    6446              : 
    6447            0 :             default: {
    6448            0 :             } break;
    6449              : 
    6450              :             } // switch ()
    6451              :         }
    6452              : 
    6453            1 :         if (comfortZone.EMSOverrideCoolingSetPointOn) {
    6454              : 
    6455            1 :             int ZoneNum = comfortZone.ActualZoneNum;
    6456            1 :             auto &zoneTstatSetpt = s_hbfs->zoneTstatSetpts(ZoneNum);
    6457              : 
    6458            1 :             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            1 :             case HVAC::SetptType::DualHeatCool: {
    6465            1 :                 zoneTstatSetpt.setptHi = comfortZone.EMSOverrideCoolingSetPointValue;
    6466            1 :             } break;
    6467              : 
    6468            0 :             default: {
    6469            0 :             } break;
    6470              : 
    6471              :             } // switch ()
    6472              :         }
    6473              :     }
    6474       248616 : }
    6475              : 
    6476              : // add values to the LEED tabular report related to schedules used by the thermostat objects
    6477           73 : void FillPredefinedTableOnThermostatSetpoints(EnergyPlusData &state)
    6478              : {
    6479              :     // J.Glazer - Aug 2017
    6480              :     using namespace OutputReportPredefined;
    6481           73 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    6482              : 
    6483           73 :     std::vector<int> uniqSch;
    6484           73 :     uniqSch.reserve(s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleHeat] + s_ztpc->NumTempControls[(int)HVAC::SetptType::SingleCool] +
    6485           73 :                     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           73 :     std::string monthAssumed;
    6491           73 :     std::string monthAssumed2;
    6492              : 
    6493           79 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeat]) {
    6494            6 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.heatSched->Num) != uniqSch.end()) {
    6495            0 :             continue;
    6496              :         }
    6497              : 
    6498            6 :         uniqSch.emplace_back(setpt.heatSched->Num);
    6499            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.heatSched->Name, setpt.Name);
    6500              : 
    6501            6 :         std::tie(setPointAt11, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 11);
    6502            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.heatSched->Name, setPointAt11);
    6503            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.heatSched->Name, numDays);
    6504              : 
    6505            6 :         std::tie(setPointAt23, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 23);
    6506            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.heatSched->Name, setPointAt23);
    6507            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.heatSched->Name, numDays);
    6508              : 
    6509            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.heatSched->Name, monthAssumed);
    6510              :     }
    6511              : 
    6512           79 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleCool]) {
    6513            6 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.coolSched->Num) != uniqSch.end()) {
    6514            0 :             continue;
    6515              :         }
    6516              : 
    6517            6 :         uniqSch.emplace_back(setpt.coolSched->Num);
    6518            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.coolSched->Name, setpt.Name);
    6519              : 
    6520            6 :         std::tie(setPointAt11, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 11);
    6521            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.coolSched->Name, setPointAt11);
    6522            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.coolSched->Name, numDays);
    6523              : 
    6524            6 :         std::tie(setPointAt23, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 23);
    6525            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.coolSched->Name, setPointAt23);
    6526            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.coolSched->Name, numDays);
    6527              : 
    6528            6 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.coolSched->Name, monthAssumed);
    6529              :     }
    6530              : 
    6531           73 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::SingleHeatCool]) {
    6532            0 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.heatSched->Num) != uniqSch.end()) {
    6533            0 :             continue;
    6534              :         }
    6535              : 
    6536            0 :         uniqSch.emplace_back(setpt.heatSched->Num);
    6537            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.heatSched->Name, setpt.Name);
    6538              : 
    6539            0 :         std::string schNm = setpt.heatSched->Name + " (summer)";
    6540            0 :         std::tie(setPointAt11, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 11);
    6541            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, schNm, setPointAt11);
    6542            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, schNm, numDays);
    6543              : 
    6544            0 :         std::tie(setPointAt23, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 23);
    6545            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, schNm, setPointAt23);
    6546            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, schNm, numDays);
    6547              : 
    6548            0 :         schNm = setpt.heatSched->Name + " (winter)";
    6549            0 :         std::tie(setPointAt11, numDays, monthAssumed2) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 11);
    6550            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, schNm, setPointAt11);
    6551            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, schNm, numDays);
    6552              : 
    6553            0 :         std::tie(setPointAt23, numDays, monthAssumed2) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 23);
    6554            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, schNm, setPointAt23);
    6555            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, schNm, numDays);
    6556              : 
    6557            0 :         PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.heatSched->Name, monthAssumed + " and " + monthAssumed2);
    6558            0 :     }
    6559              : 
    6560          106 :     for (auto &setpt : s_ztpc->tempSetptScheds[(int)HVAC::SetptType::DualHeatCool]) {
    6561           33 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.heatSched->Num) == uniqSch.end()) {
    6562           30 :             uniqSch.emplace_back(setpt.heatSched->Num);
    6563           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.heatSched->Name, setpt.Name);
    6564              : 
    6565           30 :             std::tie(setPointAt11, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 11);
    6566           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.heatSched->Name, setPointAt11);
    6567           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.heatSched->Name, numDays);
    6568              : 
    6569           30 :             std::tie(setPointAt23, numDays, monthAssumed) = setpt.heatSched->getValAndCountOnDay(state, false, Sched::DayType::Wednesday, 23);
    6570           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.heatSched->Name, setPointAt23);
    6571           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.heatSched->Name, numDays);
    6572              : 
    6573           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.heatSched->Name, monthAssumed);
    6574              :         }
    6575              : 
    6576           33 :         if (std::find(uniqSch.begin(), uniqSch.end(), setpt.coolSched->Num) == uniqSch.end()) {
    6577           30 :             uniqSch.emplace_back(setpt.coolSched->Num);
    6578           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtFirstObjUsed, setpt.coolSched->Name, setpt.Name);
    6579              : 
    6580           30 :             std::tie(setPointAt11, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 11);
    6581           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWednesday, setpt.coolSched->Name, setPointAt11);
    6582           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11amWedCnt, setpt.coolSched->Name, numDays);
    6583              : 
    6584           30 :             std::tie(setPointAt23, numDays, monthAssumed) = setpt.coolSched->getValAndCountOnDay(state, true, Sched::DayType::Wednesday, 23);
    6585           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWednesday, setpt.coolSched->Name, setPointAt23);
    6586           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdchLeedSchStPt11pmWedCnt, setpt.coolSched->Name, numDays);
    6587              : 
    6588           30 :             PreDefTableEntry(state, state.dataOutRptPredefined->pdChLeedSchStPtMonthUsed, setpt.coolSched->Name, monthAssumed);
    6589              :         }
    6590              :     }
    6591           73 : }
    6592              : 
    6593           74 : 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           74 :     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          631 :         bool operator<(const ControlTypeInfo &other) const
    6613              :         {
    6614          631 :             return std::tie(this->thermostatType, this->controlTypeName, this->heatSchName, this->coolSchName) <
    6615         1262 :                    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          224 :     auto joinStrings = [](const std::vector<ControlTypeInfo> &infos, ControlTypeInfoMemPtr memPtr) -> std::string {
    6622          224 :         std::vector<std::string> result;
    6623          224 :         result.reserve(infos.size());
    6624         1588 :         for (const auto &info : infos) {
    6625         1364 :             std::string val = info.*memPtr;
    6626         1364 :             if (val.empty()) {
    6627         1132 :                 continue;
    6628              :             }
    6629          232 :             result.emplace_back(std::move(val));
    6630         1588 :         }
    6631          672 :         return fmt::format("{}", fmt::join(result, ", "));
    6632          224 :     };
    6633              : 
    6634          130 :     for (int idx = 1; idx <= state.dataZoneCtrls->NumTempControlledZones; ++idx) {
    6635           56 :         auto &tcz = state.dataZoneCtrls->TempControlledZone(idx);
    6636           56 :         PreDefTableEntry(state, orp->pdchStatName, tcz.ZoneName, tcz.Name);
    6637           56 :         PreDefTableEntry(state, orp->pdchStatCtrlTypeSchd, tcz.ZoneName, tcz.setptTypeSched->Name);
    6638              : 
    6639           56 :         std::vector<ControlTypeInfo> infos;
    6640           56 :         infos.resize((int)HVAC::SetptType::Num);
    6641          280 :         for (HVAC::SetptType setptType : HVAC::controlledSetptTypes) {
    6642          224 :             auto &setpt = tcz.setpts[(int)setptType];
    6643              : 
    6644          224 :             auto &info = infos[(int)setptType];
    6645              : 
    6646          224 :             if (setpt.Name.empty()) {
    6647          163 :                 continue;
    6648              :             }
    6649              : 
    6650           61 :             info.thermostatType = HVAC::setptTypeNames[(int)setptType];
    6651           61 :             info.controlTypeName = setpt.Name;
    6652           61 :             switch (setptType) {
    6653           49 :             case HVAC::SetptType::DualHeatCool:
    6654              :             case HVAC::SetptType::SingleHeatCool: {
    6655           49 :                 info.coolSchName = setpt.coolSetptSched->Name;
    6656           49 :                 info.heatSchName = setpt.heatSetptSched->Name;
    6657           49 :             } break;
    6658              : 
    6659            6 :             case HVAC::SetptType::SingleCool: {
    6660            6 :                 info.coolSchName = setpt.coolSetptSched->Name;
    6661            6 :             } break;
    6662              : 
    6663            6 :             case HVAC::SetptType::SingleHeat: {
    6664            6 :                 info.heatSchName = setpt.heatSetptSched->Name;
    6665            6 :             } break;
    6666              :             }
    6667           61 :             infos.emplace_back(std::move(info));
    6668              :         }
    6669           56 :         std::sort(infos.begin(), infos.end());
    6670              : 
    6671           56 :         PreDefTableEntry(state, orp->pdchStatSchdType1, tcz.ZoneName, joinStrings(infos, &ControlTypeInfo::thermostatType));
    6672           56 :         PreDefTableEntry(state, orp->pdchStatSchdTypeName1, tcz.ZoneName, joinStrings(infos, &ControlTypeInfo::controlTypeName));
    6673           56 :         if (auto heatSchNames = joinStrings(infos, &ControlTypeInfo::heatSchName); !heatSchNames.empty()) {
    6674           55 :             PreDefTableEntry(state, orp->pdchStatSchdHeatName, tcz.ZoneName, heatSchNames);
    6675           56 :         }
    6676           56 :         if (auto coolSchNames = joinStrings(infos, &ControlTypeInfo::coolSchName); !coolSchNames.empty()) {
    6677           55 :             PreDefTableEntry(state, orp->pdchStatSchdCoolName, tcz.ZoneName, coolSchNames);
    6678           56 :         }
    6679           56 :     }
    6680           74 : }
    6681              : 
    6682       462815 : 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       462815 :     assert(zoneNum > 0);
    6690       462815 :     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        18400 :         if (spaceNum == 0) {
    6694        18031 :             if (state.dataHeatBal->Zone(zoneNum).SystemZoneNodeNumber > 0) { // roll back result for zone air node,
    6695         8193 :                 auto &zoneNode = state.dataLoopNodes->Node(state.dataHeatBal->Zone(zoneNum).SystemZoneNodeNumber);
    6696         8193 :                 zoneNode.Temp = this->XMAT[0];
    6697         8193 :                 state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->XMAT[0];
    6698         8193 :                 zoneNode.HumRat = this->WPrevZoneTS[0];
    6699         8193 :                 zoneNode.Enthalpy = Psychrometrics::PsyHFnTdbW(this->XMAT[0], this->WPrevZoneTS[0]);
    6700              :             }
    6701              :         } else {
    6702          369 :             if (state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber > 0) { // roll back result for space air node,
    6703          369 :                 auto &spaceNode = state.dataLoopNodes->Node(state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber);
    6704          369 :                 spaceNode.Temp = this->XMAT[0];
    6705          369 :                 state.dataHeatBalFanSys->TempTstatAir(zoneNum) = this->XMAT[0];
    6706          369 :                 spaceNode.HumRat = this->WPrevZoneTS[0];
    6707          369 :                 spaceNode.Enthalpy = Psychrometrics::PsyHFnTdbW(this->XMAT[0], this->WPrevZoneTS[0]);
    6708              :             }
    6709              :         }
    6710              : 
    6711        18400 :         if (state.dataHVACGlobal->NumOfSysTimeSteps !=
    6712        18400 :             state.dataHVACGlobal->NumOfSysTimeStepsLastZoneTimeStep) { // cannot reuse existing DS data, interpolate from zone time
    6713         5162 :             Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
    6714         5162 :             this->MAT = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, this->XMAT, this->DSXMAT);
    6715         5162 :             this->airHumRat = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, this->WPrevZoneTS, this->DSWPrevZoneTS);
    6716              : 
    6717         5162 :             if (spaceNum == 0 && state.dataRoomAir->anyNonMixingRoomAirModel) {
    6718            0 :                 if (state.dataRoomAir->IsZoneDispVent3Node(zoneNum) || state.dataRoomAir->IsZoneUFAD(zoneNum)) {
    6719              : 
    6720            0 :                     state.dataRoomAir->MATFloor(zoneNum) = DownInterpolate4HistoryValues(
    6721            0 :                         PriorTimeStep, TimeStepSys, state.dataRoomAir->XMATFloor(zoneNum), state.dataRoomAir->DSXMATFloor(zoneNum));
    6722            0 :                     state.dataRoomAir->MATOC(zoneNum) = DownInterpolate4HistoryValues(
    6723            0 :                         PriorTimeStep, TimeStepSys, state.dataRoomAir->XMATOC(zoneNum), state.dataRoomAir->DSXMATOC(zoneNum));
    6724            0 :                     state.dataRoomAir->MATMX(zoneNum) = DownInterpolate4HistoryValues(
    6725            0 :                         PriorTimeStep, TimeStepSys, state.dataRoomAir->XMATMX(zoneNum), state.dataRoomAir->DSXMATMX(zoneNum));
    6726              :                 }
    6727            0 :                 if (state.dataRoomAir->AirModel(zoneNum).AirModel == RoomAir::RoomAirModel::AirflowNetwork) {
    6728            0 :                     for (auto &afnNode : state.dataRoomAir->AFNZoneInfo(zoneNum).Node) {
    6729            0 :                         afnNode.AirTemp = DownInterpolate4HistoryValues(PriorTimeStep, TimeStepSys, afnNode.AirTempX, afnNode.AirTempDSX);
    6730              : 
    6731            0 :                         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       462815 :     if (UseZoneTimeStepHistory) {
    6741       377749 :         this->ZTM = this->XMAT;
    6742       377749 :         this->WPrevZoneTSTemp = this->WPrevZoneTS;
    6743              :     } else { // use down-stepped history
    6744        85066 :         this->ZTM = this->DSXMAT;
    6745        85066 :         this->WPrevZoneTSTemp = this->DSWPrevZoneTS;
    6746              :     }
    6747       462815 : }
    6748              : 
    6749       462822 : 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       462822 :     assert(zoneNum > 0);
    6754       462822 :     auto const &thisZone = state.dataHeatBal->Zone(zoneNum);
    6755       462822 :     auto const &zoneTstatSetpt = state.dataHeatBalFanSys->zoneTstatSetpts(zoneNum);
    6756              : 
    6757       462822 :     bool thisDeadBandOrSetBack = false;
    6758       462822 :     Real64 ZoneSetPoint = 0.0;
    6759       462822 :     Real64 totalLoad = 0.0;
    6760       462822 :     Real64 LoadToHeatingSetPoint = 0.0;
    6761       462822 :     Real64 LoadToCoolingSetPoint = 0.0;
    6762              : 
    6763       462822 :     auto &s_ztpc = state.dataZoneTempPredictorCorrector;
    6764              : 
    6765       462822 :     int zoneNodeNum = thisZone.SystemZoneNodeNumber;
    6766       462822 :     if (spaceNum > 0) {
    6767        44487 :         zoneNodeNum = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    6768              :     }
    6769              : 
    6770       462822 :     switch (state.dataHeatBalFanSys->TempControlType(zoneNum)) {
    6771       218760 :     case HVAC::SetptType::Uncontrolled: {
    6772              :         // Uncontrolled Zone
    6773       218760 :         LoadToHeatingSetPoint = 0.0;
    6774       218760 :         LoadToCoolingSetPoint = 0.0;
    6775       218760 :         totalLoad = 0.0;
    6776       218760 :     } break;
    6777              : 
    6778        13683 :     case HVAC::SetptType::SingleHeat: {
    6779        13683 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6780        13683 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6781        13683 :             LoadToHeatingSetPoint = (this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad);
    6782        13683 :             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        13683 :         if (RAFNFrac > 0.0) {
    6804            2 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6805              :         }
    6806        13683 :         totalLoad = LoadToHeatingSetPoint;
    6807        13683 :         ZoneSetPoint = zoneTstatSetpt.setpt;
    6808        13683 :         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        13683 :         if ((totalLoad) <= 0.0) {
    6812          327 :             thisDeadBandOrSetBack = true;
    6813              :         }
    6814        13683 :     } break;
    6815              : 
    6816        13879 :     case HVAC::SetptType::SingleCool: {
    6817        13879 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6818        13879 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6819        13879 :             LoadToCoolingSetPoint = this->tempDepLoad * zoneTstatSetpt.setpt - this->tempIndLoad;
    6820        13879 :         } 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        13879 :         if (RAFNFrac > 0.0) {
    6841            1 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6842              :         }
    6843        13879 :         if (thisZone.HasAdjustedReturnTempByITE && !(state.dataGlobal->BeginSimFlag)) {
    6844            0 :             LoadToCoolingSetPoint = this->tempDepLoad * thisZone.AdjustedReturnTempByITE - this->tempIndLoad;
    6845              :         }
    6846        13879 :         totalLoad = LoadToCoolingSetPoint;
    6847        13879 :         ZoneSetPoint = zoneTstatSetpt.setpt;
    6848        13879 :         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        13879 :         if ((totalLoad) >= 0.0) {
    6852          824 :             thisDeadBandOrSetBack = true;
    6853              :         }
    6854        13879 :     } break;
    6855              : 
    6856            1 :     case HVAC::SetptType::SingleHeatCool: {
    6857            1 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6858            1 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6859            1 :             LoadToHeatingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setpt) - this->tempIndLoad);
    6860            1 :             LoadToCoolingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setpt) - this->tempIndLoad);
    6861            1 :         } 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            1 :         ZoneSetPoint = zoneTstatSetpt.setpt;
    6886            1 :         if (RAFNFrac > 0.0) {
    6887            1 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6888              :         }
    6889            1 :         if (RAFNFrac > 0.0) {
    6890            1 :             LoadToCoolingSetPoint = LoadToCoolingSetPoint / RAFNFrac;
    6891              :         }
    6892              : 
    6893            1 :         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            1 :         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            1 :         if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
    6922            0 :             totalLoad = LoadToHeatingSetPoint;
    6923            1 :         } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
    6924            1 :             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            1 :     } break;
    6945              : 
    6946       216499 :     case HVAC::SetptType::DualHeatCool: {
    6947       216499 :         switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    6948       191641 :         case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    6949       191641 :             LoadToHeatingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setptLo) - this->tempIndLoad);
    6950       191641 :             LoadToCoolingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setptHi) - this->tempIndLoad);
    6951       191641 :         } break;
    6952              : 
    6953        24858 :         case DataHeatBalance::SolutionAlgo::AnalyticalSolution: {
    6954        24858 :             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        24858 :                 Real64 const exp_700_TA(std::exp(min(700.0, -this->tempDepLoad / this->AirPowerCap)));
    6959        24858 :                 LoadToHeatingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setptLo - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6960        24858 :                 LoadToCoolingSetPoint = this->tempDepLoad * (zoneTstatSetpt.setptHi - this->T1 * exp_700_TA) / (1.0 - exp_700_TA) - this->tempIndLoad;
    6961              :             }
    6962        24858 :         } break;
    6963              : 
    6964            0 :         case DataHeatBalance::SolutionAlgo::EulerMethod: {
    6965            0 :             LoadToHeatingSetPoint =
    6966            0 :                 this->AirPowerCap * (zoneTstatSetpt.setptLo - this->T1) + this->tempDepLoad * zoneTstatSetpt.setptLo - this->tempIndLoad;
    6967            0 :             LoadToCoolingSetPoint =
    6968            0 :                 this->AirPowerCap * (zoneTstatSetpt.setptHi - this->T1) + this->tempDepLoad * zoneTstatSetpt.setptHi - this->tempIndLoad;
    6969            0 :         } break;
    6970            0 :         default: {
    6971            0 :             assert(false);
    6972              : 
    6973              :         } break;
    6974              :         } // switch (Algo)
    6975              : 
    6976       216499 :         if (RAFNFrac > 0.0) {
    6977            2 :             LoadToHeatingSetPoint = LoadToHeatingSetPoint / RAFNFrac;
    6978              :         }
    6979       216499 :         if (RAFNFrac > 0.0) {
    6980            2 :             LoadToCoolingSetPoint = LoadToCoolingSetPoint / RAFNFrac;
    6981              :         }
    6982              : 
    6983       216499 :         if (thisZone.HasAdjustedReturnTempByITE && !(state.dataGlobal->BeginSimFlag)) {
    6984            0 :             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       216499 :         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       216499 :         if (LoadToHeatingSetPoint > 0.0 && LoadToCoolingSetPoint > 0.0) {
    7009        86344 :             totalLoad = LoadToHeatingSetPoint;
    7010        86344 :             ZoneSetPoint = zoneTstatSetpt.setptLo;
    7011       130155 :         } else if (LoadToHeatingSetPoint < 0.0 && LoadToCoolingSetPoint < 0.0) {
    7012       102411 :             totalLoad = LoadToCoolingSetPoint;
    7013       102411 :             ZoneSetPoint = zoneTstatSetpt.setptHi;
    7014        27744 :         } 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        27744 :             totalLoad = 0.0;
    7017        27744 :             if (zoneNodeNum > 0) {
    7018        27744 :                 ZoneSetPoint = state.dataLoopNodes->Node(zoneNodeNum).Temp;
    7019        27744 :                 ZoneSetPoint = max(ZoneSetPoint, zoneTstatSetpt.setptLo); // trap out of deadband
    7020        27744 :                 ZoneSetPoint = min(ZoneSetPoint, zoneTstatSetpt.setptHi); // trap out of deadband
    7021              :             }
    7022        27744 :             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       216499 :     } break;
    7038              : 
    7039            0 :     default: {
    7040            0 :     } break;
    7041              :     } // swtich (setptType)
    7042              : 
    7043       462822 :     int systemNodeNumber = 0;
    7044       462822 :     int stageNum = 0;
    7045       462822 :     if (spaceNum > 0) {
    7046        44487 :         systemNodeNumber = state.dataHeatBal->space(spaceNum).SystemZoneNodeNumber;
    7047        44487 :         stageNum = state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).StageNum;
    7048        44487 :         assert(stageNum == state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).StageNum);
    7049              :     } else {
    7050       418335 :         systemNodeNumber = thisZone.SystemZoneNodeNumber;
    7051       418335 :         stageNum = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).StageNum;
    7052              :     }
    7053              :     // Staged control zone
    7054       462822 :     if (s_ztpc->NumStageCtrZone > 0) {
    7055            0 :         if (state.dataZoneCtrls->StageZoneLogic(zoneNum)) {
    7056            0 :             if (stageNum == 0) { // No load
    7057            0 :                 LoadToHeatingSetPoint = 0.0;
    7058            0 :                 LoadToCoolingSetPoint = 0.0;
    7059            0 :                 totalLoad = 0.0;
    7060            0 :                 if (systemNodeNumber > 0) {
    7061            0 :                     ZoneSetPoint = state.dataLoopNodes->Node(systemNodeNumber).Temp;
    7062            0 :                     ZoneSetPoint = max(ZoneSetPoint, zoneTstatSetpt.setptLo); // trap out of deadband
    7063            0 :                     ZoneSetPoint = min(ZoneSetPoint, zoneTstatSetpt.setptHi); // trap out of deadband
    7064              :                 }
    7065            0 :                 thisDeadBandOrSetBack = true;
    7066              : 
    7067            0 :             } else if (stageNum < 0) { // Cooling load
    7068            0 :                 switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    7069            0 :                 case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    7070            0 :                     LoadToCoolingSetPoint = (this->tempDepLoad * (zoneTstatSetpt.setptHi) - this->tempIndLoad);
    7071            0 :                 } 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            0 :                 totalLoad = LoadToCoolingSetPoint;
    7093            0 :                 ZoneSetPoint = zoneTstatSetpt.setptHi;
    7094            0 :                 LoadToHeatingSetPoint = LoadToCoolingSetPoint;
    7095            0 :                 if ((totalLoad) >= 0.0) {
    7096            0 :                     thisDeadBandOrSetBack = true;
    7097              :                 }
    7098              :             } else { // Heating load
    7099            0 :                 switch (state.dataHeatBal->ZoneAirSolutionAlgo) {
    7100            0 :                 case DataHeatBalance::SolutionAlgo::ThirdOrder: {
    7101            0 :                     LoadToHeatingSetPoint = (this->tempDepLoad * zoneTstatSetpt.setptLo - this->tempIndLoad);
    7102            0 :                 } 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            0 :                 totalLoad = LoadToHeatingSetPoint;
    7124            0 :                 ZoneSetPoint = zoneTstatSetpt.setptLo;
    7125            0 :                 LoadToCoolingSetPoint = LoadToHeatingSetPoint;
    7126            0 :                 if ((totalLoad) <= 0.0) {
    7127            0 :                     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       462822 :     if (zoneNodeNum > 0) {
    7135       244056 :         state.dataLoopNodes->Node(zoneNodeNum).TempSetPoint = ZoneSetPoint;
    7136              :     }
    7137              : 
    7138       462822 :     state.dataZoneEnergyDemand->Setback(zoneNum) = (ZoneSetPoint > this->setPointLast);
    7139              : 
    7140       462822 :     this->setPointLast = ZoneSetPoint;
    7141       462822 :     state.dataHeatBalFanSys->zoneTstatSetpts(zoneNum).setpt = ZoneSetPoint; // needed to fix Issue # 5048
    7142       462822 :     state.dataZoneEnergyDemand->DeadBandOrSetback(zoneNum) = thisDeadBandOrSetBack;
    7143       462822 :     state.dataZoneEnergyDemand->CurDeadBandOrSetback(zoneNum) = thisDeadBandOrSetBack;
    7144              : 
    7145              :     // Apply the Zone Multiplier and Load Correction factor as needed
    7146       462822 :     if (spaceNum > 0) {
    7147        44487 :         state.dataZoneEnergyDemand->spaceSysEnergyDemand(spaceNum).reportSensibleLoadsZoneMultiplier(
    7148              :             state, zoneNum, totalLoad, LoadToHeatingSetPoint, LoadToCoolingSetPoint);
    7149              :     } else {
    7150       418335 :         state.dataZoneEnergyDemand->ZoneSysEnergyDemand(zoneNum).reportSensibleLoadsZoneMultiplier(
    7151              :             state, zoneNum, totalLoad, LoadToHeatingSetPoint, LoadToCoolingSetPoint);
    7152              :     }
    7153       462822 : }
    7154              : } // namespace EnergyPlus::ZoneTempPredictorCorrector
        

Generated by: LCOV version 2.0-1