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