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 : // ObjexxFCL Headers
49 : #include <ObjexxFCL/Array.functions.hh>
50 : #include <ObjexxFCL/Array1D.hh>
51 : #include <ObjexxFCL/ArrayS.functions.hh>
52 : #include <ObjexxFCL/Fmath.hh>
53 : #include <ObjexxFCL/member.functions.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Data/EnergyPlusData.hh>
57 : #include <EnergyPlus/DataEnvironment.hh>
58 : #include <EnergyPlus/DataErrorTracking.hh>
59 : #include <EnergyPlus/DataHVACGlobals.hh>
60 : #include <EnergyPlus/DataHeatBalFanSys.hh>
61 : #include <EnergyPlus/DataHeatBalance.hh>
62 : #include <EnergyPlus/DataLoopNode.hh>
63 : #include <EnergyPlus/DataRoomAirModel.hh>
64 : #include <EnergyPlus/DataSurfaces.hh>
65 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
66 : #include <EnergyPlus/DataZoneEquipment.hh>
67 : #include <EnergyPlus/FluidProperties.hh>
68 : #include <EnergyPlus/General.hh>
69 : #include <EnergyPlus/InternalHeatGains.hh>
70 : #include <EnergyPlus/OutputProcessor.hh>
71 : #include <EnergyPlus/Psychrometrics.hh>
72 : #include <EnergyPlus/RoomAirModelUserTempPattern.hh>
73 : #include <EnergyPlus/ScheduleManager.hh>
74 : #include <EnergyPlus/UtilityRoutines.hh>
75 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
76 :
77 : namespace EnergyPlus::RoomAirModelUserTempPattern {
78 :
79 : // MODULE INFORMATION:
80 : // AUTHOR Brent Griffith
81 : // DATE WRITTEN August 2005 (started in January 2004)
82 : // RE-ENGINEERED
83 :
84 : // PURPOSE OF THIS MODULE:
85 : // This module is the main module for running the
86 : // user-defined temperature pattern model.
87 : // This "air model" doesn't predict anything about the room air
88 : // but provides a method for users to model the
89 : // impact of non-uniform air temps. the distribution of air temperatures
90 : // is defined by the user and referred to as a "pattern"
91 :
92 : // METHODOLOGY EMPLOYED:
93 : // This module contains all subroutines required by the
94 : // user defined temperature pattern roomair modeling.
95 : // See DataRoomAir.cc for variable declarations
96 :
97 : // Using/Aliasing
98 : using namespace DataRoomAirModel;
99 :
100 : // Functions
101 :
102 37512 : void ManageUserDefinedPatterns(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
103 : {
104 :
105 : // SUBROUTINE INFORMATION:
106 : // AUTHOR Brent Griffith
107 : // DATE WRITTEN January 2004/Aug 2005
108 : // MODIFIED na
109 : // RE-ENGINEERED na
110 :
111 : // PURPOSE OF THIS SUBROUTINE:
112 : // manage the user-defined air temp. distribution model
113 :
114 : // METHODOLOGY EMPLOYED:
115 : // calls subroutines
116 :
117 : // transfer data from surface domain to air domain for the specified zone
118 37512 : InitTempDistModel(state, ZoneNum);
119 :
120 37512 : GetSurfHBDataForTempDistModel(state, ZoneNum);
121 :
122 : // perform TempDist model calculations
123 37512 : CalcTempDistModel(state, ZoneNum);
124 :
125 : // transfer data from air domain back to surface domain for the specified zone
126 37512 : SetSurfHBDataForTempDistModel(state, ZoneNum);
127 37512 : }
128 :
129 : //****************************************************
130 :
131 37512 : void InitTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
132 : {
133 :
134 : // SUBROUTINE INFORMATION:
135 : // AUTHOR <author>
136 : // DATE WRITTEN <date_written>
137 : // MODIFIED na
138 : // RE-ENGINEERED na
139 :
140 : int SurfNum; // do loop counter
141 :
142 37512 : if (state.dataRoomAirModelTempPattern->MyOneTimeFlag) {
143 1 : state.dataRoomAirModelTempPattern->MyEnvrnFlag.dimension(state.dataGlobal->NumOfZones, true);
144 1 : state.dataRoomAirModelTempPattern->MyOneTimeFlag = false;
145 : }
146 :
147 37512 : if (state.dataGlobal->BeginEnvrnFlag && state.dataRoomAirModelTempPattern->MyEnvrnFlag(ZoneNum)) {
148 81 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean = 23.0;
149 81 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = 23.0;
150 81 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = 23.0;
151 81 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = 23.0;
152 81 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient = 0.0;
153 3780 : for (SurfNum = 1; SurfNum <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++SurfNum) {
154 3699 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(SurfNum).TadjacentAir = 23.0;
155 : }
156 81 : state.dataRoomAirModelTempPattern->MyEnvrnFlag(ZoneNum) = false;
157 : }
158 :
159 37512 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataRoomAirModelTempPattern->MyEnvrnFlag(ZoneNum) = true;
160 :
161 : // init report variable
162 37512 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient = 0.0;
163 37512 : }
164 :
165 37512 : void GetSurfHBDataForTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
166 : {
167 :
168 : // SUBROUTINE INFORMATION:
169 : // AUTHOR B. Griffith
170 : // DATE WRITTEN August 2005
171 : // MODIFIED
172 : // RE-ENGINEERED na
173 :
174 : // PURPOSE OF THIS SUBROUTINE:
175 : // map data from Heat Balance domain to Room Air Modeling Domain
176 : // for the current zone, (only need mean air temp)
177 : // also acts as an init routine
178 :
179 : // METHODOLOGY EMPLOYED:
180 : // use ZT from DataHeatBalFanSys
181 :
182 : // Using/Aliasing
183 :
184 : // intialize in preperation for calculations
185 37512 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
186 37512 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
187 37512 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
188 1750560 : for (auto &e : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf)
189 1713048 : e.TadjacentAir = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
190 :
191 : // the only input this method needs is the zone MAT or ZT or ZTAV ? (original was ZT)
192 37512 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean =
193 37512 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT; // this is lagged from previous corrector result
194 37512 : }
195 :
196 : //*****************************************************************************************
197 :
198 37512 : void CalcTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
199 : {
200 :
201 : // SUBROUTINE INFORMATION:
202 : // AUTHOR Brent Griffith
203 : // DATE WRITTEN August 2005
204 : // MODIFIED
205 : // RE-ENGINEERED
206 :
207 : // PURPOSE OF THIS SUBROUTINE:
208 : // figure out which pattern is scheduled and call
209 : // appropriate subroutine
210 :
211 : // Using/Aliasing
212 : using General::FindNumberInList;
213 : using ScheduleManager::GetCurrentScheduleValue;
214 :
215 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
216 : // unused INTEGER :: thisZoneInfo
217 : Real64 AvailTest;
218 : int CurntPatternKey;
219 : int CurPatrnID;
220 :
221 : // first determine availability
222 37512 : AvailTest = GetCurrentScheduleValue(state, state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).AvailSchedID);
223 :
224 37512 : if ((AvailTest != 1.0) || (!state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).IsUsed)) {
225 : // model not to be used. Use complete mixing method
226 :
227 3498 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
228 3498 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
229 3498 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
230 166155 : for (auto &e : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf)
231 162657 : e.TadjacentAir = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
232 :
233 6996 : return;
234 :
235 : } else { // choose pattern and call subroutine
236 :
237 34014 : CurntPatternKey = GetCurrentScheduleValue(state, state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).PatternSchedID);
238 :
239 34014 : CurPatrnID = FindNumberInList(CurntPatternKey, state.dataRoomAirMod->RoomAirPattern, &TemperaturePatternStruct::PatrnID);
240 :
241 34014 : if (CurPatrnID == 0) {
242 : // throw error here ? way to test schedules before getting to this point?
243 0 : ShowFatalError(state, format("User defined room air pattern index not found: {}", CurntPatternKey));
244 0 : return;
245 : }
246 :
247 34014 : switch (state.dataRoomAirMod->RoomAirPattern(CurPatrnID).PatternMode) {
248 10492 : case DataRoomAirModel::UserDefinedPatternType::ConstGradTemp: {
249 10492 : FigureConstGradPattern(state, CurPatrnID, ZoneNum);
250 10492 : } break;
251 17860 : case DataRoomAirModel::UserDefinedPatternType::TwoGradInterp: {
252 17860 : FigureTwoGradInterpPattern(state, CurPatrnID, ZoneNum);
253 17860 : } break;
254 4868 : case DataRoomAirModel::UserDefinedPatternType::NonDimenHeight: {
255 4868 : FigureHeightPattern(state, CurPatrnID, ZoneNum);
256 4868 : } break;
257 794 : case DataRoomAirModel::UserDefinedPatternType::SurfMapTemp:
258 794 : FigureSurfMapPattern(state, CurPatrnID, ZoneNum);
259 794 : break;
260 0 : default: {
261 : // should not come here
262 0 : break;
263 : }
264 : }
265 : } // availability control construct
266 : }
267 :
268 794 : void FigureSurfMapPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
269 : {
270 :
271 : // SUBROUTINE INFORMATION:
272 : // AUTHOR B Griffith
273 : // DATE WRITTEN August 2005
274 : // MODIFIED na
275 : // RE-ENGINEERED na
276 :
277 : // PURPOSE OF THIS SUBROUTINE:
278 : // main calculation routine for surface pattern
279 :
280 : // METHODOLOGY EMPLOYED:
281 : // simple polling and applying prescribed
282 : // delta Tai's to current mean air temp
283 : // on a surface by surface basis
284 :
285 : // Using/Aliasing
286 : using General::FindNumberInList;
287 :
288 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
289 : Real64 Tmean;
290 : int found;
291 : int i;
292 :
293 794 : Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
294 :
295 39700 : for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
296 : // cycle through zone surfaces and look for match
297 77812 : found = FindNumberInList(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).SurfID,
298 38906 : state.dataRoomAirMod->RoomAirPattern(PattrnID).MapPatrn.SurfID,
299 38906 : state.dataRoomAirMod->RoomAirPattern(PattrnID).MapPatrn.NumSurfs);
300 38906 : if (found != 0) { // if surf is in map then assign, else give it MAT
301 7146 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir =
302 7146 : state.dataRoomAirMod->RoomAirPattern(PattrnID).MapPatrn.DeltaTai(found) + Tmean;
303 : } else {
304 31760 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = Tmean;
305 : }
306 : }
307 :
308 794 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTstat + Tmean;
309 794 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTleaving + Tmean;
310 794 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTexhaust + Tmean;
311 794 : }
312 :
313 4868 : void FigureHeightPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
314 : {
315 :
316 : // SUBROUTINE INFORMATION:
317 : // AUTHOR B Griffith
318 : // DATE WRITTEN August 2005
319 : // MODIFIED na
320 : // RE-ENGINEERED na
321 :
322 : // PURPOSE OF THIS SUBROUTINE:
323 : // calculate the pattern for non-dimensional vertical profile
324 :
325 : // METHODOLOGY EMPLOYED:
326 : // treat profile as lookup table and interpolate
327 :
328 : // Using/Aliasing
329 : using FluidProperties::FindArrayIndex;
330 :
331 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
332 : Real64 Tmean;
333 : int lowSideID;
334 : int highSideID;
335 : Real64 thisZeta;
336 : int i;
337 : Real64 lowSideZeta;
338 : Real64 hiSideZeta;
339 : Real64 fractBtwn;
340 : Real64 tmpDeltaTai;
341 :
342 4868 : tmpDeltaTai = 0.0;
343 4868 : Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
344 :
345 198220 : for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
346 :
347 193352 : thisZeta = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).Zeta;
348 193352 : lowSideID = FindArrayIndex(thisZeta, state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn);
349 193352 : highSideID = lowSideID + 1;
350 193352 : if (lowSideID == 0) lowSideID = 1; // protect against array bounds
351 :
352 193352 : lowSideZeta = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn(lowSideID);
353 193352 : if (highSideID <= isize(state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn)) {
354 188484 : hiSideZeta = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn(highSideID);
355 : } else { // trap array bounds
356 4868 : hiSideZeta = lowSideZeta;
357 : }
358 193352 : if ((hiSideZeta - lowSideZeta) != 0.0) {
359 183616 : fractBtwn = (thisZeta - lowSideZeta) / (hiSideZeta - lowSideZeta);
360 367232 : tmpDeltaTai = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(lowSideID) +
361 367232 : fractBtwn * (state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(highSideID) -
362 183616 : state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(lowSideID));
363 :
364 : } else { // would divide by zero, using low side value
365 :
366 9736 : tmpDeltaTai = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(lowSideID);
367 : }
368 :
369 193352 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = tmpDeltaTai + Tmean;
370 :
371 : } // surfaces in this zone
372 :
373 4868 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTstat + Tmean;
374 4868 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTleaving + Tmean;
375 4868 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTexhaust + Tmean;
376 4868 : }
377 :
378 17860 : void FigureTwoGradInterpPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
379 : {
380 :
381 : // SUBROUTINE INFORMATION:
382 : // AUTHOR B Griffith
383 : // DATE WRITTEN Aug 2005
384 : // MODIFIED na
385 : // RE-ENGINEERED na
386 :
387 : // PURPOSE OF THIS SUBROUTINE:
388 : // calculate two gradient interpolation pattern
389 :
390 : // METHODOLOGY EMPLOYED:
391 : // Case statement controls how interpolations are done
392 : // based on user selected mode.
393 : // calculations vary by mode
394 :
395 : // Using/Aliasing
396 :
397 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
398 : Real64 Tmean; // MAT deg C
399 : Real64 Grad; // vertical temperature gradient C/m
400 : Real64 DeltaT; // temperature difference
401 : Real64 CoolLoad; // sensible cooling load
402 : Real64 HeatLoad; // sensible heating load
403 : Real64 ZetaTmean; // non-dimensional height for mean air temp
404 : int i; // do loop index
405 : Real64 thisZeta; // non-dimensional height
406 : Real64 DeltaHeight; // height difference in m
407 : Real64 tempDeltaTai; // temporary temperature difference
408 :
409 17860 : if (state.dataRoomAirModelTempPattern->MyOneTimeFlag2) {
410 1 : state.dataRoomAirModelTempPattern->SetupOutputFlag.dimension(state.dataGlobal->NumOfZones, true); // init
411 1 : state.dataRoomAirModelTempPattern->MyOneTimeFlag2 = false;
412 : }
413 :
414 17860 : if (state.dataRoomAirModelTempPattern->SetupOutputFlag(ZoneNum)) {
415 24 : SetupOutputVariable(state,
416 : "Room Air Zone Vertical Temperature Gradient",
417 : OutputProcessor::Unit::K_m,
418 6 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient,
419 : OutputProcessor::SOVTimeStepType::HVAC,
420 : OutputProcessor::SOVStoreType::State,
421 12 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneName);
422 :
423 6 : state.dataRoomAirModelTempPattern->SetupOutputFlag(ZoneNum) = false;
424 : }
425 :
426 17860 : Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
427 :
428 : // determine gradient depending on mode
429 17860 : switch (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.InterpolationMode) {
430 2696 : case DataRoomAirModel::UserDefinedPatternMode::OutdoorDryBulb: {
431 10784 : Grad = OutdoorDryBulbGrad(state.dataHeatBal->Zone(ZoneNum).OutDryBulbTemp,
432 2696 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale,
433 2696 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient,
434 2696 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale,
435 2696 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
436 2696 : } break;
437 4409 : case DataRoomAirModel::UserDefinedPatternMode::ZoneAirTemp: {
438 4409 : if (Tmean >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale) {
439 1241 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
440 :
441 3168 : } else if (Tmean <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) {
442 :
443 2163 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
444 : } else { // interpolate
445 3015 : if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
446 2010 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) == 0.0) {
447 : // bad user input, trapped during get input
448 0 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
449 : } else {
450 :
451 2010 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
452 2010 : ((Tmean - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) /
453 2010 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
454 2010 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale)) *
455 2010 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
456 1005 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
457 : }
458 : }
459 4409 : } break;
460 2419 : case DataRoomAirModel::UserDefinedPatternMode::DeltaOutdoorZone: {
461 2419 : DeltaT = state.dataHeatBal->Zone(ZoneNum).OutDryBulbTemp - Tmean;
462 2419 : if (DeltaT >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale) {
463 569 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
464 :
465 1850 : } else if (DeltaT <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) {
466 :
467 1157 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
468 : } else { // interpolate
469 2079 : if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
470 1386 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) == 0.0) {
471 : // bad user input, trapped during get input
472 0 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
473 : } else {
474 :
475 1386 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
476 1386 : ((DeltaT - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) /
477 1386 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
478 1386 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale)) *
479 1386 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
480 693 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
481 : }
482 : }
483 2419 : } break;
484 4168 : case DataRoomAirModel::UserDefinedPatternMode::SensibleCooling: {
485 4168 : CoolLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).ZoneSNLoadCoolRate;
486 4168 : if (CoolLoad >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale) {
487 271 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
488 :
489 3897 : } else if (CoolLoad <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) {
490 :
491 2523 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
492 : } else { // interpolate
493 4122 : if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
494 2748 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) == 0.0) {
495 0 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
496 : } else {
497 :
498 2748 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
499 2748 : ((CoolLoad - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) /
500 2748 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
501 2748 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale)) *
502 2748 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
503 1374 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
504 : }
505 : }
506 4168 : } break;
507 4168 : case DataRoomAirModel::UserDefinedPatternMode::SensibleHeating: {
508 4168 : HeatLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).ZoneSNLoadHeatRate;
509 4168 : if (HeatLoad >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale) {
510 2296 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
511 :
512 1872 : } else if (HeatLoad <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) {
513 :
514 1871 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
515 : } else { // interpolate
516 3 : if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
517 2 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) == 0.0) {
518 0 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
519 : } else {
520 :
521 2 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
522 2 : ((HeatLoad - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) /
523 2 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
524 2 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale)) *
525 2 : (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
526 1 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
527 : }
528 : }
529 4168 : } break;
530 0 : default:
531 0 : break;
532 : }
533 :
534 17860 : ZetaTmean = 0.5; // by definition,
535 :
536 857475 : for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
537 839615 : thisZeta = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).Zeta;
538 :
539 839615 : DeltaHeight = -1.0 * (ZetaTmean - thisZeta) * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight;
540 :
541 839615 : tempDeltaTai = DeltaHeight * Grad;
542 :
543 839615 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = tempDeltaTai + Tmean;
544 : }
545 :
546 35720 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = -1.0 *
547 35720 : (0.5 * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight -
548 35720 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.TstatHeight) *
549 17860 : Grad +
550 : Tmean;
551 35720 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = -1.0 *
552 35720 : (0.5 * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight -
553 35720 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.TleavingHeight) *
554 17860 : Grad +
555 : Tmean;
556 35720 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = -1.0 *
557 35720 : (0.5 * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight -
558 35720 : state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.TexhaustHeight) *
559 17860 : Grad +
560 : Tmean;
561 :
562 17860 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient = Grad;
563 17860 : }
564 2696 : Real64 OutdoorDryBulbGrad(Real64 DryBulbTemp, // Zone(ZoneNum).OutDryBulbTemp
565 : Real64 UpperBound, // RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale
566 : Real64 HiGradient, // RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient
567 : Real64 LowerBound, // RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale
568 : Real64 LowGradient // RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient
569 : )
570 : {
571 : Real64 Grad;
572 2696 : if (DryBulbTemp >= UpperBound) {
573 1050 : Grad = HiGradient;
574 :
575 1646 : } else if (DryBulbTemp <= LowerBound) {
576 :
577 1434 : Grad = LowGradient;
578 : } else { // interpolate
579 :
580 212 : if ((UpperBound - LowerBound) == 0.0) {
581 : // bad user input. should be trapped during get input in RoomAirManager.cc
582 0 : Grad = LowGradient;
583 : } else {
584 :
585 212 : Grad = LowGradient + ((DryBulbTemp - LowerBound) / (UpperBound - LowerBound)) * (HiGradient - LowGradient);
586 : }
587 : }
588 2696 : return Grad;
589 : }
590 :
591 10492 : void FigureConstGradPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
592 : {
593 :
594 : // SUBROUTINE INFORMATION:
595 : // AUTHOR B. Griffith
596 : // DATE WRITTEN August 2005
597 : // MODIFIED na
598 : // RE-ENGINEERED na
599 :
600 : Real64 Tmean; // MAT
601 : int i; // loop counter
602 : Real64 Grad; // vertical temperature gradient
603 : Real64 ZetaTmean; // non-dimens. height for MAT, 0.5
604 : Real64 thisZeta; // temporary non-dimens height
605 : Real64 DeltaHeight; // temporary height difference
606 : Real64 tempDeltaTai; // temporary Delta Tai
607 :
608 10492 : Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
609 10492 : Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).GradPatrn.Gradient;
610 :
611 10492 : ZetaTmean = 0.5; // by definition,
612 :
613 489010 : for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
614 478518 : thisZeta = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).Zeta;
615 478518 : DeltaHeight = -1.0 * (ZetaTmean - thisZeta) * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight;
616 478518 : tempDeltaTai = DeltaHeight * Grad;
617 478518 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = tempDeltaTai + Tmean;
618 : }
619 :
620 10492 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTstat + Tmean;
621 10492 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTleaving + Tmean;
622 10492 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTexhaust + Tmean;
623 10492 : }
624 :
625 : //*****************************************************************************************
626 :
627 402 : Real64 FigureNDheightInZone(EnergyPlusData &state, int const thisHBsurf) // index in main Surface array
628 : {
629 : // FUNCTION INFORMATION:
630 : // AUTHOR B.Griffith
631 : // DATE WRITTEN aug 2005, Jan2004
632 : // MODIFIED na
633 : // RE-ENGINEERED na
634 :
635 : // PURPOSE OF THIS FUNCTION:
636 : // return a non-dimensional height zeta
637 :
638 : // METHODOLOGY EMPLOYED:
639 : // figure average floor height (follows code in surfacegeometry.cc
640 : // use ceiling height from Zone structure
641 : // non dimensionalize surface's centroid's Z value
642 :
643 : // Using/Aliasing
644 : using DataVectorTypes::Vector;
645 :
646 : // Return value
647 : Real64 FigureNDheightInZone;
648 :
649 : // FUNCTION PARAMETER DEFINITIONS:
650 402 : Real64 constexpr TolValue(0.0001);
651 :
652 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
653 : int thisZone;
654 : Real64 ZoneZorig;
655 : Real64 ZoneCeilHeight;
656 : Real64 Zcm;
657 : Real64 SurfMinZ;
658 : Real64 SurfMaxZ;
659 : Real64 Zeta;
660 : Real64 FloorCount;
661 : Real64 ZFlrAvg;
662 : Real64 ZMax;
663 : Real64 ZMin;
664 : int Count;
665 : Real64 Z1;
666 : Real64 Z2;
667 :
668 : // Get the centroid height for the surface
669 402 : Zcm = state.dataSurface->Surface(thisHBsurf).Centroid.z;
670 402 : thisZone = state.dataSurface->Surface(thisHBsurf).Zone;
671 :
672 : // this next Do block is copied from SurfaceGeometry.cc with modification for just floor Z
673 : // used find floor z.
674 402 : FloorCount = 0.0;
675 402 : ZFlrAvg = 0.0;
676 402 : ZMax = 0.0;
677 402 : ZMin = 0.0;
678 402 : Count = 0;
679 804 : for (int spaceNum : state.dataHeatBal->Zone(thisZone).spaceIndexes) {
680 402 : auto &thisSpace = state.dataHeatBal->space(spaceNum);
681 18860 : for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
682 18458 : if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Floor) {
683 : // Use Average Z for surface, more important for roofs than floors...
684 402 : ++FloorCount;
685 402 : Z1 = minval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z);
686 402 : Z2 = maxval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z);
687 402 : ZFlrAvg += (Z1 + Z2) / 2.0;
688 : }
689 18458 : if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Wall) {
690 : // Use Wall calculation in case no floor in zone
691 14472 : ++Count;
692 14472 : if (Count == 1) {
693 402 : ZMax = state.dataSurface->Surface(SurfNum).Vertex(1).z;
694 402 : ZMin = ZMax;
695 : }
696 14472 : ZMax = max(ZMax, maxval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z));
697 14472 : ZMin = min(ZMin, minval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z));
698 : }
699 : }
700 : }
701 402 : if (FloorCount > 0.0) {
702 402 : ZFlrAvg /= FloorCount;
703 : } else {
704 0 : ZFlrAvg = ZMin;
705 : }
706 402 : ZoneZorig = ZFlrAvg; // Z floor [M]
707 402 : ZoneCeilHeight = state.dataHeatBal->Zone(thisZone).CeilingHeight;
708 :
709 : // first check if some basic things are reasonable
710 :
711 402 : SurfMinZ = minval(state.dataSurface->Surface(thisHBsurf).Vertex, &Vector::z);
712 402 : SurfMaxZ = maxval(state.dataSurface->Surface(thisHBsurf).Vertex, &Vector::z);
713 :
714 402 : if (SurfMinZ < (ZoneZorig - TolValue)) {
715 0 : if (state.dataGlobal->DisplayExtraWarnings) {
716 0 : ShowWarningError(state, "RoomAirModelUserTempPattern: Problem in non-dimensional height calculation");
717 0 : ShowContinueError(
718 0 : state, "too low surface: " + state.dataSurface->Surface(thisHBsurf).Name + " in zone: " + state.dataHeatBal->Zone(thisZone).Name);
719 0 : ShowContinueError(state, format("**** Average floor height of zone is: {:.3R}", ZoneZorig));
720 0 : ShowContinueError(state, format("**** Surface minimum height is: {:.3R}", SurfMinZ));
721 : } else {
722 0 : ++state.dataErrTracking->TotalRoomAirPatternTooLow;
723 : }
724 : }
725 :
726 402 : if (SurfMaxZ > (ZoneZorig + ZoneCeilHeight + TolValue)) {
727 0 : if (state.dataGlobal->DisplayExtraWarnings) {
728 0 : ShowWarningError(state, "RoomAirModelUserTempPattern: Problem in non-dimensional height calculation");
729 0 : ShowContinueError(
730 0 : state, " too high surface: " + state.dataSurface->Surface(thisHBsurf).Name + " in zone: " + state.dataHeatBal->Zone(thisZone).Name);
731 0 : ShowContinueError(state, format("**** Average Ceiling height of zone is: {:.3R}", (ZoneZorig + ZoneCeilHeight)));
732 0 : ShowContinueError(state, format("**** Surface Maximum height is: {:.3R}", SurfMaxZ));
733 : } else {
734 0 : ++state.dataErrTracking->TotalRoomAirPatternTooHigh;
735 : }
736 : }
737 :
738 : // non dimensionalize.
739 402 : Zeta = (Zcm - ZoneZorig) / ZoneCeilHeight;
740 : // bound so that floors and ceiling are just in from endpoints.
741 :
742 402 : if (Zeta > 0.99) Zeta = 0.99;
743 :
744 402 : if (Zeta < 0.01) Zeta = 0.01;
745 :
746 402 : FigureNDheightInZone = Zeta;
747 :
748 402 : return FigureNDheightInZone;
749 : }
750 :
751 : //***************************************************
752 :
753 37512 : void SetSurfHBDataForTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
754 : {
755 :
756 : // SUBROUTINE INFORMATION:
757 : // AUTHOR Brent Griffith
758 : // DATE WRITTEN August 2005,Feb. 2004
759 : // MODIFIED na
760 : // RE-ENGINEERED na
761 :
762 : // PURPOSE OF THIS SUBROUTINE:
763 : // map data from air domain back to surface domain for each zone
764 : // collects code couples to remote data structures
765 :
766 : // METHODOLOGY EMPLOYED:
767 : // sets values in Heat balance variables
768 :
769 : // Using/Aliasing
770 : using DataHVACGlobals::RetTempMax;
771 : using DataHVACGlobals::RetTempMin;
772 : using InternalHeatGains::SumAllReturnAirLatentGains;
773 : using Psychrometrics::PsyCpAirFnW;
774 : using Psychrometrics::PsyHFnTdbW;
775 : using Psychrometrics::PsyHgAirFnWTdb;
776 : using Psychrometrics::PsyRhoAirFnPbTdbW;
777 :
778 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
779 : Real64 QRetAir; // Heat to return air from lights
780 : Real64 CpAir; // Air heat capacity [J/kg-K]
781 : Real64 TempRetAir; // Return air temperature [C]
782 : Real64 TempZoneAir; // Zone air temperature [C]
783 : int ZoneNode; // Node number of controlled zone
784 : Real64 MassFlowRA; // Return air mass flow [kg/s]
785 : Real64 FlowThisTS; // Window gap air mass flow [kg/s]
786 : Real64 WinGapFlowToRA; // Mass flow to return air from all airflow windows in zone [kg/s]
787 : Real64 WinGapFlowTtoRA; // Sum of mass flow times outlet temp for all airflow windows in zone [(kg/s)-C]
788 : Real64 WinGapTtoRA; // Temp of outlet flow mixture to return air from all airflow windows in zone [C]
789 : Real64 H2OHtOfVap; // Heat of vaporization of water (W/kg)
790 : Real64 RhoAir; // Density of air (Kg/m3)
791 : Real64 ZoneMult;
792 : Real64 SumRetAirLatentGainRate;
793 :
794 : // set air system leaving node conditions
795 : // this is not so easy. THis task is normally done in CalcZoneLeavingConditions
796 : // but efforts to do this update there were not successful.
797 : // Need to revisit how to best implement this. Ended up taking code from CalcZoneLeavingConditions
798 : // ZoneNum is already equal to ActualZoneNum , changed block of source
799 :
800 37512 : if (state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneNodeID != 0) {
801 : // the zone system node should get the conditions leaving the zone (but before return air heat gains are added).
802 37512 : state.dataLoopNodes->Node(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneNodeID).Temp =
803 37512 : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving;
804 : }
805 :
806 37512 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum);
807 75024 : for (int nodeCount = 1; nodeCount <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumReturnNodes; ++nodeCount) {
808 : // BEGIN BLOCK of code from CalcZoneLeavingConditions*********************************
809 37512 : int ReturnNode = state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ReturnNode(nodeCount);
810 37512 : ZoneNode = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneNodeID;
811 37512 : ZoneMult = state.dataHeatBal->Zone(ZoneNum).Multiplier * state.dataHeatBal->Zone(ZoneNum).ListMultiplier;
812 : // RETURN AIR HEAT GAIN from the Lights statement; this heat gain is stored in
813 : // Add sensible heat gain from refrigerated cases with under case returns
814 37512 : QRetAir = InternalHeatGains::zoneSumAllReturnAirConvectionGains(state, ZoneNum, ReturnNode);
815 :
816 37512 : CpAir = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat);
817 :
818 : // Need to add the energy to the return air from lights and from airflow windows. Where the heat
819 : // is added depends on if there is system flow or not. If there is system flow the heat is added
820 : // to the Zone Return Node. If there is no system flow then the heat is added back to the zone in the
821 : // Correct step through the SysDepZoneLoads variable.
822 :
823 37512 : MassFlowRA = state.dataLoopNodes->Node(ReturnNode).MassFlowRate / ZoneMult;
824 37512 : TempZoneAir = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving; // key difference from
825 37512 : TempRetAir = TempZoneAir;
826 37512 : WinGapFlowToRA = 0.0;
827 37512 : WinGapTtoRA = 0.0;
828 37512 : WinGapFlowTtoRA = 0.0;
829 :
830 37512 : if (state.dataHeatBal->Zone(ZoneNum).HasAirFlowWindowReturn) {
831 0 : for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
832 0 : auto &thisSpace = state.dataHeatBal->space(spaceNum);
833 0 : for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
834 0 : if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0 &&
835 0 : state.dataSurface->SurfWinAirflowDestination(SurfNum) == DataSurfaces::WindowAirFlowDestination::Return) {
836 0 : FlowThisTS = PsyRhoAirFnPbTdbW(state,
837 0 : state.dataEnvrn->OutBaroPress,
838 0 : state.dataSurface->SurfWinTAirflowGapOutlet(SurfNum),
839 0 : state.dataLoopNodes->Node(ZoneNode).HumRat) *
840 0 : state.dataSurface->SurfWinAirflowThisTS(SurfNum) * state.dataSurface->Surface(SurfNum).Width;
841 0 : WinGapFlowToRA += FlowThisTS;
842 0 : WinGapFlowTtoRA += FlowThisTS * state.dataSurface->SurfWinTAirflowGapOutlet(SurfNum);
843 : }
844 : }
845 : }
846 : }
847 37512 : if (WinGapFlowToRA > 0.0) WinGapTtoRA = WinGapFlowTtoRA / WinGapFlowToRA;
848 :
849 37512 : if (!state.dataHeatBal->Zone(ZoneNum).NoHeatToReturnAir) {
850 37512 : if (MassFlowRA > 0.0) {
851 37161 : if (WinGapFlowToRA > 0.0) {
852 : // Add heat-to-return from window gap airflow
853 0 : if (MassFlowRA >= WinGapFlowToRA) {
854 0 : TempRetAir = (WinGapFlowTtoRA + (MassFlowRA - WinGapFlowToRA) * TempZoneAir) / MassFlowRA;
855 : } else {
856 : // All of return air comes from flow through airflow windows
857 0 : TempRetAir = WinGapTtoRA;
858 : // Put heat from window airflow that exceeds return air flow into zone air
859 0 : thisZoneHB.SysDepZoneLoads += (WinGapFlowToRA - MassFlowRA) * CpAir * (WinGapTtoRA - TempZoneAir);
860 : }
861 : }
862 : // Add heat-to-return from lights
863 37161 : TempRetAir += QRetAir / (MassFlowRA * CpAir);
864 37161 : if (TempRetAir > RetTempMax) {
865 1 : state.dataLoopNodes->Node(ReturnNode).Temp = RetTempMax;
866 1 : if (!state.dataGlobal->ZoneSizingCalc) {
867 0 : thisZoneHB.SysDepZoneLoads += CpAir * MassFlowRA * (TempRetAir - RetTempMax);
868 : }
869 37160 : } else if (TempRetAir < RetTempMin) {
870 0 : state.dataLoopNodes->Node(ReturnNode).Temp = RetTempMin;
871 0 : if (!state.dataGlobal->ZoneSizingCalc) {
872 0 : thisZoneHB.SysDepZoneLoads += CpAir * MassFlowRA * (TempRetAir - RetTempMin);
873 : }
874 : } else {
875 37160 : state.dataLoopNodes->Node(ReturnNode).Temp = TempRetAir;
876 : }
877 : } else { // No return air flow
878 : // Assign all heat-to-return from window gap airflow to zone air
879 351 : if (WinGapFlowToRA > 0.0) thisZoneHB.SysDepZoneLoads += WinGapFlowToRA * CpAir * (WinGapTtoRA - TempZoneAir);
880 : // Assign all heat-to-return from lights to zone air
881 351 : if (QRetAir > 0.0) thisZoneHB.SysDepZoneLoads += QRetAir;
882 351 : state.dataLoopNodes->Node(ReturnNode).Temp = state.dataLoopNodes->Node(ZoneNode).Temp;
883 : }
884 : } else {
885 0 : state.dataLoopNodes->Node(ReturnNode).Temp = state.dataLoopNodes->Node(ZoneNode).Temp;
886 : }
887 :
888 : // Update the rest of the Return Air Node conditions, if the return air system exists!
889 37512 : state.dataLoopNodes->Node(ReturnNode).Press = state.dataLoopNodes->Node(ZoneNode).Press;
890 :
891 37512 : H2OHtOfVap = PsyHgAirFnWTdb(state.dataLoopNodes->Node(ZoneNode).HumRat, state.dataLoopNodes->Node(ReturnNode).Temp);
892 112536 : RhoAir = PsyRhoAirFnPbTdbW(
893 112536 : state, state.dataEnvrn->OutBaroPress, state.dataLoopNodes->Node(ReturnNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
894 :
895 : // Include impact of under case returns for refrigerated display cases when updateing return node
896 : // humidity ratio
897 37512 : if (!state.dataHeatBal->Zone(ZoneNum).NoHeatToReturnAir) {
898 37512 : if (MassFlowRA > 0) {
899 37161 : SumRetAirLatentGainRate = SumAllReturnAirLatentGains(state, ZoneNum, ReturnNode);
900 37161 : state.dataLoopNodes->Node(ReturnNode).HumRat =
901 37161 : state.dataLoopNodes->Node(ZoneNode).HumRat + (SumRetAirLatentGainRate / (H2OHtOfVap * MassFlowRA));
902 : } else {
903 : // If no mass flow rate exists, include the latent HVAC case credit with the latent Zone case credit
904 351 : state.dataLoopNodes->Node(ReturnNode).HumRat = state.dataLoopNodes->Node(ZoneNode).HumRat;
905 351 : state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToZone += state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToHVAC;
906 : // shouldn't the HVAC term be zeroed out then?
907 351 : SumRetAirLatentGainRate = SumAllReturnAirLatentGains(state, ZoneNum, 0);
908 351 : thisZoneHB.ZoneLatentGain += SumRetAirLatentGainRate;
909 : }
910 : } else {
911 0 : state.dataLoopNodes->Node(ReturnNode).HumRat = state.dataLoopNodes->Node(ZoneNode).HumRat;
912 0 : state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToZone += state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToHVAC;
913 : // shouldn't the HVAC term be zeroed out then?
914 0 : SumRetAirLatentGainRate = SumAllReturnAirLatentGains(state, ZoneNum, ReturnNode);
915 0 : thisZoneHB.ZoneLatentGain += SumRetAirLatentGainRate;
916 : }
917 :
918 37512 : state.dataLoopNodes->Node(ReturnNode).Enthalpy =
919 37512 : PsyHFnTdbW(state.dataLoopNodes->Node(ReturnNode).Temp, state.dataLoopNodes->Node(ReturnNode).HumRat);
920 :
921 : // END BLOCK of code from CalcZoneLeavingConditions*********************************
922 : }
923 :
924 : // set exhaust node leaving temp if present
925 37512 : if (allocated(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ExhaustAirNodeID)) {
926 37512 : auto const &APZoneInfo(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum));
927 37512 : auto const &EANodeID(APZoneInfo.ExhaustAirNodeID);
928 37512 : Real64 const Texhaust(APZoneInfo.Texhaust);
929 45848 : for (int i = 1, ie = EANodeID.u(); i <= ie; ++i) {
930 8336 : state.dataLoopNodes->Node(EANodeID(i)).Temp = Texhaust;
931 : }
932 : }
933 :
934 : // set thermostat reading for air system .
935 37512 : state.dataHeatBalFanSys->TempTstatAir(ZoneNum) = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat;
936 :
937 : // set results for all surface
938 75024 : for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
939 37512 : auto &thisSpace = state.dataHeatBal->space(spaceNum);
940 37512 : int j = 0;
941 1750560 : for (int i = thisSpace.HTSurfaceFirst; i <= thisSpace.HTSurfaceLast; ++i) {
942 1713048 : ++j;
943 1713048 : state.dataHeatBal->SurfTempEffBulkAir(i) = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(j).TadjacentAir;
944 : }
945 : }
946 :
947 : // set flag for reference air temperature mode
948 75024 : for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
949 37512 : auto &thisSpace = state.dataHeatBal->space(spaceNum);
950 1750560 : for (int i = thisSpace.HTSurfaceFirst; i <= thisSpace.HTSurfaceLast; ++i) {
951 1713048 : state.dataSurface->SurfTAirRef(i) = DataSurfaces::RefAirTemp::AdjacentAirTemp;
952 1713048 : state.dataSurface->SurfTAirRefRpt(i) = DataSurfaces::SurfTAirRefReportVals[state.dataSurface->SurfTAirRef(i)];
953 : }
954 : }
955 37512 : }
956 :
957 : //*****************************************************************************************
958 :
959 2313 : } // namespace EnergyPlus::RoomAirModelUserTempPattern
|