Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Fmath.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <EnergyPlus/Autosizing/All_Simple_Sizing.hh>
56 : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.hh>
57 : #include <EnergyPlus/BranchNodeConnections.hh>
58 : #include <EnergyPlus/Coils/CoilCoolingDX.hh>
59 : #include <EnergyPlus/CurveManager.hh>
60 : #include <EnergyPlus/DXCoils.hh>
61 : #include <EnergyPlus/Data/EnergyPlusData.hh>
62 : #include <EnergyPlus/DataContaminantBalance.hh>
63 : #include <EnergyPlus/DataHVACGlobals.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataLoopNode.hh>
66 : #include <EnergyPlus/DataSizing.hh>
67 : #include <EnergyPlus/EMSManager.hh>
68 : #include <EnergyPlus/General.hh>
69 : #include <EnergyPlus/GeneralRoutines.hh>
70 : #include <EnergyPlus/GlobalNames.hh>
71 : #include <EnergyPlus/HeatRecovery.hh>
72 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
73 : #include <EnergyPlus/NodeInputManager.hh>
74 : #include <EnergyPlus/OutputProcessor.hh>
75 : #include <EnergyPlus/OutputReportPredefined.hh>
76 : #include <EnergyPlus/Psychrometrics.hh>
77 : #include <EnergyPlus/ScheduleManager.hh>
78 : #include <EnergyPlus/UtilityRoutines.hh>
79 : #include <EnergyPlus/VariableSpeedCoils.hh>
80 :
81 : namespace EnergyPlus {
82 :
83 : namespace HeatRecovery {
84 :
85 : // Module containing the routines dealing with heat recovery from exhaust or relief air
86 :
87 : // MODULE INFORMATION:
88 : // AUTHOR Michael Wetter
89 : // DATE WRITTEN March 1999
90 : // MODIFIED F Buhl Nov 2000, D Shirey Feb 2003, R. Raustad April 2003
91 : // RE-ENGINEERED na
92 :
93 : // PURPOSE OF THIS MODULE:
94 : // To encapsulate the data and routines required to model heat
95 : // recovery components in the EnergyPlus HVAC simulation
96 :
97 : // METHODOLOGY EMPLOYED:
98 : // Heat exchanger effectiveness - NTU models are used.
99 :
100 : // REFERENCES:
101 : // M. Wetter, Simulation Model Air-to-Air Plate Heat Exchanger,LBNL Report 42354, 1999.
102 : // ARI Standard 1060-2001,Rating Air-to-Air Heat Exchangers for Energy Recovery Ventilation Equipment, www.ari.org
103 : // ASHRAE Standard 84, Method of Testing Air-To-Air Heat Exchangers, www.ashrae.org
104 : // U.S. Environmental Protection Agency software "SAVES" -
105 : // School Advanced Ventilation Engineering Software http://www.epa.gov/iaq/schooldesign/saves.html
106 :
107 : Real64 constexpr KELVZERO = 273.16;
108 : Real64 constexpr SMALL = 1.e-10;
109 : constexpr std::array<std::string_view, static_cast<int>(FrostControlOption::Num)> frostControlNamesUC = {
110 : "NONE", "EXHAUSTONLY", "EXHAUSTAIRRECIRCULATION", "MINIMUMEXHAUSTTEMPERATURE"};
111 :
112 26569 : void SimHeatRecovery(EnergyPlusData &state,
113 : std::string_view CompName, // name of the heat exchanger unit
114 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
115 : int &CompIndex, // Pointer to Component
116 : HVAC::FanOp const fanOp, // Supply air fan operating mode
117 : ObjexxFCL::Optional<Real64 const> HXPartLoadRatio, // Part load ratio requested of DX compressor
118 : ObjexxFCL::Optional_bool_const HXUnitEnable, // Flag to operate heat exchanger
119 : ObjexxFCL::Optional_int_const CompanionCoilIndex, // index of companion cooling coil
120 : ObjexxFCL::Optional_bool_const RegenInletIsOANode, // flag to determine if supply inlet is OA node, if so air flow cycles
121 : ObjexxFCL::Optional_bool_const EconomizerFlag, // economizer operation flag passed by airloop or OA sys
122 : ObjexxFCL::Optional_bool_const HighHumCtrlFlag, // high humidity control flag passed by airloop or OA sys
123 : ObjexxFCL::Optional_int_const CompanionCoilType_Num // cooling coil type of coil
124 : )
125 : {
126 :
127 : // SUBROUTINE INFORMATION:
128 : // AUTHOR Michael Wetter
129 : // DATE WRITTEN March 1999
130 : // MODIFIED Fred Buhl November 2000, R. Raustad FSEC - Feb 2009
131 : // RE-ENGINEERED na
132 :
133 : // PURPOSE OF THIS SUBROUTINE:
134 : // Manage the simulation of a heat recovery unit
135 :
136 26569 : if (state.dataHeatRecovery->GetInputFlag) {
137 6 : GetHeatRecoveryInput(state);
138 6 : state.dataHeatRecovery->GetInputFlag = false;
139 : }
140 :
141 : // Find the correct unit index
142 : int HeatExchNum; // index of unit being simulated
143 26569 : if (CompIndex == 0) {
144 14 : HeatExchNum = Util::FindItemInList(CompName, state.dataHeatRecovery->ExchCond);
145 14 : if (HeatExchNum == 0) {
146 0 : ShowFatalError(state, format("SimHeatRecovery: Unit not found={}", CompName));
147 : }
148 14 : CompIndex = HeatExchNum;
149 : } else {
150 26555 : HeatExchNum = CompIndex;
151 26555 : if (HeatExchNum > state.dataHeatRecovery->NumHeatExchangers || HeatExchNum < 1) {
152 0 : ShowFatalError(state,
153 0 : format("SimHeatRecovery: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
154 : HeatExchNum,
155 0 : state.dataHeatRecovery->NumHeatExchangers,
156 : CompName));
157 : }
158 26555 : if (state.dataHeatRecovery->CheckEquipName(HeatExchNum)) {
159 10 : if (CompName != state.dataHeatRecovery->ExchCond(HeatExchNum).Name) {
160 0 : ShowFatalError(state,
161 0 : format("SimHeatRecovery: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
162 : HeatExchNum,
163 : CompName,
164 0 : state.dataHeatRecovery->ExchCond(HeatExchNum).Name));
165 : }
166 10 : state.dataHeatRecovery->CheckEquipName(HeatExchNum) = false;
167 : }
168 : }
169 :
170 26569 : int CompanionCoilNum = present(CompanionCoilIndex) ? int(CompanionCoilIndex) : -1; // Index to companion cooling coil
171 26569 : int companionCoilType = present(CompanionCoilType_Num) ? int(CompanionCoilType_Num) : -1;
172 :
173 : bool HXUnitOn; // flag to enable heat exchanger
174 26569 : if (present(HXUnitEnable)) {
175 228 : HXUnitOn = HXUnitEnable;
176 : // When state.dataHeatRecovery->CalledFromParentObject is TRUE, this SIM routine was called by a parent object that passed in
177 : // HXUnitEnable. HX will use the DX coil part-load ratio (optional CompanionCoilIndex must be present) or PLR passed in if not used with
178 : // DX coil (optional CompanionCoilIndex must not be present).
179 228 : state.dataHeatRecovery->CalledFromParentObject = true;
180 : } else {
181 : // HX is placed on a BRANCH, optional arguments are not passed in from SimAirServingZones.
182 : // HX will calculate its own part-load ratio if optional HXUnitEnable flag is not present
183 26341 : if (present(HXPartLoadRatio)) {
184 26341 : HXUnitOn = (HXPartLoadRatio > 0.0);
185 : } else {
186 0 : HXUnitOn = true;
187 : }
188 26341 : state.dataHeatRecovery->CalledFromParentObject = false;
189 : }
190 :
191 26569 : auto &thisExch = state.dataHeatRecovery->ExchCond(HeatExchNum);
192 :
193 26569 : thisExch.initialize(state, CompanionCoilNum, companionCoilType);
194 :
195 : // call the correct heat exchanger calculation routine
196 26569 : switch (state.dataHeatRecovery->ExchCond(HeatExchNum).type) {
197 18 : case HVAC::HXType::AirToAir_FlatPlate: {
198 18 : thisExch.CalcAirToAirPlateHeatExch(state, HXUnitOn, EconomizerFlag, HighHumCtrlFlag);
199 18 : } break;
200 :
201 26547 : case HVAC::HXType::AirToAir_Generic: {
202 26547 : thisExch.CalcAirToAirGenericHeatExch(state, HXUnitOn, FirstHVACIteration, fanOp, EconomizerFlag, HighHumCtrlFlag, HXPartLoadRatio);
203 26547 : } break;
204 :
205 4 : case HVAC::HXType::Desiccant_Balanced: {
206 4 : Real64 PartLoadRatio = present(HXPartLoadRatio) ? Real64(HXPartLoadRatio) : 1.0; // Part load ratio requested of DX compressor
207 4 : bool RegInIsOANode = present(RegenInletIsOANode) && bool(RegenInletIsOANode);
208 4 : thisExch.CalcDesiccantBalancedHeatExch(state,
209 : HXUnitOn,
210 : FirstHVACIteration,
211 : fanOp,
212 : PartLoadRatio,
213 : CompanionCoilNum,
214 : companionCoilType,
215 : RegInIsOANode,
216 : EconomizerFlag,
217 : HighHumCtrlFlag);
218 4 : } break;
219 :
220 0 : default: {
221 0 : assert(false);
222 : } break;
223 : }
224 :
225 26569 : thisExch.UpdateHeatRecovery(state);
226 :
227 26569 : thisExch.ReportHeatRecovery(state);
228 26569 : }
229 :
230 18 : void GetHeatRecoveryInput(EnergyPlusData &state)
231 : {
232 :
233 : // SUBROUTINE INFORMATION:
234 : // AUTHOR Michael Wetter
235 : // DATE WRITTEN March 1999
236 : // MODIFIED F Buhl Nov 2000, D Shirey Feb 2003, R. Raustad FSEC - Feb 2009 (EconoLockout inputs)
237 : // RE-ENGINEERED na
238 :
239 : // PURPOSE OF THIS SUBROUTINE:
240 : // Obtains input data for heat recovery units and stores it in
241 : // appropriate data structures.
242 :
243 : // METHODOLOGY EMPLOYED:
244 : // Uses InputProcessor "Get" routines to obtain data.
245 :
246 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
247 : int NumAlphas; // Number of Alphas for each GetObjectItem call
248 : int NumNumbers; // Number of Numbers for each GetObjectItem call
249 : int IOStatus; // Used in GetObjectItem
250 18 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
251 18 : constexpr std::string_view RoutineName = "GetHeatRecoveryInput: "; // include trailing blank space
252 18 : constexpr std::string_view routineName = "GetHeatRecoveryInput";
253 18 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
254 :
255 18 : int NumAirToAirPlateExchs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "HeatExchanger:AirToAir:FlatPlate");
256 : int NumAirToAirGenericExchs =
257 18 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "HeatExchanger:AirToAir:SensibleAndLatent");
258 18 : int NumDesiccantBalancedExchs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "HeatExchanger:Desiccant:BalancedFlow");
259 : int NumDesBalExchsPerfDataType1 =
260 18 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "HeatExchanger:Desiccant:BalancedFlow:PerformanceDataType1");
261 18 : state.dataHeatRecovery->NumHeatExchangers = NumAirToAirPlateExchs + NumAirToAirGenericExchs + NumDesiccantBalancedExchs;
262 :
263 : // allocate the data array
264 18 : state.dataHeatRecovery->ExchCond.allocate(state.dataHeatRecovery->NumHeatExchangers);
265 18 : state.dataHeatRecovery->HeatExchangerUniqueNames.reserve(state.dataHeatRecovery->NumHeatExchangers);
266 18 : state.dataHeatRecovery->CheckEquipName.dimension(state.dataHeatRecovery->NumHeatExchangers, true);
267 :
268 18 : if (NumDesBalExchsPerfDataType1 > 0) {
269 4 : state.dataHeatRecovery->BalDesDehumPerfData.allocate(NumDesBalExchsPerfDataType1);
270 : }
271 :
272 : // loop over the air to air plate heat exchangers and load their input data
273 22 : for (int ExchIndex = 1; ExchIndex <= NumAirToAirPlateExchs; ++ExchIndex) {
274 4 : cCurrentModuleObject = "HeatExchanger:AirToAir:FlatPlate";
275 8 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
276 : cCurrentModuleObject,
277 : ExchIndex,
278 4 : state.dataIPShortCut->cAlphaArgs,
279 : NumAlphas,
280 4 : state.dataIPShortCut->rNumericArgs,
281 : NumNumbers,
282 : IOStatus,
283 4 : state.dataIPShortCut->lNumericFieldBlanks,
284 4 : state.dataIPShortCut->lAlphaFieldBlanks,
285 4 : state.dataIPShortCut->cAlphaFieldNames,
286 4 : state.dataIPShortCut->cNumericFieldNames);
287 :
288 4 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
289 4 : int const ExchNum = ExchIndex;
290 4 : auto &thisExchanger = state.dataHeatRecovery->ExchCond(ExchNum);
291 4 : thisExchanger.NumericFieldNames.allocate(NumNumbers);
292 4 : thisExchanger.NumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
293 :
294 4 : GlobalNames::VerifyUniqueInterObjectName(state,
295 4 : state.dataHeatRecovery->HeatExchangerUniqueNames,
296 4 : state.dataIPShortCut->cAlphaArgs(1),
297 : cCurrentModuleObject,
298 4 : state.dataIPShortCut->cAlphaFieldNames(1),
299 : ErrorsFound);
300 :
301 4 : thisExchanger.Name = state.dataIPShortCut->cAlphaArgs(1);
302 4 : thisExchanger.type = HVAC::HXType::AirToAir_FlatPlate;
303 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
304 3 : thisExchanger.availSched = Sched::GetScheduleAlwaysOn(state);
305 1 : } else if ((thisExchanger.availSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
306 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
307 0 : ErrorsFound = true;
308 : }
309 :
310 4 : constexpr std::array<std::string_view, static_cast<int>(HXConfiguration::Num)> hxConfigurationNamesUC = {
311 : "COUNTERFLOW", "PARALLELFLOW", "CROSSFLOWBOTHUNMIXED", "CROSS_FLOW_OTHER_NOT_USED"};
312 4 : thisExchanger.FlowArr = static_cast<HXConfiguration>(getEnumValue(hxConfigurationNamesUC, state.dataIPShortCut->cAlphaArgs(3)));
313 4 : if (thisExchanger.FlowArr == HXConfiguration::Invalid) {
314 0 : ShowSevereError(state, format("{}: incorrect flow arrangement: {}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(3)));
315 0 : ErrorsFound = true;
316 : }
317 :
318 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(4)) {
319 0 : thisExchanger.EconoLockOut = true;
320 : } else {
321 4 : BooleanSwitch toggle = getYesNoValue(state.dataIPShortCut->cAlphaArgs(4));
322 4 : if (toggle == BooleanSwitch::Invalid) {
323 0 : ShowSevereError(state, format("{}: incorrect econo lockout: {}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(4)));
324 : }
325 4 : thisExchanger.EconoLockOut = static_cast<bool>(toggle);
326 : }
327 :
328 4 : thisExchanger.hARatio = state.dataIPShortCut->rNumericArgs(1);
329 4 : thisExchanger.NomSupAirVolFlow = state.dataIPShortCut->rNumericArgs(2);
330 4 : thisExchanger.NomSupAirInTemp = state.dataIPShortCut->rNumericArgs(3);
331 4 : thisExchanger.NomSupAirOutTemp = state.dataIPShortCut->rNumericArgs(4);
332 4 : thisExchanger.NomSecAirVolFlow = state.dataIPShortCut->rNumericArgs(5);
333 4 : thisExchanger.NomSecAirInTemp = state.dataIPShortCut->rNumericArgs(6);
334 4 : thisExchanger.NomElecPower = state.dataIPShortCut->rNumericArgs(7);
335 4 : thisExchanger.SupInletNode = GetOnlySingleNode(state,
336 4 : state.dataIPShortCut->cAlphaArgs(5),
337 : ErrorsFound,
338 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirFlatPlate,
339 4 : thisExchanger.Name,
340 : DataLoopNode::NodeFluidType::Air,
341 : DataLoopNode::ConnectionType::Inlet,
342 : NodeInputManager::CompFluidStream::Primary,
343 : DataLoopNode::ObjectIsNotParent);
344 4 : thisExchanger.SupOutletNode = GetOnlySingleNode(state,
345 4 : state.dataIPShortCut->cAlphaArgs(6),
346 : ErrorsFound,
347 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirFlatPlate,
348 4 : thisExchanger.Name,
349 : DataLoopNode::NodeFluidType::Air,
350 : DataLoopNode::ConnectionType::Outlet,
351 : NodeInputManager::CompFluidStream::Primary,
352 : DataLoopNode::ObjectIsNotParent);
353 4 : thisExchanger.SecInletNode = GetOnlySingleNode(state,
354 4 : state.dataIPShortCut->cAlphaArgs(7),
355 : ErrorsFound,
356 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirFlatPlate,
357 4 : thisExchanger.Name,
358 : DataLoopNode::NodeFluidType::Air,
359 : DataLoopNode::ConnectionType::Inlet,
360 : NodeInputManager::CompFluidStream::Secondary,
361 : DataLoopNode::ObjectIsNotParent);
362 8 : thisExchanger.SecOutletNode = GetOnlySingleNode(state,
363 4 : state.dataIPShortCut->cAlphaArgs(8),
364 : ErrorsFound,
365 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirFlatPlate,
366 4 : thisExchanger.Name,
367 : DataLoopNode::NodeFluidType::Air,
368 : DataLoopNode::ConnectionType::Outlet,
369 : NodeInputManager::CompFluidStream::Secondary,
370 : DataLoopNode::ObjectIsNotParent);
371 :
372 12 : BranchNodeConnections::TestCompSet(state,
373 4 : HVAC::hxTypeNames[(int)thisExchanger.type],
374 : thisExchanger.Name,
375 4 : state.dataIPShortCut->cAlphaArgs(5),
376 4 : state.dataIPShortCut->cAlphaArgs(6),
377 : "Process Air Nodes");
378 :
379 : } // end of input loop over air to air plate heat exchangers
380 :
381 : // loop over the air to air generic heat exchangers and load their input data
382 30 : for (int ExchIndex = 1; ExchIndex <= NumAirToAirGenericExchs; ++ExchIndex) {
383 12 : cCurrentModuleObject = "HeatExchanger:AirToAir:SensibleAndLatent";
384 24 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
385 : cCurrentModuleObject,
386 : ExchIndex,
387 12 : state.dataIPShortCut->cAlphaArgs,
388 : NumAlphas,
389 12 : state.dataIPShortCut->rNumericArgs,
390 : NumNumbers,
391 : IOStatus,
392 12 : state.dataIPShortCut->lNumericFieldBlanks,
393 12 : state.dataIPShortCut->lAlphaFieldBlanks,
394 12 : state.dataIPShortCut->cAlphaFieldNames,
395 12 : state.dataIPShortCut->cNumericFieldNames);
396 :
397 12 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
398 12 : int const ExchNum = ExchIndex + NumAirToAirPlateExchs;
399 12 : auto &thisExchanger = state.dataHeatRecovery->ExchCond(ExchNum);
400 12 : thisExchanger.NumericFieldNames.allocate(NumNumbers);
401 12 : thisExchanger.NumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
402 :
403 12 : GlobalNames::VerifyUniqueInterObjectName(state,
404 12 : state.dataHeatRecovery->HeatExchangerUniqueNames,
405 12 : state.dataIPShortCut->cAlphaArgs(1),
406 : cCurrentModuleObject,
407 12 : state.dataIPShortCut->cAlphaFieldNames(1),
408 : ErrorsFound);
409 :
410 12 : thisExchanger.Name = state.dataIPShortCut->cAlphaArgs(1);
411 12 : thisExchanger.type = HVAC::HXType::AirToAir_Generic;
412 12 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
413 8 : thisExchanger.availSched = Sched::GetScheduleAlwaysOn(state);
414 4 : } else if ((thisExchanger.availSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
415 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
416 0 : ErrorsFound = true;
417 : }
418 12 : thisExchanger.NomSupAirVolFlow = state.dataIPShortCut->rNumericArgs(1);
419 12 : thisExchanger.HeatEffectSensible100 = state.dataIPShortCut->rNumericArgs(2);
420 12 : thisExchanger.HeatEffectLatent100 = state.dataIPShortCut->rNumericArgs(3);
421 12 : thisExchanger.CoolEffectSensible100 = state.dataIPShortCut->rNumericArgs(4);
422 12 : thisExchanger.CoolEffectLatent100 = state.dataIPShortCut->rNumericArgs(5);
423 12 : thisExchanger.SupInletNode = GetOnlySingleNode(state,
424 12 : state.dataIPShortCut->cAlphaArgs(3),
425 : ErrorsFound,
426 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirSensibleAndLatent,
427 12 : thisExchanger.Name,
428 : DataLoopNode::NodeFluidType::Air,
429 : DataLoopNode::ConnectionType::Inlet,
430 : NodeInputManager::CompFluidStream::Primary,
431 : DataLoopNode::ObjectIsNotParent);
432 12 : thisExchanger.SupOutletNode = GetOnlySingleNode(state,
433 12 : state.dataIPShortCut->cAlphaArgs(4),
434 : ErrorsFound,
435 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirSensibleAndLatent,
436 12 : thisExchanger.Name,
437 : DataLoopNode::NodeFluidType::Air,
438 : DataLoopNode::ConnectionType::Outlet,
439 : NodeInputManager::CompFluidStream::Primary,
440 : DataLoopNode::ObjectIsNotParent);
441 12 : thisExchanger.SecInletNode = GetOnlySingleNode(state,
442 12 : state.dataIPShortCut->cAlphaArgs(5),
443 : ErrorsFound,
444 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirSensibleAndLatent,
445 12 : thisExchanger.Name,
446 : DataLoopNode::NodeFluidType::Air,
447 : DataLoopNode::ConnectionType::Inlet,
448 : NodeInputManager::CompFluidStream::Secondary,
449 : DataLoopNode::ObjectIsNotParent);
450 12 : thisExchanger.SecOutletNode = GetOnlySingleNode(state,
451 12 : state.dataIPShortCut->cAlphaArgs(6),
452 : ErrorsFound,
453 : DataLoopNode::ConnectionObjectType::HeatExchangerAirToAirSensibleAndLatent,
454 12 : thisExchanger.Name,
455 : DataLoopNode::NodeFluidType::Air,
456 : DataLoopNode::ConnectionType::Outlet,
457 : NodeInputManager::CompFluidStream::Secondary,
458 : DataLoopNode::ObjectIsNotParent);
459 :
460 12 : thisExchanger.NomElecPower = state.dataIPShortCut->rNumericArgs(6);
461 :
462 12 : if (Util::SameString(state.dataIPShortCut->cAlphaArgs(7), "Yes")) {
463 4 : thisExchanger.ControlToTemperatureSetPoint = true;
464 : } else {
465 8 : if (!Util::SameString(state.dataIPShortCut->cAlphaArgs(7), "No")) {
466 0 : ShowSevereError(state, "Rotary HX Speed Modulation or Plate Bypass for Temperature Control for ");
467 0 : ShowContinueError(state, format("{} must be set to Yes or No", thisExchanger.Name));
468 0 : ErrorsFound = true;
469 : }
470 : }
471 :
472 12 : if (Util::SameString(state.dataIPShortCut->cAlphaArgs(8), "Plate")) {
473 6 : thisExchanger.ExchConfig = HXConfigurationType::Plate;
474 6 : } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(8), "Rotary")) {
475 6 : thisExchanger.ExchConfig = HXConfigurationType::Rotary;
476 : } else {
477 0 : ShowSevereError(state, format("{} configuration not found= {}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(8)));
478 0 : ShowContinueError(state, "HX configuration must be either Plate or Rotary");
479 0 : ErrorsFound = true;
480 : }
481 :
482 : // Added additional inputs for frost control
483 12 : thisExchanger.FrostControlType = static_cast<FrostControlOption>(getEnumValue(frostControlNamesUC, state.dataIPShortCut->cAlphaArgs(9)));
484 12 : if (thisExchanger.FrostControlType == FrostControlOption::Invalid) {
485 0 : ShowSevereError(state, format("Invalid Frost Control method for {} = {}", thisExchanger.Name, state.dataIPShortCut->cAlphaArgs(9)));
486 0 : ErrorsFound = true;
487 : }
488 :
489 12 : if (!Util::SameString(state.dataIPShortCut->cAlphaArgs(9), "None")) {
490 9 : thisExchanger.ThresholdTemperature = state.dataIPShortCut->rNumericArgs(7);
491 9 : thisExchanger.InitialDefrostTime = state.dataIPShortCut->rNumericArgs(8);
492 9 : thisExchanger.RateofDefrostTimeIncrease = state.dataIPShortCut->rNumericArgs(9);
493 : }
494 :
495 12 : if (state.dataIPShortCut->lAlphaFieldBlanks(10)) {
496 4 : thisExchanger.EconoLockOut = true;
497 : } else {
498 8 : BooleanSwitch toggle = getYesNoValue(state.dataIPShortCut->cAlphaArgs(10));
499 8 : if (toggle == BooleanSwitch::Invalid) {
500 0 : ShowSevereError(state, format("{}: incorrect econo lockout: {}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(10)));
501 : }
502 8 : thisExchanger.EconoLockOut = static_cast<bool>(toggle);
503 : }
504 :
505 : // read new curves here
506 12 : thisExchanger.HeatEffectSensibleCurveIndex =
507 12 : Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(11)); // convert curve name to number
508 12 : thisExchanger.HeatEffectLatentCurveIndex =
509 12 : Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(12)); // convert curve name to number
510 12 : thisExchanger.CoolEffectSensibleCurveIndex =
511 12 : Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(13)); // convert curve name to number
512 12 : thisExchanger.CoolEffectLatentCurveIndex =
513 12 : Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(14)); // convert curve name to number
514 :
515 36 : BranchNodeConnections::TestCompSet(state,
516 12 : HVAC::hxTypeNames[(int)thisExchanger.type],
517 : thisExchanger.Name,
518 12 : state.dataIPShortCut->cAlphaArgs(3),
519 12 : state.dataIPShortCut->cAlphaArgs(4),
520 : "Process Air Nodes");
521 : } // end of input loop over air to air generic heat exchangers
522 :
523 : // loop over the desiccant balanced heat exchangers and load their input data
524 22 : for (int ExchIndex = 1; ExchIndex <= NumDesiccantBalancedExchs; ++ExchIndex) {
525 4 : cCurrentModuleObject = "HeatExchanger:Desiccant:BalancedFlow";
526 8 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
527 : cCurrentModuleObject,
528 : ExchIndex,
529 4 : state.dataIPShortCut->cAlphaArgs,
530 : NumAlphas,
531 4 : state.dataIPShortCut->rNumericArgs,
532 : NumNumbers,
533 : IOStatus,
534 4 : state.dataIPShortCut->lNumericFieldBlanks,
535 4 : state.dataIPShortCut->lAlphaFieldBlanks,
536 4 : state.dataIPShortCut->cAlphaFieldNames,
537 4 : state.dataIPShortCut->cNumericFieldNames);
538 :
539 4 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
540 :
541 4 : int const ExchNum = ExchIndex + NumAirToAirPlateExchs + NumAirToAirGenericExchs;
542 4 : auto &thisExchanger = state.dataHeatRecovery->ExchCond(ExchNum);
543 4 : thisExchanger.NumericFieldNames.allocate(NumNumbers);
544 4 : thisExchanger.NumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
545 :
546 4 : GlobalNames::VerifyUniqueInterObjectName(state,
547 4 : state.dataHeatRecovery->HeatExchangerUniqueNames,
548 4 : state.dataIPShortCut->cAlphaArgs(1),
549 : cCurrentModuleObject,
550 4 : state.dataIPShortCut->cAlphaFieldNames(1),
551 : ErrorsFound);
552 :
553 4 : thisExchanger.Name = state.dataIPShortCut->cAlphaArgs(1);
554 4 : thisExchanger.type = HVAC::HXType::Desiccant_Balanced;
555 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
556 0 : thisExchanger.availSched = Sched::GetScheduleAlwaysOn(state);
557 4 : } else if ((thisExchanger.availSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
558 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
559 0 : ErrorsFound = true;
560 : }
561 : // desiccant HX's usually refer to process and regeneration air streams
562 : // In this module, Sup = Regeneration nodes and Sec = Process nodes
563 : // regeneration air inlet and outlet nodes
564 4 : thisExchanger.SupInletNode = GetOnlySingleNode(state,
565 4 : state.dataIPShortCut->cAlphaArgs(3),
566 : ErrorsFound,
567 : DataLoopNode::ConnectionObjectType::HeatExchangerDesiccantBalancedFlow,
568 4 : thisExchanger.Name,
569 : DataLoopNode::NodeFluidType::Air,
570 : DataLoopNode::ConnectionType::Inlet,
571 : NodeInputManager::CompFluidStream::Primary,
572 : DataLoopNode::ObjectIsNotParent);
573 4 : thisExchanger.SupOutletNode = GetOnlySingleNode(state,
574 4 : state.dataIPShortCut->cAlphaArgs(4),
575 : ErrorsFound,
576 : DataLoopNode::ConnectionObjectType::HeatExchangerDesiccantBalancedFlow,
577 4 : thisExchanger.Name,
578 : DataLoopNode::NodeFluidType::Air,
579 : DataLoopNode::ConnectionType::Outlet,
580 : NodeInputManager::CompFluidStream::Primary,
581 : DataLoopNode::ObjectIsNotParent);
582 : // process air inlet and outlet nodes
583 4 : thisExchanger.SecInletNode = GetOnlySingleNode(state,
584 4 : state.dataIPShortCut->cAlphaArgs(5),
585 : ErrorsFound,
586 : DataLoopNode::ConnectionObjectType::HeatExchangerDesiccantBalancedFlow,
587 4 : thisExchanger.Name,
588 : DataLoopNode::NodeFluidType::Air,
589 : DataLoopNode::ConnectionType::Inlet,
590 : NodeInputManager::CompFluidStream::Secondary,
591 : DataLoopNode::ObjectIsNotParent);
592 8 : thisExchanger.SecOutletNode = GetOnlySingleNode(state,
593 4 : state.dataIPShortCut->cAlphaArgs(6),
594 : ErrorsFound,
595 : DataLoopNode::ConnectionObjectType::HeatExchangerDesiccantBalancedFlow,
596 4 : thisExchanger.Name,
597 : DataLoopNode::NodeFluidType::Air,
598 : DataLoopNode::ConnectionType::Outlet,
599 : NodeInputManager::CompFluidStream::Secondary,
600 : DataLoopNode::ObjectIsNotParent);
601 :
602 : // Set up the component set for the process side of the HX (Sec = Process)
603 8 : BranchNodeConnections::TestCompSet(state,
604 4 : HVAC::hxTypeNames[(int)thisExchanger.type],
605 : thisExchanger.Name,
606 4 : state.dataLoopNodes->NodeID(thisExchanger.SecInletNode),
607 4 : state.dataLoopNodes->NodeID(thisExchanger.SecOutletNode),
608 : "Process Air Nodes");
609 :
610 : // A7 is the heat exchanger performance object type
611 : // It currently only has one choice key, with a default value, so currently no logic is needed
612 : // In the future if someone added another performance type, the logic could be added back here
613 : // HeatExchPerfType = state.dataIPShortCut->cAlphaArgs(7);
614 :
615 4 : thisExchanger.HeatExchPerfName = state.dataIPShortCut->cAlphaArgs(8);
616 :
617 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(9)) {
618 4 : thisExchanger.EconoLockOut = true;
619 : } else {
620 0 : BooleanSwitch toggle = getYesNoValue(state.dataIPShortCut->cAlphaArgs(9));
621 0 : if (toggle == BooleanSwitch::Invalid) {
622 0 : ShowSevereError(state, format("{}: incorrect econo lockout: {}", cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(9)));
623 : }
624 0 : thisExchanger.EconoLockOut = static_cast<bool>(toggle);
625 : }
626 :
627 : } // end of input loop over desiccant balanced heat exchangers
628 :
629 : // get performance data set for balanced desiccant heat exchanger
630 :
631 22 : for (int PerfDataIndex = 1; PerfDataIndex <= NumDesBalExchsPerfDataType1; ++PerfDataIndex) {
632 4 : cCurrentModuleObject = "HeatExchanger:Desiccant:BalancedFlow:PerformanceDataType1";
633 8 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
634 : cCurrentModuleObject,
635 : PerfDataIndex,
636 4 : state.dataIPShortCut->cAlphaArgs,
637 : NumAlphas,
638 4 : state.dataIPShortCut->rNumericArgs,
639 : NumNumbers,
640 : IOStatus,
641 4 : state.dataIPShortCut->lNumericFieldBlanks,
642 4 : state.dataIPShortCut->lAlphaFieldBlanks,
643 4 : state.dataIPShortCut->cAlphaFieldNames,
644 4 : state.dataIPShortCut->cNumericFieldNames);
645 4 : int const PerfDataNum = PerfDataIndex;
646 4 : auto &thisPerfData = state.dataHeatRecovery->BalDesDehumPerfData(PerfDataNum);
647 4 : thisPerfData.NumericFieldNames.allocate(NumNumbers);
648 4 : thisPerfData.NumericFieldNames = state.dataIPShortCut->cNumericFieldNames;
649 :
650 4 : thisPerfData.Name = state.dataIPShortCut->cAlphaArgs(1);
651 4 : thisPerfData.PerfType = cCurrentModuleObject;
652 4 : thisPerfData.NomSupAirVolFlow = state.dataIPShortCut->rNumericArgs(1);
653 : // check validity
654 4 : if (thisPerfData.NomSupAirVolFlow <= 0.0 && thisPerfData.NomSupAirVolFlow != DataSizing::AutoSize) {
655 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
656 0 : ShowContinueError(state, "Nominal air flow rate must be greater than zero.");
657 0 : ShowContinueError(state, format("... value entered = {:.6R}", thisPerfData.NomSupAirVolFlow));
658 0 : ErrorsFound = true;
659 : }
660 :
661 4 : thisPerfData.NomProcAirFaceVel = state.dataIPShortCut->rNumericArgs(2);
662 : // check validity
663 4 : if ((thisPerfData.NomProcAirFaceVel <= 0.0 && thisPerfData.NomProcAirFaceVel != DataSizing::AutoSize) ||
664 4 : thisPerfData.NomProcAirFaceVel > 6.0) {
665 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
666 0 : ShowContinueError(state, "Nominal air face velocity cannot be less than or equal to zero or greater than 6 m/s.");
667 0 : ShowContinueError(state, format("... value entered = {:.6R}", thisPerfData.NomProcAirFaceVel));
668 0 : ErrorsFound = true;
669 : }
670 4 : thisPerfData.NomElecPower = state.dataIPShortCut->rNumericArgs(3);
671 : // check validity
672 4 : if (thisPerfData.NomElecPower < 0.0) {
673 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
674 0 : ShowContinueError(state, "Nominal electric power cannot be less than zero.");
675 0 : ShowContinueError(state, format("... value entered = {:.6R}", thisPerfData.NomElecPower));
676 0 : ErrorsFound = true;
677 : }
678 :
679 : // regen outlet temp variables
680 36 : for (int i = 0; i < 8; ++i)
681 32 : thisPerfData.B[i] = state.dataIPShortCut->rNumericArgs(i + 4);
682 :
683 : // Check that the minimum is not greater than or equal to the maximum for each of the following model boundaries
684 4 : thisPerfData.T_MinRegenAirInHumRat = state.dataIPShortCut->rNumericArgs(12);
685 4 : thisPerfData.T_MaxRegenAirInHumRat = state.dataIPShortCut->rNumericArgs(13);
686 4 : if (thisPerfData.T_MinRegenAirInHumRat >= thisPerfData.T_MaxRegenAirInHumRat) {
687 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
688 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
689 0 : ShowContinueError(state, "... the minimum value of regeneration inlet air humidity ratio must be less than the maximum.");
690 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.T_MinRegenAirInHumRat));
691 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.T_MaxRegenAirInHumRat));
692 0 : ErrorsFound = true;
693 : }
694 4 : if (thisPerfData.T_MinRegenAirInHumRat < 0.0) {
695 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
696 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air temperature equation.");
697 0 : ShowContinueError(state, "... the minimum value of regeneration inlet air humidity ratio must be greater than or equal to 0.");
698 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.T_MinRegenAirInHumRat));
699 0 : ErrorsFound = true;
700 : }
701 4 : if (thisPerfData.T_MaxRegenAirInHumRat > 1.0) {
702 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
703 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air temperature equation.");
704 0 : ShowContinueError(state, "... the maximum value of regeneration inlet air humidity ratio must be less than or equal to 1.");
705 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.T_MaxRegenAirInHumRat));
706 0 : ErrorsFound = true;
707 : }
708 :
709 4 : thisPerfData.T_MinRegenAirInTemp = state.dataIPShortCut->rNumericArgs(14);
710 4 : thisPerfData.T_MaxRegenAirInTemp = state.dataIPShortCut->rNumericArgs(15);
711 4 : if (thisPerfData.T_MinRegenAirInTemp >= thisPerfData.T_MaxRegenAirInTemp) {
712 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
713 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
714 0 : ShowContinueError(state, "... the minimum value of regeneration inlet air temperature must be less than the maximum.");
715 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinRegenAirInTemp));
716 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxRegenAirInTemp));
717 0 : ErrorsFound = true;
718 : }
719 :
720 4 : thisPerfData.T_MinProcAirInHumRat = state.dataIPShortCut->rNumericArgs(16);
721 4 : thisPerfData.T_MaxProcAirInHumRat = state.dataIPShortCut->rNumericArgs(17);
722 4 : if (thisPerfData.T_MinProcAirInHumRat >= thisPerfData.T_MaxProcAirInHumRat) {
723 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
724 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
725 0 : ShowContinueError(state, "... the minimum value of process inlet air humidity ratio must be less than the maximum.");
726 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.T_MinProcAirInHumRat));
727 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.T_MaxProcAirInHumRat));
728 0 : ErrorsFound = true;
729 : }
730 4 : if (thisPerfData.T_MinProcAirInHumRat < 0.0) {
731 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
732 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air temperature equation.");
733 0 : ShowContinueError(state, "... the minimum value of process inlet air humidity ratio must be greater than or equal to 0.");
734 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.T_MinProcAirInHumRat));
735 0 : ErrorsFound = true;
736 : }
737 4 : if (thisPerfData.T_MaxProcAirInHumRat > 1.0) {
738 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
739 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air temperature equation.");
740 0 : ShowContinueError(state, "... the maximum value of process inlet air humidity ratio must be less than or equal to 1.");
741 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.T_MaxProcAirInHumRat));
742 0 : ErrorsFound = true;
743 : }
744 :
745 4 : thisPerfData.T_MinProcAirInTemp = state.dataIPShortCut->rNumericArgs(18);
746 4 : thisPerfData.T_MaxProcAirInTemp = state.dataIPShortCut->rNumericArgs(19);
747 4 : if (thisPerfData.T_MinProcAirInTemp >= thisPerfData.T_MaxProcAirInTemp) {
748 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
749 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
750 0 : ShowContinueError(state, "... the minimum value of process inlet air temperature must be less than the maximum.");
751 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinProcAirInTemp));
752 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxProcAirInTemp));
753 0 : ErrorsFound = true;
754 : }
755 :
756 4 : thisPerfData.T_MinFaceVel = state.dataIPShortCut->rNumericArgs(20);
757 4 : thisPerfData.T_MaxFaceVel = state.dataIPShortCut->rNumericArgs(21);
758 4 : if (thisPerfData.T_MinFaceVel >= thisPerfData.T_MaxFaceVel) {
759 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
760 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
761 0 : ShowContinueError(state, "... the minimum value of regen air velocity must be less than the maximum.");
762 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinFaceVel));
763 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxFaceVel));
764 0 : ErrorsFound = true;
765 : }
766 :
767 4 : thisPerfData.MinRegenAirOutTemp = state.dataIPShortCut->rNumericArgs(22);
768 4 : thisPerfData.MaxRegenAirOutTemp = state.dataIPShortCut->rNumericArgs(23);
769 4 : if (thisPerfData.MinRegenAirOutTemp >= thisPerfData.MaxRegenAirOutTemp) {
770 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
771 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
772 0 : ShowContinueError(state, "... the minimum value of regen outlet air temperature must be less than the maximum.");
773 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.MinRegenAirOutTemp));
774 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.MaxRegenAirOutTemp));
775 0 : ErrorsFound = true;
776 : }
777 :
778 4 : thisPerfData.T_MinRegenAirInRelHum = state.dataIPShortCut->rNumericArgs(24) / 100.0;
779 4 : thisPerfData.T_MaxRegenAirInRelHum = state.dataIPShortCut->rNumericArgs(25) / 100.0;
780 4 : if (thisPerfData.T_MinRegenAirInRelHum >= thisPerfData.T_MaxRegenAirInRelHum) {
781 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
782 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
783 0 : ShowContinueError(state, "... the minimum value of regen inlet air relative humidity must be less than the maximum.");
784 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinRegenAirInRelHum * 100.0));
785 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxRegenAirInRelHum * 100.0));
786 0 : ErrorsFound = true;
787 : }
788 4 : if (thisPerfData.T_MinRegenAirInRelHum < 0.0) {
789 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
790 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air temperature equation.");
791 0 : ShowContinueError(state, "... the minimum value of regen inlet air relative humidity must be greater than or equal to 0.");
792 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinRegenAirInRelHum * 100.0));
793 0 : ErrorsFound = true;
794 : }
795 4 : if (thisPerfData.T_MaxRegenAirInRelHum > 1.0) {
796 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
797 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air temperature equation.");
798 0 : ShowContinueError(state, "... the maximum value of regen inlet air relative humidity must be less than or equal to 100.");
799 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxRegenAirInRelHum * 100.0));
800 0 : ErrorsFound = true;
801 : }
802 :
803 4 : thisPerfData.T_MinProcAirInRelHum = state.dataIPShortCut->rNumericArgs(26) / 100.0;
804 4 : thisPerfData.T_MaxProcAirInRelHum = state.dataIPShortCut->rNumericArgs(27) / 100.0;
805 4 : if (thisPerfData.T_MinProcAirInRelHum >= thisPerfData.T_MaxProcAirInRelHum) {
806 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
807 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air temperature equation.");
808 0 : ShowContinueError(state, "... the minimum value of process inlet air relative humidity must be less than the maximum.");
809 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinProcAirInRelHum * 100.0));
810 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxProcAirInRelHum * 100.0));
811 0 : ErrorsFound = true;
812 : }
813 4 : if (thisPerfData.T_MinProcAirInRelHum < 0.0) {
814 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
815 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air temperature equation.");
816 0 : ShowContinueError(state, "... the minimum value of process inlet air relative humidity must be greater than or equal to 0.");
817 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.T_MinProcAirInRelHum * 100.0));
818 0 : ErrorsFound = true;
819 : }
820 4 : if (thisPerfData.T_MaxProcAirInRelHum > 1.0) {
821 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
822 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air temperature equation.");
823 0 : ShowContinueError(state, "... the maximum value of process inlet air relative humidity must be less than or equal to 100.");
824 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.T_MaxProcAirInRelHum * 100.0));
825 0 : ErrorsFound = true;
826 : }
827 :
828 : // regen outlet humidity ratio variables
829 36 : for (int i = 0; i < 8; ++i)
830 32 : thisPerfData.C[i] = state.dataIPShortCut->rNumericArgs(i + 28);
831 :
832 : // Check that the minimum is not greater than or equal to the maximum for each of the following model boundaries
833 4 : thisPerfData.H_MinRegenAirInHumRat = state.dataIPShortCut->rNumericArgs(36);
834 4 : thisPerfData.H_MaxRegenAirInHumRat = state.dataIPShortCut->rNumericArgs(37);
835 4 : if (thisPerfData.H_MinRegenAirInHumRat >= thisPerfData.H_MaxRegenAirInHumRat) {
836 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
837 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
838 0 : ShowContinueError(state, "... the minimum value of regeneration inlet air humidity ratio must be less than the maximum.");
839 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.H_MinRegenAirInHumRat));
840 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.H_MaxRegenAirInHumRat));
841 0 : ErrorsFound = true;
842 : }
843 4 : if (thisPerfData.H_MinRegenAirInHumRat < 0.0) {
844 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
845 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air humidity ratio equation.");
846 0 : ShowContinueError(state, "... the minimum value of regeneration inlet air humidity ratio must be greater than or equal to 0.");
847 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.H_MinRegenAirInHumRat));
848 0 : ErrorsFound = true;
849 : }
850 4 : if (thisPerfData.H_MaxRegenAirInHumRat > 1.0) {
851 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
852 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air humidity ratio equation.");
853 0 : ShowContinueError(state, "... the maximum value of regeneration inlet air humidity ratio must be less than or equal to 1.");
854 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.H_MaxRegenAirInHumRat));
855 0 : ErrorsFound = true;
856 : }
857 :
858 4 : thisPerfData.H_MinRegenAirInTemp = state.dataIPShortCut->rNumericArgs(38);
859 4 : thisPerfData.H_MaxRegenAirInTemp = state.dataIPShortCut->rNumericArgs(39);
860 4 : if (thisPerfData.H_MinRegenAirInTemp >= thisPerfData.H_MaxRegenAirInTemp) {
861 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
862 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
863 0 : ShowContinueError(state, "... the minimum value of regeneration inlet air temperature must be less than the maximum.");
864 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinRegenAirInTemp));
865 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxRegenAirInTemp));
866 0 : ErrorsFound = true;
867 : }
868 :
869 4 : thisPerfData.H_MinProcAirInHumRat = state.dataIPShortCut->rNumericArgs(40);
870 4 : thisPerfData.H_MaxProcAirInHumRat = state.dataIPShortCut->rNumericArgs(41);
871 4 : if (thisPerfData.H_MinProcAirInHumRat >= thisPerfData.H_MaxProcAirInHumRat) {
872 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
873 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
874 0 : ShowContinueError(state, "... the minimum value of process inlet air humidity ratio must be less than the maximum.");
875 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.H_MinProcAirInHumRat));
876 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.H_MaxProcAirInHumRat));
877 0 : ErrorsFound = true;
878 : }
879 4 : if (thisPerfData.H_MinProcAirInHumRat < 0.0) {
880 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
881 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air humidity ratio equation.");
882 0 : ShowContinueError(state, "... the minimum value of process inlet air humidity ratio must be greater than or equal to 0.");
883 0 : ShowContinueError(state, format("... minimum value entered by user = {:.6R}", thisPerfData.H_MinProcAirInHumRat));
884 0 : ErrorsFound = true;
885 : }
886 4 : if (thisPerfData.H_MaxProcAirInHumRat > 1.0) {
887 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
888 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air humidity ratio equation.");
889 0 : ShowContinueError(state, "... the maximum value of process inlet air humidity ratio must be less than or equal to 1.");
890 0 : ShowContinueError(state, format("... maximum value entered by user = {:.6R}", thisPerfData.H_MaxProcAirInHumRat));
891 0 : ErrorsFound = true;
892 : }
893 :
894 4 : thisPerfData.H_MinProcAirInTemp = state.dataIPShortCut->rNumericArgs(42);
895 4 : thisPerfData.H_MaxProcAirInTemp = state.dataIPShortCut->rNumericArgs(43);
896 4 : if (thisPerfData.H_MinProcAirInTemp >= thisPerfData.H_MaxProcAirInTemp) {
897 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
898 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
899 0 : ShowContinueError(state, "... the minimum value of process inlet air temperature must be less than the maximum.");
900 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinProcAirInTemp));
901 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxProcAirInTemp));
902 0 : ErrorsFound = true;
903 : }
904 :
905 4 : thisPerfData.H_MinFaceVel = state.dataIPShortCut->rNumericArgs(44);
906 4 : thisPerfData.H_MaxFaceVel = state.dataIPShortCut->rNumericArgs(45);
907 4 : if (thisPerfData.H_MinFaceVel >= thisPerfData.H_MaxFaceVel) {
908 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
909 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
910 0 : ShowContinueError(state, "... the minimum value of regen air velocity must be less than the maximum.");
911 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinFaceVel));
912 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxFaceVel));
913 0 : ErrorsFound = true;
914 : }
915 :
916 4 : thisPerfData.MinRegenAirOutHumRat = state.dataIPShortCut->rNumericArgs(46);
917 4 : thisPerfData.MaxRegenAirOutHumRat = state.dataIPShortCut->rNumericArgs(47);
918 4 : if (thisPerfData.MinRegenAirOutHumRat >= thisPerfData.MaxRegenAirOutHumRat) {
919 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
920 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
921 0 : ShowContinueError(state, "... the minimum value of regen outlet air humidity ratio must be less than the maximum.");
922 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.MinRegenAirOutHumRat));
923 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.MaxRegenAirOutHumRat));
924 0 : ErrorsFound = true;
925 : }
926 4 : if (thisPerfData.MinRegenAirOutHumRat < 0.0) {
927 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
928 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air humidity ratio equation.");
929 0 : ShowContinueError(state, "... the minimum value of regen outlet air humidity ratio must be greater than or equal to 0.");
930 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.MinRegenAirOutHumRat));
931 0 : ErrorsFound = true;
932 : }
933 4 : if (thisPerfData.MaxRegenAirOutHumRat > 1.0) {
934 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
935 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air humidity ratio equation.");
936 0 : ShowContinueError(state, "... the maximum value of regen outlet air humidity ratio must be less or equal to 1.");
937 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.MaxRegenAirOutHumRat));
938 0 : ErrorsFound = true;
939 : }
940 :
941 4 : thisPerfData.H_MinRegenAirInRelHum = state.dataIPShortCut->rNumericArgs(48) / 100.0;
942 4 : thisPerfData.H_MaxRegenAirInRelHum = state.dataIPShortCut->rNumericArgs(49) / 100.0;
943 4 : if (thisPerfData.H_MinRegenAirInRelHum >= thisPerfData.H_MaxRegenAirInRelHum) {
944 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
945 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
946 0 : ShowContinueError(state, "... the minimum value of regen inlet air relative humidity must be less than the maximum.");
947 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinRegenAirInRelHum * 100.0));
948 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxRegenAirInRelHum * 100.0));
949 0 : ErrorsFound = true;
950 : }
951 4 : if (thisPerfData.H_MinRegenAirInRelHum < 0.0) {
952 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
953 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air humidity ratio equation.");
954 0 : ShowContinueError(state, "... the minimum value of regen inlet air relative humidity must be greater than or equal to 0.");
955 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinRegenAirInRelHum * 100.0));
956 0 : ErrorsFound = true;
957 : }
958 4 : if (thisPerfData.H_MaxRegenAirInRelHum > 1.0) {
959 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
960 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air humidity ratio equation.");
961 0 : ShowContinueError(state, "... the maximum value of regen inlet air relative humidity must be less or equal to 100.");
962 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxRegenAirInRelHum * 100.0));
963 0 : ErrorsFound = true;
964 : }
965 :
966 4 : thisPerfData.H_MinProcAirInRelHum = state.dataIPShortCut->rNumericArgs(50) / 100.0;
967 4 : thisPerfData.H_MaxProcAirInRelHum = state.dataIPShortCut->rNumericArgs(51) / 100.0;
968 4 : if (thisPerfData.H_MinProcAirInRelHum >= thisPerfData.H_MaxProcAirInRelHum) {
969 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
970 0 : ShowContinueError(state, "Error found in min/max boundary for the regen outlet air humidity ratio equation.");
971 0 : ShowContinueError(state, "... the minimum value of process inlet air relative humidity must be less than the maximum.");
972 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinProcAirInRelHum * 100.0));
973 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxProcAirInRelHum * 100.0));
974 0 : ErrorsFound = true;
975 : }
976 4 : if (thisPerfData.H_MinProcAirInRelHum < 0.0) {
977 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
978 0 : ShowContinueError(state, "Error found in min boundary for the regen outlet air humidity ratio equation.");
979 0 : ShowContinueError(state, "... the minimum value of process inlet air relative humidity must be greater than or equal to 0.");
980 0 : ShowContinueError(state, format("... minimum value entered = {:.6R}", thisPerfData.H_MinProcAirInRelHum * 100.0));
981 0 : ErrorsFound = true;
982 : }
983 4 : if (thisPerfData.H_MaxProcAirInRelHum > 1.0) {
984 0 : ShowSevereError(state, format("{} \"{}\"", cCurrentModuleObject, thisPerfData.Name));
985 0 : ShowContinueError(state, "Error found in max boundary for the regen outlet air humidity ratio equation.");
986 0 : ShowContinueError(state, "... the maximum value of process inlet air relative humidity must be less than or equal to 100.");
987 0 : ShowContinueError(state, format("... maximum value entered = {:.6R}", thisPerfData.H_MaxProcAirInRelHum * 100.0));
988 0 : ErrorsFound = true;
989 : }
990 : }
991 : // getting performance data set for balanced desiccant heat exchanger ends
992 :
993 : // match desiccant heat exchanger index to performance data index
994 22 : for (int ExchIndex = 1; ExchIndex <= NumDesiccantBalancedExchs; ++ExchIndex) {
995 4 : int const ExchNum = ExchIndex + NumAirToAirPlateExchs + NumAirToAirGenericExchs;
996 4 : auto &thisExchanger = state.dataHeatRecovery->ExchCond(ExchNum);
997 4 : for (int PerfDataNum = 1; PerfDataNum <= NumDesBalExchsPerfDataType1; ++PerfDataNum) {
998 4 : if (Util::SameString(thisExchanger.HeatExchPerfName, state.dataHeatRecovery->BalDesDehumPerfData(PerfDataNum).Name)) {
999 4 : thisExchanger.PerfDataIndex = PerfDataNum;
1000 4 : break;
1001 : }
1002 : }
1003 4 : if (thisExchanger.PerfDataIndex == 0) {
1004 0 : ShowSevereError(state, format("{} \"{}\"", HVAC::hxTypeNames[(int)thisExchanger.type], thisExchanger.Name));
1005 0 : ShowContinueError(state, format("... Performance data set not found = {}", thisExchanger.HeatExchPerfName));
1006 0 : ErrorsFound = true;
1007 : } else {
1008 4 : if (!ErrorsFound) {
1009 4 : thisExchanger.FaceArea = state.dataHeatRecovery->BalDesDehumPerfData(thisExchanger.PerfDataIndex).NomSupAirVolFlow /
1010 4 : (state.dataHeatRecovery->BalDesDehumPerfData(thisExchanger.PerfDataIndex).NomProcAirFaceVel);
1011 : }
1012 : }
1013 : }
1014 : // matching done
1015 :
1016 : // setup common report variables for heat exchangers
1017 38 : for (int ExchIndex = 1; ExchIndex <= state.dataHeatRecovery->NumHeatExchangers; ++ExchIndex) {
1018 20 : int const ExchNum = ExchIndex;
1019 20 : auto &thisExchanger = state.dataHeatRecovery->ExchCond(ExchNum);
1020 : // CurrentModuleObject='HeatExchanger:AirToAir:FlatPlate/AirToAir:SensibleAndLatent/Desiccant:BalancedFlow')
1021 40 : SetupOutputVariable(state,
1022 : "Heat Exchanger Sensible Heating Rate",
1023 : Constant::Units::W,
1024 20 : thisExchanger.SensHeatingRate,
1025 : OutputProcessor::TimeStepType::System,
1026 : OutputProcessor::StoreType::Average,
1027 20 : thisExchanger.Name);
1028 40 : SetupOutputVariable(state,
1029 : "Heat Exchanger Sensible Heating Energy",
1030 : Constant::Units::J,
1031 20 : thisExchanger.SensHeatingEnergy,
1032 : OutputProcessor::TimeStepType::System,
1033 : OutputProcessor::StoreType::Sum,
1034 20 : thisExchanger.Name);
1035 40 : SetupOutputVariable(state,
1036 : "Heat Exchanger Latent Gain Rate",
1037 : Constant::Units::W,
1038 20 : thisExchanger.LatHeatingRate,
1039 : OutputProcessor::TimeStepType::System,
1040 : OutputProcessor::StoreType::Average,
1041 20 : thisExchanger.Name);
1042 40 : SetupOutputVariable(state,
1043 : "Heat Exchanger Latent Gain Energy",
1044 : Constant::Units::J,
1045 20 : thisExchanger.LatHeatingEnergy,
1046 : OutputProcessor::TimeStepType::System,
1047 : OutputProcessor::StoreType::Sum,
1048 20 : thisExchanger.Name);
1049 40 : SetupOutputVariable(state,
1050 : "Heat Exchanger Total Heating Rate",
1051 : Constant::Units::W,
1052 20 : thisExchanger.TotHeatingRate,
1053 : OutputProcessor::TimeStepType::System,
1054 : OutputProcessor::StoreType::Average,
1055 20 : thisExchanger.Name);
1056 40 : SetupOutputVariable(state,
1057 : "Heat Exchanger Total Heating Energy",
1058 : Constant::Units::J,
1059 20 : thisExchanger.TotHeatingEnergy,
1060 : OutputProcessor::TimeStepType::System,
1061 : OutputProcessor::StoreType::Sum,
1062 20 : thisExchanger.Name,
1063 : Constant::eResource::EnergyTransfer,
1064 : OutputProcessor::Group::HVAC,
1065 : OutputProcessor::EndUseCat::HeatRecoveryForHeating);
1066 40 : SetupOutputVariable(state,
1067 : "Heat Exchanger Sensible Cooling Rate",
1068 : Constant::Units::W,
1069 20 : thisExchanger.SensCoolingRate,
1070 : OutputProcessor::TimeStepType::System,
1071 : OutputProcessor::StoreType::Average,
1072 20 : thisExchanger.Name);
1073 40 : SetupOutputVariable(state,
1074 : "Heat Exchanger Sensible Cooling Energy",
1075 : Constant::Units::J,
1076 20 : thisExchanger.SensCoolingEnergy,
1077 : OutputProcessor::TimeStepType::System,
1078 : OutputProcessor::StoreType::Sum,
1079 20 : thisExchanger.Name);
1080 40 : SetupOutputVariable(state,
1081 : "Heat Exchanger Latent Cooling Rate",
1082 : Constant::Units::W,
1083 20 : thisExchanger.LatCoolingRate,
1084 : OutputProcessor::TimeStepType::System,
1085 : OutputProcessor::StoreType::Average,
1086 20 : thisExchanger.Name);
1087 40 : SetupOutputVariable(state,
1088 : "Heat Exchanger Latent Cooling Energy",
1089 : Constant::Units::J,
1090 20 : thisExchanger.LatCoolingEnergy,
1091 : OutputProcessor::TimeStepType::System,
1092 : OutputProcessor::StoreType::Sum,
1093 20 : thisExchanger.Name);
1094 40 : SetupOutputVariable(state,
1095 : "Heat Exchanger Total Cooling Rate",
1096 : Constant::Units::W,
1097 20 : thisExchanger.TotCoolingRate,
1098 : OutputProcessor::TimeStepType::System,
1099 : OutputProcessor::StoreType::Average,
1100 20 : thisExchanger.Name);
1101 40 : SetupOutputVariable(state,
1102 : "Heat Exchanger Total Cooling Energy",
1103 : Constant::Units::J,
1104 20 : thisExchanger.TotCoolingEnergy,
1105 : OutputProcessor::TimeStepType::System,
1106 : OutputProcessor::StoreType::Sum,
1107 20 : thisExchanger.Name,
1108 : Constant::eResource::EnergyTransfer,
1109 : OutputProcessor::Group::HVAC,
1110 : OutputProcessor::EndUseCat::HeatRecoveryForCooling);
1111 :
1112 40 : SetupOutputVariable(state,
1113 : "Heat Exchanger Electricity Rate",
1114 : Constant::Units::W,
1115 20 : thisExchanger.ElecUseRate,
1116 : OutputProcessor::TimeStepType::System,
1117 : OutputProcessor::StoreType::Average,
1118 20 : thisExchanger.Name);
1119 40 : SetupOutputVariable(state,
1120 : "Heat Exchanger Electricity Energy",
1121 : Constant::Units::J,
1122 20 : thisExchanger.ElecUseEnergy,
1123 : OutputProcessor::TimeStepType::System,
1124 : OutputProcessor::StoreType::Sum,
1125 20 : thisExchanger.Name,
1126 : Constant::eResource::Electricity,
1127 : OutputProcessor::Group::HVAC,
1128 : OutputProcessor::EndUseCat::HeatRecovery);
1129 : }
1130 :
1131 : // setup additional report variables for generic heat exchangers
1132 30 : for (int ExchIndex = 1; ExchIndex <= NumAirToAirGenericExchs; ++ExchIndex) {
1133 : // generic heat exchangers are read in after flat plate heat exchanger objects (index needs to be set correctly)
1134 : // CurrentModuleObject=HeatExchanger:AirToAir:SensibleAndLatent
1135 12 : int const ExchNum = ExchIndex + NumAirToAirPlateExchs;
1136 12 : auto &thisExchanger = state.dataHeatRecovery->ExchCond(ExchNum);
1137 24 : SetupOutputVariable(state,
1138 : "Heat Exchanger Sensible Effectiveness",
1139 : Constant::Units::None,
1140 12 : thisExchanger.SensEffectiveness,
1141 : OutputProcessor::TimeStepType::System,
1142 : OutputProcessor::StoreType::Average,
1143 12 : thisExchanger.Name);
1144 24 : SetupOutputVariable(state,
1145 : "Heat Exchanger Latent Effectiveness",
1146 : Constant::Units::None,
1147 12 : thisExchanger.LatEffectiveness,
1148 : OutputProcessor::TimeStepType::System,
1149 : OutputProcessor::StoreType::Average,
1150 12 : thisExchanger.Name);
1151 24 : SetupOutputVariable(state,
1152 : "Heat Exchanger Supply Air Bypass Mass Flow Rate",
1153 : Constant::Units::kg_s,
1154 12 : thisExchanger.SupBypassMassFlow,
1155 : OutputProcessor::TimeStepType::System,
1156 : OutputProcessor::StoreType::Average,
1157 12 : thisExchanger.Name);
1158 24 : SetupOutputVariable(state,
1159 : "Heat Exchanger Exhaust Air Bypass Mass Flow Rate",
1160 : Constant::Units::kg_s,
1161 12 : thisExchanger.SecBypassMassFlow,
1162 : OutputProcessor::TimeStepType::System,
1163 : OutputProcessor::StoreType::Average,
1164 12 : thisExchanger.Name);
1165 24 : SetupOutputVariable(state,
1166 : "Heat Exchanger Defrost Time Fraction",
1167 : Constant::Units::None,
1168 12 : thisExchanger.DefrostFraction,
1169 : OutputProcessor::TimeStepType::System,
1170 : OutputProcessor::StoreType::Average,
1171 12 : thisExchanger.Name);
1172 : }
1173 :
1174 18 : if (ErrorsFound) {
1175 0 : ShowFatalError(state, format("{}Errors found in input. Program terminates.", RoutineName));
1176 : }
1177 18 : }
1178 :
1179 26582 : void HeatExchCond::initialize(EnergyPlusData &state, int const CompanionCoilIndex, int const CompanionCoilType_Num)
1180 : {
1181 :
1182 : // SUBROUTINE INFORMATION:
1183 : // AUTHOR Michael Wetter
1184 : // DATE WRITTEN March 1999
1185 : // MODIFIED F Buhl Nov 2000, D Shirey Feb 2003
1186 : // B Griffith May 2009, EMS setpoint check
1187 : // RE-ENGINEERED na
1188 :
1189 : // PURPOSE OF THIS SUBROUTINE:
1190 : // This subroutine is for initializations of the Heat Recovery Components.
1191 :
1192 : // METHODOLOGY EMPLOYED:
1193 : // Uses the status flags to trigger initializations.
1194 :
1195 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1196 : Real64 CMin0; // minimum capacity flow
1197 : Real64 CMax0; // maximum capacity flow
1198 : Real64 Eps0; // effectiveness at rated conditions
1199 : Real64 NTU0; // NTU at rated conditions
1200 : Real64 RhoAir; // air density at outside pressure & standard temperature and humidity
1201 : Real64 CpAir; // heat capacity of air
1202 : // of humidity ratio and temperature
1203 : Real64 Z; // Min/max flow ratio
1204 :
1205 26582 : if (!state.dataGlobal->SysSizingCalc && this->MySizeFlag) {
1206 16 : this->size(state);
1207 16 : this->MySizeFlag = false;
1208 : }
1209 :
1210 : // Do the Begin Environment initializations
1211 26582 : if (state.dataGlobal->BeginEnvrnFlag && this->myEnvrnFlag) {
1212 11 : bool FatalError = false;
1213 : // I believe that all of these initializations should be taking place at the SCFM conditions
1214 11 : RhoAir = state.dataEnvrn->StdRhoAir;
1215 : // RhoAir = PsyRhoAirFnPbTdbW(101325.0,20.0,0.0) do we want standard air density at sea level for generic ERVs per ARI 1060?
1216 11 : CpAir = Psychrometrics::PsyCpAirFnW(0.0);
1217 :
1218 11 : CalculateNTUBoundsErrors ErrStat = CalculateNTUBoundsErrors::NoError;
1219 11 : switch (this->type) {
1220 2 : case HVAC::HXType::AirToAir_FlatPlate:
1221 2 : this->NomSupAirMassFlow = RhoAir * this->NomSupAirVolFlow;
1222 2 : this->NomSecAirMassFlow = RhoAir * this->NomSecAirVolFlow;
1223 : // Note: the capacity stream is here simply the mass flow
1224 : // since the thermal capacity can be assumed to be
1225 : // equal for both streams
1226 2 : if (this->NomSupAirMassFlow > this->NomSecAirMassFlow) {
1227 0 : CMin0 = this->NomSecAirMassFlow;
1228 0 : CMax0 = this->NomSupAirMassFlow;
1229 : } else {
1230 2 : CMin0 = this->NomSupAirMassFlow;
1231 2 : CMax0 = this->NomSecAirMassFlow;
1232 : }
1233 :
1234 4 : Eps0 = this->NomSupAirMassFlow *
1235 2 : SafeDiv(this->NomSupAirOutTemp - this->NomSupAirInTemp, CMin0 * (this->NomSecAirInTemp - this->NomSupAirInTemp));
1236 2 : Z = CMin0 / CMax0;
1237 :
1238 2 : CalculateNTUfromEpsAndZ(state, NTU0, ErrStat, Z, this->FlowArr, Eps0);
1239 :
1240 2 : switch (ErrStat) {
1241 2 : case CalculateNTUBoundsErrors::NoError:
1242 2 : break; // great!
1243 0 : case CalculateNTUBoundsErrors::MassFlowRatio:
1244 0 : FatalError = true;
1245 0 : ShowSevereError(state, format("In the HeatExchanger:AirToAir:FlatPlate component {}", this->Name));
1246 0 : ShowContinueError(state, " the mass flow ratio is out of bounds");
1247 0 : ShowContinueError(state, format("The mass flow ratio is (Min_Mass_Flow_Rate / Max_Mass_Flow_Rate) = {:.2R}", Z));
1248 0 : ShowContinueError(state, "The mass flow ratio should be >= 0.0 and <= 1.0");
1249 0 : ShowContinueError(state,
1250 0 : format("Min_Mass_Flow_Rate = {:.2R} [air density] * {:.1R} [Min_Vol_Flow_Rate]",
1251 : RhoAir,
1252 0 : min(this->NomSupAirVolFlow, this->NomSecAirVolFlow)));
1253 0 : ShowContinueError(state,
1254 0 : format("Max_Mass_Flow_Rate = {:.2R} [air density] * {:.1R} [Max_Vol_Flow_Rate]",
1255 : RhoAir,
1256 0 : max(this->NomSupAirVolFlow, this->NomSecAirVolFlow)));
1257 0 : break;
1258 0 : case CalculateNTUBoundsErrors::NominalEffectiveness1:
1259 0 : FatalError = true;
1260 0 : ShowSevereError(state, format("In the HeatExchanger:AirToAir:FlatPlate component {}", this->Name));
1261 0 : ShowContinueError(state, " the calculated nominal effectiveness is out of bounds");
1262 0 : ShowContinueError(state, format("The effectiveness is {:.3R}", Eps0));
1263 0 : ShowContinueError(state, format("The effectiveness should be >= 0.0 and <= {:.3R}", 1.0 / (1.0 + Z)));
1264 0 : ShowContinueError(state,
1265 : "Eff = (Nom_Sup_Mass_Flow_Rate/Min_Mass_Flow_Rate)*(T_nom_sup_out-T_nom_sup_in)/(T_nom_sec_in-T_nom_sup_in)");
1266 0 : ShowContinueError(state, "The temperatures are user inputs. The mass flow rates are user input volume flow rates");
1267 0 : ShowContinueError(state, format(" times the density of air [{:.2R} kg/m3]", RhoAir));
1268 0 : ShowContinueError(state, "Change these inputs to obtain a physically realizable heat exchanger effectiveness");
1269 0 : break;
1270 0 : case CalculateNTUBoundsErrors::NominalEffectiveness2:
1271 0 : FatalError = true;
1272 0 : ShowSevereError(state, format("In the HeatExchanger:AirToAir:FlatPlate component {}", this->Name));
1273 0 : ShowContinueError(state, " the calculated nominal effectiveness is out of bounds");
1274 0 : ShowContinueError(state, format("The effectiveness is {:.3R}", Eps0));
1275 0 : ShowContinueError(state, format("The effectiveness should be >= 0.0 and <= {:.3R}", (1.0 - std::exp(-Z)) / Z));
1276 0 : ShowContinueError(state,
1277 : "Eff = (Nom_Sup_Mass_Flow_Rate/Min_Mass_Flow_Rate)*(T_nom_sup_out-T_nom_sup_in)/(T_nom_sec_in-T_nom_sup_in)");
1278 0 : ShowContinueError(state, "The temperatures are user inputs. The mass flow rates are user input volume flow rates");
1279 0 : ShowContinueError(state, format(" times the density of air [{:.2R} kg/m3]", RhoAir));
1280 0 : ShowContinueError(state, "Change these inputs to obtain a physically realizable heat exchanger effectiveness");
1281 0 : break;
1282 0 : case CalculateNTUBoundsErrors::Quantity:
1283 0 : FatalError = true;
1284 0 : ShowSevereError(state, format("In the HeatExchanger:AirToAir:FlatPlate component {}", this->Name));
1285 0 : ShowContinueError(state, " the quantity Eff_nom*(Min_Mass_Flow_Rate / Max_Mass_Flow_Rate) is out of bounds");
1286 0 : ShowContinueError(state, format("The value is {:.3R}", Eps0 * Z));
1287 0 : ShowContinueError(state, format("The value should be >= 0.0 and <= {:.3R}", 1.0 - std::exp(Z * (SMALL - 1.0))));
1288 0 : ShowContinueError(
1289 : state,
1290 : "Eff_nom = (Nom_Sup_Mass_Flow_Rate/Min_Mass_Flow_Rate) * (T_nom_sup_out - T_nom_sup_in)/(T_nom_sec_in - T_nom_sup_in)");
1291 0 : ShowContinueError(state, "The temperatures are user inputs. The mass flow rates are user input volume flow rates");
1292 0 : ShowContinueError(state, format(" times the density of air [{:.2R} kg/m3]", RhoAir));
1293 0 : ShowContinueError(state,
1294 : "Change these inputs to obtain a physically realizable product of effectiveness times min/max mass ratio "
1295 : "for this heat exchanger");
1296 0 : break;
1297 0 : case CalculateNTUBoundsErrors::NominalEffectiveness3:
1298 0 : FatalError = true;
1299 0 : ShowSevereError(state, format("In the HeatExchanger:AirToAir:FlatPlate component {}", this->Name));
1300 0 : ShowContinueError(state, " the calculated nominal effectiveness is out of bounds");
1301 0 : ShowContinueError(state, format("The effectiveness is {:.3R}", Eps0));
1302 0 : ShowContinueError(state, "The effectiveness should be >= 0.0 and <= 1.0");
1303 0 : ShowContinueError(state,
1304 : "Eff = (Nom_Sup_Mass_Flow_Rate/Min_Mass_Flow_Rate)*(T_nom_sup_out-T_nom_sup_in)/(T_nom_sec_in-T_nom_sup_in)");
1305 0 : ShowContinueError(state, "The temperatures are user inputs. The mass flow rates are user input volume flow rates");
1306 0 : ShowContinueError(state, format(" times the density of air [{:.2R} kg/m3]", RhoAir));
1307 0 : ShowContinueError(state, "Change these inputs to obtain a physically realizable heat exchanger effectiveness");
1308 0 : break;
1309 0 : case CalculateNTUBoundsErrors::Invalid:
1310 : case CalculateNTUBoundsErrors::Num:
1311 0 : break; // function won't actually return ::Invalid or ::Num, this is just so the compiler doesn't complain about the missing cases
1312 : }
1313 :
1314 2 : if (FatalError) {
1315 0 : ShowFatalError(state, "Heat exchanger design calculation caused fatal error: program terminated.");
1316 : }
1317 :
1318 2 : this->UA0 = NTU0 * CMin0 * CpAir;
1319 2 : this->mTSup0 = this->NomSupAirMassFlow * (this->NomSupAirInTemp + KELVZERO);
1320 2 : this->mTSec0 = this->NomSecAirMassFlow * (this->NomSecAirInTemp + KELVZERO);
1321 :
1322 : // check validity
1323 2 : if (this->NomSupAirMassFlow * this->NomSecAirMassFlow < HVAC::SmallMassFlow * HVAC::SmallMassFlow) {
1324 0 : ShowFatalError(state, "Mass flow in HeatExchanger:AirToAir:FlatPlate too small in initialization.");
1325 : }
1326 :
1327 2 : if (this->mTSup0 < HVAC::SmallMassFlow) {
1328 0 : ShowFatalError(state, "(m*T)Sup,in in HeatExchanger:AirToAir:FlatPlate too small in initialization.");
1329 : }
1330 :
1331 2 : if (this->mTSec0 < HVAC::SmallMassFlow) {
1332 0 : ShowFatalError(state, "(m*T)Sec,in in HeatExchanger:AirToAir:FlatPlate too small in initialization.");
1333 : }
1334 :
1335 2 : if (CMin0 < HVAC::SmallMassFlow) {
1336 0 : ShowFatalError(state, "CMin0 in HeatExchanger:AirToAir:FlatPlate too small in initialization.");
1337 : }
1338 2 : break;
1339 :
1340 9 : case HVAC::HXType::AirToAir_Generic:
1341 9 : if (this->SupOutletNode > 0 && this->ControlToTemperatureSetPoint) {
1342 4 : if (state.dataLoopNodes->Node(this->SupOutletNode).TempSetPoint == DataLoopNode::SensedNodeFlagValue) {
1343 0 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
1344 0 : ShowSevereError(state,
1345 0 : format("Missing temperature setpoint for {} \"{}\" :", HVAC::hxTypeNames[(int)this->type], this->Name));
1346 0 : ShowContinueError(
1347 : state, " use a Setpoint Manager to establish a setpoint at the supply air outlet node of the Heat Exchanger.");
1348 0 : ShowFatalError(state, " Previous condition causes program termination.");
1349 : } else {
1350 : // need call to EMS to check node
1351 0 : EMSManager::CheckIfNodeSetPointManagedByEMS(state, this->SupOutletNode, HVAC::CtrlVarType::Temp, FatalError);
1352 0 : if (FatalError) {
1353 0 : ShowSevereError(
1354 0 : state, format("Missing temperature setpoint for {} \"{}\" :", HVAC::hxTypeNames[(int)this->type], this->Name));
1355 0 : ShowContinueError(
1356 : state, " use a Setpoint Manager to establish a setpoint at the supply air outlet node of the Heat Exchanger.");
1357 0 : ShowContinueError(
1358 : state, " or use an EMS actuator to establish a setpoint at the supply air outlet node of the Heat Exchanger.");
1359 0 : ShowFatalError(state, " Previous condition causes program termination.");
1360 : }
1361 : }
1362 : }
1363 : }
1364 9 : break;
1365 :
1366 0 : default:
1367 : // nothing
1368 0 : break;
1369 : }
1370 :
1371 11 : this->myEnvrnFlag = false;
1372 : }
1373 :
1374 26582 : if (!state.dataGlobal->BeginEnvrnFlag) {
1375 26163 : this->myEnvrnFlag = true;
1376 : }
1377 :
1378 : // Do these initializations every time step
1379 26582 : int const SupInNode = this->SupInletNode;
1380 26582 : int const SecInNode = this->SecInletNode;
1381 :
1382 : // Get information from inlet nodes
1383 :
1384 26582 : this->SupInTemp = state.dataLoopNodes->Node(SupInNode).Temp;
1385 26582 : this->SupInHumRat = state.dataLoopNodes->Node(SupInNode).HumRat;
1386 26582 : this->SupInEnth = state.dataLoopNodes->Node(SupInNode).Enthalpy;
1387 26582 : this->SupInMassFlow = state.dataLoopNodes->Node(SupInNode).MassFlowRate;
1388 26582 : this->SecInTemp = state.dataLoopNodes->Node(SecInNode).Temp;
1389 26582 : this->SecInHumRat = state.dataLoopNodes->Node(SecInNode).HumRat;
1390 26582 : this->SecInEnth = state.dataLoopNodes->Node(SecInNode).Enthalpy;
1391 26582 : this->SecInMassFlow = state.dataLoopNodes->Node(SecInNode).MassFlowRate;
1392 :
1393 : // initialize the output variables
1394 26582 : this->SensHeatingRate = 0.0;
1395 26582 : this->SensHeatingEnergy = 0.0;
1396 26582 : this->LatHeatingRate = 0.0;
1397 26582 : this->LatHeatingEnergy = 0.0;
1398 26582 : this->TotHeatingRate = 0.0;
1399 26582 : this->TotHeatingEnergy = 0.0;
1400 26582 : this->SensCoolingRate = 0.0;
1401 26582 : this->SensCoolingEnergy = 0.0;
1402 26582 : this->LatCoolingRate = 0.0;
1403 26582 : this->LatCoolingEnergy = 0.0;
1404 26582 : this->TotCoolingRate = 0.0;
1405 26582 : this->TotCoolingEnergy = 0.0;
1406 26582 : this->ElecUseRate = 0.0;
1407 26582 : this->ElecUseEnergy = 0.0;
1408 26582 : this->SensEffectiveness = 0.0;
1409 26582 : this->LatEffectiveness = 0.0;
1410 26582 : this->SupBypassMassFlow = 0.0;
1411 26582 : this->SecBypassMassFlow = 0.0;
1412 :
1413 : // Initialize inlet conditions
1414 :
1415 26582 : switch (this->type) {
1416 26578 : case HVAC::HXType::AirToAir_FlatPlate:
1417 : case HVAC::HXType::AirToAir_Generic:
1418 26578 : break;
1419 :
1420 4 : case HVAC::HXType::Desiccant_Balanced:
1421 4 : if (this->MySetPointTest) {
1422 4 : if (!state.dataGlobal->SysSizingCalc && state.dataHVACGlobal->DoSetPointTest) {
1423 0 : if (!state.dataHeatRecovery->CalledFromParentObject) {
1424 0 : if (state.dataLoopNodes->Node(this->SecOutletNode).HumRatMax == DataLoopNode::SensedNodeFlagValue) {
1425 0 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
1426 0 : ShowWarningError(
1427 : state,
1428 0 : format("Missing optional HumRatMax setpoint for {} \"{}\"", HVAC::hxTypeNames[(int)this->type], this->Name));
1429 0 : ShowContinueError(state,
1430 : "...the simulation will continue without control of the desiccant heat exchanger to a maximum "
1431 : "humidity ratio setpoint.");
1432 0 : ShowContinueError(state,
1433 : "...use a Setpoint Manager to establish a setpoint at the process air outlet node of the "
1434 : "desiccant Heat Exchanger if control is desired.");
1435 : } else {
1436 0 : bool LocalWarningError = false;
1437 : // need call to EMS to check node
1438 0 : EMSManager::CheckIfNodeSetPointManagedByEMS(
1439 : state, this->SecOutletNode, HVAC::CtrlVarType::MaxHumRat, LocalWarningError);
1440 0 : state.dataLoopNodes->NodeSetpointCheck(this->SecOutletNode).needsSetpointChecking = false;
1441 0 : if (LocalWarningError) {
1442 0 : ShowWarningError(
1443 : state,
1444 0 : format("Missing optional HumRatMax setpoint for {} \"{}\"", HVAC::hxTypeNames[(int)this->type], this->Name));
1445 0 : ShowContinueError(state,
1446 : "...the simulation will continue without control of the desiccant heat exchanger to a "
1447 : "maximum humidity ratio setpoint.");
1448 0 : ShowContinueError(state,
1449 : "...use a Setpoint Manager to establish a setpoint at the process air outlet node of the "
1450 : "desiccant Heat Exchanger if control is desired.");
1451 0 : ShowContinueError(state,
1452 : "...or use an EMS Actuator to establish a maximum humidity ratio setpoint at the process "
1453 : "air outlet node of the desiccant Heat Exchanger if control is desired.");
1454 : }
1455 : }
1456 : }
1457 : }
1458 0 : this->MySetPointTest = false;
1459 : }
1460 : }
1461 :
1462 4 : if ((CompanionCoilIndex > -1) &&
1463 2 : ((CompanionCoilType_Num == HVAC::CoilDX_CoolingSingleSpeed) || (CompanionCoilType_Num == HVAC::Coil_CoolingAirToAirVariableSpeed) ||
1464 : (CompanionCoilType_Num == HVAC::CoilDX_Cooling))) {
1465 :
1466 3 : if (CompanionCoilType_Num == HVAC::CoilDX_CoolingSingleSpeed || CompanionCoilType_Num == HVAC::CoilDX_CoolingTwoStageWHumControl) {
1467 4 : if (state.dataDXCoils->DXCoilFullLoadOutAirTemp(CompanionCoilIndex) == 0.0 ||
1468 0 : state.dataDXCoils->DXCoilFullLoadOutAirHumRat(CompanionCoilIndex) == 0.0) {
1469 : // DX Coil is OFF, read actual inlet conditions
1470 2 : state.dataHeatRecovery->FullLoadOutAirTemp = this->SecInTemp;
1471 2 : state.dataHeatRecovery->FullLoadOutAirHumRat = this->SecInHumRat;
1472 : } else {
1473 : // DX Coil is ON, read full load DX coil outlet conditions (conditions HX sees when ON)
1474 0 : state.dataHeatRecovery->FullLoadOutAirTemp = state.dataDXCoils->DXCoilFullLoadOutAirTemp(CompanionCoilIndex);
1475 0 : state.dataHeatRecovery->FullLoadOutAirHumRat = state.dataDXCoils->DXCoilFullLoadOutAirHumRat(CompanionCoilIndex);
1476 : }
1477 1 : } else if (CompanionCoilType_Num == HVAC::Coil_CoolingAirToAirVariableSpeed) {
1478 : // how to support VS dx coil here?
1479 1 : state.dataHeatRecovery->FullLoadOutAirTemp = state.dataVariableSpeedCoils->VarSpeedCoil(CompanionCoilIndex).OutletAirDBTemp;
1480 1 : state.dataHeatRecovery->FullLoadOutAirHumRat = state.dataVariableSpeedCoils->VarSpeedCoil(CompanionCoilIndex).OutletAirHumRat;
1481 0 : } else if (CompanionCoilType_Num == HVAC::CoilDX_Cooling) {
1482 : // Use the new coil option:
1483 0 : state.dataHeatRecovery->FullLoadOutAirTemp = state.dataCoilCoolingDX->coilCoolingDXs[CompanionCoilIndex].outletAirDryBulbTemp;
1484 0 : state.dataHeatRecovery->FullLoadOutAirHumRat = state.dataCoilCoolingDX->coilCoolingDXs[CompanionCoilIndex].outletAirHumRat;
1485 : } else {
1486 : //
1487 : }
1488 :
1489 : } else {
1490 :
1491 : // HX only (not used in conjunction with DX coil), read inlet conditions
1492 1 : state.dataHeatRecovery->FullLoadOutAirTemp = this->SecInTemp;
1493 1 : state.dataHeatRecovery->FullLoadOutAirHumRat = this->SecInHumRat;
1494 : }
1495 4 : break;
1496 :
1497 0 : default:
1498 : // Will never get here
1499 0 : break;
1500 : }
1501 26582 : }
1502 :
1503 25 : void HeatExchCond::size(EnergyPlusData &state)
1504 : {
1505 :
1506 : // SUBROUTINE INFORMATION:
1507 : // AUTHOR Richard Raustad
1508 : // DATE WRITTEN October 2007
1509 : // MODIFIED February 2014 Daeho Kang, enable sizing multiple HX types and add additional sizing fields
1510 : // RE-ENGINEERED na
1511 :
1512 : // PURPOSE OF THIS SUBROUTINE:
1513 : // This subroutine is for sizing Heat Exchanger components for which flow rates have not been
1514 : // specified in the input. Currently, only nominal supply air flow rate for the generic HX can be autosized.
1515 :
1516 : // METHODOLOGY EMPLOYED:
1517 : // Obtains flow rates from the system or OA system sizing arrays
1518 :
1519 : // SUBROUTINE PARAMETER DEFINITIONS:
1520 25 : std::string_view RoutineName = "SizeHeatRecovery";
1521 :
1522 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1523 25 : std::string SizingString; // input field sizing description
1524 :
1525 25 : auto &ZoneEqSizing(state.dataSize->ZoneEqSizing);
1526 :
1527 25 : state.dataSize->HRFlowSizingFlag = true;
1528 25 : bool PrintFlag = true; // true when sizing information is reported in the eio file
1529 25 : int FieldNum = 0; // IDD numeric field index where input field description is found
1530 25 : switch (this->type) {
1531 5 : case HVAC::HXType::Desiccant_Balanced:
1532 5 : PrintFlag = false;
1533 5 : break;
1534 18 : case HVAC::HXType::AirToAir_Generic:
1535 18 : FieldNum = 1;
1536 18 : break;
1537 2 : case HVAC::HXType::AirToAir_FlatPlate:
1538 2 : FieldNum = 2;
1539 2 : break;
1540 0 : default:
1541 0 : assert(0);
1542 : }
1543 :
1544 50 : std::string CompName = this->Name;
1545 50 : std::string CompType = std::string(HVAC::hxTypeNames[(int)this->type]);
1546 25 : if (FieldNum > 0) {
1547 20 : SizingString = this->NumericFieldNames(FieldNum) + " [m3/s]";
1548 : } else {
1549 5 : SizingString = "Nominal Supply Air Flow Rate [m3/s]"; // desiccant balanced flow does not have an input for air volume flow rate
1550 : }
1551 25 : if (state.dataSize->CurZoneEqNum > 0) {
1552 2 : if (this->NomSupAirVolFlow == DataSizing::AutoSize) {
1553 1 : if (ZoneEqSizing(state.dataSize->CurZoneEqNum).DesignSizeFromParent) {
1554 : // Heat recovery heat exchanger in zoneHVAC equipment should have been sized to OA flow in the parent equipment
1555 1 : state.dataSize->DataConstantUsedForSizing = ZoneEqSizing(state.dataSize->CurZoneEqNum).AirVolFlow;
1556 : } else {
1557 0 : state.dataSize->DataConstantUsedForSizing =
1558 0 : std::max(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow,
1559 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow);
1560 : }
1561 1 : state.dataSize->DataFractionUsedForSizing = 1.0;
1562 : } else {
1563 1 : if (state.dataSize->ZoneSizingRunDone) {
1564 0 : if (ZoneEqSizing(state.dataSize->CurZoneEqNum).DesignSizeFromParent) {
1565 : // Heat recovery heat exchanger in zoneHVAC equipment should have been sized to OA flow in the parent equipment
1566 0 : state.dataSize->DataConstantUsedForSizing = ZoneEqSizing(state.dataSize->CurZoneEqNum).AirVolFlow;
1567 : } else {
1568 0 : state.dataSize->DataConstantUsedForSizing =
1569 0 : std::max(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow,
1570 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow);
1571 : }
1572 0 : state.dataSize->DataFractionUsedForSizing = 1.0;
1573 : }
1574 : }
1575 : }
1576 25 : Real64 TempSize = this->NomSupAirVolFlow;
1577 25 : bool errorsFound = false;
1578 25 : SystemAirFlowSizer sizerSystemAirFlow;
1579 25 : sizerSystemAirFlow.overrideSizingString(SizingString);
1580 : // sizerSystemAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1581 25 : sizerSystemAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1582 25 : this->NomSupAirVolFlow = sizerSystemAirFlow.size(state, TempSize, errorsFound);
1583 25 : state.dataSize->DataConstantUsedForSizing = 0.0;
1584 25 : state.dataSize->DataFractionUsedForSizing = 0.0;
1585 25 : if (this->type == HVAC::HXType::AirToAir_FlatPlate) {
1586 2 : PrintFlag = true;
1587 2 : FieldNum = 5;
1588 2 : CompName = this->Name;
1589 2 : CompType = HVAC::hxTypeNames[(int)this->type];
1590 2 : SizingString = this->NumericFieldNames(FieldNum) + " [m3/s]";
1591 2 : if (this->NomSecAirVolFlow == DataSizing::AutoSize) {
1592 0 : state.dataSize->DataConstantUsedForSizing = this->NomSupAirVolFlow;
1593 0 : state.dataSize->DataFractionUsedForSizing = 1.0;
1594 : } else {
1595 2 : if (state.dataSize->ZoneSizingRunDone || state.dataSize->SysSizingRunDone) {
1596 0 : state.dataSize->DataConstantUsedForSizing = this->NomSupAirVolFlow;
1597 0 : state.dataSize->DataFractionUsedForSizing = 1.0;
1598 : }
1599 : }
1600 2 : TempSize = this->NomSecAirVolFlow;
1601 2 : bool errorsFound2 = false;
1602 2 : SystemAirFlowSizer sizerSystemAirFlow2;
1603 2 : sizerSystemAirFlow2.overrideSizingString(SizingString);
1604 : // sizerSystemAirFlow2.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1605 2 : sizerSystemAirFlow2.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1606 2 : this->NomSecAirVolFlow = sizerSystemAirFlow2.size(state, TempSize, errorsFound2);
1607 2 : state.dataSize->DataConstantUsedForSizing = 0.0;
1608 2 : state.dataSize->DataFractionUsedForSizing = 0.0;
1609 2 : }
1610 25 : state.dataSize->HRFlowSizingFlag = false;
1611 25 : if (this->type == HVAC::HXType::Desiccant_Balanced) {
1612 :
1613 5 : int const BalDesDehumPerfIndex = this->PerfDataIndex; // index of dehum performance data1 object
1614 :
1615 5 : FieldNum = 1;
1616 5 : PrintFlag = true;
1617 5 : CompName = state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).Name;
1618 5 : CompType = state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).PerfType;
1619 5 : SizingString = state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).NumericFieldNames(FieldNum) + " [m3/s]";
1620 5 : TempSize = state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).NomSupAirVolFlow;
1621 5 : bool errorsFound2 = false;
1622 5 : SystemAirFlowSizer sizerSystemAirFlow3;
1623 5 : sizerSystemAirFlow3.overrideSizingString(SizingString);
1624 : // sizerSystemAirFlow3.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1625 5 : sizerSystemAirFlow3.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1626 5 : state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).NomSupAirVolFlow =
1627 5 : sizerSystemAirFlow3.size(state, TempSize, errorsFound2);
1628 :
1629 5 : state.dataSize->DataAirFlowUsedForSizing = state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).NomSupAirVolFlow;
1630 5 : TempSize = state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).NomProcAirFaceVel;
1631 5 : bool errorsFound3 = false;
1632 5 : DesiccantDehumidifierBFPerfDataFaceVelocitySizer sizerDesDehumBFFaceVel;
1633 5 : sizerDesDehumBFFaceVel.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1634 5 : state.dataHeatRecovery->BalDesDehumPerfData(BalDesDehumPerfIndex).NomProcAirFaceVel =
1635 5 : sizerDesDehumBFFaceVel.size(state, TempSize, errorsFound3);
1636 :
1637 5 : state.dataSize->DataAirFlowUsedForSizing = 0.0;
1638 5 : }
1639 :
1640 : // std 229 new heat recovery table variables
1641 25 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchAirHRInputObjName, this->Name, this->Name);
1642 50 : OutputReportPredefined::PreDefTableEntry(state,
1643 25 : state.dataOutRptPredefined->pdchAirHRInputObjType,
1644 : this->Name,
1645 25 : this->type == HVAC::HXType::AirToAir_FlatPlate
1646 : ? "Flat Plate"
1647 23 : : (this->type == HVAC::HXType::Desiccant_Balanced ? "Desiccant Balanced" : "Generic"));
1648 50 : OutputReportPredefined::PreDefTableEntry(state,
1649 25 : state.dataOutRptPredefined->pdchAirHRPlateOrRotary,
1650 : this->Name,
1651 25 : this->type == HVAC::HXType::AirToAir_FlatPlate ? "FlatPlate" : " Rotary");
1652 :
1653 50 : OutputReportPredefined::PreDefTableEntry(
1654 25 : state, state.dataOutRptPredefined->pdchAirHRSenEffAt100PerHeatAirFlow, this->Name, this->HeatEffectSensible100);
1655 50 : OutputReportPredefined::PreDefTableEntry(
1656 25 : state, state.dataOutRptPredefined->pdchAirHRSenEffAt100PerCoolAirFlow, this->Name, this->CoolEffectSensible100);
1657 50 : OutputReportPredefined::PreDefTableEntry(
1658 25 : state, state.dataOutRptPredefined->pdchAirHRLatEffAt100PerHeatAirFlow, this->Name, this->HeatEffectLatent100);
1659 50 : OutputReportPredefined::PreDefTableEntry(
1660 25 : state, state.dataOutRptPredefined->pdchAirHRLatEffAt100PerCoolAirFlow, this->Name, this->CoolEffectLatent100);
1661 :
1662 50 : OutputReportPredefined::PreDefTableEntry(
1663 25 : state, state.dataOutRptPredefined->pdchAirHRExhaustAirflow, this->Name, this->NomSecAirMassFlow); // ? Nomsec ==? exhaust?
1664 50 : OutputReportPredefined::PreDefTableEntry(
1665 25 : state, state.dataOutRptPredefined->pdchAirHROutdoorAirflow, this->Name, this->NomSupAirMassFlow); // ? NomSup ==? outdoor air?
1666 25 : }
1667 :
1668 : void
1669 18 : HeatExchCond::CalcAirToAirPlateHeatExch(EnergyPlusData &state,
1670 : bool const HXUnitOn, // flag to simulate heat exchanger heat recovery
1671 : ObjexxFCL::Optional_bool_const EconomizerFlag, // economizer flag pass by air loop or OA sys
1672 : ObjexxFCL::Optional_bool_const HighHumCtrlFlag // high humidity control flag passed by airloop or OA sys
1673 : )
1674 : {
1675 :
1676 : // SUBROUTINE INFORMATION:
1677 : // AUTHOR Michael Wetter
1678 : // DATE WRITTEN March 1999
1679 : // MODIFIED F. Buhl Nov 2000, R. Raustad - FSEC, Feb 2009 - added economizer flags
1680 : // Both the economizer and high humidity control flags can disable the HX
1681 : // RE-ENGINEERED na
1682 :
1683 : // PURPOSE OF THIS SUBROUTINE:
1684 : // Calculate the outlet conditions for an air to air plate heat
1685 : // exchanger given the inlet conditions.
1686 :
1687 : // METHODOLOGY EMPLOYED:
1688 : // This is a static heat exchanger model. No geometrical input data
1689 : // is needed. No knowledge of h*A values is needed except the ratio
1690 : // of the primary side to the secondary side convective heat transfer
1691 : // coefficient times the exchanger surface area. Effectiveness - NTU
1692 : // heat exchanger formulas are used.
1693 :
1694 : // The time varying load is calculated based on the variation of the
1695 : // convective heat transfer coefficient.The variation is a function of
1696 : // mass flow rate and inlet temperature. An iterative solution is only
1697 : // required during initialization in one specific flow arrangement. During
1698 : // the time steps the solution is explicit. The iteration is done with
1699 : // the Regula Falsi algorithm. Convergence is always achieved.
1700 :
1701 : // REFERENCES:
1702 : // M. Wetter, Simulation Model Air-to-Air Plate Heat Exchanger
1703 : // LBNL Report 42354, 1999.
1704 :
1705 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1706 18 : bool UnitOn = true; // unit on flag
1707 18 : Real64 QTrans = 0.0; // heat transferred in the heat exchanger [W]
1708 18 : Real64 ElecCons = 0.0; // electricity consumption rate [W]
1709 :
1710 18 : bool EconomizerActiveFlag = present(EconomizerFlag) && bool(EconomizerFlag); // local representing the economizer status when PRESENT
1711 18 : bool HighHumCtrlActiveFlag = present(HighHumCtrlFlag) && bool(HighHumCtrlFlag); // local representing high humidity control when PRESENT
1712 :
1713 : Real64 UnitSupMassFlow; // supply air mass flow rate passing through the unit [kg/s]
1714 : Real64 UnitSecMassFlow; // secondary air mass flow rate passing through the unit [kg/s]
1715 18 : if ((EconomizerActiveFlag || HighHumCtrlActiveFlag) && this->EconoLockOut) {
1716 0 : UnitSupMassFlow = 0.0; // set HX supply flow to 0, all supply air will go through supply bypass
1717 0 : UnitSecMassFlow = 0.0; // set HX secondary flow to 0, all secondary air will got through secondary bypass
1718 0 : UnitOn = false; // turn off HX calculations when in economizer mode
1719 : } else {
1720 : // if economizer operation is not allowed, air always passes through HX
1721 : // if CompanionCoilNum > 0, air always passes through HX (no economizer operation allowed)
1722 18 : UnitSupMassFlow = min(this->NomSupAirMassFlow, this->SupInMassFlow);
1723 18 : UnitSecMassFlow = min(this->NomSecAirMassFlow, this->SecInMassFlow);
1724 : }
1725 :
1726 18 : if (this->availSched->getCurrentVal() <= 0.0) UnitOn = false;
1727 18 : if (this->SupInMassFlow <= HVAC::SmallMassFlow) UnitOn = false;
1728 18 : if (this->SecInMassFlow <= HVAC::SmallMassFlow) UnitOn = false;
1729 18 : if (!HXUnitOn) UnitOn = false;
1730 :
1731 18 : if (UnitOn) {
1732 : // unit is on
1733 : // calculate the UA for this time step
1734 : // ratio of supply nominal m*T to actual m*T
1735 12 : Real64 const QuotSup = SafeDiv(this->mTSup0, UnitSupMassFlow * (this->SupInTemp + KELVZERO));
1736 : // ratio of secondary nominal m*T to actual m*T
1737 12 : Real64 const QuotExh = SafeDiv(this->mTSec0, UnitSecMassFlow * (this->SecInTemp + KELVZERO));
1738 : // denominator of UA calculation
1739 12 : Real64 const Deno = std::pow(QuotSup, 0.78) + this->hARatio * std::pow(QuotExh, 0.78);
1740 : // present UA
1741 12 : Real64 const UA = this->UA0 * (this->hARatio + 1.0) / Deno;
1742 : // calculate the NTU
1743 12 : Real64 const CSup = UnitSupMassFlow * Psychrometrics::PsyCpAirFnW(this->SupInHumRat);
1744 : // secondary air capacitance rate [J/C/s]
1745 12 : Real64 const CSec = UnitSecMassFlow * Psychrometrics::PsyCpAirFnW(this->SecInHumRat);
1746 : // note: no C can be zero since otherwise we wouldn't be here
1747 : Real64 Z; // Ratio of minimum air capacitance rate to maximum air capacitance rate
1748 : Real64 CMin; // minimum air capacitance rate [J/C/s]
1749 12 : if (CSup < CSec) {
1750 0 : CMin = CSup;
1751 0 : Z = CMin / CSec;
1752 : } else {
1753 12 : CMin = CSec;
1754 12 : Z = CMin / CSup;
1755 : }
1756 : // Number of heat transfer units
1757 12 : Real64 const NTU = UA / CMin;
1758 : // Get the effectiveness
1759 12 : Real64 Eps = CalculateEpsFromNTUandZ(state, NTU, Z, this->FlowArr);
1760 : // use the effectiveness to calculate the unit outlet conditions
1761 12 : Real64 TempSupOut = this->SupInTemp + Eps * CMin / CSup * (this->SecInTemp - this->SupInTemp);
1762 12 : QTrans = CSup * (TempSupOut - this->SupInTemp);
1763 : // unit secondary outlet temperature [C]
1764 12 : Real64 TempSecOut = this->SecInTemp - QTrans / CSec;
1765 : // unit supply outlet humidity ratio [kg water / kg dry air]
1766 12 : Real64 HumRatSupOut = this->SupInHumRat;
1767 : // unit supply outlet enthalpy [J/kg]
1768 12 : Real64 const EnthSupOut = Psychrometrics::PsyHFnTdbW(TempSupOut, HumRatSupOut);
1769 : // check for saturation in supply outlet
1770 : // unit supply outlet temperature at saturation (at EnthSupOut) [C]
1771 12 : Real64 const TempSupOutSat = Psychrometrics::PsyTsatFnHPb(state, EnthSupOut, state.dataEnvrn->OutBaroPress);
1772 12 : if (TempSupOutSat > TempSupOut) {
1773 0 : TempSupOut = TempSupOutSat;
1774 0 : HumRatSupOut = Psychrometrics::PsyWFnTdbH(state, TempSupOut, EnthSupOut);
1775 : }
1776 : // unit secondary outlet humidity ratio [kg water / kg dry air]
1777 12 : Real64 HumRatSecOut = this->SecInHumRat;
1778 : // unit secondary outlet enthalpy [J/kgC]
1779 12 : Real64 const EnthSecOut = Psychrometrics::PsyHFnTdbW(TempSecOut, HumRatSecOut);
1780 : // check for saturation in secondary outlet
1781 : // unit secondary outlet temperature at saturation (at EnthsSecOut) [C]
1782 12 : Real64 const TempSecOutSat = Psychrometrics::PsyTsatFnHPb(state, EnthSecOut, state.dataEnvrn->OutBaroPress);
1783 12 : if (TempSecOutSat > TempSecOut) {
1784 0 : TempSecOut = TempSecOutSat;
1785 0 : HumRatSecOut = Psychrometrics::PsyWFnTdbH(state, TempSecOut, EnthSecOut);
1786 : }
1787 : // calculate outlet conditions by mixing bypass air stream with air that went through the
1788 : // heat exchanger core.
1789 12 : Real64 local_SupBypassMassFlow = max(0.0, this->SupInMassFlow - UnitSupMassFlow); // supply air mass flow rate bypassing unit [kg/s]
1790 12 : Real64 local_SecBypassMassFlow = max(0.0, this->SecInMassFlow - UnitSecMassFlow); // secondary air mass flow rate bypassing unit [kg/s]
1791 :
1792 12 : this->SupOutEnth = (UnitSupMassFlow * EnthSupOut + local_SupBypassMassFlow * this->SupInEnth) / this->SupInMassFlow;
1793 12 : this->SupOutHumRat = (UnitSupMassFlow * HumRatSupOut + local_SupBypassMassFlow * this->SupInHumRat) / this->SupInMassFlow;
1794 12 : this->SupOutTemp = Psychrometrics::PsyTdbFnHW(this->SupOutEnth, this->SupOutHumRat);
1795 12 : this->SupOutMassFlow = this->SupInMassFlow;
1796 12 : this->SecOutEnth = (UnitSecMassFlow * EnthSecOut + local_SecBypassMassFlow * this->SecInEnth) / this->SecInMassFlow;
1797 12 : this->SecOutHumRat = (UnitSecMassFlow * HumRatSecOut + local_SecBypassMassFlow * this->SecInHumRat) / this->SecInMassFlow;
1798 12 : this->SecOutTemp = Psychrometrics::PsyTdbFnHW(this->SecOutEnth, this->SecOutHumRat);
1799 12 : this->SecOutMassFlow = this->SecInMassFlow;
1800 12 : ElecCons = this->NomElecPower;
1801 :
1802 : } else {
1803 : // the unit is off. Pass through the air streams with no change
1804 6 : this->SupOutEnth = this->SupInEnth;
1805 6 : this->SupOutHumRat = this->SupInHumRat;
1806 6 : this->SupOutTemp = this->SupInTemp;
1807 6 : this->SupOutMassFlow = this->SupInMassFlow;
1808 6 : this->SecOutEnth = this->SecInEnth;
1809 6 : this->SecOutHumRat = this->SecInHumRat;
1810 6 : this->SecOutTemp = this->SecInTemp;
1811 6 : this->SecOutMassFlow = this->SecInMassFlow;
1812 : }
1813 : // supply air capacitance rate [J/C/s]
1814 18 : Real64 const CSup = this->SupInMassFlow * Psychrometrics::PsyCpAirFnW(this->SupInHumRat);
1815 : // sensible heat recovery rate to supply air (heating +, cooling -)
1816 18 : Real64 const SensHeatRecRate = CSup * (this->SupOutTemp - this->SupInTemp);
1817 : // total heat recovery rate to supply air (heating +, cooling -)
1818 18 : Real64 const TotHeatRecRate = this->SupOutMassFlow * (this->SupOutEnth - this->SupInEnth);
1819 : // latent heat recovery rate to supply air (heating [humidify] +, cooling [dehumidify] -)
1820 18 : Real64 const LatHeatRecRate = TotHeatRecRate - SensHeatRecRate;
1821 :
1822 18 : if (SensHeatRecRate > 0.0) {
1823 3 : this->SensHeatingRate = SensHeatRecRate;
1824 3 : this->SensCoolingRate = 0.0;
1825 : } else {
1826 15 : this->SensHeatingRate = 0.0;
1827 15 : this->SensCoolingRate = std::abs(SensHeatRecRate);
1828 : }
1829 18 : if (LatHeatRecRate > 0.0) {
1830 6 : this->LatHeatingRate = LatHeatRecRate;
1831 6 : this->LatCoolingRate = 0.0;
1832 : } else {
1833 12 : this->LatHeatingRate = 0.0;
1834 12 : this->LatCoolingRate = std::abs(LatHeatRecRate);
1835 : }
1836 18 : if (TotHeatRecRate > 0.0) {
1837 3 : this->TotHeatingRate = TotHeatRecRate;
1838 3 : this->TotCoolingRate = 0.0;
1839 : } else {
1840 15 : this->TotHeatingRate = 0.0;
1841 15 : this->TotCoolingRate = std::abs(TotHeatRecRate);
1842 : }
1843 :
1844 18 : this->ElecUseRate = ElecCons;
1845 18 : }
1846 :
1847 26560 : void HeatExchCond::CalcAirToAirGenericHeatExch(
1848 : EnergyPlusData &state,
1849 : bool const HXUnitOn, // flag to simulate heat exchanger heat recovery
1850 : bool const FirstHVACIteration, // first HVAC iteration flag
1851 : HVAC::FanOp const fanOp, // Supply air fan operating mode (1=cycling, 2=constant)
1852 : ObjexxFCL::Optional_bool_const EconomizerFlag, // economizer flag pass by air loop or OA sys
1853 : ObjexxFCL::Optional_bool_const HighHumCtrlFlag, // high humidity control flag passed by airloop or OA sys
1854 : ObjexxFCL::Optional<Real64 const> HXPartLoadRatio //
1855 : )
1856 : {
1857 :
1858 : // SUBROUTINE INFORMATION:
1859 : // AUTHOR Don Shirey
1860 : // DATE WRITTEN February 2003
1861 : // MODIFIED R. Raustad - FSEC, Feb 2009 - added economizer flags
1862 : // Both the economizer and high humidity control flags can disable the HX
1863 : // RE-ENGINEERED Richard Raustad, June 2003
1864 :
1865 : // PURPOSE OF THIS SUBROUTINE:
1866 : // Calculate the outlet conditions for an air to air generic heat
1867 : // exchanger given the inlet conditions.
1868 :
1869 : // METHODOLOGY EMPLOYED:
1870 : // This is a standard heat exchanger effectiveness model. No geometrical input data
1871 : // is needed. The model uses heat exchanger effectiveness performance data
1872 : // to calculate the air temperature and humidity ratio of the leaving
1873 : // supply and secondary air streams. Linear interpolation (or extrapolation)
1874 : // is assumed to obtain heat exchanger effectiveness at off-rated conditions.
1875 : // Economizer operation is allowed through the use of a Controller: Outside Air
1876 : // object.
1877 :
1878 : // REFERENCES:
1879 : // ARI Standard 1060-2001,Rating Air-to-Air Heat Exchangers for Energy Recovery Ventilation Equipment, www.ari.org
1880 : // ASHRAE Standard 84, Method of Testing Air-To-Air Heat Exchangers, www.ashrae.org
1881 : // U.S. Environmental Protection Agency software "SAVES" -
1882 : // School Advanced Ventilation Engineering Software http://www.epa.gov/iaq/schooldesign/saves.html
1883 :
1884 : // SUBROUTINE PARAMETER DEFINITIONS:
1885 26560 : Real64 constexpr ErrorTol(0.001); // error tolerance
1886 :
1887 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1888 : int SupOutNode;
1889 : Real64 Error; // iteration loop error variable
1890 : Real64 Iter; // iteration counter
1891 : Real64 ControlFraction; // fraction of effectiveness when rotary HX speed or plate bypass modulation is used for
1892 : // temperature control
1893 : Real64 RhoSup; // supply air density at actual pressure, temperature and humidity conditions [kg/m3]
1894 : Real64 RhoSec; // secondary air density at actual pressure, temperature and humidity conditions [kg/m3]
1895 : Real64 RhoStd; // standard air density at actual pressure, 20C dry-bulb temp and 0.0 absolute humidity [kg/m3]
1896 : Real64 CSup; // supply air heat capacity rate [W/K]
1897 : Real64 CSec; // secondary air heat capacity rate [W/K]
1898 : Real64 CMin; // minimum air heat capacity rate [W/K]
1899 : Real64 TempSecOutSat; // secondary air outlet temperature at saturation (at EnthsSecOut) [C]
1900 : Real64 HXSecAirVolFlowRate; // air volume flow rate of the secondary air stream through the heat exchanger [m3/sec]
1901 : Real64 HXSupAirVolFlowRate; // air volume flow rate of the supply air stream through the heat exchanger [m3/sec]
1902 : Real64 HXAvgAirVolFlowRate; // average air volume flow rate through the heat exchanger [m3/sec]
1903 : Real64 HXAirVolFlowRatio; // ratio of avg actual air volume flow through HX to nominal HX air volume flow [-]
1904 : Real64 HXTempSetPoint; // setpoint temperature at supply outlet node of HX when ControlToTemperatureSetPoint = Yes
1905 : Real64 MassFlowSecIn; // secondary air mass flow rate at HX inlet
1906 : // REAL(r64) :: MassFlowSecOut ! secondary air mass flow rate at HX outlet
1907 : Real64 MassFlowSupIn; // supply air mass flow rate at HX inlet
1908 : Real64 MassFlowSupOut; // supply air mass flow rate through HX core outlet
1909 : Real64 MassFlowSupBypass; // supply air bypass mass flow rate around HX core
1910 : Real64 TempSupIn; // supply side temperature of air entering HX
1911 : Real64 TempSupOut; // supply side temperature of air leaving HX core
1912 : Real64 HumRatSupIn; // supply side humidity ratio of air entering HX
1913 : Real64 TempSecIn; // secondary side temperature of air entering HX
1914 : Real64 SensHeatRecRate; // sensible heat recovery rate to supply air (heating +, cooling -)
1915 : Real64 LatHeatRecRate; // latent heat recovery rate to supply air (heating [humidify] +, cooling [dehumidify] -)
1916 : Real64 TotHeatRecRate; // total heat recovery rate to supply air (heating +, cooling -)
1917 : Real64 AirSidePLR;
1918 :
1919 : // Initialize local variables
1920 26560 : bool UnitOn = true; // unit on flag
1921 26560 : Real64 QSensTrans = 0.0; // sensible heat transferred by the heat exchanger [W]
1922 26560 : Real64 QTotTrans = 0.0; // total heat (sensible + latent) transferred by the heat exchanger [W]
1923 :
1924 26560 : this->DefrostFraction = 0.0;
1925 26560 : this->SensEffectiveness = 0.0;
1926 26560 : this->LatEffectiveness = 0.0;
1927 26560 : this->ElecUseRate = 0.0;
1928 26560 : this->SupOutTemp = this->SupInTemp;
1929 26560 : this->SecOutTemp = this->SecInTemp;
1930 26560 : this->SupOutHumRat = this->SupInHumRat;
1931 26560 : this->SecOutHumRat = this->SecInHumRat;
1932 26560 : this->SupOutEnth = this->SupInEnth;
1933 26560 : this->SecOutEnth = this->SecInEnth;
1934 26560 : SupOutNode = this->SupOutletNode;
1935 26560 : HXTempSetPoint = state.dataLoopNodes->Node(SupOutNode).TempSetPoint;
1936 :
1937 26560 : bool EconomizerActiveFlag = present(EconomizerFlag) && bool(EconomizerFlag); // local representing the economizer status when PRESENT
1938 26560 : bool HighHumCtrlActiveFlag = present(HighHumCtrlFlag) && bool(HighHumCtrlFlag); // local representing high humidity control when PRESENT
1939 :
1940 : // Determine mass flow through heat exchanger and mass flow being bypassed (only flat plate bypasses flow)
1941 26560 : if (((EconomizerActiveFlag || HighHumCtrlActiveFlag) && this->EconoLockOut) && this->ExchConfig == HXConfigurationType::Plate) {
1942 15 : this->SupBypassMassFlow = this->SupInMassFlow;
1943 15 : this->SupOutMassFlow = this->SupInMassFlow;
1944 15 : this->SecBypassMassFlow = this->SecInMassFlow;
1945 15 : this->SecOutMassFlow = this->SecInMassFlow;
1946 : } else { // No bypass mass flow
1947 26545 : this->SupOutMassFlow = this->SupInMassFlow;
1948 26545 : this->SecOutMassFlow = this->SecInMassFlow;
1949 26545 : this->SupBypassMassFlow = 0.0;
1950 26545 : this->SecBypassMassFlow = 0.0;
1951 : }
1952 : // Unit is scheduled OFF, so bypass heat exchange calcs
1953 26560 : if (this->availSched->getCurrentVal() <= 0.0) UnitOn = false;
1954 : //! Economizer is active, so bypass heat exchange calcs. This applies to both flat plate and rotary HX's
1955 26560 : if ((EconomizerActiveFlag || HighHumCtrlActiveFlag) && this->EconoLockOut) {
1956 21 : UnitOn = false;
1957 : }
1958 : // Determine if unit is ON or OFF based on air mass flow through the supply and secondary airstreams and operation flag
1959 26560 : if (this->SupInMassFlow <= HVAC::SmallMassFlow) UnitOn = false;
1960 26560 : if (this->SecInMassFlow <= HVAC::SmallMassFlow) UnitOn = false;
1961 26560 : if (!HXUnitOn) UnitOn = false;
1962 26560 : if (this->NomSupAirVolFlow == 0.0) UnitOn = false;
1963 :
1964 26560 : if (UnitOn) {
1965 22490 : bool FrostControlFlag = false; // unit is in frost control mode when TRUE
1966 : // Unit is on.
1967 22490 : if (present(HXPartLoadRatio) && fanOp == HVAC::FanOp::Cycling) {
1968 135 : if (HXPartLoadRatio > 0) {
1969 135 : AirSidePLR = HXPartLoadRatio;
1970 : } else {
1971 0 : AirSidePLR = 1.0;
1972 : }
1973 : } else {
1974 22355 : AirSidePLR = 1.0;
1975 : }
1976 :
1977 22490 : if (fanOp == HVAC::FanOp::Cycling) {
1978 135 : this->SupInMassFlow /= AirSidePLR;
1979 135 : this->SupOutMassFlow /= AirSidePLR;
1980 135 : this->SecInMassFlow /= AirSidePLR;
1981 135 : this->SecOutMassFlow /= AirSidePLR;
1982 135 : this->SupBypassMassFlow /= AirSidePLR;
1983 135 : this->SecBypassMassFlow /= AirSidePLR;
1984 : }
1985 :
1986 : // In the future, use actual node pressures in the following air density calls
1987 22490 : RhoStd = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, 20.0, 0.0);
1988 22490 : HXSupAirVolFlowRate = this->SupOutMassFlow / RhoStd; // volume flow using standard density
1989 22490 : HXSecAirVolFlowRate = this->SecOutMassFlow / RhoStd;
1990 : // Limit unbalanced volumetric flow ratio to 2:1
1991 22490 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
1992 1444 : if (HXSupAirVolFlowRate != 0.0 && HXSecAirVolFlowRate != 0.0) {
1993 1444 : if (((HXSupAirVolFlowRate / HXSecAirVolFlowRate) > 2.0) || ((HXSecAirVolFlowRate / HXSupAirVolFlowRate) > 2.0)) {
1994 1 : ++this->UnBalancedErrCount;
1995 1 : if (this->UnBalancedErrCount <= 2) {
1996 2 : ShowSevereError(state,
1997 2 : format("{}: \"{}\" unbalanced air volume flow ratio through the heat exchanger is greater than 2:1.",
1998 1 : HVAC::hxTypeNames[(int)this->type],
1999 1 : this->Name));
2000 2 : ShowContinueErrorTimeStamp(
2001 2 : state, format("...HX Supply air to Exhaust air flow ratio = {:.5R}.", HXSupAirVolFlowRate / HXSecAirVolFlowRate));
2002 : } else {
2003 0 : ShowRecurringWarningErrorAtEnd(
2004 : state,
2005 0 : format("{} \"{}\": Unbalanced air volume flow ratio exceeds 2:1 warning continues. HX flow ratio statistics follow.",
2006 0 : HVAC::hxTypeNames[(int)this->type],
2007 0 : this->Name),
2008 0 : this->UnBalancedErrIndex,
2009 0 : HXSupAirVolFlowRate / HXSecAirVolFlowRate,
2010 0 : HXSupAirVolFlowRate / HXSecAirVolFlowRate);
2011 : }
2012 : }
2013 : }
2014 : }
2015 : // Calculate average volumetric flow rate of the two air streams
2016 22490 : HXAvgAirVolFlowRate = (HXSecAirVolFlowRate + HXSupAirVolFlowRate) / 2.0;
2017 22490 : HXAirVolFlowRatio = HXAvgAirVolFlowRate / this->NomSupAirVolFlow;
2018 : // Average air volume flow rate must be between 50% and 130% of nominal supply air volume flow
2019 22490 : if (HXAirVolFlowRatio > 1.3 || HXAirVolFlowRatio < 0.5) {
2020 616 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
2021 88 : ++this->LowFlowErrCount;
2022 88 : if (this->LowFlowErrCount == 1) {
2023 2 : ShowWarningError(state, format("{} \"{}\"", HVAC::hxTypeNames[(int)this->type], this->Name));
2024 4 : ShowContinueError(state, "Average air volume flow rate is <50% or >130% of the nominal HX supply air volume flow rate.");
2025 2 : ShowContinueErrorTimeStamp(state, format("Air volume flow rate ratio = {:.3R}.", HXAirVolFlowRatio));
2026 : } else {
2027 688 : ShowRecurringWarningErrorAtEnd(
2028 : state,
2029 172 : format(
2030 : "{} \"{}\": Average air volume flow rate is <50% or >130% warning continues. Air flow rate ratio statistics follow.",
2031 86 : HVAC::hxTypeNames[(int)this->type],
2032 86 : this->Name),
2033 86 : this->LowFlowErrIndex,
2034 : HXAirVolFlowRatio,
2035 : HXAirVolFlowRatio);
2036 : }
2037 : }
2038 : }
2039 :
2040 : // Determine heat exchanger effectiveness using avg air volume flow rate based on actual inlet air density
2041 : // Linearly interpolate and extrapolate (within limits) from effectiveness input values
2042 22490 : RhoSup = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->SupInTemp, this->SupInHumRat);
2043 22490 : RhoSec = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->SecInTemp, this->SecInHumRat);
2044 22490 : HXSupAirVolFlowRate = this->SupOutMassFlow / RhoSup;
2045 22490 : HXSecAirVolFlowRate = this->SecOutMassFlow / RhoSec;
2046 22490 : HXAvgAirVolFlowRate = (HXSecAirVolFlowRate + HXSupAirVolFlowRate) / 2.0;
2047 22490 : HXAirVolFlowRatio = HXAvgAirVolFlowRate / this->NomSupAirVolFlow;
2048 :
2049 22490 : if (this->SupInTemp < this->SecInTemp) {
2050 : // Use heating effectiveness values
2051 4403 : this->SensEffectiveness = this->HeatEffectSensible100;
2052 4403 : if (this->HeatEffectSensibleCurveIndex > 0) {
2053 4396 : this->SensEffectiveness *= Curve::CurveValue(state, this->HeatEffectSensibleCurveIndex, HXAirVolFlowRatio);
2054 : }
2055 4403 : this->LatEffectiveness = this->HeatEffectLatent100;
2056 4403 : if (this->HeatEffectLatentCurveIndex > 0) {
2057 4396 : this->LatEffectiveness *= Curve::CurveValue(state, this->HeatEffectLatentCurveIndex, HXAirVolFlowRatio);
2058 : }
2059 : } else {
2060 : // Use cooling effectiveness values
2061 18087 : this->SensEffectiveness = this->CoolEffectSensible100;
2062 18087 : if (this->CoolEffectSensibleCurveIndex > 0) {
2063 18083 : this->SensEffectiveness *= Curve::CurveValue(state, this->CoolEffectSensibleCurveIndex, HXAirVolFlowRatio);
2064 : }
2065 18087 : this->LatEffectiveness = this->CoolEffectLatent100;
2066 18087 : if (this->CoolEffectLatentCurveIndex > 0) {
2067 18083 : this->LatEffectiveness *= Curve::CurveValue(state, this->CoolEffectLatentCurveIndex, HXAirVolFlowRatio);
2068 : }
2069 : }
2070 :
2071 : // Keep effectiveness between 0 and 1.0 ??
2072 : // HXOpSensEffect = MAX(MIN(HXOpSensEffect,1.0),0.0)
2073 : // HXOpLatEffect = MAX(MIN(HXOpLatEffect,1.0),0.0)
2074 22490 : if (this->SensEffectiveness < 0.0) {
2075 : // The model should at least guard against negative numbers
2076 0 : this->SensEffectiveness = 0.0;
2077 0 : if (!this->SensEffectivenessFlag) {
2078 0 : ShowWarningError(
2079 : state,
2080 0 : format(
2081 : "HeatExchanger:AirToAir:SensibleAndLatent =\"{}\" sensible effectiveness is less than zero. Check the following inputs.",
2082 0 : this->Name));
2083 0 : if (this->SupInTemp < this->SecInTemp) {
2084 0 : ShowContinueError(state, format("...Sensible Effectiveness at 100% Heating Air Flow = {:.2R}", this->HeatEffectSensible100));
2085 0 : ShowContinueError(state, "...Sensible effectiveness reset to zero and the simulation continues.");
2086 : } else {
2087 0 : ShowContinueError(state, format("...Sensible Effectiveness at 100% Cooling Air Flow = {:.2R}", this->CoolEffectSensible100));
2088 0 : ShowContinueError(state, "...Sensible effectiveness reset to zero and the simulation continues.");
2089 : }
2090 0 : ShowContinueError(state, format("...Heat Exchanger Air Volume Flow Ratio = {:.2R}", HXAirVolFlowRatio));
2091 0 : this->SensEffectivenessFlag = true;
2092 : }
2093 : }
2094 22490 : if (this->LatEffectiveness < 0.0) {
2095 : // The model should at least guard against negative numbers
2096 0 : this->LatEffectiveness = 0.0;
2097 0 : if (!this->LatEffectivenessFlag) {
2098 0 : ShowWarningError(
2099 : state,
2100 0 : format("HeatExchanger:AirToAir:SensibleAndLatent =\"{}\" latent effectiveness is less than zero. Check the following inputs.",
2101 0 : this->Name));
2102 0 : if (this->SupInTemp < this->SecInTemp) {
2103 0 : ShowContinueError(state, format("...Latent Effectiveness at 100% Heating Air Flow = {:.2R}", this->HeatEffectLatent100));
2104 0 : ShowContinueError(state, "...Latent effectiveness reset to zero and the simulation continues.");
2105 : } else {
2106 0 : ShowContinueError(state, format("...Latent Effectiveness at 100% Cooling Air Flow = {:.2R}", this->CoolEffectLatent100));
2107 0 : ShowContinueError(state, "...Latent effectiveness reset to zero and the simulation continues.");
2108 : }
2109 0 : ShowContinueError(state, format("...Heat Exchanger Air Volume Flow Ratio = {:.2R}", HXAirVolFlowRatio));
2110 0 : this->LatEffectivenessFlag = true;
2111 : }
2112 : }
2113 : // Use the effectiveness to calculate the air conditions exiting the heat exchanger (all air flow through the HX)
2114 : // Include EATR and OACF in the following calculations at some point
2115 :
2116 22490 : CSup = this->SupOutMassFlow * Psychrometrics::PsyCpAirFnW(this->SupInHumRat);
2117 22490 : CSec = this->SecOutMassFlow * Psychrometrics::PsyCpAirFnW(this->SecInHumRat);
2118 22490 : CMin = min(CSup, CSec);
2119 :
2120 22490 : this->SupOutTemp = this->SupInTemp + this->SensEffectiveness * CMin / CSup * (this->SecInTemp - this->SupInTemp);
2121 22490 : this->SupOutHumRat = this->SupInHumRat + this->LatEffectiveness * CMin / CSup * (this->SecInHumRat - this->SupInHumRat);
2122 22490 : this->SupOutEnth = Psychrometrics::PsyHFnTdbW(this->SupOutTemp, this->SupOutHumRat);
2123 :
2124 : // Check for saturation in supply outlet and reset temp, then humidity ratio at constant enthalpy
2125 22490 : if (Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress) > this->SupOutTemp) {
2126 3 : this->SupOutTemp = Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress);
2127 3 : this->SupOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SupOutTemp, this->SupOutEnth);
2128 : }
2129 22490 : QSensTrans = CSup * (this->SupInTemp - this->SupOutTemp);
2130 22490 : this->SecOutTemp = this->SecInTemp + QSensTrans / CSec;
2131 22490 : QTotTrans = this->SupOutMassFlow * (this->SupInEnth - this->SupOutEnth);
2132 22490 : this->SecOutEnth = this->SecInEnth + QTotTrans / this->SecOutMassFlow;
2133 22490 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth);
2134 : // Control the supply air outlet temperature to a setpoint for Heating Mode only
2135 : // (ControlFraction = 0 HX fully bypassed, ControlFraction = 1 air passed entirely through HX)
2136 : // (supply air stream bypass mass flow rate proportional to ControlFraction except when frost control is active)
2137 22490 : if (this->ControlToTemperatureSetPoint) {
2138 15677 : if ((this->SupInTemp - this->SupOutTemp) != 0.0) {
2139 15677 : if ((this->SupOutTemp > HXTempSetPoint && this->SupInTemp < HXTempSetPoint) ||
2140 13373 : (this->SupInTemp > HXTempSetPoint && this->SupOutTemp < HXTempSetPoint)) {
2141 2306 : ControlFraction = max(0.0, min(1.0, std::abs((this->SupInTemp - HXTempSetPoint) / (this->SupInTemp - this->SupOutTemp))));
2142 13371 : } else if ((this->SupInTemp < this->SupOutTemp && this->SupOutTemp < HXTempSetPoint) ||
2143 13337 : (this->SupInTemp > this->SupOutTemp && this->SupOutTemp > HXTempSetPoint)) {
2144 13361 : ControlFraction = 1.0;
2145 : } else {
2146 10 : ControlFraction = 0.0;
2147 : }
2148 : } else {
2149 : // ELSE fully bypass HX to maintain supply outlet temp as high as possible
2150 0 : ControlFraction = 0.0;
2151 : }
2152 15677 : if (this->ExchConfig == HXConfigurationType::Rotary) {
2153 : // Rotary HX's never get bypassed, rotational speed is modulated
2154 2 : this->SensEffectiveness *= ControlFraction;
2155 2 : this->LatEffectiveness *= ControlFraction;
2156 : } else { // HX is a plate heat exchanger, bypass air to control SA temperature
2157 15675 : Error = 1.0;
2158 15675 : Iter = 0.0;
2159 15675 : MassFlowSupIn = this->SupInMassFlow;
2160 15675 : MassFlowSupOut = this->SupOutMassFlow;
2161 15675 : MassFlowSupBypass = this->SupBypassMassFlow;
2162 15675 : MassFlowSecIn = this->SecInMassFlow;
2163 15675 : TempSupIn = this->SupInTemp;
2164 15675 : TempSupOut = this->SupOutTemp;
2165 15675 : HumRatSupIn = this->SupInHumRat;
2166 15675 : TempSecIn = this->SecInTemp;
2167 22585 : while ((std::abs(Error) > ErrorTol && Iter < 10 && ControlFraction < 1.0) || Iter == 1) {
2168 6920 : MassFlowSupOut = MassFlowSupIn * ControlFraction;
2169 6920 : MassFlowSupBypass = MassFlowSupIn * (1.0 - ControlFraction);
2170 6920 : HXSupAirVolFlowRate = MassFlowSupOut / RhoSup;
2171 6920 : HXAvgAirVolFlowRate = (HXSecAirVolFlowRate + HXSupAirVolFlowRate) / 2.0;
2172 6920 : HXAirVolFlowRatio = HXAvgAirVolFlowRate / this->NomSupAirVolFlow;
2173 6920 : CSup = MassFlowSupOut * Psychrometrics::PsyCpAirFnW(HumRatSupIn);
2174 6920 : CMin = min(CSup, CSec);
2175 6920 : if (TempSupIn < TempSecIn) {
2176 : // Use heating effectiveness values
2177 6918 : this->SensEffectiveness = this->HeatEffectSensible100;
2178 6918 : if (this->HeatEffectSensibleCurveIndex > 0) {
2179 6916 : this->SensEffectiveness *= Curve::CurveValue(state, this->HeatEffectSensibleCurveIndex, HXAirVolFlowRatio);
2180 : }
2181 6918 : this->LatEffectiveness = this->HeatEffectLatent100;
2182 6918 : if (this->HeatEffectLatentCurveIndex > 0) {
2183 6916 : this->LatEffectiveness *= Curve::CurveValue(state, this->HeatEffectLatentCurveIndex, HXAirVolFlowRatio);
2184 : }
2185 : } else {
2186 : // Use cooling effectiveness values
2187 2 : this->SensEffectiveness = this->CoolEffectSensible100;
2188 2 : if (this->CoolEffectSensibleCurveIndex > 0) {
2189 0 : this->SensEffectiveness *= Curve::CurveValue(state, this->CoolEffectSensibleCurveIndex, HXAirVolFlowRatio);
2190 : }
2191 2 : this->LatEffectiveness = this->CoolEffectLatent100;
2192 2 : if (this->CoolEffectLatentCurveIndex > 0) {
2193 0 : this->LatEffectiveness *= Curve::CurveValue(state, this->CoolEffectLatentCurveIndex, HXAirVolFlowRatio);
2194 : }
2195 : }
2196 :
2197 6920 : if (this->SensEffectiveness < 0.0) {
2198 : // The model should at least guard against negative numbers
2199 0 : this->SensEffectiveness = 0.0;
2200 0 : if (!this->SensEffectivenessFlag) {
2201 0 : ShowWarningError(state,
2202 0 : format("HeatExchanger:AirToAir:SensibleAndLatent =\"{}\" sensible effectiveness is less than zero. "
2203 : "Check the following inputs.",
2204 0 : this->Name));
2205 0 : if (this->SupInTemp < this->SecInTemp) {
2206 0 : ShowContinueError(
2207 0 : state, format("...Sensible Effectiveness at 100% Heating Air Flow = {:.2R}", this->HeatEffectSensible100));
2208 0 : ShowContinueError(state, "...Sensible effectiveness reset to zero and the simulation continues.");
2209 : } else {
2210 0 : ShowContinueError(
2211 0 : state, format("...Sensible Effectiveness at 100% Cooling Air Flow = {:.2R}", this->CoolEffectSensible100));
2212 0 : ShowContinueError(state, "...Sensible effectiveness reset to zero and the simulation continues.");
2213 : }
2214 0 : ShowContinueError(state, format("...Heat Exchanger Air Volume Flow Ratio = {:.2R}", HXAirVolFlowRatio));
2215 0 : this->SensEffectivenessFlag = true;
2216 : }
2217 : }
2218 6920 : if (this->LatEffectiveness < 0.0) {
2219 : // The model should at least guard against negative numbers
2220 0 : this->LatEffectiveness = 0.0;
2221 0 : if (!this->LatEffectivenessFlag) {
2222 0 : ShowWarningError(state,
2223 0 : format("HeatExchanger:AirToAir:SensibleAndLatent =\"{}\" latent effectiveness is less than zero. "
2224 : "Check the following inputs.",
2225 0 : this->Name));
2226 0 : if (this->SupInTemp < this->SecInTemp) {
2227 0 : ShowContinueError(state,
2228 0 : format("...Latent Effectiveness at 100% Heating Air Flow = {:.2R}", this->HeatEffectLatent100));
2229 0 : ShowContinueError(state, "...Latent effectiveness reset to zero and the simulation continues.");
2230 : } else {
2231 0 : ShowContinueError(state,
2232 0 : format("...Latent Effectiveness at 100% Cooling Air Flow = {:.2R}", this->CoolEffectLatent100));
2233 0 : ShowContinueError(state, "...Latent effectiveness reset to zero and the simulation continues.");
2234 : }
2235 0 : ShowContinueError(state, format("...Heat Exchanger Air Volume Flow Ratio = {:.2R}", HXAirVolFlowRatio));
2236 0 : this->LatEffectivenessFlag = true;
2237 : }
2238 : }
2239 :
2240 6920 : if (CSup == 0.0) {
2241 : // IF CSup = 0, then supply air mass flow rate = 0 and HX is fully bypassed. Fix divide by 0 error below DO loop.
2242 10 : CSup = 1.0;
2243 10 : CMin = 0.0;
2244 10 : break;
2245 : }
2246 6910 : TempSupOut = (MassFlowSupOut * (TempSupIn + this->SensEffectiveness * CMin / CSup * (TempSecIn - TempSupIn)) +
2247 6910 : MassFlowSupBypass * TempSupIn) /
2248 : MassFlowSupIn;
2249 6910 : Error = (TempSupOut - HXTempSetPoint);
2250 : // IF supply inlet temp = supply outlet temp, fully bypass HX - ELSE control to SP
2251 6910 : if (TempSupIn != TempSupOut) {
2252 6910 : ControlFraction = max(0.0, min(1.0, std::abs(ControlFraction * (TempSupIn - HXTempSetPoint) / (TempSupIn - TempSupOut))));
2253 0 : } else if (std::abs(TempSupOut - HXTempSetPoint) < ErrorTol) {
2254 : // IF TempSupIn = TempSupOut then TempSecIn = TempSupIn (ControlFraction = ?)
2255 : // Do nothing, variables in ELSE below have already been calculated
2256 0 : break;
2257 : } else {
2258 : // or HX is fully bypassed (ControlFraction = 0) which actually should be caught in IF(CSup .EQ. 0.0)THEN above.
2259 0 : ControlFraction = 0.0;
2260 0 : MassFlowSupOut = MassFlowSupIn * ControlFraction;
2261 0 : MassFlowSupBypass = MassFlowSupIn * (1.0 - ControlFraction);
2262 0 : CSup = 1.0;
2263 0 : CMin = 0.0;
2264 0 : break;
2265 : }
2266 6910 : ++Iter;
2267 : }
2268 :
2269 15675 : this->SupInMassFlow = MassFlowSupIn;
2270 15675 : this->SupOutMassFlow = MassFlowSupOut;
2271 15675 : this->SupBypassMassFlow = MassFlowSupBypass;
2272 15675 : this->SecInMassFlow = MassFlowSecIn;
2273 15675 : this->SupInTemp = TempSupIn;
2274 15675 : this->SupOutTemp = TempSupOut;
2275 15675 : this->SupInHumRat = HumRatSupIn;
2276 15675 : this->SecInTemp = TempSecIn;
2277 :
2278 : } // ENDIF for "IF (thisExch%ExchConfig == 'ROTARY') THEN"
2279 15677 : this->SupOutTemp = this->SupInTemp + this->SensEffectiveness * CMin / CSup * (this->SecInTemp - this->SupInTemp);
2280 15677 : this->SupOutHumRat = this->SupInHumRat + this->LatEffectiveness * CMin / CSup * (this->SecInHumRat - this->SupInHumRat);
2281 15677 : this->SupOutEnth = Psychrometrics::PsyHFnTdbW(this->SupOutTemp, this->SupOutHumRat);
2282 :
2283 : // Check for saturation in supply outlet and reset temp, then humidity ratio at constant enthalpy
2284 15677 : if (Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress) > this->SupOutTemp) {
2285 0 : this->SupOutTemp = Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress);
2286 0 : this->SupOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SupOutTemp, this->SupOutEnth);
2287 : }
2288 :
2289 15677 : QSensTrans = CSup * (this->SupInTemp - this->SupOutTemp);
2290 15677 : this->SecOutTemp = this->SecInTemp + QSensTrans / CSec;
2291 15677 : QTotTrans = this->SupOutMassFlow * (this->SupInEnth - this->SupOutEnth);
2292 15677 : this->SecOutEnth = this->SecInEnth + QTotTrans / this->SecOutMassFlow;
2293 15677 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth);
2294 :
2295 : } // ENDIF for "IF(thisExch%ControlToTemperatureSetPoint .AND... THEN, ELSE"
2296 :
2297 22490 : if (fanOp == HVAC::FanOp::Cycling) {
2298 135 : this->SupInMassFlow *= AirSidePLR;
2299 135 : this->SupOutMassFlow *= AirSidePLR;
2300 135 : this->SecInMassFlow *= AirSidePLR;
2301 135 : this->SecOutMassFlow *= AirSidePLR;
2302 135 : this->SupBypassMassFlow *= AirSidePLR;
2303 135 : this->SecBypassMassFlow *= AirSidePLR;
2304 22355 : } else if (fanOp == HVAC::FanOp::Continuous) {
2305 22355 : this->SupOutTemp = this->SupOutTemp * AirSidePLR + this->SupInTemp * (1.0 - AirSidePLR);
2306 22355 : this->SupOutHumRat = this->SupOutHumRat * AirSidePLR + this->SupInHumRat * (1.0 - AirSidePLR);
2307 22355 : this->SupOutEnth = this->SupOutEnth * AirSidePLR + this->SupOutEnth * (1.0 - AirSidePLR);
2308 22355 : this->SecOutTemp = this->SecOutTemp * AirSidePLR + this->SecInTemp * (1.0 - AirSidePLR);
2309 22355 : this->SecOutHumRat = this->SecOutHumRat * AirSidePLR + this->SecInHumRat * (1.0 - AirSidePLR);
2310 22355 : this->SecOutEnth = this->SecOutEnth * AirSidePLR + this->SecOutEnth * (1.0 - AirSidePLR);
2311 : }
2312 :
2313 22490 : if ((this->FrostControlType == FrostControlOption::MinimumExhaustTemperature && this->SecOutTemp < this->ThresholdTemperature) ||
2314 18114 : (this->FrostControlType == FrostControlOption::ExhaustAirRecirculation && this->SupInTemp <= this->ThresholdTemperature) ||
2315 18114 : (this->FrostControlType == FrostControlOption::ExhaustOnly && this->SupInTemp <= this->ThresholdTemperature)) {
2316 4376 : this->FrostControl(state);
2317 4376 : FrostControlFlag = true;
2318 : }
2319 :
2320 : // check for saturation in secondary outlet
2321 22490 : TempSecOutSat = Psychrometrics::PsyTsatFnHPb(state, this->SecOutEnth, state.dataEnvrn->OutBaroPress);
2322 22490 : if (TempSecOutSat > this->SecOutTemp) {
2323 4 : this->SecOutTemp = TempSecOutSat;
2324 4 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth);
2325 : }
2326 :
2327 : // calculate outlet conditions by mixing bypass air stream with air that went through the
2328 : // heat exchanger core. Perform this mixing only when no frost control is used or
2329 : // heat exchanger is not in frost control mode. Mixing similar to this is performed
2330 : // in the frost control subroutine when in frost control mode.
2331 22490 : if (!FrostControlFlag) {
2332 18114 : this->SupOutEnth = (this->SupOutMassFlow * this->SupOutEnth + this->SupBypassMassFlow * this->SupInEnth) / this->SupInMassFlow;
2333 18114 : this->SupOutHumRat = (this->SupOutMassFlow * this->SupOutHumRat + this->SupBypassMassFlow * this->SupInHumRat) / this->SupInMassFlow;
2334 18114 : this->SupOutTemp = Psychrometrics::PsyTdbFnHW(this->SupOutEnth, this->SupOutHumRat);
2335 18114 : this->SupOutMassFlow = this->SupInMassFlow;
2336 18114 : this->SecOutEnth = (this->SecOutMassFlow * this->SecOutEnth + this->SecBypassMassFlow * this->SecInEnth) / this->SecInMassFlow;
2337 18114 : this->SecOutHumRat = (this->SecOutMassFlow * this->SecOutHumRat + this->SecBypassMassFlow * this->SecInHumRat) / this->SecInMassFlow;
2338 18114 : this->SecOutTemp = Psychrometrics::PsyTdbFnHW(this->SecOutEnth, this->SecOutHumRat);
2339 18114 : this->SecOutMassFlow = this->SecInMassFlow;
2340 : }
2341 :
2342 22490 : this->ElecUseRate = this->NomElecPower;
2343 :
2344 : } // ENDIF for "IF (UnitOn) THEN"
2345 :
2346 : // Calculate heat transfer from the unit using the final supply inlet and supply outlet air conditions
2347 26560 : CSup = this->SupOutMassFlow * Psychrometrics::PsyCpAirFnW(this->SupInHumRat);
2348 26560 : SensHeatRecRate = CSup * (this->SupOutTemp - this->SupInTemp);
2349 26560 : TotHeatRecRate = this->SupOutMassFlow * (this->SupOutEnth - this->SupInEnth);
2350 26560 : LatHeatRecRate = TotHeatRecRate - SensHeatRecRate;
2351 :
2352 : // Set report variables based on sign of recovery rate
2353 26560 : if (SensHeatRecRate > 0.0) {
2354 4393 : this->SensHeatingRate = SensHeatRecRate;
2355 4393 : this->SensCoolingRate = 0.0;
2356 : } else {
2357 22167 : this->SensHeatingRate = 0.0;
2358 22167 : this->SensCoolingRate = std::abs(SensHeatRecRate);
2359 : }
2360 26560 : if (LatHeatRecRate > 0.0) {
2361 2696 : this->LatHeatingRate = LatHeatRecRate;
2362 2696 : this->LatCoolingRate = 0.0;
2363 : } else {
2364 23864 : this->LatHeatingRate = 0.0;
2365 23864 : this->LatCoolingRate = std::abs(LatHeatRecRate);
2366 : }
2367 26560 : if (TotHeatRecRate > 0.0) {
2368 4392 : this->TotHeatingRate = TotHeatRecRate;
2369 4392 : this->TotCoolingRate = 0.0;
2370 : } else {
2371 22168 : this->TotHeatingRate = 0.0;
2372 22168 : this->TotCoolingRate = std::abs(TotHeatRecRate);
2373 : }
2374 26560 : }
2375 :
2376 4 : void HeatExchCond::CalcDesiccantBalancedHeatExch(
2377 : EnergyPlusData &state,
2378 : bool const HXUnitOn, // flag to simulate heat exchanger heat recovery
2379 : bool const FirstHVACIteration, // First HVAC iteration flag
2380 : HVAC::FanOp const fanOp, // Supply air fan operating mode (1=cycling, 2=constant)
2381 : Real64 const PartLoadRatio, // Part load ratio requested of DX compressor
2382 : int const CompanionCoilIndex, // index of companion cooling coil
2383 : int const CompanionCoilType, // type of cooling coil
2384 : bool const RegenInletIsOANode, // Flag to determine if regen side inlet is OANode, if so this air stream cycles
2385 : ObjexxFCL::Optional_bool_const EconomizerFlag, // economizer flag pass by air loop or OA sys
2386 : ObjexxFCL::Optional_bool_const HighHumCtrlFlag // high humidity control flag passed by airloop or OA sys
2387 : )
2388 : {
2389 :
2390 : // SUBROUTINE INFORMATION:
2391 : // AUTHOR Mangesh Basarkar, FSEC
2392 : // DATE WRITTEN January 2007
2393 : // MODIFIED R. Raustad - FSEC, Feb 2009 - added economizer flags
2394 : // Both the economizer and high humidity control flags can disable the HX
2395 : // RE-ENGINEERED na
2396 :
2397 : // PURPOSE OF THIS SUBROUTINE:
2398 : // Calculate the outlet conditions for a balanced air-to-air desiccant heat exchanger
2399 : // given the inlet conditions and face velocity. Performance map is provided by user.
2400 :
2401 : // METHODOLOGY EMPLOYED:
2402 : // This is an empirical heat exchanger model. The model uses heat exchanger performance data to
2403 : // calculate the air temperature and humidity ratio of the leaving supply and secondary air streams.
2404 : // Humidity control can enable/disable heat recovery through the use of the HXUnitOn Subroutine argument.
2405 :
2406 : // Using/Aliasing
2407 : using DataLoopNode::SensedNodeFlagValue;
2408 :
2409 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2410 : bool UnitOn; // unit on flag
2411 : Real64 RhoStd; // standard air density at actual pressure, 20C dry-bulb temp and 0.0 absolute humidity [kg/m3]
2412 : Real64 CSup; // supply air heat capacity rate [W/K]
2413 : Real64 CSec; // secondary air heat capacity rate [W/K]
2414 : Real64 TempSecOutSat; // secondary air outlet temperature at saturation (at EnthsSecOut) [C]
2415 : Real64 SensHeatRecRate; // sensible heat recovery rate to supply air (heating +, cooling -)
2416 : Real64 TotHeatRecRate; // total heat recovery rate to supply air (heating +, cooling -)
2417 : Real64 ProcessSensHeatRecRate; // process sensible heat recovery rate (heating +, cooling -)
2418 : Real64 ProcessTotHeatRecRate; // process total heat recovery rate (heating +, cooling -)
2419 : Real64 ProcessLatHeatRecRate; // process latent heat recovery rate (heating [humidify] +, cooling [dehumidify] -)
2420 :
2421 : Real64 BalFaceVelActual; // operating face velocity [m/s]
2422 4 : Real64 FullLoadSupOutTemp(0); // empirical model supply outlet temperature [C]
2423 4 : Real64 FullLoadSupOutHumRat(0); // empirical model supply outlet humidity ratio [kg/kg]
2424 : Real64 FullLoadDeltaT; // empirical model heat exchanger delta temperature [C]
2425 : Real64 FullLoadDeltaW; // empirical model heat exchanger delta humidity ratio [kg/kg]
2426 : Real64 T_RegenInTemp; // empirical model supply (regen) inlet temperature for temperature equation [C]
2427 : Real64 T_RegenInHumRat; // empirical model supply (regen) inlet humidity ratio for temperature equation [kg/kg]
2428 : Real64 T_ProcInTemp; // empirical model secondary (process) inlet temperature for temperature equation [C]
2429 : Real64 T_ProcInHumRat; // empirical model secondary (process) inlet humidity ratio for temperature equation [kg/kg]
2430 : Real64 T_FaceVel; // empirical model face velocity for temperature equation [m/s]
2431 : Real64 H_RegenInTemp; // empirical model supply (regen) inlet temperature for humidity ratio equation [C]
2432 : Real64 H_RegenInHumRat; // empirical model supply (regen) inlet humidity ratio for humidity ratio equation [kg/kg]
2433 : Real64 H_ProcInTemp; // empirical model secondary (process) inlet temperature for humidity ratio equation [C]
2434 : Real64 H_ProcInHumRat; // empirical model secondary (process) inlet humidity ratio for humidity ratio equation [kg/kg]
2435 : Real64 H_FaceVel; // empirical model face velocity for humidity ratio equation [m/s]
2436 : Real64 MaxHumRatNeeded; // maximum humidity ratio setpoint for balanced desiccant HX [kg/kg]
2437 : Real64 MinHumRatNeeded; // minimum humidity ratio setpoint for balanced desiccant HX [kg/kg]
2438 : Real64 HXPartLoadRatio; // local heat exchanger part-load ratio
2439 : Real64 TestSaturationEnthalpy; // enthalpy used to test for regeneration outlet condition over saturation curve (J/kg)
2440 : Real64 AverageMassFlowRate; // average of supply (regen) and secondary (process) mass flow rates [kg/s]
2441 : bool EconomizerActiveFlag; // local representing the economizer status when PRESENT
2442 : bool HighHumCtrlActiveFlag; // local representing high humidity control when PRESENT
2443 :
2444 : // Initialize local variables
2445 4 : UnitOn = true;
2446 4 : SensHeatRecRate = 0.0;
2447 4 : TotHeatRecRate = 0.0;
2448 4 : HXPartLoadRatio = PartLoadRatio;
2449 4 : this->DefrostFraction = 0.0;
2450 4 : this->ElecUseRate = 0.0;
2451 4 : this->SupOutTemp = this->SupInTemp;
2452 4 : this->SecOutTemp = this->SecInTemp;
2453 4 : this->SupOutHumRat = this->SupInHumRat;
2454 4 : this->SecOutHumRat = this->SecInHumRat;
2455 4 : this->SupOutEnth = this->SupInEnth;
2456 4 : this->SecOutEnth = this->SecInEnth;
2457 4 : this->SupOutMassFlow = this->SupInMassFlow;
2458 4 : this->SecOutMassFlow = this->SecInMassFlow;
2459 4 : AverageMassFlowRate = (this->SupOutMassFlow + this->SecOutMassFlow) / 2.0;
2460 :
2461 4 : if (present(EconomizerFlag)) {
2462 0 : EconomizerActiveFlag = EconomizerFlag;
2463 : } else {
2464 4 : EconomizerActiveFlag = false;
2465 : }
2466 :
2467 4 : if (present(HighHumCtrlFlag)) {
2468 0 : HighHumCtrlActiveFlag = HighHumCtrlFlag;
2469 : } else {
2470 4 : HighHumCtrlActiveFlag = false;
2471 : }
2472 :
2473 : // Unit is scheduled OFF, so bypass heat exchange calcs
2474 4 : if (this->availSched->getCurrentVal() <= 0.0) UnitOn = false;
2475 : // Determine if unit is ON or OFF based on air mass flow through the supply and secondary airstreams and operation flag
2476 4 : if (this->SupInMassFlow <= HVAC::SmallMassFlow) UnitOn = false;
2477 4 : if (this->SecInMassFlow <= HVAC::SmallMassFlow) UnitOn = false;
2478 4 : if (HXPartLoadRatio == 0.0) UnitOn = false;
2479 4 : if (!HXUnitOn) UnitOn = false;
2480 4 : if ((EconomizerActiveFlag || HighHumCtrlActiveFlag) && this->EconoLockOut) UnitOn = false;
2481 :
2482 4 : if (UnitOn) {
2483 0 : constexpr std::string_view ThisSubTSat = "CalcDesiccantBalancedHeatExch: TSat";
2484 0 : constexpr std::string_view ThisSubSecOutHumRat = "CalcDesiccantBalancedHeatExch: SecOutHumRat";
2485 0 : constexpr std::string_view ThisSubTestSatSec = "CalcDesiccantBalancedHeatExch: TestSatSec";
2486 : Real64 local_SupInMassFlow; // Supply side HX mass flow rate
2487 : Real64 local_SecInMassFlow; // Secondary side HX mass flow rate
2488 :
2489 : // Use local variables to perform checks
2490 0 : local_SecInMassFlow = this->SecInMassFlow;
2491 0 : local_SupInMassFlow = this->SupInMassFlow;
2492 :
2493 : // In constant fan mode, process air mass flow rate is full flow and supply (regen) air cycles based on PLR.
2494 : // If supply (regen) inlet is OA node, regen mass flow rate is proportional to PLR.
2495 : // If both of the above is true then boost local variable up to full flow
2496 0 : if ((fanOp == HVAC::FanOp::Continuous) && RegenInletIsOANode) {
2497 0 : local_SupInMassFlow /= HXPartLoadRatio;
2498 : }
2499 : // for cycling fan case, boost both local variables up to full flow
2500 0 : if (fanOp == HVAC::FanOp::Cycling) {
2501 0 : local_SupInMassFlow /= HXPartLoadRatio; // supply = regen
2502 0 : local_SecInMassFlow /= HXPartLoadRatio; // secondary = process
2503 : }
2504 :
2505 : // Check for balanced flow condition
2506 0 : this->CheckForBalancedFlow(state, local_SecInMassFlow, local_SupInMassFlow, FirstHVACIteration);
2507 :
2508 0 : auto const &perf = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex);
2509 :
2510 0 : T_ProcInTemp = state.dataHeatRecovery->FullLoadOutAirTemp;
2511 0 : T_ProcInHumRat = state.dataHeatRecovery->FullLoadOutAirHumRat;
2512 0 : T_RegenInTemp = this->SupInTemp;
2513 0 : T_RegenInHumRat = this->SupInHumRat;
2514 :
2515 : // Must use the same density used to convert volumetric flow rate to mass flow rate to get back to velocity
2516 0 : RhoStd = state.dataEnvrn->StdRhoAir; // PsyRhoAirFnPbTdbW(StdBaroPress,20.0d0, 0.0d0)
2517 0 : BalFaceVelActual = local_SupInMassFlow / (RhoStd * this->FaceArea);
2518 :
2519 0 : T_FaceVel = BalFaceVelActual;
2520 :
2521 : // Call model check routines only when HX is active, if coil is off these checks do not apply (no potential for heat transfer)
2522 : // Check RH limits and warn user if out of bounds (T_* not modified in subroutine)
2523 :
2524 0 : this->CheckModelBoundsRH_TempEq(state, T_RegenInTemp, T_RegenInHumRat, T_ProcInTemp, T_ProcInHumRat, FirstHVACIteration);
2525 : // Check model boundaries and cap empirical model independent variables as needed (T_* may be modified on return from sub)
2526 0 : this->CheckModelBoundsTempEq(state, T_RegenInTemp, T_RegenInHumRat, T_ProcInTemp, T_ProcInHumRat, T_FaceVel, FirstHVACIteration);
2527 :
2528 0 : if (T_ProcInTemp != 0.0 && T_RegenInTemp != 0.0) {
2529 0 : FullLoadSupOutTemp = perf.B[0] + perf.B[1] * T_RegenInHumRat + perf.B[2] * T_RegenInTemp +
2530 0 : perf.B[3] * (T_RegenInHumRat / T_RegenInTemp) + perf.B[4] * T_ProcInHumRat + perf.B[5] * T_ProcInTemp +
2531 0 : perf.B[6] * (T_ProcInHumRat / T_ProcInTemp) + perf.B[7] * T_FaceVel;
2532 :
2533 : // Check model boundary for supply (regen) temp and do not cap value if out of bounds, check that supply in temp > out temp
2534 0 : this->CheckModelBoundOutput_Temp(state, this->SupInTemp, FullLoadSupOutTemp, FirstHVACIteration);
2535 0 : FullLoadDeltaT = FullLoadSupOutTemp - this->SupInTemp;
2536 : } else {
2537 0 : FullLoadDeltaT = 0.0;
2538 : }
2539 :
2540 0 : H_ProcInTemp = state.dataHeatRecovery->FullLoadOutAirTemp;
2541 0 : H_ProcInHumRat = state.dataHeatRecovery->FullLoadOutAirHumRat;
2542 0 : H_RegenInTemp = this->SupInTemp;
2543 0 : H_RegenInHumRat = this->SupInHumRat;
2544 0 : H_FaceVel = BalFaceVelActual;
2545 :
2546 : // Call model check routines only when HX is active, if coil is off these checks do not apply (no potential for heat transfer)
2547 : // Check RH limits and warn user if out of bounds (H_* not modified in subroutine)
2548 :
2549 0 : this->CheckModelBoundsRH_HumRatEq(state, H_RegenInTemp, H_RegenInHumRat, H_ProcInTemp, H_ProcInHumRat, FirstHVACIteration);
2550 : // Check model boundaries and cap empirical model independent variables as needed (H_* may be modified on return from sub)
2551 0 : this->CheckModelBoundsHumRatEq(state, H_RegenInTemp, H_RegenInHumRat, H_ProcInTemp, H_ProcInHumRat, H_FaceVel, FirstHVACIteration);
2552 :
2553 : // Calc curve
2554 0 : if (H_ProcInTemp != 0.0 && H_RegenInTemp != 0.0) {
2555 0 : FullLoadSupOutHumRat = perf.C[0] + perf.C[1] * H_RegenInHumRat + perf.C[2] * H_RegenInTemp +
2556 0 : perf.C[3] * (H_RegenInHumRat / H_RegenInTemp) + perf.C[4] * H_ProcInHumRat + perf.C[5] * H_ProcInTemp +
2557 0 : perf.C[6] * (H_ProcInHumRat / H_ProcInTemp) + perf.C[7] * H_FaceVel;
2558 :
2559 : // Check model boundary for supply (regen) hum rat and do not cap value if out of bounds, check that supply in HR < out HR
2560 0 : this->CheckModelBoundOutput_HumRat(state, this->SupInHumRat, FullLoadSupOutHumRat, FirstHVACIteration);
2561 0 : FullLoadDeltaW = FullLoadSupOutHumRat - this->SupInHumRat;
2562 : } else {
2563 0 : FullLoadDeltaW = 0.0;
2564 : }
2565 :
2566 : // Check for saturation in the model's calculated supply outlet and reset temp, then humidity ratio at constant enthalpy
2567 : // Reset delta T and delta W such that the model does not allow an outlet condition over saturation
2568 0 : TestSaturationEnthalpy = Psychrometrics::PsyHFnTdbW(FullLoadSupOutTemp, FullLoadSupOutHumRat);
2569 0 : if (Psychrometrics::PsyTsatFnHPb(state, TestSaturationEnthalpy, state.dataEnvrn->OutBaroPress, ThisSubTSat) > FullLoadSupOutTemp) {
2570 0 : constexpr std::string_view ThisSubTSatFullLoadOutTemp = "CalcDesiccantBalancedHeatExch: TSat-FullLoadOutTemp";
2571 0 : constexpr std::string_view ThisSubTSatFullLoadOutHumRat = "CalcDesiccantBalancedHeatExch: TSat-FullLoadOutHumRat";
2572 0 : FullLoadSupOutTemp =
2573 0 : Psychrometrics::PsyTsatFnHPb(state, TestSaturationEnthalpy, state.dataEnvrn->OutBaroPress, ThisSubTSatFullLoadOutTemp);
2574 0 : FullLoadSupOutHumRat = Psychrometrics::PsyWFnTdbH(state, FullLoadSupOutTemp, TestSaturationEnthalpy, ThisSubTSatFullLoadOutHumRat);
2575 0 : FullLoadDeltaT = FullLoadSupOutTemp - this->SupInTemp;
2576 0 : FullLoadDeltaW = FullLoadSupOutHumRat - this->SupInHumRat;
2577 : }
2578 :
2579 0 : if (!state.dataHeatRecovery->CalledFromParentObject) {
2580 : // calculate part-load ratio for HX
2581 0 : MaxHumRatNeeded = state.dataLoopNodes->Node(this->SecOutletNode).HumRatMax;
2582 0 : MinHumRatNeeded = state.dataLoopNodes->Node(this->SecOutletNode).HumRatMin;
2583 : // Calculate partload fraction of dehumidification capacity required to meet setpoint
2584 :
2585 : // check the model output, if the regen delta W is positive, the process air stream is dehumidified
2586 0 : if (FullLoadDeltaW > 0) {
2587 : // check for a setpoint, if no setpoint then PLR remains at 1
2588 0 : if (MaxHumRatNeeded != SensedNodeFlagValue) {
2589 0 : if (this->SecInHumRat > MaxHumRatNeeded && MaxHumRatNeeded > 0.0) {
2590 0 : HXPartLoadRatio = (this->SecInHumRat - MaxHumRatNeeded) / FullLoadDeltaW;
2591 : } else {
2592 0 : HXPartLoadRatio = 0.0;
2593 : }
2594 : }
2595 : // check the model output, if the regen delta W is negative, the process air stream is humidified
2596 0 : } else if (FullLoadDeltaW < 0) {
2597 : // check for a setpoint, if no setpoint then PLR remains at 1
2598 0 : if (MinHumRatNeeded != SensedNodeFlagValue) {
2599 0 : if (this->SecInHumRat < MinHumRatNeeded && MinHumRatNeeded > 0.0) {
2600 0 : HXPartLoadRatio = (this->SecInHumRat - MinHumRatNeeded) / FullLoadDeltaW;
2601 : } else {
2602 0 : HXPartLoadRatio = 0.0;
2603 : }
2604 : }
2605 : }
2606 :
2607 0 : HXPartLoadRatio = max(0.0, HXPartLoadRatio);
2608 0 : HXPartLoadRatio = min(1.0, HXPartLoadRatio);
2609 :
2610 0 : } else if (CompanionCoilType > 0 && CompanionCoilIndex > -1) {
2611 0 : if (CompanionCoilType == HVAC::CoilDX_Cooling) {
2612 0 : HXPartLoadRatio = state.dataCoilCoolingDX->coilCoolingDXs[CompanionCoilIndex].partLoadRatioReport;
2613 0 : } else if (CompanionCoilType == HVAC::Coil_CoolingAirToAirVariableSpeed) {
2614 0 : HXPartLoadRatio = state.dataVariableSpeedCoils->VarSpeedCoil(CompanionCoilIndex).PartLoadRatio;
2615 : } else {
2616 0 : HXPartLoadRatio = state.dataDXCoils->DXCoilPartLoadRatio(CompanionCoilIndex);
2617 : }
2618 : }
2619 :
2620 0 : Real64 constexpr lowerLimit = 1.e-5;
2621 0 : if (fanOp == HVAC::FanOp::Cycling || RegenInletIsOANode) {
2622 : // Supply (regen) air stream mass flow rate is cycling and proportional to PLR, outlet conditions are full load
2623 : // conditions
2624 0 : this->SupOutTemp = this->SupInTemp + FullLoadDeltaT;
2625 0 : this->SupOutHumRat = min(1.0, max(lowerLimit, this->SupInHumRat + FullLoadDeltaW));
2626 : } else {
2627 : // Supply (regen) air stream mass flow rate is constant and outlet conditions are averaged
2628 0 : this->SupOutTemp = this->SupInTemp + (FullLoadDeltaT * HXPartLoadRatio);
2629 0 : this->SupOutHumRat = min(1.0, max(lowerLimit, this->SupInHumRat + (FullLoadDeltaW * HXPartLoadRatio)));
2630 : }
2631 :
2632 : // for a balanced flow HX, use average mass flow rate and actual node conditions to calculate CSup and CSec
2633 : // the mass flow rate on the process and secondary side of HX may be imbalanced when the HX is used in the OA branch
2634 : // use the average mass flow rate to avoid psych warnings, mass flow rates will converge at the end of the iteration
2635 : // if the air mass flow rates do not converge, this model should not be used
2636 0 : CSup = AverageMassFlowRate * Psychrometrics::PsyCpAirFnW(this->SupInHumRat);
2637 0 : CSec = AverageMassFlowRate * Psychrometrics::PsyCpAirFnW(this->SecInHumRat);
2638 :
2639 0 : this->SupOutEnth = Psychrometrics::PsyHFnTdbW(this->SupOutTemp, this->SupOutHumRat);
2640 :
2641 0 : SensHeatRecRate = CSup * (this->SupOutTemp - this->SupInTemp);
2642 :
2643 0 : TotHeatRecRate = AverageMassFlowRate * (this->SupOutEnth - this->SupInEnth);
2644 :
2645 : // now calculate process side heat transfer
2646 :
2647 0 : this->SecOutEnth = this->SecInEnth - TotHeatRecRate / AverageMassFlowRate;
2648 :
2649 0 : this->SecOutTemp = this->SecInTemp - SensHeatRecRate / CSec;
2650 :
2651 0 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth, ThisSubSecOutHumRat);
2652 :
2653 : // check for saturation in process (secondary) outlet
2654 : // The process outlet conditions should never be over the saturation curve for the balanced desiccant model
2655 : // although this may occur during warmup. This check is included here for consistency.
2656 0 : TempSecOutSat = Psychrometrics::PsyTsatFnHPb(state, this->SecOutEnth, state.dataEnvrn->OutBaroPress, ThisSubTestSatSec);
2657 0 : if (TempSecOutSat > this->SecOutTemp) {
2658 0 : constexpr std::string_view ThisSubTSatSecOutHumRat = "CalcDesiccantBalancedHeatExch: TSat-SecOutHumRat";
2659 0 : this->SecOutTemp = TempSecOutSat;
2660 0 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth, ThisSubTSatSecOutHumRat);
2661 : }
2662 :
2663 0 : this->ElecUseRate = perf.NomElecPower * HXPartLoadRatio;
2664 :
2665 : } // ENDIF for "IF (UnitOn) THEN"
2666 :
2667 : // Report the process side heat transfer
2668 4 : CSec = AverageMassFlowRate * Psychrometrics::PsyCpAirFnW(this->SecInHumRat);
2669 4 : ProcessSensHeatRecRate = CSec * (this->SecOutTemp - this->SecInTemp);
2670 :
2671 4 : ProcessTotHeatRecRate = this->SecOutMassFlow * (this->SecOutEnth - this->SecInEnth);
2672 :
2673 4 : ProcessLatHeatRecRate = ProcessTotHeatRecRate - ProcessSensHeatRecRate;
2674 :
2675 : // Set report variables based on sign of recovery rate
2676 4 : if (ProcessSensHeatRecRate > 0.0) {
2677 0 : this->SensHeatingRate = ProcessSensHeatRecRate;
2678 0 : this->SensCoolingRate = 0.0;
2679 : } else {
2680 4 : this->SensHeatingRate = 0.0;
2681 4 : this->SensCoolingRate = std::abs(ProcessSensHeatRecRate);
2682 : }
2683 4 : if (ProcessLatHeatRecRate > 0.0) {
2684 0 : this->LatHeatingRate = ProcessLatHeatRecRate;
2685 0 : this->LatCoolingRate = 0.0;
2686 : } else {
2687 4 : this->LatHeatingRate = 0.0;
2688 4 : this->LatCoolingRate = std::abs(ProcessLatHeatRecRate);
2689 : }
2690 4 : if (ProcessTotHeatRecRate > 0.0) {
2691 0 : this->TotHeatingRate = ProcessTotHeatRecRate;
2692 0 : this->TotCoolingRate = 0.0;
2693 : } else {
2694 4 : this->TotHeatingRate = 0.0;
2695 4 : this->TotCoolingRate = std::abs(ProcessTotHeatRecRate);
2696 : }
2697 4 : }
2698 :
2699 4376 : void HeatExchCond::FrostControl(EnergyPlusData &state)
2700 : {
2701 :
2702 : // SUBROUTINE INFORMATION:
2703 : // AUTHOR Richard Raustad, FSEC
2704 : // DATE WRITTEN June 2003
2705 : // MODIFIED na
2706 : // RE-ENGINEERED na
2707 :
2708 : // PURPOSE OF THIS SUBROUTINE:
2709 : // Calculates fraction of timestep necessary to eliminate frost on ERV surface
2710 : // by comparing secondary outlet or outdoor temperature to a frost control threshold
2711 : // temperature. Supply air and secondary air outlet conditions are calculated
2712 : // based on frost control method selected.
2713 :
2714 : // SUBROUTINE PARAMETER DEFINITIONS:
2715 4376 : Real64 constexpr ErrorTol(0.001); // error tolerance for iteration loop
2716 :
2717 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2718 4376 : Real64 DFFraction = 0.0; // fraction of timestep ERV is in frost control mode
2719 : Real64 Error; // iteration loop error variable
2720 : Real64 Iter; // iteration counter
2721 : Real64 QTotTrans; // total heat transfer by ERV [W]
2722 : Real64 QSensTrans; // sensible heat transfer by ERV [W]
2723 : Real64 HXSecAirVolFlowRate; // air volume flow rate of the secondary air stream through the heat exchanger [m3/sec]
2724 : Real64 HXSupAirVolFlowRate; // air volume flow rate of the supply air stream through the heat exchanger [m3/sec]
2725 : Real64 HXAvgAirVolFlowRate; // average air volume flow rate through the heat exchanger [m3/sec]
2726 : Real64 HXAirVolFlowRatio; // nominal to actual air volume flow ratio
2727 : Real64 MassFlowSupIn; // supply air mass flow rate at HX inlet
2728 : Real64 MassFlowSupOut; // supply air mass flow rate through HX core outlet
2729 : Real64 MassFlowSupBypass; // supply air bypass mass flow rate around HX core
2730 : Real64 TempSupIn; // supply side temperature of air entering HX
2731 : Real64 TempSupOut; // supply side temperature of air leaving HX core
2732 : Real64 HumRatSupIn; // supply side humidity ratio of air entering HX
2733 : Real64 TempSecIn; // secondary side temperature of air entering HX
2734 : Real64 TempSecOut; // secondary side temperature of air leaving HX core
2735 :
2736 4376 : this->SupOutMassFlow = this->SupInMassFlow;
2737 4376 : this->SecOutMassFlow = this->SecInMassFlow;
2738 4376 : this->SupBypassMassFlow = 0.0;
2739 4376 : this->SecBypassMassFlow = 0.0;
2740 : // density of supply air [kg/m3]
2741 4376 : Real64 const RhoSup = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->SupInTemp, this->SupInHumRat);
2742 : // density of secondary air [kg/m3]
2743 4376 : Real64 const RhoSec = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, this->SecInTemp, this->SecInHumRat);
2744 : // mdot Cp of supply air [W/K]
2745 4376 : Real64 CSup = this->SupOutMassFlow * Psychrometrics::PsyCpAirFnW(this->SupInHumRat);
2746 : // mdot Cp of secondary air [W/K]
2747 4376 : Real64 const CSec = this->SecOutMassFlow * Psychrometrics::PsyCpAirFnW(this->SecInHumRat);
2748 : // minimum mdot Cp of supply or secondary air [W/K]
2749 4376 : Real64 CMin = min(CSup, CSec);
2750 : // threshold temperature below which frost control is active
2751 4376 : Real64 const TempThreshold = this->ThresholdTemperature;
2752 :
2753 4376 : if (this->ControlToTemperatureSetPoint) {
2754 : // Recalculate HX outlet conditions as if control to temperature setpoint was not activated,
2755 : // because defrost will override those results
2756 :
2757 2336 : HXSupAirVolFlowRate = this->SupOutMassFlow / RhoSup;
2758 2336 : HXSecAirVolFlowRate = this->SecOutMassFlow / RhoSec;
2759 2336 : HXAvgAirVolFlowRate = (HXSecAirVolFlowRate + HXSupAirVolFlowRate) / 2.0;
2760 2336 : HXAirVolFlowRatio = HXAvgAirVolFlowRate / this->NomSupAirVolFlow;
2761 2336 : this->SensEffectiveness = this->HeatEffectSensible100;
2762 2336 : if (this->HeatEffectSensibleCurveIndex > 0) {
2763 2336 : this->SensEffectiveness *= Curve::CurveValue(state, this->HeatEffectSensibleCurveIndex, HXAirVolFlowRatio);
2764 : }
2765 2336 : this->LatEffectiveness = this->HeatEffectLatent100;
2766 2336 : if (this->HeatEffectLatentCurveIndex > 0) {
2767 2336 : this->LatEffectiveness *= Curve::CurveValue(state, this->HeatEffectLatentCurveIndex, HXAirVolFlowRatio);
2768 : }
2769 2336 : this->SupOutTemp = this->SupInTemp + this->SensEffectiveness * CMin / CSup * (this->SecInTemp - this->SupInTemp);
2770 2336 : this->SupOutHumRat = this->SupInHumRat + this->LatEffectiveness * CMin / CSup * (this->SecInHumRat - this->SupInHumRat);
2771 2336 : this->SupOutEnth = Psychrometrics::PsyHFnTdbW(this->SupOutTemp, this->SupOutHumRat);
2772 :
2773 : // Check for saturation in supply outlet and reset temp, then humidity ratio at constant enthalpy
2774 2336 : if (Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress) > this->SupOutTemp) {
2775 0 : this->SupOutTemp = Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress);
2776 0 : this->SupOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SupOutTemp, this->SupOutEnth);
2777 : }
2778 :
2779 2336 : QSensTrans = CSup * (this->SupInTemp - this->SupOutTemp);
2780 2336 : this->SecOutTemp = this->SecInTemp + QSensTrans / CSec;
2781 2336 : QTotTrans = this->SupOutMassFlow * (this->SupInEnth - this->SupOutEnth);
2782 2336 : this->SecOutEnth = this->SecInEnth + QTotTrans / this->SecOutMassFlow;
2783 2336 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth);
2784 : }
2785 :
2786 : // Check frost control by type
2787 :
2788 4376 : switch (this->FrostControlType) {
2789 4376 : case FrostControlOption::MinimumExhaustTemperature:
2790 : // A plate HX will bypass air on the supply side to keep exhaust temp above a
2791 : // threshold temperature and requires recalculating effectiveness based on
2792 : // the reduced air flow rate. A rotary HX modulates rotational speed to try to keep the
2793 : // exhaust air temperature above the threshold temperature. Assume that
2794 : // sensible and latent effectiveness decrease proportionally with rotary HX speed.
2795 :
2796 4376 : DFFraction = max(0.0, min(1.0, SafeDiv((TempThreshold - this->SecOutTemp), (this->SecInTemp - this->SecOutTemp))));
2797 4376 : if (this->ExchConfig == HXConfigurationType::Rotary) {
2798 0 : this->SensEffectiveness *= (1.0 - DFFraction);
2799 0 : this->LatEffectiveness *= (1.0 - DFFraction);
2800 : } else { // HX is a plate heat exchanger, bypass air to eliminate frost
2801 4376 : Error = 1.0;
2802 4376 : Iter = 0.0;
2803 4376 : MassFlowSupIn = this->SupInMassFlow;
2804 4376 : MassFlowSupOut = this->SupOutMassFlow;
2805 4376 : MassFlowSupBypass = this->SupBypassMassFlow;
2806 4376 : TempSupIn = this->SupInTemp;
2807 4376 : HumRatSupIn = this->SupInHumRat;
2808 4376 : TempSecIn = this->SecInTemp;
2809 :
2810 48136 : while (std::abs(Error) > ErrorTol && Iter < 10) {
2811 43760 : MassFlowSupOut = MassFlowSupIn * (1.0 - DFFraction);
2812 43760 : MassFlowSupBypass = MassFlowSupIn * DFFraction;
2813 43760 : HXSupAirVolFlowRate = MassFlowSupOut / RhoSup;
2814 43760 : HXSecAirVolFlowRate = this->SecOutMassFlow / RhoSec;
2815 43760 : HXAvgAirVolFlowRate = (HXSecAirVolFlowRate + HXSupAirVolFlowRate) / 2.0;
2816 43760 : HXAirVolFlowRatio = HXAvgAirVolFlowRate / this->NomSupAirVolFlow;
2817 43760 : CSup = MassFlowSupOut * Psychrometrics::PsyCpAirFnW(HumRatSupIn);
2818 43760 : CMin = min(CSup, CSec);
2819 43760 : if (TempSupIn < TempSecIn) {
2820 : // Use heating effectiveness values
2821 43760 : this->SensEffectiveness = this->HeatEffectSensible100;
2822 43760 : if (this->HeatEffectSensibleCurveIndex > 0) {
2823 43760 : this->SensEffectiveness *= Curve::CurveValue(state, this->HeatEffectSensibleCurveIndex, HXAirVolFlowRatio);
2824 : }
2825 43760 : this->LatEffectiveness = this->HeatEffectLatent100;
2826 43760 : if (this->HeatEffectLatentCurveIndex > 0) {
2827 43760 : this->LatEffectiveness *= Curve::CurveValue(state, this->HeatEffectLatentCurveIndex, HXAirVolFlowRatio);
2828 : }
2829 : } else {
2830 : // Use cooling effectiveness values
2831 0 : this->SensEffectiveness = this->CoolEffectSensible100;
2832 0 : if (this->CoolEffectSensibleCurveIndex > 0) {
2833 0 : this->SensEffectiveness *= Curve::CurveValue(state, this->CoolEffectSensibleCurveIndex, HXAirVolFlowRatio);
2834 : }
2835 0 : this->LatEffectiveness = this->CoolEffectLatent100;
2836 0 : if (this->CoolEffectLatentCurveIndex > 0) {
2837 0 : this->LatEffectiveness *= Curve::CurveValue(state, this->CoolEffectLatentCurveIndex, HXAirVolFlowRatio);
2838 : }
2839 : }
2840 : // calculation of local variable Csup can be 0, guard against divide by 0.
2841 43760 : TempSupOut = TempSupIn + this->SensEffectiveness * SafeDiv(CMin, CSup) * (TempSecIn - TempSupIn);
2842 43760 : QSensTrans = CSup * (TempSupIn - TempSupOut);
2843 : // Csec cannot be 0 in this subroutine
2844 43760 : TempSecOut = TempSecIn + QSensTrans / CSec;
2845 43760 : Error = (TempSecOut - TempThreshold);
2846 : // recalculate DFFraction until convergence, guard against divide by 0 (unlikely).
2847 43760 : DFFraction = max(0.0, min(1.0, DFFraction * SafeDiv((TempSecIn - TempSecOut), (TempSecIn - TempThreshold))));
2848 43760 : ++Iter;
2849 : }
2850 4376 : this->SupInMassFlow = MassFlowSupIn;
2851 4376 : this->SupOutMassFlow = MassFlowSupOut;
2852 4376 : this->SupBypassMassFlow = MassFlowSupBypass;
2853 : }
2854 4376 : this->SupOutTemp = this->SupInTemp + this->SensEffectiveness * SafeDiv(CMin, CSup) * (this->SecInTemp - this->SupInTemp);
2855 4376 : this->SupOutHumRat = this->SupInHumRat + this->LatEffectiveness * SafeDiv(CMin, CSup) * (this->SecInHumRat - this->SupInHumRat);
2856 4376 : this->SupOutEnth = Psychrometrics::PsyHFnTdbW(this->SupOutTemp, this->SupOutHumRat);
2857 :
2858 : // Check for saturation in supply outlet and reset temp, then humidity ratio at constant enthalpy
2859 4376 : if (Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress) > this->SupOutTemp) {
2860 3 : this->SupOutTemp = Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress);
2861 3 : this->SupOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SupOutTemp, this->SupOutEnth);
2862 : }
2863 :
2864 4376 : QSensTrans = CSup * (this->SupInTemp - this->SupOutTemp);
2865 4376 : this->SecOutTemp = this->SecInTemp + QSensTrans / CSec;
2866 4376 : QTotTrans = this->SupOutMassFlow * (this->SupInEnth - this->SupOutEnth);
2867 4376 : this->SecOutEnth = this->SecInEnth + QTotTrans / this->SecOutMassFlow;
2868 4376 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth);
2869 :
2870 : // Perform mixing of core air stream and bypass air stream and set mass flow rates at outlet nodes
2871 4376 : this->SupOutEnth = (this->SupOutMassFlow * this->SupOutEnth + this->SupBypassMassFlow * this->SupInEnth) / this->SupInMassFlow;
2872 4376 : this->SupOutHumRat = (this->SupOutMassFlow * this->SupOutHumRat + this->SupBypassMassFlow * this->SupInHumRat) / this->SupInMassFlow;
2873 4376 : this->SupOutTemp = Psychrometrics::PsyTdbFnHW(this->SupOutEnth, this->SupOutHumRat);
2874 4376 : this->SupOutMassFlow = this->SupInMassFlow;
2875 4376 : this->SecOutEnth = (this->SecOutMassFlow * this->SecOutEnth + this->SecBypassMassFlow * this->SecInEnth) / this->SecInMassFlow;
2876 4376 : this->SecOutHumRat = (this->SecOutMassFlow * this->SecOutHumRat + this->SecBypassMassFlow * this->SecInHumRat) / this->SecInMassFlow;
2877 4376 : this->SecOutTemp = Psychrometrics::PsyTdbFnHW(this->SecOutEnth, this->SecOutHumRat);
2878 4376 : this->SecOutMassFlow = this->SecInMassFlow;
2879 4376 : break;
2880 :
2881 0 : case FrostControlOption::ExhaustAirRecirculation:
2882 : // Directing exhaust outlet air back across the HX core on the supply side
2883 : // Assume no heat exchange when in frost control mode, full heat exchange otherwise
2884 0 : DFFraction = max(0.0, min((this->InitialDefrostTime + this->RateofDefrostTimeIncrease * (TempThreshold - this->SupInTemp)), 1.0));
2885 :
2886 : // Calculate derated heat transfer using outlet air conditions assuming no defrost (calculated earlier)
2887 : // and (1-DefrostFraction)
2888 0 : QSensTrans = (1.0 - DFFraction) * CSup * (this->SupInTemp - this->SupOutTemp);
2889 0 : QTotTrans = (1.0 - DFFraction) * this->SupOutMassFlow * (this->SupInEnth - this->SupOutEnth);
2890 :
2891 0 : this->SupOutMassFlow = (1.0 - DFFraction) * this->SupInMassFlow + DFFraction * this->SecInMassFlow;
2892 :
2893 : // Blend supply outlet condition of HX core with exhaust air inlet to get final supply air outlet conditions
2894 0 : this->SupOutTemp = ((1.0 - DFFraction) * this->SupInMassFlow * this->SupOutTemp + DFFraction * this->SecInMassFlow * this->SecInTemp) /
2895 0 : this->SupOutMassFlow;
2896 :
2897 0 : this->SupOutHumRat =
2898 0 : ((1.0 - DFFraction) * this->SupInMassFlow * this->SupOutHumRat + DFFraction * this->SecInMassFlow * this->SecInHumRat) /
2899 0 : this->SupOutMassFlow;
2900 :
2901 0 : this->SupOutEnth = Psychrometrics::PsyHFnTdbW(this->SupOutTemp, this->SupOutHumRat);
2902 : // No need to check for saturation after SA out and EA inlet are blended
2903 :
2904 : // Derate effectiveness based on frost control time fraction for reporting purposes
2905 0 : this->SensEffectiveness *= (1.0 - DFFraction);
2906 0 : this->LatEffectiveness *= (1.0 - DFFraction);
2907 :
2908 : // Secondary air outlet conditions are previously calculated as the conditions when not
2909 : // in defrost, and this is what we want to report so no changes here.
2910 : // Average SupInMassFlow and SecOutMassFlow rates have been reduced due to frost control
2911 : // Equipment attached to the supply inlet node may have problems with our setting the
2912 : // mass flow rate in the next statement. This is done only to simulate exhaust air recirc.
2913 0 : state.dataLoopNodes->Node(this->SupInletNode).MassFlowRate = this->SupInMassFlow * (1.0 - DFFraction);
2914 0 : this->SecOutMassFlow *= (1.0 - DFFraction);
2915 0 : break;
2916 :
2917 0 : case FrostControlOption::ExhaustOnly:
2918 :
2919 : // Perform frost control by bypassing the supply air around the HX core during the defrost
2920 : // time period. HX heat transfer is reduced proportionally to (1 - defrosttimefraction)
2921 :
2922 0 : DFFraction = max(0.0, min((this->InitialDefrostTime + this->RateofDefrostTimeIncrease * (TempThreshold - this->SupInTemp)), 1.0));
2923 :
2924 : // Calculate derated heat transfer based on defrost time
2925 0 : QSensTrans = (1.0 - DFFraction) * CSup * (this->SupInTemp - this->SupOutTemp);
2926 0 : QTotTrans = (1.0 - DFFraction) * this->SupOutMassFlow * (this->SupInEnth - this->SupOutEnth);
2927 :
2928 : // Calculate the air conditions leaving heat exchanger unit
2929 : // Heat exchanger effectiveness is not derated, HX is fully bypassed during frost control
2930 :
2931 0 : this->SupBypassMassFlow = this->SupInMassFlow * DFFraction;
2932 0 : this->SupOutTemp = this->SupInTemp - QSensTrans / CSup;
2933 0 : this->SupOutEnth = this->SupInEnth - QTotTrans / this->SupOutMassFlow;
2934 0 : this->SupOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SupOutTemp, this->SupOutEnth);
2935 :
2936 0 : if (Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress) > this->SupOutTemp) {
2937 0 : this->SupOutTemp = Psychrometrics::PsyTsatFnHPb(state, this->SupOutEnth, state.dataEnvrn->OutBaroPress);
2938 0 : this->SupOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SupOutTemp, this->SupOutEnth);
2939 0 : QSensTrans = CSup * (this->SupInTemp - this->SupOutTemp);
2940 : // Should we be updating the sensible and latent effectiveness values also?
2941 : }
2942 :
2943 0 : this->SecOutEnth = this->SecInEnth + QTotTrans / this->SecOutMassFlow;
2944 0 : this->SecOutTemp = this->SecInTemp + QSensTrans / CSec;
2945 0 : this->SecOutHumRat = Psychrometrics::PsyWFnTdbH(state, this->SecOutTemp, this->SecOutEnth);
2946 0 : break;
2947 0 : default:
2948 0 : break; // :: None means don't do anything here, and ::Invalid is caught on GetInput
2949 : }
2950 :
2951 4376 : this->DefrostFraction = DFFraction;
2952 4376 : }
2953 :
2954 26582 : void HeatExchCond::UpdateHeatRecovery(EnergyPlusData &state)
2955 : {
2956 :
2957 : // SUBROUTINE INFORMATION:
2958 : // AUTHOR Michael Wetter
2959 : // DATE WRITTEN March 1999
2960 : // MODIFIED Fred Buhl November 2000
2961 : // RE-ENGINEERED na
2962 :
2963 : // PURPOSE OF THIS SUBROUTINE:
2964 : // Moves heat exchanger output to the outlet nodes.
2965 :
2966 26582 : int const SupInNode = this->SupInletNode;
2967 26582 : int const SupOutNode = this->SupOutletNode;
2968 26582 : int const SecInNode = this->SecInletNode;
2969 26582 : int const SecOutNode = this->SecOutletNode;
2970 :
2971 26582 : auto &thisSupInNode = state.dataLoopNodes->Node(SupInNode);
2972 26582 : auto &thisSupOutNode = state.dataLoopNodes->Node(SupOutNode);
2973 26582 : auto &thisSecInNode = state.dataLoopNodes->Node(SecInNode);
2974 26582 : auto &thisSecOutNode = state.dataLoopNodes->Node(SecOutNode);
2975 :
2976 : // Set the outlet air nodes of the heat exchanger
2977 26582 : thisSupOutNode.Temp = this->SupOutTemp;
2978 26582 : thisSupOutNode.HumRat = this->SupOutHumRat;
2979 26582 : thisSupOutNode.Enthalpy = this->SupOutEnth;
2980 26582 : thisSupOutNode.MassFlowRate = this->SupOutMassFlow;
2981 26582 : thisSecOutNode.Temp = this->SecOutTemp;
2982 26582 : thisSecOutNode.HumRat = this->SecOutHumRat;
2983 26582 : thisSecOutNode.Enthalpy = this->SecOutEnth;
2984 26582 : thisSecOutNode.MassFlowRate = this->SecOutMassFlow;
2985 :
2986 : // Set the outlet nodes for properties that just pass through & not used
2987 26582 : thisSupOutNode.Quality = thisSupInNode.Quality;
2988 26582 : thisSupOutNode.Press = thisSupInNode.Press;
2989 26582 : thisSupOutNode.MassFlowRateMin = thisSupInNode.MassFlowRateMin;
2990 26582 : thisSupOutNode.MassFlowRateMax = thisSupInNode.MassFlowRateMax;
2991 26582 : thisSupOutNode.MassFlowRateMinAvail = thisSupInNode.MassFlowRateMinAvail;
2992 26582 : thisSupOutNode.MassFlowRateMaxAvail = thisSupInNode.MassFlowRateMaxAvail;
2993 26582 : thisSecOutNode.Quality = thisSecInNode.Quality;
2994 26582 : thisSecOutNode.Press = thisSecInNode.Press;
2995 26582 : thisSecOutNode.MassFlowRateMin = thisSecInNode.MassFlowRateMin;
2996 26582 : thisSecOutNode.MassFlowRateMax = thisSecInNode.MassFlowRateMax;
2997 26582 : thisSecOutNode.MassFlowRateMinAvail = thisSecInNode.MassFlowRateMinAvail;
2998 26582 : thisSecOutNode.MassFlowRateMaxAvail = thisSecInNode.MassFlowRateMaxAvail;
2999 :
3000 26582 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
3001 0 : thisSupOutNode.CO2 = thisSupInNode.CO2;
3002 0 : thisSecOutNode.CO2 = thisSecInNode.CO2;
3003 : }
3004 26582 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
3005 0 : thisSupOutNode.GenContam = thisSupInNode.GenContam;
3006 0 : thisSecOutNode.GenContam = thisSecInNode.GenContam;
3007 : }
3008 26582 : }
3009 :
3010 26569 : void HeatExchCond::ReportHeatRecovery(EnergyPlusData &state)
3011 : {
3012 :
3013 : // SUBROUTINE INFORMATION:
3014 : // AUTHOR Michael Wetter
3015 : // DATE WRITTEN March 1999
3016 : // MODIFIED F Buhl Nov 2000, D Shirey Feb/June 2003
3017 : // RE-ENGINEERED na
3018 :
3019 : // PURPOSE OF THIS SUBROUTINE:
3020 : // Fill remaining report variables
3021 :
3022 26569 : Real64 const TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
3023 26569 : this->ElecUseEnergy = this->ElecUseRate * TimeStepSysSec;
3024 26569 : this->SensHeatingEnergy = this->SensHeatingRate * TimeStepSysSec;
3025 26569 : this->LatHeatingEnergy = this->LatHeatingRate * TimeStepSysSec;
3026 26569 : this->TotHeatingEnergy = this->TotHeatingRate * TimeStepSysSec;
3027 26569 : this->SensCoolingEnergy = this->SensCoolingRate * TimeStepSysSec;
3028 26569 : this->LatCoolingEnergy = this->LatCoolingRate * TimeStepSysSec;
3029 26569 : this->TotCoolingEnergy = this->TotCoolingRate * TimeStepSysSec;
3030 :
3031 26569 : state.dataHVACGlobal->AirToAirHXElecPower = this->ElecUseRate;
3032 26569 : }
3033 :
3034 100674 : Real64 SafeDiv(Real64 const a, Real64 const b)
3035 : {
3036 :
3037 : // SUBROUTINE INFORMATION:
3038 : // AUTHOR Michael Wetter
3039 : // DATE WRITTEN March 1999
3040 : // MODIFIED na
3041 : // RE-ENGINEERED na
3042 :
3043 : // PURPOSE OF THIS FUNCTION:
3044 : // Returns a / b while preventing division by zero
3045 :
3046 : // METHODOLOGY EMPLOYED:
3047 : // Check for small or zero values before performing division
3048 :
3049 100674 : if (std::abs(b) < SMALL) {
3050 2 : return a / sign(SMALL, b);
3051 : } else {
3052 100672 : return a / b;
3053 : }
3054 : }
3055 :
3056 12 : Real64 CalculateEpsFromNTUandZ(EnergyPlusData &state,
3057 : Real64 const NTU, // number of transfer units
3058 : Real64 const Z, // capacity rate ratio
3059 : HXConfiguration const FlowArr // flow arrangement
3060 : )
3061 : {
3062 :
3063 : // SUBROUTINE INFORMATION:
3064 : // AUTHOR Michael Wetter
3065 : // DATE WRITTEN March 1999
3066 : // MODIFIED Fred Buhl November 2000
3067 : // RE-ENGINEERED na
3068 :
3069 : // PURPOSE OF THIS SUBROUTINE:
3070 : // Calculates eps, the exchanger effectiveness,
3071 : // from NTU, the number of transfer units,
3072 : // from Z, the capacity rate ratio, and
3073 : // from the flow arrangement
3074 :
3075 : // METHODOLOGY EMPLOYED:
3076 : // Uses the effectiveness - NTU heat exchanger formulas
3077 :
3078 : // REFERENCES:
3079 : // M. Wetter, Simulation Model Air-to-Air Plate Heat Exchanger
3080 : // LBNL Report 42354, 1999.
3081 : // Also see:
3082 : // ASHRAE HVAC 2 Toolkit, pages 4-3 through 4-5
3083 :
3084 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3085 : Real64 Temp; // temporary variable
3086 12 : Real64 Eps = 0.0; // heat exchanger effectiveness, return value
3087 :
3088 : // check input validity
3089 12 : if (Z < 0.0 || Z > 1.0) {
3090 0 : ShowFatalError(state, format("Variable Z ({:.2R}) out of range [0.0,1.0] in CalculateEpsFromNTUandZ", Z));
3091 : }
3092 :
3093 : // effectiveness
3094 12 : if (NTU < SMALL) {
3095 0 : Eps = 0.0;
3096 12 : } else if (Z < SMALL) { // Eps independent of flow arrangement
3097 0 : Eps = 1.0 - std::exp(-NTU);
3098 : } else {
3099 12 : switch (FlowArr) {
3100 0 : case HXConfiguration::CounterFlow: { // COUNTER FLOW
3101 0 : if (std::abs(Z - 1.0) < SMALL) {
3102 0 : Eps = NTU / (NTU + 1.0);
3103 : } else {
3104 0 : Temp = std::exp(-NTU * (1.0 - Z));
3105 0 : Eps = (1.0 - Temp) / (1.0 - Z * Temp);
3106 : }
3107 0 : } break;
3108 12 : case HXConfiguration::ParallelFlow: { // PARALLEL FLOW
3109 12 : Temp = (1.0 + Z);
3110 12 : Eps = (1.0 - std::exp(-NTU * Temp)) / Temp;
3111 12 : } break;
3112 0 : case HXConfiguration::CrossFlowBothUnmixed: { // CROSS FLOW BOTH UNMIXED
3113 0 : Temp = Z * std::pow(NTU, -0.22);
3114 0 : Eps = 1.0 - std::exp(std::expm1(-NTU * Temp) / Temp);
3115 0 : } break;
3116 0 : case HXConfiguration::CrossFlowOther: { // CROSS FLOW, Cmax MIXED, Cmin UNMIXED
3117 0 : Eps = (1.0 - std::exp(-Z * (1.0 - std::exp(-NTU)))) / Z;
3118 0 : } break;
3119 0 : default: {
3120 0 : ShowFatalError(state, format("HeatRecovery: Illegal flow arrangement in CalculateEpsFromNTUandZ, Value={}", FlowArr));
3121 0 : } break;
3122 : }
3123 : }
3124 12 : return Eps;
3125 : }
3126 :
3127 2 : void CalculateNTUfromEpsAndZ(EnergyPlusData &state,
3128 : Real64 &NTU, // number of transfer units
3129 : CalculateNTUBoundsErrors &Err, // error indicator
3130 : Real64 const Z, // capacity rate ratio
3131 : HXConfiguration const FlowArr, // flow arrangement
3132 : Real64 const Eps // heat exchanger effectiveness
3133 : )
3134 : {
3135 :
3136 : // SUBROUTINE INFORMATION:
3137 : // AUTHOR Michael Wetter
3138 : // DATE WRITTEN March 1999
3139 : // MODIFIED Fred Buhl November 2000
3140 : // RE-ENGINEERED na
3141 :
3142 : // PURPOSE OF THIS SUBROUTINE:
3143 : // Calculates NTU, the number of transfer units,
3144 : // based on eps, the exchanger effectiveness,
3145 : // Z, the capacity rate ratio, and
3146 : // from the flow arrangement
3147 :
3148 : // METHODOLOGY EMPLOYED:
3149 : // Uses the effectiveness - NTU heat exchanger formulas
3150 :
3151 : // REFERENCES:
3152 : // M. Wetter, Simulation Model Air-to-Air Plate Heat Exchanger
3153 : // LBNL Report 42354, 1999.
3154 : // Also see:
3155 : // ASHRAE HVAC 2 Toolkit, pages 4-3 through 4-5
3156 :
3157 2 : NTU = 0.0;
3158 : // check input validity
3159 2 : if (Z < 0.0 || Z > 1.0) {
3160 0 : Err = CalculateNTUBoundsErrors::MassFlowRatio;
3161 0 : return;
3162 : }
3163 :
3164 2 : if (FlowArr == HXConfiguration::ParallelFlow) {
3165 2 : if (Eps < 0.0 || Eps > 1.0 / (1.0 + Z)) {
3166 0 : Err = CalculateNTUBoundsErrors::NominalEffectiveness1;
3167 0 : return;
3168 : }
3169 0 : } else if (FlowArr == HXConfiguration::CrossFlowOther) {
3170 0 : if (Eps < 0.0 || Eps > (1.0 - std::exp(-Z)) / Z) {
3171 0 : Err = CalculateNTUBoundsErrors::NominalEffectiveness2;
3172 0 : return;
3173 : }
3174 : // check product (Eps*Z)
3175 0 : if (Eps * Z < 0.0 || Eps * Z > 1.0 - std::exp(Z * (SMALL - 1.0))) {
3176 0 : Err = CalculateNTUBoundsErrors::Quantity;
3177 0 : return;
3178 : }
3179 : // check product (Eps*Z)
3180 : } else {
3181 0 : if (Eps < 0.0 || Eps > 1.0) {
3182 0 : Err = CalculateNTUBoundsErrors::NominalEffectiveness3;
3183 0 : return;
3184 : }
3185 : }
3186 :
3187 2 : if (Eps < SMALL) { // no effectiveness. Set NTU = 0
3188 0 : NTU = 0.0;
3189 2 : } else if (Z < SMALL) { // Eps independent of flow arrangement
3190 0 : NTU = -std::log(1.0 - Eps);
3191 : } else {
3192 : // calculate based on configuration
3193 2 : switch (FlowArr) {
3194 0 : case HXConfiguration::CounterFlow: { // COUNTER FLOW
3195 0 : if (std::abs(Z - 1.0) < SMALL) {
3196 0 : NTU = Eps / (1.0 - Eps);
3197 : } else {
3198 0 : NTU = 1.0 / (Z - 1.0) * std::log((1.0 - Eps) / (1.0 - Eps * Z));
3199 : }
3200 0 : } break;
3201 2 : case HXConfiguration::ParallelFlow: { // PARALLEL FLOW
3202 2 : NTU = -std::log1p(-Eps - Eps * Z) / (Z + 1.0);
3203 2 : } break;
3204 0 : case HXConfiguration::CrossFlowBothUnmixed: { // CROSS FLOW BOTH UNMIXED
3205 0 : NTU = GetNTUforCrossFlowBothUnmixed(state, Eps, Z);
3206 0 : } break;
3207 0 : case HXConfiguration::CrossFlowOther: { // CROSS FLOW, Cmax MIXED, Cmin UNMIXED
3208 0 : NTU = -std::log1p(std::log(1.0 - Eps * Z) / Z);
3209 0 : } break;
3210 0 : default: {
3211 0 : ShowFatalError(state, format("HeatRecovery: Illegal flow arrangement in CalculateNTUfromEpsAndZ, Value={}", FlowArr));
3212 0 : } break;
3213 : }
3214 : }
3215 : }
3216 :
3217 0 : Real64 GetNTUforCrossFlowBothUnmixed(EnergyPlusData &state,
3218 : Real64 const Eps, // heat exchanger effectiveness
3219 : Real64 const Z // capacity rate ratio
3220 : )
3221 : {
3222 :
3223 : // FUNCTION INFORMATION:
3224 : // AUTHOR Michael Wetter
3225 : // DATE WRITTEN March 1999
3226 : // MODIFIED Fred Buhl November 2000
3227 : // RE-ENGINEERED na
3228 :
3229 : // PURPOSE OF THIS FUNCTION:
3230 : // Calculates the NTU value based on the exchanger effectiveness
3231 : // and the capacity ratio for cross flow exchanger, both
3232 : // streams unmixed
3233 :
3234 : // METHODOLOGY EMPLOYED:
3235 : // Uses a Regula Falsi solver function to numerically invert the formula
3236 : // giving effectiveness as a function of NTU and Z..
3237 :
3238 : // REFERENCES:
3239 : // M. Wetter, Simulation Model Air-to-Air Plate Heat Exchanger
3240 : // LBNL Report 42354, 1999.
3241 : // Also see:
3242 : // ASHRAE HVAC 2 Toolkit, pages 4-3 through 4-5
3243 :
3244 : // Return value
3245 : Real64 NTU; // result variable; number of transfer units
3246 :
3247 : // FUNCTION PARAMETER DEFINITIONS:
3248 0 : Real64 constexpr Acc(0.0001); // Accuracy of result
3249 0 : int constexpr MaxIte(500); // Maximum number of iterations
3250 :
3251 : int SolFla; // Flag of solver
3252 0 : Real64 constexpr NTU0(0.0); // lower bound for NTU
3253 0 : Real64 constexpr NTU1(50.0); // upper bound for NTU
3254 0 : auto f = [Eps, Z](Real64 const NTU) { return 1.0 - std::exp(std::expm1(-std::pow(NTU, 0.78) * Z) / Z * std::pow(NTU, 0.22)) - Eps; };
3255 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, NTU, f, NTU0, NTU1);
3256 :
3257 0 : if (SolFla == -2) {
3258 0 : ShowFatalError(state, "HeatRecovery: Bad initial bounds for NTU in GetNTUforCrossFlowBothUnmixed");
3259 0 : } else if (SolFla == -1) {
3260 0 : ShowFatalError(state, "HeatRecovery: No convergence in solving for NTU in GetNTUforCrossFlowBothUnmixed");
3261 : }
3262 :
3263 0 : return NTU;
3264 : }
3265 :
3266 0 : void HeatExchCond::CheckModelBoundsTempEq(EnergyPlusData &state,
3267 : Real64 &T_RegenInTemp, // current regen inlet temperature (C) for regen outlet temp eqn
3268 : Real64 &T_RegenInHumRat, // current regen inlet hum rat for regen outlet temp eqn
3269 : Real64 &T_ProcInTemp, // current process inlet temperature (C) for regen outlet temp eqn
3270 : Real64 &T_ProcInHumRat, // current process inlet hum rat for regen outlet temp eqn
3271 : Real64 &T_FaceVel, // current process and regen face velocity (m/s)
3272 : bool const FirstHVACIteration // First HVAC iteration flag
3273 : ) const
3274 : {
3275 :
3276 : // SUBROUTINE INFORMATION:
3277 : // AUTHOR Mangesh Basarkar, FSEC
3278 : // DATE WRITTEN January 2007
3279 : // MODIFIED na
3280 : // RE-ENGINEERED na
3281 :
3282 : // PURPOSE OF THIS SUBROUTINE:
3283 : // To verify that the empirical model's independent variables are within the limits used during the
3284 : // development of the empirical model.
3285 :
3286 : // METHODOLOGY EMPLOYED:
3287 : // The empirical models used for simulating a desiccant enhanced cooling coil are based on a limited data set.
3288 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
3289 : // The range of each independent variable is provided by the user and are based on the limits of the
3290 : // empirical model. These limits are tested in this subroutine each time step and returned for use by the calling
3291 : // routine.
3292 :
3293 : // Using/Aliasing
3294 : using General::CreateSysTimeIntervalString;
3295 :
3296 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3297 0 : auto &thisError = state.dataHeatRecovery->error1;
3298 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
3299 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
3300 :
3301 : // current end time is compared with last to see if time step changed
3302 :
3303 : // calculate end time of current time step
3304 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
3305 :
3306 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
3307 : // Wait for next time step to print warnings. If simulation iterates, print out
3308 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
3309 : // If a warning occurs and the simulation down shifts, the warning is not valid.
3310 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
3311 :
3312 : // print error for variables of regeneration outlet temperature equation
3313 : // Regen inlet temp for temp eqn
3314 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.print) {
3315 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.count;
3316 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.count < 2) {
3317 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.buffer1);
3318 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.buffer2);
3319 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.buffer3);
3320 0 : ShowContinueError(state,
3321 : "...Using regeneration inlet air temperatures that are outside the regeneration outlet air temperature "
3322 : "equation model boundaries may adversely affect desiccant model performance.");
3323 : } else {
3324 0 : ShowRecurringWarningErrorAtEnd(state,
3325 0 : format("{} \"{}\" - Regeneration inlet air temp used in regen outlet air temperature equation is "
3326 : "out of range error continues...",
3327 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3328 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3329 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.index,
3330 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.last,
3331 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.last);
3332 : }
3333 : }
3334 : // Regen inlet humidity ratio for temp eqn
3335 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.print) {
3336 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.count;
3337 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.count < 2) {
3338 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.buffer1);
3339 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.buffer2);
3340 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.buffer3);
3341 0 : ShowContinueError(state,
3342 : "...Using regeneration inlet air humidity ratios that are outside the regeneration outlet air temperature "
3343 : "equation model boundaries may adversely affect desiccant model performance.");
3344 : } else {
3345 0 : ShowRecurringWarningErrorAtEnd(state,
3346 0 : format("{} \"{}\" - Regeneration inlet air humidity ratio used in regen outlet temperature "
3347 : "equation is out of range error continues...",
3348 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3349 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3350 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.index,
3351 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.last,
3352 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.last);
3353 : }
3354 : }
3355 : // Process inlet temp for temp eqn
3356 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.print) {
3357 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.count;
3358 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.count < 2) {
3359 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.buffer1);
3360 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.buffer2);
3361 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.buffer3);
3362 0 : ShowContinueError(state,
3363 : "...Using process inlet air temperatures that are outside the regeneration outlet air temperature equation "
3364 : "model boundaries may adversely affect desiccant model performance.");
3365 : } else {
3366 0 : ShowRecurringWarningErrorAtEnd(
3367 : state,
3368 0 : format(
3369 : "{} \"{}\" - Process inlet air temperature used in regen outlet temperature equation is out of range error continues...",
3370 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3371 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3372 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.index,
3373 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.last,
3374 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.last);
3375 : }
3376 : }
3377 : // Process inlet humidity ratio for temp eqn
3378 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.print) {
3379 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.count;
3380 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.count < 2) {
3381 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.buffer1);
3382 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.buffer2);
3383 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.buffer3);
3384 0 : ShowContinueError(state,
3385 : "...Using process inlet air humidity ratios that are outside the regeneration outlet air temperature equation "
3386 : "model boundaries may adversely affect desiccant model performance.");
3387 : } else {
3388 0 : ShowRecurringWarningErrorAtEnd(state,
3389 0 : format("{} \"{}\" - Process inlet air humidity ratio used in regen outlet temperature equation is "
3390 : "out of range error continues...",
3391 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3392 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3393 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.index,
3394 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.last,
3395 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.last);
3396 : }
3397 : }
3398 : // Process and regeneration face velocity for temp eqn
3399 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.print) {
3400 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.count;
3401 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.count < 2) {
3402 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.buffer1);
3403 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.buffer2);
3404 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.buffer3);
3405 0 : ShowContinueError(state,
3406 : "...Using process and regeneration face velocities that are outside the regeneration outlet air temperature "
3407 : "equation model boundaries may adversely affect desiccant model performance.");
3408 : } else {
3409 0 : ShowRecurringWarningErrorAtEnd(state,
3410 0 : format("{} \"{}\" - Process and regen inlet air face velocity used in regen outlet temperature "
3411 : "equation is out of range error continues...",
3412 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3413 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3414 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.index,
3415 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.last,
3416 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.last);
3417 : }
3418 : }
3419 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
3420 :
3421 : // save last system time step and last end time of current time step (used to determine if warning is valid)
3422 0 : thisError.TimeStepSysLast = TimeStepSys;
3423 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
3424 :
3425 : // If regen and process inlet temperatures are the same the coil is off, do not print out of bounds warning for this case
3426 0 : if (std::abs(T_RegenInTemp - T_ProcInTemp) < SMALL) {
3427 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.print = false;
3428 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.print = false;
3429 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.print = false;
3430 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.print = false;
3431 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.print = false;
3432 0 : return;
3433 : }
3434 :
3435 : // check boundaries of independent variables and post warnings to individual buffers to print at end of time step
3436 : // checking model bounds for variables of regeneration outlet temperature equation
3437 : // Regen inlet temp
3438 0 : if (T_RegenInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInTemp ||
3439 0 : T_RegenInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInTemp) {
3440 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.last = T_RegenInTemp;
3441 0 : thisError.OutputChar = format("{:.2R}", T_RegenInTemp);
3442 0 : thisError.OutputCharLo = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInTemp);
3443 0 : thisError.OutputCharHi = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInTemp);
3444 0 : if (T_RegenInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInTemp) {
3445 0 : T_RegenInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInTemp;
3446 : }
3447 0 : if (T_RegenInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInTemp) {
3448 0 : T_RegenInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInTemp;
3449 : }
3450 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3451 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.print = true;
3452 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.buffer1 = format(
3453 : "{} \"{}\" - Regeneration inlet air temperature used in regen outlet air temperature equation is outside model boundaries at {}.",
3454 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3455 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3456 0 : thisError.OutputChar);
3457 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.buffer2 =
3458 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} {}",
3459 0 : thisError.OutputCharLo,
3460 0 : thisError.OutputCharHi,
3461 0 : state.dataEnvrn->EnvironmentName,
3462 0 : state.dataEnvrn->CurMnDy,
3463 0 : CreateSysTimeIntervalString(state));
3464 0 : thisError.CharValue = format("{:.6R}", T_RegenInTemp);
3465 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.buffer3 =
3466 0 : format("...Regeneration outlet air temperature equation: regeneration inlet air temperature passed to the model = {}",
3467 0 : thisError.CharValue);
3468 : } else {
3469 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.print = false;
3470 : }
3471 : } else {
3472 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInTempError.print = false;
3473 : }
3474 : // regen inlet humidity ratio
3475 0 : if (T_RegenInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInHumRat ||
3476 0 : T_RegenInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInHumRat) {
3477 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.last = T_RegenInHumRat;
3478 0 : thisError.OutputChar = format("{:.6R}", T_RegenInHumRat);
3479 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInHumRat);
3480 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInHumRat);
3481 0 : if (T_RegenInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInHumRat) {
3482 0 : T_RegenInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInHumRat;
3483 : }
3484 0 : if (T_RegenInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInHumRat) {
3485 0 : T_RegenInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInHumRat;
3486 : }
3487 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3488 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.print = true;
3489 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.buffer1 =
3490 0 : format("{} \"{}\" - Regeneration inlet air humidity ratio used in regen outlet air temperature equation is outside model "
3491 : "boundaries at {}.",
3492 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3493 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3494 0 : thisError.OutputChar);
3495 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.buffer2 =
3496 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} {}",
3497 0 : thisError.OutputCharLo,
3498 0 : thisError.OutputCharHi,
3499 0 : state.dataEnvrn->EnvironmentName,
3500 0 : state.dataEnvrn->CurMnDy,
3501 0 : CreateSysTimeIntervalString(state));
3502 0 : thisError.CharValue = format("{:.6R}", T_RegenInHumRat);
3503 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.buffer3 =
3504 0 : format("...Regeneration outlet air temperature equation: regeneration inlet air humidity ratio passed to the model = {}",
3505 0 : thisError.CharValue);
3506 : } else {
3507 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.print = false;
3508 : }
3509 : } else {
3510 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_RegenInHumRatError.print = false;
3511 : }
3512 : // process inlet temp
3513 0 : if (T_ProcInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInTemp ||
3514 0 : T_ProcInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInTemp) {
3515 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.last = T_ProcInTemp;
3516 0 : thisError.OutputChar = format("{:.2R}", T_ProcInTemp);
3517 0 : thisError.OutputCharLo = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInTemp);
3518 0 : thisError.OutputCharHi = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInTemp);
3519 0 : if (T_ProcInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInTemp) {
3520 0 : T_ProcInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInTemp;
3521 : }
3522 0 : if (T_ProcInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInTemp) {
3523 0 : T_ProcInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInTemp;
3524 : }
3525 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3526 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.print = true;
3527 : // Suppress warning message when process inlet temperature = 0 (DX coil is off)
3528 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.last == 0.0)
3529 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.print = false;
3530 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.buffer1 = format(
3531 : "{} \"{}\" - Process inlet air temperature used in regen outlet air temperature equation is outside model boundaries at {}.",
3532 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3533 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3534 0 : thisError.OutputChar);
3535 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.buffer2 =
3536 0 : format("...Valid range = {} to {}. Occurrence info = {},{} {}",
3537 0 : thisError.OutputCharLo,
3538 0 : thisError.OutputCharHi,
3539 0 : state.dataEnvrn->EnvironmentName,
3540 0 : state.dataEnvrn->CurMnDy,
3541 0 : CreateSysTimeIntervalString(state));
3542 0 : thisError.CharValue = format("{:.6R}", T_ProcInTemp);
3543 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.buffer3 = format(
3544 0 : "...Regeneration outlet air temperature equation: process inlet air temperature passed to the model = {}", thisError.CharValue);
3545 : } else {
3546 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.print = false;
3547 : }
3548 : } else {
3549 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInTempError.print = false;
3550 : }
3551 : // process inlet humidity ratio
3552 0 : if (T_ProcInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInHumRat ||
3553 0 : T_ProcInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInHumRat) {
3554 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.last = T_ProcInHumRat;
3555 0 : thisError.OutputChar = format("{:.6R}", T_ProcInHumRat);
3556 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInHumRat);
3557 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInHumRat);
3558 0 : if (T_ProcInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInHumRat) {
3559 0 : T_ProcInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInHumRat;
3560 : }
3561 0 : if (T_ProcInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInHumRat) {
3562 0 : T_ProcInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInHumRat;
3563 : }
3564 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3565 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.print = true;
3566 : // Suppress warning message when process inlet humrat = 0 (DX coil is off)
3567 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.last == 0.0)
3568 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.print = false;
3569 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.buffer1 = format(
3570 : "{} \"{}\" - Process inlet air humidity ratio used in regen outlet air temperature equation is outside model boundaries at {}.",
3571 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3572 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3573 0 : thisError.OutputChar);
3574 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.buffer2 =
3575 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} {}",
3576 0 : thisError.OutputCharLo,
3577 0 : thisError.OutputCharHi,
3578 0 : state.dataEnvrn->EnvironmentName,
3579 0 : state.dataEnvrn->CurMnDy,
3580 0 : CreateSysTimeIntervalString(state));
3581 0 : thisError.CharValue = format("{:.6R}", T_ProcInHumRat);
3582 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.buffer3 =
3583 0 : format("...Regeneration outlet air temperature equation: process inlet air humidity ratio passed to the model = {}",
3584 0 : thisError.CharValue);
3585 : } else {
3586 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.print = false;
3587 : }
3588 : } else {
3589 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.print = false;
3590 : }
3591 : // regeneration and process face velocity
3592 0 : if (T_FaceVel < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinFaceVel ||
3593 0 : T_FaceVel > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxFaceVel) {
3594 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.last = T_FaceVel;
3595 0 : thisError.OutputChar = format("{:.6R}", T_FaceVel);
3596 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinFaceVel);
3597 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxFaceVel);
3598 0 : if (T_FaceVel < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinFaceVel) {
3599 0 : T_FaceVel = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinFaceVel;
3600 : }
3601 0 : if (T_FaceVel > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxFaceVel) {
3602 0 : T_FaceVel = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxFaceVel;
3603 : }
3604 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3605 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.print = true;
3606 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.buffer1 =
3607 0 : format("{} \"{}\" - Process and regen inlet air face velocity used in regen outlet air temperature equation is outside model "
3608 : "boundaries at {}.",
3609 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3610 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3611 0 : thisError.OutputChar);
3612 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.buffer2 =
3613 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} {}",
3614 0 : thisError.OutputCharLo,
3615 0 : thisError.OutputCharHi,
3616 0 : state.dataEnvrn->EnvironmentName,
3617 0 : state.dataEnvrn->CurMnDy,
3618 0 : CreateSysTimeIntervalString(state));
3619 0 : thisError.CharValue = format("{:.6R}", T_FaceVel);
3620 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.buffer3 = format(
3621 0 : "...Regeneration outlet air temperature equation: process and regen face velocity passed to the model = {}", thisError.CharValue);
3622 : } else {
3623 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.print = false;
3624 : }
3625 : } else {
3626 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_FaceVelError.print = false;
3627 : }
3628 : }
3629 :
3630 0 : void HeatExchCond::CheckModelBoundsHumRatEq(EnergyPlusData &state,
3631 : Real64 &H_RegenInTemp, // current regen inlet temperature (C) for regen outlet hum rat eqn
3632 : Real64 &H_RegenInHumRat, // current regen inlet hum rat for regen outlet hum rat eqn
3633 : Real64 &H_ProcInTemp, // current process inlet temperature (C) for regen outlet hum rat eqn
3634 : Real64 &H_ProcInHumRat, // current process inlet hum rat for regen outlet hum rat eqn
3635 : Real64 &H_FaceVel, // current process and regen face velocity (m/s)
3636 : bool const FirstHVACIteration // First HVAC iteration flag
3637 : ) const
3638 : {
3639 :
3640 : // SUBROUTINE INFORMATION:
3641 : // AUTHOR Mangesh Basarkar, FSEC
3642 : // DATE WRITTEN January 2007
3643 : // MODIFIED na
3644 : // RE-ENGINEERED na
3645 :
3646 : // PURPOSE OF THIS SUBROUTINE:
3647 : // To verify that the empirical model's independent variables are within the limits used during the
3648 : // development of the empirical model.
3649 :
3650 : // METHODOLOGY EMPLOYED:
3651 : // The empirical models used for simulating a desiccant enhanced cooling coil are based on a limited data set.
3652 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
3653 : // The range of each independent variable is provided by the user and are based on the limits of the
3654 : // empirical model. These limits are tested in this subroutine each time step and returned for use by the calling
3655 : // routine.
3656 :
3657 : // Using/Aliasing
3658 : using General::CreateSysTimeIntervalString;
3659 :
3660 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3661 0 : auto &thisError = state.dataHeatRecovery->error2;
3662 :
3663 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
3664 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
3665 : // current end time is compared with last to see if time step changed
3666 :
3667 : // calculate end time of current time step
3668 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
3669 :
3670 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
3671 : // Wait for next time step to print warnings. If simulation iterates, print out
3672 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
3673 : // If a warning occurs and the simulation down shifts, the warning is not valid.
3674 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
3675 :
3676 : // print error for variables of regeneration outlet humidity ratio equation
3677 : // Regen inlet temp for humidity ratio eqn
3678 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.print) {
3679 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.count;
3680 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.count < 2) {
3681 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.buffer1);
3682 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.buffer2);
3683 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.buffer3);
3684 0 : ShowContinueError(state,
3685 : "...Using regeneration inlet air temperatures that are outside the regeneration inlet air temperature equation "
3686 : "model boundaries may adversely affect desiccant model performance.");
3687 : } else {
3688 0 : ShowRecurringWarningErrorAtEnd(state,
3689 0 : format("{} \"{}\" - Regeneration inlet air temperature used in regen outlet air humidity ratio "
3690 : "equation is out of range error continues...",
3691 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3692 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3693 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.index,
3694 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.last,
3695 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.last);
3696 : }
3697 : }
3698 : // Regen inlet humidity ratio for humidity ratio eqn
3699 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.print) {
3700 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.count;
3701 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.count < 2) {
3702 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.buffer1);
3703 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.buffer2);
3704 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.buffer3);
3705 0 : ShowContinueError(state,
3706 : "...Using regeneration inlet air humidity ratios that are outside the regeneration outlet air humidity ratio "
3707 : "equation model boundaries may adversely affect desiccant model performance.");
3708 : } else {
3709 0 : ShowRecurringWarningErrorAtEnd(state,
3710 0 : format("{} \"{}\" - Regeneration inlet air humidity ratio used in regen outlet air humidity ratio "
3711 : "equation is out of range error continues...",
3712 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3713 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3714 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.index,
3715 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.last,
3716 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.last);
3717 : }
3718 : }
3719 : // Process inlet temp for humidity ratio eqn
3720 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.print) {
3721 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.count;
3722 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.count < 2) {
3723 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.buffer1);
3724 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.buffer2);
3725 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.buffer3);
3726 0 : ShowContinueError(state,
3727 : "...Using process inlet air temperatures that are outside the regeneration outlet air humidity ratio equation "
3728 : "model may adversely affect desiccant model performance.");
3729 : } else {
3730 0 : ShowRecurringWarningErrorAtEnd(state,
3731 0 : format("{} \"{}\" - Process inlet air temperature used in regen outlet air humidity ratio "
3732 : "equation is out of range error continues...",
3733 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3734 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3735 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.index,
3736 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.last,
3737 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.last);
3738 : }
3739 : }
3740 : // Process inlet humidity ratio for humidity ratio eqn
3741 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.print) {
3742 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.count;
3743 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.count < 2) {
3744 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.buffer1);
3745 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.buffer2);
3746 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.buffer3);
3747 0 : ShowContinueError(state,
3748 : "...Using process inlet air humidity ratios that are outside the regeneration outlet humidity ratio equation "
3749 : "model boundaries may adversely affect desiccant model performance.");
3750 : } else {
3751 0 : ShowRecurringWarningErrorAtEnd(state,
3752 0 : format("{} \"{}\" - Process inlet air humidity ratio used in regen outlet air humidity ratio "
3753 : "equation is out of range error continues...",
3754 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3755 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3756 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_ProcInHumRatError.index,
3757 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.last,
3758 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.last);
3759 : }
3760 : }
3761 : // Process and regeneration face velocity for humidity ratio eqn
3762 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.print) {
3763 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.count;
3764 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.count < 2) {
3765 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.buffer1);
3766 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.buffer2);
3767 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.buffer3);
3768 0 : ShowContinueError(state,
3769 : "...Using process and regeneration face velocities that are outside the regeneration outlet air humidity ratio "
3770 : "equation model boundaries may adversely affect desiccant model performance.");
3771 : } else {
3772 0 : ShowRecurringWarningErrorAtEnd(state,
3773 0 : format("{} \"{}\" - Process and regen face velocity used in regen outlet air humidity ratio "
3774 : "equation is out of range error continues...",
3775 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3776 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
3777 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.index,
3778 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.last,
3779 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.last);
3780 : }
3781 : }
3782 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
3783 :
3784 : // save last system time step and last end time of current time step (used to determine if warning is valid)
3785 0 : thisError.TimeStepSysLast = TimeStepSys;
3786 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
3787 :
3788 : // If regen and process inlet temperatures are the same the coil is off, do not print out of bounds warning for this case
3789 0 : if (std::abs(H_RegenInTemp - H_ProcInTemp) < SMALL) {
3790 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.print = false;
3791 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.print = false;
3792 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.print = false;
3793 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.print = false;
3794 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.print = false;
3795 0 : return;
3796 : }
3797 :
3798 : // check boundaries of independent variables and post warnings to individual buffers to print at end of time step
3799 : // checking model bounds for variables of regeneration outlet humidity ratio equation
3800 : // Regen inlet temp
3801 0 : if (H_RegenInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInTemp ||
3802 0 : H_RegenInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInTemp) {
3803 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.last = H_RegenInTemp;
3804 0 : thisError.OutputChar = format("{:.2R}", H_RegenInTemp);
3805 0 : thisError.OutputCharLo = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInTemp);
3806 0 : thisError.OutputCharHi = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInTemp);
3807 0 : if (H_RegenInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInTemp) {
3808 0 : H_RegenInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInTemp;
3809 : }
3810 0 : if (H_RegenInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInTemp) {
3811 0 : H_RegenInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInTemp;
3812 : }
3813 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3814 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.print = true;
3815 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.buffer1 =
3816 0 : format("{} \"{}\" - Regeneration inlet air temperature used in regen outlet air humidity ratio equation is outside model "
3817 : "boundaries at {}.",
3818 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3819 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3820 0 : thisError.OutputChar);
3821 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.buffer2 =
3822 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} , {}",
3823 0 : thisError.OutputCharLo,
3824 0 : thisError.OutputCharHi,
3825 0 : state.dataEnvrn->EnvironmentName,
3826 0 : state.dataEnvrn->CurMnDy,
3827 0 : CreateSysTimeIntervalString(state));
3828 0 : thisError.CharValue = format("{:.2R}", H_RegenInTemp);
3829 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.buffer3 =
3830 0 : format("...Regeneration outlet air humidity ratio equation: regeneration inlet air temperature passed to the model = {}",
3831 0 : thisError.CharValue);
3832 : } else {
3833 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.print = false;
3834 : }
3835 : } else {
3836 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInTempError.print = false;
3837 : }
3838 : // regen inlet humidity ratio
3839 0 : if (H_RegenInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInHumRat ||
3840 0 : H_RegenInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInHumRat) {
3841 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.last = H_RegenInHumRat;
3842 0 : thisError.OutputChar = format("{:.6R}", H_RegenInHumRat);
3843 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInHumRat);
3844 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInHumRat);
3845 0 : if (H_RegenInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInHumRat) {
3846 0 : H_RegenInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInHumRat;
3847 : }
3848 0 : if (H_RegenInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInHumRat) {
3849 0 : H_RegenInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInHumRat;
3850 : }
3851 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3852 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.print = true;
3853 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.buffer1 =
3854 0 : format("{} \"{}\" - Regeneration inlet air humidity ratio used in regen outlet air humidity ratio equation is outside model "
3855 : "boundaries at {}.",
3856 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3857 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3858 0 : thisError.OutputChar);
3859 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.buffer2 =
3860 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} {}",
3861 0 : thisError.OutputCharLo,
3862 0 : thisError.OutputCharHi,
3863 0 : state.dataEnvrn->EnvironmentName,
3864 0 : state.dataEnvrn->CurMnDy,
3865 0 : CreateSysTimeIntervalString(state));
3866 0 : thisError.CharValue = format("{:.6R}", H_RegenInHumRat);
3867 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.buffer3 =
3868 0 : format("...Regeneration outlet air humidity ratio equation: regeneration inlet air humidity ratio passed to the model = {}",
3869 0 : thisError.CharValue);
3870 : } else {
3871 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.print = false;
3872 : }
3873 : } else {
3874 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_RegenInHumRatError.print = false;
3875 : }
3876 : // process inlet temp
3877 0 : if (H_ProcInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInTemp ||
3878 0 : H_ProcInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInTemp) {
3879 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.last = H_ProcInTemp;
3880 0 : thisError.OutputChar = format("{:.2R}", H_ProcInTemp);
3881 0 : thisError.OutputCharLo = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInTemp);
3882 0 : thisError.OutputCharHi = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInTemp);
3883 0 : if (H_ProcInTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInTemp) {
3884 0 : H_ProcInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInTemp;
3885 : }
3886 0 : if (H_ProcInTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInTemp) {
3887 0 : H_ProcInTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInTemp;
3888 : }
3889 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3890 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.print = true;
3891 : // Suppress warning message when process inlet temperature = 0 (DX coil is off)
3892 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.last == 0.0)
3893 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.print = false;
3894 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.buffer1 = format(
3895 : "{} \"{}\" - Process inlet air temperature used in regen outlet air humidity ratio equation is outside model boundaries at {}.",
3896 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3897 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3898 0 : thisError.OutputChar);
3899 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.buffer2 =
3900 0 : format("...Valid range = {} to {}. Occurrence info = {}, {} {}",
3901 0 : thisError.OutputCharLo,
3902 0 : thisError.OutputCharHi,
3903 0 : state.dataEnvrn->EnvironmentName,
3904 0 : state.dataEnvrn->CurMnDy,
3905 0 : CreateSysTimeIntervalString(state));
3906 0 : thisError.CharValue = format("{:.6R}", H_ProcInTemp);
3907 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.buffer3 =
3908 0 : format("...Regeneration outlet air humidity ratio equation: process inlet air temperature passed to the model = {}",
3909 0 : thisError.CharValue);
3910 : } else {
3911 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.print = false;
3912 : }
3913 : } else {
3914 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInTempError.print = false;
3915 : }
3916 : // process inlet humidity ratio
3917 0 : if (H_ProcInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInHumRat ||
3918 0 : H_ProcInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInHumRat) {
3919 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.last = H_ProcInHumRat;
3920 0 : thisError.OutputChar = format("{:.6R}", H_ProcInHumRat);
3921 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInHumRat);
3922 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInHumRat);
3923 0 : if (H_ProcInHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInHumRat) {
3924 0 : H_ProcInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInHumRat;
3925 : }
3926 0 : if (H_ProcInHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInHumRat) {
3927 0 : H_ProcInHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInHumRat;
3928 : }
3929 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3930 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.print = true;
3931 : // Suppress warning message when process inlet humrat = 0 (DX coil is off)
3932 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.last == 0.0)
3933 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.print = false;
3934 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.buffer1 =
3935 0 : format("{} \"{}\" - Process inlet air humidity ratio used in regen outlet air humidity ratio equation is outside model "
3936 : "boundaries at {}.",
3937 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3938 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3939 0 : thisError.OutputChar);
3940 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.buffer2 =
3941 0 : format("...Valid range = {} to {}. Occurrence info = {}, {}, {}",
3942 0 : thisError.OutputCharLo,
3943 0 : thisError.OutputCharHi,
3944 0 : state.dataEnvrn->EnvironmentName,
3945 0 : state.dataEnvrn->CurMnDy,
3946 0 : CreateSysTimeIntervalString(state));
3947 0 : thisError.CharValue = format("{:.6R}", H_ProcInHumRat);
3948 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.buffer3 =
3949 0 : format("...Regeneration outlet air humidity ratio equation: process inlet air humidity ratio passed to the model = {}",
3950 0 : thisError.CharValue);
3951 : } else {
3952 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.print = false;
3953 : }
3954 : } else {
3955 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_ProcInHumRatError.print = false;
3956 : }
3957 : // regeneration and process face velocity
3958 0 : if (H_FaceVel < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinFaceVel ||
3959 0 : H_FaceVel > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxFaceVel) {
3960 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.last = H_FaceVel;
3961 0 : thisError.OutputChar = format("{:.6R}", H_FaceVel);
3962 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinFaceVel);
3963 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxFaceVel);
3964 0 : if (H_FaceVel < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinFaceVel) {
3965 0 : H_FaceVel = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinFaceVel;
3966 : }
3967 0 : if (H_FaceVel > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxFaceVel) {
3968 0 : H_FaceVel = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxFaceVel;
3969 : }
3970 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
3971 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.print = true;
3972 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.buffer1 =
3973 0 : format("{} \"{}\" - Process and regen inlet air face velocity used in regen outlet air humidity ratio equation is outside model "
3974 : "boundaries at {}.",
3975 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
3976 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
3977 0 : thisError.OutputChar);
3978 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.buffer2 =
3979 0 : format("...Valid range = {} to {}. Occurrence info = {}, {}, {}",
3980 0 : thisError.OutputCharLo,
3981 0 : thisError.OutputCharHi,
3982 0 : state.dataEnvrn->EnvironmentName,
3983 0 : state.dataEnvrn->CurMnDy,
3984 0 : CreateSysTimeIntervalString(state));
3985 0 : thisError.CharValue = format("{:.6R}", H_FaceVel);
3986 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.buffer3 =
3987 0 : format("...Regeneration outlet air humidity ratio equation: process and regeneration face velocity passed to the model = {}",
3988 0 : thisError.CharValue);
3989 : } else {
3990 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.print = false;
3991 : }
3992 : } else {
3993 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_FaceVelError.print = false;
3994 : }
3995 : }
3996 :
3997 0 : void HeatExchCond::CheckModelBoundOutput_Temp(EnergyPlusData &state,
3998 : Real64 const RegenInTemp, // current regen inlet temp passed to eqn
3999 : Real64 &RegenOutTemp, // current regen outlet temp from eqn
4000 : bool const FirstHVACIteration // First HVAC iteration flag
4001 : ) const
4002 : {
4003 :
4004 : // SUBROUTINE INFORMATION:
4005 : // AUTHOR Mangesh Basarkar, FSEC
4006 : // DATE WRITTEN January 2007
4007 : // MODIFIED June 2007, R. Raustad, changed requirement that regen outlet temp be less than inlet temp
4008 : // RE-ENGINEERED na
4009 :
4010 : // PURPOSE OF THIS SUBROUTINE:
4011 : // To verify that the empirical model's independent variables are within the limits used during the
4012 : // development of the empirical model.
4013 :
4014 : // METHODOLOGY EMPLOYED:
4015 : // The empirical models used for simulating a desiccant enhanced cooling coil are based on a limited data set.
4016 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
4017 : // The range of each independent variable is provided by the user and are based on the limits of the
4018 : // empirical model. These limits are tested in this subroutine each time step and returned for use by the calling
4019 : // routine.
4020 :
4021 : // Using/Aliasing
4022 : using General::CreateSysTimeIntervalString;
4023 :
4024 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4025 0 : auto &thisError = state.dataHeatRecovery->error3;
4026 :
4027 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
4028 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
4029 : // current end time is compared with last to see if time step changed
4030 :
4031 : // calculate end time of current time step
4032 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
4033 :
4034 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
4035 : // Wait for next time step to print warnings. If simulation iterates, print out
4036 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
4037 : // If a warning occurs and the simulation down shifts, the warning is not valid.
4038 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
4039 :
4040 : // print error when regeneration outlet temperature is greater than regen inlet temperature
4041 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.print) {
4042 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.count;
4043 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.count < 2) {
4044 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.buffer1);
4045 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.buffer2);
4046 0 : ShowContinueError(state,
4047 : "...Regeneration outlet air temperature should always be less than or equal to regen inlet air temperature. "
4048 : "Verify correct model coefficients.");
4049 : } else {
4050 0 : ShowRecurringWarningErrorAtEnd(
4051 : state,
4052 0 : format("{} \"{}\" - Regeneration outlet air temperature above regen inlet air temperature error continues...",
4053 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4054 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4055 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.index,
4056 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.last,
4057 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.last);
4058 : }
4059 : }
4060 :
4061 : // print error for variables of regeneration outlet temperature
4062 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.print) {
4063 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.count;
4064 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.count < 2) {
4065 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.buffer1);
4066 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.buffer2);
4067 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.buffer3);
4068 0 : ShowContinueError(state,
4069 : "...Regeneration outlet air temperature should always be less than or equal to regen inlet air temperature. "
4070 : "Verify correct model coefficients.");
4071 : } else {
4072 0 : ShowRecurringWarningErrorAtEnd(
4073 : state,
4074 0 : format("{} \"{}\" - Regeneration outlet air temperature should be less than regen inlet air temperature error continues...",
4075 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4076 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4077 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.index,
4078 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.last,
4079 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.last);
4080 : }
4081 : }
4082 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
4083 :
4084 : // save last system time step and last end time of current time step (used to determine if warning is valid)
4085 0 : thisError.TimeStepSysLast = TimeStepSys;
4086 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
4087 :
4088 : // checking model regeneration outlet temperature to always be less than or equal to regeneration inlet temperature
4089 0 : if (RegenOutTemp > RegenInTemp) {
4090 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.last = RegenOutTemp;
4091 0 : thisError.OutputChar = format("{:.2R}", RegenOutTemp);
4092 0 : thisError.OutputCharHi = format("{:.2R}", RegenInTemp);
4093 : // IF(RegenOutTemp .GT. RegenInTemp)THEN
4094 : // RegenOutTemp = RegenInTemp
4095 : // END IF
4096 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
4097 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.print = true;
4098 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.buffer1 =
4099 0 : format("{} \"{}\" - Regeneration outlet air temperature is greater than inlet temperature at {}.",
4100 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4101 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4102 0 : thisError.OutputChar);
4103 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.buffer2 =
4104 0 : format("...Regen inlet air temperature = {}. Occurrence info = {}, {}, {}",
4105 0 : thisError.OutputCharHi,
4106 0 : state.dataEnvrn->EnvironmentName,
4107 0 : state.dataEnvrn->CurMnDy,
4108 0 : CreateSysTimeIntervalString(state));
4109 0 : thisError.CharValue = format("{:.6R}", RegenOutTemp);
4110 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempFailedError.buffer3 = format(
4111 0 : "...Regen outlet air temperature equation: regeneration outlet air temperature allowed from the model = {}", thisError.CharValue);
4112 : } else {
4113 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.print = false;
4114 : }
4115 : } else {
4116 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.print = false;
4117 : }
4118 :
4119 : // check boundaries of regen outlet temperature and post warnings to individual buffers to print at end of time step
4120 : // checking model bounds for regeneration outlet temperature
4121 0 : if (RegenOutTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutTemp ||
4122 0 : RegenOutTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutTemp) {
4123 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.last = RegenOutTemp;
4124 0 : thisError.OutputChar = format("{:.2R}", RegenOutTemp);
4125 0 : thisError.OutputCharLo = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutTemp);
4126 0 : thisError.OutputCharHi = format("{:.2R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutTemp);
4127 0 : if (RegenOutTemp < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutTemp) {
4128 0 : RegenOutTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutTemp;
4129 : }
4130 0 : if (RegenOutTemp > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutTemp) {
4131 0 : RegenOutTemp = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutTemp;
4132 : }
4133 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
4134 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.print = true;
4135 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.buffer1 =
4136 0 : format("{} \"{}\" - Regeneration outlet air temperature equation is outside model boundaries at {}.",
4137 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4138 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4139 0 : thisError.OutputChar);
4140 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.buffer2 =
4141 0 : format("...Valid range = {} to {}. Occurrence info = {}, {}, {}",
4142 0 : thisError.OutputCharLo,
4143 0 : thisError.OutputCharHi,
4144 0 : state.dataEnvrn->EnvironmentName,
4145 0 : state.dataEnvrn->CurMnDy,
4146 0 : CreateSysTimeIntervalString(state));
4147 0 : thisError.CharValue = format("{:.6R}", RegenOutTemp);
4148 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.buffer3 = format(
4149 0 : "...Regen outlet air temperature equation: regeneration outlet air temperature allowed from the model = {}", thisError.CharValue);
4150 : } else {
4151 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.print = false;
4152 : }
4153 : } else {
4154 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutTempError.print = false;
4155 : }
4156 0 : }
4157 :
4158 0 : void HeatExchCond::CheckModelBoundOutput_HumRat(EnergyPlusData &state,
4159 : Real64 const RegenInHumRat, // current regen inlet hum rat passed to eqn
4160 : Real64 &RegenOutHumRat, // current regen outlet hum rat from eqn
4161 : bool const FirstHVACIteration // First HVAC iteration flag
4162 : ) const
4163 : {
4164 :
4165 : // SUBROUTINE INFORMATION:
4166 : // AUTHOR Mangesh Basarkar, FSEC
4167 : // DATE WRITTEN January 2007
4168 : // MODIFIED June 2007, R. Raustad, changed requirement that regen outlet temp be less than inlet temp
4169 : // RE-ENGINEERED na
4170 :
4171 : // PURPOSE OF THIS SUBROUTINE:
4172 : // To verify that the empirical model's independent variables are within the limits used during the
4173 : // development of the empirical model.
4174 :
4175 : // METHODOLOGY EMPLOYED:
4176 : // The empirical models used for simulating a desiccant enhanced cooling coil are based on a limited data set.
4177 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
4178 : // The range of each independent variable is provided by the user and are based on the limits of the
4179 : // empirical model. These limits are tested in this subroutine each time step and returned for use by the calling
4180 : // routine.
4181 : // REFERENCES:
4182 : // na
4183 :
4184 : // Using/Aliasing
4185 : using General::CreateSysTimeIntervalString;
4186 :
4187 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4188 0 : auto &thisError = state.dataHeatRecovery->error4; // (THIS_AUTO_OK)
4189 :
4190 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
4191 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
4192 :
4193 : // current end time is compared with last to see if time step changed
4194 :
4195 : // calculate end time of current time step
4196 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
4197 :
4198 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
4199 : // Wait for next time step to print warnings. If simulation iterates, print out
4200 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
4201 : // If a warning occurs and the simulation down shifts, the warning is not valid.
4202 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
4203 :
4204 : // print error when regeneration outlet humidity ratio is less than regeneration inlet humidity ratio
4205 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.print) {
4206 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.count;
4207 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.count < 2) {
4208 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.buffer1);
4209 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.buffer2);
4210 0 : ShowContinueError(state,
4211 : "...Regeneration outlet air humidity ratio should always be greater than or equal to regen inlet air humidity "
4212 : "ratio. Verify correct model coefficients.");
4213 : } else {
4214 0 : ShowRecurringWarningErrorAtEnd(state,
4215 0 : format("{} \"{}\" - Regeneration outlet air humidity ratio should be greater than regen inlet air "
4216 : "humidity ratio error continues...",
4217 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4218 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4219 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.index,
4220 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.last,
4221 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.last);
4222 : }
4223 : }
4224 :
4225 : // print error for regeneration outlet humidity ratio
4226 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.print) {
4227 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.count;
4228 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.count < 2) {
4229 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.buffer1);
4230 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.buffer2);
4231 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.buffer3);
4232 0 : ShowContinueError(
4233 : state,
4234 : "...Regeneration outlet air humidity ratio outside model boundaries may adversely affect desiccant model performance.");
4235 : } else {
4236 0 : ShowRecurringWarningErrorAtEnd(state,
4237 0 : format("{} \"{}\" - Regeneration outlet air humidity ratio is out of range error continues...",
4238 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4239 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4240 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.index,
4241 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.last,
4242 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.last);
4243 : }
4244 : }
4245 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
4246 :
4247 : // save last system time step and last end time of current time step (used to determine if warning is valid)
4248 0 : thisError.TimeStepSysLast = TimeStepSys;
4249 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
4250 :
4251 : // checking for regeneration outlet humidity ratio less than or equal to regeneration inlet humidity ratio
4252 0 : if (RegenOutHumRat < RegenInHumRat) {
4253 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.last = RegenOutHumRat;
4254 0 : thisError.OutputChar = format("{:.6R}", RegenOutHumRat);
4255 0 : thisError.OutputCharHi = format("{:.6R}", RegenInHumRat);
4256 : // IF(RegenOutHumRat .LT. RegenInHumRat)THEN
4257 : // RegenOutHumRat = RegenInHumRat
4258 : // END IF
4259 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
4260 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.print = true;
4261 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.buffer1 =
4262 0 : format("{} \"{}\" - Regeneration outlet air humidity ratio is less than the inlet air humidity ratio at {}.",
4263 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4264 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4265 0 : thisError.OutputChar);
4266 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.buffer2 =
4267 0 : format("...Regen inlet air humidity ratio = {}. Occurrence info = {}, {}, {}",
4268 0 : thisError.OutputCharHi,
4269 0 : state.dataEnvrn->EnvironmentName,
4270 0 : state.dataEnvrn->CurMnDy,
4271 0 : CreateSysTimeIntervalString(state));
4272 0 : thisError.CharValue = format("{:.6R}", RegenOutHumRat);
4273 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.buffer3 =
4274 0 : format("...Regen outlet air humidity ratio equation: regeneration outlet air humidity ratio allowed from the model = {}",
4275 0 : thisError.CharValue);
4276 : } else {
4277 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.print = false;
4278 : }
4279 : } else {
4280 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatFailedErr.print = false;
4281 : }
4282 :
4283 : // check boundaries of regen outlet humrat and post warnings to individual buffers to print at end of time step
4284 : // checking model bounds for regeneration outlet humidity ratio
4285 0 : if (RegenOutHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutHumRat ||
4286 0 : RegenOutHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutHumRat) {
4287 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.last = RegenOutHumRat;
4288 0 : thisError.OutputChar = format("{:.6R}", RegenOutHumRat);
4289 0 : thisError.OutputCharLo = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutHumRat);
4290 0 : thisError.OutputCharHi = format("{:.6R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutHumRat);
4291 0 : if (RegenOutHumRat < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutHumRat) {
4292 0 : RegenOutHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MinRegenAirOutHumRat;
4293 : }
4294 0 : if (RegenOutHumRat > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutHumRat) {
4295 0 : RegenOutHumRat = state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).MaxRegenAirOutHumRat;
4296 : }
4297 0 : if (!state.dataGlobal->WarmupFlag && !FirstHVACIteration) {
4298 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.print = true;
4299 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.buffer1 =
4300 0 : format("{} \"{}\" - Regeneration outlet air humidity ratio is outside model boundaries at {}.",
4301 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4302 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4303 0 : thisError.OutputChar);
4304 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.buffer2 =
4305 0 : format("...Valid range = {} to {}. Occurrence info = {}, {}, {}",
4306 0 : thisError.OutputCharLo,
4307 0 : thisError.OutputCharHi,
4308 0 : state.dataEnvrn->EnvironmentName,
4309 0 : state.dataEnvrn->CurMnDy,
4310 0 : CreateSysTimeIntervalString(state));
4311 0 : thisError.CharValue = format("{:.6R}", RegenOutHumRat);
4312 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.buffer3 =
4313 0 : format("...Regen outlet air humidity ratio equation: regeneration outlet air humidity ratio allowed from the model = {}",
4314 0 : thisError.CharValue);
4315 : } else {
4316 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.print = false;
4317 : }
4318 : } else {
4319 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenOutHumRatError.print = false;
4320 : }
4321 0 : }
4322 :
4323 0 : void HeatExchCond::CheckModelBoundsRH_TempEq(EnergyPlusData &state,
4324 : Real64 const T_RegenInTemp, // current regen inlet temperature passed to eqn
4325 : Real64 const T_RegenInHumRat, // current regen inlet hum rat passed to eqn
4326 : Real64 const T_ProcInTemp, // current process inlet temperature passed to eqn
4327 : Real64 const T_ProcInHumRat, // current regen outlet hum rat from eqn
4328 : bool const FirstHVACIteration // first HVAC iteration flag
4329 : ) const
4330 : {
4331 :
4332 : // SUBROUTINE INFORMATION:
4333 : // AUTHOR Richard Raustad, FSEC
4334 : // DATE WRITTEN January 2007
4335 : // MODIFIED na
4336 : // RE-ENGINEERED na
4337 :
4338 : // PURPOSE OF THIS SUBROUTINE:
4339 : // To verify that the empirical model's independent variables result in a relative humidity that is within the range
4340 : // of relative humidities used when creating the empirical model. Both the regeneration and process inlet are tested.
4341 :
4342 : // METHODOLOGY EMPLOYED:
4343 : // The empirical models used for simulating a desiccant enhanced cooling coil are based on a limited data set.
4344 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
4345 : // In addition, the range of relative humidities in the original data set may influence the output of the
4346 : // empirical model. This subroutine tests the relative humidities passed to the empirical model and warns the
4347 : // user if these relative humidities are out of bounds based on the limits set by the user.
4348 : // REFERENCES:
4349 : // na
4350 :
4351 : // Using/Aliasing
4352 : using General::CreateSysTimeIntervalString;
4353 :
4354 : using Psychrometrics::PsyRhFnTdbWPb;
4355 :
4356 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
4357 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
4358 0 : auto &RegenInletRH = state.dataHeatRecovery->RegenInletRH2;
4359 0 : auto &ProcInletRH = state.dataHeatRecovery->ProcInletRH2;
4360 0 : auto &thisError = state.dataHeatRecovery->error6;
4361 : // current end time is compared with last to see if time step changed
4362 :
4363 0 : if (state.dataGlobal->WarmupFlag || FirstHVACIteration) return;
4364 :
4365 : // calculate end time of current time step
4366 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
4367 :
4368 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
4369 : // Wait for next time step to print warnings. If simulation iterates, print out
4370 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
4371 : // If a warning occurs and the simulation down shifts, the warning is not valid.
4372 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
4373 :
4374 : // print error when regeneration inlet relative humidity is outside model boundaries
4375 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.print) {
4376 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.count;
4377 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.count < 2) {
4378 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.buffer1);
4379 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.buffer2);
4380 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.buffer3);
4381 0 : ShowContinueError(state,
4382 : "...Using regeneration inlet air relative humidities that are outside the regeneration outlet temperature "
4383 : "equation model boundaries may adversely affect desiccant model performance. Verify correct model "
4384 : "coefficients.");
4385 : } else {
4386 0 : ShowRecurringWarningErrorAtEnd(state,
4387 0 : format("{} \"{}\" - Regeneration inlet air relative humidity related to regen outlet air "
4388 : "temperature equation is outside model boundaries error continues...",
4389 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4390 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4391 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.index,
4392 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.last,
4393 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.last);
4394 : }
4395 : }
4396 :
4397 : // print error when process inlet relative humidity is outside model boundaries
4398 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.print) {
4399 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.count;
4400 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.count < 2) {
4401 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.buffer1);
4402 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.buffer2);
4403 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.buffer3);
4404 0 : ShowContinueError(state,
4405 : "...Using process inlet air relative humidities that are outside the regeneration outlet temperature equation "
4406 : "model boundaries may adversely affect desiccant model performance. Verify correct model coefficients.");
4407 : } else {
4408 0 : ShowRecurringWarningErrorAtEnd(state,
4409 0 : format("{} \"{}\" - Process inlet air relative humidity related to regen outlet air temperature "
4410 : "equation is outside model boundaries error continues...",
4411 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4412 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4413 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.index,
4414 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.last,
4415 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.last);
4416 : }
4417 : }
4418 :
4419 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
4420 :
4421 : // save last system time step and last end time of current time step (used to determine if warning is valid)
4422 0 : thisError.TimeStepSysLast = TimeStepSys;
4423 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
4424 :
4425 : // Check that condition is not above saturation curve prior to next calc (PsyRhFnTdbWPb) to avoid psyc routine errors
4426 : // *
4427 : // *
4428 : // x------*---------- T_HumRat
4429 : // | *
4430 : // | *
4431 : // *----------------- PsyWFnTdpPb(Tdp,Pb)
4432 : // * |
4433 : // |
4434 : // T_Temp
4435 0 : if (T_RegenInHumRat > Psychrometrics::PsyWFnTdpPb(state, T_RegenInTemp, state.dataEnvrn->OutBaroPress) ||
4436 0 : T_ProcInHumRat > Psychrometrics::PsyWFnTdpPb(state, T_ProcInTemp, state.dataEnvrn->OutBaroPress)) {
4437 : // reset RH print flags just in case
4438 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.print = false;
4439 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.print = false;
4440 0 : return;
4441 : }
4442 :
4443 : // If regen and process inlet temperatures are the same the coil is off, do not print out of bounds warning for this case
4444 0 : if (std::abs(T_RegenInTemp - T_ProcInTemp) < SMALL) {
4445 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.print = false;
4446 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.print = false;
4447 0 : return;
4448 : }
4449 :
4450 0 : RegenInletRH = PsyRhFnTdbWPb(state, T_RegenInTemp, T_RegenInHumRat, state.dataEnvrn->OutBaroPress);
4451 0 : ProcInletRH = min(1.0, PsyRhFnTdbWPb(state, T_ProcInTemp, T_ProcInHumRat, state.dataEnvrn->OutBaroPress));
4452 :
4453 : // checking if regeneration inlet relative humidity is within model boundaries
4454 0 : if (RegenInletRH < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInRelHum ||
4455 0 : RegenInletRH > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInRelHum) {
4456 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.last = RegenInletRH * 100.0;
4457 0 : thisError.OutputChar = format("{:.1R}", RegenInletRH * 100.0);
4458 0 : thisError.OutputCharLo = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinRegenAirInRelHum * 100.0);
4459 0 : thisError.OutputCharHi = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxRegenAirInRelHum * 100.0);
4460 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.print = true;
4461 :
4462 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.buffer1 =
4463 0 : format("{} \"{}\" - Regeneration inlet air relative humidity related to regen outlet air temperature equation is outside model "
4464 : "boundaries at {}.",
4465 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4466 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4467 0 : thisError.OutputChar);
4468 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.buffer2 =
4469 0 : format("...Model limit on regeneration inlet air relative humidity is {} to {}.", thisError.OutputCharLo, thisError.OutputCharHi);
4470 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.buffer3 = format(
4471 0 : "...Occurrence info = {}, {}, {}", state.dataEnvrn->EnvironmentName, state.dataEnvrn->CurMnDy, CreateSysTimeIntervalString(state));
4472 : } else {
4473 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumTempErr.print = false;
4474 : }
4475 :
4476 : // checking if process inlet relative humidity is within model boundaries
4477 0 : if (ProcInletRH < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInRelHum ||
4478 0 : ProcInletRH > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInRelHum) {
4479 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.last = ProcInletRH * 100.0;
4480 0 : thisError.OutputChar = format("{:.1R}", ProcInletRH * 100.0);
4481 0 : thisError.OutputCharLo = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MinProcAirInRelHum * 100.0);
4482 0 : thisError.OutputCharHi = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).T_MaxProcAirInRelHum * 100.0);
4483 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.print = true;
4484 :
4485 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.buffer1 = format(
4486 : "{} \"{}\" - Process inlet air relative humidity related to regen outlet air temperature equation is outside model boundaries at {}.",
4487 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4488 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4489 0 : thisError.OutputChar);
4490 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.buffer2 =
4491 0 : format("...Model limit on process inlet air relative humidity is {} to {}.", thisError.OutputCharLo, thisError.OutputCharHi);
4492 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.buffer3 = format(
4493 0 : "...Occurrence info = {}, {}, {}", state.dataEnvrn->EnvironmentName, state.dataEnvrn->CurMnDy, CreateSysTimeIntervalString(state));
4494 : } else {
4495 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumTempErr.print = false;
4496 : }
4497 : }
4498 :
4499 0 : void HeatExchCond::CheckModelBoundsRH_HumRatEq(EnergyPlusData &state,
4500 : Real64 const H_RegenInTemp, // current regen inlet temperature passed to eqn
4501 : Real64 const H_RegenInHumRat, // current regen inlet hum rat passed to eqn
4502 : Real64 const H_ProcInTemp, // current process inlet temperature passed to eqn
4503 : Real64 const H_ProcInHumRat, // current process inlet hum rat passed to eqn
4504 : bool const FirstHVACIteration // first HVAC iteration flag
4505 : ) const
4506 : {
4507 :
4508 : // SUBROUTINE INFORMATION:
4509 : // AUTHOR Richard Raustad, FSEC
4510 : // DATE WRITTEN January 2007
4511 : // MODIFIED na
4512 : // RE-ENGINEERED na
4513 :
4514 : // PURPOSE OF THIS SUBROUTINE:
4515 : // To verify that the empirical model's independent variables result in a relative humidity that is within the range
4516 : // of relative humidities used when creating the empirical model. Both the regeneration and process inlet are tested.
4517 :
4518 : // METHODOLOGY EMPLOYED:
4519 : // The empirical models used for simulating a desiccant enhanced cooling coil are based on a limited data set.
4520 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
4521 : // In addition, the range of relative humidities in the original data set may influence the output of the
4522 : // empirical model. This subroutine tests the relative humidities passed to the empirical model and warns the
4523 : // user if these relative humidities are out of bounds based on the limits set by the user.
4524 :
4525 : // Using/Aliasing
4526 : using General::CreateSysTimeIntervalString;
4527 :
4528 : using Psychrometrics::PsyRhFnTdbWPb;
4529 :
4530 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
4531 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
4532 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
4533 0 : auto &RegenInletRH = state.dataHeatRecovery->RegenInletRH;
4534 0 : auto &ProcInletRH = state.dataHeatRecovery->ProcInletRH;
4535 0 : auto &thisError = state.dataHeatRecovery->error5;
4536 : // current end time is compared with last to see if time step changed
4537 :
4538 0 : if (state.dataGlobal->WarmupFlag || FirstHVACIteration) return;
4539 :
4540 : // calculate end time of current time step
4541 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
4542 :
4543 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
4544 : // Wait for next time step to print warnings. If simulation iterates, print out
4545 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
4546 : // If a warning occurs and the simulation down shifts, the warning is not valid.
4547 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
4548 :
4549 : // print error when regeneration inlet relative humidity is outside model boundaries
4550 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.print) {
4551 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.count;
4552 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.count < 2) {
4553 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.buffer1);
4554 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.buffer2);
4555 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.buffer3);
4556 0 : ShowContinueError(state,
4557 : "...Using regeneration inlet air relative humidities that are outside the regeneration outlet humidity ratio "
4558 : "equation model boundaries may adversely affect desiccant model performance. Verify correct model "
4559 : "coefficients.");
4560 : } else {
4561 0 : ShowRecurringWarningErrorAtEnd(state,
4562 0 : format("{} \"{}\" - Regeneration inlet air relative humidity related to regen outlet air humidity "
4563 : "ratio equation is outside model boundaries error continues...",
4564 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4565 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4566 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.index,
4567 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.last,
4568 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.last);
4569 : }
4570 : }
4571 :
4572 : // print error when process inlet relative humidity is outside model boundaries
4573 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.print) {
4574 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.count;
4575 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.count < 2) {
4576 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.buffer1);
4577 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.buffer2);
4578 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.buffer3);
4579 0 : ShowContinueError(state,
4580 : "...Using process inlet air relative humidities that are outside the regeneration outlet humidity ratio "
4581 : "equation model boundaries may adversely affect desiccant model performance. Verify correct model "
4582 : "coefficients.");
4583 : } else {
4584 0 : ShowRecurringWarningErrorAtEnd(state,
4585 0 : format("{} \"{}\" - Process inlet air relative humidity related to regen outlet air humidity "
4586 : "ratio equation is outside model boundaries error continues...",
4587 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4588 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name),
4589 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.index,
4590 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.last,
4591 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.last);
4592 : }
4593 : }
4594 :
4595 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
4596 :
4597 : // save last system time step and last end time of current time step (used to determine if warning is valid)
4598 0 : thisError.TimeStepSysLast = TimeStepSys;
4599 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
4600 :
4601 : // Check that condition is not above saturation curve prior to next calc (PsyRhFnTdbWPb) to avoid psyc routine errors
4602 : // *
4603 : // *
4604 : // x------*---------- H_HumRat
4605 : // | *
4606 : // | *
4607 : // *----------------- PsyWFnTdpPb(Tdp,Pb)
4608 : // * |
4609 : // |
4610 : // H_Temp
4611 0 : if (H_RegenInHumRat > Psychrometrics::PsyWFnTdpPb(state, H_RegenInTemp, state.dataEnvrn->OutBaroPress) ||
4612 0 : H_ProcInHumRat > Psychrometrics::PsyWFnTdpPb(state, H_ProcInTemp, state.dataEnvrn->OutBaroPress)) {
4613 : // reset RH print flags just in case
4614 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.print = false;
4615 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.print = false;
4616 0 : return;
4617 : }
4618 :
4619 : // If regen and process inlet temperatures are the same the coil is off, do not print out of bounds warning for this case
4620 0 : if (std::abs(H_RegenInTemp - H_ProcInTemp) < SMALL) {
4621 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.print = false;
4622 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.print = false;
4623 0 : return;
4624 : }
4625 :
4626 0 : RegenInletRH = PsyRhFnTdbWPb(state, H_RegenInTemp, H_RegenInHumRat, state.dataEnvrn->OutBaroPress);
4627 0 : ProcInletRH = min(1.0, PsyRhFnTdbWPb(state, H_ProcInTemp, H_ProcInHumRat, state.dataEnvrn->OutBaroPress));
4628 :
4629 : // checking if regeneration inlet relative humidity is within model boundaries
4630 0 : if (RegenInletRH < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInRelHum ||
4631 0 : RegenInletRH > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInRelHum) {
4632 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.last = RegenInletRH * 100.0;
4633 0 : thisError.OutputChar = format("{:.1R}", RegenInletRH * 100.0);
4634 0 : thisError.OutputCharLo = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinRegenAirInRelHum * 100.0);
4635 0 : thisError.OutputCharHi = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxRegenAirInRelHum * 100.0);
4636 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.print = true;
4637 :
4638 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.buffer1 =
4639 0 : format("{} \"{}\" - Regeneration inlet air relative humidity related to regen outlet air humidity ratio equation is outside model "
4640 : "boundaries at {}.",
4641 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4642 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4643 0 : thisError.OutputChar);
4644 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.buffer2 =
4645 0 : format("...Model limit on regeneration inlet air relative humidity is {} to {}.", thisError.OutputCharLo, thisError.OutputCharHi);
4646 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.buffer3 = format(
4647 0 : "...Occurrence info = {}, {}, {}", state.dataEnvrn->EnvironmentName, state.dataEnvrn->CurMnDy, CreateSysTimeIntervalString(state));
4648 : } else {
4649 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).regenInRelHumHumRatErr.print = false;
4650 : }
4651 :
4652 : // checking if process inlet relative humidity is within model boundaries
4653 0 : if (ProcInletRH < state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInRelHum ||
4654 0 : ProcInletRH > state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInRelHum) {
4655 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.last = ProcInletRH * 100.0;
4656 0 : thisError.OutputChar = format("{:.1R}", ProcInletRH * 100.0);
4657 0 : thisError.OutputCharLo = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MinProcAirInRelHum * 100.0);
4658 0 : thisError.OutputCharHi = format("{:.1R}", state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).H_MaxProcAirInRelHum * 100.0);
4659 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.print = true;
4660 :
4661 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.buffer1 =
4662 0 : format("{} \"{}\" - Process inlet air relative humidity related to regen outlet air humidity ratio equation is outside model "
4663 : "boundaries at {}.",
4664 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).PerfType,
4665 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).Name,
4666 0 : thisError.OutputChar);
4667 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.buffer2 =
4668 0 : format("...Model limit on process inlet air relative humidity is {} to {}.", thisError.OutputCharLo, thisError.OutputCharHi);
4669 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.buffer3 = format(
4670 0 : "...Occurrence info = {}, {}, {}", state.dataEnvrn->EnvironmentName, state.dataEnvrn->CurMnDy, CreateSysTimeIntervalString(state));
4671 : } else {
4672 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).procInRelHumHumRatErr.print = false;
4673 : }
4674 : }
4675 :
4676 0 : void HeatExchCond::CheckForBalancedFlow(EnergyPlusData &state,
4677 : Real64 const ProcessInMassFlow, // current process inlet air mass flow rate (m3/s)
4678 : Real64 const RegenInMassFlow, // current regeneration inlet air mass flow rate (m3/s)
4679 : bool const FirstHVACIteration // first HVAC iteration flag
4680 : ) const
4681 : {
4682 :
4683 : // SUBROUTINE INFORMATION:
4684 : // AUTHOR Richard Raustad, FSEC
4685 : // DATE WRITTEN June 2007
4686 : // MODIFIED na
4687 : // RE-ENGINEERED na
4688 :
4689 : // PURPOSE OF THIS SUBROUTINE:
4690 : // To verify that the balanced flow desiccant heat exchanger has the same regeneration and process air flow rates.
4691 :
4692 : // METHODOLOGY EMPLOYED:
4693 : // Check that the regeneration and process air mass flow rates are within 2%.
4694 : // REFERENCES:
4695 : // na
4696 :
4697 : // Using/Aliasing
4698 : using General::CreateSysTimeIntervalString;
4699 :
4700 0 : auto &thisError = state.dataHeatRecovery->error7;
4701 :
4702 0 : Real64 SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
4703 0 : Real64 TimeStepSys = state.dataHVACGlobal->TimeStepSys;
4704 :
4705 : // current end time is compared with last to see if time step changed
4706 : Real64 ABSImbalancedFlow; // absolute value of process and regeneration air flow imbalance fraction
4707 :
4708 0 : if (state.dataGlobal->WarmupFlag || FirstHVACIteration) return;
4709 :
4710 : // calculate end time of current time step
4711 0 : thisError.CurrentEndTime = state.dataGlobal->CurrentTime + SysTimeElapsed;
4712 :
4713 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
4714 : // Wait for next time step to print warnings. If simulation iterates, print out
4715 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
4716 : // If a warning occurs and the simulation down shifts, the warning is not valid.
4717 0 : if (thisError.CurrentEndTime > thisError.CurrentEndTimeLast && TimeStepSys >= thisError.TimeStepSysLast) {
4718 :
4719 : // print error when regeneration inlet relative humidity is outside model boundaries
4720 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.print) {
4721 0 : ++state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.count;
4722 0 : if (state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.count < 2) {
4723 0 : ShowWarningError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.buffer1);
4724 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.buffer2);
4725 0 : ShowContinueError(state, state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.buffer3);
4726 : // CALL ShowContinueError(state, '...Using regeneration inlet air relative humidities that are outside the regeneration
4727 : // '&
4728 : // //'outlet humidity ratio equation model boundaries may adversely affect desiccant model performance. '&
4729 : // //'Verify correct model coefficients.')
4730 : } else {
4731 0 : ShowRecurringWarningErrorAtEnd(state,
4732 0 : format("{} \"{}\" - unbalanced air flow rate is limited to 2% error continues with the imbalanced "
4733 : "fraction statistics reported...",
4734 0 : HVAC::hxTypeNames[(int)this->type],
4735 0 : this->Name),
4736 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.index,
4737 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.last,
4738 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.last);
4739 : }
4740 : }
4741 :
4742 : } // IF(CurrentEndTime .GT. CurrentEndTimeLast .AND. TimeStepSys .GE. TimeStepSysLast)THEN
4743 :
4744 : // save last system time step and last end time of current time step (used to determine if warning is valid)
4745 0 : thisError.TimeStepSysLast = TimeStepSys;
4746 0 : thisError.CurrentEndTimeLast = thisError.CurrentEndTime;
4747 :
4748 : // checking if regeneration inlet relative humidity is within model boundaries
4749 0 : ABSImbalancedFlow = std::abs(RegenInMassFlow - ProcessInMassFlow) / RegenInMassFlow;
4750 0 : if (ABSImbalancedFlow > 0.02) {
4751 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.last = ABSImbalancedFlow;
4752 0 : thisError.OutputCharLo = format("{:.6R}", RegenInMassFlow);
4753 0 : thisError.OutputCharHi = format("{:.6R}", ProcessInMassFlow);
4754 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.print = true;
4755 :
4756 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.buffer1 =
4757 0 : format("{} \"{}\" - unbalanced air flow rate is limited to 2%.", HVAC::hxTypeNames[(int)this->type], this->Name);
4758 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.buffer2 = format(
4759 0 : "...Regeneration air mass flow rate is {} and process air mass flow rate is {}.", thisError.OutputCharLo, thisError.OutputCharHi);
4760 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.buffer3 = format(
4761 0 : "...Occurrence info = {}, {}, {}", state.dataEnvrn->EnvironmentName, state.dataEnvrn->CurMnDy, CreateSysTimeIntervalString(state));
4762 : } else {
4763 0 : state.dataHeatRecovery->BalDesDehumPerfData(this->PerfDataIndex).imbalancedFlowErr.print = false;
4764 : }
4765 : }
4766 :
4767 9 : int GetSupplyInletNode(EnergyPlusData &state,
4768 : std::string const &HXName, // must match HX names for the state.dataHeatRecovery->ExchCond type
4769 : bool &ErrorsFound // set to true if problem
4770 : )
4771 : {
4772 :
4773 : // FUNCTION INFORMATION:
4774 : // AUTHOR Richard Raustad
4775 : // DATE WRITTEN February 2007
4776 : // MODIFIED na
4777 : // RE-ENGINEERED na
4778 :
4779 : // PURPOSE OF THIS FUNCTION:
4780 : // This function looks up the given HX and returns the supply air inlet node number.
4781 : // If incorrect HX name is given, ErrorsFound is returned as true and node number as zero.
4782 :
4783 : // Obtains and Allocates heat exchanger related parameters from input file
4784 9 : if (state.dataHeatRecovery->GetInputFlag) { // First time subroutine has been entered
4785 5 : GetHeatRecoveryInput(state);
4786 5 : state.dataHeatRecovery->GetInputFlag = false;
4787 : }
4788 :
4789 9 : int const WhichHX = Util::FindItemInList(HXName, state.dataHeatRecovery->ExchCond);
4790 9 : if (WhichHX != 0) {
4791 9 : return state.dataHeatRecovery->ExchCond(WhichHX).SupInletNode;
4792 : } else {
4793 0 : ShowSevereError(state, format("GetSupplyInletNode: Could not find heat exchanger = \"{}\"", HXName));
4794 0 : ErrorsFound = true;
4795 0 : return 0;
4796 : }
4797 : }
4798 :
4799 9 : int GetSupplyOutletNode(EnergyPlusData &state,
4800 : std::string const &HXName, // must match HX names for the state.dataHeatRecovery->ExchCond type
4801 : bool &ErrorsFound // set to true if problem
4802 : )
4803 : {
4804 :
4805 : // FUNCTION INFORMATION:
4806 : // AUTHOR Richard Raustad
4807 : // DATE WRITTEN February 2007
4808 : // MODIFIED na
4809 : // RE-ENGINEERED na
4810 :
4811 : // PURPOSE OF THIS FUNCTION:
4812 : // This function looks up the given HX and returns the supply air outlet node number.
4813 : // If incorrect HX name is given, ErrorsFound is returned as true and node number as zero.
4814 :
4815 : // Obtains and Allocates heat exchanger related parameters from input file
4816 9 : if (state.dataHeatRecovery->GetInputFlag) { // First time subroutine has been entered
4817 0 : GetHeatRecoveryInput(state);
4818 0 : state.dataHeatRecovery->GetInputFlag = false;
4819 : }
4820 :
4821 9 : int const WhichHX = Util::FindItemInList(HXName, state.dataHeatRecovery->ExchCond);
4822 9 : if (WhichHX != 0) {
4823 9 : return state.dataHeatRecovery->ExchCond(WhichHX).SupOutletNode;
4824 : } else {
4825 0 : ShowSevereError(state, format("GetSupplyOutletNode: Could not find heat exchanger = \"{}\"", HXName));
4826 0 : ErrorsFound = true;
4827 0 : return 0;
4828 : }
4829 : }
4830 :
4831 7 : int GetSecondaryInletNode(EnergyPlusData &state,
4832 : std::string const &HXName, // must match HX names for the state.dataHeatRecovery->ExchCond type
4833 : bool &ErrorsFound // set to true if problem
4834 : )
4835 : {
4836 :
4837 : // FUNCTION INFORMATION:
4838 : // AUTHOR Richard Raustad
4839 : // DATE WRITTEN February 2007
4840 : // MODIFIED na
4841 : // RE-ENGINEERED na
4842 :
4843 : // PURPOSE OF THIS FUNCTION:
4844 : // This function looks up the given HX and returns the secondary air inlet node number.
4845 : // If incorrect HX name is given, ErrorsFound is returned as true and node number as zero.
4846 :
4847 : // Obtains and Allocates heat exchanger related parameters from input file
4848 7 : if (state.dataHeatRecovery->GetInputFlag) { // First time subroutine has been entered
4849 4 : GetHeatRecoveryInput(state);
4850 4 : state.dataHeatRecovery->GetInputFlag = false;
4851 : }
4852 :
4853 7 : int const WhichHX = Util::FindItemInList(HXName, state.dataHeatRecovery->ExchCond);
4854 7 : if (WhichHX != 0) {
4855 7 : return state.dataHeatRecovery->ExchCond(WhichHX).SecInletNode;
4856 : } else {
4857 0 : ShowSevereError(state, format("GetSecondaryInletNode: Could not find heat exchanger = \"{}\"", HXName));
4858 0 : ErrorsFound = true;
4859 0 : return 0;
4860 : }
4861 : }
4862 :
4863 7 : int GetSecondaryOutletNode(EnergyPlusData &state,
4864 : std::string const &HXName, // must match HX names for the state.dataHeatRecovery->ExchCond type
4865 : bool &ErrorsFound // set to true if problem
4866 : )
4867 : {
4868 :
4869 : // FUNCTION INFORMATION:
4870 : // AUTHOR Richard Raustad
4871 : // DATE WRITTEN February 2007
4872 : // MODIFIED na
4873 : // RE-ENGINEERED na
4874 :
4875 : // PURPOSE OF THIS FUNCTION:
4876 : // This function looks up the given HX assisted cooling coil and returns the secondary air outlet node number.
4877 : // If incorrect HX name is given, ErrorsFound is returned as true and node number as zero.
4878 :
4879 : // Obtains and Allocates heat exchanger related parameters from input file
4880 7 : if (state.dataHeatRecovery->GetInputFlag) { // First time subroutine has been entered
4881 0 : GetHeatRecoveryInput(state);
4882 0 : state.dataHeatRecovery->GetInputFlag = false;
4883 : }
4884 :
4885 7 : int const WhichHX = Util::FindItemInList(HXName, state.dataHeatRecovery->ExchCond);
4886 7 : if (WhichHX != 0) {
4887 7 : return state.dataHeatRecovery->ExchCond(WhichHX).SecOutletNode;
4888 : } else {
4889 0 : ShowSevereError(state, format("GetSecondaryOutletNode: Could not find heat exchanger = \"{}\"", HXName));
4890 0 : ErrorsFound = true;
4891 0 : return 0;
4892 : }
4893 : }
4894 :
4895 0 : Real64 GetSupplyAirFlowRate(EnergyPlusData &state,
4896 : std::string const &HXName, // must match HX names for the state.dataHeatRecovery->ExchCond type
4897 : bool &ErrorsFound // set to true if problem
4898 : )
4899 : {
4900 :
4901 : // FUNCTION INFORMATION:
4902 : // AUTHOR Richard Raustad
4903 : // DATE WRITTEN October 2007
4904 : // MODIFIED na
4905 : // RE-ENGINEERED na
4906 :
4907 : // PURPOSE OF THIS FUNCTION:
4908 : // This function looks up the given Generic HX and the volumetric air flow rate.
4909 : // If incorrect HX name is given, ErrorsFound is returned as true and air flow rate as zero.
4910 :
4911 : // Obtains and Allocates heat exchanger related parameters from input file
4912 0 : if (state.dataHeatRecovery->GetInputFlag) { // First time subroutine has been entered
4913 0 : GetHeatRecoveryInput(state);
4914 0 : state.dataHeatRecovery->GetInputFlag = false;
4915 : }
4916 :
4917 0 : int const WhichHX = Util::FindItemInList(HXName, state.dataHeatRecovery->ExchCond);
4918 0 : if (WhichHX != 0) {
4919 0 : return state.dataHeatRecovery->ExchCond(WhichHX).NomSupAirVolFlow;
4920 : } else {
4921 0 : ShowSevereError(state, format("GetSupplyAirFlowRate: Could not find heat exchanger = \"{}\"", HXName));
4922 0 : ShowContinueError(state, "... Supply Air Flow Rate returned as 0.");
4923 0 : ErrorsFound = true;
4924 0 : return 0.0;
4925 : }
4926 : }
4927 :
4928 0 : HVAC::HXType GetHeatExchangerObjectTypeNum(EnergyPlusData &state,
4929 : std::string const &HXName, // must match HX names for the state.dataHeatRecovery->ExchCond type
4930 : bool &ErrorsFound // set to true if problem
4931 : )
4932 : {
4933 :
4934 : // FUNCTION INFORMATION:
4935 : // AUTHOR Richard Raustad
4936 : // DATE WRITTEN October 2007
4937 : // MODIFIED na
4938 : // RE-ENGINEERED na
4939 :
4940 : // PURPOSE OF THIS FUNCTION:
4941 : // This function looks up the given Generic HX and the volumetric air flow rate.
4942 : // If incorrect HX name is given, ErrorsFound is returned as true and air flow rate as zero.
4943 :
4944 : // Obtains and Allocates heat exchanger related parameters from input file
4945 0 : if (state.dataHeatRecovery->GetInputFlag) { // First time subroutine has been entered
4946 0 : GetHeatRecoveryInput(state);
4947 0 : state.dataHeatRecovery->GetInputFlag = false;
4948 : }
4949 :
4950 0 : int const WhichHX = Util::FindItemInList(HXName, state.dataHeatRecovery->ExchCond);
4951 0 : if (WhichHX != 0) {
4952 0 : return state.dataHeatRecovery->ExchCond(WhichHX).type;
4953 : } else {
4954 0 : ShowSevereError(state, format("GetHeatExchangerObjectTypeNum: Could not find heat exchanger = \"{}\"", HXName));
4955 0 : ErrorsFound = true;
4956 0 : return HVAC::HXType::Invalid;
4957 : }
4958 : }
4959 :
4960 : } // namespace HeatRecovery
4961 :
4962 : } // namespace EnergyPlus
|