Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, 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 <algorithm>
50 : #include <boost/math/tools/roots.hpp>
51 : #include <cassert>
52 : #include <cmath>
53 :
54 : // ObjexxFCL Headers
55 : #include <ObjexxFCL/Fmath.hh>
56 : #include <ObjexxFCL/string.functions.hh>
57 :
58 : // EnergyPlus Headers
59 : #include <EnergyPlus/Construction.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataEnvironment.hh>
62 : #include <EnergyPlus/DataHeatBalFanSys.hh>
63 : #include <EnergyPlus/DataHeatBalSurface.hh>
64 : #include <EnergyPlus/DataHeatBalance.hh>
65 : #include <EnergyPlus/DataIPShortCuts.hh>
66 : #include <EnergyPlus/DataPrecisionGlobals.hh>
67 : #include <EnergyPlus/DataRoomAirModel.hh>
68 : #include <EnergyPlus/DataViewFactorInformation.hh>
69 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
70 : #include <EnergyPlus/FileSystem.hh>
71 : #include <EnergyPlus/General.hh>
72 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
73 : #include <EnergyPlus/OutputProcessor.hh>
74 : #include <EnergyPlus/OutputReportPredefined.hh>
75 : #include <EnergyPlus/OutputReportTabular.hh>
76 : #include <EnergyPlus/Psychrometrics.hh>
77 : #include <EnergyPlus/ScheduleManager.hh>
78 : #include <EnergyPlus/ThermalComfort.hh>
79 : #include <EnergyPlus/UtilityRoutines.hh>
80 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
81 :
82 : namespace EnergyPlus {
83 :
84 : namespace ThermalComfort {
85 :
86 : // Module containing the routines dealing with the CalcThermalComfortFanger,
87 : // CalcThermalComfortPierce, and CalcThermalComfortKSU
88 :
89 : // MODULE INFORMATION:
90 : // AUTHOR Jaewook Lee
91 : // DATE WRITTEN January 2000
92 : // MODIFIED Rick Strand (for E+ implementation February 2000)
93 :
94 : // PURPOSE OF THIS MODULE:
95 : // To calculate thermal comfort indices based on the
96 : // three thermal comfort prediction models (Fanger, Pierce, KSU)
97 :
98 : // METHODOLOGY EMPLOYED:
99 : // For each thermal comfort model type, the subroutines will loop through
100 : // the people statements and perform the requested thermal comfort evaluations
101 :
102 : // Using/Aliasing
103 : using DataHeatBalance::PeopleData;
104 : using Psychrometrics::PsyRhFnTdbWPb;
105 : using ScheduleManager::GetCurrentScheduleValue;
106 :
107 2804483 : void ManageThermalComfort(EnergyPlusData &state, bool const InitializeOnly) // when called from ZTPC and calculations aren't needed
108 : {
109 :
110 : // SUBROUTINE INFORMATION:
111 : // AUTHOR Rick Strand
112 : // DATE WRITTEN February 2000
113 :
114 2804483 : if (state.dataThermalComforts->FirstTimeFlag) {
115 796 : InitThermalComfort(state); // Mainly sets up output stuff
116 796 : state.dataThermalComforts->FirstTimeFlag = false;
117 : }
118 :
119 2804483 : if (state.dataGlobal->DayOfSim == 1) {
120 706211 : if (state.dataGlobal->HourOfDay < 7) {
121 181328 : state.dataThermalComforts->TemporarySixAMTemperature = 1.868132;
122 524883 : } else if (state.dataGlobal->HourOfDay == 7) {
123 28948 : if (state.dataGlobal->TimeStep == 1) {
124 5246 : state.dataThermalComforts->TemporarySixAMTemperature = state.dataEnvrn->OutDryBulbTemp;
125 : }
126 : }
127 : } else {
128 2098272 : if (state.dataGlobal->HourOfDay == 7) {
129 87428 : if (state.dataGlobal->TimeStep == 1) {
130 16345 : state.dataThermalComforts->TemporarySixAMTemperature = state.dataEnvrn->OutDryBulbTemp;
131 : }
132 : }
133 : }
134 :
135 2804483 : if (InitializeOnly) return;
136 :
137 2804482 : if (state.dataGlobal->BeginEnvrnFlag) {
138 6443 : state.dataThermalComforts->ZoneOccHrs = 0.0;
139 : }
140 :
141 2804482 : if (!state.dataGlobal->DoingSizing && !state.dataGlobal->WarmupFlag) {
142 482304 : CalcThermalComfortFanger(state);
143 482304 : if (state.dataHeatBal->AnyThermalComfortPierceModel) CalcThermalComfortPierceASHRAE(state);
144 482304 : if (state.dataHeatBal->AnyThermalComfortKSUModel) CalcThermalComfortKSU(state);
145 482304 : if (state.dataHeatBal->AnyThermalComfortCoolingEffectModel) CalcThermalComfortCoolingEffectASH(state);
146 482304 : if (state.dataHeatBal->AnyThermalComfortAnkleDraftModel) CalcThermalComfortAnkleDraftASH(state);
147 482304 : CalcThermalComfortSimpleASH55(state);
148 482304 : CalcIfSetPointMet(state);
149 482304 : if (state.dataHeatBal->AdaptiveComfortRequested_ASH55) CalcThermalComfortAdaptiveASH55(state, false);
150 482304 : if (state.dataHeatBal->AdaptiveComfortRequested_CEN15251) CalcThermalComfortAdaptiveCEN15251(state, false);
151 : }
152 : }
153 :
154 796 : void InitThermalComfort(EnergyPlusData &state)
155 : {
156 :
157 : // SUBROUTINE INFORMATION:
158 : // AUTHOR Rick Strand
159 : // DATE WRITTEN February 2000
160 :
161 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
162 : int Loop; // DO loop counter
163 796 : std::string CurrentGroupName;
164 :
165 796 : state.dataThermalComforts->ThermalComfortData.allocate(state.dataHeatBal->TotPeople);
166 :
167 4910 : for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
168 :
169 4114 : CurrentGroupName = state.dataHeatBal->People(Loop).Name;
170 :
171 : // CurrentModuleObject='People'
172 : // MJW MRT ToDo: Rename most Zone Therml Comfort output varibles to People Thermal Comfort ('cause they're keyed by People name)
173 4114 : if (state.dataHeatBal->People(Loop).Fanger) {
174 5094 : SetupOutputVariable(state,
175 : "Zone Thermal Comfort Fanger Model PMV",
176 : Constant::Units::None,
177 2547 : state.dataThermalComforts->ThermalComfortData(Loop).FangerPMV,
178 : OutputProcessor::TimeStepType::Zone,
179 : OutputProcessor::StoreType::Average,
180 2547 : state.dataHeatBal->People(Loop).Name);
181 5094 : SetupOutputVariable(state,
182 : "Zone Thermal Comfort Fanger Model PPD",
183 : Constant::Units::Perc,
184 2547 : state.dataThermalComforts->ThermalComfortData(Loop).FangerPPD,
185 : OutputProcessor::TimeStepType::Zone,
186 : OutputProcessor::StoreType::Average,
187 2547 : state.dataHeatBal->People(Loop).Name);
188 5094 : SetupOutputVariable(state,
189 : "Zone Thermal Comfort Clothing Surface Temperature",
190 : Constant::Units::C,
191 2547 : state.dataThermalComforts->ThermalComfortData(Loop).CloSurfTemp,
192 : OutputProcessor::TimeStepType::Zone,
193 : OutputProcessor::StoreType::Average,
194 2547 : state.dataHeatBal->People(Loop).Name);
195 : }
196 :
197 4114 : if (state.dataHeatBal->People(Loop).Pierce) {
198 22 : SetupOutputVariable(state,
199 : "Zone Thermal Comfort Pierce Model Effective Temperature PMV",
200 : Constant::Units::None,
201 11 : state.dataThermalComforts->ThermalComfortData(Loop).PiercePMVET,
202 : OutputProcessor::TimeStepType::Zone,
203 : OutputProcessor::StoreType::Average,
204 11 : state.dataHeatBal->People(Loop).Name);
205 22 : SetupOutputVariable(state,
206 : "Zone Thermal Comfort Pierce Model Standard Effective Temperature PMV",
207 : Constant::Units::None,
208 11 : state.dataThermalComforts->ThermalComfortData(Loop).PiercePMVSET,
209 : OutputProcessor::TimeStepType::Zone,
210 : OutputProcessor::StoreType::Average,
211 11 : state.dataHeatBal->People(Loop).Name);
212 22 : SetupOutputVariable(state,
213 : "Zone Thermal Comfort Pierce Model Discomfort Index",
214 : Constant::Units::None,
215 11 : state.dataThermalComforts->ThermalComfortData(Loop).PierceDISC,
216 : OutputProcessor::TimeStepType::Zone,
217 : OutputProcessor::StoreType::Average,
218 11 : state.dataHeatBal->People(Loop).Name);
219 22 : SetupOutputVariable(state,
220 : "Zone Thermal Comfort Pierce Model Thermal Sensation Index",
221 : Constant::Units::None,
222 11 : state.dataThermalComforts->ThermalComfortData(Loop).PierceTSENS,
223 : OutputProcessor::TimeStepType::Zone,
224 : OutputProcessor::StoreType::Average,
225 11 : state.dataHeatBal->People(Loop).Name);
226 22 : SetupOutputVariable(state,
227 : "Zone Thermal Comfort Pierce Model Standard Effective Temperature",
228 : Constant::Units::C,
229 11 : state.dataThermalComforts->ThermalComfortData(Loop).PierceSET,
230 : OutputProcessor::TimeStepType::Zone,
231 : OutputProcessor::StoreType::Average,
232 11 : state.dataHeatBal->People(Loop).Name);
233 : }
234 :
235 4114 : if (state.dataHeatBal->People(Loop).KSU) {
236 12 : SetupOutputVariable(state,
237 : "Zone Thermal Comfort KSU Model Thermal Sensation Vote",
238 : Constant::Units::None,
239 6 : state.dataThermalComforts->ThermalComfortData(Loop).KsuTSV,
240 : OutputProcessor::TimeStepType::Zone,
241 : OutputProcessor::StoreType::Average,
242 6 : state.dataHeatBal->People(Loop).Name);
243 : }
244 :
245 4114 : if ((state.dataHeatBal->People(Loop).Fanger) || (state.dataHeatBal->People(Loop).Pierce) || (state.dataHeatBal->People(Loop).KSU)) {
246 5108 : SetupOutputVariable(state,
247 : "Zone Thermal Comfort Mean Radiant Temperature",
248 : Constant::Units::C,
249 2554 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortMRT,
250 : OutputProcessor::TimeStepType::Zone,
251 : OutputProcessor::StoreType::Average,
252 2554 : state.dataHeatBal->People(Loop).Name);
253 5108 : SetupOutputVariable(state,
254 : "Zone Thermal Comfort Operative Temperature",
255 : Constant::Units::C,
256 2554 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortOpTemp,
257 : OutputProcessor::TimeStepType::Zone,
258 : OutputProcessor::StoreType::Average,
259 2554 : state.dataHeatBal->People(Loop).Name);
260 5108 : SetupOutputVariable(state,
261 : "Zone Thermal Comfort Clothing Value",
262 : Constant::Units::clo,
263 2554 : state.dataThermalComforts->ThermalComfortData(Loop).ClothingValue,
264 : OutputProcessor::TimeStepType::Zone,
265 : OutputProcessor::StoreType::Average,
266 2554 : state.dataHeatBal->People(Loop).Name);
267 : }
268 :
269 4114 : if (state.dataHeatBal->People(Loop).AdaptiveASH55) {
270 6 : SetupOutputVariable(state,
271 : "Zone Thermal Comfort ASHRAE 55 Adaptive Model 90% Acceptability Status",
272 : Constant::Units::None,
273 6 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveASH5590,
274 : OutputProcessor::TimeStepType::Zone,
275 : OutputProcessor::StoreType::Average,
276 6 : state.dataHeatBal->People(Loop).Name);
277 6 : SetupOutputVariable(state,
278 : "Zone Thermal Comfort ASHRAE 55 Adaptive Model 80% Acceptability Status",
279 : Constant::Units::None,
280 6 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveASH5580,
281 : OutputProcessor::TimeStepType::Zone,
282 : OutputProcessor::StoreType::Average,
283 6 : state.dataHeatBal->People(Loop).Name);
284 12 : SetupOutputVariable(state,
285 : "Zone Thermal Comfort ASHRAE 55 Adaptive Model Running Average Outdoor Air Temperature",
286 : Constant::Units::C,
287 6 : state.dataThermalComforts->ThermalComfortData(Loop).ASHRAE55RunningMeanOutdoorTemp,
288 : OutputProcessor::TimeStepType::Zone,
289 : OutputProcessor::StoreType::Average,
290 6 : state.dataHeatBal->People(Loop).Name);
291 12 : SetupOutputVariable(state,
292 : "Zone Thermal Comfort ASHRAE 55 Adaptive Model Temperature",
293 : Constant::Units::C,
294 6 : state.dataThermalComforts->ThermalComfortData(Loop).TComfASH55,
295 : OutputProcessor::TimeStepType::Zone,
296 : OutputProcessor::StoreType::Average,
297 6 : state.dataHeatBal->People(Loop).Name);
298 : }
299 :
300 4114 : if (state.dataHeatBal->People(Loop).AdaptiveCEN15251) {
301 1 : SetupOutputVariable(state,
302 : "Zone Thermal Comfort CEN 15251 Adaptive Model Category I Status",
303 : Constant::Units::None,
304 1 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatI,
305 : OutputProcessor::TimeStepType::Zone,
306 : OutputProcessor::StoreType::Average,
307 1 : state.dataHeatBal->People(Loop).Name);
308 1 : SetupOutputVariable(state,
309 : "Zone Thermal Comfort CEN 15251 Adaptive Model Category II Status",
310 : Constant::Units::None,
311 1 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatII,
312 : OutputProcessor::TimeStepType::Zone,
313 : OutputProcessor::StoreType::Average,
314 1 : state.dataHeatBal->People(Loop).Name);
315 1 : SetupOutputVariable(state,
316 : "Zone Thermal Comfort CEN 15251 Adaptive Model Category III Status",
317 : Constant::Units::None,
318 1 : state.dataThermalComforts->ThermalComfortData(Loop).ThermalComfortAdaptiveCEN15251CatIII,
319 : OutputProcessor::TimeStepType::Zone,
320 : OutputProcessor::StoreType::Average,
321 1 : state.dataHeatBal->People(Loop).Name);
322 2 : SetupOutputVariable(state,
323 : "Zone Thermal Comfort CEN 15251 Adaptive Model Running Average Outdoor Air Temperature",
324 : Constant::Units::C,
325 1 : state.dataThermalComforts->ThermalComfortData(Loop).CEN15251RunningMeanOutdoorTemp,
326 : OutputProcessor::TimeStepType::Zone,
327 : OutputProcessor::StoreType::Average,
328 1 : state.dataHeatBal->People(Loop).Name);
329 2 : SetupOutputVariable(state,
330 : "Zone Thermal Comfort CEN 15251 Adaptive Model Temperature",
331 : Constant::Units::C,
332 1 : state.dataThermalComforts->ThermalComfortData(Loop).TComfCEN15251,
333 : OutputProcessor::TimeStepType::Zone,
334 : OutputProcessor::StoreType::Average,
335 1 : state.dataHeatBal->People(Loop).Name);
336 : }
337 4114 : if (state.dataHeatBal->People(Loop).CoolingEffectASH55) {
338 2 : SetupOutputVariable(state,
339 : "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect",
340 : Constant::Units::C,
341 1 : state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectASH55,
342 : OutputProcessor::TimeStepType::Zone,
343 : OutputProcessor::StoreType::Average,
344 1 : state.dataHeatBal->People(Loop).Name);
345 2 : SetupOutputVariable(state,
346 : "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect Adjusted PMV",
347 : Constant::Units::None,
348 1 : state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectAdjustedPMVASH55,
349 : OutputProcessor::TimeStepType::Zone,
350 : OutputProcessor::StoreType::Average,
351 1 : state.dataHeatBal->People(Loop).Name);
352 2 : SetupOutputVariable(state,
353 : "Zone Thermal Comfort ASHRAE 55 Elevated Air Speed Cooling Effect Adjusted PPD",
354 : Constant::Units::None,
355 1 : state.dataThermalComforts->ThermalComfortData(Loop).CoolingEffectAdjustedPPDASH55,
356 : OutputProcessor::TimeStepType::Zone,
357 : OutputProcessor::StoreType::Average,
358 1 : state.dataHeatBal->People(Loop).Name);
359 : }
360 4114 : if (state.dataHeatBal->People(Loop).AnkleDraftASH55) {
361 2 : SetupOutputVariable(state,
362 : "Zone Thermal Comfort ASHRAE 55 Ankle Draft PPD",
363 : Constant::Units::None,
364 1 : state.dataThermalComforts->ThermalComfortData(Loop).AnkleDraftPPDASH55,
365 : OutputProcessor::TimeStepType::Zone,
366 : OutputProcessor::StoreType::Average,
367 1 : state.dataHeatBal->People(Loop).Name);
368 : }
369 : }
370 796 : state.dataThermalComforts->ThermalComfortInASH55.allocate(state.dataGlobal->NumOfZones);
371 :
372 : // ASHRAE 55 Warning. If any people statement for a zone is true, set that zone to true
373 4910 : for (Loop = 1; Loop <= state.dataHeatBal->TotPeople; ++Loop) {
374 4114 : if (state.dataHeatBal->People(Loop).Show55Warning) {
375 0 : state.dataThermalComforts->ThermalComfortInASH55(state.dataHeatBal->People(Loop).ZonePtr).Enable55Warning = true;
376 : }
377 : }
378 :
379 : // CurrentModuleObject='Zone'
380 5852 : for (Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
381 10112 : SetupOutputVariable(state,
382 : "Zone Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time",
383 : Constant::Units::hr,
384 5056 : state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotSummer,
385 : OutputProcessor::TimeStepType::Zone,
386 : OutputProcessor::StoreType::Sum,
387 5056 : state.dataHeatBal->Zone(Loop).Name);
388 10112 : SetupOutputVariable(state,
389 : "Zone Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time",
390 : Constant::Units::hr,
391 5056 : state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotWinter,
392 : OutputProcessor::TimeStepType::Zone,
393 : OutputProcessor::StoreType::Sum,
394 5056 : state.dataHeatBal->Zone(Loop).Name);
395 10112 : SetupOutputVariable(state,
396 : "Zone Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time",
397 : Constant::Units::hr,
398 5056 : state.dataThermalComforts->ThermalComfortInASH55(Loop).timeNotEither,
399 : OutputProcessor::TimeStepType::Zone,
400 : OutputProcessor::StoreType::Sum,
401 5056 : state.dataHeatBal->Zone(Loop).Name);
402 : }
403 1592 : SetupOutputVariable(state,
404 : "Facility Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time",
405 : Constant::Units::hr,
406 796 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer,
407 : OutputProcessor::TimeStepType::Zone,
408 : OutputProcessor::StoreType::Sum,
409 : "Facility");
410 1592 : SetupOutputVariable(state,
411 : "Facility Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time",
412 : Constant::Units::hr,
413 796 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter,
414 : OutputProcessor::TimeStepType::Zone,
415 : OutputProcessor::StoreType::Sum,
416 : "Facility");
417 1592 : SetupOutputVariable(state,
418 : "Facility Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time",
419 : Constant::Units::hr,
420 796 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either,
421 : OutputProcessor::TimeStepType::Zone,
422 : OutputProcessor::StoreType::Sum,
423 : "Facility");
424 :
425 796 : state.dataThermalComforts->ThermalComfortSetPoint.allocate(state.dataGlobal->NumOfZones);
426 5852 : for (Loop = 1; Loop <= state.dataGlobal->NumOfZones; ++Loop) {
427 10112 : SetupOutputVariable(state,
428 : "Zone Heating Setpoint Not Met Time",
429 : Constant::Units::hr,
430 5056 : state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetHeating,
431 : OutputProcessor::TimeStepType::Zone,
432 : OutputProcessor::StoreType::Sum,
433 5056 : state.dataHeatBal->Zone(Loop).Name);
434 10112 : SetupOutputVariable(state,
435 : "Zone Heating Setpoint Not Met While Occupied Time",
436 : Constant::Units::hr,
437 5056 : state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetHeatingOccupied,
438 : OutputProcessor::TimeStepType::Zone,
439 : OutputProcessor::StoreType::Sum,
440 5056 : state.dataHeatBal->Zone(Loop).Name);
441 10112 : SetupOutputVariable(state,
442 : "Zone Cooling Setpoint Not Met Time",
443 : Constant::Units::hr,
444 5056 : state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetCooling,
445 : OutputProcessor::TimeStepType::Zone,
446 : OutputProcessor::StoreType::Sum,
447 5056 : state.dataHeatBal->Zone(Loop).Name);
448 10112 : SetupOutputVariable(state,
449 : "Zone Cooling Setpoint Not Met While Occupied Time",
450 : Constant::Units::hr,
451 5056 : state.dataThermalComforts->ThermalComfortSetPoint(Loop).notMetCoolingOccupied,
452 : OutputProcessor::TimeStepType::Zone,
453 : OutputProcessor::StoreType::Sum,
454 5056 : state.dataHeatBal->Zone(Loop).Name);
455 : }
456 :
457 1592 : SetupOutputVariable(state,
458 : "Facility Heating Setpoint Not Met Time",
459 : Constant::Units::hr,
460 796 : state.dataThermalComforts->AnyZoneNotMetHeating,
461 : OutputProcessor::TimeStepType::Zone,
462 : OutputProcessor::StoreType::Sum,
463 : "Facility");
464 1592 : SetupOutputVariable(state,
465 : "Facility Cooling Setpoint Not Met Time",
466 : Constant::Units::hr,
467 796 : state.dataThermalComforts->AnyZoneNotMetCooling,
468 : OutputProcessor::TimeStepType::Zone,
469 : OutputProcessor::StoreType::Sum,
470 : "Facility");
471 1592 : SetupOutputVariable(state,
472 : "Facility Heating Setpoint Not Met While Occupied Time",
473 : Constant::Units::hr,
474 796 : state.dataThermalComforts->AnyZoneNotMetHeatingOccupied,
475 : OutputProcessor::TimeStepType::Zone,
476 : OutputProcessor::StoreType::Sum,
477 : "Facility");
478 1592 : SetupOutputVariable(state,
479 : "Facility Cooling Setpoint Not Met While Occupied Time",
480 : Constant::Units::hr,
481 796 : state.dataThermalComforts->AnyZoneNotMetCoolingOccupied,
482 : OutputProcessor::TimeStepType::Zone,
483 : OutputProcessor::StoreType::Sum,
484 : "Facility");
485 :
486 796 : GetAngleFactorList(state);
487 :
488 796 : state.dataThermalComforts->ZoneOccHrs.dimension(state.dataGlobal->NumOfZones, 0.0);
489 796 : }
490 :
491 492055 : void CalcThermalComfortFanger(EnergyPlusData &state,
492 : ObjexxFCL::Optional_int_const PNum, // People number for thermal comfort control
493 : ObjexxFCL::Optional<Real64 const> Tset, // Temperature setpoint for thermal comfort control
494 : ObjexxFCL::Optional<Real64> PMVResult // PMV value for thermal comfort control
495 : )
496 : {
497 :
498 : // SUBROUTINE INFORMATION:
499 : // AUTHOR Jaewook Lee
500 : // DATE WRITTEN January 2000
501 : // MODIFIED Rick Strand (for E+ implementation February 2000)
502 : // Brent Griffith modifications for CR 5641 (October 2005)
503 : // L. Gu, Added optional arguments for thermal comfort control (May 2006)
504 : // T. Hong, added Fanger PPD (April 2009)
505 :
506 : // PURPOSE OF THIS SUBROUTINE:
507 : // This subroutine calculates PMV(Predicted Mean Vote) using the Fanger thermal
508 : // comfort model. This subroutine is also used for thermal comfort control by determining
509 : // the temperature at which the PMV is equal to a PMV setpoint specified by the user.
510 :
511 : // METHODOLOGY EMPLOYED:
512 : // This subroutine is based heavily upon the work performed by Dan Maloney for
513 : // the BLAST program. Many of the equations are based on the original Fanger
514 : // development. See documentation for further details and references.
515 :
516 : // REFERENCES:
517 : // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
518 : // BG note (10/21/2005), This formulation is based on the the BASIC program
519 : // that is included in ASHRAE Standard 55 Normative Appendix D.
520 :
521 2592652 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
522 2100597 : ++state.dataThermalComforts->PeopleNum) {
523 :
524 : // Optional argument is used to access people object when thermal comfort control is used
525 2100597 : if (present(PNum)) {
526 941349 : if (state.dataThermalComforts->PeopleNum != PNum) continue;
527 : }
528 :
529 : // If optional argument is used do not cycle regardless of thermal comfort reporting type
530 2081095 : if ((!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Fanger) && (!present(PNum))) continue;
531 :
532 1168999 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ZonePtr;
533 1168999 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
534 1168999 : if (present(PNum)) {
535 9751 : state.dataThermalComforts->AirTemp = Tset;
536 : } else {
537 1159248 : state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
538 : }
539 1168999 : if (state.dataRoomAir->anyNonMixingRoomAirModel) {
540 3168 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ZonePtr;
541 6336 : if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
542 3168 : state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
543 0 : state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum); // PH 3/7/04
544 : // UCSD-CV
545 3168 : } else if (state.dataRoomAir->IsZoneCrossVent(state.dataThermalComforts->ZoneNum)) {
546 0 : if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Jet) {
547 0 : state.dataThermalComforts->AirTemp = state.dataRoomAir->ZTJET(state.dataThermalComforts->ZoneNum);
548 0 : } else if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Recirculation) {
549 0 : state.dataThermalComforts->AirTemp = state.dataRoomAir->ZTJET(state.dataThermalComforts->ZoneNum);
550 : }
551 : }
552 : }
553 1168999 : state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
554 : // Use mean air temp for calculating RH when thermal comfort control is used
555 1168999 : if (present(PNum)) {
556 19502 : state.dataThermalComforts->RelHum =
557 19502 : PsyRhFnTdbWPb(state,
558 9751 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).MAT,
559 : thisZoneHB.airHumRatAvgComf,
560 9751 : state.dataEnvrn->OutBaroPress);
561 : } else {
562 2318496 : state.dataThermalComforts->RelHum =
563 1159248 : PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
564 : }
565 1168999 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TemperatureInZone = state.dataThermalComforts->AirTemp;
566 1168999 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).RelativeHumidityInZone = state.dataThermalComforts->RelHum * 100.0;
567 :
568 : // Metabolic rate of body (W/m2)
569 2337998 : state.dataThermalComforts->ActLevel =
570 1168999 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ActivityLevelPtr) / BodySurfArea;
571 : // Energy consumption by external work (W/m2)
572 2337998 : state.dataThermalComforts->WorkEff =
573 1168999 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).WorkEffPtr) *
574 1168999 : state.dataThermalComforts->ActLevel;
575 : // Clothing unit
576 : Real64 IntermediateClothing;
577 1168999 : switch (state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).clothingType) {
578 1168855 : case DataHeatBalance::ClothingType::InsulationSchedule:
579 2337710 : state.dataThermalComforts->CloUnit =
580 1168855 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
581 1168855 : break;
582 96 : case DataHeatBalance::ClothingType::DynamicAshrae55:
583 96 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
584 96 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
585 96 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
586 96 : state.dataThermalComforts->CloUnit;
587 96 : DynamicClothingModel(state);
588 192 : state.dataThermalComforts->CloUnit =
589 96 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue;
590 96 : break;
591 48 : case DataHeatBalance::ClothingType::CalculationSchedule:
592 : IntermediateClothing =
593 48 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingMethodPtr);
594 48 : if (IntermediateClothing == 1.0) {
595 24 : state.dataThermalComforts->CloUnit =
596 12 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
597 12 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
598 12 : state.dataThermalComforts->CloUnit;
599 36 : } else if (IntermediateClothing == 2.0) {
600 36 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
601 36 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
602 36 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
603 36 : state.dataThermalComforts->CloUnit;
604 36 : DynamicClothingModel(state);
605 36 : state.dataThermalComforts->CloUnit =
606 36 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue;
607 : } else {
608 0 : state.dataThermalComforts->CloUnit =
609 0 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
610 0 : ShowWarningError(state,
611 0 : format("PEOPLE=\"{}\", Scheduled clothing value will be used rather than clothing calculation method.",
612 0 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name));
613 : }
614 48 : break;
615 0 : default:
616 0 : ShowSevereError(
617 0 : state, format("PEOPLE=\"{}\", Incorrect Clothing Type", state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name));
618 : }
619 :
620 1168999 : if (state.dataRoomAir->anyNonMixingRoomAirModel && state.dataRoomAir->IsZoneCrossVent(state.dataThermalComforts->ZoneNum)) {
621 0 : if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Jet) {
622 0 : state.dataThermalComforts->AirVel = state.dataRoomAir->Ujet(state.dataThermalComforts->ZoneNum);
623 0 : } else if (state.dataRoomAir->ZoneCrossVent(state.dataThermalComforts->ZoneNum).VforComfort == RoomAir::Comfort::Recirculation) {
624 0 : state.dataThermalComforts->AirVel = state.dataRoomAir->Urec(state.dataThermalComforts->ZoneNum);
625 : } else {
626 0 : state.dataThermalComforts->AirVel = 0.2;
627 : }
628 : } else {
629 2337998 : state.dataThermalComforts->AirVel =
630 1168999 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AirVelocityPtr);
631 : // Ensure air velocity within the reasonable range. Otherwise reccusive warnings is provided
632 1168999 : if (present(PNum) && (state.dataThermalComforts->AirVel < 0.1 || state.dataThermalComforts->AirVel > 0.5)) {
633 0 : if (state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AirVelErrIndex == 0) {
634 0 : ShowWarningMessage(state,
635 0 : format("PEOPLE=\"{}\", Air velocity is beyond the reasonable range (0.1,0.5) for thermal comfort control.",
636 0 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name));
637 0 : ShowContinueErrorTimeStamp(state, "");
638 : }
639 0 : ShowRecurringWarningErrorAtEnd(state,
640 0 : "PEOPLE=\"" + state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name +
641 : "\",Air velocity is still beyond the reasonable range (0.1,0.5)",
642 0 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AirVelErrIndex,
643 0 : state.dataThermalComforts->AirVel,
644 0 : state.dataThermalComforts->AirVel,
645 : _,
646 : "[m/s]",
647 : "[m/s]");
648 : }
649 : }
650 :
651 1168999 : Real64 PMV = CalcFangerPMV(state,
652 1168999 : state.dataThermalComforts->AirTemp,
653 1168999 : state.dataThermalComforts->RadTemp,
654 1168999 : state.dataThermalComforts->RelHum,
655 1168999 : state.dataThermalComforts->AirVel,
656 1168999 : state.dataThermalComforts->ActLevel,
657 1168999 : state.dataThermalComforts->CloUnit,
658 1168999 : state.dataThermalComforts->WorkEff);
659 :
660 1168999 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).FangerPMV = PMV;
661 :
662 : // Pass resulting PMV based on temperature setpoint (Tset) when using thermal comfort control
663 1168999 : if (present(PNum)) {
664 9751 : PMVResult = PMV;
665 : }
666 1168999 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortMRT =
667 1168999 : state.dataThermalComforts->RadTemp;
668 1168999 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
669 1168999 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
670 1168999 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).CloSurfTemp = state.dataThermalComforts->CloSurfTemp;
671 :
672 : // Calculate the Fanger PPD (Predicted Percentage of Dissatisfied), as a %
673 1168999 : Real64 PPD = CalcFangerPPD(PMV);
674 1168999 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).FangerPPD = PPD;
675 : }
676 492055 : }
677 :
678 1169479 : Real64 CalcFangerPMV(
679 : EnergyPlusData &state, Real64 AirTemp, Real64 RadTemp, Real64 RelHum, Real64 AirVel, Real64 ActLevel, Real64 CloUnit, Real64 WorkEff)
680 : {
681 :
682 : // Using/Aliasing
683 : using Psychrometrics::PsyPsatFnTemp;
684 :
685 : // SUBROUTINE PARAMETER DEFINITIONS:
686 1169479 : int constexpr MaxIter(150); // Limit of iteration
687 1169479 : Real64 constexpr StopIterCrit(0.00015); // Stop criteria for iteration
688 :
689 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
690 : Real64 P1; // Intermediate variables to calculate clothed body ratio and clothing temperature
691 : Real64 P2; // Intermediate variables to calculate clothed body ratio and clothing temperature
692 : Real64 P3; // Intermediate variables to calculate clothed body ratio and clothing temperature
693 : Real64 P4; // Intermediate variables to calculate clothed body ratio and clothing temperature
694 : Real64 XF; // Intermediate variables to calculate clothed body ratio and clothing temperature
695 : Real64 XN; // Intermediate variables to calculate clothed body ratio and clothing temperature
696 : Real64 PMV; // temporary variable to store calculated Fanger PMV value
697 : // VapPress = CalcSatVapPressFromTemp(AirTemp) !original
698 : // VapPress = RelHum*VapPress !original might be in torrs
699 :
700 1169479 : state.dataThermalComforts->VapPress = PsyPsatFnTemp(state, AirTemp); // use psych routines inside E+ , returns Pa
701 :
702 1169479 : state.dataThermalComforts->VapPress *= RelHum; // in units of [Pa]
703 :
704 1169479 : state.dataThermalComforts->IntHeatProd = ActLevel - WorkEff;
705 :
706 : // Compute the Corresponding Clothed Body Ratio
707 1169479 : state.dataThermalComforts->CloBodyRat = 1.05 + 0.1 * CloUnit; // The ratio of the surface area of the clothed body
708 : // to the surface area of nude body
709 :
710 1169479 : if (CloUnit < 0.5) state.dataThermalComforts->CloBodyRat = state.dataThermalComforts->CloBodyRat - 0.05 + 0.1 * CloUnit;
711 :
712 1169479 : state.dataThermalComforts->AbsRadTemp = RadTemp + TAbsConv;
713 1169479 : state.dataThermalComforts->AbsAirTemp = AirTemp + TAbsConv;
714 :
715 1169479 : state.dataThermalComforts->CloInsul = CloUnit * state.dataThermalComforts->CloBodyRat * 0.155; // Thermal resistance of the clothing // icl
716 :
717 1169479 : P2 = state.dataThermalComforts->CloInsul * 3.96;
718 1169479 : P3 = state.dataThermalComforts->CloInsul * 100.0;
719 1169479 : P1 = state.dataThermalComforts->CloInsul * state.dataThermalComforts->AbsAirTemp; // p4
720 1169479 : P4 = 308.7 - 0.028 * state.dataThermalComforts->IntHeatProd + P2 * pow_4(state.dataThermalComforts->AbsRadTemp / 100.0); // p5
721 :
722 : // First guess for clothed surface tempeature
723 1169479 : state.dataThermalComforts->AbsCloSurfTemp = state.dataThermalComforts->AbsAirTemp + (35.5 - AirTemp) / (3.5 * (CloUnit + 0.1));
724 1169479 : XN = state.dataThermalComforts->AbsCloSurfTemp / 100.0;
725 1169479 : state.dataThermalComforts->HcFor = 12.1 * std::sqrt(AirVel); // Heat transfer coefficient by forced convection
726 1169479 : state.dataThermalComforts->IterNum = 0;
727 1169479 : XF = XN;
728 :
729 : // COMPUTE SURFACE TEMPERATURE OF CLOTHING BY ITERATIONS
730 6933572 : while (((std::abs(XN - XF) > StopIterCrit) || (state.dataThermalComforts->IterNum == 0)) && (state.dataThermalComforts->IterNum < MaxIter)) {
731 5764093 : XF = (XF + XN) / 2.0;
732 11528186 : state.dataThermalComforts->HcNat =
733 5764093 : 2.38 * root_4(std::abs(100.0 * XF - state.dataThermalComforts->AbsAirTemp)); // Heat transfer coefficient by natural convection
734 5764093 : state.dataThermalComforts->Hc =
735 5764093 : max(state.dataThermalComforts->HcFor, state.dataThermalComforts->HcNat); // Determination of convective heat transfer coefficient
736 5764093 : XN = (P4 + P1 * state.dataThermalComforts->Hc - P2 * pow_4(XF)) / (100.0 + P3 * state.dataThermalComforts->Hc);
737 5764093 : ++state.dataThermalComforts->IterNum;
738 5764093 : if (state.dataThermalComforts->IterNum > MaxIter) {
739 0 : ShowWarningError(state, "Max iteration exceeded in CalcThermalFanger");
740 : }
741 : }
742 1169479 : state.dataThermalComforts->AbsCloSurfTemp = 100.0 * XN;
743 1169479 : state.dataThermalComforts->CloSurfTemp = state.dataThermalComforts->AbsCloSurfTemp - TAbsConv;
744 :
745 : // COMPUTE PREDICTED MEAN VOTE
746 : // Sensible heat loss
747 : // RadHeatLoss = RadSurfEff*CloBodyRat*SkinEmiss*StefanBoltz* & !original
748 : // (AbsCloSurfTemp**4 - AbsRadTemp**4) ! Heat loss by radiation
749 :
750 : // following line is ln 480 in ASHRAE 55 append. D
751 2338958 : state.dataThermalComforts->RadHeatLoss =
752 1169479 : 3.96 * state.dataThermalComforts->CloBodyRat *
753 1169479 : (pow_4(state.dataThermalComforts->AbsCloSurfTemp * 0.01) - pow_4(state.dataThermalComforts->AbsRadTemp * 0.01));
754 :
755 1169479 : state.dataThermalComforts->ConvHeatLoss = state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->Hc *
756 1169479 : (state.dataThermalComforts->CloSurfTemp - AirTemp); // Heat loss by convection
757 :
758 1169479 : state.dataThermalComforts->DryHeatLoss = state.dataThermalComforts->RadHeatLoss + state.dataThermalComforts->ConvHeatLoss;
759 :
760 : // Evaporative heat loss
761 : // Heat loss by regulatory sweating
762 1169479 : state.dataThermalComforts->EvapHeatLossRegComf = 0.0;
763 1169479 : if (state.dataThermalComforts->IntHeatProd > 58.2) {
764 1169479 : state.dataThermalComforts->EvapHeatLossRegComf = 0.42 * (state.dataThermalComforts->IntHeatProd - ActLevelConv);
765 : }
766 : // SkinTempComf = 35.7 - 0.028*IntHeatProd ! Skin temperature required to achieve thermal comfort
767 : // SatSkinVapPress = 1.92*SkinTempComf - 25.3 ! Water vapor pressure at required skin temperature
768 : // Heat loss by diffusion
769 : // EvapHeatLossDiff = 0.4148*(SatSkinVapPress - VapPress) !original
770 2338958 : state.dataThermalComforts->EvapHeatLossDiff =
771 1169479 : 3.05 * 0.001 *
772 1169479 : (5733.0 - 6.99 * state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->VapPress); // ln 440 in ASHRAE 55 Append. D
773 :
774 1169479 : state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossRegComf + state.dataThermalComforts->EvapHeatLossDiff;
775 : // Heat loss by respiration
776 : // original: LatRespHeatLoss = 0.0023*ActLevel*(44. - VapPress) ! Heat loss by latent respiration
777 2338958 : state.dataThermalComforts->LatRespHeatLoss =
778 1169479 : 1.7 * 0.00001 * ActLevel * (5867.0 - state.dataThermalComforts->VapPress); // ln 460 in ASHRAE 55 Append. D
779 :
780 : // LatRespHeatLoss = 0.017251*ActLevel*(5.8662 - VapPress)
781 : // V-1.2.2 'fix' BG 3/2005 5th term in LHS Eq (58) in 2001 HOF Ch. 8
782 : // this was wrong because VapPress needed to be kPa
783 :
784 1169479 : state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevel * (34.0 - AirTemp); // Heat loss by dry respiration.
785 :
786 1169479 : state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
787 :
788 1169479 : state.dataThermalComforts->ThermSensTransCoef = 0.303 * std::exp(-0.036 * ActLevel) + 0.028; // Thermal transfer coefficient to calculate PMV
789 :
790 1169479 : PMV = state.dataThermalComforts->ThermSensTransCoef * (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->EvapHeatLoss -
791 1169479 : state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLoss);
792 :
793 1169479 : return PMV;
794 : }
795 :
796 1169191 : Real64 CalcFangerPPD(Real64 PMV)
797 : {
798 : Real64 PPD;
799 1169191 : Real64 expTest1 = -0.03353 * pow_4(PMV) - 0.2179 * pow_2(PMV);
800 1169191 : if (expTest1 > DataPrecisionGlobals::EXP_LowerLimit) {
801 1161492 : PPD = 100.0 - 95.0 * std::exp(expTest1);
802 : } else {
803 7699 : PPD = 100.0;
804 : }
805 :
806 1169191 : if (PPD < 0.0) {
807 0 : PPD = 0.0;
808 1169191 : } else if (PPD > 100.0) {
809 0 : PPD = 100.0;
810 : }
811 1169191 : return PPD;
812 : }
813 :
814 384 : Real64 CalcRelativeAirVelocity(Real64 AirVel, Real64 ActMet)
815 : {
816 384 : if (ActMet > 1) {
817 384 : return AirVel + 0.3 * (ActMet - 1);
818 : } else {
819 0 : return AirVel;
820 : }
821 : }
822 :
823 2736 : void GetThermalComfortInputsASHRAE(EnergyPlusData &state)
824 : {
825 2736 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ZonePtr;
826 2736 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
827 : // (var TA)
828 2736 : state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
829 2736 : if (state.dataRoomAir->anyNonMixingRoomAirModel) {
830 0 : if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
831 0 : state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
832 0 : state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum); // PH 3/7/04
833 : }
834 : }
835 : // (var TR)
836 2736 : state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
837 : // (var RH)
838 5472 : state.dataThermalComforts->RelHum =
839 2736 : PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
840 : // Metabolic rate of body (W/m2) (var RM, M)
841 5472 : state.dataThermalComforts->ActLevel =
842 2736 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ActivityLevelPtr) / BodySurfAreaPierce;
843 : // Energy consumption by external work (W/m2) (var WME)
844 5472 : state.dataThermalComforts->WorkEff =
845 2736 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).WorkEffPtr) *
846 2736 : state.dataThermalComforts->ActLevel;
847 :
848 : // Clothing unit (var CLO)
849 : Real64 IntermediateClothing;
850 2736 : switch (state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).clothingType) {
851 2688 : case DataHeatBalance::ClothingType::InsulationSchedule:
852 5376 : state.dataThermalComforts->CloUnit =
853 2688 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
854 2688 : break;
855 48 : case DataHeatBalance::ClothingType::DynamicAshrae55:
856 48 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
857 48 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
858 48 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue = state.dataThermalComforts->CloUnit;
859 48 : DynamicClothingModel(state);
860 48 : state.dataThermalComforts->CloUnit = state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue;
861 48 : break;
862 0 : case DataHeatBalance::ClothingType::CalculationSchedule:
863 0 : IntermediateClothing = GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingMethodPtr);
864 0 : if (IntermediateClothing == 1.0) {
865 0 : state.dataThermalComforts->CloUnit =
866 0 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
867 0 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
868 0 : state.dataThermalComforts->CloUnit;
869 0 : } else if (IntermediateClothing == 2.0) {
870 0 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
871 0 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
872 0 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
873 0 : state.dataThermalComforts->CloUnit;
874 0 : DynamicClothingModel(state);
875 0 : state.dataThermalComforts->CloUnit =
876 0 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue;
877 : } else {
878 0 : state.dataThermalComforts->CloUnit =
879 0 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
880 0 : ShowWarningError(state, "Scheduled clothing value will be used rather than clothing calculation method.");
881 : }
882 0 : break;
883 0 : default:
884 0 : ShowSevereError(state, "Incorrect Clothing Type");
885 : }
886 : // (var VEL)
887 5472 : state.dataThermalComforts->AirVel =
888 2736 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AirVelocityPtr);
889 : // (var MET)
890 2736 : state.dataThermalComforts->ActMet = state.dataThermalComforts->ActLevel / ActLevelConv;
891 2736 : }
892 :
893 5424 : Real64 CalcStandardEffectiveTemp(
894 : EnergyPlusData &state, Real64 AirTemp, Real64 RadTemp, Real64 RelHum, Real64 AirVel, Real64 ActMet, Real64 CloUnit, Real64 WorkEff)
895 : {
896 :
897 : // Thermal const
898 5424 : constexpr Real64 CloFac(0.25); // Clothing factor determined experimentally (var KCLO)
899 5424 : constexpr Real64 BodyWeight(69.9); // (var BODYWEIGHT)
900 5424 : constexpr Real64 SweatContConst(170.0); // Proportionality constant for sweat control; g/m2.hr (var CSW)
901 5424 : constexpr Real64 DriCoeffVasodilation(120); // driving coefficient for vasodilation (var CDIL)
902 5424 : constexpr Real64 DriCoeffVasoconstriction(0.5); // (var CSTR)
903 5424 : constexpr Real64 MaxSkinBloodFlow(90.0); // Max. value of skin blood flow
904 5424 : constexpr Real64 MinSkinBloodFlow(0.5); // Min. value of skin blood flow
905 5424 : constexpr Real64 RegSweatMax(500); // Max. value of regulatory sweating; w/m2
906 :
907 : // Standard condition const
908 : // Definition of vascular control signals CoreTempSet, SkinTempSet, and AvgBodyTempSet are the setpoints for core, skin and
909 : // average body temperatures corresponding to physiol. neutrality SkinMassRatSet is the ratio of skin mass to total body mass (skin+core)
910 : // Typical values for CoreTempSet, SkinTempSet and SkinMassRatSet are 36.8, 33.7 and 0.10 SkinMassRat is the actual skin to total body mass
911 : // ratio
912 5424 : constexpr Real64 SkinTempSet(33.7); // (var TempSkinNeutral)
913 5424 : constexpr Real64 CoreTempSet(36.8); // (var TempCoreNeutral)
914 5424 : constexpr Real64 SkinBloodFlowSet(6.3); // (var SkinBloodFlowNeutral)
915 5424 : constexpr Real64 SkinMassRatSet(0.1); // (var ALFA)
916 :
917 5424 : if (AirVel < 0.1) AirVel = 0.1;
918 :
919 : // (var VaporPressure)
920 5424 : state.dataThermalComforts->VapPress = RelHum * CalcSatVapPressFromTempTorr(AirTemp);
921 5424 : Real64 ActLevel = ActLevelConv * ActMet;
922 5424 : state.dataThermalComforts->IntHeatProd = ActLevel - WorkEff;
923 :
924 : // Step 1: CALCULATE VARIABLESS THAT REMAIN CONSTANT FOR AN HOUR
925 5424 : Real64 PInAtmospheres = state.dataEnvrn->OutBaroPress / 101325;
926 5424 : Real64 RClo = CloUnit * 0.155; // (var RCL)
927 5424 : Real64 TotCloFac = 1.0 + 0.15 * CloUnit;
928 5424 : Real64 LewisRatio = 2.2 / PInAtmospheres; // Lewis Relation is 2.2 at sea level, 25C (var LR)
929 : Real64 EvapEff; // evaporative efficiency
930 :
931 : // APPROXIMATE THE FOLLOWING VALUES TO START
932 5424 : state.dataThermalComforts->SkinTemp = SkinTempSet;
933 5424 : state.dataThermalComforts->CoreTemp = CoreTempSet;
934 5424 : Real64 SkinBloodFlow = SkinBloodFlowSet;
935 5424 : Real64 SkinMassRat = SkinMassRatSet;
936 :
937 : // Mass transfer equation between skin and environment
938 : // CloInsul is efficiency of mass transfer for CloUnit.
939 5424 : if (CloUnit <= 0) {
940 0 : EvapEff = 0.38 * std::pow(AirVel, -0.29); // (var WCRIT)
941 0 : state.dataThermalComforts->CloInsul = 1.0;
942 : } else {
943 5424 : EvapEff = 0.59 * std::pow(AirVel, -0.08); // (var ICL)
944 5424 : state.dataThermalComforts->CloInsul = 0.45;
945 : }
946 :
947 5424 : Real64 CorrectedHC = 3.0 * std::pow(PInAtmospheres, 0.53); // corrected convective heat transfer coefficient
948 5424 : Real64 ForcedHC = 8.600001 * std::pow((AirVel * PInAtmospheres), 0.53); // forced convective heat transfer coefficient, W/(m2 °C) (CHCV)
949 5424 : state.dataThermalComforts->Hc = std::max(CorrectedHC, ForcedHC); // (CHC)
950 5424 : state.dataThermalComforts->Hr = 4.7; // (CHR)
951 5424 : state.dataThermalComforts->EvapHeatLoss = 0.1 * ActMet;
952 5424 : Real64 RAir = 1.0 / (TotCloFac * (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr)); // resistance of air layer to dry heat (RA)
953 5424 : state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hr * RadTemp + state.dataThermalComforts->Hc * AirTemp) /
954 5424 : (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr); // operative temperature (TOP)
955 5424 : Real64 ActLevelStart = ActLevel; // ActLevel gets increased by shivering in the following
956 5424 : Real64 AvgBodyTempSet = SkinMassRatSet * SkinTempSet + (1.0 - SkinMassRatSet) * CoreTempSet; // (var TempBodyNeutral)
957 :
958 : // Step 2: BEGIN MINUTE BY MINUTE CALCULATIONS FOR ONE HOUR SIMULATION OF TEMPERATURE REGULATION.
959 : // This section simulates the temperature regulation over 1 minute.
960 : // Inputs are the physiological data from the previous time step and the current environmental conditions. Loop and must be increased from the
961 : // start level, not perpetually increased
962 330864 : for (int IterMin = 1; IterMin <= 60; ++IterMin) {
963 : // Dry heat balance: solve for CloSurfTemp and Hr, GUESS CloSurfTemp TO START
964 650880 : state.dataThermalComforts->CloSurfTemp =
965 325440 : (RAir * state.dataThermalComforts->SkinTemp + RClo * state.dataThermalComforts->OpTemp) / (RAir + RClo);
966 325440 : bool converged = false;
967 661595 : while (!converged) {
968 672310 : state.dataThermalComforts->Hr =
969 336155 : 4.0 * StefanBoltz * std::pow((state.dataThermalComforts->CloSurfTemp + RadTemp) / 2.0 + 273.15, 3) * 0.72;
970 336155 : RAir = 1.0 / (TotCloFac * (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr));
971 336155 : state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hr * RadTemp + state.dataThermalComforts->Hc * AirTemp) /
972 336155 : (state.dataThermalComforts->Hc + state.dataThermalComforts->Hr);
973 336155 : Real64 CloSurfTempNew = (RAir * state.dataThermalComforts->SkinTemp + RClo * state.dataThermalComforts->OpTemp) / (RAir + RClo);
974 336155 : if (std::abs(CloSurfTempNew - state.dataThermalComforts->CloSurfTemp) <= 0.01) {
975 325440 : converged = true;
976 : }
977 336155 : state.dataThermalComforts->CloSurfTemp = CloSurfTempNew;
978 : }
979 :
980 : // CALCULATE THE COMBINED HEAT TRANSFER COEFF. (H)
981 325440 : state.dataThermalComforts->H = state.dataThermalComforts->Hr + state.dataThermalComforts->Hc;
982 : // Heat flow from Clothing surface to environment
983 325440 : state.dataThermalComforts->DryHeatLoss = (state.dataThermalComforts->SkinTemp - state.dataThermalComforts->OpTemp) / (RAir + RClo);
984 :
985 : // dry and latent respiratory heat losses
986 650880 : state.dataThermalComforts->LatRespHeatLoss =
987 325440 : 0.0023 * ActLevel * (44.0 - state.dataThermalComforts->VapPress); // latent heat loss due to respiration
988 325440 : state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevel * (34.0 - AirTemp);
989 :
990 325440 : state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
991 :
992 : // Heat flows to skin and core: 5.28 is skin conductance in the absence of skin blood flow
993 650880 : state.dataThermalComforts->HeatFlow =
994 325440 : (state.dataThermalComforts->CoreTemp - state.dataThermalComforts->SkinTemp) * (5.28 + 1.163 * SkinBloodFlow);
995 :
996 325440 : Real64 CoreHeatStorage = ActLevel - state.dataThermalComforts->HeatFlow - state.dataThermalComforts->RespHeatLoss -
997 325440 : WorkEff; // rate of energy storage in the core
998 325440 : Real64 SkinHeatStorage = state.dataThermalComforts->HeatFlow - state.dataThermalComforts->DryHeatLoss -
999 325440 : state.dataThermalComforts->EvapHeatLoss; // rate of energy storage in the skin
1000 :
1001 : // Thermal capacities
1002 325440 : state.dataThermalComforts->CoreThermCap = 0.97 * (1 - SkinMassRat) * BodyWeight;
1003 325440 : state.dataThermalComforts->SkinThermCap = 0.97 * SkinMassRat * BodyWeight;
1004 :
1005 : // Temperature changes in 1 minute
1006 325440 : state.dataThermalComforts->CoreTempChange = (CoreHeatStorage * BodySurfAreaPierce / (state.dataThermalComforts->CoreThermCap * 60.0));
1007 325440 : state.dataThermalComforts->SkinTempChange = (SkinHeatStorage * BodySurfAreaPierce) / (state.dataThermalComforts->SkinThermCap * 60.0);
1008 :
1009 325440 : state.dataThermalComforts->CoreTemp += state.dataThermalComforts->CoreTempChange;
1010 325440 : state.dataThermalComforts->SkinTemp += state.dataThermalComforts->SkinTempChange;
1011 650880 : state.dataThermalComforts->AvgBodyTemp =
1012 325440 : SkinMassRat * state.dataThermalComforts->SkinTemp + (1.0 - SkinMassRat) * state.dataThermalComforts->CoreTemp;
1013 :
1014 : Real64 SkinThermSigWarm; // vasodialtion signal (WARMS)
1015 : Real64 SkinThermSigCold; // vasoconstriction signal
1016 325440 : Real64 SkinSignal = state.dataThermalComforts->SkinTemp - SkinTempSet; // thermoregulatory control signal from the skin
1017 325440 : if (SkinSignal > 0) {
1018 130449 : SkinThermSigWarm = SkinSignal;
1019 130449 : SkinThermSigCold = 0.0;
1020 : } else {
1021 194991 : SkinThermSigCold = -SkinSignal;
1022 194991 : SkinThermSigWarm = 0.0;
1023 : }
1024 :
1025 : Real64 CoreThermSigWarm; // vasodialtion signal (WARMC)
1026 : Real64 CoreThermSigCold; // vasoconstriction signal
1027 325440 : Real64 CoreSignal = state.dataThermalComforts->CoreTemp - CoreTempSet; // thermoregulatory control signal from the skin, °C
1028 325440 : if (CoreSignal > 0) {
1029 214784 : CoreThermSigWarm = CoreSignal;
1030 214784 : CoreThermSigCold = 0.0;
1031 : } else {
1032 110656 : CoreThermSigCold = -CoreSignal;
1033 110656 : CoreThermSigWarm = 0.0;
1034 : }
1035 :
1036 : Real64 BodyThermSigWarm; // WARMB
1037 325440 : Real64 BodySignal = state.dataThermalComforts->AvgBodyTemp - AvgBodyTempSet;
1038 :
1039 325440 : if (BodySignal > 0) {
1040 129060 : BodyThermSigWarm = BodySignal;
1041 : } else {
1042 196380 : BodyThermSigWarm = 0.0;
1043 : }
1044 :
1045 325440 : state.dataThermalComforts->VasodilationFac = DriCoeffVasodilation * CoreThermSigWarm;
1046 325440 : state.dataThermalComforts->VasoconstrictFac = DriCoeffVasoconstriction * SkinThermSigCold;
1047 325440 : SkinBloodFlow = (SkinBloodFlowSet + state.dataThermalComforts->VasodilationFac) / (1.0 + state.dataThermalComforts->VasoconstrictFac);
1048 : // SkinBloodFlow is never below 0.5 liter/(m2.hr) nor above 90 liter/(m2.hr)
1049 325440 : if (SkinBloodFlow < MinSkinBloodFlow) SkinBloodFlow = MinSkinBloodFlow;
1050 325440 : if (SkinBloodFlow > MaxSkinBloodFlow) SkinBloodFlow = MaxSkinBloodFlow;
1051 325440 : SkinMassRat = 0.0417737 + 0.7451832 / (SkinBloodFlow + 0.585417); // ratio of skin-core masses change with SkinBloodFlow
1052 :
1053 325440 : Real64 RegSweat = SweatContConst * BodyThermSigWarm * std::exp(SkinThermSigWarm / 10.7); // control of regulatory sweating
1054 325440 : if (RegSweat > RegSweatMax) RegSweat = RegSweatMax;
1055 325440 : state.dataThermalComforts->EvapHeatLossRegSweat = 0.68 * RegSweat; // heat lost by vaporization sweat
1056 :
1057 : // adjustment of metabolic heat due to shivering (Stolwijk, Hardy)
1058 325440 : state.dataThermalComforts->ShivResponse = 19.4 * SkinThermSigCold * CoreThermSigCold;
1059 325440 : ActLevel = ActLevelStart + state.dataThermalComforts->ShivResponse;
1060 :
1061 : // Evaluation of heat transfer by evaporation at skin surface
1062 325440 : Real64 AirEvapHeatResist = 1.0 / (LewisRatio * TotCloFac * state.dataThermalComforts->Hc); // evaporative resistance air layer
1063 325440 : Real64 CloEvapHeatResist = RClo / (LewisRatio * state.dataThermalComforts->CloInsul);
1064 325440 : Real64 TotEvapHeatResist = AirEvapHeatResist + CloEvapHeatResist;
1065 325440 : state.dataThermalComforts->SatSkinVapPress = CalcSatVapPressFromTempTorr(state.dataThermalComforts->SkinTemp); // PSSK
1066 650880 : state.dataThermalComforts->EvapHeatLossMax =
1067 325440 : (state.dataThermalComforts->SatSkinVapPress - state.dataThermalComforts->VapPress) / TotEvapHeatResist; // TotEvapHeatResist;
1068 650880 : state.dataThermalComforts->SkinWetSweat =
1069 325440 : state.dataThermalComforts->EvapHeatLossRegSweat /
1070 325440 : state.dataThermalComforts->EvapHeatLossMax; // ratio heat loss sweating to max heat loss sweating
1071 :
1072 650880 : state.dataThermalComforts->SkinWetDiff =
1073 325440 : (1.0 - state.dataThermalComforts->SkinWetSweat) * 0.06; // 0.06 if SkinWetDiff for nonsweating skin --- Kerslake
1074 325440 : state.dataThermalComforts->EvapHeatLossDiff = state.dataThermalComforts->SkinWetDiff * state.dataThermalComforts->EvapHeatLossMax;
1075 325440 : state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossRegSweat + state.dataThermalComforts->EvapHeatLossDiff;
1076 325440 : state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
1077 :
1078 : // Beginning of dripping (Sweat not evaporated on skin surface)
1079 325440 : if (state.dataThermalComforts->SkinWetTot >= EvapEff) {
1080 82551 : state.dataThermalComforts->SkinWetTot = EvapEff;
1081 82551 : state.dataThermalComforts->SkinWetSweat = EvapEff / 0.94;
1082 165102 : state.dataThermalComforts->EvapHeatLossRegSweat =
1083 82551 : state.dataThermalComforts->SkinWetSweat * state.dataThermalComforts->EvapHeatLossMax;
1084 82551 : state.dataThermalComforts->SkinWetDiff = (1.0 - state.dataThermalComforts->SkinWetSweat) * 0.06;
1085 82551 : state.dataThermalComforts->EvapHeatLossDiff = state.dataThermalComforts->SkinWetDiff * state.dataThermalComforts->EvapHeatLossMax;
1086 82551 : state.dataThermalComforts->EvapHeatLoss =
1087 82551 : state.dataThermalComforts->EvapHeatLossRegSweat + state.dataThermalComforts->EvapHeatLossDiff;
1088 : }
1089 :
1090 : // When EvapHeatLossMax<0. condensation on skin occurs.
1091 325440 : if (state.dataThermalComforts->EvapHeatLossMax < 0.0) {
1092 18882 : state.dataThermalComforts->SkinWetDiff = 0.0;
1093 18882 : state.dataThermalComforts->EvapHeatLossDiff = 0.0;
1094 18882 : state.dataThermalComforts->EvapHeatLoss = 0.0;
1095 18882 : state.dataThermalComforts->SkinWetTot = EvapEff;
1096 18882 : state.dataThermalComforts->SkinWetSweat = EvapEff;
1097 18882 : state.dataThermalComforts->EvapHeatLossRegSweat = 0.0;
1098 : }
1099 : // Vapor pressure at skin (as measured by dewpoint sensors)
1100 650880 : state.dataThermalComforts->SkinVapPress = state.dataThermalComforts->SkinWetTot * state.dataThermalComforts->SatSkinVapPress +
1101 325440 : (1.0 - state.dataThermalComforts->SkinWetTot) * state.dataThermalComforts->VapPress;
1102 : } // END OF MINUTE BY MINUTE TEMPERATURE REGULATION LOOP
1103 :
1104 : // EvapHeatLossMax is readjusted for EvapEff
1105 5424 : state.dataThermalComforts->EvapHeatLossMax *= EvapEff;
1106 :
1107 : // Step 3: Heat transfer indices in real environment. Computation of comfort indices.
1108 : // Inputs to this SECTION are the physiological data from the simulation of temperature regulation loop.
1109 5424 : Real64 EffectSkinHeatLoss = state.dataThermalComforts->DryHeatLoss + state.dataThermalComforts->EvapHeatLoss;
1110 : // ET*(standardization humidity/REAL(r64) CloUnit, StdAtm and Hc)
1111 5424 : state.dataThermalComforts->CloBodyRat = 1.0 + CloFac * CloUnit;
1112 : Real64 EffectCloUnit =
1113 5424 : CloUnit - (state.dataThermalComforts->CloBodyRat - 1.0) / (0.155 * state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->H);
1114 5424 : Real64 EffectCloThermEff = 1.0 / (1.0 + 0.155 * state.dataThermalComforts->Hc * EffectCloUnit);
1115 10848 : state.dataThermalComforts->CloPermeatEff =
1116 5424 : 1.0 / (1.0 + (0.155 / state.dataThermalComforts->CloInsul) * state.dataThermalComforts->Hc * EffectCloUnit);
1117 : // Get a low approximation for ET* and solve balance equation by iteration
1118 5424 : Real64 ET = state.dataThermalComforts->SkinTemp - EffectSkinHeatLoss / (state.dataThermalComforts->H * EffectCloThermEff);
1119 : Real64 EnergyBalErrET;
1120 : while (true) {
1121 258551 : Real64 StdVapPressET = CalcSatVapPressFromTempTorr(ET); // THE STANDARD VAPOR PRESSURE AT THE EFFECTIVE TEMP : StdVapPressET
1122 258551 : EnergyBalErrET = EffectSkinHeatLoss - state.dataThermalComforts->H * EffectCloThermEff * (state.dataThermalComforts->SkinTemp - ET) -
1123 258551 : state.dataThermalComforts->SkinWetTot * LewisRatio * state.dataThermalComforts->Hc *
1124 258551 : state.dataThermalComforts->CloPermeatEff * (state.dataThermalComforts->SatSkinVapPress - StdVapPressET / 2.0);
1125 258551 : if (EnergyBalErrET >= 0.0) break;
1126 253127 : ET += 0.1;
1127 253127 : }
1128 5424 : state.dataThermalComforts->EffTemp = ET;
1129 :
1130 : // Standard effective temperature SET* standardized humidity. Hc, CloUnit, StdAtm normalized for given ActLel AirVel
1131 : // Standard environment
1132 5424 : Real64 StdHr = state.dataThermalComforts->Hr;
1133 : Real64 StdHc; // standard conv. heat tr. coeff. (level walking/still air)
1134 5424 : if (ActMet <= 0.85) {
1135 0 : StdHc = 3.0; // minimum value of Hc at sea leAirVel = 3.0 (AirVel = .137 m/s)
1136 : } else {
1137 5424 : StdHc = 5.66 * std::pow(ActMet - 0.85, 0.39);
1138 : }
1139 5424 : if (StdHc <= 3.0) StdHc = 3.0;
1140 5424 : Real64 StdH = StdHc + StdHr; // StdH Standard combined heat transfer coefficient
1141 : // standard MET - StdCloUnit relation gives SET* = 24 C when PMV = 0
1142 5424 : Real64 StdCloUnit = 1.52 / (ActMet - WorkEff / ActLevelConv + 0.6944) - 0.1835; // RCLOS
1143 5424 : Real64 StdRClo = 0.155 * StdCloUnit; // RCLS
1144 5424 : Real64 StdCloBodyRat = 1.0 + CloFac * StdCloUnit; // FACLS
1145 5424 : Real64 StdEffectCloThermEff = 1.0 / (1.0 + 0.155 * StdCloBodyRat * StdH * StdCloUnit); // FCLS
1146 5424 : Real64 StdCloInsul = state.dataThermalComforts->CloInsul * StdHc / StdH * (1 - StdEffectCloThermEff) /
1147 5424 : (StdHc / StdH - state.dataThermalComforts->CloInsul * StdEffectCloThermEff);
1148 5424 : Real64 StdREvap = 1.0 / (LewisRatio * StdCloBodyRat * StdHc);
1149 5424 : Real64 StdREvapClo = StdRClo / (LewisRatio * StdCloInsul);
1150 5424 : Real64 StdHEvap = 1.0 / (StdREvap + StdREvapClo);
1151 5424 : Real64 StdRAir = 1.0 / (StdCloBodyRat * StdH);
1152 5424 : Real64 StdHDry = 1.0 / (StdRAir + StdRClo);
1153 :
1154 : // Get a low approximation for SET* and solve balance equ. by iteration
1155 5424 : Real64 StdEffectSkinHeatLoss = state.dataThermalComforts->DryHeatLoss + state.dataThermalComforts->EvapHeatLoss;
1156 5424 : Real64 OldSET = round((state.dataThermalComforts->SkinTemp - StdEffectSkinHeatLoss / StdHDry) * 100) / 100;
1157 5424 : Real64 delta = 0.0001;
1158 5424 : Real64 err = 100.0;
1159 20286 : while (std::abs(err) > 0.01) {
1160 14862 : Real64 StdVapPressSET_1 = CalcSatVapPressFromTempTorr(OldSET); // StdVapPressSET *= VapPressConv;
1161 : Real64 EnergyBalErrSET_1 =
1162 14862 : StdEffectSkinHeatLoss - StdHDry * (state.dataThermalComforts->SkinTemp - OldSET) -
1163 14862 : state.dataThermalComforts->SkinWetTot * StdHEvap * (state.dataThermalComforts->SatSkinVapPress - StdVapPressSET_1 / 2.0);
1164 14862 : Real64 StdVapPressSET_2 = CalcSatVapPressFromTempTorr(OldSET + delta);
1165 : Real64 EnergyBalErrSET_2 =
1166 14862 : StdEffectSkinHeatLoss - StdHDry * (state.dataThermalComforts->SkinTemp - (OldSET + delta)) -
1167 14862 : state.dataThermalComforts->SkinWetTot * StdHEvap * (state.dataThermalComforts->SatSkinVapPress - StdVapPressSET_2 / 2.0);
1168 14862 : Real64 NewSET = OldSET - delta * EnergyBalErrSET_1 / (EnergyBalErrSET_2 - EnergyBalErrSET_1);
1169 14862 : err = NewSET - OldSET;
1170 14862 : OldSET = NewSET;
1171 : }
1172 5424 : Real64 SET = OldSET;
1173 : // PMV*(PMVET in prgm) uses ET instead of OpTemp
1174 5424 : state.dataThermalComforts->DryHeatLossET = StdH * StdEffectCloThermEff * (state.dataThermalComforts->SkinTemp - ET);
1175 : // SPMV*(PMVSET in prgm) uses SET instead of OpTemp
1176 5424 : state.dataThermalComforts->DryHeatLossSET = StdH * StdEffectCloThermEff * (state.dataThermalComforts->SkinTemp - SET);
1177 5424 : return SET;
1178 : }
1179 :
1180 1968 : void CalcThermalComfortPierceASHRAE(EnergyPlusData &state)
1181 : {
1182 : // This subroutine calculates ET, SET, SETPMV, SETPPD using Pierce two-node model.
1183 : // Reference: ANSI/ASHRAE Standard 55-2017 Appendix D.
1184 :
1185 5568 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
1186 3600 : ++state.dataThermalComforts->PeopleNum) {
1187 :
1188 3600 : if (!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Pierce) continue;
1189 :
1190 : // STEP 1: Get input (TA, TR, RH, VEL, CLO, MET, WME)
1191 2352 : GetThermalComfortInputsASHRAE(state);
1192 :
1193 : // STEP 2: Calculate SET.
1194 2352 : Real64 SET = CalcStandardEffectiveTemp(state,
1195 2352 : state.dataThermalComforts->AirTemp,
1196 2352 : state.dataThermalComforts->RadTemp,
1197 2352 : state.dataThermalComforts->RelHum,
1198 2352 : state.dataThermalComforts->AirVel,
1199 2352 : state.dataThermalComforts->ActMet,
1200 2352 : state.dataThermalComforts->CloUnit,
1201 2352 : state.dataThermalComforts->WorkEff);
1202 :
1203 : // STEP 3: Report SET related variables.
1204 : // Fanger's comfort equation. Thermal transfer coefficient to calculate PMV
1205 2352 : state.dataThermalComforts->ThermSensTransCoef = 0.303 * std::exp(-0.036 * state.dataThermalComforts->ActLevel) + 0.028;
1206 : // Fanger's reg. sweating at comfort threshold (PMV=0) is:
1207 2352 : state.dataThermalComforts->EvapHeatLossRegComf = (state.dataThermalComforts->IntHeatProd - ActLevelConv) * 0.42;
1208 2352 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PiercePMVET =
1209 2352 : state.dataThermalComforts->ThermSensTransCoef *
1210 2352 : (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLossET -
1211 2352 : state.dataThermalComforts->EvapHeatLossDiff - state.dataThermalComforts->EvapHeatLossRegComf);
1212 2352 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PiercePMVSET =
1213 2352 : state.dataThermalComforts->ThermSensTransCoef *
1214 2352 : (state.dataThermalComforts->IntHeatProd - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->DryHeatLossSET -
1215 2352 : state.dataThermalComforts->EvapHeatLossDiff - state.dataThermalComforts->EvapHeatLossRegComf);
1216 :
1217 : // PHeat stress and heat strain indices derived from EvapHeatLoss, DISC (discomfort) varies with relative thermoregulatory strain
1218 2352 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PierceDISC =
1219 2352 : 5.0 * (state.dataThermalComforts->EvapHeatLossRegSweat - state.dataThermalComforts->EvapHeatLossRegComf) /
1220 2352 : (state.dataThermalComforts->EvapHeatLossMax - state.dataThermalComforts->EvapHeatLossRegComf -
1221 2352 : state.dataThermalComforts->EvapHeatLossDiff);
1222 :
1223 : // Thermal sensation TSENS as function of mean body temp.-
1224 : // AvgBodyTempLow is AvgBodyTemp when DISC is 0. (lower limit of zone of evap. regul.)
1225 2352 : Real64 AvgBodyTempLow = (0.185 / ActLevelConv) * (state.dataThermalComforts->ActLevel - state.dataThermalComforts->WorkEff) + 36.313;
1226 : // AvgBodyTempHigh is AvgBodyTemp when HSI=100 (upper limit of zone of evap. regul.)
1227 2352 : Real64 AvgBodyTempHigh = (0.359 / ActLevelConv) * (state.dataThermalComforts->ActLevel - state.dataThermalComforts->WorkEff) + 36.664;
1228 :
1229 : // TSENS=DISC=4.7 when HSI =1 00 (HSI is Belding's classic heat stress index)
1230 : // In cold, DISC &TSENS are the same and neg. fct of AvgBodyTemp
1231 2352 : if (state.dataThermalComforts->AvgBodyTemp > AvgBodyTempLow) {
1232 888 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PierceTSENS =
1233 888 : 4.7 * (state.dataThermalComforts->AvgBodyTemp - AvgBodyTempLow) / (AvgBodyTempHigh - AvgBodyTempLow);
1234 :
1235 : } else {
1236 1464 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PierceTSENS =
1237 1464 : 0.68175 * (state.dataThermalComforts->AvgBodyTemp - AvgBodyTempLow);
1238 1464 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PierceDISC =
1239 1464 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PierceTSENS;
1240 : }
1241 :
1242 2352 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortMRT =
1243 2352 : state.dataThermalComforts->RadTemp;
1244 2352 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
1245 2352 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
1246 2352 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).PierceSET = SET;
1247 : }
1248 1968 : }
1249 :
1250 192 : void CalcThermalComfortCoolingEffectASH(EnergyPlusData &state)
1251 : {
1252 : // This subroutine calculates ASHRAE Cooling effect adjusted PMV and PPD
1253 : // Reference: ANSI/ASHRAE Standard 55-2017 Appendix D.
1254 :
1255 384 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
1256 192 : ++state.dataThermalComforts->PeopleNum) {
1257 :
1258 192 : if (!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).CoolingEffectASH55) continue;
1259 :
1260 : // Get input (TA, TR, RH, VEL, CLO, MET, WME)
1261 192 : GetThermalComfortInputsASHRAE(state);
1262 :
1263 : // Calculate elevated air cooling effect using the SET function.
1264 192 : Real64 CoolingEffect = 0;
1265 : Real64 CoolingEffectAdjustedPMV;
1266 192 : CalcCoolingEffectAdjustedPMV(state, CoolingEffect, CoolingEffectAdjustedPMV);
1267 :
1268 : // Report.
1269 192 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).CoolingEffectASH55 = CoolingEffect;
1270 192 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).CoolingEffectAdjustedPMVASH55 =
1271 : CoolingEffectAdjustedPMV;
1272 192 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).CoolingEffectAdjustedPPDASH55 =
1273 192 : CalcFangerPPD(CoolingEffectAdjustedPMV);
1274 : }
1275 192 : }
1276 :
1277 192 : void CalcCoolingEffectAdjustedPMV(EnergyPlusData &state, Real64 &CoolingEffect, Real64 &CoolingEffectAdjustedPMV)
1278 : {
1279 : // Calculate SET without cooling effect.
1280 192 : Real64 RelAirVel = CalcRelativeAirVelocity(state.dataThermalComforts->AirVel, state.dataThermalComforts->ActMet);
1281 192 : Real64 SET = CalcStandardEffectiveTemp(state,
1282 192 : state.dataThermalComforts->AirTemp,
1283 192 : state.dataThermalComforts->RadTemp,
1284 192 : state.dataThermalComforts->RelHum,
1285 : RelAirVel,
1286 192 : state.dataThermalComforts->ActMet,
1287 192 : state.dataThermalComforts->CloUnit,
1288 192 : state.dataThermalComforts->WorkEff);
1289 :
1290 : // TODO - This should use the ASHRAE55-2017 PMV calc program. The current Fanger PMV program are not consistent with the new standard.
1291 192 : Real64 ASHRAE55PMV = CalcFangerPMV(state,
1292 192 : state.dataThermalComforts->AirTemp,
1293 192 : state.dataThermalComforts->RadTemp,
1294 192 : state.dataThermalComforts->RelHum,
1295 : RelAirVel,
1296 192 : state.dataThermalComforts->ActLevel,
1297 192 : state.dataThermalComforts->CloUnit,
1298 192 : state.dataThermalComforts->WorkEff);
1299 :
1300 192 : Real64 StillAirVel = 0.1;
1301 23040 : auto ce_root_function = [&state, &StillAirVel, &SET](Real64 x) {
1302 20160 : return CalcStandardEffectiveTemp(state,
1303 2880 : state.dataThermalComforts->AirTemp - x,
1304 2880 : state.dataThermalComforts->RadTemp - x,
1305 2880 : state.dataThermalComforts->RelHum,
1306 : StillAirVel,
1307 2880 : state.dataThermalComforts->ActMet,
1308 2880 : state.dataThermalComforts->CloUnit,
1309 2880 : state.dataThermalComforts->WorkEff) -
1310 2880 : SET;
1311 192 : };
1312 :
1313 2688 : auto ce_root_termination = [](Real64 min, Real64 max) { return abs(max - min) <= 0.01; };
1314 192 : Real64 lowerBound = 0.0;
1315 192 : Real64 upperBound = 50.0;
1316 :
1317 : try {
1318 192 : std::pair<Real64, Real64> solverResult = boost::math::tools::bisect(ce_root_function, lowerBound, upperBound, ce_root_termination);
1319 192 : CoolingEffect = (solverResult.first + solverResult.second) / 2;
1320 0 : } catch (const std::exception &e) {
1321 0 : ShowRecurringWarningErrorAtEnd(state,
1322 0 : "The cooling effect could not be solved for People=\"" +
1323 0 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name + "\"" +
1324 : "As a result, no cooling effect will be applied to adjust the PMV and PPD results.",
1325 0 : state.dataThermalComforts->CoolingEffectWarningInd);
1326 0 : CoolingEffect = 0;
1327 0 : }
1328 :
1329 192 : if (CoolingEffect > 0) {
1330 192 : CoolingEffectAdjustedPMV = CalcFangerPMV(state,
1331 192 : state.dataThermalComforts->AirTemp - CoolingEffect,
1332 192 : state.dataThermalComforts->RadTemp - CoolingEffect,
1333 192 : state.dataThermalComforts->RelHum,
1334 : StillAirVel,
1335 192 : state.dataThermalComforts->ActLevel,
1336 192 : state.dataThermalComforts->CloUnit,
1337 192 : state.dataThermalComforts->WorkEff);
1338 : } else {
1339 0 : CoolingEffectAdjustedPMV = ASHRAE55PMV;
1340 : }
1341 192 : }
1342 :
1343 192 : void CalcThermalComfortAnkleDraftASH(EnergyPlusData &state)
1344 : {
1345 : // This subroutine calculates ASHRAE Ankle draft PPD
1346 : // Reference: ANSI/ASHRAE Standard 55-2017 Appendix I.
1347 :
1348 384 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
1349 192 : ++state.dataThermalComforts->PeopleNum) {
1350 :
1351 192 : if (!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AnkleDraftASH55) continue;
1352 :
1353 192 : GetThermalComfortInputsASHRAE(state);
1354 192 : Real64 RelAirVel = CalcRelativeAirVelocity(state.dataThermalComforts->AirVel, state.dataThermalComforts->ActMet);
1355 192 : Real64 PPD_AD = -1.0;
1356 192 : if (state.dataThermalComforts->ActMet < 1.3 && state.dataThermalComforts->CloUnit < 0.7 && RelAirVel < 0.2) {
1357 : Real64 AnkleAirVel =
1358 96 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AnkleAirVelocityPtr);
1359 96 : Real64 PMV = CalcFangerPMV(state,
1360 96 : state.dataThermalComforts->AirTemp,
1361 96 : state.dataThermalComforts->RadTemp,
1362 96 : state.dataThermalComforts->RelHum,
1363 : RelAirVel,
1364 96 : state.dataThermalComforts->ActLevel,
1365 96 : state.dataThermalComforts->CloUnit,
1366 96 : state.dataThermalComforts->WorkEff);
1367 96 : PPD_AD = (std::exp(-2.58 + 3.05 * AnkleAirVel - 1.06 * PMV) / (1 + std::exp(-2.58 + 3.05 * AnkleAirVel - 1.06 * PMV))) * 100.0;
1368 :
1369 : } else {
1370 96 : if (state.dataGlobal->DisplayExtraWarnings) {
1371 0 : if (RelAirVel >= 0.2) {
1372 0 : ShowRecurringWarningErrorAtEnd(
1373 : state,
1374 : "Relative air velocity is above 0.2 m/s in Ankle draft PPD calculations. PPD at ankle draft will be set to -1.0.",
1375 0 : state.dataThermalComforts->AnkleDraftAirVelWarningInd,
1376 : RelAirVel,
1377 : RelAirVel,
1378 : _,
1379 : "[m/s]",
1380 : "[m/s]");
1381 : }
1382 0 : if (state.dataThermalComforts->ActMet >= 1.3) {
1383 0 : ShowRecurringWarningErrorAtEnd(
1384 : state,
1385 : "Metabolic rate is above 1.3 met in Ankle draft PPD calculations. PPD at ankle draft will be set to -1.0.",
1386 0 : state.dataThermalComforts->AnkleDraftActMetWarningInd,
1387 0 : state.dataThermalComforts->ActMet,
1388 0 : state.dataThermalComforts->ActMet,
1389 : _,
1390 : "[m/s]",
1391 : "[m/s]");
1392 : }
1393 0 : if (state.dataThermalComforts->CloUnit >= 0.7) {
1394 0 : ShowRecurringWarningErrorAtEnd(
1395 : state,
1396 : "Clothing unit is above 0.7 in Ankle draft PPD calculations. PPD at ankle draft will be set to -1.0.",
1397 0 : state.dataThermalComforts->AnkleDraftCloUnitWarningInd,
1398 0 : state.dataThermalComforts->CloUnit,
1399 0 : state.dataThermalComforts->CloUnit,
1400 : _,
1401 : "[m/s]",
1402 : "[m/s]");
1403 : }
1404 : }
1405 : }
1406 192 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).AnkleDraftPPDASH55 = PPD_AD;
1407 : }
1408 192 : }
1409 :
1410 624 : void CalcThermalComfortKSU(EnergyPlusData &state)
1411 : {
1412 :
1413 : // SUBROUTINE INFORMATION:
1414 : // AUTHOR Jaewook Lee
1415 : // DATE WRITTEN January 2000
1416 : // MODIFIED Rick Strand (for E+ implementation February 2000)
1417 :
1418 : // PURPOSE OF THIS SUBROUTINE:
1419 : // This subroutine calculates TSV using the KSU 2 Node model.
1420 :
1421 : // METHODOLOGY EMPLOYED:
1422 : // This subroutine is based heavily upon the work performed by Dan Maloney for
1423 : // the BLAST program. Many of the equations are based on the original Pierce
1424 : // development. See documentation for further details and references.
1425 :
1426 : // REFERENCES:
1427 : // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
1428 :
1429 : // SUBROUTINE PARAMETER DEFINITIONS:
1430 624 : Real64 constexpr CloEmiss(0.8); // Clothing Emissivity
1431 :
1432 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1433 : Real64 BodyWt; // Weight of body, kg
1434 : Real64 DayNum; // Number of days of acclimation
1435 : int NumDay; // Loop counter for DayNum
1436 : Real64 EmissAvg; // Average emissivity
1437 : int IncreDayNum; // Number of days of increment in the outputs as desired
1438 : Real64 IntHeatProdMet; // Internal heat production in MET
1439 : Real64 IntHeatProdMetMax; // Maximum value of internal heat production in MET
1440 : int LastDayNum; // Number of days for the last print out
1441 : Real64 SkinWetFac; // Skin wettedness factor
1442 : Real64 SkinWetNeut; // Skin wettedness at neutral state
1443 : int StartDayNum; // Number of days for the first print out
1444 : // Unacclimated man = 1, Acclimated man = 14
1445 : Real64 SweatSuppFac; // Sweat suppression factor due to skin wettedness
1446 : Real64 TempDiffer; // Temperature difference between the rectal and esophageal temperatures
1447 : // If not measured, set it to be 0.5 Deg. C.
1448 : int TempIndiceNum; // Number of temperature indices
1449 : Real64 ThermCndctMin; // Minimum value of thermal conductance
1450 : Real64 ThermCndctNeut; // Thermal conductance at neutral state
1451 : Real64 TimeExpos; // Time period in the exposure, hr
1452 : Real64 TimeInterval; // Time interval of outputs desired, hr
1453 : Real64 TSVMax; // Maximum value of thermal sensation vote
1454 : Real64 IntermediateClothing;
1455 :
1456 624 : TempIndiceNum = 2;
1457 :
1458 : // NEXT GROUP OF VARIABLE ARE FIXED FOR BLAST PROGRAM - UNACCLIMATED MAN
1459 : // THE TSV MODEL CAN BE APPLIED TO UNACCLIMATED MAN ONLY.
1460 624 : TimeInterval = 1.0;
1461 624 : TSVMax = 4.0;
1462 624 : StartDayNum = 1;
1463 624 : LastDayNum = 1;
1464 624 : IncreDayNum = 1;
1465 624 : TimeExpos = 1.0;
1466 624 : TempDiffer = 0.5;
1467 :
1468 2496 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
1469 1872 : ++state.dataThermalComforts->PeopleNum) {
1470 : // THE NEXT SIX VARIABLES WILL BE READ IN FROM INPUT DECK
1471 1872 : if (!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).KSU) continue;
1472 :
1473 1248 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ZonePtr;
1474 1248 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum);
1475 :
1476 1248 : state.dataThermalComforts->AirTemp = thisZoneHB.ZTAVComf;
1477 1248 : if (state.dataRoomAir->anyNonMixingRoomAirModel) {
1478 0 : if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
1479 0 : state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
1480 0 : state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum); // PH 3/7/04
1481 : }
1482 : }
1483 1248 : state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
1484 2496 : state.dataThermalComforts->RelHum =
1485 1248 : PsyRhFnTdbWPb(state, state.dataThermalComforts->AirTemp, thisZoneHB.airHumRatAvgComf, state.dataEnvrn->OutBaroPress);
1486 2496 : state.dataThermalComforts->ActLevel =
1487 1248 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ActivityLevelPtr) / BodySurfArea;
1488 2496 : state.dataThermalComforts->WorkEff =
1489 1248 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).WorkEffPtr) *
1490 1248 : state.dataThermalComforts->ActLevel;
1491 :
1492 1248 : switch (state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).clothingType) {
1493 1152 : case DataHeatBalance::ClothingType::InsulationSchedule:
1494 2304 : state.dataThermalComforts->CloUnit =
1495 1152 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
1496 1152 : break;
1497 48 : case DataHeatBalance::ClothingType::DynamicAshrae55:
1498 48 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
1499 48 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
1500 48 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
1501 48 : state.dataThermalComforts->CloUnit;
1502 48 : DynamicClothingModel(state);
1503 96 : state.dataThermalComforts->CloUnit =
1504 48 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue;
1505 48 : break;
1506 48 : case DataHeatBalance::ClothingType::CalculationSchedule:
1507 : IntermediateClothing =
1508 48 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingMethodPtr);
1509 48 : if (IntermediateClothing == 1.0) {
1510 24 : state.dataThermalComforts->CloUnit =
1511 12 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
1512 12 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
1513 12 : state.dataThermalComforts->CloUnit;
1514 36 : } else if (IntermediateClothing == 2.0) {
1515 36 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
1516 36 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
1517 36 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
1518 36 : state.dataThermalComforts->CloUnit;
1519 36 : DynamicClothingModel(state);
1520 36 : state.dataThermalComforts->CloUnit =
1521 36 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue;
1522 : } else {
1523 0 : state.dataThermalComforts->CloUnit =
1524 0 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ClothingPtr);
1525 0 : ShowWarningError(state,
1526 0 : format("PEOPLE=\"{}\", Scheduled clothing value will be used rather than clothing calculation method.",
1527 0 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name));
1528 : }
1529 48 : break;
1530 0 : default:
1531 0 : ShowSevereError(
1532 0 : state, format("PEOPLE=\"{}\", Incorrect Clothing Type", state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).Name));
1533 : }
1534 :
1535 2496 : state.dataThermalComforts->AirVel =
1536 1248 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AirVelocityPtr);
1537 1248 : state.dataThermalComforts->IntHeatProd = state.dataThermalComforts->ActLevel - state.dataThermalComforts->WorkEff;
1538 : // THE FOLLOWING ARE TYPICAL VALUES SET FOR BLAST RUNS
1539 : // STANDARD MAN: 70. KG WEIGHT, 1.8 M2 SURFACE AREA
1540 1248 : BodyWt = 70.0;
1541 1248 : state.dataThermalComforts->CoreTemp = 37.0;
1542 1248 : state.dataThermalComforts->SkinTemp = 31.0;
1543 :
1544 : // CALCULATIONS NEEDED FOR THE PASSIVE STATE EQUATIONS
1545 1248 : state.dataThermalComforts->CoreThermCap = 0.9 * BodyWt * 0.97 / BodySurfArea;
1546 1248 : state.dataThermalComforts->SkinThermCap = 0.1 * BodyWt * 0.97 / BodySurfArea;
1547 : // KERSLAKE'S FORMULA (0.05<AirVel<5. M/S)
1548 1248 : if (state.dataThermalComforts->AirVel < 0.137) state.dataThermalComforts->AirVel = 0.137;
1549 1248 : state.dataThermalComforts->Hc = 8.3 * std::sqrt(state.dataThermalComforts->AirVel);
1550 1248 : EmissAvg = RadSurfEff * CloEmiss + (1.0 - RadSurfEff) * 1.0;
1551 : // IBERALL EQUATION
1552 1248 : state.dataThermalComforts->Hr = EmissAvg * (3.87 + 0.031 * state.dataThermalComforts->RadTemp);
1553 1248 : state.dataThermalComforts->H = state.dataThermalComforts->Hr + state.dataThermalComforts->Hc;
1554 1248 : state.dataThermalComforts->OpTemp = (state.dataThermalComforts->Hc * state.dataThermalComforts->AirTemp +
1555 1248 : state.dataThermalComforts->Hr * state.dataThermalComforts->RadTemp) /
1556 1248 : state.dataThermalComforts->H;
1557 1248 : state.dataThermalComforts->VapPress = CalcSatVapPressFromTemp(state.dataThermalComforts->AirTemp);
1558 1248 : state.dataThermalComforts->VapPress *= state.dataThermalComforts->RelHum;
1559 1248 : state.dataThermalComforts->CloBodyRat = 1.0 + 0.2 * state.dataThermalComforts->CloUnit;
1560 2496 : state.dataThermalComforts->CloThermEff =
1561 1248 : 1.0 / (1.0 + 0.155 * state.dataThermalComforts->H * state.dataThermalComforts->CloBodyRat * state.dataThermalComforts->CloUnit);
1562 1248 : state.dataThermalComforts->CloPermeatEff = 1.0 / (1.0 + 0.143 * state.dataThermalComforts->Hc * state.dataThermalComforts->CloUnit);
1563 : // BASIC INFORMATION FOR THERMAL SENSATION.
1564 1248 : IntHeatProdMet = state.dataThermalComforts->IntHeatProd / ActLevelConv;
1565 1248 : IntHeatProdMetMax = max(1.0, IntHeatProdMet);
1566 1248 : ThermCndctNeut = 12.05 * std::exp(0.2266 * (IntHeatProdMetMax - 1.0));
1567 1248 : SkinWetNeut = 0.02 + 0.4 * (1.0 - std::exp(-0.6 * (IntHeatProdMetMax - 1.0)));
1568 1248 : ThermCndctMin = (ThermCndctNeut - 5.3) * 0.26074074 + 5.3;
1569 1248 : Real64 const ThemCndct_75_fac(1.0 / (75.0 - ThermCndctNeut));
1570 1248 : Real64 const ThemCndct_fac(1.0 / (ThermCndctNeut - ThermCndctMin));
1571 : // CALCULATE THE PHYSIOLOGICAL REACTIONS OF AN UNACCLIMATED
1572 : // MAN (LastDayNum = 1), OR AN ACCLIMATED MAN (LastDayNum = 14, IncreDayNum = 13),
1573 1248 : assert(IncreDayNum > 0); // Autodesk:F2C++ Loop setup assumption
1574 2496 : for (NumDay = StartDayNum; NumDay <= LastDayNum; NumDay += IncreDayNum) {
1575 : // INITIAL CONDITIONS IN AN EXPOSURE
1576 1248 : DayNum = double(NumDay);
1577 1248 : state.dataThermalComforts->Time = 0.0;
1578 1248 : state.dataThermalComforts->TimeChange = 0.01;
1579 1248 : SweatSuppFac = 1.0;
1580 1248 : state.dataThermalComforts->Temp(1) = state.dataThermalComforts->CoreTemp;
1581 1248 : state.dataThermalComforts->Temp(2) = state.dataThermalComforts->SkinTemp;
1582 1248 : state.dataThermalComforts->Coeff(1) = state.dataThermalComforts->Coeff(2) = 0.0;
1583 : // PHYSIOLOGICAL ADJUSTMENTS IN HEAT ACCLIMATION.
1584 1248 : state.dataThermalComforts->AcclPattern = 1.0 - std::exp(-0.12 * (DayNum - 1.0));
1585 1248 : state.dataThermalComforts->CoreTempNeut = 36.9 - 0.6 * state.dataThermalComforts->AcclPattern;
1586 1248 : state.dataThermalComforts->SkinTempNeut = 33.8 - 1.6 * state.dataThermalComforts->AcclPattern;
1587 1248 : state.dataThermalComforts->ActLevel -= 0.07 * state.dataThermalComforts->ActLevel * state.dataThermalComforts->AcclPattern;
1588 1248 : Real64 const SkinTempNeut_fac(1.0 / (1.0 - SkinWetNeut));
1589 : // CALCULATION OF CoreTempChange/TempChange & SkinTempChange/TempChange
1590 1248 : DERIV(state, TempIndiceNum, state.dataThermalComforts->Temp, state.dataThermalComforts->TempChange);
1591 : while (true) {
1592 : // CALCULATION OF THERMAL SENSATION VOTE (TSV).
1593 : // THE TSV MODEL CAN BE APPLIED TO UNACCLIMATED MAN ONLY.
1594 124800 : SkinWetFac = (state.dataThermalComforts->SkinWetSweat - SkinWetNeut) * SkinTempNeut_fac;
1595 124800 : state.dataThermalComforts->VasodilationFac = (state.dataThermalComforts->ThermCndct - ThermCndctNeut) * ThemCndct_75_fac;
1596 124800 : state.dataThermalComforts->VasoconstrictFac = (ThermCndctNeut - state.dataThermalComforts->ThermCndct) * ThemCndct_fac;
1597 : // IF VasodilationFac < 0.0, VASOCONSTRICTION OCCURS AND RESULTS IN COLD SENSATION.
1598 : // OTHERWISE NORMAL BLOOD FLOW OR VASODILATION OCCURS AND RESULTS IN
1599 : // THERMAL NEUTRALITY OR WARM SENSATION.
1600 124800 : if (state.dataThermalComforts->VasodilationFac < 0) {
1601 71095 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).KsuTSV =
1602 71095 : -1.46153 * state.dataThermalComforts->VasoconstrictFac + 3.74721 * pow_2(state.dataThermalComforts->VasoconstrictFac) -
1603 71095 : 6.168856 * pow_3(state.dataThermalComforts->VasoconstrictFac);
1604 : } else {
1605 53705 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).KsuTSV =
1606 53705 : (5.0 - 6.56 * (state.dataThermalComforts->RelHum - 0.50)) * SkinWetFac;
1607 53705 : if (state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).KsuTSV > TSVMax)
1608 0 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).KsuTSV = TSVMax;
1609 : }
1610 :
1611 124800 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortMRT =
1612 124800 : state.dataThermalComforts->RadTemp;
1613 124800 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
1614 124800 : (state.dataThermalComforts->RadTemp + state.dataThermalComforts->AirTemp) / 2.0;
1615 :
1616 124800 : state.dataThermalComforts->CoreTemp = state.dataThermalComforts->Temp(1);
1617 124800 : state.dataThermalComforts->SkinTemp = state.dataThermalComforts->Temp(2);
1618 124800 : state.dataThermalComforts->EvapHeatLossSweatPrev = state.dataThermalComforts->EvapHeatLossSweat;
1619 :
1620 124800 : RKG(state,
1621 : TempIndiceNum,
1622 124800 : state.dataThermalComforts->TimeChange,
1623 124800 : state.dataThermalComforts->Time,
1624 124800 : state.dataThermalComforts->Temp,
1625 124800 : state.dataThermalComforts->TempChange,
1626 124800 : state.dataThermalComforts->Coeff);
1627 :
1628 124800 : if (state.dataThermalComforts->Time > TimeExpos) break;
1629 : }
1630 : }
1631 : }
1632 624 : }
1633 :
1634 625248 : void DERIV(EnergyPlusData &state,
1635 : [[maybe_unused]] int &TempIndiceNum, // Number of temperature indices unused1208
1636 : [[maybe_unused]] Array1D<Real64> &Temp, // Temperature unused1208
1637 : Array1D<Real64> &TempChange // Change of temperature
1638 : )
1639 : {
1640 :
1641 : // SUBROUTINE INFORMATION:
1642 : // AUTHOR Jaewook Lee
1643 : // DATE WRITTEN January 2000
1644 : // MODIFIED Rick Strand (for E+ implementation February 2000)
1645 :
1646 : // PURPOSE OF THIS SUBROUTINE:
1647 : // THIS SUBROUTINE CALCULATES HEAT TRANSFER TERMS INVOLVED IN THE
1648 : // THERMOREGULATORY SYSTEM TO OBTAIN THE RATES OF CHANGE OF CoreTemp & SkinTemp
1649 : // VIZ., CoreTempChange/TempChange & SkinTempChange/TempChange RESPECTIVELY.
1650 :
1651 : // METHODOLOGY EMPLOYED:
1652 : // This subroutine is based heavily upon the work performed by Dan Maloney for
1653 : // the BLAST program. Many of the equations are based on the original Pierce
1654 : // development. See documentation for further details and references.
1655 :
1656 : // REFERENCES:
1657 : // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
1658 :
1659 : // Argument array dimensioning
1660 : // EP_SIZE_CHECK(Temp, 2);
1661 625248 : EP_SIZE_CHECK(TempChange, 2);
1662 :
1663 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1664 : Real64 ActLevelTot; // Total activity level
1665 : Real64 CoreSignalShiv; // Core signal when shivering occurs
1666 : Real64 CoreSignalShivMax; // Maximum value of core signal when shivering occurs
1667 : Real64 CoreSignalSkinSens; // The sensitivity of the skin signal increases
1668 : Real64 CoreSignalSweatMax; // Maximum value of core signal when sweating occurs
1669 : Real64 CoreSignalSweatWarm; // Core signal when sweating occurs
1670 : Real64 CoreTempSweat; // Core temperature when sweating occurs
1671 : Real64 CoreSignalWarm; // Warm core signal
1672 : Real64 CoreSignalWarmMax; // Maximum value of warm core signal
1673 : Real64 EvapHeatLossDrySweat; // Evaporative heat loss by sweating when total skin wettedness < 0.4
1674 : Real64 Err; // Stop criteria for iteration
1675 : Real64 ErrPrev; // Previous value of stop criteria for iteration
1676 : Real64 EvapHeatLossSweatEst; // Estimated evaporative heat loss by sweating
1677 : Real64 EvapHeatLossSweatEstNew; // New value of estimated evaporative heat loss by sweating
1678 : Real64 IntHeatProdTot; // Total internal heat production
1679 : Real64 SkinCndctMax; // Maximum value of skin conductance
1680 : Real64 SkinSignalCold; // Cold skin signal
1681 : Real64 SkinSignalColdMax; // Maximum value of cold skin signal
1682 : Real64 SkinSignalSweatCold; // Cold skin signal for sweat inhibition
1683 : Real64 SkinSignalSweatColdMax; // Maximum value of cold skin signal for sweat inhibition
1684 : Real64 SkinCndctDilation; // Overall skin conductance due to vasodilation
1685 : Real64 SkinCndctConstriction; // Overall skin conductance due to vasoconstriction
1686 : Real64 SkinSignalShiv; // Skin signal when shivering occurs
1687 : Real64 SkinSignalShivMax; // Maximum value of skin signal when shivering occurs
1688 : Real64 SkinSignalSweatMax; // Skin signal when sweating occurs
1689 : Real64 SkinSignalSweatWarm; // Maximum value of skin signal when sweating occurs
1690 : Real64 SkinSignalWarm; // Warm skin signal
1691 : Real64 SkinSignalWarmMax; // Maximum value of warm skin signal
1692 : Real64 SkinTempSweat; // Skin temperature when sweating occurs
1693 : Real64 SkinWetSignal; // Skin wettedness signal
1694 : Real64 SweatCtrlFac; // Sweat control factor
1695 : Real64 SweatSuppFac; // Sweat suppression factor due to skin wettedness
1696 : Real64 WeighFac; // Weighting factor of core siganl
1697 :
1698 : // THE CONTROLLING SYSTEM.
1699 : // THE CONTROLLING SIGNALS :
1700 : // SIGNALS FOR KS.
1701 625248 : CoreSignalWarm = state.dataThermalComforts->CoreTemp - 36.98;
1702 625248 : SkinSignalWarm = state.dataThermalComforts->SkinTemp - 33.8;
1703 625248 : SkinSignalCold = 32.1 - state.dataThermalComforts->SkinTemp;
1704 625248 : CoreSignalSkinSens = state.dataThermalComforts->CoreTemp - 35.15;
1705 625248 : CoreSignalWarmMax = max(0.0, CoreSignalWarm);
1706 625248 : SkinSignalWarmMax = max(0.0, SkinSignalWarm);
1707 625248 : SkinSignalColdMax = max(0.0, SkinSignalCold);
1708 :
1709 : // SIGNALS FOR EvapHeatLossSweat.
1710 625248 : CoreTempSweat = state.dataThermalComforts->CoreTemp;
1711 625248 : if (CoreTempSweat > 38.29) CoreTempSweat = 38.29;
1712 625248 : CoreSignalSweatWarm = CoreTempSweat - state.dataThermalComforts->CoreTempNeut;
1713 625248 : SkinTempSweat = state.dataThermalComforts->SkinTemp;
1714 625248 : if (SkinTempSweat > 36.1) SkinTempSweat = 36.1;
1715 625248 : SkinSignalSweatWarm = SkinTempSweat - state.dataThermalComforts->SkinTempNeut;
1716 625248 : CoreSignalSweatMax = max(0.0, CoreSignalSweatWarm);
1717 625248 : SkinSignalSweatMax = max(0.0, SkinSignalSweatWarm);
1718 625248 : SkinSignalSweatCold = 33.37 - state.dataThermalComforts->SkinTemp;
1719 625248 : if (state.dataThermalComforts->SkinTempNeut < 33.37)
1720 0 : SkinSignalSweatCold = state.dataThermalComforts->SkinTempNeut - state.dataThermalComforts->SkinTemp;
1721 625248 : SkinSignalSweatColdMax = max(0.0, SkinSignalSweatCold);
1722 :
1723 : // SIGNALS FOR SHIVERING.
1724 625248 : CoreSignalShiv = 36.9 - state.dataThermalComforts->CoreTemp;
1725 625248 : SkinSignalShiv = 32.5 - state.dataThermalComforts->SkinTemp;
1726 625248 : CoreSignalShivMax = max(0.0, CoreSignalShiv);
1727 625248 : SkinSignalShivMax = max(0.0, SkinSignalShiv);
1728 :
1729 : // CONTROLLING FUNCTIONS :
1730 : // SHIVERING RESPONSE IN W/M**2.
1731 625248 : state.dataThermalComforts->ShivResponse = 20.0 * CoreSignalShivMax * SkinSignalShivMax + 5.0 * SkinSignalShivMax;
1732 625248 : if (state.dataThermalComforts->CoreTemp >= 37.1) state.dataThermalComforts->ShivResponse = 0.0;
1733 :
1734 : // SWEAT FUNCTION IN W/M**2.
1735 625248 : WeighFac = 260.0 + 70.0 * state.dataThermalComforts->AcclPattern;
1736 625248 : SweatCtrlFac = 1.0 + 0.05 * std::pow(SkinSignalSweatColdMax, 2.4);
1737 :
1738 : // EvapHeatLossDrySweat = SWEAT WHEN SkinWetTot < 0.4.
1739 625248 : EvapHeatLossDrySweat =
1740 625248 : ((WeighFac * CoreSignalSweatMax + 0.1 * WeighFac * SkinSignalSweatMax) * std::exp(SkinSignalSweatMax / 8.5)) / SweatCtrlFac;
1741 :
1742 : // MAXIMUM EVAPORATIVE POWER, EvapHeatLossMax, IN W/M**2.
1743 625248 : state.dataThermalComforts->SkinVapPress = CalcSatVapPressFromTemp(state.dataThermalComforts->SkinTemp);
1744 625248 : state.dataThermalComforts->EvapHeatLossMax = 2.2 * state.dataThermalComforts->Hc *
1745 625248 : (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress) *
1746 625248 : state.dataThermalComforts->CloPermeatEff;
1747 625248 : if (state.dataThermalComforts->EvapHeatLossMax > 0.0) {
1748 625248 : state.dataThermalComforts->SkinWetSweat = EvapHeatLossDrySweat / state.dataThermalComforts->EvapHeatLossMax;
1749 625248 : state.dataThermalComforts->EvapHeatLossDiff = 0.408 * (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress);
1750 625248 : state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->SkinWetSweat * state.dataThermalComforts->EvapHeatLossMax +
1751 625248 : (1.0 - state.dataThermalComforts->SkinWetSweat) * state.dataThermalComforts->EvapHeatLossDiff;
1752 625248 : state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
1753 625248 : if (state.dataThermalComforts->Time == 0.0) {
1754 2496 : state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossDrySweat;
1755 2496 : state.dataThermalComforts->EvapHeatLossSweatPrev = EvapHeatLossDrySweat;
1756 : }
1757 625248 : if (state.dataThermalComforts->SkinWetTot > 0.4) {
1758 :
1759 : // ITERATION FOR SWEAT WHEN SkinWetTot IS GREATER THAT 0.4.
1760 62255 : state.dataThermalComforts->IterNum = 0;
1761 62255 : if (state.dataThermalComforts->SkinWetSweat > 1.0) state.dataThermalComforts->SkinWetSweat = 1.0;
1762 : while (true) {
1763 92470 : EvapHeatLossSweatEst = state.dataThermalComforts->EvapHeatLossSweatPrev;
1764 92470 : state.dataThermalComforts->SkinWetSweat = EvapHeatLossSweatEst / state.dataThermalComforts->EvapHeatLossMax;
1765 :
1766 92470 : if (state.dataThermalComforts->SkinWetSweat > 1.0) state.dataThermalComforts->SkinWetSweat = 1.0;
1767 :
1768 184940 : state.dataThermalComforts->EvapHeatLossDiff =
1769 92470 : 0.408 * (state.dataThermalComforts->SkinVapPress - state.dataThermalComforts->VapPress);
1770 184940 : state.dataThermalComforts->EvapHeatLoss =
1771 92470 : (1.0 - state.dataThermalComforts->SkinWetTot) * state.dataThermalComforts->EvapHeatLossDiff +
1772 92470 : state.dataThermalComforts->EvapHeatLossSweat;
1773 92470 : state.dataThermalComforts->SkinWetTot = state.dataThermalComforts->EvapHeatLoss / state.dataThermalComforts->EvapHeatLossMax;
1774 :
1775 92470 : if (state.dataThermalComforts->SkinWetTot > 1.0) state.dataThermalComforts->SkinWetTot = 1.0;
1776 :
1777 92470 : SkinWetSignal = max(0.0, state.dataThermalComforts->SkinWetTot - 0.4);
1778 92470 : SweatSuppFac = 0.5 + 0.5 * std::exp(-5.6 * SkinWetSignal);
1779 92470 : EvapHeatLossSweatEstNew = SweatSuppFac * EvapHeatLossDrySweat;
1780 :
1781 92470 : if (state.dataThermalComforts->IterNum == 0) state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossSweatEstNew;
1782 :
1783 92470 : Err = EvapHeatLossSweatEst - EvapHeatLossSweatEstNew;
1784 :
1785 92470 : if (state.dataThermalComforts->IterNum != 0) {
1786 30215 : if ((ErrPrev * Err) < 0.0)
1787 30138 : state.dataThermalComforts->EvapHeatLossSweat = (EvapHeatLossSweatEst + EvapHeatLossSweatEstNew) / 2.0;
1788 30215 : if ((ErrPrev * Err) >= 0.0) state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossSweatEstNew;
1789 : }
1790 :
1791 : // STOP CRITERION FOR THE ITERATION.
1792 92470 : if ((std::abs(Err) <= 0.5) || (state.dataThermalComforts->IterNum >= 10)) break;
1793 30215 : ++state.dataThermalComforts->IterNum;
1794 30215 : state.dataThermalComforts->EvapHeatLossSweatPrev = state.dataThermalComforts->EvapHeatLossSweat;
1795 30215 : ErrPrev = Err;
1796 : }
1797 :
1798 : } else {
1799 562993 : state.dataThermalComforts->EvapHeatLossSweat = EvapHeatLossDrySweat;
1800 : }
1801 :
1802 : } else {
1803 0 : state.dataThermalComforts->SkinWetSweat = 1.0;
1804 0 : state.dataThermalComforts->SkinWetTot = 1.0;
1805 0 : state.dataThermalComforts->EvapHeatLossSweat = 0.5 * EvapHeatLossDrySweat;
1806 0 : state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossSweat;
1807 : }
1808 :
1809 : // OVERALL SKIN CONDUCTANCE, KS, IN W/M**2/C.
1810 : // SkinCndctDilation = EFFECT DUE TO VASODILATION.
1811 : // SkinCndctConstriction = EFFECT DUE TO VASOCONSTRICTION.
1812 625248 : SkinCndctDilation = 42.45 * CoreSignalWarmMax + 8.15 * std::pow(CoreSignalSkinSens, 0.8) * SkinSignalWarmMax;
1813 625248 : SkinCndctConstriction = 1.0 + 0.4 * SkinSignalColdMax;
1814 : // ThermCndct IS EQUIVALENT TO KS
1815 625248 : state.dataThermalComforts->ThermCndct = 5.3 + (6.75 + SkinCndctDilation) / SkinCndctConstriction;
1816 625248 : SkinCndctMax = 75.0 + 10.0 * state.dataThermalComforts->AcclPattern;
1817 625248 : if (state.dataThermalComforts->ThermCndct > SkinCndctMax) state.dataThermalComforts->ThermCndct = SkinCndctMax;
1818 :
1819 : // PASSIVE ENERGY BALANCE EQUATIONS.
1820 : // TOTAL METABOLIC HEAT PRODUCTION RATE, ActLevel, IN W/M**2.
1821 625248 : ActLevelTot = state.dataThermalComforts->ActLevel + state.dataThermalComforts->ShivResponse;
1822 625248 : IntHeatProdTot = ActLevelTot - state.dataThermalComforts->WorkEff;
1823 : // RESPIRATION HEAT LOSS, RespHeatLoss, IN W/M**0.
1824 625248 : state.dataThermalComforts->LatRespHeatLoss = 0.0023 * ActLevelTot * (44.0 - state.dataThermalComforts->VapPress);
1825 625248 : state.dataThermalComforts->DryRespHeatLoss = 0.0014 * ActLevelTot * (34.0 - state.dataThermalComforts->AirTemp);
1826 625248 : state.dataThermalComforts->RespHeatLoss = state.dataThermalComforts->LatRespHeatLoss + state.dataThermalComforts->DryRespHeatLoss;
1827 : // HEAT FLOW FROM CORE TO SKIN, HeatFlow, IN W/M**2.
1828 1250496 : state.dataThermalComforts->HeatFlow =
1829 625248 : state.dataThermalComforts->ThermCndct * (state.dataThermalComforts->CoreTemp - state.dataThermalComforts->SkinTemp);
1830 : // TempChange(1) = CoreTempChange/TempChange, IN C/HR.
1831 625248 : TempChange(1) = (IntHeatProdTot - state.dataThermalComforts->RespHeatLoss - state.dataThermalComforts->HeatFlow) /
1832 625248 : state.dataThermalComforts->CoreThermCap;
1833 625248 : if (state.dataThermalComforts->EvapHeatLoss > state.dataThermalComforts->EvapHeatLossMax)
1834 0 : state.dataThermalComforts->EvapHeatLoss = state.dataThermalComforts->EvapHeatLossMax;
1835 :
1836 : // DRY HEAT EXCHANGE BY RADIATION & CONVECTION, R+C, IN W/M**2.
1837 625248 : state.dataThermalComforts->DryHeatLoss = state.dataThermalComforts->H * state.dataThermalComforts->CloBodyRat *
1838 625248 : state.dataThermalComforts->CloThermEff *
1839 625248 : (state.dataThermalComforts->SkinTemp - state.dataThermalComforts->OpTemp);
1840 : // TempChange(2) = SkinTempChange/TempChange, IN C/HR.
1841 625248 : TempChange(2) = (state.dataThermalComforts->HeatFlow - state.dataThermalComforts->EvapHeatLoss - state.dataThermalComforts->DryHeatLoss) /
1842 625248 : state.dataThermalComforts->SkinThermCap;
1843 625248 : }
1844 :
1845 124800 : void RKG(EnergyPlusData &state, int &NEQ, Real64 &H, Real64 &X, Array1D<Real64> &Y, Array1D<Real64> &DY, Array1D<Real64> &C)
1846 : {
1847 :
1848 : // SUBROUTINE INFORMATION:
1849 : // AUTHOR Jaewook Lee
1850 : // DATE WRITTEN January 2000
1851 : // MODIFIED Rick Strand (for E+ implementation February 2000)
1852 :
1853 : // PURPOSE OF THIS SUBROUTINE:
1854 : // This is a subroutine for integration by Runga-Kutta's method.
1855 :
1856 : // METHODOLOGY EMPLOYED:
1857 : // This subroutine is based heavily upon the work performed by Dan Maloney for
1858 : // the BLAST program. Many of the equations are based on the original Pierce
1859 : // development. See documentation for further details and references.
1860 :
1861 : // REFERENCES:
1862 : // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
1863 :
1864 : // Argument array dimensioning
1865 124800 : EP_SIZE_CHECK(Y, NEQ);
1866 124800 : EP_SIZE_CHECK(DY, NEQ);
1867 124800 : EP_SIZE_CHECK(C, NEQ);
1868 :
1869 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1870 : int I;
1871 : int J;
1872 : Real64 B;
1873 : Real64 H2;
1874 : static constexpr std::array<Real64, 2> A = {0.29289321881345, 1.70710678118654};
1875 :
1876 124800 : H2 = 0.5 * H;
1877 :
1878 124800 : DERIV(state, NEQ, Y, DY);
1879 374400 : for (I = 1; I <= NEQ; ++I) {
1880 249600 : B = H2 * DY(I) - C(I);
1881 249600 : Y(I) += B;
1882 249600 : C(I) += 3.0 * B - H2 * DY(I);
1883 : }
1884 :
1885 124800 : X += H2;
1886 :
1887 374400 : for (J = 0; J < 2; ++J) {
1888 249600 : DERIV(state, NEQ, Y, DY);
1889 748800 : for (I = 1; I <= NEQ; ++I) {
1890 499200 : B = A[J] * (H * DY(I) - C(I));
1891 499200 : Y(I) += B;
1892 499200 : C(I) += 3.0 * B - A[J] * H * DY(I);
1893 : }
1894 : }
1895 :
1896 124800 : X += H2;
1897 124800 : DERIV(state, NEQ, Y, DY);
1898 :
1899 374400 : for (I = 1; I <= NEQ; ++I) {
1900 249600 : B = (H * DY(I) - 2.0 * C(I)) / 6.0;
1901 249600 : Y(I) += B;
1902 249600 : C(I) += 3.0 * B - H2 * DY(I);
1903 : }
1904 :
1905 124800 : DERIV(state, NEQ, Y, DY);
1906 124800 : }
1907 :
1908 796 : void GetAngleFactorList(EnergyPlusData &state)
1909 : {
1910 :
1911 : // SUBROUTINE INFORMATION:
1912 : // AUTHOR Jaewook Lee
1913 : // DATE WRITTEN July 2001
1914 :
1915 : static constexpr std::string_view routineName("GetAngleFactorList: "); // include trailing blank space
1916 796 : Real64 constexpr AngleFacLimit(0.01); // To set the limit of sum of angle factors
1917 :
1918 796 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
1919 : int IOStatus;
1920 : int NumAlphas; // Number of Alphas from InputProcessor
1921 : int NumNumbers; // Number of Numbers from Input Processor
1922 796 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
1923 :
1924 796 : cCurrentModuleObject = "ComfortViewFactorAngles";
1925 796 : int NumOfAngleFactorLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
1926 796 : state.dataThermalComforts->AngleFactorList.allocate(NumOfAngleFactorLists);
1927 :
1928 799 : for (int Item = 1; Item <= NumOfAngleFactorLists; ++Item) {
1929 :
1930 3 : Real64 AllAngleFacSummed = 0.0; // Sum of angle factors in each zone
1931 3 : auto &thisAngFacList(state.dataThermalComforts->AngleFactorList(Item));
1932 :
1933 6 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1934 : cCurrentModuleObject,
1935 : Item,
1936 3 : state.dataIPShortCut->cAlphaArgs,
1937 : NumAlphas,
1938 3 : state.dataIPShortCut->rNumericArgs,
1939 : NumNumbers,
1940 : IOStatus,
1941 3 : state.dataIPShortCut->lNumericFieldBlanks,
1942 3 : state.dataIPShortCut->lAlphaFieldBlanks,
1943 3 : state.dataIPShortCut->cAlphaFieldNames,
1944 3 : state.dataIPShortCut->cNumericFieldNames);
1945 :
1946 3 : thisAngFacList.Name = state.dataIPShortCut->cAlphaArgs(1); // no need for verification/uniqueness.
1947 :
1948 3 : thisAngFacList.TotAngleFacSurfaces = NumNumbers;
1949 3 : thisAngFacList.SurfaceName.allocate(thisAngFacList.TotAngleFacSurfaces);
1950 3 : thisAngFacList.SurfacePtr.allocate(thisAngFacList.TotAngleFacSurfaces);
1951 3 : thisAngFacList.AngleFactor.allocate(thisAngFacList.TotAngleFacSurfaces);
1952 :
1953 21 : for (int SurfNum = 1; SurfNum <= thisAngFacList.TotAngleFacSurfaces; ++SurfNum) {
1954 18 : thisAngFacList.SurfaceName(SurfNum) = state.dataIPShortCut->cAlphaArgs(SurfNum + 1);
1955 18 : thisAngFacList.SurfacePtr(SurfNum) = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(SurfNum + 1), state.dataSurface->Surface);
1956 18 : thisAngFacList.AngleFactor(SurfNum) = state.dataIPShortCut->rNumericArgs(SurfNum);
1957 : // Error trap for surfaces that do not exist or surfaces not in the zone
1958 18 : if (thisAngFacList.SurfacePtr(SurfNum) == 0) {
1959 0 : ShowSevereError(state,
1960 0 : format("{}: invalid {}, entered value={}",
1961 : cCurrentModuleObject,
1962 0 : state.dataIPShortCut->cAlphaFieldNames(SurfNum + 1),
1963 0 : state.dataIPShortCut->cAlphaArgs(SurfNum + 1)));
1964 0 : ShowContinueError(state,
1965 0 : format("ref {}={} not found in {}={}",
1966 0 : state.dataIPShortCut->cAlphaFieldNames(1),
1967 0 : state.dataIPShortCut->cAlphaArgs(1),
1968 0 : state.dataIPShortCut->cAlphaFieldNames(2),
1969 0 : state.dataIPShortCut->cAlphaArgs(2)));
1970 0 : ErrorsFound = true;
1971 : } else {
1972 : // Found Surface, is it in same enclosure?
1973 18 : auto &thisSurf = state.dataSurface->Surface(thisAngFacList.SurfacePtr(SurfNum));
1974 18 : if (SurfNum == 1) thisAngFacList.EnclosurePtr = thisSurf.RadEnclIndex; // Save enclosure num of first surface
1975 18 : if (thisAngFacList.EnclosurePtr != thisSurf.RadEnclIndex) {
1976 0 : ShowWarningError(state,
1977 0 : format("{}: For {}=\"{}\", surfaces are not all in the same radiant enclosure.",
1978 : routineName,
1979 : cCurrentModuleObject,
1980 0 : thisAngFacList.Name));
1981 0 : ShowContinueError(state,
1982 0 : format("... Surface=\"{}\" is in enclosure=\"{}\"",
1983 0 : state.dataSurface->Surface(thisAngFacList.SurfacePtr(1)).Name,
1984 0 : state.dataViewFactor->EnclRadInfo(thisAngFacList.EnclosurePtr).Name));
1985 0 : ShowContinueError(state,
1986 0 : format("... Surface=\"{}\" is in enclosure=\"{}\"",
1987 0 : thisSurf.Name,
1988 0 : state.dataViewFactor->EnclRadInfo(thisSurf.RadEnclIndex).Name));
1989 : }
1990 : }
1991 :
1992 18 : AllAngleFacSummed += thisAngFacList.AngleFactor(SurfNum);
1993 : }
1994 :
1995 3 : if (std::abs(AllAngleFacSummed - 1.0) > AngleFacLimit) {
1996 0 : ShowSevereError(state, format("{}=\"{}\", invalid - Sum[AngleFactors]", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
1997 0 : ShowContinueError(state,
1998 0 : format("...Sum of Angle Factors [{:.3R}] should not deviate from expected sum [1.0] by more than limit [{:.3R}].",
1999 : AllAngleFacSummed,
2000 : AngleFacLimit));
2001 0 : ErrorsFound = true;
2002 : }
2003 : }
2004 :
2005 796 : if (ErrorsFound) {
2006 0 : ShowFatalError(state, "GetAngleFactorList: Program terminated due to preceding errors.");
2007 : }
2008 :
2009 4910 : for (int Item = 1; Item <= state.dataHeatBal->TotPeople; ++Item) {
2010 4114 : auto &thisPeople = state.dataHeatBal->People(Item);
2011 4114 : if (thisPeople.MRTCalcType != DataHeatBalance::CalcMRT::AngleFactor) continue;
2012 3 : thisPeople.AngleFactorListPtr = Util::FindItemInList(thisPeople.AngleFactorListName, state.dataThermalComforts->AngleFactorList);
2013 3 : int WhichAFList = thisPeople.AngleFactorListPtr;
2014 3 : if (WhichAFList == 0 && (thisPeople.Fanger || thisPeople.Pierce || thisPeople.KSU)) {
2015 0 : ShowSevereError(state, format("{}{}=\"{}\", invalid", routineName, cCurrentModuleObject, thisPeople.AngleFactorListName));
2016 0 : ShowContinueError(state, format("... Angle Factor List Name not found for PEOPLE=\"{}\"", thisPeople.Name));
2017 0 : ErrorsFound = true;
2018 : } else {
2019 3 : auto &thisAngFacList = state.dataThermalComforts->AngleFactorList(WhichAFList);
2020 3 : if (state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum != thisAngFacList.EnclosurePtr &&
2021 0 : (thisPeople.Fanger || thisPeople.Pierce || thisPeople.KSU)) {
2022 0 : ShowWarningError(state,
2023 0 : format("{}{}=\"{}\", radiant enclosure mismatch.", routineName, cCurrentModuleObject, thisAngFacList.Name));
2024 0 : ShowContinueError(
2025 : state,
2026 0 : format("...Enclosure=\"{}\" doe not match enclosure=\"{}\" for PEOPLE=\"{}\"",
2027 0 : state.dataViewFactor->EnclRadInfo(thisAngFacList.EnclosurePtr).Name,
2028 0 : state.dataViewFactor->EnclRadInfo(state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum).Name,
2029 0 : thisPeople.Name));
2030 : }
2031 : }
2032 : }
2033 :
2034 796 : if (ErrorsFound) {
2035 0 : ShowFatalError(state, "GetAngleFactorList: Program terminated due to preceding errors.");
2036 : }
2037 796 : }
2038 :
2039 864 : Real64 CalcAngleFactorMRT(EnergyPlusData &state, int const AngleFacNum)
2040 : {
2041 :
2042 : // SUBROUTINE INFORMATION:
2043 : // AUTHOR Jaewook Lee
2044 : // DATE WRITTEN July 2001
2045 : // MODIFIED November 2017 (R Strand): Added fourth power and emissivity to calculation
2046 :
2047 : // Return value
2048 : Real64 CalcAngleFactorMRT;
2049 :
2050 864 : Real64 SurfTempEmissAngleFacSummed = 0.0;
2051 864 : Real64 SumSurfaceEmissAngleFactor = 0.0;
2052 :
2053 864 : auto &thisAngFacList(state.dataThermalComforts->AngleFactorList(AngleFacNum));
2054 :
2055 6048 : for (int SurfNum = 1; SurfNum <= thisAngFacList.TotAngleFacSurfaces; ++SurfNum) {
2056 5184 : Real64 SurfaceTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(thisAngFacList.SurfacePtr(SurfNum)) + Constant::Kelvin;
2057 : Real64 SurfEAF =
2058 5184 : state.dataConstruction->Construct(state.dataSurface->Surface(thisAngFacList.SurfacePtr(SurfNum)).Construction).InsideAbsorpThermal *
2059 5184 : thisAngFacList.AngleFactor(SurfNum);
2060 5184 : SurfTempEmissAngleFacSummed += SurfEAF * pow_4(SurfaceTemp);
2061 5184 : SumSurfaceEmissAngleFactor += SurfEAF;
2062 : }
2063 :
2064 864 : CalcAngleFactorMRT = root_4(SurfTempEmissAngleFacSummed / SumSurfaceEmissAngleFactor) - Constant::Kelvin;
2065 :
2066 864 : return CalcAngleFactorMRT;
2067 : }
2068 :
2069 4752 : Real64 CalcSurfaceWeightedMRT(EnergyPlusData &state, int const SurfNum, bool AverageWithSurface)
2070 : {
2071 :
2072 : // Purpose: Calculate a modified zone MRT that excludes the Surface( SurfNum ).
2073 : // This is necessary for the surface weighted option to not in essence
2074 : // double count SurfNum in the MRT calculation when averaged with the Surface( SurfNum ).
2075 : // Other than that, the method here is the same as CalculateZoneMRT. Once a modified zone
2076 : // MRT is calculated, the subroutine then calculates and returns the
2077 : // RadTemp (radiant temperature) for use by the thermal comfort routines
2078 : // that is the average of the surface temperature to be weighted and
2079 : // the modified zone MRT.
2080 :
2081 : // Return value
2082 4752 : Real64 CalcSurfaceWeightedMRT = 0.0;
2083 :
2084 : // Initialize ZoneAESum for all zones and SurfaceAE for all surfaces at the start of the simulation
2085 4752 : if (state.dataThermalComforts->FirstTimeSurfaceWeightedFlag) {
2086 11 : state.dataThermalComforts->FirstTimeError = true;
2087 11 : state.dataThermalComforts->FirstTimeSurfaceWeightedFlag = false;
2088 72 : for (auto const &thisRadEnclosure : state.dataViewFactor->EnclRadInfo) {
2089 593 : for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
2090 532 : auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
2091 532 : thisSurface2.AE = thisSurface2.Area * state.dataConstruction->Construct(thisSurface2.Construction).InsideAbsorpThermal;
2092 : }
2093 : // Do NOT include the contribution of the Surface that is being surface weighted in this calculation since it will already be
2094 : // accounted for
2095 593 : for (int const SurfNum1 : thisRadEnclosure.SurfacePtr) {
2096 532 : auto &thisSurface1 = state.dataSurface->Surface(SurfNum1);
2097 532 : thisSurface1.enclAESum = 0.0;
2098 5706 : for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
2099 5174 : if (SurfNum2 == SurfNum1) continue;
2100 4642 : auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
2101 4642 : thisSurface1.enclAESum += thisSurface2.AE;
2102 : }
2103 : }
2104 11 : }
2105 : }
2106 :
2107 : // Calculate the sum of area*emissivity and area*emissivity*temperature for all surfaces in the zone EXCEPT the surface being weighted
2108 4752 : Real64 sumAET = 0.0; // Intermediate calculational variable (area*emissivity*T) sum
2109 :
2110 4752 : auto &thisSurface = state.dataSurface->Surface(SurfNum);
2111 4752 : auto &thisRadEnclosure = state.dataViewFactor->EnclRadInfo(thisSurface.RadEnclIndex);
2112 : // Recalc SurfaceEnclAESum only if needed due to window shades or EMS
2113 4752 : if (thisRadEnclosure.radReCalc) {
2114 0 : thisSurface.enclAESum = 0.0;
2115 0 : for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
2116 0 : if (SurfNum2 == SurfNum) continue;
2117 0 : auto &thisSurface2 = state.dataSurface->Surface(SurfNum2);
2118 0 : thisSurface2.AE = thisSurface2.Area * state.dataConstruction->Construct(thisSurface2.Construction).InsideAbsorpThermal;
2119 0 : thisSurface.enclAESum += thisSurface2.AE;
2120 : }
2121 : }
2122 47808 : for (int const SurfNum2 : thisRadEnclosure.SurfacePtr) {
2123 43056 : if (SurfNum2 == SurfNum) continue;
2124 38304 : sumAET += state.dataSurface->Surface(SurfNum2).AE * state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum2);
2125 : }
2126 :
2127 : // Now weight the MRT
2128 4752 : auto &thisSurfaceTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum);
2129 4752 : if (thisSurface.enclAESum > 0.01) {
2130 4752 : CalcSurfaceWeightedMRT = sumAET / thisSurface.enclAESum;
2131 : // if averaged with surface--half comes from the surface used for weighting (SurfNum) and the rest from the calculated MRT that excludes
2132 : // this surface
2133 4752 : if (AverageWithSurface) {
2134 2016 : CalcSurfaceWeightedMRT = 0.5 * (thisSurfaceTemp + CalcSurfaceWeightedMRT);
2135 : }
2136 : } else {
2137 0 : if (state.dataThermalComforts->FirstTimeError) {
2138 0 : int spaceNum = thisSurface.spaceNum;
2139 0 : ShowWarningError(state,
2140 0 : format("CalcSurfaceWeightedMRT: Areas*Inside surface emissivities are summing to zero for Enclosure=\"{}\"",
2141 0 : thisRadEnclosure.Name));
2142 0 : ShowContinueError(state,
2143 0 : format("As a result, the MAT for Space={} will be used for MRT when calculating the surface weighted MRT.",
2144 0 : state.dataHeatBal->space(spaceNum).Name));
2145 0 : ShowContinueError(state, format("for Surface={}", thisSurface.Name));
2146 0 : state.dataThermalComforts->FirstTimeError = false;
2147 0 : CalcSurfaceWeightedMRT = state.dataZoneTempPredictorCorrector->spaceHeatBalance(spaceNum).MAT;
2148 0 : if (AverageWithSurface) {
2149 0 : CalcSurfaceWeightedMRT = 0.5 * (thisSurfaceTemp + CalcSurfaceWeightedMRT);
2150 : }
2151 : }
2152 : }
2153 :
2154 4752 : return CalcSurfaceWeightedMRT;
2155 : }
2156 :
2157 626496 : Real64 CalcSatVapPressFromTemp(Real64 const Temp)
2158 : {
2159 :
2160 : // FUNCTION INFORMATION:
2161 : // AUTHOR Jaewook Lee
2162 : // DATE WRITTEN January 2000
2163 : // MODIFIED Rick Strand (for E+ implementation February 2000)
2164 :
2165 : // PURPOSE OF THIS FUNCTION:
2166 : // THIS IS A FUNCTION TO CALCULATE THE SATURATED VAPOR PRESSURE
2167 : // FROM AIR TEMPERATURE
2168 :
2169 : // METHODOLOGY EMPLOYED:
2170 : // This function is based upon the work performed by Dan Maloney for
2171 : // the BLAST program.
2172 : // REFERENCES:
2173 : // Maloney, Dan, M.S. Thesis, University of Illinois at Urbana-Champaign
2174 :
2175 626496 : Real64 const XT(Temp / 100.0);
2176 626496 : return 6.16796 + 358.1855 * pow_2(XT) - 550.3543 * pow_3(XT) + 1048.8115 * pow_4(XT);
2177 :
2178 : // Helper function for pierceSET calculates Saturated Vapor Pressure (Torr) at Temperature T (°C)
2179 : // return Math.exp(18.6686 - 4030.183/(T + 235.0));
2180 : }
2181 :
2182 619139 : Real64 CalcSatVapPressFromTempTorr(Real64 const Temp)
2183 : {
2184 : // Helper function for pierceSET calculates Saturated Vapor Pressure (Torr) at Temperature T (°C)
2185 619139 : return std::exp(18.6686 - 4030.183 / (Temp + 235.0));
2186 : }
2187 :
2188 1174807 : Real64 CalcRadTemp(EnergyPlusData &state, int const PeopleListNum)
2189 : {
2190 :
2191 : // FUNCTION INFORMATION:
2192 : // AUTHOR Jaewook Lee
2193 : // DATE WRITTEN November 2000
2194 : // MODIFIED Rick Strand (for E+ implementation November 2000)
2195 : // Rick Strand (for high temperature radiant heaters March 2001)
2196 :
2197 : // PURPOSE OF THIS FUNCTION:
2198 : // THIS IS A FUNCTION TO CALCULATE EITHER ZONE AVERAGED MRT OR
2199 : // SURFACE WEIGHTED MRT
2200 :
2201 : // METHODOLOGY EMPLOYED:
2202 : // The method here is fairly straight-forward. If the user has selected
2203 : // a zone average MRT calculation, then there is nothing to do other than
2204 : // to assign the function value because the zone MRT has already been
2205 : // calculated. Note that this value is an "area-emissivity" weighted value.
2206 : // If the user wants to place the occupant "near" a particular surface,
2207 : // then at the limit half of the radiant field will be from this surface.
2208 : // As a result, an average of the zone MRT and the surface temperature
2209 : // is taken to arrive at an approximate radiant temperature.
2210 : // If a high temperature radiant heater is present, then this must also be
2211 : // taken into account. The equation used to account for this factor is
2212 : // based on equation 49 on page 150 of Fanger's text (see reference below).
2213 : // The additional assumptions for EnergyPlus are that the radiant energy
2214 : // from the heater must be spread over the average area of a human being
2215 : // (see parameter below) and that the emissivity and absorptivity of the
2216 : // occupant are equivalent for the dominant wavelength of radiant energy
2217 : // from the heater. These assumptions might be off slightly, but it does
2218 : // allow for an approximation of the effects of surfaces and heaters
2219 : // within a space. Future additions might include the effect of direct
2220 : // solar energy on occupants.
2221 :
2222 1174807 : Real64 CalcRadTemp = 0.0;
2223 1174807 : Real64 constexpr AreaEff = 1.8; // Effective area of a "standard" person in meters squared
2224 1174807 : Real64 constexpr StefanBoltzmannConst = 5.6697e-8; // Stefan-Boltzmann constant in W/(m2*K4)
2225 :
2226 1174807 : auto &thisPeople = state.dataHeatBal->People(PeopleListNum);
2227 1174807 : switch (thisPeople.MRTCalcType) {
2228 1171927 : case DataHeatBalance::CalcMRT::EnclosureAveraged: {
2229 1171927 : int enclNum = state.dataHeatBal->space(thisPeople.spaceIndex).radiantEnclosureNum;
2230 1171927 : state.dataThermalComforts->RadTemp = state.dataViewFactor->EnclRadInfo(enclNum).MRT;
2231 1171927 : } break;
2232 2016 : case DataHeatBalance::CalcMRT::SurfaceWeighted: {
2233 2016 : state.dataThermalComforts->RadTemp = CalcSurfaceWeightedMRT(state, thisPeople.SurfacePtr);
2234 2016 : } break;
2235 864 : case DataHeatBalance::CalcMRT::AngleFactor: {
2236 864 : state.dataThermalComforts->RadTemp = CalcAngleFactorMRT(state, thisPeople.AngleFactorListPtr);
2237 864 : } break;
2238 0 : default:
2239 0 : break;
2240 : }
2241 :
2242 : // If high temperature radiant heater present and on, then must account for this in MRT calculation
2243 : // MJW MRT ToDo: Think about what happens here - at a minimum, set a flag to skip this if there isn't any radiant HVAC
2244 1174807 : state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) =
2245 1174807 : state.dataHeatBalFanSys->ZoneQHTRadSysToPerson(state.dataThermalComforts->ZoneNum) +
2246 1174807 : state.dataHeatBalFanSys->ZoneQCoolingPanelToPerson(state.dataThermalComforts->ZoneNum) +
2247 1174807 : state.dataHeatBalFanSys->ZoneQHWBaseboardToPerson(state.dataThermalComforts->ZoneNum) +
2248 1174807 : state.dataHeatBalFanSys->ZoneQSteamBaseboardToPerson(state.dataThermalComforts->ZoneNum) +
2249 1174807 : state.dataHeatBalFanSys->ZoneQElecBaseboardToPerson(state.dataThermalComforts->ZoneNum);
2250 1174807 : if (state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) > 0.0) {
2251 1334 : state.dataThermalComforts->RadTemp += Constant::Kelvin; // Convert to Kelvin
2252 1334 : state.dataThermalComforts->RadTemp =
2253 1334 : root_4(pow_4(state.dataThermalComforts->RadTemp) +
2254 1334 : (state.dataHeatBalFanSys->ZoneQdotRadHVACToPerson(state.dataThermalComforts->ZoneNum) / AreaEff / StefanBoltzmannConst));
2255 1334 : state.dataThermalComforts->RadTemp -= Constant::Kelvin; // Convert back to Celsius
2256 : }
2257 :
2258 1174807 : CalcRadTemp = state.dataThermalComforts->RadTemp;
2259 :
2260 1174807 : return CalcRadTemp;
2261 : }
2262 :
2263 482304 : void CalcThermalComfortSimpleASH55(EnergyPlusData &state)
2264 : {
2265 : // SUBROUTINE INFORMATION:
2266 : // AUTHOR Jason Glazer
2267 : // DATE WRITTEN June 2005
2268 :
2269 : // PURPOSE OF THIS SUBROUTINE:
2270 : // Determines if the space is within the ASHRAE 55-2004 comfort region
2271 : // based on operative temperature and humidity ratio
2272 :
2273 : // Using/Aliasing
2274 : using OutputReportTabular::isInQuadrilateral;
2275 : using namespace OutputReportPredefined;
2276 :
2277 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2278 : Real64 OperTemp;
2279 : Real64 NumberOccupants;
2280 : bool isComfortableWithSummerClothes;
2281 : bool isComfortableWithWinterClothes;
2282 : int iPeople;
2283 : int iZone;
2284 : Real64 allowedHours;
2285 : bool showWarning;
2286 :
2287 482304 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer = 0.0;
2288 482304 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter = 0.0;
2289 482304 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either = 0.0;
2290 :
2291 : // assume the zone is unoccupied
2292 2905008 : for (auto &e : state.dataThermalComforts->ThermalComfortInASH55)
2293 2905008 : e.ZoneIsOccupied = false;
2294 : // loop through the people objects and determine if the zone is currently occupied
2295 2553648 : for (iPeople = 1; iPeople <= state.dataHeatBal->TotPeople; ++iPeople) {
2296 2071344 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(iPeople).ZonePtr;
2297 2071344 : NumberOccupants = state.dataHeatBal->People(iPeople).NumberOfPeople *
2298 2071344 : GetCurrentScheduleValue(state, state.dataHeatBal->People(iPeople).NumberOfPeoplePtr);
2299 2071344 : if (NumberOccupants > 0) {
2300 852210 : state.dataThermalComforts->ThermalComfortInASH55(state.dataThermalComforts->ZoneNum).ZoneIsOccupied = true;
2301 : }
2302 : }
2303 : // loop through the zones and determine if in simple ashrae 55 comfort regions
2304 : // MJW MRT ToDo: Extend ASHRAE 55 to spaces?
2305 2905008 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2306 2422704 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
2307 850294 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone);
2308 : // keep track of occupied hours
2309 850294 : state.dataThermalComforts->ZoneOccHrs(iZone) += state.dataGlobal->TimeStepZone;
2310 850294 : Real64 CurAirTemp = thisZoneHB.ZTAVComf;
2311 850294 : if (state.dataRoomAir->anyNonMixingRoomAirModel) {
2312 2524 : if (state.dataRoomAir->IsZoneDispVent3Node(iZone) || state.dataRoomAir->IsZoneUFAD(iZone)) {
2313 808 : CurAirTemp = state.dataRoomAir->TCMF(iZone);
2314 : }
2315 : }
2316 850294 : Real64 CurMeanRadiantTemp = thisZoneHB.MRT;
2317 850294 : OperTemp = CurAirTemp * 0.5 + CurMeanRadiantTemp * 0.5;
2318 : // for debugging
2319 : // ThermalComfortInASH55(iZone)%dCurAirTemp = CurAirTemp
2320 : // ThermalComfortInASH55(iZone)%dCurMeanRadiantTemp = CurMeanRadiantTemp
2321 : // ThermalComfortInASH55(iZone)%dOperTemp = OperTemp
2322 : // ThermalComfortInASH55(iZone)%dHumidRatio = HumidRatio
2323 : // From ASHRAE Standard 55-2004 Appendix D
2324 : // Run AirTemp(C) RH(%) Season HumidRatio
2325 : // 1 19.6 86 Winter 0.012
2326 : // 2 23.9 66 Winter 0.012
2327 : // 3 25.7 15 Winter 0.003
2328 : // 4 21.2 20 Winter 0.003
2329 : // 5 23.6 67 Summer 0.012
2330 : // 6 26.8 56 Summer 0.012
2331 : // 7 27.9 13 Summer 0.003
2332 : // 8 24.7 16 Summer 0.003
2333 : // But the standard says "no recommended lower humidity limit" so it should
2334 : // really extend down to the 0.0 Humidity ratio line. Extrapolating we get
2335 : // the values that are shown in the following table
2336 : // Run AirTemp(C) Season HumidRatio
2337 : // 1 19.6 Winter 0.012
2338 : // 2 23.9 Winter 0.012
2339 : // 3 26.3 Winter 0.000
2340 : // 4 21.7 Winter 0.000
2341 : // 5 23.6 Summer 0.012
2342 : // 6 26.8 Summer 0.012
2343 : // 7 28.3 Summer 0.000
2344 : // 8 25.1 Summer 0.000
2345 : // check summer clothing conditions
2346 : isComfortableWithSummerClothes =
2347 850294 : isInQuadrilateral(OperTemp, thisZoneHB.airHumRatAvgComf, 25.1, 0.0, 23.6, 0.012, 26.8, 0.012, 28.3, 0.0);
2348 : // check winter clothing conditions
2349 : isComfortableWithWinterClothes =
2350 850294 : isInQuadrilateral(OperTemp, thisZoneHB.airHumRatAvgComf, 21.7, 0.0, 19.6, 0.012, 23.9, 0.012, 26.3, 0.0);
2351 850294 : if (isComfortableWithSummerClothes) {
2352 331749 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = 0.0;
2353 : } else {
2354 518545 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = state.dataGlobal->TimeStepZone;
2355 518545 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer += state.dataGlobal->TimeStepZone;
2356 518545 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer = state.dataGlobal->TimeStepZone;
2357 : }
2358 850294 : if (isComfortableWithWinterClothes) {
2359 296897 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = 0.0;
2360 : } else {
2361 553397 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = state.dataGlobal->TimeStepZone;
2362 553397 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter += state.dataGlobal->TimeStepZone;
2363 553397 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter = state.dataGlobal->TimeStepZone;
2364 : }
2365 850294 : if (isComfortableWithSummerClothes || isComfortableWithWinterClothes) {
2366 569204 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = 0.0;
2367 : } else {
2368 281090 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = state.dataGlobal->TimeStepZone;
2369 281090 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither += state.dataGlobal->TimeStepZone;
2370 281090 : state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either = state.dataGlobal->TimeStepZone;
2371 : }
2372 : } else {
2373 : // when no one present in that portion of the zone then no one can be uncomfortable
2374 1572410 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotSummer = 0.0;
2375 1572410 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotWinter = 0.0;
2376 1572410 : state.dataThermalComforts->ThermalComfortInASH55(iZone).timeNotEither = 0.0;
2377 : }
2378 : }
2379 : // accumulate total time
2380 482304 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Summer;
2381 482304 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Winter;
2382 482304 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either += state.dataThermalComforts->AnyZoneTimeNotSimpleASH55Either;
2383 :
2384 482304 : if (state.dataGlobal->EndDesignDayEnvrnsFlag) {
2385 814 : allowedHours = double(state.dataGlobal->NumOfDayInEnvrn) * 24.0 * 0.04;
2386 : // first check if warning should be printed
2387 814 : showWarning = false;
2388 6174 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2389 5360 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).Enable55Warning) {
2390 0 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither > allowedHours) {
2391 0 : showWarning = true;
2392 : }
2393 : }
2394 : }
2395 : // if any zones should be warning print it out
2396 814 : if (showWarning) {
2397 0 : ShowWarningError(state, format("More than 4% of time ({:.1R} hours) uncomfortable in one or more zones ", allowedHours));
2398 0 : ShowContinueError(state, "Based on ASHRAE 55-2004 graph (Section 5.2.1.1)");
2399 0 : if (state.dataEnvrn->RunPeriodEnvironment) {
2400 0 : ShowContinueError(state,
2401 0 : format("During Environment [{}]: {}", state.dataEnvrn->EnvironmentStartEnd, state.dataEnvrn->EnvironmentName));
2402 : } else {
2403 0 : ShowContinueError(
2404 : state,
2405 0 : format("During SizingPeriod Environment [{}]: {}", state.dataEnvrn->EnvironmentStartEnd, state.dataEnvrn->EnvironmentName));
2406 : }
2407 0 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2408 0 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).Enable55Warning) {
2409 0 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither > allowedHours) {
2410 0 : ShowContinueError(state,
2411 0 : format("{:.1R} hours were uncomfortable in zone: {}",
2412 0 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither,
2413 0 : state.dataHeatBal->Zone(iZone).Name));
2414 : }
2415 : }
2416 : }
2417 : }
2418 : // put in predefined reports
2419 6174 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2420 10720 : PreDefTableEntry(state,
2421 5360 : state.dataOutRptPredefined->pdchSCwinterClothes,
2422 5360 : state.dataHeatBal->Zone(iZone).Name,
2423 5360 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter);
2424 10720 : PreDefTableEntry(state,
2425 5360 : state.dataOutRptPredefined->pdchSCsummerClothes,
2426 5360 : state.dataHeatBal->Zone(iZone).Name,
2427 5360 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer);
2428 10720 : PreDefTableEntry(state,
2429 5360 : state.dataOutRptPredefined->pdchSCeitherClothes,
2430 5360 : state.dataHeatBal->Zone(iZone).Name,
2431 5360 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither);
2432 : }
2433 2442 : PreDefTableEntry(
2434 1628 : state, state.dataOutRptPredefined->pdchSCwinterClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter);
2435 2442 : PreDefTableEntry(
2436 1628 : state, state.dataOutRptPredefined->pdchSCsummerClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer);
2437 2442 : PreDefTableEntry(
2438 1628 : state, state.dataOutRptPredefined->pdchSCeitherClothes, "Facility", state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either);
2439 : // set value for ABUPS report
2440 814 : state.dataOutRptPredefined->TotalTimeNotSimpleASH55EitherForABUPS = state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either;
2441 : // reset accumulation for new environment
2442 6174 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2443 5360 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter = 0.0;
2444 5360 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer = 0.0;
2445 5360 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither = 0.0;
2446 : }
2447 814 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter = 0.0;
2448 814 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer = 0.0;
2449 814 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either = 0.0;
2450 : // report how the aggregation is conducted
2451 814 : switch (state.dataGlobal->KindOfSim) {
2452 788 : case Constant::KindOfSim::DesignDay: {
2453 788 : addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the Design Days");
2454 788 : } break;
2455 3 : case Constant::KindOfSim::RunPeriodDesign: {
2456 3 : addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the RunPeriods for Design");
2457 3 : } break;
2458 6 : case Constant::KindOfSim::RunPeriodWeather: {
2459 6 : addFootNoteSubTable(state, state.dataOutRptPredefined->pdstSimpleComfort, "Aggregated over the RunPeriods for Weather");
2460 6 : } break;
2461 17 : default:
2462 17 : break;
2463 : }
2464 : // report number of occupied hours per week for LEED report
2465 6174 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2466 10720 : PreDefTableEntry(state,
2467 5360 : state.dataOutRptPredefined->pdchLeedSutHrsWeek,
2468 5360 : state.dataHeatBal->Zone(iZone).Name,
2469 5360 : 7 * 24 * (state.dataThermalComforts->ZoneOccHrs(iZone) / (state.dataGlobal->NumOfDayInEnvrn * 24)));
2470 : }
2471 : }
2472 482304 : }
2473 :
2474 0 : void ResetThermalComfortSimpleASH55(EnergyPlusData &state)
2475 : {
2476 : // Jason Glazer - October 2015
2477 : // Reset thermal comfort table gathering arrays to zero for multi-year simulations
2478 : // so that only last year is reported in tabular reports
2479 : int iZone;
2480 0 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2481 0 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotWinter = 0.0;
2482 0 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotSummer = 0.0;
2483 0 : state.dataThermalComforts->ThermalComfortInASH55(iZone).totalTimeNotEither = 0.0;
2484 : }
2485 0 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Winter = 0.0;
2486 0 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Summer = 0.0;
2487 0 : state.dataThermalComforts->TotalAnyZoneTimeNotSimpleASH55Either = 0.0;
2488 0 : }
2489 :
2490 482304 : void CalcIfSetPointMet(EnergyPlusData &state)
2491 : {
2492 : // SUBROUTINE INFORMATION:
2493 : // AUTHOR Jason Glazer
2494 : // DATE WRITTEN July 2005
2495 :
2496 : // PURPOSE OF THIS SUBROUTINE:
2497 : // Report if the setpoint temperature has been met.
2498 : // Add calculation of how far away from setpoint and if setpoint was not met
2499 : // during all times and during occupancy.
2500 :
2501 : // Using/Aliasing
2502 : using namespace OutputReportPredefined;
2503 482304 : auto &deviationFromSetPtThresholdClg = state.dataHVACGlobal->deviationFromSetPtThresholdClg;
2504 482304 : auto &deviationFromSetPtThresholdHtg = state.dataHVACGlobal->deviationFromSetPtThresholdHtg;
2505 :
2506 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2507 : Real64 SensibleLoadPredictedNoAdj;
2508 : Real64 deltaT;
2509 : int iZone;
2510 : bool testHeating;
2511 : bool testCooling;
2512 :
2513 : // Get the load predicted - the sign will indicate if heating or cooling
2514 : // was called for
2515 482304 : state.dataThermalComforts->AnyZoneNotMetHeating = 0.0;
2516 482304 : state.dataThermalComforts->AnyZoneNotMetCooling = 0.0;
2517 482304 : state.dataThermalComforts->AnyZoneNotMetOccupied = 0.0;
2518 482304 : state.dataThermalComforts->AnyZoneNotMetHeatingOccupied = 0.0;
2519 482304 : state.dataThermalComforts->AnyZoneNotMetCoolingOccupied = 0.0;
2520 2905008 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2521 2422704 : SensibleLoadPredictedNoAdj = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(iZone).TotalOutputRequired;
2522 2422704 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCooling = 0.0;
2523 2422704 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeating = 0.0;
2524 2422704 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCoolingOccupied = 0.0;
2525 2422704 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeatingOccupied = 0.0;
2526 :
2527 2422704 : testHeating = (state.dataHeatBalFanSys->TempControlType(iZone) != HVAC::ThermostatType::SingleCooling);
2528 2422704 : testCooling = (state.dataHeatBalFanSys->TempControlType(iZone) != HVAC::ThermostatType::SingleHeating);
2529 :
2530 2422704 : if (testHeating && (SensibleLoadPredictedNoAdj > 0)) { // heating
2531 679874 : if (state.dataRoomAir->AirModel(iZone).AirModel != RoomAir::RoomAirModel::Mixing) {
2532 2304 : deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - state.dataHeatBalFanSys->ZoneThermostatSetPointLo(iZone);
2533 : } else {
2534 677570 : if (state.dataZoneTempPredictorCorrector->NumOnOffCtrZone > 0) {
2535 1008 : deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV -
2536 1008 : state.dataHeatBalFanSys->ZoneThermostatSetPointLoAver(iZone);
2537 : } else {
2538 676562 : deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV -
2539 676562 : state.dataHeatBalFanSys->ZoneThermostatSetPointLo(iZone);
2540 : }
2541 : }
2542 679874 : if (deltaT < deviationFromSetPtThresholdHtg) {
2543 191846 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeating = state.dataGlobal->TimeStepZone;
2544 191846 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating += state.dataGlobal->TimeStepZone;
2545 191846 : if (state.dataThermalComforts->AnyZoneNotMetHeating == 0.0)
2546 40360 : state.dataThermalComforts->AnyZoneNotMetHeating = state.dataGlobal->TimeStepZone;
2547 191846 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
2548 40172 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetHeatingOccupied = state.dataGlobal->TimeStepZone;
2549 40172 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied += state.dataGlobal->TimeStepZone;
2550 40172 : if (state.dataThermalComforts->AnyZoneNotMetHeatingOccupied == 0.0)
2551 10651 : state.dataThermalComforts->AnyZoneNotMetHeatingOccupied = state.dataGlobal->TimeStepZone;
2552 40172 : if (state.dataThermalComforts->AnyZoneNotMetOccupied == 0.0)
2553 10643 : state.dataThermalComforts->AnyZoneNotMetOccupied = state.dataGlobal->TimeStepZone;
2554 : }
2555 : }
2556 1742830 : } else if (testCooling && (SensibleLoadPredictedNoAdj < 0)) { // cooling
2557 640739 : if (state.dataRoomAir->AirModel(iZone).AirModel != RoomAir::RoomAirModel::Mixing) {
2558 1848 : deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - state.dataHeatBalFanSys->ZoneThermostatSetPointHi(iZone);
2559 : } else {
2560 638891 : if (state.dataZoneTempPredictorCorrector->NumOnOffCtrZone > 0) {
2561 525 : deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV -
2562 525 : state.dataHeatBalFanSys->ZoneThermostatSetPointHiAver(iZone);
2563 : } else {
2564 638366 : deltaT = state.dataZoneTempPredictorCorrector->zoneHeatBalance(iZone).ZTAV -
2565 638366 : state.dataHeatBalFanSys->ZoneThermostatSetPointHi(iZone);
2566 : }
2567 : }
2568 :
2569 640739 : if (state.dataHeatBal->Zone(iZone).HasAdjustedReturnTempByITE) {
2570 960 : deltaT = state.dataHeatBalFanSys->TempTstatAir(iZone) - state.dataHeatBal->Zone(iZone).AdjustedReturnTempByITE;
2571 : }
2572 640739 : if (deltaT > deviationFromSetPtThresholdClg) {
2573 66312 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCooling = state.dataGlobal->TimeStepZone;
2574 66312 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling += state.dataGlobal->TimeStepZone;
2575 66312 : if (state.dataThermalComforts->AnyZoneNotMetCooling == 0.0)
2576 28483 : state.dataThermalComforts->AnyZoneNotMetCooling = state.dataGlobal->TimeStepZone;
2577 66312 : if (state.dataThermalComforts->ThermalComfortInASH55(iZone).ZoneIsOccupied) {
2578 37087 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).notMetCoolingOccupied = state.dataGlobal->TimeStepZone;
2579 37087 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied += state.dataGlobal->TimeStepZone;
2580 37087 : if (state.dataThermalComforts->AnyZoneNotMetCoolingOccupied == 0.0)
2581 16046 : state.dataThermalComforts->AnyZoneNotMetCoolingOccupied = state.dataGlobal->TimeStepZone;
2582 37087 : if (state.dataThermalComforts->AnyZoneNotMetOccupied == 0.0)
2583 16027 : state.dataThermalComforts->AnyZoneNotMetOccupied = state.dataGlobal->TimeStepZone;
2584 : }
2585 : }
2586 : }
2587 : }
2588 482304 : state.dataThermalComforts->TotalAnyZoneNotMetHeating += state.dataThermalComforts->AnyZoneNotMetHeating;
2589 482304 : state.dataThermalComforts->TotalAnyZoneNotMetCooling += state.dataThermalComforts->AnyZoneNotMetCooling;
2590 482304 : state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied += state.dataThermalComforts->AnyZoneNotMetHeatingOccupied;
2591 482304 : state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied += state.dataThermalComforts->AnyZoneNotMetCoolingOccupied;
2592 482304 : state.dataThermalComforts->TotalAnyZoneNotMetOccupied += state.dataThermalComforts->AnyZoneNotMetOccupied;
2593 :
2594 : // was EndEnvrnsFlag prior to CR7562
2595 482304 : if (state.dataGlobal->EndDesignDayEnvrnsFlag) {
2596 6174 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2597 10720 : PreDefTableEntry(state,
2598 5360 : state.dataOutRptPredefined->pdchULnotMetHeat,
2599 5360 : state.dataHeatBal->Zone(iZone).Name,
2600 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating);
2601 10720 : PreDefTableEntry(state,
2602 5360 : state.dataOutRptPredefined->pdchULnotMetCool,
2603 5360 : state.dataHeatBal->Zone(iZone).Name,
2604 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling);
2605 10720 : PreDefTableEntry(state,
2606 5360 : state.dataOutRptPredefined->pdchULnotMetHeatOcc,
2607 5360 : state.dataHeatBal->Zone(iZone).Name,
2608 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied);
2609 10720 : PreDefTableEntry(state,
2610 5360 : state.dataOutRptPredefined->pdchULnotMetCoolOcc,
2611 5360 : state.dataHeatBal->Zone(iZone).Name,
2612 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied);
2613 : }
2614 814 : PreDefTableEntry(state, state.dataOutRptPredefined->pdchULnotMetHeat, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetHeating);
2615 814 : PreDefTableEntry(state, state.dataOutRptPredefined->pdchULnotMetCool, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetCooling);
2616 2442 : PreDefTableEntry(
2617 1628 : state, state.dataOutRptPredefined->pdchULnotMetHeatOcc, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied);
2618 2442 : PreDefTableEntry(
2619 1628 : state, state.dataOutRptPredefined->pdchULnotMetCoolOcc, "Facility", state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied);
2620 : // set value for ABUPS report
2621 814 : state.dataOutRptPredefined->TotalNotMetHeatingOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied;
2622 814 : state.dataOutRptPredefined->TotalNotMetCoolingOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied;
2623 814 : state.dataOutRptPredefined->TotalNotMetOccupiedForABUPS = state.dataThermalComforts->TotalAnyZoneNotMetOccupied;
2624 : // reset counters
2625 6174 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2626 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating = 0.0;
2627 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling = 0.0;
2628 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied = 0.0;
2629 5360 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied = 0.0;
2630 : }
2631 814 : state.dataThermalComforts->TotalAnyZoneNotMetHeating = 0.0;
2632 814 : state.dataThermalComforts->TotalAnyZoneNotMetCooling = 0.0;
2633 814 : state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied = 0.0;
2634 814 : state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied = 0.0;
2635 814 : state.dataThermalComforts->TotalAnyZoneNotMetOccupied = 0.0;
2636 : // report how the aggregation is conducted
2637 814 : switch (state.dataGlobal->KindOfSim) {
2638 788 : case Constant::KindOfSim::DesignDay: {
2639 788 : addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the Design Days");
2640 788 : } break;
2641 3 : case Constant::KindOfSim::RunPeriodDesign: {
2642 3 : addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the RunPeriods for Design");
2643 3 : } break;
2644 6 : case Constant::KindOfSim::RunPeriodWeather: {
2645 6 : addFootNoteSubTable(state, state.dataOutRptPredefined->pdstUnmetLoads, "Aggregated over the RunPeriods for Weather");
2646 6 : } break;
2647 17 : default:
2648 17 : break;
2649 : }
2650 : }
2651 482304 : }
2652 :
2653 0 : void ResetSetPointMet(EnergyPlusData &state)
2654 : {
2655 : // Jason Glazer - October 2015
2656 : // Reset set point not met table gathering arrays to zero for multi-year simulations
2657 : // so that only last year is reported in tabular reports
2658 : int iZone;
2659 0 : for (iZone = 1; iZone <= state.dataGlobal->NumOfZones; ++iZone) {
2660 0 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeating = 0.0;
2661 0 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCooling = 0.0;
2662 0 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetHeatingOccupied = 0.0;
2663 0 : state.dataThermalComforts->ThermalComfortSetPoint(iZone).totalNotMetCoolingOccupied = 0.0;
2664 : }
2665 0 : state.dataThermalComforts->TotalAnyZoneNotMetHeating = 0.0;
2666 0 : state.dataThermalComforts->TotalAnyZoneNotMetCooling = 0.0;
2667 0 : state.dataThermalComforts->TotalAnyZoneNotMetHeatingOccupied = 0.0;
2668 0 : state.dataThermalComforts->TotalAnyZoneNotMetCoolingOccupied = 0.0;
2669 0 : state.dataThermalComforts->TotalAnyZoneNotMetOccupied = 0.0;
2670 0 : }
2671 :
2672 973 : void CalcThermalComfortAdaptiveASH55(
2673 : EnergyPlusData &state,
2674 : bool const initiate, // true if supposed to initiate
2675 : ObjexxFCL::Optional_bool_const wthrsim, // true if this is a weather simulation
2676 : ObjexxFCL::Optional<Real64 const> avgdrybulb // approximate avg drybulb for design day. will be used as previous period in design day
2677 : )
2678 : {
2679 :
2680 : // SUBROUTINE INFORMATION:
2681 : // AUTHOR Tyler Hoyt
2682 : // DATE WRITTEN July 2011
2683 :
2684 : // PURPOSE OF THIS SUBROUTINE:
2685 : // Sets up and carries out ASHRAE55-2010 adaptive comfort model calculations.
2686 : // Output provided are state variables for the 80% and 90% acceptability limits
2687 : // in the model, the comfort temperature, and the 30-day running average or
2688 : // monthly average outdoor air temperature as parsed from the .STAT file.
2689 :
2690 : // METHODOLOGY EMPLOYED:
2691 : // In order for the calculations to be possible the user must provide either
2692 : // a .STAT file or .EPW file for the purpose of computing a monthly average
2693 : // temperature or thirty-day running average. The subroutine need only open
2694 : // the relevant file once to initialize, and then operates within the loop.
2695 :
2696 : // Using/Aliasing
2697 973 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
2698 : using OutputReportTabular::GetColumnUsingTabs;
2699 : using OutputReportTabular::StrToReal;
2700 :
2701 : // SUBROUTINE PARAMETER DEFINITIONS:
2702 :
2703 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2704 973 : std::string lineAvg;
2705 973 : std::string epwLine;
2706 : Real64 dryBulb;
2707 : Real64 tComf;
2708 : Real64 numOccupants;
2709 : int readStat;
2710 : int jStartDay;
2711 : int calcStartDay;
2712 : int calcStartHr;
2713 : int calcEndDay;
2714 : int calcEndHr;
2715 : std::string::size_type pos;
2716 : int ind;
2717 : int i;
2718 : int j;
2719 : bool weathersimulation;
2720 : Real64 inavgdrybulb;
2721 :
2722 973 : if (initiate) { // not optional on initiate=true. would otherwise check for presence
2723 13 : weathersimulation = wthrsim;
2724 13 : state.dataThermalComforts->avgDryBulbASH = 0.0;
2725 13 : state.dataThermalComforts->runningAverageASH = 0.0;
2726 13 : state.dataThermalComforts->monthlyTemp = 0.0;
2727 13 : inavgdrybulb = avgdrybulb;
2728 : } else {
2729 960 : weathersimulation = false;
2730 960 : inavgdrybulb = 0.0;
2731 : }
2732 :
2733 973 : if (initiate && weathersimulation) {
2734 5 : const bool statFileExists = FileSystem::fileExists(state.files.inStatFilePath.filePath);
2735 5 : const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
2736 :
2737 5 : readStat = 0;
2738 5 : if (statFileExists) {
2739 10 : auto statFile = state.files.inStatFilePath.open(state, "CalcThermalComfortAdapctiveASH55");
2740 460 : while (statFile.good()) {
2741 460 : auto lineIn = statFile.readLine();
2742 460 : if (has(lineIn.data, "Monthly Statistics for Dry Bulb temperatures")) {
2743 40 : for (i = 1; i <= 7; ++i) {
2744 35 : lineIn = statFile.readLine();
2745 : }
2746 5 : lineIn = statFile.readLine();
2747 5 : lineAvg = lineIn.data;
2748 5 : break;
2749 : }
2750 460 : }
2751 65 : for (i = 1; i <= 12; ++i) {
2752 60 : state.dataThermalComforts->monthlyTemp(i) = StrToReal(GetColumnUsingTabs(lineAvg, i + 2));
2753 : }
2754 5 : state.dataThermalComforts->useStatData = true;
2755 5 : } else if (epwFileExists) {
2756 : // determine number of days in year
2757 : int DaysInYear;
2758 0 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
2759 0 : DaysInYear = 366;
2760 : } else {
2761 0 : DaysInYear = 365;
2762 : }
2763 0 : state.dataThermalComforts->DailyAveOutTemp = 0.0;
2764 :
2765 0 : auto epwFile = state.files.inputWeatherFilePath.open(state, "CalcThermalComfortAdaptiveASH55");
2766 0 : for (i = 1; i <= 8; ++i) { // Headers
2767 0 : epwLine = epwFile.readLine().data;
2768 : }
2769 0 : jStartDay = state.dataEnvrn->DayOfYear - 1;
2770 0 : calcStartDay = jStartDay - 30;
2771 0 : if (calcStartDay >= 0) {
2772 0 : calcStartHr = 24 * calcStartDay + 1;
2773 0 : for (i = 1; i <= calcStartHr - 1; ++i) {
2774 0 : epwFile.readLine();
2775 : }
2776 0 : for (i = 1; i <= 30; ++i) {
2777 0 : state.dataThermalComforts->avgDryBulbASH = 0.0;
2778 0 : for (j = 1; j <= 24; ++j) {
2779 0 : epwLine = epwFile.readLine().data;
2780 0 : for (ind = 1; ind <= 6; ++ind) {
2781 0 : pos = index(epwLine, ',');
2782 0 : epwLine.erase(0, pos + 1);
2783 : }
2784 0 : pos = index(epwLine, ',');
2785 0 : dryBulb = StrToReal(epwLine.substr(0, pos));
2786 0 : state.dataThermalComforts->avgDryBulbASH += (dryBulb / 24.0);
2787 : }
2788 0 : state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->avgDryBulbASH;
2789 : }
2790 : } else { // Do special things for wrapping the epw
2791 0 : calcEndDay = jStartDay;
2792 0 : calcStartDay += DaysInYear;
2793 0 : calcEndHr = 24 * calcEndDay;
2794 0 : calcStartHr = 24 * calcStartDay + 1;
2795 0 : for (i = 1; i <= calcEndDay; ++i) {
2796 0 : state.dataThermalComforts->avgDryBulbASH = 0.0;
2797 0 : for (j = 1; j <= 24; ++j) {
2798 0 : epwLine = epwFile.readLine().data;
2799 0 : for (ind = 1; ind <= 6; ++ind) {
2800 0 : pos = index(epwLine, ',');
2801 0 : epwLine.erase(0, pos + 1);
2802 : }
2803 0 : pos = index(epwLine, ',');
2804 0 : dryBulb = StrToReal(epwLine.substr(0, pos));
2805 0 : state.dataThermalComforts->avgDryBulbASH += (dryBulb / 24.0);
2806 : }
2807 0 : state.dataThermalComforts->DailyAveOutTemp(i + 30 - calcEndDay) = state.dataThermalComforts->avgDryBulbASH;
2808 : }
2809 0 : for (i = calcEndHr + 1; i <= calcStartHr - 1; ++i) {
2810 0 : epwLine = epwFile.readLine().data;
2811 : }
2812 0 : for (i = 1; i <= 30 - calcEndDay; ++i) {
2813 0 : state.dataThermalComforts->avgDryBulbASH = 0.0;
2814 0 : for (j = 1; j <= 24; ++j) {
2815 0 : epwLine = epwFile.readLine().data;
2816 0 : for (ind = 1; ind <= 6; ++ind) {
2817 0 : pos = index(epwLine, ',');
2818 0 : epwLine.erase(0, pos + 1);
2819 : }
2820 0 : pos = index(epwLine, ',');
2821 0 : dryBulb = StrToReal(epwLine.substr(0, pos));
2822 0 : state.dataThermalComforts->avgDryBulbASH += (dryBulb / 24.0);
2823 : }
2824 0 : state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->avgDryBulbASH;
2825 : }
2826 : }
2827 0 : state.dataThermalComforts->useEpwData = true;
2828 0 : }
2829 973 : } else if (initiate && !weathersimulation) {
2830 8 : state.dataThermalComforts->runningAverageASH = inavgdrybulb;
2831 8 : state.dataThermalComforts->monthlyTemp = inavgdrybulb;
2832 8 : state.dataThermalComforts->avgDryBulbASH = 0.0;
2833 : }
2834 :
2835 973 : if (initiate) return;
2836 :
2837 960 : if (state.dataGlobal->BeginDayFlag && state.dataThermalComforts->useEpwData) {
2838 : // Update the running average, reset the daily avg
2839 0 : state.dataThermalComforts->DailyAveOutTemp(30) = state.dataThermalComforts->avgDryBulbASH;
2840 0 : Real64 sum = 0.0;
2841 0 : for (i = 1; i <= 29; i++) {
2842 0 : sum += state.dataThermalComforts->DailyAveOutTemp(i);
2843 : }
2844 0 : state.dataThermalComforts->runningAverageASH = (sum + state.dataThermalComforts->avgDryBulbASH) / 30.0;
2845 0 : for (i = 1; i <= 29; i++) {
2846 0 : state.dataThermalComforts->DailyAveOutTemp(i) = state.dataThermalComforts->DailyAveOutTemp(i + 1);
2847 : }
2848 0 : state.dataThermalComforts->avgDryBulbASH = 0.0;
2849 : }
2850 :
2851 : // If exists BeginMonthFlag we can use it to call InvJulianDay once per month.
2852 960 : if (state.dataGlobal->BeginDayFlag && state.dataThermalComforts->useStatData) {
2853 : // CALL InvJulianDay(DayOfYear,pMonth,pDay,0)
2854 : // runningAverageASH = monthlyTemp(pMonth)
2855 0 : state.dataThermalComforts->runningAverageASH = state.dataThermalComforts->monthlyTemp(state.dataEnvrn->Month);
2856 : }
2857 :
2858 : // Update the daily average
2859 : // IF (BeginHourFlag .and. useEpwData) THEN
2860 960 : if (state.dataGlobal->BeginHourFlag) {
2861 192 : state.dataThermalComforts->avgDryBulbASH += (state.dataEnvrn->OutDryBulbTemp / 24.0);
2862 : }
2863 :
2864 2496 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
2865 1536 : ++state.dataThermalComforts->PeopleNum) {
2866 1536 : if (!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AdaptiveASH55) continue;
2867 1536 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ZonePtr;
2868 1536 : state.dataThermalComforts->AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).ZTAVComf;
2869 1536 : if (state.dataRoomAir->anyNonMixingRoomAirModel) {
2870 288 : if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
2871 0 : state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
2872 288 : state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum);
2873 : }
2874 : }
2875 1536 : state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
2876 1536 : state.dataThermalComforts->OpTemp = (state.dataThermalComforts->AirTemp + state.dataThermalComforts->RadTemp) / 2.0;
2877 1536 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
2878 1536 : state.dataThermalComforts->OpTemp;
2879 1536 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ASHRAE55RunningMeanOutdoorTemp =
2880 1536 : state.dataThermalComforts->runningAverageASH;
2881 1536 : if (state.dataThermalComforts->runningAverageASH >= 10.0 && state.dataThermalComforts->runningAverageASH <= 33.5) {
2882 : // Calculate the comfort here (people/output handling loop)
2883 768 : numOccupants = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).NumberOfPeople *
2884 768 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).NumberOfPeoplePtr);
2885 768 : tComf = 0.31 * state.dataThermalComforts->runningAverageASH + 17.8;
2886 768 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).TComfASH55 = tComf;
2887 768 : if (numOccupants > 0) {
2888 438 : if (state.dataThermalComforts->OpTemp < tComf + 2.5 && state.dataThermalComforts->OpTemp > tComf - 2.5) {
2889 : // 80% and 90% limits okay
2890 336 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5590 = 1;
2891 336 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5580 = 1;
2892 102 : } else if (state.dataThermalComforts->OpTemp < tComf + 3.5 && state.dataThermalComforts->OpTemp > tComf - 3.5) {
2893 : // 80% only
2894 41 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5590 = 0;
2895 41 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5580 = 1;
2896 41 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetASH5590 += SysTimeElapsed;
2897 : } else {
2898 : // Neither
2899 61 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5590 = 0;
2900 61 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5580 = 0;
2901 61 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetASH5580 += SysTimeElapsed;
2902 61 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetASH5590 += SysTimeElapsed;
2903 : }
2904 : } else {
2905 : // Unoccupied
2906 330 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5590 = -1;
2907 330 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5580 = -1;
2908 : }
2909 : } else {
2910 : // Monthly temp out of range
2911 768 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5590 = -1;
2912 768 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveASH5580 = -1;
2913 768 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).TComfASH55 = -1.0;
2914 : }
2915 : }
2916 986 : }
2917 :
2918 291 : void CalcThermalComfortAdaptiveCEN15251(
2919 : EnergyPlusData &state,
2920 : bool const initiate, // true if supposed to initiate
2921 : ObjexxFCL::Optional_bool_const wthrsim, // true if this is a weather simulation
2922 : ObjexxFCL::Optional<Real64 const> avgdrybulb // approximate avg drybulb for design day. will be used as previous period in design day
2923 : )
2924 : {
2925 :
2926 : // SUBROUTINE INFORMATION:
2927 : // AUTHOR Tyler Hoyt
2928 : // DATE WRITTEN July 2011
2929 :
2930 : // PURPOSE OF THIS SUBROUTINE:
2931 : // Sets up and carries out CEN-15251 adaptive comfort model calculations.
2932 : // Output provided are state variables for the Category I, II, and III
2933 : // limits of the model, the comfort temperature, and the 5-day weighted
2934 : // moving average of the outdoor air temperature.
2935 :
2936 : // Using/Aliasing
2937 291 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
2938 : using OutputReportTabular::GetColumnUsingTabs;
2939 : using OutputReportTabular::StrToReal;
2940 :
2941 : // SUBROUTINE PARAMETER DEFINITIONS:
2942 : static Real64 constexpr alpha(0.8);
2943 : static constexpr std::array<Real64, 7> alpha_pow = {0.262144, 0.32768, 0.4096, 0.512, 0.64, 0.8, 1.0}; // alpha^(6-0)
2944 :
2945 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2946 291 : std::string epwLine;
2947 : Real64 dryBulb;
2948 : Real64 tComf;
2949 : Real64 tComfLow;
2950 : Real64 numOccupants;
2951 : int readStat;
2952 : int jStartDay;
2953 : int calcStartDay;
2954 : int calcStartHr;
2955 : int calcEndDay;
2956 : int calcEndHr;
2957 : std::string::size_type pos;
2958 : int ind;
2959 : int i;
2960 : int j;
2961 : bool weathersimulation;
2962 : Real64 inavgdrybulb;
2963 291 : int constexpr numHeaderRowsInEpw = 8;
2964 :
2965 291 : if (initiate) { // not optional on initiate=true. would otherwise check for presence
2966 3 : weathersimulation = wthrsim;
2967 3 : inavgdrybulb = avgdrybulb;
2968 3 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
2969 3 : state.dataThermalComforts->runningAverageCEN = 0.0;
2970 : } else {
2971 288 : weathersimulation = false;
2972 288 : inavgdrybulb = 0.0;
2973 : }
2974 :
2975 291 : if (initiate && weathersimulation) {
2976 1 : const bool epwFileExists = FileSystem::fileExists(state.files.inputWeatherFilePath.filePath);
2977 1 : readStat = 0;
2978 1 : if (epwFileExists) {
2979 : // determine number of days in year
2980 : int DaysInYear;
2981 1 : if (state.dataEnvrn->CurrentYearIsLeapYear) {
2982 0 : DaysInYear = 366;
2983 : } else {
2984 1 : DaysInYear = 365;
2985 : }
2986 :
2987 2 : auto epwFile = state.files.inputWeatherFilePath.open(state, "CalcThermalComfortAdaptiveCEN15251");
2988 9 : for (i = 1; i <= numHeaderRowsInEpw; ++i) {
2989 8 : epwFile.readLine();
2990 : }
2991 1 : jStartDay = state.dataEnvrn->DayOfYear - 1;
2992 1 : calcStartDay = jStartDay - 7;
2993 1 : if (calcStartDay > 0) {
2994 1 : calcStartHr = 24 * calcStartDay + 1;
2995 2713 : for (i = 1; i <= calcStartHr - 1; ++i) {
2996 2712 : epwFile.readLine();
2997 : }
2998 1 : state.dataThermalComforts->runningAverageCEN = 0.0;
2999 8 : for (i = 0; i < 7; ++i) {
3000 7 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
3001 175 : for (j = 1; j <= 24; ++j) {
3002 168 : epwLine = epwFile.readLine().data;
3003 1176 : for (ind = 1; ind <= 6; ++ind) {
3004 1008 : pos = index(epwLine, ',');
3005 1008 : epwLine.erase(0, pos + 1);
3006 : }
3007 168 : pos = index(epwLine, ',');
3008 168 : dryBulb = StrToReal(epwLine.substr(0, pos));
3009 168 : state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
3010 : }
3011 7 : state.dataThermalComforts->runningAverageCEN += alpha_pow[i] * state.dataThermalComforts->avgDryBulbCEN;
3012 : }
3013 : } else { // Do special things for wrapping the epw
3014 0 : calcEndDay = jStartDay;
3015 0 : calcStartDay += DaysInYear;
3016 0 : calcEndHr = 24 * calcEndDay;
3017 0 : calcStartHr = 24 * calcStartDay + 1;
3018 0 : for (i = 1; i <= calcEndDay; ++i) {
3019 0 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
3020 0 : for (j = 1; j <= 24; ++j) {
3021 0 : epwLine = epwFile.readLine().data;
3022 0 : for (ind = 1; ind <= 6; ++ind) {
3023 0 : pos = index(epwLine, ',');
3024 0 : epwLine.erase(0, pos + 1);
3025 : }
3026 0 : pos = index(epwLine, ',');
3027 0 : dryBulb = StrToReal(epwLine.substr(0, pos));
3028 0 : state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
3029 : }
3030 0 : state.dataThermalComforts->runningAverageCEN += std::pow(alpha, calcEndDay - i) * state.dataThermalComforts->avgDryBulbCEN;
3031 : }
3032 0 : for (i = calcEndHr + 1; i <= calcStartHr - 1; ++i) {
3033 0 : epwFile.readLine();
3034 : }
3035 0 : for (i = 0; i < 7 - calcEndDay; ++i) {
3036 0 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
3037 0 : for (j = 1; j <= 24; ++j) {
3038 0 : epwLine = epwFile.readLine().data;
3039 0 : for (ind = 1; ind <= 6; ++ind) {
3040 0 : pos = index(epwLine, ',');
3041 0 : epwLine.erase(0, pos + 1);
3042 : }
3043 0 : pos = index(epwLine, ',');
3044 0 : dryBulb = StrToReal(epwLine.substr(0, pos));
3045 0 : state.dataThermalComforts->avgDryBulbCEN += (dryBulb / 24.0);
3046 : }
3047 0 : state.dataThermalComforts->runningAverageCEN += alpha_pow[i] * state.dataThermalComforts->avgDryBulbCEN;
3048 : }
3049 : }
3050 1 : state.dataThermalComforts->runningAverageCEN *= (1.0 - alpha);
3051 1 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
3052 1 : state.dataThermalComforts->useEpwDataCEN = true;
3053 1 : state.dataThermalComforts->firstDaySet = true;
3054 1 : }
3055 291 : } else if (initiate && !weathersimulation) {
3056 2 : state.dataThermalComforts->runningAverageCEN = inavgdrybulb;
3057 2 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
3058 : }
3059 291 : if (initiate) return;
3060 :
3061 288 : if (state.dataGlobal->BeginDayFlag && !state.dataThermalComforts->firstDaySet) {
3062 : // Update the running average, reset the daily avg
3063 4 : state.dataThermalComforts->runningAverageCEN =
3064 2 : alpha * state.dataThermalComforts->runningAverageCEN + (1.0 - alpha) * state.dataThermalComforts->avgDryBulbCEN;
3065 2 : state.dataThermalComforts->avgDryBulbCEN = 0.0;
3066 : }
3067 :
3068 288 : state.dataThermalComforts->firstDaySet = false;
3069 :
3070 : // Update the daily average
3071 288 : if (state.dataGlobal->BeginHourFlag) {
3072 48 : state.dataThermalComforts->avgDryBulbCEN += (state.dataEnvrn->OutDryBulbTemp / 24.0);
3073 : }
3074 :
3075 576 : for (state.dataThermalComforts->PeopleNum = 1; state.dataThermalComforts->PeopleNum <= state.dataHeatBal->TotPeople;
3076 288 : ++state.dataThermalComforts->PeopleNum) {
3077 288 : if (!state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).AdaptiveCEN15251) continue;
3078 288 : state.dataThermalComforts->ZoneNum = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).ZonePtr;
3079 288 : state.dataThermalComforts->AirTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(state.dataThermalComforts->ZoneNum).ZTAVComf;
3080 288 : if (state.dataRoomAir->anyNonMixingRoomAirModel) {
3081 288 : if (state.dataRoomAir->IsZoneDispVent3Node(state.dataThermalComforts->ZoneNum) ||
3082 0 : state.dataRoomAir->IsZoneUFAD(state.dataThermalComforts->ZoneNum)) {
3083 288 : state.dataThermalComforts->AirTemp = state.dataRoomAir->TCMF(state.dataThermalComforts->ZoneNum);
3084 : }
3085 : }
3086 288 : state.dataThermalComforts->RadTemp = CalcRadTemp(state, state.dataThermalComforts->PeopleNum);
3087 288 : state.dataThermalComforts->OpTemp = (state.dataThermalComforts->AirTemp + state.dataThermalComforts->RadTemp) / 2.0;
3088 288 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortOpTemp =
3089 288 : state.dataThermalComforts->OpTemp;
3090 288 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).CEN15251RunningMeanOutdoorTemp =
3091 288 : state.dataThermalComforts->runningAverageCEN;
3092 288 : if (state.dataThermalComforts->runningAverageCEN >= 10.0 && state.dataThermalComforts->runningAverageCEN <= 30.0) {
3093 : // Calculate the comfort here (people/output handling loop)
3094 144 : numOccupants = state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).NumberOfPeople *
3095 144 : GetCurrentScheduleValue(state, state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).NumberOfPeoplePtr);
3096 144 : tComf = 0.33 * state.dataThermalComforts->runningAverageCEN + 18.8;
3097 144 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).TComfCEN15251 = tComf;
3098 144 : if (numOccupants > 0) {
3099 78 : if (state.dataThermalComforts->runningAverageCEN < 15) {
3100 0 : tComfLow = 23.75; // Lower limit is constant in this region
3101 : } else {
3102 78 : tComfLow = tComf;
3103 : }
3104 78 : if (state.dataThermalComforts->OpTemp < tComf + 2.0 && state.dataThermalComforts->OpTemp > tComfLow - 2.0) {
3105 : // Within Cat I, II, III Limits
3106 21 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatI = 1;
3107 21 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatII = 1;
3108 21 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatIII = 1;
3109 57 : } else if (state.dataThermalComforts->OpTemp < tComf + 3.0 && state.dataThermalComforts->OpTemp > tComfLow - 3.0) {
3110 : // Within Cat II, III Limits
3111 16 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatI = 0;
3112 16 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatII = 1;
3113 16 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatIII = 1;
3114 16 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetCEN15251CatI += SysTimeElapsed;
3115 41 : } else if (state.dataThermalComforts->OpTemp < tComf + 4.0 && state.dataThermalComforts->OpTemp > tComfLow - 4.0) {
3116 : // Within Cat III Limits
3117 25 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatI = 0;
3118 25 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatII = 0;
3119 25 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatIII = 1;
3120 25 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetCEN15251CatI += SysTimeElapsed;
3121 25 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetCEN15251CatII += SysTimeElapsed;
3122 : } else {
3123 : // None
3124 16 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatI = 0;
3125 16 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatII = 0;
3126 16 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatIII = 0;
3127 16 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetCEN15251CatI += SysTimeElapsed;
3128 16 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetCEN15251CatII += SysTimeElapsed;
3129 16 : state.dataHeatBal->People(state.dataThermalComforts->PeopleNum).TimeNotMetCEN15251CatIII += SysTimeElapsed;
3130 : }
3131 : } else {
3132 : // Unoccupied
3133 66 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatI = -1;
3134 66 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatII = -1;
3135 66 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatIII = -1;
3136 : }
3137 : } else {
3138 : // Monthly temp out of range
3139 144 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatI = -1;
3140 144 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatII = -1;
3141 144 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ThermalComfortAdaptiveCEN15251CatIII = -1;
3142 144 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).TComfCEN15251 = -1.0;
3143 : }
3144 : }
3145 291 : }
3146 :
3147 264 : void DynamicClothingModel(EnergyPlusData &state)
3148 : {
3149 : // SUBROUTINE INFORMATION:
3150 : // AUTHOR Kwang Ho Lee
3151 : // DATE WRITTEN June 2013
3152 :
3153 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3154 : Real64 TemporaryVariable;
3155 :
3156 264 : if (state.dataThermalComforts->TemporarySixAMTemperature < -5.0) {
3157 96 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue = 1.0;
3158 168 : } else if ((state.dataThermalComforts->TemporarySixAMTemperature >= -5.0) && (state.dataThermalComforts->TemporarySixAMTemperature < 5.0)) {
3159 72 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue =
3160 72 : 0.818 - 0.0364 * state.dataThermalComforts->TemporarySixAMTemperature;
3161 96 : } else if ((state.dataThermalComforts->TemporarySixAMTemperature >= 5.0) && (state.dataThermalComforts->TemporarySixAMTemperature < 26.0)) {
3162 96 : TemporaryVariable = -0.1635 - 0.0066 * state.dataThermalComforts->TemporarySixAMTemperature;
3163 96 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue = std::pow(10.0, TemporaryVariable);
3164 0 : } else if (state.dataThermalComforts->TemporarySixAMTemperature >= 26.0) {
3165 0 : state.dataThermalComforts->ThermalComfortData(state.dataThermalComforts->PeopleNum).ClothingValue = 0.46;
3166 : }
3167 264 : }
3168 :
3169 : } // namespace ThermalComfort
3170 :
3171 : } // namespace EnergyPlus
|