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