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