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

Generated by: LCOV version 2.0-1