Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Autosizing/CoolingAirFlowSizing.hh>
57 : #include <EnergyPlus/Autosizing/CoolingCapacitySizing.hh>
58 : #include <EnergyPlus/Autosizing/HeatingAirFlowSizing.hh>
59 : #include <EnergyPlus/Autosizing/HeatingCapacitySizing.hh>
60 : #include <EnergyPlus/Autosizing/SystemAirFlowSizing.hh>
61 : #include <EnergyPlus/BranchNodeConnections.hh>
62 : #include <EnergyPlus/Data/EnergyPlusData.hh>
63 : #include <EnergyPlus/DataAirSystems.hh>
64 : #include <EnergyPlus/DataContaminantBalance.hh>
65 : #include <EnergyPlus/DataEnvironment.hh>
66 : #include <EnergyPlus/DataHVACGlobals.hh>
67 : #include <EnergyPlus/DataHeatBalance.hh>
68 : #include <EnergyPlus/DataLoopNode.hh>
69 : #include <EnergyPlus/DataSizing.hh>
70 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
71 : #include <EnergyPlus/DataZoneEquipment.hh>
72 : #include <EnergyPlus/Fans.hh>
73 : #include <EnergyPlus/FluidProperties.hh>
74 : #include <EnergyPlus/General.hh>
75 : #include <EnergyPlus/GeneralRoutines.hh>
76 : #include <EnergyPlus/HVACHXAssistedCoolingCoil.hh>
77 : #include <EnergyPlus/HeatingCoils.hh>
78 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
79 : #include <EnergyPlus/NodeInputManager.hh>
80 : #include <EnergyPlus/OutAirNodeManager.hh>
81 : #include <EnergyPlus/OutputProcessor.hh>
82 : #include <EnergyPlus/PlantUtilities.hh>
83 : #include <EnergyPlus/Psychrometrics.hh>
84 : #include <EnergyPlus/ScheduleManager.hh>
85 : #include <EnergyPlus/SingleDuct.hh>
86 : #include <EnergyPlus/SteamCoils.hh>
87 : #include <EnergyPlus/UnitVentilator.hh>
88 : #include <EnergyPlus/UtilityRoutines.hh>
89 : #include <EnergyPlus/WaterCoils.hh>
90 : #include <EnergyPlus/ZonePlenum.hh>
91 :
92 : namespace EnergyPlus {
93 :
94 : namespace UnitVentilator {
95 :
96 : // Module containing the routines dealing with the Unit Ventilator
97 :
98 : // MODULE INFORMATION:
99 : // AUTHOR Rick Strand
100 : // DATE WRITTEN May 2000
101 : // MODIFIED March 2001 (addition of gas and electric coils)
102 : // October 2003 (addition of cooling coil type)
103 : // MODIFIED Bereket Nigusse, FSEC, October 2013, Added cycling fan operating mode
104 :
105 : // PURPOSE OF THIS MODULE:
106 : // To simulate unit ventilators.
107 :
108 : // METHODOLOGY EMPLOYED:
109 : // Units are modeled as a collection of components: outside air mixer,
110 : // fan, heating coil and/or cooling coil plus an integrated control
111 : // algorithm that adjusts the hot or cold water flow to meet the zone
112 : // load. Outside air mixing is handled locally as either fixed percent
113 : // or as attempting to meet a prescribed mixed air temperature.
114 :
115 : // REFERENCES:
116 : // ASHRAE Systems and Equipment Handbook (SI), 1996. pp. 31.1-31.3
117 : // Fred Buhl's fan coil module (FanCoilUnits.cc)
118 :
119 : static constexpr std::string_view fluidNameSteam("STEAM");
120 : static constexpr std::string_view fluidNameWater("WATER");
121 : static constexpr std::array<std::string_view, static_cast<int>(CoilsUsed::Num)> CoilsUsedNamesUC = {
122 : "NONE", "HEATINGANDCOOLING", "HEATING", "COOLING"};
123 : static constexpr std::array<std::string_view, static_cast<int>(OAControl::Num)> OAControlNamesUC = {
124 : "VARIABLEPERCENT", "FIXEDTEMPERATURE", "FIXEDAMOUNT"};
125 : static constexpr std::array<std::string_view, static_cast<int>(HeatCoilType::Num)> HeatCoilTypeNamesUC = {
126 : "COIL:HEATING:ELECTRIC", "COIL:HEATING:FUEL", "COIL:HEATING:WATER", "COIL:HEATING:STEAM"};
127 : static constexpr std::array<std::string_view, static_cast<int>(CoolCoilType::Num)> CoolCoilTypeNamesUC = {
128 : "COIL:COOLING:WATER", "COIL:COOLING:WATER:DETAILEDGEOMETRY", "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED"};
129 :
130 162760 : void SimUnitVentilator(EnergyPlusData &state,
131 : std::string_view CompName, // name of the fan coil unit
132 : int const ZoneNum, // number of zone being served
133 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
134 : Real64 &PowerMet, // Sensible power supplied (W)
135 : Real64 &LatOutputProvided, // Latent add/removal supplied by window AC (kg/s), dehumid = negative
136 : int &CompIndex)
137 : {
138 :
139 : // SUBROUTINE INFORMATION:
140 : // AUTHOR Rick Strand
141 : // DATE WRITTEN May 2000
142 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided)
143 :
144 : // PURPOSE OF THIS SUBROUTINE:
145 : // This is the main driver subroutine for the Unit Ventilator simulation.
146 :
147 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
148 : int UnitVentNum; // index of unit ventilator being simulated
149 :
150 162760 : if (state.dataUnitVentilators->GetUnitVentilatorInputFlag) {
151 5 : GetUnitVentilatorInput(state);
152 5 : state.dataUnitVentilators->GetUnitVentilatorInputFlag = false;
153 : }
154 :
155 : // Find the correct Unit Ventilator Equipment
156 162760 : if (CompIndex == 0) {
157 25 : UnitVentNum = Util::FindItemInList(CompName, state.dataUnitVentilators->UnitVent);
158 25 : if (UnitVentNum == 0) {
159 0 : ShowFatalError(state, format("SimUnitVentilator: Unit not found={}", CompName));
160 : }
161 25 : CompIndex = UnitVentNum;
162 : } else {
163 162735 : UnitVentNum = CompIndex;
164 162735 : if (UnitVentNum > state.dataUnitVentilators->NumOfUnitVents || UnitVentNum < 1) {
165 0 : ShowFatalError(state,
166 0 : format("SimUnitVentilator: Invalid CompIndex passed={}, Number of Units={}, Entered Unit name={}",
167 : UnitVentNum,
168 0 : state.dataUnitVentilators->NumOfUnitVents,
169 : CompName));
170 : }
171 162735 : if (state.dataUnitVentilators->CheckEquipName(UnitVentNum)) {
172 25 : if (CompName != state.dataUnitVentilators->UnitVent(UnitVentNum).Name) {
173 0 : ShowFatalError(state,
174 0 : format("SimUnitVentilator: Invalid CompIndex passed={}, Unit name={}, stored Unit Name for that index={}",
175 : UnitVentNum,
176 : CompName,
177 0 : state.dataUnitVentilators->UnitVent(UnitVentNum).Name));
178 : }
179 25 : state.dataUnitVentilators->CheckEquipName(UnitVentNum) = false;
180 : }
181 : }
182 :
183 162760 : state.dataSize->ZoneEqUnitVent = true;
184 :
185 162760 : InitUnitVentilator(state, UnitVentNum, FirstHVACIteration, ZoneNum);
186 :
187 162760 : CalcUnitVentilator(state, UnitVentNum, ZoneNum, FirstHVACIteration, PowerMet, LatOutputProvided);
188 :
189 162760 : ReportUnitVentilator(state, UnitVentNum);
190 :
191 162760 : state.dataSize->ZoneEqUnitVent = false;
192 162760 : }
193 :
194 5 : void GetUnitVentilatorInput(EnergyPlusData &state)
195 : {
196 :
197 : // SUBROUTINE INFORMATION:
198 : // AUTHOR Rick Strand
199 : // DATE WRITTEN May 2000
200 : // MODIFIED Chandan Sharma, FSEC, March 2011: Added zone sys avail manager
201 : // Bereket Nigusse, FSEC, April 2011: eliminated input node names
202 : // & added fan object type
203 :
204 : // PURPOSE OF THIS SUBROUTINE:
205 : // This subroutine obtains the input for unit ventilators and sets
206 : // up the appropriate derived type.
207 :
208 : // REFERENCES:
209 : // Fred Buhl's fan coil module (FanCoilUnits.cc)
210 :
211 : // SUBROUTINE PARAMETER DEFINITIONS:
212 : static constexpr std::string_view RoutineName("GetUnitVentilatorInput: "); // include trailing blank
213 : static constexpr std::string_view routineName = "GetUnitVentilatorInput"; // include trailing blank
214 :
215 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
216 5 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
217 : int IOStatus; // Used in GetObjectItem
218 : bool IsNotOK; // TRUE if there was a problem with a list name
219 : int NumFields; // Total number of fields in object
220 : int NumAlphas; // Number of Alphas for each GetObjectItem call
221 : int NumNumbers; // Number of Numbers for each GetObjectItem call
222 : bool IsValid; // Set for outside air node check
223 5 : bool errFlag(false); // interim error flag
224 5 : std::string cCoolingCoilType; // Cooling coil object type
225 5 : std::string cHeatingCoilType; // Heating coil object type
226 : Real64 FanVolFlow; // volumetric flow rate of fan
227 5 : Array1D_string Alphas; // Alpha items for object
228 5 : Array1D<Real64> Numbers; // Numeric items for object
229 5 : Array1D_string cAlphaFields; // Alpha field names
230 5 : Array1D_string cNumericFields; // Numeric field names
231 5 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
232 5 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
233 : bool ZoneNodeNotFound; // used in error checking
234 :
235 : // Figure out how many unit ventilators there are in the input file
236 :
237 5 : std::string CurrentModuleObject = state.dataUnitVentilators->cMO_UnitVentilator;
238 5 : state.dataUnitVentilators->NumOfUnitVents = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
239 5 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNumbers);
240 :
241 5 : Alphas.allocate(NumAlphas);
242 5 : Numbers.dimension(NumNumbers, 0.0);
243 5 : cAlphaFields.allocate(NumAlphas);
244 5 : cNumericFields.allocate(NumNumbers);
245 5 : lAlphaBlanks.dimension(NumAlphas, true);
246 5 : lNumericBlanks.dimension(NumNumbers, true);
247 :
248 : // Allocate the local derived type and do one-time initializations for all parts of it
249 5 : if (state.dataUnitVentilators->NumOfUnitVents > 0) {
250 5 : state.dataUnitVentilators->UnitVent.allocate(state.dataUnitVentilators->NumOfUnitVents);
251 5 : state.dataUnitVentilators->CheckEquipName.allocate(state.dataUnitVentilators->NumOfUnitVents);
252 5 : state.dataUnitVentilators->UnitVentNumericFields.allocate(state.dataUnitVentilators->NumOfUnitVents);
253 : }
254 5 : state.dataUnitVentilators->CheckEquipName = true;
255 :
256 : // Loop over all of the unit ventilators found in the input file.
257 30 : for (int UnitVentNum = 1; UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents; ++UnitVentNum) {
258 :
259 25 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
260 :
261 25 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
262 : CurrentModuleObject,
263 : UnitVentNum,
264 : Alphas,
265 : NumAlphas,
266 : Numbers,
267 : NumNumbers,
268 : IOStatus,
269 : lNumericBlanks,
270 : lAlphaBlanks,
271 : cAlphaFields,
272 : cNumericFields);
273 :
274 25 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
275 :
276 25 : state.dataUnitVentilators->UnitVentNumericFields(UnitVentNum).FieldNames.allocate(NumNumbers);
277 25 : state.dataUnitVentilators->UnitVentNumericFields(UnitVentNum).FieldNames = "";
278 25 : state.dataUnitVentilators->UnitVentNumericFields(UnitVentNum).FieldNames = cNumericFields;
279 25 : Util::IsNameEmpty(state, Alphas(1), CurrentModuleObject, ErrorsFound);
280 :
281 25 : unitVent.Name = Alphas(1);
282 25 : if (lAlphaBlanks(2)) {
283 0 : unitVent.SchedPtr = ScheduleManager::ScheduleAlwaysOn;
284 : } else {
285 25 : unitVent.SchedPtr = ScheduleManager::GetScheduleIndex(state, Alphas(2)); // convert schedule name to pointer
286 25 : if (unitVent.SchedPtr == 0) {
287 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, Alphas(1)));
288 0 : ShowContinueError(state, format("not found: {}=\"{}\".", cAlphaFields(2), Alphas(2)));
289 0 : ErrorsFound = true;
290 : }
291 : }
292 :
293 25 : unitVent.MaxAirVolFlow = Numbers(1);
294 :
295 : // Outside air information:
296 25 : unitVent.MinOutAirVolFlow = Numbers(2);
297 :
298 25 : unitVent.MinOASchedPtr = ScheduleManager::GetScheduleIndex(state, Alphas(4)); // convert schedule name to pointer
299 25 : if (unitVent.MinOASchedPtr == 0) {
300 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
301 0 : ShowContinueError(state, format("not found: {}=\"{}\".", cAlphaFields(4), Alphas(4)));
302 0 : ErrorsFound = true;
303 : }
304 :
305 25 : unitVent.OutAirVolFlow = Numbers(3);
306 25 : cCoolingCoilType = "";
307 25 : cHeatingCoilType = "";
308 :
309 : {
310 25 : unitVent.OAControlType = (OAControl)getEnumValue(OAControlNamesUC, Alphas(3));
311 25 : switch (unitVent.OAControlType) {
312 25 : case OAControl::VariablePercent:
313 : case OAControl::FixedAmount: {
314 25 : unitVent.MaxOASchedPtr = ScheduleManager::GetScheduleIndex(state, Alphas(5)); // convert schedule name to pointer
315 25 : if (unitVent.MaxOASchedPtr == 0) {
316 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
317 0 : ShowContinueError(state, format("not found: {}=\"{}\".", cAlphaFields(5), Alphas(5)));
318 0 : ErrorsFound = true;
319 25 : } else if (!ScheduleManager::CheckScheduleValueMinMax(state, unitVent.MaxOASchedPtr, ">=", 0.0, "<=", 1.0)) {
320 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
321 0 : ShowContinueError(state, format("out of range [0,1]: {}=\"{}\".", cAlphaFields(5), Alphas(5)));
322 0 : ErrorsFound = true;
323 : }
324 25 : } break;
325 0 : case OAControl::FixedTemperature: {
326 0 : unitVent.TempSchedPtr = ScheduleManager::GetScheduleIndex(state, Alphas(5)); // convert schedule name to pointer
327 0 : if (unitVent.TempSchedPtr == 0) {
328 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
329 0 : ShowContinueError(state, format("not found: {}=\"{}\".", cAlphaFields(5), Alphas(5)));
330 0 : ErrorsFound = true;
331 : }
332 0 : } break;
333 0 : default: {
334 0 : assert(false);
335 : } break;
336 : }
337 : }
338 :
339 : // Main air nodes (except outside air node):
340 : // For node connections, this object is both a parent and a non-parent, because the
341 : // OA mixing box is not called out as a separate component, its nodes must be connected
342 : // as ObjectIsNotParent. But for the fan and coils, the nodes are connected as ObjectIsParent
343 : // To support the diagramming tool, the unit ventilator inlet node must appear both as
344 : // an inlet to the unit ventilator parent object and as an inlet to the implied
345 : // non-parent OA mixing box within the unit ventilator.
346 : // Because there is overlap between the nodes that are parent and non-parent, use a different
347 : // object type for the non parent nodes
348 25 : unitVent.AirInNode = NodeInputManager::GetOnlySingleNode(state,
349 25 : Alphas(6),
350 : ErrorsFound,
351 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitVentilator,
352 25 : Alphas(1),
353 : DataLoopNode::NodeFluidType::Air,
354 : DataLoopNode::ConnectionType::Inlet,
355 : NodeInputManager::CompFluidStream::Primary,
356 : DataLoopNode::ObjectIsParent);
357 25 : unitVent.AirOutNode = NodeInputManager::GetOnlySingleNode(state,
358 25 : Alphas(7),
359 : ErrorsFound,
360 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitVentilator,
361 25 : Alphas(1),
362 : DataLoopNode::NodeFluidType::Air,
363 : DataLoopNode::ConnectionType::Outlet,
364 : NodeInputManager::CompFluidStream::Primary,
365 : DataLoopNode::ObjectIsParent);
366 :
367 : // Get AirTerminal mixer data
368 25 : SingleDuct::GetATMixer(state,
369 25 : unitVent.Name,
370 25 : unitVent.ATMixerName,
371 25 : unitVent.ATMixerIndex,
372 25 : unitVent.ATMixerType,
373 25 : unitVent.ATMixerPriNode,
374 25 : unitVent.ATMixerSecNode,
375 25 : unitVent.ATMixerOutNode,
376 : unitVent.AirOutNode);
377 25 : if (unitVent.ATMixerType == HVAC::MixerType::InletSide || unitVent.ATMixerType == HVAC::MixerType::SupplySide) {
378 5 : unitVent.ATMixerExists = true;
379 : }
380 25 : unitVent.ZonePtr =
381 25 : DataZoneEquipment::GetZoneEquipControlledZoneNum(state, DataZoneEquipment::ZoneEquipType::UnitVentilator, unitVent.Name);
382 25 : if (unitVent.ZonePtr == 0) {
383 0 : ErrorsFound = true;
384 : }
385 :
386 25 : if (!unitVent.ATMixerExists) {
387 20 : unitVent.AirInNode = NodeInputManager::GetOnlySingleNode(state,
388 20 : Alphas(6),
389 : ErrorsFound,
390 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitVentilator,
391 40 : Alphas(1) + "-OA MIXER",
392 : DataLoopNode::NodeFluidType::Air,
393 : DataLoopNode::ConnectionType::Inlet,
394 : NodeInputManager::CompFluidStream::Primary,
395 : DataLoopNode::ObjectIsNotParent);
396 : }
397 :
398 25 : unitVent.FanName = Alphas(12);
399 25 : unitVent.fanType = static_cast<HVAC::FanType>(getEnumValue(HVAC::fanTypeNamesUC, Alphas(11)));
400 :
401 25 : if (unitVent.fanType != HVAC::FanType::Constant && unitVent.fanType != HVAC::FanType::VAV && unitVent.fanType != HVAC::FanType::OnOff &&
402 10 : unitVent.fanType != HVAC::FanType::SystemModel) {
403 0 : ShowSevereInvalidKey(state, eoh, cAlphaFields(11), Alphas(11));
404 0 : ShowContinueError(state, "Fan Type must be Fan:OnOff, Fan:ConstantVolume, Fan:VariableVolume, or Fan:SystemModel");
405 0 : ErrorsFound = true;
406 :
407 25 : } else if ((unitVent.Fan_Index = Fans::GetFanIndex(state, unitVent.FanName)) == 0) {
408 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(12), unitVent.FanName);
409 0 : ErrorsFound = true;
410 :
411 : } else {
412 25 : auto *fan = state.dataFans->fans(unitVent.Fan_Index);
413 25 : unitVent.FanOutletNode = fan->outletNodeNum;
414 25 : unitVent.FanAvailSchedPtr = fan->availSchedNum; // Get the fan's availability schedule
415 25 : FanVolFlow = fan->maxAirFlowRate;
416 25 : if (FanVolFlow != DataSizing::AutoSize && unitVent.MaxAirVolFlow != DataSizing::AutoSize && FanVolFlow < unitVent.MaxAirVolFlow) {
417 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
418 0 : ShowContinueError(state,
419 0 : format("...air flow rate [{:.7T}] in fan object {} is less than the unit ventilator maximum "
420 : "supply air flow rate [{:.7T}].",
421 : FanVolFlow,
422 0 : unitVent.FanName,
423 0 : unitVent.MaxAirVolFlow));
424 0 : ShowContinueError(state,
425 : "...the fan flow rate must be greater than or equal to the unit ventilator maximum supply "
426 : "air flow rate.");
427 0 : ErrorsFound = true;
428 25 : } else if (FanVolFlow == DataSizing::AutoSize && unitVent.MaxAirVolFlow != DataSizing::AutoSize) {
429 0 : ShowWarningError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
430 0 : ShowContinueError(state, "...the fan flow rate is autosized while the unit ventilator flow rate is not.");
431 0 : ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
432 25 : } else if (FanVolFlow != DataSizing::AutoSize && unitVent.MaxAirVolFlow == DataSizing::AutoSize) {
433 0 : ShowWarningError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
434 0 : ShowContinueError(state, "...the unit ventilator flow rate is autosized while the fan flow rate is not.");
435 0 : ShowContinueError(state, "...this can lead to unexpected results where the fan flow rate is less than required.");
436 : }
437 : }
438 :
439 : // For node connections, this object is both a parent and a non-parent, because the
440 : // OA mixing box is not called out as a separate component, its nodes must be connected
441 : // as ObjectIsNotParent. But for the fan and coils, the nodes are connected as ObjectIsParent
442 : // Because there is overlap between the nodes that are parent and non-parent, use a different
443 : // object type for the non parent nodes
444 : // Set connection type to 'OutdoorAir', because this is hardwired to OA conditions
445 25 : if (!unitVent.ATMixerExists) {
446 20 : unitVent.OutsideAirNode = NodeInputManager::GetOnlySingleNode(state,
447 20 : Alphas(8),
448 : ErrorsFound,
449 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitVentilator,
450 40 : Alphas(1) + "-OA MIXER",
451 : DataLoopNode::NodeFluidType::Air,
452 : DataLoopNode::ConnectionType::OutsideAirReference,
453 : NodeInputManager::CompFluidStream::Primary,
454 : DataLoopNode::ObjectIsNotParent);
455 20 : if (!lAlphaBlanks(8)) {
456 20 : OutAirNodeManager::CheckAndAddAirNodeNumber(state, unitVent.OutsideAirNode, IsValid);
457 20 : if (!IsValid) {
458 0 : ShowWarningError(state, format("{}{} Adding {}={}", RoutineName, CurrentModuleObject, cAlphaFields(8), Alphas(8)));
459 : }
460 : }
461 :
462 20 : unitVent.AirReliefNode = NodeInputManager::GetOnlySingleNode(state,
463 20 : Alphas(9),
464 : ErrorsFound,
465 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitVentilator,
466 40 : Alphas(1) + "-OA MIXER",
467 : DataLoopNode::NodeFluidType::Air,
468 : DataLoopNode::ConnectionType::ReliefAir,
469 : NodeInputManager::CompFluidStream::Primary,
470 : DataLoopNode::ObjectIsNotParent);
471 :
472 20 : unitVent.OAMixerOutNode = NodeInputManager::GetOnlySingleNode(state,
473 20 : Alphas(10),
474 : ErrorsFound,
475 : DataLoopNode::ConnectionObjectType::ZoneHVACUnitVentilator,
476 40 : Alphas(1) + "-OA MIXER",
477 : DataLoopNode::NodeFluidType::Air,
478 : DataLoopNode::ConnectionType::Outlet,
479 : NodeInputManager::CompFluidStream::Primary,
480 : DataLoopNode::ObjectIsNotParent);
481 : } else {
482 5 : unitVent.OutsideAirNode = unitVent.ATMixerPriNode;
483 5 : unitVent.OAMixerOutNode = unitVent.ATMixerOutNode;
484 5 : if (!lAlphaBlanks(8) || !lAlphaBlanks(9) || !lAlphaBlanks(10)) {
485 0 : ShowWarningError(state, format("{}{}=\"{}\" is connected to central DOA.", RoutineName, CurrentModuleObject, unitVent.Name));
486 0 : if (!lAlphaBlanks(8)) {
487 0 : ShowContinueError(state, format("... input field {} should have been blank. Specified = {}", cAlphaFields(8), Alphas(8)));
488 : }
489 0 : if (!lAlphaBlanks(9)) {
490 0 : ShowContinueError(state, format("... input field {} should have been blank. Specified = {}", cAlphaFields(9), Alphas(9)));
491 : }
492 0 : if (!lAlphaBlanks(10)) {
493 0 : ShowContinueError(state, format("... input field {} should have been blank. Specified = {}", cAlphaFields(10), Alphas(10)));
494 : }
495 : }
496 : }
497 :
498 25 : if (unitVent.OAControlType == OAControl::FixedAmount) {
499 3 : unitVent.OutAirVolFlow = unitVent.MinOutAirVolFlow;
500 3 : unitVent.MaxOASchedPtr = unitVent.MinOASchedPtr;
501 : }
502 :
503 25 : if (!unitVent.ATMixerExists) {
504 : // Add fan to component sets array
505 60 : BranchNodeConnections::SetUpCompSets(state,
506 : CurrentModuleObject,
507 : unitVent.Name,
508 20 : Alphas(11),
509 : unitVent.FanName,
510 20 : state.dataLoopNodes->NodeID(unitVent.OAMixerOutNode),
511 20 : state.dataLoopNodes->NodeID(unitVent.FanOutletNode));
512 : } else {
513 5 : if (unitVent.ATMixerType == HVAC::MixerType::InletSide) {
514 : // Add fan to component sets array
515 9 : BranchNodeConnections::SetUpCompSets(state,
516 : CurrentModuleObject,
517 : unitVent.Name,
518 3 : Alphas(11),
519 : unitVent.FanName,
520 3 : state.dataLoopNodes->NodeID(unitVent.ATMixerOutNode),
521 3 : state.dataLoopNodes->NodeID(unitVent.FanOutletNode));
522 : }
523 5 : if (unitVent.ATMixerType == HVAC::MixerType::SupplySide) {
524 : // Add fan to component sets array
525 6 : BranchNodeConnections::SetUpCompSets(state,
526 : CurrentModuleObject,
527 : unitVent.Name,
528 2 : Alphas(11),
529 : unitVent.FanName,
530 2 : state.dataLoopNodes->NodeID(unitVent.AirInNode),
531 2 : state.dataLoopNodes->NodeID(unitVent.FanOutletNode));
532 : }
533 : }
534 :
535 25 : if (!lAlphaBlanks(19)) {
536 0 : unitVent.AvailManagerListName = Alphas(19);
537 : }
538 :
539 25 : unitVent.HVACSizingIndex = 0;
540 25 : if (!lAlphaBlanks(20)) {
541 0 : unitVent.HVACSizingIndex = Util::FindItemInList(Alphas(20), state.dataSize->ZoneHVACSizing);
542 0 : if (unitVent.HVACSizingIndex == 0) {
543 0 : ShowSevereError(state, format("{} = {} not found.", cAlphaFields(20), Alphas(20)));
544 0 : ShowContinueError(state, format("Occurs in {} = \"{}\".", CurrentModuleObject, unitVent.Name));
545 0 : ErrorsFound = true;
546 : }
547 : }
548 :
549 25 : unitVent.CoilOption = (CoilsUsed)getEnumValue(CoilsUsedNamesUC, Alphas(13));
550 :
551 25 : unitVent.FanSchedPtr = ScheduleManager::GetScheduleIndex(state, Alphas(14));
552 : // Default to cycling fan when fan mode schedule is not present
553 25 : if (!lAlphaBlanks(14) && unitVent.FanSchedPtr == 0) {
554 0 : ShowSevereError(state, format("{} \"{}\" {} not found: {}", CurrentModuleObject, unitVent.Name, cAlphaFields(14), Alphas(14)));
555 0 : ErrorsFound = true;
556 25 : } else if (lAlphaBlanks(14)) {
557 16 : if (unitVent.fanType == HVAC::FanType::OnOff || unitVent.fanType == HVAC::FanType::SystemModel) {
558 1 : unitVent.fanOp = HVAC::FanOp::Cycling;
559 : } else {
560 15 : unitVent.fanOp = HVAC::FanOp::Continuous;
561 : }
562 : }
563 :
564 : // Check fan's schedule for cycling fan operation if constant volume fan is used
565 25 : if (unitVent.FanSchedPtr > 0 && unitVent.fanType == HVAC::FanType::Constant) {
566 0 : if (!ScheduleManager::CheckScheduleValueMinMax(state, unitVent.FanSchedPtr, ">", 0.0, "<=", 1.0)) {
567 0 : ShowSevereError(state, format("{} = {}", CurrentModuleObject, Alphas(1)));
568 0 : ShowContinueError(state, format("For {} = {}", cAlphaFields(11), Alphas(11)));
569 0 : ShowContinueError(state, "Fan operating mode must be continuous (fan operating mode schedule values > 0).");
570 0 : ShowContinueError(state, format("Error found in {} = {}", cAlphaFields(14), Alphas(14)));
571 0 : ShowContinueError(state, "...schedule values must be (>0., <=1.)");
572 0 : ErrorsFound = true;
573 : }
574 : }
575 :
576 : // Get Coil information
577 25 : if (unitVent.CoilOption == CoilsUsed::Both || unitVent.CoilOption == CoilsUsed::Heating) {
578 23 : if ((!lAlphaBlanks(16))) {
579 23 : unitVent.HCoilPresent = true;
580 23 : cHeatingCoilType = Alphas(15);
581 23 : unitVent.HCoilTypeCh = cHeatingCoilType;
582 23 : unitVent.HCoilType = (HeatCoilType)getEnumValue(HeatCoilTypeNamesUC, cHeatingCoilType);
583 23 : unitVent.HeatingCoilType = (DataPlant::PlantEquipmentType)getEnumValue(DataPlant::PlantEquipTypeNamesUC, cHeatingCoilType);
584 :
585 23 : unitVent.HCoilName = Alphas(16);
586 23 : ValidateComponent(state, cHeatingCoilType, unitVent.HCoilName, IsNotOK, CurrentModuleObject);
587 23 : if (IsNotOK) {
588 0 : ShowContinueError(state, format("...specified in {} = \"{}\".", CurrentModuleObject, unitVent.Name));
589 0 : ErrorsFound = true;
590 : } else {
591 : // The heating coil control node is necessary for a hot water coil, but not necessary for electric or gas.
592 23 : if (unitVent.HCoilType == HeatCoilType::Water || unitVent.HCoilType == HeatCoilType::Steam) {
593 : // mine the hot water or steam node from the coil object
594 15 : if (unitVent.HCoilType == HeatCoilType::Water) {
595 15 : unitVent.HCoil_Index = WaterCoils::GetCompIndex(state, WaterCoils::CoilModel::HeatingSimple, unitVent.HCoilName);
596 15 : unitVent.HotControlNode = state.dataWaterCoils->WaterCoil(unitVent.HCoil_Index).WaterInletNodeNum;
597 15 : unitVent.MaxVolHotWaterFlow = state.dataWaterCoils->WaterCoil(unitVent.HCoil_Index).MaxWaterVolFlowRate;
598 : // Could probably remove MaxVolHotSteamFlow here
599 15 : unitVent.MaxVolHotSteamFlow = unitVent.MaxVolHotWaterFlow;
600 : } else {
601 0 : unitVent.HCoil_Index = SteamCoils::GetCompIndex(state, unitVent.HCoilName);
602 0 : unitVent.HotControlNode = state.dataSteamCoils->SteamCoil(unitVent.HCoil_Index).SteamInletNodeNum;
603 : // Could probably replace MaxVolHotWaterFlow here with MaxVolHotSteamFlow
604 0 : unitVent.MaxVolHotWaterFlow = state.dataSteamCoils->SteamCoil(unitVent.HCoil_Index).MaxSteamVolFlowRate;
605 : // unitVent.MaxVolHotWaterFlow =
606 : // SteamCoils::GetCoilMaxWaterFlowRate(state, "Coil:Heating:Steam", unitVent.HCoilName, ErrorsFound);
607 0 : unitVent.MaxVolHotSteamFlow = unitVent.MaxVolHotWaterFlow;
608 : }
609 : }
610 : }
611 :
612 23 : unitVent.HotControlOffset = Numbers(4);
613 : // Set default convergence tolerance
614 23 : if (unitVent.HotControlOffset <= 0.0) {
615 0 : unitVent.HotControlOffset = 0.001;
616 : }
617 : } else { // heating coil is required for these options
618 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
619 0 : ShowContinueError(state, format("a heating coil is required for {}=\"{}\".", cAlphaFields(13), Alphas(13)));
620 0 : ErrorsFound = true;
621 : } // IF (.NOT. lAlphaBlanks(15)) THEN - from the start of heating coil information
622 : } // is option both or heating only
623 :
624 25 : if (unitVent.CoilOption == CoilsUsed::Both || unitVent.CoilOption == CoilsUsed::Cooling) {
625 9 : if (!lAlphaBlanks(18)) {
626 9 : unitVent.CCoilPresent = true;
627 9 : errFlag = false;
628 :
629 9 : cCoolingCoilType = Alphas(17);
630 9 : unitVent.CCoilTypeCh = cCoolingCoilType;
631 9 : unitVent.CCoilType = (CoolCoilType)getEnumValue(CoolCoilTypeNamesUC, cCoolingCoilType);
632 9 : unitVent.CoolingCoilType = (DataPlant::PlantEquipmentType)getEnumValue(DataPlant::PlantEquipTypeNamesUC, cCoolingCoilType);
633 9 : unitVent.CCoilPlantName = Alphas(18);
634 :
635 9 : if (cCoolingCoilType == "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED") {
636 0 : unitVent.CCoilType = CoolCoilType::HXAssisted;
637 0 : HVACHXAssistedCoolingCoil::GetHXCoilTypeAndName(
638 0 : state, cCoolingCoilType, Alphas(18), ErrorsFound, unitVent.CCoilPlantType, unitVent.CCoilPlantName);
639 0 : if (Util::SameString(unitVent.CCoilPlantType, "Coil:Cooling:Water")) {
640 0 : unitVent.CoolingCoilType = DataPlant::PlantEquipmentType::CoilWaterCooling;
641 0 : } else if (Util::SameString(unitVent.CCoilPlantType, "Coil:Cooling:Water:DetailedGeometry")) {
642 0 : unitVent.CoolingCoilType = DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling;
643 : } else {
644 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
645 0 : ShowContinueError(state, format("For: {}=\"{}\".", cAlphaFields(17), Alphas(17)));
646 0 : ShowContinueError(state, format("Invalid Coil Type={}, Name={}", unitVent.CCoilPlantType, unitVent.CCoilPlantName));
647 0 : ShowContinueError(state,
648 : "must be \"Coil:Cooling:Water\", \"Coil:Cooling:Water:DetailedGeometry\" or, "
649 : "\"CoilSystem:Cooling:Water:HeatExchangerAssisted\".");
650 0 : errFlag = true;
651 0 : ErrorsFound = true;
652 : }
653 : }
654 :
655 9 : if (!errFlag) {
656 9 : unitVent.CCoilName = Alphas(18);
657 9 : ValidateComponent(state, cCoolingCoilType, unitVent.CCoilName, IsNotOK, CurrentModuleObject);
658 9 : if (IsNotOK) {
659 0 : ShowContinueError(state, format("...specified in {} = \"{}\".", CurrentModuleObject, unitVent.Name));
660 0 : ErrorsFound = true;
661 : } else {
662 9 : if (unitVent.CCoilType != CoolCoilType::HXAssisted) {
663 9 : WaterCoils::CoilModel coilModel = WaterCoils::CoilModel::CoolingSimple;
664 9 : if (unitVent.CCoilType == CoolCoilType::Detailed) coilModel = WaterCoils::CoilModel::CoolingDetailed;
665 9 : unitVent.CCoil_Index = WaterCoils::GetCompIndex(state, coilModel, unitVent.CCoilName);
666 9 : unitVent.ColdControlNode = state.dataWaterCoils->WaterCoil(unitVent.CCoil_Index).WaterInletNodeNum;
667 9 : unitVent.MaxVolColdWaterFlow = state.dataWaterCoils->WaterCoil(unitVent.CCoil_Index).MaxWaterVolFlowRate;
668 : } else {
669 : // special case, call the parent and return the child water inlet node and water volume flow rate
670 0 : unitVent.ColdControlNode =
671 0 : HVACHXAssistedCoolingCoil::GetCoilWaterInletNode(state, unitVent.CCoilTypeCh, unitVent.CCoilName, errFlag);
672 0 : unitVent.MaxVolColdWaterFlow = HVACHXAssistedCoolingCoil::GetCoilMaxWaterFlowRate(
673 0 : state, "CoilSystem:Cooling:Water:HeatExchangerAssisted", unitVent.CCoilName, errFlag);
674 : }
675 : // Other error checks should trap before it gets to this point in the code, but including just in case.
676 9 : if (errFlag) {
677 0 : ShowContinueError(state, format("...specified in {} = \"{}\".", CurrentModuleObject, unitVent.Name));
678 0 : ErrorsFound = true;
679 : }
680 : }
681 : }
682 :
683 9 : unitVent.MinVolColdWaterFlow = 0.0;
684 9 : unitVent.ColdControlOffset = Numbers(5);
685 : // Set default convergence tolerance
686 9 : if (unitVent.ColdControlOffset <= 0.0) {
687 0 : unitVent.ColdControlOffset = 0.001;
688 : }
689 : } else { // Cooling Coil is required for this/these options
690 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
691 0 : ShowContinueError(state, format("a cooling coil is required for {}=\"{}\".", cAlphaFields(13), Alphas(13)));
692 0 : ErrorsFound = true;
693 : } // IF (.NOT. lAlphaBlanks(17)) THEN - from the start of cooling coil information
694 : }
695 25 : if (!unitVent.ATMixerExists) {
696 : // check that unit ventilator air inlet node is the same as a zone exhaust node
697 20 : ZoneNodeNotFound = true;
698 20 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumExhaustNodes; ++NodeNum) {
699 20 : if (unitVent.AirInNode == state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).ExhaustNode(NodeNum)) {
700 20 : ZoneNodeNotFound = false;
701 20 : break;
702 : }
703 : }
704 20 : if (ZoneNodeNotFound) {
705 0 : bool InletNodeFound = false;
706 0 : if (unitVent.ZonePtr > 0) {
707 0 : InletNodeFound = ZonePlenum::ValidateInducedNode(state,
708 : unitVent.AirInNode,
709 0 : state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumReturnNodes,
710 0 : state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).ReturnNode);
711 : }
712 0 : if (!InletNodeFound) {
713 0 : ShowSevereError(state,
714 0 : format("{} = \"{}\". Unit ventilator air inlet node name must be the same either as a zone exhaust node name "
715 : "or an induce air node in ZoePlenum.",
716 : CurrentModuleObject,
717 0 : unitVent.Name));
718 0 : ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
719 0 : ShowContinueError(state, "..Induced Air Outlet Node name is specified in AirLoopHVAC:ReturnPlenum object.");
720 0 : ShowContinueError(state,
721 0 : format("..Unit ventilator unit air inlet node name = {}", state.dataLoopNodes->NodeID(unitVent.AirInNode)));
722 0 : ErrorsFound = true;
723 : }
724 : }
725 : // check that unit ventilator air outlet node is the same as a zone inlet node.
726 20 : ZoneNodeNotFound = true;
727 20 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumInletNodes; ++NodeNum) {
728 20 : if (unitVent.AirOutNode == state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).InletNode(NodeNum)) {
729 20 : ZoneNodeNotFound = false;
730 20 : break;
731 : }
732 : }
733 20 : if (ZoneNodeNotFound) {
734 0 : ShowSevereError(state,
735 0 : format("{} = \"{}\". Unit ventilator air outlet node name must be the same as a zone inlet node name.",
736 : CurrentModuleObject,
737 0 : unitVent.Name));
738 0 : ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
739 0 : ShowContinueError(state, format("..Unit ventilator air outlet node name = {}", state.dataLoopNodes->NodeID(unitVent.AirOutNode)));
740 0 : ErrorsFound = true;
741 : }
742 : } else {
743 5 : if (unitVent.ATMixerType == HVAC::MixerType::InletSide) {
744 : // check that unit ventilator air outlet node is the same as a zone inlet node.
745 3 : ZoneNodeNotFound = true;
746 3 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumInletNodes; ++NodeNum) {
747 3 : if (unitVent.AirOutNode == state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).InletNode(NodeNum)) {
748 3 : ZoneNodeNotFound = false;
749 3 : break;
750 : }
751 : }
752 3 : if (ZoneNodeNotFound) {
753 0 : ShowSevereError(state,
754 0 : format("{} = \"{}\". Unit ventilator air outlet node name must be the same as a zone inlet node name.",
755 : CurrentModuleObject,
756 0 : unitVent.Name));
757 0 : ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
758 0 : ShowContinueError(state,
759 0 : format("..Unit ventilator air outlet node name = {}", state.dataLoopNodes->NodeID(unitVent.AirOutNode)));
760 0 : ErrorsFound = true;
761 : }
762 :
763 : // check that the air mixer out node is the unit ventilator air inlet node
764 3 : if (unitVent.AirInNode != unitVent.ATMixerOutNode) {
765 0 : ShowSevereError(state,
766 0 : format("{} = \"{}\". unit ventilator air inlet node name must be the same as the mixer outlet node name.",
767 : CurrentModuleObject,
768 0 : unitVent.Name));
769 0 : ShowContinueError(state, "..Air terminal mixer outlet node name is specified in AirTerminal:SingleDuct:Mixer object.");
770 0 : ShowContinueError(state,
771 0 : format("..Unit ventilator air inlet node name = {}", state.dataLoopNodes->NodeID(unitVent.AirInNode)));
772 0 : ErrorsFound = true;
773 : }
774 : }
775 5 : if (unitVent.ATMixerType == HVAC::MixerType::SupplySide) {
776 : // check that the mixer secondary air node is the unit ventilator air outlet node
777 2 : if (unitVent.AirOutNode != unitVent.ATMixerSecNode) {
778 0 : ShowSevereError(
779 : state,
780 0 : format("{} = \"{}\". unit ventilator air outlet node name must be the same as the mixer secondary air inlet node name.",
781 : CurrentModuleObject,
782 0 : unitVent.Name));
783 0 : ShowContinueError(state, "..Air terminal mixer secondary node name is specified in AirTerminal:SingleDuct:Mixer object.");
784 0 : ShowContinueError(state,
785 0 : format("..Unit ventilator air outlet node name = {}", state.dataLoopNodes->NodeID(unitVent.AirOutNode)));
786 0 : ErrorsFound = true;
787 : }
788 :
789 : // check that air teminal mixer outlet node is the same as a zone inlet node.
790 2 : ZoneNodeNotFound = true;
791 2 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumInletNodes; ++NodeNum) {
792 2 : if (unitVent.ATMixerOutNode == state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).InletNode(NodeNum)) {
793 2 : ZoneNodeNotFound = false;
794 2 : break;
795 : }
796 : }
797 2 : if (ZoneNodeNotFound) {
798 0 : ShowSevereError(state,
799 0 : format("{} = \"{}\". Air mixer outlet node name must be the same as a zone inlet node name.",
800 : CurrentModuleObject,
801 0 : unitVent.Name));
802 0 : ShowContinueError(state, "..Zone inlet node name is specified in ZoneHVAC:EquipmentConnections object.");
803 0 : ShowContinueError(state,
804 0 : format("..Air terminal mixer outlet node name = {}", state.dataLoopNodes->NodeID(unitVent.ATMixerOutNode)));
805 0 : ErrorsFound = true;
806 : } else {
807 2 : bool ExhastNodeNotFound = true;
808 : // check exhaust node
809 2 : for (int NodeNum = 1; NodeNum <= state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumExhaustNodes; ++NodeNum) {
810 2 : if (unitVent.AirInNode == state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).ExhaustNode(NodeNum)) {
811 2 : ExhastNodeNotFound = false;
812 2 : break;
813 : }
814 : }
815 : // check induce node
816 2 : if (ExhastNodeNotFound) {
817 0 : bool InletNodeFound = false;
818 0 : if (unitVent.ZonePtr > 0) {
819 : InletNodeFound =
820 0 : ZonePlenum::ValidateInducedNode(state,
821 : unitVent.AirInNode,
822 0 : state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).NumReturnNodes,
823 0 : state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).ReturnNode);
824 : }
825 0 : if (!InletNodeFound) {
826 0 : ShowSevereError(state, format("{}{}=\"{}\".", RoutineName, CurrentModuleObject, unitVent.Name));
827 0 : ShowContinueError(
828 : state,
829 : "..UnitVentilator inlet node name must be the same as either a zone exhaust node name or an induced "
830 : "air node in ZonePlenum.");
831 0 : ShowContinueError(state, "..Zone exhaust node name is specified in ZoneHVAC:EquipmentConnections object.");
832 0 : ShowContinueError(state, "..Induced Air Outlet Node name is specified in AirLoopHVAC:ReturnPlenum object.");
833 0 : ShowContinueError(state,
834 0 : format("..UnitVentilator inlet node name = {}", state.dataLoopNodes->NodeID(unitVent.AirInNode)));
835 0 : ErrorsFound = true;
836 : }
837 : }
838 : }
839 : }
840 : }
841 : {
842 25 : switch (unitVent.CoilOption) {
843 9 : case CoilsUsed::Both: {
844 : // Add cooling coil to component sets array when present
845 18 : BranchNodeConnections::SetUpCompSets(state,
846 : CurrentModuleObject,
847 : unitVent.Name,
848 : cCoolingCoilType,
849 : unitVent.CCoilName,
850 9 : state.dataLoopNodes->NodeID(unitVent.FanOutletNode),
851 : "UNDEFINED");
852 :
853 : // Add heating coil to component sets array when cooling coil present
854 18 : BranchNodeConnections::SetUpCompSets(state,
855 : CurrentModuleObject,
856 : unitVent.Name,
857 : cHeatingCoilType,
858 : unitVent.HCoilName,
859 : "UNDEFINED",
860 9 : state.dataLoopNodes->NodeID(unitVent.AirOutNode));
861 9 : } break;
862 14 : case CoilsUsed::Heating: {
863 : // Add heating coil to component sets array when no cooling coil present
864 28 : BranchNodeConnections::SetUpCompSets(state,
865 : CurrentModuleObject,
866 : unitVent.Name,
867 : cHeatingCoilType,
868 : unitVent.HCoilName,
869 14 : state.dataLoopNodes->NodeID(unitVent.FanOutletNode),
870 14 : state.dataLoopNodes->NodeID(unitVent.AirOutNode));
871 14 : } break;
872 0 : case CoilsUsed::Cooling: {
873 : // Add cooling coil to component sets array when no heating coil present
874 0 : BranchNodeConnections::SetUpCompSets(state,
875 : CurrentModuleObject,
876 : unitVent.Name,
877 : cCoolingCoilType,
878 : unitVent.CCoilName,
879 0 : state.dataLoopNodes->NodeID(unitVent.FanOutletNode),
880 0 : state.dataLoopNodes->NodeID(unitVent.AirOutNode));
881 0 : } break;
882 2 : default: {
883 2 : } break;
884 : }
885 : }
886 : } // ...loop over all of the unit ventilators found in the input file
887 :
888 5 : Alphas.deallocate();
889 5 : Numbers.deallocate();
890 5 : cAlphaFields.deallocate();
891 5 : cNumericFields.deallocate();
892 5 : lAlphaBlanks.deallocate();
893 5 : lNumericBlanks.deallocate();
894 :
895 5 : if (ErrorsFound) ShowFatalError(state, format("{}Errors found in input.", RoutineName));
896 :
897 : // Setup Report variables for the Unit Ventilators, CurrentModuleObject='ZoneHVAC:UnitVentilator'
898 30 : for (int UnitVentNum = 1; UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents; ++UnitVentNum) {
899 :
900 25 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
901 :
902 50 : SetupOutputVariable(state,
903 : "Zone Unit Ventilator Heating Rate",
904 : Constant::Units::W,
905 25 : unitVent.HeatPower,
906 : OutputProcessor::TimeStepType::System,
907 : OutputProcessor::StoreType::Average,
908 25 : unitVent.Name);
909 50 : SetupOutputVariable(state,
910 : "Zone Unit Ventilator Heating Energy",
911 : Constant::Units::J,
912 25 : unitVent.HeatEnergy,
913 : OutputProcessor::TimeStepType::System,
914 : OutputProcessor::StoreType::Sum,
915 25 : unitVent.Name);
916 50 : SetupOutputVariable(state,
917 : "Zone Unit Ventilator Total Cooling Rate",
918 : Constant::Units::W,
919 25 : unitVent.TotCoolPower,
920 : OutputProcessor::TimeStepType::System,
921 : OutputProcessor::StoreType::Average,
922 25 : unitVent.Name);
923 50 : SetupOutputVariable(state,
924 : "Zone Unit Ventilator Total Cooling Energy",
925 : Constant::Units::J,
926 25 : unitVent.TotCoolEnergy,
927 : OutputProcessor::TimeStepType::System,
928 : OutputProcessor::StoreType::Sum,
929 25 : unitVent.Name);
930 50 : SetupOutputVariable(state,
931 : "Zone Unit Ventilator Sensible Cooling Rate",
932 : Constant::Units::W,
933 25 : unitVent.SensCoolPower,
934 : OutputProcessor::TimeStepType::System,
935 : OutputProcessor::StoreType::Average,
936 25 : unitVent.Name);
937 50 : SetupOutputVariable(state,
938 : "Zone Unit Ventilator Sensible Cooling Energy",
939 : Constant::Units::J,
940 25 : unitVent.SensCoolEnergy,
941 : OutputProcessor::TimeStepType::System,
942 : OutputProcessor::StoreType::Sum,
943 25 : unitVent.Name);
944 50 : SetupOutputVariable(state,
945 : "Zone Unit Ventilator Fan Electricity Rate",
946 : Constant::Units::W,
947 25 : unitVent.ElecPower,
948 : OutputProcessor::TimeStepType::System,
949 : OutputProcessor::StoreType::Average,
950 25 : unitVent.Name);
951 : // Note that the unit vent fan electric is NOT metered because this value is already metered through the fan component
952 50 : SetupOutputVariable(state,
953 : "Zone Unit Ventilator Fan Electricity Energy",
954 : Constant::Units::J,
955 25 : unitVent.ElecEnergy,
956 : OutputProcessor::TimeStepType::System,
957 : OutputProcessor::StoreType::Sum,
958 25 : unitVent.Name);
959 25 : SetupOutputVariable(state,
960 : "Zone Unit Ventilator Fan Availability Status",
961 : Constant::Units::None,
962 25 : (int &)unitVent.availStatus,
963 : OutputProcessor::TimeStepType::System,
964 : OutputProcessor::StoreType::Average,
965 25 : unitVent.Name);
966 25 : if (unitVent.fanType == HVAC::FanType::OnOff) {
967 0 : SetupOutputVariable(state,
968 : "Zone Unit Ventilator Fan Part Load Ratio",
969 : Constant::Units::None,
970 0 : unitVent.FanPartLoadRatio,
971 : OutputProcessor::TimeStepType::System,
972 : OutputProcessor::StoreType::Average,
973 0 : unitVent.Name);
974 : }
975 : }
976 :
977 30 : for (int UnitVentNum = 1; UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents; ++UnitVentNum) {
978 :
979 25 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
980 25 : auto &coilReportObj = state.dataRptCoilSelection->coilSelectionReportObj;
981 :
982 25 : if (unitVent.HCoilPresent) {
983 23 : coilReportObj->setCoilSupplyFanInfo(
984 23 : state, unitVent.HCoilName, unitVent.HCoilTypeCh, unitVent.FanName, unitVent.fanType, unitVent.Fan_Index);
985 : }
986 25 : if (unitVent.CCoilPresent) {
987 9 : coilReportObj->setCoilSupplyFanInfo(
988 9 : state, unitVent.CCoilName, unitVent.CCoilTypeCh, unitVent.FanName, unitVent.fanType, unitVent.Fan_Index);
989 : }
990 : }
991 5 : }
992 :
993 162760 : void InitUnitVentilator(EnergyPlusData &state,
994 : int const UnitVentNum, // index for the current unit ventilator
995 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
996 : int const ZoneNum // number of zone being served
997 : )
998 : {
999 :
1000 : // SUBROUTINE INFORMATION:
1001 : // AUTHOR Rick Strand
1002 : // DATE WRITTEN May 2000
1003 : // MODIFIED Chandan Sharma, FSEC, March 2011: Added zone sys avail manager
1004 :
1005 : // PURPOSE OF THIS SUBROUTINE:
1006 : // This subroutine initializes all of the data elements which are necessary
1007 : // to simulate a unit ventilator.
1008 :
1009 : // METHODOLOGY EMPLOYED:
1010 : // Uses the status flags to trigger initializations.
1011 :
1012 162760 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
1013 :
1014 : static constexpr std::string_view RoutineName("InitUnitVentilator");
1015 :
1016 162760 : bool SetMassFlowRateToZero = false; // TRUE when mass flow rates need to be set to zero
1017 :
1018 : // Do the one time initializations
1019 162760 : if (state.dataUnitVentilators->MyOneTimeFlag) {
1020 :
1021 5 : state.dataUnitVentilators->MyEnvrnFlag.allocate(state.dataUnitVentilators->NumOfUnitVents);
1022 5 : state.dataUnitVentilators->MySizeFlag.allocate(state.dataUnitVentilators->NumOfUnitVents);
1023 5 : state.dataUnitVentilators->MyPlantScanFlag.allocate(state.dataUnitVentilators->NumOfUnitVents);
1024 5 : state.dataUnitVentilators->MyZoneEqFlag.allocate(state.dataUnitVentilators->NumOfUnitVents);
1025 5 : state.dataUnitVentilators->MyEnvrnFlag = true;
1026 5 : state.dataUnitVentilators->MySizeFlag = true;
1027 5 : state.dataUnitVentilators->MyPlantScanFlag = true;
1028 5 : state.dataUnitVentilators->MyZoneEqFlag = true;
1029 5 : state.dataUnitVentilators->MyOneTimeFlag = false;
1030 : }
1031 :
1032 162760 : if (allocated(state.dataAvail->ZoneComp)) {
1033 162760 : auto &availMgr = state.dataAvail->ZoneComp(DataZoneEquipment::ZoneEquipType::UnitVentilator).ZoneCompAvailMgrs(UnitVentNum);
1034 162760 : if (state.dataUnitVentilators->MyZoneEqFlag(UnitVentNum)) { // initialize the name of each availability manager list and zone number
1035 25 : availMgr.AvailManagerListName = unitVent.AvailManagerListName;
1036 25 : availMgr.ZoneNum = ZoneNum;
1037 25 : state.dataUnitVentilators->MyZoneEqFlag(UnitVentNum) = false;
1038 : }
1039 162760 : unitVent.availStatus = availMgr.availStatus;
1040 : }
1041 :
1042 162760 : if (state.dataUnitVentilators->MyPlantScanFlag(UnitVentNum) && allocated(state.dataPlnt->PlantLoop)) {
1043 25 : if ((unitVent.HeatingCoilType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
1044 10 : (unitVent.HeatingCoilType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
1045 15 : bool errFlag = false;
1046 30 : PlantUtilities::ScanPlantLoopsForObject(
1047 15 : state, unitVent.HCoilName, unitVent.HeatingCoilType, unitVent.HWplantLoc, errFlag, _, _, _, _, _);
1048 15 : if (errFlag) {
1049 0 : ShowContinueError(state, format("Reference Unit=\"{}\", type=ZoneHVAC:UnitVentilator", unitVent.Name));
1050 0 : ShowFatalError(state, "InitUnitVentilator: Program terminated due to previous condition(s).");
1051 : }
1052 :
1053 15 : unitVent.HotCoilOutNodeNum = DataPlant::CompData::getPlantComponent(state, unitVent.HWplantLoc).NodeNumOut;
1054 : }
1055 25 : if ((unitVent.CoolingCoilType == DataPlant::PlantEquipmentType::CoilWaterCooling) ||
1056 16 : (unitVent.CoolingCoilType == DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling)) {
1057 9 : bool errFlag = false;
1058 18 : PlantUtilities::ScanPlantLoopsForObject(
1059 9 : state, unitVent.CCoilPlantName, unitVent.CoolingCoilType, unitVent.CWPlantLoc, errFlag, _, _, _, _, _);
1060 9 : if (errFlag) {
1061 0 : ShowContinueError(state, format("Reference Unit=\"{}\", type=ZoneHVAC:UnitVentilator", unitVent.Name));
1062 0 : ShowFatalError(state, "InitUnitVentilator: Program terminated due to previous condition(s).");
1063 : }
1064 :
1065 9 : unitVent.ColdCoilOutNodeNum = DataPlant::CompData::getPlantComponent(state, unitVent.CWPlantLoc).NodeNumOut;
1066 9 : } else {
1067 16 : if (unitVent.CCoilPresent)
1068 0 : ShowFatalError(state, format("InitUnitVentilator: Unit={}, invalid cooling coil type. Program terminated.", unitVent.Name));
1069 : }
1070 25 : state.dataUnitVentilators->MyPlantScanFlag(UnitVentNum) = false;
1071 162735 : } else if (state.dataUnitVentilators->MyPlantScanFlag(UnitVentNum) && !state.dataGlobal->AnyPlantInModel) {
1072 0 : state.dataUnitVentilators->MyPlantScanFlag(UnitVentNum) = false;
1073 : }
1074 :
1075 162760 : if (!state.dataUnitVentilators->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
1076 5 : state.dataUnitVentilators->ZoneEquipmentListChecked = true;
1077 30 : for (int Loop = 1; Loop <= state.dataUnitVentilators->NumOfUnitVents; ++Loop) {
1078 25 : if (DataZoneEquipment::CheckZoneEquipmentList(state, "ZoneHVAC:UnitVentilator", state.dataUnitVentilators->UnitVent(Loop).Name))
1079 25 : continue;
1080 0 : ShowSevereError(
1081 : state,
1082 0 : format("InitUnitVentilator: Unit=[UNIT VENTILATOR,{}] is not on any ZoneHVAC:EquipmentList. It will not be simulated.",
1083 0 : state.dataUnitVentilators->UnitVent(Loop).Name));
1084 : }
1085 : }
1086 :
1087 162785 : if (!state.dataGlobal->SysSizingCalc && state.dataUnitVentilators->MySizeFlag(UnitVentNum) &&
1088 25 : !state.dataUnitVentilators->MyPlantScanFlag(UnitVentNum)) {
1089 :
1090 25 : SizeUnitVentilator(state, UnitVentNum);
1091 :
1092 25 : state.dataUnitVentilators->MySizeFlag(UnitVentNum) = false;
1093 : }
1094 :
1095 162760 : int InNode = unitVent.AirInNode;
1096 162760 : int OutNode = unitVent.AirOutNode;
1097 162760 : int OutsideAirNode = unitVent.OutsideAirNode;
1098 162760 : int AirRelNode = unitVent.AirReliefNode;
1099 :
1100 : // Do the one time initializations
1101 162905 : if (state.dataGlobal->BeginEnvrnFlag && state.dataUnitVentilators->MyEnvrnFlag(UnitVentNum) &&
1102 145 : !state.dataUnitVentilators->MyPlantScanFlag(UnitVentNum)) {
1103 145 : Real64 RhoAir = state.dataEnvrn->StdRhoAir;
1104 :
1105 : // set the mass flow rates from the input volume flow rates
1106 145 : unitVent.MaxAirMassFlow = RhoAir * unitVent.MaxAirVolFlow;
1107 145 : unitVent.OutAirMassFlow = RhoAir * unitVent.OutAirVolFlow;
1108 145 : unitVent.MinOutAirMassFlow = RhoAir * unitVent.MinOutAirVolFlow;
1109 145 : if (unitVent.OutAirMassFlow > unitVent.MaxAirMassFlow) {
1110 0 : unitVent.OutAirMassFlow = unitVent.MaxAirMassFlow;
1111 0 : unitVent.MinOutAirMassFlow = unitVent.OutAirMassFlow * (unitVent.MinOutAirVolFlow / unitVent.OutAirVolFlow);
1112 0 : ShowWarningError(state,
1113 0 : format("Outdoor air mass flow rate higher than unit flow rate, reset to unit flow rate for {}", unitVent.Name));
1114 : }
1115 :
1116 : // set the node max and min mass flow rates
1117 145 : state.dataLoopNodes->Node(OutsideAirNode).MassFlowRateMax = unitVent.OutAirMassFlow;
1118 145 : state.dataLoopNodes->Node(OutsideAirNode).MassFlowRateMin = 0.0;
1119 :
1120 145 : state.dataLoopNodes->Node(OutNode).MassFlowRateMax = unitVent.MaxAirMassFlow;
1121 145 : state.dataLoopNodes->Node(OutNode).MassFlowRateMin = 0.0;
1122 :
1123 145 : state.dataLoopNodes->Node(InNode).MassFlowRateMax = unitVent.MaxAirMassFlow;
1124 145 : state.dataLoopNodes->Node(InNode).MassFlowRateMin = 0.0;
1125 :
1126 145 : if (unitVent.HCoilPresent) { // Only initialize these if a heating coil is actually present
1127 :
1128 135 : if (unitVent.HCoilType == HeatCoilType::Water) {
1129 :
1130 87 : Real64 rho = FluidProperties::GetDensityGlycol(state,
1131 87 : state.dataPlnt->PlantLoop(unitVent.HWplantLoc.loopNum).FluidName,
1132 : Constant::HWInitConvTemp,
1133 87 : state.dataPlnt->PlantLoop(unitVent.HWplantLoc.loopNum).FluidIndex,
1134 : RoutineName);
1135 :
1136 87 : unitVent.MaxHotWaterFlow = rho * unitVent.MaxVolHotWaterFlow;
1137 87 : unitVent.MinHotWaterFlow = rho * unitVent.MinVolHotWaterFlow;
1138 :
1139 87 : PlantUtilities::InitComponentNodes(
1140 : state, unitVent.MinHotWaterFlow, unitVent.MaxHotWaterFlow, unitVent.HotControlNode, unitVent.HotCoilOutNodeNum);
1141 : }
1142 135 : if (unitVent.HCoilType == HeatCoilType::Steam) {
1143 0 : Real64 TempSteamIn = 100.00;
1144 : Real64 SteamDensity =
1145 0 : FluidProperties::GetSatDensityRefrig(state, fluidNameSteam, TempSteamIn, 1.0, unitVent.HCoil_FluidIndex, RoutineName);
1146 0 : unitVent.MaxHotSteamFlow = SteamDensity * unitVent.MaxVolHotSteamFlow;
1147 0 : unitVent.MinHotSteamFlow = SteamDensity * unitVent.MinVolHotSteamFlow;
1148 :
1149 0 : PlantUtilities::InitComponentNodes(
1150 : state, unitVent.MinHotSteamFlow, unitVent.MaxHotSteamFlow, unitVent.HotControlNode, unitVent.HotCoilOutNodeNum);
1151 : }
1152 : } //(UnitVent(UnitVentNum)%HCoilPresent)
1153 :
1154 145 : if (unitVent.CCoilPresent) { // Only initialize these if a cooling coil is actually present
1155 53 : Real64 rho = FluidProperties::GetDensityGlycol(state,
1156 53 : state.dataPlnt->PlantLoop(unitVent.CWPlantLoc.loopNum).FluidName,
1157 : 5.0,
1158 53 : state.dataPlnt->PlantLoop(unitVent.CWPlantLoc.loopNum).FluidIndex,
1159 : RoutineName);
1160 :
1161 53 : unitVent.MaxColdWaterFlow = rho * unitVent.MaxVolColdWaterFlow;
1162 53 : unitVent.MinColdWaterFlow = rho * unitVent.MinVolColdWaterFlow;
1163 53 : PlantUtilities::InitComponentNodes(
1164 : state, unitVent.MinColdWaterFlow, unitVent.MaxColdWaterFlow, unitVent.ColdControlNode, unitVent.ColdCoilOutNodeNum);
1165 : }
1166 145 : state.dataUnitVentilators->MyEnvrnFlag(UnitVentNum) = false;
1167 : } // ...end start of environment inits
1168 :
1169 162760 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataUnitVentilators->MyEnvrnFlag(UnitVentNum) = true;
1170 :
1171 : // These initializations are done every iteration...
1172 :
1173 162760 : state.dataUnitVentilators->QZnReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // zone load needed
1174 162760 : unitVent.FanPartLoadRatio = 0.0;
1175 :
1176 162760 : if (unitVent.FanSchedPtr > 0) {
1177 58303 : if (ScheduleManager::GetCurrentScheduleValue(state, unitVent.FanSchedPtr) == 0.0) {
1178 0 : unitVent.fanOp = HVAC::FanOp::Cycling;
1179 : } else {
1180 58303 : unitVent.fanOp = HVAC::FanOp::Continuous;
1181 : }
1182 : }
1183 :
1184 162760 : if (ScheduleManager::GetCurrentScheduleValue(state, unitVent.SchedPtr) > 0) {
1185 325380 : if ((ScheduleManager::GetCurrentScheduleValue(state, unitVent.FanAvailSchedPtr) > 0 || state.dataHVACGlobal->TurnFansOn) &&
1186 162690 : !state.dataHVACGlobal->TurnFansOff) {
1187 316407 : if ((std::abs(state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired) < HVAC::SmallLoad) ||
1188 153717 : (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum))) {
1189 100522 : SetMassFlowRateToZero = true;
1190 : }
1191 : } else {
1192 0 : SetMassFlowRateToZero = true;
1193 : }
1194 : } else {
1195 70 : SetMassFlowRateToZero = true;
1196 : }
1197 :
1198 162760 : auto &inNode(state.dataLoopNodes->Node(InNode));
1199 162760 : auto &outNode(state.dataLoopNodes->Node(OutNode));
1200 162760 : auto &oaNode(state.dataLoopNodes->Node(OutsideAirNode));
1201 162760 : if (SetMassFlowRateToZero) {
1202 100592 : inNode.MassFlowRate = 0.0;
1203 100592 : inNode.MassFlowRateMaxAvail = 0.0;
1204 100592 : inNode.MassFlowRateMinAvail = 0.0;
1205 100592 : outNode.MassFlowRate = 0.0;
1206 100592 : outNode.MassFlowRateMaxAvail = 0.0;
1207 100592 : outNode.MassFlowRateMinAvail = 0.0;
1208 100592 : oaNode.MassFlowRate = 0.0;
1209 100592 : oaNode.MassFlowRateMaxAvail = 0.0;
1210 100592 : oaNode.MassFlowRateMinAvail = 0.0;
1211 100592 : if (!unitVent.ATMixerExists) {
1212 74959 : auto &relNode(state.dataLoopNodes->Node(AirRelNode));
1213 74959 : relNode.MassFlowRate = 0.0;
1214 74959 : relNode.MassFlowRateMaxAvail = 0.0;
1215 74959 : relNode.MassFlowRateMinAvail = 0.0;
1216 : }
1217 : } else {
1218 62168 : inNode.MassFlowRate = unitVent.MaxAirMassFlow;
1219 62168 : inNode.MassFlowRateMaxAvail = unitVent.MaxAirMassFlow;
1220 62168 : inNode.MassFlowRateMinAvail = unitVent.MaxAirMassFlow;
1221 62168 : outNode.MassFlowRate = unitVent.MaxAirMassFlow;
1222 62168 : outNode.MassFlowRateMaxAvail = unitVent.MaxAirMassFlow;
1223 62168 : outNode.MassFlowRateMinAvail = unitVent.MaxAirMassFlow;
1224 62168 : oaNode.MassFlowRate = unitVent.OutAirMassFlow;
1225 62168 : oaNode.MassFlowRateMaxAvail = unitVent.OutAirMassFlow;
1226 62168 : oaNode.MassFlowRateMinAvail = unitVent.OutAirMassFlow;
1227 62168 : if (!unitVent.ATMixerExists) {
1228 43016 : auto &relNode(state.dataLoopNodes->Node(AirRelNode));
1229 43016 : relNode.MassFlowRate = unitVent.OutAirMassFlow;
1230 43016 : relNode.MassFlowRateMaxAvail = unitVent.OutAirMassFlow;
1231 43016 : relNode.MassFlowRateMinAvail = unitVent.OutAirMassFlow;
1232 : }
1233 : }
1234 :
1235 : // Initialize the relief air (same as inlet conditions to the unit ventilator...
1236 : // Note that mass flow rates will be taken care of later.
1237 162760 : if (!unitVent.ATMixerExists) {
1238 117975 : state.dataLoopNodes->Node(AirRelNode) = state.dataLoopNodes->Node(InNode);
1239 : }
1240 162760 : state.dataUnitVentilators->OAMassFlowRate = 0.0;
1241 :
1242 : // Just in case the unit is off and conditions do not get sent through
1243 : // the unit for some reason, set the outlet conditions equal to the inlet
1244 : // conditions of the unit ventilator
1245 162760 : outNode.Temp = inNode.Temp;
1246 162760 : outNode.Press = inNode.Press;
1247 162760 : outNode.HumRat = inNode.HumRat;
1248 162760 : outNode.Enthalpy = inNode.Enthalpy;
1249 :
1250 : // These initializations only need to be done once at the start of the iterations...
1251 162760 : if (FirstHVACIteration) {
1252 : // Initialize the outside air conditions...
1253 75660 : if (!unitVent.ATMixerExists) {
1254 59050 : state.dataLoopNodes->Node(OutsideAirNode).Temp = state.dataLoopNodes->Node(OutsideAirNode).OutAirDryBulb;
1255 : }
1256 : }
1257 162760 : }
1258 :
1259 25 : void SizeUnitVentilator(EnergyPlusData &state, int const UnitVentNum)
1260 : {
1261 :
1262 : // SUBROUTINE INFORMATION:
1263 : // AUTHOR Fred Buhl
1264 : // DATE WRITTEN February 2002
1265 : // MODIFIED August 2013 Daeho Kang, add component sizing table entries
1266 : // July 2014, B. Nigusse, added scalable sizing
1267 :
1268 : // PURPOSE OF THIS SUBROUTINE:
1269 : // This subroutine is for sizing Unit Ventilator components for which flow rates have not been
1270 : // specified in the input.
1271 :
1272 : // METHODOLOGY EMPLOYED:
1273 : // Obtains flow rates from the zone sizing arrays and plant sizing data.
1274 :
1275 25 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
1276 25 : auto &ZoneEqSizing = state.dataSize->ZoneEqSizing(state.dataSize->CurZoneEqNum);
1277 :
1278 : // SUBROUTINE PARAMETER DEFINITIONS:
1279 : static constexpr std::string_view RoutineName("SizeUnitVentilator");
1280 :
1281 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1282 25 : int PltSizCoolNum = 0; // index of plant sizing object for 1st cooling loop
1283 25 : Real64 DesCoolingLoad = 0.0;
1284 25 : Real64 DesHeatingLoad = 0.0;
1285 25 : Real64 TempSteamIn = 0.0;
1286 25 : Real64 EnthSteamInDry = 0.0;
1287 25 : Real64 EnthSteamOutWet = 0.0;
1288 25 : Real64 LatentHeatSteam = 0.0;
1289 25 : Real64 SteamDensity = 0.0;
1290 25 : int CoilWaterOutletNode = 0;
1291 25 : int CoilSteamOutletNode = 0;
1292 25 : std::string CoolingCoilName;
1293 25 : std::string CoolingCoilType;
1294 25 : Real64 rho = 0.0;
1295 25 : Real64 Cp = 0.0;
1296 :
1297 : Real64 TempSize; // autosized value of coil input field
1298 : int SizingMethod; // Integer representation of sizing method name (e.g., CoolingAirflowSizing, HeatingAirflowSizing, CoolingCapacitySizing,
1299 : // HeatingCapacitySizing, etc.)
1300 : bool PrintFlag; // TRUE when sizing information is reported in the eio file
1301 25 : int SAFMethod(0); // supply air flow rate sizing method (SupplyAirFlowRate, FlowPerFloorArea, FractionOfAutosizedCoolingAirflow,
1302 : // FractionOfAutosizedHeatingAirflow ...)
1303 25 : int CapSizingMethod(0); // capacity sizing methods (HeatingDesignCapacity, CapacityPerFloorArea, FractionOfAutosizedCoolingCapacity, and
1304 : // FractionOfAutosizedHeatingCapacity )
1305 : Real64 WaterCoilSizDeltaT; // water coil deltaT for design water flow rate autosizing
1306 :
1307 25 : int PltSizHeatNum = 0;
1308 25 : bool ErrorsFound = false;
1309 25 : bool IsAutoSize = false;
1310 25 : Real64 OutAirVolFlowDes = 0.0;
1311 25 : Real64 OutAirVolFlowUser = 0.0;
1312 25 : Real64 MinOutAirVolFlowDes = 0.0;
1313 25 : Real64 MinOutAirVolFlowUser = 0.0;
1314 25 : Real64 MaxVolHotWaterFlowDes = 0.0;
1315 25 : Real64 MaxVolHotWaterFlowUser = 0.0;
1316 25 : Real64 MaxVolHotSteamFlowDes = 0.0;
1317 25 : Real64 MaxVolHotSteamFlowUser = 0.0;
1318 25 : Real64 MaxVolColdWaterFlowDes = 0.0;
1319 25 : Real64 MaxVolColdWaterFlowUser = 0.0;
1320 25 : Real64 CoolingAirVolFlowScalable = 0.0;
1321 25 : Real64 HeatingAirVolFlowScalable = 0.0;
1322 25 : state.dataSize->DataScalableSizingON = false;
1323 25 : state.dataSize->DataScalableCapSizingON = false;
1324 25 : std::string CompType = state.dataUnitVentilators->cMO_UnitVentilator;
1325 25 : std::string CompName = unitVent.Name;
1326 25 : state.dataSize->DataZoneNumber = unitVent.ZonePtr;
1327 25 : bool DoWaterCoilSizing = false;
1328 :
1329 25 : state.dataSize->DataFanType = unitVent.fanType;
1330 25 : state.dataSize->DataFanIndex = unitVent.Fan_Index;
1331 : // unit ventilator is always blow thru
1332 25 : state.dataSize->DataFanPlacement = HVAC::FanPlace::BlowThru;
1333 :
1334 25 : state.dataSize->ZoneCoolingOnlyFan = (unitVent.CoilOption == CoilsUsed::Both) || (unitVent.CoilOption == CoilsUsed::Cooling);
1335 25 : state.dataSize->ZoneHeatingOnlyFan = (unitVent.CoilOption == CoilsUsed::Both) || (unitVent.CoilOption == CoilsUsed::Heating);
1336 :
1337 25 : if (state.dataSize->CurZoneEqNum > 0) {
1338 25 : if (unitVent.HVACSizingIndex > 0) {
1339 0 : auto &zoneHVACSizing = state.dataSize->ZoneHVACSizing(unitVent.HVACSizingIndex);
1340 :
1341 : // initialize OA flow for sizing other inputs (e.g., inlet temp, capacity, etc.)
1342 0 : if (unitVent.OutAirVolFlow == DataSizing::AutoSize) {
1343 0 : ZoneEqSizing.OAVolFlow = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1344 : } else {
1345 0 : ZoneEqSizing.OAVolFlow = unitVent.OutAirVolFlow;
1346 : }
1347 0 : if (unitVent.ATMixerExists) { // set up ATMixer conditions for scalable capacity sizing
1348 0 : ZoneEqSizing.OAVolFlow = 0.0; // Equipment OA flow should always be 0 when ATMixer is used
1349 0 : SingleDuct::setATMixerSizingProperties(state, unitVent.ATMixerIndex, unitVent.ZonePtr, state.dataSize->CurZoneEqNum);
1350 : }
1351 :
1352 : // N1 , \field Maximum Supply Air Flow Rate
1353 0 : PrintFlag = true;
1354 :
1355 0 : if (zoneHVACSizing.CoolingSAFMethod > 0 && state.dataSize->ZoneCoolingOnlyFan && !state.dataSize->ZoneHeatingOnlyFan) {
1356 :
1357 0 : SAFMethod = zoneHVACSizing.CoolingSAFMethod;
1358 0 : SizingMethod = HVAC::CoolingAirflowSizing;
1359 0 : ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1360 0 : switch (SAFMethod) {
1361 0 : case DataSizing::None:
1362 : case DataSizing::SupplyAirFlowRate:
1363 : case DataSizing::FlowPerFloorArea:
1364 : case DataSizing::FractionOfAutosizedCoolingAirflow: {
1365 : switch (SAFMethod) {
1366 0 : case DataSizing::SupplyAirFlowRate: {
1367 0 : if (zoneHVACSizing.MaxCoolAirVolFlow > 0.0) {
1368 0 : ZoneEqSizing.AirVolFlow = zoneHVACSizing.MaxCoolAirVolFlow;
1369 0 : ZoneEqSizing.SystemAirFlow = true;
1370 : }
1371 0 : TempSize = zoneHVACSizing.MaxCoolAirVolFlow;
1372 0 : } break;
1373 0 : case DataSizing::FlowPerFloorArea: {
1374 0 : ZoneEqSizing.SystemAirFlow = true;
1375 0 : ZoneEqSizing.AirVolFlow =
1376 0 : zoneHVACSizing.MaxCoolAirVolFlow * state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1377 0 : TempSize = ZoneEqSizing.AirVolFlow;
1378 0 : state.dataSize->DataScalableSizingON = true;
1379 0 : } break;
1380 0 : case DataSizing::FractionOfAutosizedCoolingAirflow: {
1381 0 : state.dataSize->DataFracOfAutosizedCoolingAirflow = zoneHVACSizing.MaxCoolAirVolFlow;
1382 0 : TempSize = DataSizing::AutoSize;
1383 0 : state.dataSize->DataScalableSizingON = true;
1384 0 : } break;
1385 0 : default: {
1386 0 : TempSize = zoneHVACSizing.MaxCoolAirVolFlow;
1387 0 : } break;
1388 : }
1389 0 : bool errorsFound = false;
1390 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1391 0 : sizingCoolingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1392 : : "Maximum Supply Air Flow Rate [m3/s]");
1393 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1394 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1395 0 : CoolingAirVolFlowScalable = sizingCoolingAirFlow.size(state, TempSize, errorsFound);
1396 0 : } break;
1397 0 : case DataSizing::FlowPerCoolingCapacity: {
1398 0 : SizingMethod = HVAC::CoolingCapacitySizing;
1399 0 : TempSize = DataSizing::AutoSize;
1400 0 : PrintFlag = false;
1401 0 : state.dataSize->DataScalableSizingON = true;
1402 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1403 0 : CoolingCapacitySizer sizerCoolingCapacity;
1404 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1405 0 : state.dataSize->DataAutosizedCoolingCapacity = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1406 0 : state.dataSize->DataFlowPerCoolingCapacity = zoneHVACSizing.MaxCoolAirVolFlow;
1407 0 : PrintFlag = true;
1408 0 : TempSize = DataSizing::AutoSize;
1409 0 : bool errorsFound = false;
1410 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1411 0 : sizingCoolingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1412 : : "Maximum Supply Air Flow Rate [m3/s]");
1413 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1414 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1415 0 : CoolingAirVolFlowScalable = sizingCoolingAirFlow.size(state, TempSize, errorsFound);
1416 0 : } break;
1417 0 : default: {
1418 0 : } break;
1419 : }
1420 : // DataScalableSizingON = false;
1421 :
1422 0 : } else if (zoneHVACSizing.HeatingSAFMethod > 0 && state.dataSize->ZoneHeatingOnlyFan && !state.dataSize->ZoneCoolingOnlyFan) {
1423 0 : SizingMethod = HVAC::HeatingAirflowSizing;
1424 0 : SAFMethod = zoneHVACSizing.HeatingSAFMethod;
1425 0 : ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1426 0 : switch (SAFMethod) {
1427 0 : case DataSizing::None:
1428 : case DataSizing::SupplyAirFlowRate:
1429 : case DataSizing::FlowPerFloorArea:
1430 : case DataSizing::FractionOfAutosizedHeatingAirflow: {
1431 : switch (SAFMethod) {
1432 0 : case DataSizing::SupplyAirFlowRate: {
1433 0 : if (zoneHVACSizing.MaxHeatAirVolFlow > 0.0) {
1434 0 : ZoneEqSizing.AirVolFlow = zoneHVACSizing.MaxHeatAirVolFlow;
1435 0 : ZoneEqSizing.SystemAirFlow = true;
1436 : }
1437 0 : TempSize = zoneHVACSizing.MaxHeatAirVolFlow;
1438 0 : } break;
1439 0 : case DataSizing::FlowPerFloorArea: {
1440 0 : ZoneEqSizing.SystemAirFlow = true;
1441 0 : ZoneEqSizing.AirVolFlow =
1442 0 : zoneHVACSizing.MaxHeatAirVolFlow * state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1443 0 : TempSize = ZoneEqSizing.AirVolFlow;
1444 0 : state.dataSize->DataScalableSizingON = true;
1445 0 : } break;
1446 0 : case DataSizing::FractionOfAutosizedHeatingAirflow: {
1447 0 : state.dataSize->DataFracOfAutosizedHeatingAirflow = zoneHVACSizing.MaxHeatAirVolFlow;
1448 0 : TempSize = DataSizing::AutoSize;
1449 0 : state.dataSize->DataScalableSizingON = true;
1450 0 : } break;
1451 0 : default: {
1452 0 : TempSize = zoneHVACSizing.MaxHeatAirVolFlow;
1453 0 : } break;
1454 : }
1455 0 : bool errorsFound = false;
1456 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1457 0 : sizingHeatingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1458 : : "Maximum Supply Air Flow Rate [m3/s]");
1459 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1460 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1461 0 : HeatingAirVolFlowScalable = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1462 0 : } break;
1463 0 : case DataSizing::FlowPerHeatingCapacity: {
1464 0 : SizingMethod = HVAC::HeatingCapacitySizing;
1465 0 : TempSize = DataSizing::AutoSize;
1466 0 : PrintFlag = false;
1467 0 : state.dataSize->DataScalableSizingON = true;
1468 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1469 0 : bool errorsFound = false;
1470 0 : HeatingCapacitySizer sizerHeatingCapacity;
1471 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1472 0 : state.dataSize->DataAutosizedHeatingCapacity = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1473 0 : state.dataSize->DataFlowPerHeatingCapacity = zoneHVACSizing.MaxHeatAirVolFlow;
1474 0 : SizingMethod = HVAC::HeatingAirflowSizing;
1475 0 : PrintFlag = true;
1476 0 : TempSize = DataSizing::AutoSize;
1477 0 : errorsFound = false;
1478 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1479 0 : sizingHeatingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1480 : : "Maximum Supply Air Flow Rate [m3/s]");
1481 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1482 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1483 0 : HeatingAirVolFlowScalable = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1484 0 : } break;
1485 0 : default: {
1486 0 : } break;
1487 : }
1488 : // DataScalableSizingON = false;
1489 : } else {
1490 :
1491 0 : if (unitVent.CoilOption != CoilsUsed::None) {
1492 0 : if (zoneHVACSizing.CoolingSAFMethod > 0) {
1493 0 : SAFMethod = zoneHVACSizing.CoolingSAFMethod;
1494 0 : SizingMethod = HVAC::CoolingAirflowSizing;
1495 0 : ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1496 0 : switch (SAFMethod) {
1497 0 : case DataSizing::None:
1498 : case DataSizing::SupplyAirFlowRate:
1499 : case DataSizing::FlowPerFloorArea:
1500 : case DataSizing::FractionOfAutosizedCoolingAirflow: {
1501 : switch (SAFMethod) {
1502 0 : case DataSizing::SupplyAirFlowRate: {
1503 0 : if (zoneHVACSizing.MaxCoolAirVolFlow > 0.0) {
1504 0 : ZoneEqSizing.AirVolFlow = zoneHVACSizing.MaxCoolAirVolFlow;
1505 0 : ZoneEqSizing.SystemAirFlow = true;
1506 : }
1507 0 : TempSize = zoneHVACSizing.MaxCoolAirVolFlow;
1508 0 : } break;
1509 0 : case DataSizing::FlowPerFloorArea: {
1510 0 : ZoneEqSizing.SystemAirFlow = true;
1511 0 : ZoneEqSizing.AirVolFlow =
1512 0 : zoneHVACSizing.MaxCoolAirVolFlow * state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1513 0 : TempSize = ZoneEqSizing.AirVolFlow;
1514 0 : state.dataSize->DataScalableSizingON = true;
1515 0 : } break;
1516 0 : case DataSizing::FractionOfAutosizedCoolingAirflow: {
1517 0 : state.dataSize->DataFracOfAutosizedCoolingAirflow = zoneHVACSizing.MaxCoolAirVolFlow;
1518 0 : TempSize = DataSizing::AutoSize;
1519 0 : state.dataSize->DataScalableSizingON = true;
1520 0 : } break;
1521 0 : default: {
1522 0 : TempSize = zoneHVACSizing.MaxCoolAirVolFlow;
1523 0 : } break;
1524 : }
1525 0 : bool errorsFound = false;
1526 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1527 0 : sizingCoolingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1528 : : "Maximum Supply Air Flow Rate [m3/s]");
1529 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1530 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1531 0 : CoolingAirVolFlowScalable = sizingCoolingAirFlow.size(state, TempSize, errorsFound);
1532 0 : } break;
1533 0 : case DataSizing::FlowPerCoolingCapacity: {
1534 0 : SizingMethod = HVAC::CoolingCapacitySizing;
1535 0 : TempSize = DataSizing::AutoSize;
1536 0 : PrintFlag = false;
1537 0 : state.dataSize->DataScalableSizingON = true;
1538 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
1539 0 : CoolingCapacitySizer sizerCoolingCapacity;
1540 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1541 0 : state.dataSize->DataAutosizedCoolingCapacity = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
1542 0 : state.dataSize->DataFlowPerCoolingCapacity = zoneHVACSizing.MaxCoolAirVolFlow;
1543 0 : PrintFlag = true;
1544 0 : TempSize = DataSizing::AutoSize;
1545 0 : bool errorsFound = false;
1546 0 : CoolingAirFlowSizer sizingCoolingAirFlow;
1547 0 : sizingCoolingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1548 : : "Maximum Supply Air Flow Rate [m3/s]");
1549 : // sizingCoolingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1550 0 : sizingCoolingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1551 0 : CoolingAirVolFlowScalable = sizingCoolingAirFlow.size(state, TempSize, errorsFound);
1552 0 : } break;
1553 0 : default: {
1554 0 : } break;
1555 : }
1556 0 : } else if (zoneHVACSizing.HeatingSAFMethod > 0) {
1557 0 : SizingMethod = HVAC::HeatingAirflowSizing;
1558 0 : SAFMethod = zoneHVACSizing.HeatingSAFMethod;
1559 0 : ZoneEqSizing.SizingMethod(SizingMethod) = SAFMethod;
1560 0 : switch (SAFMethod) {
1561 0 : case DataSizing::None:
1562 : case DataSizing::SupplyAirFlowRate:
1563 : case DataSizing::FlowPerFloorArea:
1564 : case DataSizing::FractionOfAutosizedHeatingAirflow: {
1565 : switch (SAFMethod) {
1566 0 : case DataSizing::SupplyAirFlowRate: {
1567 0 : if (zoneHVACSizing.MaxHeatAirVolFlow > 0.0) {
1568 0 : ZoneEqSizing.AirVolFlow = zoneHVACSizing.MaxHeatAirVolFlow;
1569 0 : ZoneEqSizing.SystemAirFlow = true;
1570 : }
1571 0 : TempSize = zoneHVACSizing.MaxHeatAirVolFlow;
1572 0 : } break;
1573 0 : case DataSizing::FlowPerFloorArea: {
1574 0 : ZoneEqSizing.SystemAirFlow = true;
1575 0 : ZoneEqSizing.AirVolFlow =
1576 0 : zoneHVACSizing.MaxHeatAirVolFlow * state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1577 0 : TempSize = ZoneEqSizing.AirVolFlow;
1578 0 : state.dataSize->DataScalableSizingON = true;
1579 0 : } break;
1580 0 : case DataSizing::FractionOfAutosizedHeatingAirflow: {
1581 0 : state.dataSize->DataFracOfAutosizedHeatingAirflow = zoneHVACSizing.MaxHeatAirVolFlow;
1582 0 : TempSize = DataSizing::AutoSize;
1583 0 : state.dataSize->DataScalableSizingON = true;
1584 0 : } break;
1585 0 : default: {
1586 0 : TempSize = zoneHVACSizing.MaxHeatAirVolFlow;
1587 0 : } break;
1588 : }
1589 0 : bool errorsFound = false;
1590 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1591 0 : sizingHeatingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1592 : : "Maximum Supply Air Flow Rate [m3/s]");
1593 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1594 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1595 0 : HeatingAirVolFlowScalable = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1596 0 : } break;
1597 0 : case DataSizing::FlowPerHeatingCapacity: {
1598 0 : SizingMethod = HVAC::HeatingCapacitySizing;
1599 0 : TempSize = DataSizing::AutoSize;
1600 0 : PrintFlag = false;
1601 0 : state.dataSize->DataScalableSizingON = true;
1602 0 : state.dataSize->DataFlowUsedForSizing = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1603 0 : bool errorsFound = false;
1604 0 : HeatingCapacitySizer sizerHeatingCapacity;
1605 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1606 0 : state.dataSize->DataAutosizedHeatingCapacity = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1607 0 : state.dataSize->DataFlowPerHeatingCapacity = zoneHVACSizing.MaxHeatAirVolFlow;
1608 0 : SizingMethod = HVAC::HeatingAirflowSizing;
1609 0 : PrintFlag = true;
1610 0 : TempSize = DataSizing::AutoSize;
1611 0 : errorsFound = false;
1612 0 : HeatingAirFlowSizer sizingHeatingAirFlow;
1613 0 : sizingHeatingAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1614 : : "Maximum Supply Air Flow Rate [m3/s]");
1615 : // sizingHeatingAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1616 0 : sizingHeatingAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1617 0 : HeatingAirVolFlowScalable = sizingHeatingAirFlow.size(state, TempSize, errorsFound);
1618 0 : } break;
1619 0 : default: {
1620 0 : } break;
1621 : }
1622 : }
1623 : // DataScalableSizingON = false;
1624 : } else { // if (unitVent.CoilOption != CoilsUsed::None)
1625 :
1626 0 : PrintFlag = true;
1627 0 : if (unitVent.MaxAirVolFlow == DataSizing::AutoSize) {
1628 0 : TempSize = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1629 : } else {
1630 0 : TempSize = unitVent.MaxAirVolFlow;
1631 : }
1632 0 : bool errorsFound = false;
1633 0 : SystemAirFlowSizer sizerSystemAirFlow;
1634 : // sizerSystemAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1635 0 : sizerSystemAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1636 : : "Maximum Supply Air Flow Rate [m3/s]");
1637 0 : sizerSystemAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1638 0 : HeatingAirVolFlowScalable = sizerSystemAirFlow.size(state, TempSize, errorsFound);
1639 0 : }
1640 : }
1641 :
1642 0 : unitVent.MaxAirVolFlow = max(CoolingAirVolFlowScalable, HeatingAirVolFlowScalable);
1643 :
1644 : } else {
1645 : // no scalble sizing method has been specified. Sizing proceeds using the method specified in the zoneHVAC object
1646 : // N1 , \field Maximum Supply Air Flow Rate
1647 25 : PrintFlag = true;
1648 25 : if (unitVent.CoilOption == CoilsUsed::None) {
1649 :
1650 2 : if (unitVent.MaxAirVolFlow == DataSizing::AutoSize) {
1651 0 : TempSize = state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA;
1652 : } else {
1653 2 : TempSize = unitVent.MaxAirVolFlow;
1654 : }
1655 :
1656 : } else {
1657 23 : TempSize = unitVent.MaxAirVolFlow;
1658 : }
1659 25 : bool errorsFound = false;
1660 25 : SystemAirFlowSizer sizerSystemAirFlow;
1661 : // sizerSystemAirFlow.setHVACSizingIndexData(FanCoil(FanCoilNum).HVACSizingIndex);
1662 25 : sizerSystemAirFlow.overrideSizingString(state.dataGlobal->isEpJSON ? "maximum_supply_air_flow_rate [m3/s]"
1663 : : "Maximum Supply Air Flow Rate [m3/s]");
1664 25 : sizerSystemAirFlow.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1665 25 : unitVent.MaxAirVolFlow = sizerSystemAirFlow.size(state, TempSize, errorsFound);
1666 25 : }
1667 : }
1668 :
1669 25 : IsAutoSize = false;
1670 25 : if (unitVent.OutAirVolFlow == DataSizing::AutoSize) {
1671 15 : IsAutoSize = true;
1672 : }
1673 25 : if (state.dataSize->CurZoneEqNum > 0) {
1674 25 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1675 10 : if (unitVent.OutAirVolFlow > 0.0) {
1676 20 : BaseSizer::reportSizerOutput(state,
1677 10 : state.dataUnitVentilators->cMO_UnitVentilator,
1678 : unitVent.Name,
1679 : "User-Specified Maximum Outdoor Air Flow Rate [m3/s]",
1680 : unitVent.OutAirVolFlow);
1681 : }
1682 : } else {
1683 15 : CheckZoneSizing(state, state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name);
1684 15 : if (unitVent.OAControlType == OAControl::FixedAmount) {
1685 0 : OutAirVolFlowDes = min(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA, unitVent.MaxAirVolFlow);
1686 : } else {
1687 15 : OutAirVolFlowDes = unitVent.MaxAirVolFlow;
1688 : }
1689 :
1690 15 : if (IsAutoSize) {
1691 15 : unitVent.OutAirVolFlow = OutAirVolFlowDes;
1692 30 : BaseSizer::reportSizerOutput(state,
1693 15 : state.dataUnitVentilators->cMO_UnitVentilator,
1694 : unitVent.Name,
1695 : "Design Size Maximum Outdoor Air Flow Rate [m3/s]",
1696 : OutAirVolFlowDes);
1697 : } else {
1698 0 : if (unitVent.OutAirVolFlow > 0.0 && OutAirVolFlowDes > 0.0) {
1699 0 : OutAirVolFlowUser = unitVent.OutAirVolFlow;
1700 0 : BaseSizer::reportSizerOutput(state,
1701 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1702 : unitVent.Name,
1703 : "Design Size Maximum Outdoor Air Flow Rate [m3/s]",
1704 : OutAirVolFlowDes,
1705 : "User-Specified Maximum Outdoor Air Flow Rate [m3/s]",
1706 : OutAirVolFlowUser);
1707 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1708 0 : if ((std::abs(OutAirVolFlowDes - OutAirVolFlowUser) / OutAirVolFlowUser) > state.dataSize->AutoVsHardSizingThreshold) {
1709 0 : ShowMessage(state,
1710 0 : format("SizeUnitVentilator: Potential issue with equipment sizing for {} {}",
1711 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1712 0 : unitVent.Name));
1713 0 : ShowContinueError(state, format("User-Specified Maximum Outdoor Air Flow Rate of {:.5R} [m3/s]", OutAirVolFlowUser));
1714 0 : ShowContinueError(
1715 0 : state, format("differs from Design Size Maximum Outdoor Air Flow Rate of {:.5R} [m3/s]", OutAirVolFlowDes));
1716 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1717 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1718 : }
1719 : }
1720 : }
1721 : }
1722 : }
1723 25 : ZoneEqSizing.OAVolFlow = unitVent.OutAirVolFlow;
1724 :
1725 25 : if (unitVent.ATMixerExists) { // set up ATMixer conditions for use in component sizing
1726 5 : ZoneEqSizing.OAVolFlow = 0.0; // Equipment OA flow should always be 0 when ATMixer is used
1727 5 : SingleDuct::setATMixerSizingProperties(state, unitVent.ATMixerIndex, unitVent.ZonePtr, state.dataSize->CurZoneEqNum);
1728 : }
1729 : }
1730 :
1731 25 : IsAutoSize = false;
1732 25 : if (unitVent.MinOutAirVolFlow == DataSizing::AutoSize) {
1733 15 : IsAutoSize = true;
1734 : }
1735 25 : if (state.dataSize->CurZoneEqNum > 0) {
1736 25 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1737 10 : if (unitVent.MinOutAirVolFlow > 0.0) {
1738 20 : BaseSizer::reportSizerOutput(state,
1739 10 : state.dataUnitVentilators->cMO_UnitVentilator,
1740 : unitVent.Name,
1741 : "User-Specified Minimum Outdoor Air Flow Rate [m3/s]",
1742 : unitVent.MinOutAirVolFlow);
1743 : }
1744 : } else {
1745 15 : CheckZoneSizing(state, state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name);
1746 15 : MinOutAirVolFlowDes = min(state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).MinOA, unitVent.MaxAirVolFlow);
1747 15 : if (MinOutAirVolFlowDes < HVAC::SmallAirVolFlow) {
1748 0 : MinOutAirVolFlowDes = 0.0;
1749 : }
1750 15 : if (IsAutoSize) {
1751 15 : unitVent.MinOutAirVolFlow = MinOutAirVolFlowDes;
1752 30 : BaseSizer::reportSizerOutput(state,
1753 15 : state.dataUnitVentilators->cMO_UnitVentilator,
1754 : unitVent.Name,
1755 : "Design Size Minimum Outdoor Air Flow Rate [m3/s]",
1756 : MinOutAirVolFlowDes);
1757 : } else {
1758 0 : if (unitVent.MinOutAirVolFlow > 0.0 && MinOutAirVolFlowDes > 0.0) {
1759 0 : MinOutAirVolFlowUser = unitVent.MinOutAirVolFlow;
1760 0 : BaseSizer::reportSizerOutput(state,
1761 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1762 : unitVent.Name,
1763 : "Design Size Minimum Outdoor Air Flow Rate [m3/s]",
1764 : MinOutAirVolFlowDes,
1765 : "User-Specified Minimum Outdoor Air Flow Rate [m3/s]",
1766 : MinOutAirVolFlowUser);
1767 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1768 0 : if ((std::abs(MinOutAirVolFlowDes - MinOutAirVolFlowUser) / MinOutAirVolFlowUser) >
1769 0 : state.dataSize->AutoVsHardSizingThreshold) {
1770 0 : ShowMessage(state,
1771 0 : format("SizeUnitVentilator: Potential issue with equipment sizing for {} = \"{}\".",
1772 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1773 0 : unitVent.Name));
1774 0 : ShowContinueError(state,
1775 0 : format("User-Specified Minimum Outdoor Air Flow Rate of {:.5R} [m3/s]", MinOutAirVolFlowUser));
1776 0 : ShowContinueError(
1777 0 : state, format("differs from Design Size Minimum Outdoor Air Flow Rate of {:.5R} [m3/s]", MinOutAirVolFlowDes));
1778 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1779 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1780 : }
1781 : }
1782 : }
1783 : }
1784 : }
1785 : }
1786 :
1787 25 : IsAutoSize = false;
1788 25 : if (unitVent.MaxVolHotWaterFlow == DataSizing::AutoSize) {
1789 9 : IsAutoSize = true;
1790 : }
1791 25 : if (unitVent.HCoilType == HeatCoilType::Water) {
1792 15 : if (state.dataSize->CurZoneEqNum > 0) {
1793 15 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1794 6 : if (unitVent.MaxVolHotWaterFlow > 0.0) {
1795 12 : BaseSizer::reportSizerOutput(state,
1796 6 : state.dataUnitVentilators->cMO_UnitVentilator,
1797 : unitVent.Name,
1798 : "User-Specified Maximum Hot Water Flow [m3/s]",
1799 : unitVent.MaxVolHotWaterFlow);
1800 : }
1801 : } else {
1802 9 : CheckZoneSizing(state, state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name);
1803 :
1804 9 : CoilWaterOutletNode = WaterCoils::GetCoilWaterOutletNode(state, "Coil:Heating:Water", unitVent.HCoilName, ErrorsFound);
1805 9 : if (IsAutoSize) {
1806 9 : PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(
1807 : state, "COIL:HEATING:WATER", unitVent.HCoilName, unitVent.HotControlNode, CoilWaterOutletNode, ErrorsFound);
1808 :
1809 9 : if (state.dataWaterCoils->WaterCoil(unitVent.HCoil_Index).UseDesignWaterDeltaTemp) {
1810 0 : WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(unitVent.HCoil_Index).DesignWaterDeltaTemp;
1811 0 : DoWaterCoilSizing = true;
1812 : } else {
1813 9 : if (PltSizHeatNum > 0) {
1814 9 : WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizHeatNum).DeltaT;
1815 9 : DoWaterCoilSizing = true;
1816 : } else {
1817 0 : DoWaterCoilSizing = false;
1818 : // If there is no heating Plant Sizing object and autosizing was requested, issue fatal error message
1819 0 : ShowSevereError(state, "Autosizing of water flow requires a heating loop Sizing:Plant object");
1820 0 : ShowContinueError(state,
1821 0 : format("Occurs in {} = \"{}\"", state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name));
1822 0 : ErrorsFound = true;
1823 : }
1824 : }
1825 :
1826 9 : if (DoWaterCoilSizing) {
1827 9 : if (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatMassFlow >= HVAC::SmallAirVolFlow) {
1828 9 : SizingMethod = HVAC::HeatingCapacitySizing;
1829 9 : if (unitVent.HVACSizingIndex > 0) {
1830 0 : auto &zoneHVACSizing = state.dataSize->ZoneHVACSizing(unitVent.HVACSizingIndex);
1831 0 : CapSizingMethod = zoneHVACSizing.HeatingCapMethod;
1832 0 : ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
1833 0 : switch (CapSizingMethod) {
1834 0 : case DataSizing::HeatingDesignCapacity:
1835 : case DataSizing::CapacityPerFloorArea:
1836 : case DataSizing::FractionOfAutosizedHeatingCapacity: {
1837 : switch (CapSizingMethod) {
1838 0 : case DataSizing::HeatingDesignCapacity: {
1839 0 : if (zoneHVACSizing.ScaledHeatingCapacity > 0.0) {
1840 0 : ZoneEqSizing.HeatingCapacity = true;
1841 0 : ZoneEqSizing.DesHeatingLoad = zoneHVACSizing.ScaledHeatingCapacity;
1842 : } else {
1843 0 : state.dataSize->DataFlowUsedForSizing =
1844 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1845 : }
1846 0 : TempSize = zoneHVACSizing.ScaledHeatingCapacity;
1847 0 : } break;
1848 0 : case DataSizing::CapacityPerFloorArea: {
1849 0 : ZoneEqSizing.HeatingCapacity = true;
1850 0 : ZoneEqSizing.DesHeatingLoad = zoneHVACSizing.ScaledHeatingCapacity *
1851 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1852 0 : state.dataSize->DataScalableCapSizingON = true;
1853 0 : } break;
1854 0 : case DataSizing::FractionOfAutosizedHeatingCapacity: {
1855 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = zoneHVACSizing.ScaledHeatingCapacity;
1856 0 : state.dataSize->DataFlowUsedForSizing =
1857 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1858 0 : TempSize = DataSizing::AutoSize;
1859 0 : state.dataSize->DataScalableCapSizingON = true;
1860 0 : } break;
1861 0 : default: {
1862 0 : } break;
1863 : }
1864 0 : } break;
1865 0 : default: {
1866 0 : } break;
1867 : }
1868 0 : PrintFlag = false;
1869 0 : bool errorsFound = false;
1870 0 : HeatingCapacitySizer sizerHeatingCapacity;
1871 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1872 0 : DesHeatingLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1873 0 : state.dataSize->DataScalableCapSizingON = false;
1874 : } else {
1875 9 : PrintFlag = false;
1876 9 : TempSize = DataSizing::AutoSize;
1877 18 : state.dataSize->DataFlowUsedForSizing =
1878 9 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1879 9 : bool errorsFound = false;
1880 9 : HeatingCapacitySizer sizerHeatingCapacity;
1881 9 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
1882 9 : DesHeatingLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
1883 9 : }
1884 9 : rho = FluidProperties::GetDensityGlycol(state,
1885 9 : state.dataPlnt->PlantLoop(unitVent.HWplantLoc.loopNum).FluidName,
1886 : Constant::HWInitConvTemp,
1887 9 : state.dataPlnt->PlantLoop(unitVent.HWplantLoc.loopNum).FluidIndex,
1888 : RoutineName);
1889 9 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
1890 9 : state.dataPlnt->PlantLoop(unitVent.HWplantLoc.loopNum).FluidName,
1891 : Constant::HWInitConvTemp,
1892 9 : state.dataPlnt->PlantLoop(unitVent.HWplantLoc.loopNum).FluidIndex,
1893 : RoutineName);
1894 9 : MaxVolHotWaterFlowDes = DesHeatingLoad / (WaterCoilSizDeltaT * Cp * rho);
1895 :
1896 : } else {
1897 0 : MaxVolHotWaterFlowDes = 0.0;
1898 : }
1899 : }
1900 : }
1901 9 : if (IsAutoSize) {
1902 9 : unitVent.MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
1903 18 : BaseSizer::reportSizerOutput(state,
1904 9 : state.dataUnitVentilators->cMO_UnitVentilator,
1905 : unitVent.Name,
1906 : "Design Size Maximum Hot Water Flow [m3/s]",
1907 : MaxVolHotWaterFlowDes);
1908 : } else {
1909 0 : if (unitVent.MaxVolHotWaterFlow > 0.0 && MaxVolHotWaterFlowDes > 0.0) {
1910 0 : MaxVolHotWaterFlowUser = unitVent.MaxVolHotWaterFlow;
1911 0 : BaseSizer::reportSizerOutput(state,
1912 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1913 : unitVent.Name,
1914 : "Design Size Maximum Hot Water Flow [m3/s]",
1915 : MaxVolHotWaterFlowDes,
1916 : "User-Specified Maximum Hot Water Flow [m3/s]",
1917 : MaxVolHotWaterFlowUser);
1918 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1919 0 : if ((std::abs(MaxVolHotWaterFlowDes - MaxVolHotWaterFlowUser) / MaxVolHotWaterFlowUser) >
1920 0 : state.dataSize->AutoVsHardSizingThreshold) {
1921 0 : ShowMessage(state,
1922 0 : format("SizeUnitVentilator: Potential issue with equipment sizing for {} {}",
1923 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1924 0 : unitVent.Name));
1925 0 : ShowContinueError(state,
1926 0 : format("User-Specified Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowUser));
1927 0 : ShowContinueError(
1928 0 : state, format("differs from Design Size Maximum Hot Water Flow of {:.5R} [m3/s]", MaxVolHotWaterFlowDes));
1929 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1930 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1931 : }
1932 : }
1933 : }
1934 : }
1935 : }
1936 : }
1937 : } else {
1938 10 : unitVent.MaxVolHotWaterFlow = 0.0;
1939 : }
1940 :
1941 25 : IsAutoSize = false;
1942 25 : if (unitVent.MaxVolHotSteamFlow == DataSizing::AutoSize) {
1943 9 : IsAutoSize = true;
1944 : }
1945 25 : if (unitVent.HCoilType == HeatCoilType::Steam) {
1946 0 : if (state.dataSize->CurZoneEqNum > 0) {
1947 0 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1948 0 : if (unitVent.MaxVolHotSteamFlow > 0.0) {
1949 0 : BaseSizer::reportSizerOutput(state,
1950 0 : state.dataUnitVentilators->cMO_UnitVentilator,
1951 : unitVent.Name,
1952 : "User-Specified Maximum Steam Flow [m3/s]",
1953 : unitVent.MaxVolHotSteamFlow);
1954 : }
1955 : } else {
1956 0 : CheckZoneSizing(state, state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name);
1957 :
1958 0 : CoilSteamOutletNode = SteamCoils::GetCoilSteamOutletNode(state, "Coil:Heating:Steam", unitVent.HCoilName, ErrorsFound);
1959 0 : if (IsAutoSize) {
1960 0 : PltSizHeatNum = PlantUtilities::MyPlantSizingIndex(
1961 : state, "Coil:Heating:Steam", unitVent.HCoilName, unitVent.HotControlNode, CoilSteamOutletNode, ErrorsFound);
1962 0 : if (PltSizHeatNum > 0) {
1963 0 : if (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatMassFlow >= HVAC::SmallAirVolFlow) {
1964 0 : SizingMethod = HVAC::HeatingCapacitySizing;
1965 0 : if (unitVent.HVACSizingIndex > 0) {
1966 0 : auto &zoneHVACSizing = state.dataSize->ZoneHVACSizing(unitVent.HVACSizingIndex);
1967 0 : CapSizingMethod = zoneHVACSizing.HeatingCapMethod;
1968 0 : ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
1969 0 : switch (CapSizingMethod) {
1970 0 : case DataSizing::HeatingDesignCapacity:
1971 : case DataSizing::CapacityPerFloorArea:
1972 : case DataSizing::FractionOfAutosizedHeatingCapacity: {
1973 0 : if (CapSizingMethod == DataSizing::HeatingDesignCapacity) {
1974 0 : if (zoneHVACSizing.ScaledHeatingCapacity > 0.0) {
1975 0 : ZoneEqSizing.HeatingCapacity = true;
1976 0 : ZoneEqSizing.DesHeatingLoad = zoneHVACSizing.ScaledHeatingCapacity;
1977 : } else {
1978 0 : state.dataSize->DataFlowUsedForSizing =
1979 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1980 : }
1981 0 : TempSize = zoneHVACSizing.ScaledHeatingCapacity;
1982 0 : } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
1983 0 : ZoneEqSizing.HeatingCapacity = true;
1984 0 : ZoneEqSizing.DesHeatingLoad = zoneHVACSizing.ScaledHeatingCapacity *
1985 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
1986 0 : state.dataSize->DataScalableCapSizingON = true;
1987 0 : } else if (CapSizingMethod == DataSizing::FractionOfAutosizedHeatingCapacity) {
1988 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = zoneHVACSizing.ScaledHeatingCapacity;
1989 0 : state.dataSize->DataFlowUsedForSizing =
1990 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
1991 0 : TempSize = DataSizing::AutoSize;
1992 0 : state.dataSize->DataScalableCapSizingON = true;
1993 : }
1994 0 : } break;
1995 0 : default: {
1996 0 : } break;
1997 : }
1998 0 : PrintFlag = false;
1999 0 : bool errorsFound = false;
2000 0 : HeatingCapacitySizer sizerHeatingCapacity;
2001 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
2002 0 : DesHeatingLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
2003 0 : state.dataSize->DataScalableCapSizingON = false;
2004 : } else {
2005 0 : PrintFlag = false;
2006 0 : TempSize = DataSizing::AutoSize;
2007 0 : state.dataSize->DataFlowUsedForSizing =
2008 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesHeatVolFlow;
2009 0 : bool errorsFound = false;
2010 0 : HeatingCapacitySizer sizerHeatingCapacity;
2011 0 : sizerHeatingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
2012 0 : DesHeatingLoad = sizerHeatingCapacity.size(state, TempSize, errorsFound);
2013 0 : }
2014 0 : TempSteamIn = 100.00;
2015 0 : EnthSteamInDry = FluidProperties::GetSatEnthalpyRefrig(
2016 0 : state, fluidNameSteam, TempSteamIn, 1.0, state.dataUnitVentilators->RefrigIndex, RoutineName);
2017 0 : EnthSteamOutWet = FluidProperties::GetSatEnthalpyRefrig(
2018 0 : state, fluidNameSteam, TempSteamIn, 0.0, state.dataUnitVentilators->RefrigIndex, RoutineName);
2019 0 : LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
2020 0 : SteamDensity = FluidProperties::GetSatDensityRefrig(
2021 0 : state, fluidNameSteam, TempSteamIn, 1.0, state.dataUnitVentilators->RefrigIndex, RoutineName);
2022 0 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
2023 : fluidNameWater,
2024 0 : state.dataSize->PlantSizData(PltSizHeatNum).ExitTemp,
2025 0 : state.dataUnitVentilators->DummyWaterIndex,
2026 : RoutineName);
2027 0 : MaxVolHotSteamFlowDes =
2028 0 : DesHeatingLoad / (SteamDensity * (LatentHeatSteam + state.dataSize->PlantSizData(PltSizHeatNum).DeltaT * Cp));
2029 : } else {
2030 0 : MaxVolHotSteamFlowDes = 0.0;
2031 : }
2032 : } else {
2033 0 : ShowSevereError(state, "Autosizing of Steam flow requires a heating loop Sizing:Plant object");
2034 0 : ShowContinueError(state, format("Occurs in {} = \"{}\"", state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name));
2035 0 : ErrorsFound = true;
2036 : }
2037 : }
2038 0 : if (IsAutoSize) {
2039 0 : unitVent.MaxVolHotSteamFlow = MaxVolHotSteamFlowDes;
2040 0 : BaseSizer::reportSizerOutput(state,
2041 0 : state.dataUnitVentilators->cMO_UnitVentilator,
2042 : unitVent.Name,
2043 : "Design Size Maximum Steam Flow [m3/s]",
2044 : MaxVolHotSteamFlowDes);
2045 : } else {
2046 0 : if (unitVent.MaxVolHotSteamFlow > 0.0 && MaxVolHotSteamFlowDes > 0.0) {
2047 0 : MaxVolHotSteamFlowUser = unitVent.MaxVolHotSteamFlow;
2048 0 : BaseSizer::reportSizerOutput(state,
2049 0 : state.dataUnitVentilators->cMO_UnitVentilator,
2050 : unitVent.Name,
2051 : "Design Size Maximum Steam Flow [m3/s]",
2052 : MaxVolHotSteamFlowDes,
2053 : "User-Specified Maximum Steam Flow [m3/s]",
2054 : MaxVolHotSteamFlowUser);
2055 0 : if (state.dataGlobal->DisplayExtraWarnings) {
2056 0 : if ((std::abs(MaxVolHotSteamFlowDes - MaxVolHotSteamFlowUser) / MaxVolHotSteamFlowUser) >
2057 0 : state.dataSize->AutoVsHardSizingThreshold) {
2058 0 : ShowMessage(state,
2059 0 : format("SizeUnitVentilator: Potential issue with equipment sizing for {} = \"{}\"",
2060 0 : state.dataUnitVentilators->cMO_UnitVentilator,
2061 0 : unitVent.Name));
2062 0 : ShowContinueError(state, format("User-Specified Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowUser));
2063 0 : ShowContinueError(state,
2064 0 : format("differs from Design Size Maximum Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowDes));
2065 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
2066 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
2067 : }
2068 : }
2069 : }
2070 : }
2071 : }
2072 : }
2073 : } else {
2074 25 : unitVent.MaxVolHotSteamFlow = 0.0;
2075 : }
2076 :
2077 25 : IsAutoSize = false;
2078 25 : if (unitVent.MaxVolColdWaterFlow == DataSizing::AutoSize) {
2079 6 : IsAutoSize = true;
2080 : }
2081 25 : if (unitVent.CCoilType == CoolCoilType::Water || unitVent.CCoilType == CoolCoilType::Detailed ||
2082 16 : unitVent.CCoilType == CoolCoilType::HXAssisted) {
2083 :
2084 9 : if (state.dataSize->CurZoneEqNum > 0) {
2085 9 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
2086 3 : if (unitVent.MaxVolColdWaterFlow > 0.0) {
2087 6 : BaseSizer::reportSizerOutput(state,
2088 3 : state.dataUnitVentilators->cMO_UnitVentilator,
2089 : unitVent.Name,
2090 : "User-Specified Maximum Cold Water Flow [m3/s]",
2091 : unitVent.MaxVolColdWaterFlow);
2092 : }
2093 : } else {
2094 6 : CheckZoneSizing(state, state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name);
2095 :
2096 6 : if (unitVent.CCoilType == CoolCoilType::HXAssisted) {
2097 0 : CoolingCoilName = HVACHXAssistedCoolingCoil::GetHXDXCoilName(state, unitVent.CCoilTypeCh, unitVent.CCoilName, ErrorsFound);
2098 0 : CoolingCoilType = HVACHXAssistedCoolingCoil::GetHXCoilType(state, unitVent.CCoilTypeCh, unitVent.CCoilName, ErrorsFound);
2099 : } else {
2100 6 : CoolingCoilName = unitVent.CCoilName;
2101 6 : CoolingCoilType = unitVent.CCoilTypeCh;
2102 : }
2103 6 : CoilWaterOutletNode = WaterCoils::GetCoilWaterOutletNode(state, CoolingCoilType, CoolingCoilName, ErrorsFound);
2104 6 : if (IsAutoSize) {
2105 6 : PltSizCoolNum = PlantUtilities::MyPlantSizingIndex(
2106 : state, CoolingCoilType, CoolingCoilName, unitVent.ColdControlNode, CoilWaterOutletNode, ErrorsFound);
2107 6 : if (state.dataWaterCoils->WaterCoil(unitVent.CCoil_Index).UseDesignWaterDeltaTemp) {
2108 0 : WaterCoilSizDeltaT = state.dataWaterCoils->WaterCoil(unitVent.CCoil_Index).DesignWaterDeltaTemp;
2109 0 : DoWaterCoilSizing = true;
2110 : } else {
2111 6 : if (PltSizCoolNum > 0) {
2112 6 : WaterCoilSizDeltaT = state.dataSize->PlantSizData(PltSizCoolNum).DeltaT;
2113 6 : DoWaterCoilSizing = true;
2114 : } else {
2115 0 : DoWaterCoilSizing = false;
2116 : // If there is no cooling Plant Sizing object and autosizing was requested, issue fatal error message
2117 0 : ShowSevereError(state, "Autosizing of water coil requires a cooling loop Sizing:Plant object");
2118 0 : ShowContinueError(state,
2119 0 : format("Occurs in {} = \"{}\"", state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name));
2120 0 : ErrorsFound = true;
2121 : }
2122 : }
2123 6 : if (DoWaterCoilSizing) {
2124 6 : if (state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolMassFlow >= HVAC::SmallAirVolFlow) {
2125 6 : SizingMethod = HVAC::CoolingCapacitySizing;
2126 6 : if (unitVent.HVACSizingIndex > 0) {
2127 0 : auto &zoneHVACSizing = state.dataSize->ZoneHVACSizing(unitVent.HVACSizingIndex);
2128 0 : CapSizingMethod = zoneHVACSizing.CoolingCapMethod;
2129 0 : ZoneEqSizing.SizingMethod(SizingMethod) = CapSizingMethod;
2130 0 : switch (CapSizingMethod) {
2131 0 : case DataSizing::CoolingDesignCapacity:
2132 : case DataSizing::CapacityPerFloorArea:
2133 : case DataSizing::FractionOfAutosizedCoolingCapacity: {
2134 0 : if (CapSizingMethod == DataSizing::CoolingDesignCapacity) {
2135 0 : if (zoneHVACSizing.ScaledCoolingCapacity > 0.0) {
2136 0 : ZoneEqSizing.CoolingCapacity = true;
2137 0 : ZoneEqSizing.DesCoolingLoad = zoneHVACSizing.ScaledCoolingCapacity;
2138 : } else {
2139 0 : state.dataSize->DataFlowUsedForSizing =
2140 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
2141 : }
2142 0 : TempSize = zoneHVACSizing.ScaledCoolingCapacity;
2143 0 : } else if (CapSizingMethod == DataSizing::CapacityPerFloorArea) {
2144 0 : ZoneEqSizing.CoolingCapacity = true;
2145 0 : ZoneEqSizing.DesCoolingLoad = zoneHVACSizing.ScaledCoolingCapacity *
2146 0 : state.dataHeatBal->Zone(state.dataSize->DataZoneNumber).FloorArea;
2147 0 : state.dataSize->DataScalableCapSizingON = true;
2148 0 : } else if (CapSizingMethod == DataSizing::FractionOfAutosizedCoolingCapacity) {
2149 0 : state.dataSize->DataFracOfAutosizedHeatingCapacity = zoneHVACSizing.ScaledCoolingCapacity;
2150 0 : state.dataSize->DataFlowUsedForSizing =
2151 0 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
2152 0 : TempSize = DataSizing::AutoSize;
2153 0 : state.dataSize->DataScalableCapSizingON = true;
2154 : }
2155 0 : } break;
2156 0 : default: {
2157 0 : } break;
2158 : }
2159 0 : PrintFlag = false;
2160 0 : CoolingCapacitySizer sizerCoolingCapacity;
2161 0 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
2162 0 : DesCoolingLoad = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
2163 0 : state.dataSize->DataScalableCapSizingON = false;
2164 : } else {
2165 6 : PrintFlag = false;
2166 6 : TempSize = DataSizing::AutoSize;
2167 12 : state.dataSize->DataFlowUsedForSizing =
2168 6 : state.dataSize->FinalZoneSizing(state.dataSize->CurZoneEqNum).DesCoolVolFlow;
2169 6 : CoolingCapacitySizer sizerCoolingCapacity;
2170 6 : sizerCoolingCapacity.initializeWithinEP(state, CompType, CompName, PrintFlag, RoutineName);
2171 6 : DesCoolingLoad = sizerCoolingCapacity.size(state, TempSize, ErrorsFound);
2172 6 : }
2173 6 : rho = FluidProperties::GetDensityGlycol(state,
2174 6 : state.dataPlnt->PlantLoop(unitVent.CWPlantLoc.loopNum).FluidName,
2175 : 5.,
2176 6 : state.dataPlnt->PlantLoop(unitVent.CWPlantLoc.loopNum).FluidIndex,
2177 : RoutineName);
2178 6 : Cp = FluidProperties::GetSpecificHeatGlycol(state,
2179 6 : state.dataPlnt->PlantLoop(unitVent.CWPlantLoc.loopNum).FluidName,
2180 : 5.,
2181 6 : state.dataPlnt->PlantLoop(unitVent.CWPlantLoc.loopNum).FluidIndex,
2182 : RoutineName);
2183 6 : MaxVolColdWaterFlowDes = DesCoolingLoad / (WaterCoilSizDeltaT * Cp * rho);
2184 :
2185 6 : if (MaxVolColdWaterFlowDes < 0.0) {
2186 0 : ShowWarningError(state, "Autosizing of water flow resulted in negative value.");
2187 0 : ShowContinueError(state,
2188 0 : format("Occurs in {} = \"{}\"", state.dataUnitVentilators->cMO_UnitVentilator, unitVent.Name));
2189 0 : ShowContinueError(state, "...Sizing information found during sizing simulation:");
2190 0 : ShowContinueError(state, format("...Calculated coil design load = {:.3T} W", DesCoolingLoad));
2191 0 : ShowContinueError(state, format("...Calculated water flow rate = {:.3T} m3/s", MaxVolColdWaterFlowDes));
2192 0 : ShowContinueError(state,
2193 : "...Water flow rate will be set to 0. Check sizing inputs for zone and plant, inputs for water "
2194 : "cooling coil object, and design day specifications.");
2195 0 : ShowContinueError(state, "...Consider autosizing all inputs if not already doing so.");
2196 0 : MaxVolColdWaterFlowDes = 0.0;
2197 : }
2198 : } else {
2199 0 : MaxVolColdWaterFlowDes = 0.0;
2200 : }
2201 : }
2202 : }
2203 6 : if (IsAutoSize) {
2204 6 : unitVent.MaxVolColdWaterFlow = MaxVolColdWaterFlowDes;
2205 12 : BaseSizer::reportSizerOutput(state,
2206 6 : state.dataUnitVentilators->cMO_UnitVentilator,
2207 : unitVent.Name,
2208 : "Design Size Maximum Cold Water Flow [m3/s]",
2209 : MaxVolColdWaterFlowDes);
2210 : } else {
2211 0 : if (unitVent.MaxVolColdWaterFlow > 0.0 && MaxVolColdWaterFlowDes > 0.0) {
2212 0 : MaxVolColdWaterFlowUser = unitVent.MaxVolColdWaterFlow;
2213 0 : BaseSizer::reportSizerOutput(state,
2214 0 : state.dataUnitVentilators->cMO_UnitVentilator,
2215 : unitVent.Name,
2216 : "Design Size Maximum Cold Water Flow [m3/s]",
2217 : MaxVolColdWaterFlowDes,
2218 : "User-Specified Maximum Cold Water Flow [m3/s]",
2219 : MaxVolColdWaterFlowUser);
2220 0 : if (state.dataGlobal->DisplayExtraWarnings) {
2221 0 : if ((std::abs(MaxVolColdWaterFlowDes - MaxVolColdWaterFlowUser) / MaxVolColdWaterFlowUser) >
2222 0 : state.dataSize->AutoVsHardSizingThreshold) {
2223 0 : ShowMessage(state,
2224 0 : format("SizeUnitVentilator: Potential issue with equipment sizing for {} = \"{}\"",
2225 0 : state.dataUnitVentilators->cMO_UnitVentilator,
2226 0 : unitVent.Name));
2227 0 : ShowContinueError(state,
2228 0 : format("User-Specified Maximum Cold Water Flow of {:.5R} [m3/s]", MaxVolColdWaterFlowUser));
2229 0 : ShowContinueError(
2230 0 : state, format("differs from Design Size Maximum Cold Water Flow of {:.5R} [m3/s]", MaxVolColdWaterFlowDes));
2231 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
2232 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
2233 : }
2234 : }
2235 : }
2236 : }
2237 : }
2238 : }
2239 : }
2240 :
2241 : // set the design air flow rates for the heating and cooling coils
2242 25 : if (unitVent.CCoilType == CoolCoilType::HXAssisted) {
2243 0 : CoolingCoilName = HVACHXAssistedCoolingCoil::GetHXDXCoilName(state, unitVent.CCoilTypeCh, unitVent.CCoilName, ErrorsFound);
2244 0 : CoolingCoilType = HVACHXAssistedCoolingCoil::GetHXCoilType(state, unitVent.CCoilTypeCh, unitVent.CCoilName, ErrorsFound);
2245 : } else {
2246 25 : CoolingCoilName = unitVent.CCoilName;
2247 25 : CoolingCoilType = unitVent.CCoilTypeCh;
2248 : }
2249 25 : WaterCoils::SetCoilDesFlow(state, CoolingCoilType, CoolingCoilName, unitVent.MaxAirVolFlow, ErrorsFound);
2250 25 : WaterCoils::SetCoilDesFlow(state, unitVent.HCoilTypeCh, unitVent.HCoilName, unitVent.MaxAirVolFlow, ErrorsFound);
2251 :
2252 25 : if (state.dataSize->CurZoneEqNum > 0) {
2253 25 : ZoneEqSizing.MaxHWVolFlow = unitVent.MaxVolHotWaterFlow;
2254 25 : ZoneEqSizing.MaxCWVolFlow = unitVent.MaxVolColdWaterFlow;
2255 : }
2256 :
2257 25 : if (ErrorsFound) {
2258 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
2259 : }
2260 25 : }
2261 :
2262 162760 : void CalcUnitVentilator(EnergyPlusData &state,
2263 : int &UnitVentNum, // number of the current fan coil unit being simulated
2264 : int const ZoneNum, // number of zone being served
2265 : bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
2266 : Real64 &PowerMet, // Sensible power supplied (W)
2267 : Real64 &LatOutputProvided // Latent power supplied (kg/s), negative = dehumidification
2268 : )
2269 : {
2270 :
2271 : // SUBROUTINE INFORMATION:
2272 : // AUTHOR Rick Strand
2273 : // DATE WRITTEN May 2000
2274 : // MODIFIED Don Shirey, Aug 2009 (LatOutputProvided)
2275 : // July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
2276 :
2277 : // PURPOSE OF THIS SUBROUTINE:
2278 : // This subroutine mainly controls the action of the unit ventilator
2279 : // (or more exactly, it controls the amount of outside air brought in)
2280 : // based on the user input for controls and the defined controls
2281 : // algorithms. There are currently (at the initial creation of this
2282 : // subroutine) two control methods: variable percent (ASHRAE "Cycle I"
2283 : // or "Cycle II") and fixed temperature (ASHRAE "Cycle III").
2284 :
2285 : // METHODOLOGY EMPLOYED:
2286 : // Unit is controlled based on user input and what is happening in the
2287 : // simulation. There are various cases to consider:
2288 : // 1. OFF: Unit is schedule off or there is no load on it. All flow
2289 : // rates are set to zero and the temperatures are set to zone conditions
2290 : // (except for the outside air inlet).
2291 : // 2. HEATING/VARIABLE PERCENT: The unit is on, there is a heating load,
2292 : // and variable percent control is specified. The outside air fraction
2293 : // is set to the minimum outside air fraction (schedule based) and the
2294 : // heating coil is activated.
2295 : // 3. HEATING/FIXED TEMPERATURE: The unit is on, there is a heating load,
2296 : // and fixed temperature control is specified. The outside air fraction
2297 : // is varied in an attempt to obtain a mixed air temperature equal to
2298 : // the user specified temperature (schedule based). The heating coil
2299 : // is activated, if necessary.
2300 : // 4. COOLING/NO COIL: The unit is on, there is a cooling load, and no
2301 : // coil is present or it has been scheduled off. Set the amount of
2302 : // outside air based on the control type. Simulate the "mixing box".
2303 : // 5. COOLING/WITH COIL: The unit is on, there is a cooling load, and
2304 : // a cooling coil is present and scheduled on. Tries to use outside
2305 : // air as best as possible and then calls a cooling coil
2306 : // Note: controls are strictly temperature based and do not factor
2307 : // humidity into the equation (not an enthalpy economy cycle but rather
2308 : // a simple return air economy cycle). In addition, temperature predictions
2309 : // are not strict energy balances here in the control routine though
2310 : // in the mixing routine an energy balance is preserved.
2311 :
2312 : // REFERENCES:
2313 : // ASHRAE Systems and Equipment Handbook (SI), 1996. page 31.3
2314 :
2315 : // Using/Aliasing
2316 162760 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
2317 :
2318 : // Smallest allowed temperature difference for comparisons (below this value the temperatures are assumed equal)
2319 162760 : Real64 constexpr LowTempDiff(0.1);
2320 : // Smallest allowed outside air fraction difference for comparison (below this value the fractions are assumed equal)
2321 162760 : Real64 constexpr LowOAFracDiff(0.01);
2322 162760 : int constexpr MaxIter(50);
2323 :
2324 : Real64 AirMassFlow; // air mass flow rate [kg/sec]
2325 : Real64 MaxOAFrac; // maximum possible outside air fraction
2326 : Real64 MinOAFrac; // minimum possible outside air fraction
2327 : Real64 Tdesired; // desired temperature after mixing inlet and outdoor air [degrees C]
2328 : Real64 Tinlet; // temperature of air coming into the unit ventilator [degrees C]
2329 : Real64 Toutdoor; // temperature of outdoor air being introduced into the unit ventilator [degrees C]
2330 : Real64 mdot;
2331 :
2332 162760 : switch (unitVent.CoilOption) {
2333 152086 : case CoilsUsed::Both:
2334 : case CoilsUsed::Heating: {
2335 152086 : switch (unitVent.HCoilType) {
2336 97656 : case HeatCoilType::Water: {
2337 97656 : WaterCoils::CheckWaterCoilSchedule(state, unitVent.HCoilName, unitVent.HCoilSchedValue, unitVent.HCoil_Index);
2338 97656 : } break;
2339 0 : case HeatCoilType::Steam: {
2340 0 : SteamCoils::CheckSteamCoilSchedule(state, "Coil:Heating:Steam", unitVent.HCoilName, unitVent.HCoilSchedValue, unitVent.HCoil_Index);
2341 0 : } break;
2342 27215 : case HeatCoilType::Electric: {
2343 27215 : HeatingCoils::CheckHeatingCoilSchedule(
2344 27215 : state, "Coil:Heating:Electric", unitVent.HCoilName, unitVent.HCoilSchedValue, unitVent.HCoil_Index);
2345 27215 : } break;
2346 27215 : case HeatCoilType::Gas: {
2347 27215 : HeatingCoils::CheckHeatingCoilSchedule(
2348 27215 : state, "Coil:Heating:Fuel", unitVent.HCoilName, unitVent.HCoilSchedValue, unitVent.HCoil_Index);
2349 27215 : } break;
2350 0 : default: {
2351 0 : } break;
2352 : }
2353 152086 : } break;
2354 10674 : default: {
2355 10674 : } break;
2356 : }
2357 :
2358 162760 : switch (unitVent.CoilOption) {
2359 59767 : case CoilsUsed::Both:
2360 : case CoilsUsed::Cooling: {
2361 59767 : switch (unitVent.CCoilType) {
2362 59767 : case CoolCoilType::Water: {
2363 59767 : WaterCoils::CheckWaterCoilSchedule(state, unitVent.CCoilName, unitVent.CCoilSchedValue, unitVent.CCoil_Index);
2364 59767 : } break;
2365 0 : case CoolCoilType::Detailed: {
2366 0 : WaterCoils::CheckWaterCoilSchedule(state, unitVent.CCoilName, unitVent.CCoilSchedValue, unitVent.CCoil_Index);
2367 0 : } break;
2368 0 : case CoolCoilType::HXAssisted: {
2369 0 : HVACHXAssistedCoolingCoil::CheckHXAssistedCoolingCoilSchedule(
2370 0 : state, "CoilSystem:Cooling:Water:HeatExchangerAssisted", unitVent.CCoilName, unitVent.CCoilSchedValue, unitVent.CCoil_Index);
2371 0 : } break;
2372 0 : default: {
2373 0 : assert(false);
2374 : } break;
2375 : }
2376 59767 : } break;
2377 102993 : default: {
2378 102993 : } break;
2379 : }
2380 :
2381 : // initialize local variables
2382 162760 : int ControlNode = 0;
2383 162760 : Real64 QUnitOut = 0.0;
2384 162760 : Real64 ControlOffset = 0.0;
2385 162760 : Real64 MaxWaterFlow = 0.0;
2386 162760 : Real64 MinWaterFlow = 0.0;
2387 162760 : Real64 NoOutput = 0.0;
2388 162760 : Real64 FullOutput = 0.0;
2389 162760 : int SolFlag = 0; // # of iterations IF positive, -1 means failed to converge, -2 means bounds are incorrect
2390 162760 : HVAC::FanOp fanOp = unitVent.fanOp;
2391 162760 : Real64 PartLoadFrac = 0.0;
2392 :
2393 162760 : auto &inletNode(state.dataLoopNodes->Node(unitVent.AirInNode));
2394 162760 : auto &outletNode(state.dataLoopNodes->Node(unitVent.AirOutNode));
2395 162760 : auto &outsideAirNode(state.dataLoopNodes->Node(unitVent.OutsideAirNode));
2396 :
2397 316519 : if ((std::abs(state.dataUnitVentilators->QZnReq) < HVAC::SmallLoad) || (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum)) ||
2398 378687 : (ScheduleManager::GetCurrentScheduleValue(state, unitVent.SchedPtr) <= 0) ||
2399 62168 : ((ScheduleManager::GetCurrentScheduleValue(state, unitVent.FanAvailSchedPtr) <= 0 && !state.dataHVACGlobal->TurnFansOn) ||
2400 62168 : state.dataHVACGlobal->TurnFansOff)) {
2401 :
2402 : // Unit is off or has no load upon it; set the flow rates to zero and then
2403 : // simulate the components with the no flow conditions
2404 100592 : AirMassFlow = outletNode.MassFlowRate;
2405 100592 : state.dataUnitVentilators->HCoilOn = false;
2406 100592 : if (unitVent.HotControlNode > 0) {
2407 57042 : mdot = 0.0;
2408 57042 : PlantUtilities::SetComponentFlowRate(state, mdot, unitVent.HotControlNode, unitVent.HotCoilOutNodeNum, unitVent.HWplantLoc);
2409 : }
2410 100592 : if (unitVent.ColdControlNode > 0) {
2411 35900 : mdot = 0.0;
2412 35900 : PlantUtilities::SetComponentFlowRate(state, mdot, unitVent.ColdControlNode, unitVent.ColdCoilOutNodeNum, unitVent.CWPlantLoc);
2413 : }
2414 :
2415 100592 : if (fanOp == HVAC::FanOp::Cycling) {
2416 4492 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
2417 4492 : if (inletNode.MassFlowRateMax > 0.0) {
2418 4491 : unitVent.FanPartLoadRatio = inletNode.MassFlowRate / inletNode.MassFlowRateMax;
2419 : }
2420 : } else {
2421 96100 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut);
2422 : }
2423 : } else { // Unit is on-->this section is intended to control the outside air and the main
2424 : // result is to set the outside air flow rate variable OAMassFlowRate
2425 62168 : unitVent.FanPartLoadRatio = 1.0;
2426 62168 : if (state.dataUnitVentilators->QZnReq > HVAC::SmallLoad) { // HEATING MODE
2427 :
2428 24500 : ControlNode = unitVent.HotControlNode;
2429 24500 : ControlOffset = unitVent.HotControlOffset;
2430 24500 : MaxWaterFlow = unitVent.MaxHotWaterFlow;
2431 24500 : MinWaterFlow = unitVent.MinHotWaterFlow;
2432 : // On the first HVAC iteration the system values are given to the controller, but after that
2433 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
2434 24500 : if (!FirstHVACIteration && unitVent.HCoilType == HeatCoilType::Water) {
2435 9838 : MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
2436 9838 : MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
2437 : }
2438 :
2439 24500 : state.dataUnitVentilators->HCoilOn = true;
2440 :
2441 24500 : if (outsideAirNode.MassFlowRate > 0.0) {
2442 24500 : MinOAFrac = ScheduleManager::GetCurrentScheduleValue(state, unitVent.MinOASchedPtr) *
2443 24500 : (unitVent.MinOutAirMassFlow / outsideAirNode.MassFlowRate);
2444 : } else {
2445 0 : MinOAFrac = 0.0;
2446 : }
2447 24500 : MinOAFrac = min(1.0, max(0.0, MinOAFrac));
2448 :
2449 24500 : if ((!unitVent.HCoilPresent) || (unitVent.HCoilSchedValue <= 0.0)) {
2450 : // In heating mode, but there is no coil to provide heating. This is handled
2451 : // differently than if there was a heating coil present. Fixed temperature
2452 : // will still try to vary the amount of outside air to meet the desired
2453 : // mixed air temperature, while variable percent will go to full ventilation
2454 : // when it is most advantageous.
2455 :
2456 : {
2457 996 : switch (unitVent.OAControlType) {
2458 0 : case OAControl::FixedAmount: {
2459 : // In this control type, the outdoor air flow rate is fixed to the minimum value
2460 : // which is equal to the maximum value, regardless of all the other conditions.
2461 :
2462 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2463 0 : } break;
2464 996 : case OAControl::VariablePercent: {
2465 : // This algorithm is probably a bit simplistic in that it just bounces
2466 : // back and forth between the maximum outside air and the minimum. In
2467 : // REAL(r64)ity, a system *might* vary between the two based on the load in
2468 : // the zone.
2469 996 : Tinlet = inletNode.Temp;
2470 996 : Toutdoor = outsideAirNode.Temp;
2471 :
2472 996 : if (Tinlet >= Toutdoor) {
2473 :
2474 996 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2475 :
2476 : } else { // Tinlet < Toutdoor
2477 :
2478 0 : MaxOAFrac = ScheduleManager::GetCurrentScheduleValue(state, unitVent.MaxOASchedPtr);
2479 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2480 : }
2481 996 : } break;
2482 0 : case OAControl::FixedTemperature: {
2483 : // In heating mode, the outside air for "fixed temperature" attempts
2484 : // to control the outside air fraction so that a desired temperature
2485 : // is met (if possible). If this desired temperature is between the
2486 : // outside air temperature and the zone air temperature (inlet air
2487 : // temperature), then this is possible. If not, the control will try
2488 : // to maximize the amount of air coming from the source that is closer
2489 : // in temperature to the desired temperature.
2490 0 : Tdesired = ScheduleManager::GetCurrentScheduleValue(state, unitVent.TempSchedPtr);
2491 0 : Tinlet = inletNode.Temp;
2492 0 : Toutdoor = outsideAirNode.Temp;
2493 0 : MaxOAFrac = 1.0;
2494 :
2495 0 : if (std::abs(Tinlet - Toutdoor) <= LowTempDiff) { // no difference in indoor and outdoor conditions-->set OA to minimum
2496 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2497 0 : } else if (std::abs(MaxOAFrac - MinOAFrac) <= LowOAFracDiff) { // no difference in outside air fractions
2498 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2499 0 : } else if (((Tdesired <= Tinlet) && (Tdesired >= Toutdoor)) || ((Tdesired >= Tinlet) && (Tdesired <= Toutdoor))) {
2500 : // Desired temperature is between the inlet and outdoor temperatures
2501 : // so vary the flow rate between no outside air and no recirculation air
2502 : // then applying the maximum and minimum limits the user has scheduled
2503 : // to make sure too much/little outside air is being introduced
2504 0 : state.dataUnitVentilators->OAMassFlowRate = ((Tdesired - Tinlet) / (Toutdoor - Tinlet)) * inletNode.MassFlowRate;
2505 0 : state.dataUnitVentilators->OAMassFlowRate =
2506 0 : max(state.dataUnitVentilators->OAMassFlowRate, (MinOAFrac * outsideAirNode.MassFlowRate));
2507 0 : state.dataUnitVentilators->OAMassFlowRate =
2508 0 : min(state.dataUnitVentilators->OAMassFlowRate, (MaxOAFrac * outsideAirNode.MassFlowRate));
2509 0 : } else if ((Tdesired < Tinlet) && (Tdesired < Toutdoor)) {
2510 : // Desired temperature is below both the inlet and outdoor temperatures
2511 : // so use whichever flow rate (max or min) that will get closer
2512 0 : if (Tinlet < Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2513 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2514 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2515 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2516 : }
2517 0 : } else if ((Tdesired > Tinlet) && (Tdesired > Toutdoor)) {
2518 : // Desired temperature is above both the inlet and outdoor temperatures
2519 : // so use whichever flow rate (max or min) that will get closer
2520 0 : if (Tinlet > Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2521 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2522 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2523 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2524 : }
2525 : } else {
2526 : // It should NEVER get to this point, but just in case...
2527 0 : ShowFatalError(state,
2528 0 : format("ZoneHVAC:UnitVentilator simulation control: illogical condition for {}", unitVent.Name));
2529 : }
2530 0 : } break;
2531 0 : default: {
2532 0 : assert(false);
2533 : } break;
2534 : }
2535 : }
2536 :
2537 996 : if (fanOp == HVAC::FanOp::Cycling) {
2538 0 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
2539 0 : if (inletNode.MassFlowRateMax > 0.0) {
2540 0 : unitVent.FanPartLoadRatio = inletNode.MassFlowRate / inletNode.MassFlowRateMax;
2541 : }
2542 : } else {
2543 996 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut);
2544 : }
2545 :
2546 : } else { // Coil/no coil block
2547 : // There is a heating load and there is a heating coil present (presumably).
2548 : // Variable percent will throttle outside air back to the minimum while
2549 : // fixed temperature will still try to vary the outside air amount to meet
2550 : // the desired mixed air temperature.
2551 :
2552 : {
2553 23504 : switch (unitVent.OAControlType) {
2554 3114 : case OAControl::FixedAmount: {
2555 : // In this control type, the outdoor air flow rate is fixed to the maximum value
2556 : // which is equal to the minimum value, regardless of all the other conditions.
2557 3114 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2558 3114 : } break;
2559 20390 : case OAControl::VariablePercent: {
2560 : // In heating mode, the outside air for "variable percent" control
2561 : // is set to the minimum value
2562 20390 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2563 20390 : } break;
2564 0 : case OAControl::FixedTemperature: {
2565 : // In heating mode, the outside air for "fixed temperature" attempts
2566 : // to control the outside air fraction so that a desired temperature
2567 : // is met (if possible). If this desired temperature is between the
2568 : // outside air temperature and the zone air temperature (inlet air
2569 : // temperature), then this is possible. If not, the control will try
2570 : // to maximize the amount of air coming from the source that is closer
2571 : // in temperature to the desired temperature.
2572 0 : Tdesired = ScheduleManager::GetCurrentScheduleValue(state, unitVent.TempSchedPtr);
2573 0 : Tinlet = inletNode.Temp;
2574 0 : Toutdoor = outsideAirNode.Temp;
2575 0 : MaxOAFrac = 1.0;
2576 :
2577 0 : if (std::abs(Tinlet - Toutdoor) <= LowTempDiff) { // no difference in indoor and outdoor conditions-->set OA to minimum
2578 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2579 0 : } else if (std::abs(MaxOAFrac - MinOAFrac) <= LowOAFracDiff) { // no difference in outside air fractions
2580 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2581 0 : } else if (((Tdesired <= Tinlet) && (Tdesired >= Toutdoor)) || ((Tdesired >= Tinlet) && (Tdesired <= Toutdoor))) {
2582 : // Desired temperature is between the inlet and outdoor temperatures
2583 : // so vary the flow rate between no outside air and no recirculation air
2584 : // then applying the maximum and minimum limits the user has scheduled
2585 : // to make sure too much/little outside air is being introduced
2586 0 : state.dataUnitVentilators->OAMassFlowRate = ((Tdesired - Tinlet) / (Toutdoor - Tinlet)) * inletNode.MassFlowRate;
2587 0 : state.dataUnitVentilators->OAMassFlowRate =
2588 0 : max(state.dataUnitVentilators->OAMassFlowRate, (MinOAFrac * outsideAirNode.MassFlowRate));
2589 0 : state.dataUnitVentilators->OAMassFlowRate =
2590 0 : min(state.dataUnitVentilators->OAMassFlowRate, (MaxOAFrac * outsideAirNode.MassFlowRate));
2591 0 : } else if ((Tdesired < Tinlet) && (Tdesired < Toutdoor)) {
2592 : // Desired temperature is below both the inlet and outdoor temperatures
2593 : // so use whichever flow rate (max or min) that will get closer
2594 0 : if (Tinlet < Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2595 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2596 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2597 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2598 : }
2599 0 : } else if ((Tdesired > Tinlet) && (Tdesired > Toutdoor)) {
2600 : // Desired temperature is above both the inlet and outdoor temperatures
2601 : // so use whichever flow rate (max or min) that will get closer
2602 0 : if (Tinlet > Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2603 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2604 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2605 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2606 : }
2607 : } else {
2608 : // It should NEVER get to this point, but just in case...
2609 0 : ShowFatalError(state,
2610 0 : format("ZoneHVAC:UnitVentilator simulation control: illogical condition for {}", unitVent.Name));
2611 : }
2612 0 : } break;
2613 0 : default: {
2614 0 : assert(false);
2615 : } break;
2616 : }
2617 : }
2618 :
2619 23504 : if (fanOp == HVAC::FanOp::Cycling) {
2620 :
2621 : // Find part load ratio of unit ventilator coils
2622 714 : PartLoadFrac = 0.0;
2623 714 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, NoOutput, fanOp, PartLoadFrac);
2624 714 : if ((NoOutput - state.dataUnitVentilators->QZnReq) < HVAC::SmallLoad) {
2625 : // Unit ventilator is unable to meet the load with coil off, set PLR = 1
2626 714 : PartLoadFrac = 1.0;
2627 714 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, FullOutput, fanOp, PartLoadFrac);
2628 714 : if ((FullOutput - state.dataUnitVentilators->QZnReq) > HVAC::SmallLoad) {
2629 : // Unit ventilator full load capacity is able to meet the load, Find PLR
2630 : // Tolerance is in fraction of load, MaxIter = 30, SolFalg = # of iterations or error as appropriate
2631 23476 : auto f = [&state, UnitVentNum, FirstHVACIteration, fanOp](Real64 const PartLoadRatio) {
2632 4588 : Real64 QUnitOut = 0.0; // heating/Cooling provided by unit ventilator [watts]
2633 4588 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadRatio);
2634 4588 : if (state.dataUnitVentilators->QZnReq) {
2635 4588 : return (QUnitOut - state.dataUnitVentilators->QZnReq) / state.dataUnitVentilators->QZnReq;
2636 : } else
2637 0 : return 0.0;
2638 536 : };
2639 536 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadFrac, f, 0.0, 1.0);
2640 : }
2641 : }
2642 :
2643 714 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
2644 714 : unitVent.PartLoadFrac = PartLoadFrac;
2645 714 : unitVent.FanPartLoadRatio = PartLoadFrac;
2646 :
2647 : } else { // Not a cycling operating mode
2648 :
2649 : {
2650 22790 : switch (unitVent.HCoilType) {
2651 17091 : case HeatCoilType::Water: {
2652 : // control water flow to obtain output matching QZnReq
2653 51273 : ControlCompOutput(state,
2654 17091 : unitVent.Name,
2655 17091 : state.dataUnitVentilators->cMO_UnitVentilator,
2656 : UnitVentNum,
2657 : FirstHVACIteration,
2658 17091 : state.dataUnitVentilators->QZnReq,
2659 : ControlNode,
2660 : MaxWaterFlow,
2661 : MinWaterFlow,
2662 : ControlOffset,
2663 17091 : unitVent.ControlCompTypeNum,
2664 17091 : unitVent.CompErrIndex,
2665 : _,
2666 : _,
2667 : _,
2668 : _,
2669 : _,
2670 17091 : unitVent.HWplantLoc);
2671 17091 : } break;
2672 5699 : case HeatCoilType::Gas:
2673 : case HeatCoilType::Electric:
2674 : case HeatCoilType::Steam: {
2675 5699 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut);
2676 5699 : } break;
2677 0 : default: {
2678 0 : assert(false);
2679 : } break;
2680 : }
2681 : }
2682 : }
2683 : } // Coil/no coil block
2684 :
2685 : } else { // COOLING MODE
2686 :
2687 37668 : ControlNode = unitVent.ColdControlNode;
2688 37668 : ControlOffset = unitVent.ColdControlOffset;
2689 37668 : MaxWaterFlow = unitVent.MaxColdWaterFlow;
2690 37668 : MinWaterFlow = unitVent.MinColdWaterFlow;
2691 : // On the first HVAC iteration the system values are given to the controller, but after that
2692 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
2693 37668 : if ((!FirstHVACIteration) && (ControlNode > 0) && (unitVent.CCoilPresent)) {
2694 8039 : MaxWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMaxAvail;
2695 8039 : MinWaterFlow = state.dataLoopNodes->Node(ControlNode).MassFlowRateMinAvail;
2696 : }
2697 37668 : state.dataUnitVentilators->HCoilOn = false;
2698 :
2699 37668 : Tinlet = inletNode.Temp;
2700 37668 : Toutdoor = outsideAirNode.Temp;
2701 :
2702 37668 : if (outsideAirNode.MassFlowRate > 0.0) {
2703 37668 : MinOAFrac = ScheduleManager::GetCurrentScheduleValue(state, unitVent.MinOASchedPtr) *
2704 37668 : (unitVent.MinOutAirMassFlow / outsideAirNode.MassFlowRate);
2705 : } else {
2706 0 : MinOAFrac = 0.0;
2707 : }
2708 37668 : MinOAFrac = min(1.0, max(0.0, MinOAFrac));
2709 :
2710 37668 : if ((!unitVent.CCoilPresent) || (unitVent.CCoilSchedValue <= 0.0)) {
2711 : // In cooling mode, but there is no coil to provide cooling. This is handled
2712 : // differently than if there was a cooling coil present. Fixed temperature
2713 : // will still try to vary the amount of outside air to meet the desired
2714 : // mixed air temperature, while variable percent will go to full ventilation
2715 : // when it is most advantageous.
2716 : {
2717 24191 : switch (unitVent.OAControlType) {
2718 2934 : case OAControl::FixedAmount: {
2719 : // In this control type, the outdoor air flow rate is fixed to the maximum value
2720 : // which is equal to the minimum value, regardless of all the other conditions.
2721 2934 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2722 2934 : } break;
2723 21257 : case OAControl::VariablePercent: {
2724 42514 : state.dataUnitVentilators->OAMassFlowRate =
2725 21257 : SetOAMassFlowRateForCoolingVariablePercent(state,
2726 : UnitVentNum,
2727 : MinOAFrac,
2728 : outsideAirNode.MassFlowRate,
2729 : ScheduleManager::GetCurrentScheduleValue(state, unitVent.MaxOASchedPtr),
2730 : Tinlet,
2731 : Toutdoor);
2732 21257 : } break;
2733 0 : case OAControl::FixedTemperature: {
2734 : // This is basically the same algorithm as for the heating case...
2735 0 : Tdesired = ScheduleManager::GetCurrentScheduleValue(state, unitVent.TempSchedPtr);
2736 0 : MaxOAFrac = 1.0;
2737 :
2738 0 : if (std::abs(Tinlet - Toutdoor) <= LowTempDiff) { // no difference in indoor and outdoor conditions-->set OA to minimum
2739 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2740 0 : } else if (std::abs(MaxOAFrac - MinOAFrac) <= LowOAFracDiff) { // no difference in outside air fractions
2741 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2742 0 : } else if (((Tdesired <= Tinlet) && (Tdesired >= Toutdoor)) || ((Tdesired >= Tinlet) && (Tdesired <= Toutdoor))) {
2743 : // Desired temperature is between the inlet and outdoor temperatures
2744 : // so vary the flow rate between no outside air and no recirculation air
2745 : // then applying the maximum and minimum limits the user has scheduled
2746 : // to make sure too much/little outside air is being introduced
2747 0 : state.dataUnitVentilators->OAMassFlowRate = ((Tdesired - Tinlet) / (Toutdoor - Tinlet)) * inletNode.MassFlowRate;
2748 0 : state.dataUnitVentilators->OAMassFlowRate =
2749 0 : max(state.dataUnitVentilators->OAMassFlowRate, (MinOAFrac * outsideAirNode.MassFlowRate));
2750 0 : state.dataUnitVentilators->OAMassFlowRate =
2751 0 : min(state.dataUnitVentilators->OAMassFlowRate, (MaxOAFrac * outsideAirNode.MassFlowRate));
2752 0 : } else if ((Tdesired < Tinlet) && (Tdesired < Toutdoor)) {
2753 : // Desired temperature is below both the inlet and outdoor temperatures
2754 : // so use whichever flow rate (max or min) that will get closer
2755 0 : if (Tinlet < Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2756 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2757 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2758 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2759 : }
2760 0 : } else if ((Tdesired > Tinlet) && (Tdesired > Toutdoor)) {
2761 : // Desired temperature is above both the inlet and outdoor temperatures
2762 : // so use whichever flow rate (max or min) that will get closer
2763 0 : if (Tinlet > Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2764 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2765 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2766 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2767 : }
2768 : } else {
2769 : // It should NEVER get to this point, but just in case...
2770 0 : ShowFatalError(state,
2771 0 : format("ZoneHVAC:UnitVentilator simulation control: illogical condition for {}", unitVent.Name));
2772 : }
2773 0 : } break;
2774 0 : default: {
2775 0 : assert(false);
2776 : } break;
2777 : }
2778 : }
2779 :
2780 24191 : if (fanOp == HVAC::FanOp::Cycling) {
2781 0 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
2782 0 : if (inletNode.MassFlowRateMax > 0.0) {
2783 0 : unitVent.FanPartLoadRatio = inletNode.MassFlowRate / inletNode.MassFlowRateMax;
2784 : }
2785 : } else {
2786 24191 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut);
2787 : }
2788 :
2789 : } else {
2790 : // There is a cooling load and there is a cooling coil present (presumably).
2791 : // Variable percent will throttle outside air back to the minimum while
2792 : // fixed temperature will still try to vary the outside air amount to meet
2793 : // the desired mixed air temperature.
2794 :
2795 : {
2796 13477 : switch (unitVent.OAControlType) {
2797 1234 : case OAControl::FixedAmount: {
2798 : // In this control type, the outdoor air flow rate is fixed to the maximum value
2799 : // which is equal to the minimum value, regardless of all the other conditions.
2800 1234 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2801 1234 : } break;
2802 12243 : case OAControl::VariablePercent: {
2803 24486 : state.dataUnitVentilators->OAMassFlowRate =
2804 12243 : SetOAMassFlowRateForCoolingVariablePercent(state,
2805 : UnitVentNum,
2806 : MinOAFrac,
2807 : outsideAirNode.MassFlowRate,
2808 : ScheduleManager::GetCurrentScheduleValue(state, unitVent.MaxOASchedPtr),
2809 : Tinlet,
2810 : Toutdoor);
2811 12243 : } break;
2812 0 : case OAControl::FixedTemperature: {
2813 : // This is basically the same algorithm as for the heating case...
2814 0 : Tdesired = ScheduleManager::GetCurrentScheduleValue(state, unitVent.TempSchedPtr);
2815 :
2816 0 : MaxOAFrac = 1.0;
2817 :
2818 0 : if (std::abs(Tinlet - Toutdoor) <= LowTempDiff) { // no difference in indoor and outdoor conditions-->set OA to minimum
2819 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2820 0 : } else if (std::abs(MaxOAFrac - MinOAFrac) <= LowOAFracDiff) { // no difference in outside air fractions
2821 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2822 0 : } else if (((Tdesired <= Tinlet) && (Tdesired >= Toutdoor)) || ((Tdesired >= Tinlet) && (Tdesired <= Toutdoor))) {
2823 : // Desired temperature is between the inlet and outdoor temperatures
2824 : // so vary the flow rate between no outside air and no recirculation air
2825 : // then applying the maximum and minimum limits the user has scheduled
2826 : // to make sure too much/little outside air is being introduced
2827 0 : state.dataUnitVentilators->OAMassFlowRate = ((Tdesired - Tinlet) / (Toutdoor - Tinlet)) * inletNode.MassFlowRate;
2828 0 : state.dataUnitVentilators->OAMassFlowRate =
2829 0 : max(state.dataUnitVentilators->OAMassFlowRate, (MinOAFrac * outsideAirNode.MassFlowRate));
2830 0 : state.dataUnitVentilators->OAMassFlowRate =
2831 0 : min(state.dataUnitVentilators->OAMassFlowRate, (MaxOAFrac * outsideAirNode.MassFlowRate));
2832 0 : } else if ((Tdesired < Tinlet) && (Tdesired < Toutdoor)) {
2833 : // Desired temperature is below both the inlet and outdoor temperatures
2834 : // so use whichever flow rate (max or min) that will get closer
2835 0 : if (Tinlet < Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2836 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2837 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2838 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2839 : }
2840 0 : } else if ((Tdesired > Tinlet) && (Tdesired > Toutdoor)) {
2841 : // Desired temperature is above both the inlet and outdoor temperatures
2842 : // so use whichever flow rate (max or min) that will get closer
2843 0 : if (Tinlet > Toutdoor) { // Tinlet closer to Tdesired so use minimum outside air
2844 0 : state.dataUnitVentilators->OAMassFlowRate = MinOAFrac * outsideAirNode.MassFlowRate;
2845 : } else { // Toutdoor closer to Tdesired so use maximum outside air
2846 0 : state.dataUnitVentilators->OAMassFlowRate = MaxOAFrac * outsideAirNode.MassFlowRate;
2847 : }
2848 : } else {
2849 : // It should NEVER get to this point, but just in case...
2850 0 : ShowFatalError(state,
2851 0 : format("ZoneHVAC:UnitVentilator simulation control: illogical condition for {}", unitVent.Name));
2852 : }
2853 0 : } break;
2854 0 : default: {
2855 0 : assert(false);
2856 : } break;
2857 : }
2858 : }
2859 :
2860 13477 : if (fanOp == HVAC::FanOp::Cycling) {
2861 :
2862 1236 : state.dataUnitVentilators->HCoilOn = false;
2863 : // Find part load ratio of unit ventilator coils
2864 1236 : PartLoadFrac = 0.0;
2865 1236 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, NoOutput, fanOp, PartLoadFrac);
2866 1236 : if ((NoOutput - state.dataUnitVentilators->QZnReq) > HVAC::SmallLoad) {
2867 : // Unit ventilator is unable to meet the load with coil off, set PLR = 1
2868 1236 : PartLoadFrac = 1.0;
2869 1236 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, FullOutput, fanOp, PartLoadFrac);
2870 1236 : if ((FullOutput - state.dataUnitVentilators->QZnReq) < HVAC::SmallLoad) {
2871 : // Unit ventilator full load capacity is able to meet the load, Find PLR
2872 : // Tolerance is in fraction of load, MaxIter = 30, SolFalg = # of iterations or error as appropriate
2873 25108 : auto f = [&state, UnitVentNum, FirstHVACIteration, fanOp](Real64 const PartLoadRatio) {
2874 4820 : Real64 QUnitOut = 0.0; // heating/Cooling provided by unit ventilator [watts]
2875 :
2876 : // Convert parameters to usable variables
2877 4820 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadRatio);
2878 4820 : if (state.dataUnitVentilators->QZnReq) {
2879 4820 : return (QUnitOut - state.dataUnitVentilators->QZnReq) / state.dataUnitVentilators->QZnReq;
2880 : }
2881 0 : return 0.0;
2882 1008 : };
2883 1008 : General::SolveRoot(state, 0.001, MaxIter, SolFlag, PartLoadFrac, f, 0.0, 1.0);
2884 : }
2885 : }
2886 1236 : CalcUnitVentilatorComponents(state, UnitVentNum, FirstHVACIteration, QUnitOut, fanOp, PartLoadFrac);
2887 1236 : unitVent.PartLoadFrac = PartLoadFrac;
2888 1236 : unitVent.FanPartLoadRatio = PartLoadFrac;
2889 :
2890 : } else { // NOT a cycling operating mode
2891 : // control water flow to obtain output matching QZnReq
2892 12241 : state.dataUnitVentilators->HCoilOn = false;
2893 36723 : ControlCompOutput(state,
2894 12241 : unitVent.Name,
2895 12241 : state.dataUnitVentilators->cMO_UnitVentilator,
2896 : UnitVentNum,
2897 : FirstHVACIteration,
2898 12241 : state.dataUnitVentilators->QZnReq,
2899 : ControlNode,
2900 : MaxWaterFlow,
2901 : MinWaterFlow,
2902 : ControlOffset,
2903 12241 : unitVent.ControlCompTypeNum,
2904 12241 : unitVent.CompErrIndex,
2905 : _,
2906 : _,
2907 : _,
2908 : _,
2909 : _,
2910 12241 : unitVent.CWPlantLoc);
2911 :
2912 : } // end from IF (fanOp .EQ. HVAC::FanOp::Cycling) THEN
2913 : }
2914 :
2915 : } // ...end of HEATING/COOLING IF-THEN block
2916 :
2917 62168 : AirMassFlow = outletNode.MassFlowRate;
2918 62168 : QUnitOut = AirMassFlow *
2919 62168 : (Psychrometrics::PsyHFnTdbW(outletNode.Temp, inletNode.HumRat) - Psychrometrics::PsyHFnTdbW(inletNode.Temp, inletNode.HumRat));
2920 :
2921 : } // ...end of unit ON/OFF IF-THEN block
2922 :
2923 162760 : Real64 QTotUnitOut = AirMassFlow * (outletNode.Enthalpy - inletNode.Enthalpy);
2924 :
2925 : // Report variables...
2926 162760 : unitVent.HeatPower = max(0.0, QUnitOut);
2927 162760 : unitVent.SensCoolPower = std::abs(min(0.0, QUnitOut));
2928 162760 : unitVent.TotCoolPower = std::abs(min(0.0, QTotUnitOut));
2929 162760 : unitVent.ElecPower = state.dataFans->fans(unitVent.Fan_Index)->totalPower;
2930 :
2931 162760 : PowerMet = QUnitOut;
2932 162760 : LatOutputProvided = AirMassFlow * (outletNode.HumRat - inletNode.HumRat); // Latent rate (kg/s), dehumid = negative;
2933 162760 : }
2934 :
2935 489413 : void CalcUnitVentilatorComponents(EnergyPlusData &state,
2936 : int const UnitVentNum, // Unit index in unit ventilator array
2937 : bool const FirstHVACIteration, // flag for 1st HVAV iteration in the time step
2938 : Real64 &LoadMet, // load met by unit (watts)
2939 : ObjexxFCL::Optional<HVAC::FanOp const> fanOp, // Fan Type
2940 : ObjexxFCL::Optional<Real64 const> PartLoadFrac // Part Load Ratio of coil and fan
2941 : )
2942 : {
2943 :
2944 : // SUBROUTINE INFORMATION:
2945 : // AUTHOR Rick Strand
2946 : // DATE WRITTEN May 2000
2947 : // MODIFIED July 2012, Chandan Sharma - FSEC: Added zone sys avail managers
2948 :
2949 : // PURPOSE OF THIS SUBROUTINE:
2950 : // This subroutine launches the individual component simulations.
2951 : // This is called either when the unit is off to carry null conditions
2952 : // through the unit or during control iterations to continue updating
2953 : // what is going on within the unit.
2954 :
2955 : // METHODOLOGY EMPLOYED:
2956 : // Simply calls the different components in order. Only slight wrinkles
2957 : // here are that the unit ventilator has it's own outside air mixer and
2958 : // that a cooling coil must be present in order to call a cooling coil
2959 : // simulation. Other than that, the subroutine is very straightforward.
2960 :
2961 489413 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
2962 :
2963 : Real64 mdot; // hot water or steam mass flow rate for current time step
2964 :
2965 489413 : auto &inletNode = state.dataLoopNodes->Node(unitVent.AirInNode);
2966 489413 : auto &outletNode = state.dataLoopNodes->Node(unitVent.AirOutNode);
2967 489413 : state.dataUnitVentilators->ZoneNode = state.dataZoneEquip->ZoneEquipConfig(unitVent.ZonePtr).ZoneNode;
2968 489413 : Real64 QCoilReq = state.dataUnitVentilators->QZnReq;
2969 :
2970 489413 : if (fanOp != HVAC::FanOp::Cycling) {
2971 :
2972 469663 : if (unitVent.ATMixerExists) {
2973 135851 : state.dataUnitVentilators->ATMixOutNode = unitVent.ATMixerOutNode;
2974 135851 : state.dataUnitVentilators->ATMixerPriNode = unitVent.ATMixerPriNode;
2975 135851 : if (unitVent.ATMixerType == HVAC::MixerType::InletSide) {
2976 : // set the primary air inlet mass flow rate
2977 100448 : state.dataLoopNodes->Node(state.dataUnitVentilators->ATMixerPriNode).MassFlowRate =
2978 100448 : min(min(state.dataLoopNodes->Node(state.dataUnitVentilators->ATMixerPriNode).MassFlowRateMaxAvail,
2979 100448 : state.dataUnitVentilators->OAMassFlowRate),
2980 : inletNode.MassFlowRate);
2981 : // now calculate the the mixer outlet conditions (and the secondary air inlet flow rate)
2982 100448 : SingleDuct::SimATMixer(state, unitVent.ATMixerName, FirstHVACIteration, unitVent.ATMixerIndex);
2983 : }
2984 : } else {
2985 333812 : SimUnitVentOAMixer(state, UnitVentNum, fanOp);
2986 : }
2987 469663 : if (unitVent.fanType != HVAC::FanType::SystemModel) {
2988 311762 : state.dataFans->fans(unitVent.Fan_Index)->simulate(state, FirstHVACIteration);
2989 : } else {
2990 157901 : state.dataHVACGlobal->OnOffFanPartLoadFraction = 1.0; // used for cycling fan, set to 1.0 to be sure
2991 157901 : state.dataFans->fans(unitVent.Fan_Index)->simulate(state, FirstHVACIteration);
2992 : }
2993 :
2994 469663 : if (unitVent.CCoilPresent) {
2995 217612 : if (unitVent.CCoilType == CoolCoilType::HXAssisted) {
2996 0 : HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
2997 0 : state, unitVent.CCoilName, FirstHVACIteration, HVAC::CompressorOp::On, 0.0, unitVent.CCoil_Index, HVAC::FanOp::Continuous);
2998 : } else {
2999 217612 : WaterCoils::SimulateWaterCoilComponents(state, unitVent.CCoilName, FirstHVACIteration, unitVent.CCoil_Index);
3000 : }
3001 : }
3002 :
3003 469663 : if (unitVent.HCoilPresent) {
3004 :
3005 : {
3006 458989 : switch (unitVent.HCoilType) {
3007 346908 : case HeatCoilType::Water: {
3008 346908 : WaterCoils::SimulateWaterCoilComponents(state, unitVent.HCoilName, FirstHVACIteration, unitVent.HCoil_Index);
3009 346908 : } break;
3010 0 : case HeatCoilType::Steam: {
3011 0 : if (!state.dataUnitVentilators->HCoilOn) {
3012 0 : QCoilReq = 0.0;
3013 : } else {
3014 0 : int HCoilInAirNode = unitVent.FanOutletNode;
3015 0 : Real64 CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(unitVent.AirInNode).HumRat);
3016 0 : QCoilReq = state.dataUnitVentilators->QZnReq -
3017 0 : state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
3018 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp - state.dataLoopNodes->Node(unitVent.AirInNode).Temp);
3019 : }
3020 :
3021 0 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
3022 :
3023 0 : SteamCoils::SimulateSteamCoilComponents(state, unitVent.HCoilName, FirstHVACIteration, unitVent.HCoil_Index, QCoilReq);
3024 0 : } break;
3025 112081 : case HeatCoilType::Electric:
3026 : case HeatCoilType::Gas: {
3027 112081 : if (!state.dataUnitVentilators->HCoilOn) {
3028 106382 : QCoilReq = 0.0;
3029 : } else {
3030 5699 : int HCoilInAirNode = unitVent.FanOutletNode;
3031 5699 : Real64 CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(unitVent.AirInNode).HumRat);
3032 5699 : QCoilReq = state.dataUnitVentilators->QZnReq -
3033 5699 : state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
3034 5699 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp - state.dataLoopNodes->Node(unitVent.AirInNode).Temp);
3035 : }
3036 :
3037 112081 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
3038 :
3039 112081 : HeatingCoils::SimulateHeatingCoilComponents(state, unitVent.HCoilName, FirstHVACIteration, QCoilReq, unitVent.HCoil_Index);
3040 112081 : } break;
3041 0 : default: {
3042 0 : assert(false);
3043 : } break;
3044 : }
3045 : }
3046 :
3047 : } // (UnitVent(UnitVentNum)%HCoilPresent)
3048 :
3049 : } else { // Fan is Fan:OnOff and is cycling
3050 :
3051 19750 : inletNode.MassFlowRate = inletNode.MassFlowRateMax * PartLoadFrac;
3052 : // Set the fan inlet node maximum available mass flow rates for cycling fans
3053 19750 : inletNode.MassFlowRateMaxAvail = inletNode.MassFlowRate;
3054 :
3055 19750 : if (unitVent.ATMixerExists) {
3056 0 : state.dataUnitVentilators->ATMixOutNode = unitVent.ATMixerOutNode;
3057 0 : state.dataUnitVentilators->ATMixerPriNode = unitVent.ATMixerPriNode;
3058 0 : if (unitVent.ATMixerType == HVAC::MixerType::InletSide) {
3059 : // set the primary air inlet mass flow rate
3060 0 : state.dataLoopNodes->Node(state.dataUnitVentilators->ATMixerPriNode).MassFlowRate =
3061 0 : min(min(state.dataLoopNodes->Node(state.dataUnitVentilators->ATMixerPriNode).MassFlowRateMaxAvail,
3062 0 : state.dataUnitVentilators->OAMassFlowRate),
3063 : inletNode.MassFlowRate);
3064 : // now calculate the mixer outlet conditions (and the secondary air inlet flow rate)
3065 0 : SingleDuct::SimATMixer(state, unitVent.ATMixerName, FirstHVACIteration, unitVent.ATMixerIndex);
3066 : }
3067 : } else {
3068 19750 : SimUnitVentOAMixer(state, UnitVentNum, fanOp);
3069 : }
3070 :
3071 19750 : state.dataFans->fans(unitVent.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
3072 :
3073 19750 : if (unitVent.CCoilPresent) {
3074 :
3075 : // where is mdot set ?
3076 19750 : CalcMdotCCoilCycFan(state, mdot, QCoilReq, state.dataUnitVentilators->QZnReq, UnitVentNum, PartLoadFrac);
3077 19750 : PlantUtilities::SetComponentFlowRate(state, mdot, unitVent.ColdControlNode, unitVent.ColdCoilOutNodeNum, unitVent.CWPlantLoc);
3078 :
3079 19750 : if (unitVent.CCoilType == CoolCoilType::HXAssisted) {
3080 0 : HVACHXAssistedCoolingCoil::SimHXAssistedCoolingCoil(
3081 0 : state, unitVent.CCoilName, FirstHVACIteration, HVAC::CompressorOp::On, PartLoadFrac, unitVent.CCoil_Index, fanOp);
3082 : } else {
3083 39500 : WaterCoils::SimulateWaterCoilComponents(
3084 19750 : state, unitVent.CCoilName, FirstHVACIteration, unitVent.CCoil_Index, QCoilReq, fanOp, PartLoadFrac);
3085 : }
3086 : }
3087 :
3088 19750 : if (unitVent.HCoilPresent) {
3089 :
3090 : {
3091 19750 : switch (unitVent.HCoilType) {
3092 19750 : case HeatCoilType::Water: {
3093 19750 : if (!state.dataUnitVentilators->HCoilOn) {
3094 13020 : QCoilReq = 0.0;
3095 13020 : mdot = 0.0;
3096 : } else {
3097 6730 : int HCoilInAirNode = unitVent.FanOutletNode;
3098 6730 : Real64 CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(unitVent.AirInNode).HumRat);
3099 6730 : QCoilReq = state.dataUnitVentilators->QZnReq -
3100 6730 : state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
3101 6730 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp - state.dataLoopNodes->Node(unitVent.AirInNode).Temp);
3102 6730 : mdot = unitVent.MaxHotWaterFlow * PartLoadFrac;
3103 : }
3104 :
3105 19750 : if (QCoilReq < 0.0) QCoilReq = 0.0; // a heating coil can only heat, not cool
3106 19750 : PlantUtilities::SetComponentFlowRate(state, mdot, unitVent.HotControlNode, unitVent.HotCoilOutNodeNum, unitVent.HWplantLoc);
3107 39500 : WaterCoils::SimulateWaterCoilComponents(
3108 19750 : state, unitVent.HCoilName, FirstHVACIteration, unitVent.HCoil_Index, QCoilReq, fanOp, PartLoadFrac);
3109 19750 : } break;
3110 0 : case HeatCoilType::Steam: {
3111 0 : if (!state.dataUnitVentilators->HCoilOn) {
3112 0 : QCoilReq = 0.0;
3113 0 : mdot = 0.0;
3114 : } else {
3115 0 : int HCoilInAirNode = unitVent.FanOutletNode;
3116 0 : Real64 CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(unitVent.AirInNode).HumRat);
3117 0 : QCoilReq = state.dataUnitVentilators->QZnReq -
3118 0 : state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
3119 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp - state.dataLoopNodes->Node(unitVent.AirInNode).Temp);
3120 0 : mdot = unitVent.MaxHotSteamFlow * PartLoadFrac;
3121 : }
3122 :
3123 0 : if (QCoilReq < 0.0) QCoilReq = 0.0;
3124 0 : PlantUtilities::SetComponentFlowRate(state, mdot, unitVent.HotControlNode, unitVent.HotCoilOutNodeNum, unitVent.HWplantLoc);
3125 0 : SteamCoils::SimulateSteamCoilComponents(
3126 0 : state, unitVent.HCoilName, FirstHVACIteration, unitVent.HCoil_Index, QCoilReq, _, fanOp, PartLoadFrac);
3127 0 : } break;
3128 0 : case HeatCoilType::Electric:
3129 : case HeatCoilType::Gas: {
3130 0 : if (!state.dataUnitVentilators->HCoilOn) {
3131 0 : QCoilReq = 0.0;
3132 : } else {
3133 0 : int HCoilInAirNode = unitVent.FanOutletNode;
3134 0 : Real64 CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(unitVent.AirInNode).HumRat);
3135 0 : QCoilReq = state.dataUnitVentilators->QZnReq -
3136 0 : state.dataLoopNodes->Node(HCoilInAirNode).MassFlowRate * CpAirZn *
3137 0 : (state.dataLoopNodes->Node(HCoilInAirNode).Temp - state.dataLoopNodes->Node(unitVent.AirInNode).Temp);
3138 : }
3139 0 : if (QCoilReq < 0.0) QCoilReq = 0.0;
3140 0 : HeatingCoils::SimulateHeatingCoilComponents(
3141 0 : state, unitVent.HCoilName, FirstHVACIteration, QCoilReq, unitVent.HCoil_Index, _, _, fanOp, PartLoadFrac);
3142 0 : } break;
3143 0 : default: {
3144 0 : assert(false);
3145 : } break;
3146 : }
3147 : }
3148 : }
3149 : }
3150 489413 : Real64 AirMassFlow = outletNode.MassFlowRate;
3151 : // calculate delivered load
3152 489413 : if (unitVent.ATMixerExists) {
3153 135851 : auto &ATMixOutNode(state.dataLoopNodes->Node(state.dataUnitVentilators->ATMixOutNode));
3154 135851 : auto &ATMixerPriNode(state.dataLoopNodes->Node(state.dataUnitVentilators->ATMixerPriNode));
3155 135851 : if (unitVent.ATMixerType == HVAC::MixerType::SupplySide) {
3156 : // set the primary air inlet mass flow rate
3157 35403 : ATMixerPriNode.MassFlowRate = min(ATMixerPriNode.MassFlowRateMaxAvail, state.dataUnitVentilators->OAMassFlowRate);
3158 : // now calculate the the mixer outlet conditions (and the secondary air inlet flow rate)
3159 35403 : SingleDuct::SimATMixer(state, unitVent.ATMixerName, FirstHVACIteration, unitVent.ATMixerIndex);
3160 35403 : Real64 SpecHumMin = min(ATMixOutNode.HumRat, inletNode.HumRat); // (kg moisture / kg moist air)
3161 70806 : LoadMet = ATMixOutNode.MassFlowRate *
3162 35403 : (Psychrometrics::PsyHFnTdbW(ATMixOutNode.Temp, SpecHumMin) - Psychrometrics::PsyHFnTdbW(inletNode.Temp, SpecHumMin));
3163 : } else {
3164 : // ATM Mixer on inlet side
3165 100448 : auto &zoneNode(state.dataLoopNodes->Node(state.dataUnitVentilators->ZoneNode));
3166 100448 : LoadMet = AirMassFlow *
3167 100448 : (Psychrometrics::PsyHFnTdbW(outletNode.Temp, zoneNode.HumRat) - Psychrometrics::PsyHFnTdbW(zoneNode.Temp, zoneNode.HumRat));
3168 : }
3169 : } else {
3170 353562 : LoadMet = AirMassFlow *
3171 353562 : (Psychrometrics::PsyHFnTdbW(outletNode.Temp, inletNode.HumRat) - Psychrometrics::PsyHFnTdbW(inletNode.Temp, inletNode.HumRat));
3172 : }
3173 489413 : }
3174 :
3175 353562 : void SimUnitVentOAMixer(EnergyPlusData &state,
3176 : int const UnitVentNum, // Unit index in unit ventilator array
3177 : HVAC::FanOp const fanOp // unit ventilator fan operating mode
3178 : )
3179 : {
3180 :
3181 : // SUBROUTINE INFORMATION:
3182 : // AUTHOR Rick Strand
3183 : // DATE WRITTEN May 2000
3184 :
3185 : // PURPOSE OF THIS SUBROUTINE:
3186 : // This responsibility of this subroutine is to set the air flow rates
3187 : // through the mixing box portion of the unit ventilator and then perform
3188 : // an energy balance to arrive at outlet conditions which then would
3189 : // serve as inlet conditions to the coils (or outlet conditions for
3190 : // the device). There is some question as to whether this needs to be
3191 : // called every time the coils and fan are called since how the fans and
3192 : // coil operate won't presumable change how the mixer operates. The
3193 : // method in which this routine is called is slightly cleaner though
3194 : // from a code readability standpoint though less efficient.
3195 :
3196 : // METHODOLOGY EMPLOYED:
3197 : // The OAMassFlowRate has already been calculated in the main control
3198 : // algorithm. Use this flow rate to establish all of the other flow
3199 : // rates and perform an energy balance on the mixing of the return and
3200 : // outdoor air streams.
3201 :
3202 353562 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
3203 353562 : auto &airRelNode = state.dataLoopNodes->Node(unitVent.AirReliefNode);
3204 353562 : auto &inletNode = state.dataLoopNodes->Node(unitVent.AirInNode);
3205 353562 : auto &OAMixOutNode = state.dataLoopNodes->Node(unitVent.OAMixerOutNode);
3206 353562 : auto &outsideAirNode = state.dataLoopNodes->Node(unitVent.OutsideAirNode);
3207 353562 : Real64 OutAirMassFlowRate = state.dataUnitVentilators->OAMassFlowRate;
3208 :
3209 : // Limit the outdoor air mass flow rate if cycling fan
3210 353562 : if (fanOp == HVAC::FanOp::Cycling) {
3211 19750 : OutAirMassFlowRate = min(state.dataUnitVentilators->OAMassFlowRate, inletNode.MassFlowRate);
3212 : }
3213 :
3214 : // "Resolve" the air flow rates...
3215 353562 : outsideAirNode.MassFlowRate = OutAirMassFlowRate;
3216 353562 : outsideAirNode.MassFlowRateMinAvail = OutAirMassFlowRate;
3217 353562 : outsideAirNode.MassFlowRateMaxAvail = OutAirMassFlowRate;
3218 :
3219 353562 : airRelNode.MassFlowRate = OutAirMassFlowRate;
3220 353562 : airRelNode.MassFlowRateMinAvail = OutAirMassFlowRate;
3221 353562 : airRelNode.MassFlowRateMaxAvail = OutAirMassFlowRate;
3222 :
3223 353562 : OAMixOutNode.MassFlowRate = inletNode.MassFlowRate;
3224 353562 : OAMixOutNode.MassFlowRateMinAvail = inletNode.MassFlowRate;
3225 353562 : OAMixOutNode.MassFlowRateMaxAvail = inletNode.MassFlowRate;
3226 :
3227 : // "Inlet" conditions for InletNode and OutsideAirNode have already
3228 : // been set elsewhere so we just need to set the "outlet" conditions
3229 353562 : airRelNode.Temp = inletNode.Temp;
3230 353562 : airRelNode.Press = inletNode.Press;
3231 353562 : airRelNode.HumRat = inletNode.HumRat;
3232 353562 : airRelNode.Enthalpy = inletNode.Enthalpy;
3233 :
3234 353562 : Real64 OAFraction = 0.0; // Outside air fraction of inlet air
3235 353562 : if (inletNode.MassFlowRate > 0.0) {
3236 275103 : OAFraction = outsideAirNode.MassFlowRate / inletNode.MassFlowRate;
3237 : }
3238 :
3239 : // Perform an energy and moisture mass balance on the mixing portion of the unit ventilator
3240 353562 : OAMixOutNode.Enthalpy = OAFraction * outsideAirNode.Enthalpy + (1.0 - OAFraction) * inletNode.Enthalpy;
3241 353562 : OAMixOutNode.HumRat = OAFraction * outsideAirNode.HumRat + (1.0 - OAFraction) * inletNode.HumRat;
3242 :
3243 : // Find the other key state points based on calculated conditions
3244 353562 : OAMixOutNode.Temp = Psychrometrics::PsyTdbFnHW(OAMixOutNode.Enthalpy, OAMixOutNode.HumRat);
3245 353562 : OAMixOutNode.Press = inletNode.Press;
3246 :
3247 353562 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
3248 0 : airRelNode.CO2 = inletNode.CO2;
3249 0 : OAMixOutNode.CO2 = OAFraction * outsideAirNode.CO2 + (1.0 - OAFraction) * inletNode.CO2;
3250 : }
3251 353562 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
3252 0 : airRelNode.GenContam = inletNode.GenContam;
3253 0 : OAMixOutNode.GenContam = OAFraction * outsideAirNode.GenContam + (1.0 - OAFraction) * inletNode.GenContam;
3254 : }
3255 353562 : }
3256 :
3257 162760 : void ReportUnitVentilator(EnergyPlusData &state, int const UnitVentNum) // Unit index in unit ventilator array
3258 : {
3259 :
3260 : // SUBROUTINE INFORMATION:
3261 : // AUTHOR Rick Strand
3262 : // DATE WRITTEN May 2000
3263 :
3264 : // Using/Aliasing
3265 162760 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
3266 162760 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
3267 :
3268 162760 : unitVent.HeatEnergy = unitVent.HeatPower * TimeStepSysSec;
3269 162760 : unitVent.SensCoolEnergy = unitVent.SensCoolPower * TimeStepSysSec;
3270 162760 : unitVent.TotCoolEnergy = unitVent.TotCoolPower * TimeStepSysSec;
3271 162760 : unitVent.ElecEnergy = unitVent.ElecPower * TimeStepSysSec;
3272 :
3273 162760 : if (unitVent.FirstPass) { // reset sizing flags so other zone equipment can size normally
3274 40 : if (!state.dataGlobal->SysSizingCalc) {
3275 25 : DataSizing::resetHVACSizingGlobals(state, state.dataSize->CurZoneEqNum, 0, unitVent.FirstPass);
3276 : }
3277 : }
3278 162760 : }
3279 :
3280 8595 : int GetUnitVentilatorOutAirNode(EnergyPlusData &state, int const UnitVentNum)
3281 : {
3282 :
3283 : // FUNCTION INFORMATION:
3284 : // AUTHOR B Griffith
3285 : // DATE WRITTEN Dec 2006
3286 :
3287 : int GetUnitVentilatorOutAirNode;
3288 :
3289 8595 : if (state.dataUnitVentilators->GetUnitVentilatorInputFlag) {
3290 0 : GetUnitVentilatorInput(state);
3291 0 : state.dataUnitVentilators->GetUnitVentilatorInputFlag = false;
3292 : }
3293 :
3294 8595 : GetUnitVentilatorOutAirNode = 0;
3295 8595 : if (UnitVentNum > 0 && UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents) {
3296 8595 : GetUnitVentilatorOutAirNode = state.dataUnitVentilators->UnitVent(UnitVentNum).OutsideAirNode;
3297 : }
3298 :
3299 8595 : return GetUnitVentilatorOutAirNode;
3300 : }
3301 :
3302 8595 : int GetUnitVentilatorZoneInletAirNode(EnergyPlusData &state, int const UnitVentNum)
3303 : {
3304 :
3305 : // FUNCTION INFORMATION:
3306 : // AUTHOR B Griffith
3307 : // DATE WRITTEN Dec 2006
3308 :
3309 : int GetUnitVentilatorZoneInletAirNode;
3310 :
3311 8595 : if (state.dataUnitVentilators->GetUnitVentilatorInputFlag) {
3312 0 : GetUnitVentilatorInput(state);
3313 0 : state.dataUnitVentilators->GetUnitVentilatorInputFlag = false;
3314 : }
3315 :
3316 8595 : GetUnitVentilatorZoneInletAirNode = 0;
3317 8595 : if (UnitVentNum > 0 && UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents) {
3318 8595 : GetUnitVentilatorZoneInletAirNode = state.dataUnitVentilators->UnitVent(UnitVentNum).AirOutNode;
3319 : }
3320 :
3321 8595 : return GetUnitVentilatorZoneInletAirNode;
3322 : }
3323 :
3324 8595 : int GetUnitVentilatorMixedAirNode(EnergyPlusData &state, int const UnitVentNum)
3325 : {
3326 :
3327 : // FUNCTION INFORMATION:
3328 : // AUTHOR B Griffith
3329 : // DATE WRITTEN Dec 2006
3330 :
3331 : int GetUnitVentilatorMixedAirNode;
3332 :
3333 8595 : if (state.dataUnitVentilators->GetUnitVentilatorInputFlag) {
3334 0 : GetUnitVentilatorInput(state);
3335 0 : state.dataUnitVentilators->GetUnitVentilatorInputFlag = false;
3336 : }
3337 :
3338 8595 : GetUnitVentilatorMixedAirNode = 0;
3339 8595 : if (UnitVentNum > 0 && UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents) {
3340 8595 : GetUnitVentilatorMixedAirNode = state.dataUnitVentilators->UnitVent(UnitVentNum).OAMixerOutNode;
3341 : }
3342 :
3343 8595 : return GetUnitVentilatorMixedAirNode;
3344 : }
3345 :
3346 8595 : int GetUnitVentilatorReturnAirNode(EnergyPlusData &state, int const UnitVentNum)
3347 : {
3348 :
3349 : // FUNCTION INFORMATION:
3350 : // AUTHOR B Griffith
3351 : // DATE WRITTEN Dec 2006
3352 :
3353 : int GetUnitVentilatorReturnAirNode;
3354 :
3355 8595 : if (state.dataUnitVentilators->GetUnitVentilatorInputFlag) {
3356 0 : GetUnitVentilatorInput(state);
3357 0 : state.dataUnitVentilators->GetUnitVentilatorInputFlag = false;
3358 : }
3359 :
3360 8595 : GetUnitVentilatorReturnAirNode = 0;
3361 8595 : if (UnitVentNum > 0 && UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents) {
3362 8595 : GetUnitVentilatorReturnAirNode = state.dataUnitVentilators->UnitVent(UnitVentNum).AirInNode;
3363 : }
3364 :
3365 8595 : return GetUnitVentilatorReturnAirNode;
3366 : }
3367 :
3368 0 : int getUnitVentilatorIndex(EnergyPlusData &state, std::string_view CompName)
3369 : {
3370 0 : if (state.dataUnitVentilators->GetUnitVentilatorInputFlag) {
3371 0 : GetUnitVentilatorInput(state);
3372 0 : state.dataUnitVentilators->GetUnitVentilatorInputFlag = false;
3373 : }
3374 0 : for (int UnitVentNum = 1; UnitVentNum <= state.dataUnitVentilators->NumOfUnitVents; ++UnitVentNum) {
3375 0 : if (Util::SameString(state.dataUnitVentilators->UnitVent(UnitVentNum).Name, CompName)) {
3376 0 : return UnitVentNum;
3377 : }
3378 : }
3379 :
3380 0 : return 0;
3381 : }
3382 :
3383 33500 : Real64 SetOAMassFlowRateForCoolingVariablePercent(EnergyPlusData &state,
3384 : int const UnitVentNum, // Unit Ventilator index
3385 : Real64 const MinOAFrac, // Minimum Outside Air Fraction
3386 : Real64 const MassFlowRate, // Design Outside Air Mass Flow Rate
3387 : Real64 const MaxOAFrac, // Maximum Outside Air Fraction
3388 : Real64 const Tinlet, // Inlet Temperature to Unit or Zone Temperature
3389 : Real64 const Toutdoor // Outdoor Air Temperature
3390 : )
3391 : {
3392 :
3393 33500 : Real64 ActualOAMassFlowRate(0.0); // Result or return value
3394 :
3395 33500 : if (Tinlet <= Toutdoor) {
3396 :
3397 21187 : ActualOAMassFlowRate = MinOAFrac * MassFlowRate;
3398 :
3399 : } else { // Tinlet > Toutdoor
3400 : // Use cooler outside air to provide "free" cooling without over-cooling.
3401 : // First, use a simple load equals mass flow times Cp time Delta T equation to find OA Mass Flow Rate.
3402 : // This must include the enthalpy difference across the fan. Otherwise, this will potentially put a
3403 : // small load on the cooling coil (if it exists) or will leave a small load that is not met when it could be.
3404 : // Then, limit the OA Mass Flow Rate between the MinOA flow and the MaxOA flow.
3405 :
3406 12313 : auto &unitVent = state.dataUnitVentilators->UnitVent(UnitVentNum);
3407 12313 : Real64 EnthDiffAcrossFan(0.0); // Temperature difference across the fan
3408 12313 : if (!unitVent.ATMixerExists) {
3409 9956 : EnthDiffAcrossFan =
3410 9956 : state.dataLoopNodes->Node(unitVent.FanOutletNode).Enthalpy - state.dataLoopNodes->Node(unitVent.OAMixerOutNode).Enthalpy;
3411 : } else {
3412 2357 : if (unitVent.ATMixerType == HVAC::MixerType::InletSide) {
3413 2287 : EnthDiffAcrossFan =
3414 2287 : state.dataLoopNodes->Node(unitVent.FanOutletNode).Enthalpy - state.dataLoopNodes->Node(unitVent.ATMixerOutNode).Enthalpy;
3415 : }
3416 2357 : if (unitVent.ATMixerType == HVAC::MixerType::SupplySide) {
3417 70 : EnthDiffAcrossFan =
3418 70 : state.dataLoopNodes->Node(unitVent.FanOutletNode).Enthalpy - state.dataLoopNodes->Node(unitVent.AirInNode).Enthalpy;
3419 : }
3420 : }
3421 :
3422 12313 : ActualOAMassFlowRate = (std::abs(state.dataUnitVentilators->QZnReq) + (MassFlowRate * std::abs(EnthDiffAcrossFan))) /
3423 12313 : (Psychrometrics::PsyCpAirFnW(state.dataEnvrn->OutHumRat) * (Tinlet - Toutdoor));
3424 :
3425 12313 : ActualOAMassFlowRate = max(ActualOAMassFlowRate, (MinOAFrac * MassFlowRate));
3426 12313 : ActualOAMassFlowRate = min(ActualOAMassFlowRate, (MaxOAFrac * MassFlowRate));
3427 : }
3428 :
3429 33500 : return ActualOAMassFlowRate;
3430 : }
3431 :
3432 19750 : void CalcMdotCCoilCycFan(EnergyPlusData &state,
3433 : Real64 &mdot, // mass flow rate
3434 : Real64 &QCoilReq, // Remaining load to cooling coil
3435 : Real64 const QZnReq, // Zone load to setpoint
3436 : int const UnitVentNum, // Unit Ventilator index
3437 : Real64 const PartLoadRatio // Part load ratio for unit ventilator
3438 : )
3439 : {
3440 :
3441 19750 : if (QZnReq >= 0.0) { // Heating requested so no cooling coil needed
3442 8811 : mdot = 0.0;
3443 : } else { // Cooling so set first guess at flow rate
3444 10939 : mdot = state.dataUnitVentilators->UnitVent(UnitVentNum).MaxColdWaterFlow * PartLoadRatio;
3445 : }
3446 :
3447 : // Check to see what outside air will do, "turn off" cooling coil if OA can handle the load
3448 19750 : int CCoilInAirNode = state.dataUnitVentilators->UnitVent(UnitVentNum).FanOutletNode;
3449 19750 : int AirInNode = state.dataUnitVentilators->UnitVent(UnitVentNum).AirInNode;
3450 19750 : Real64 CpAirZn = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(AirInNode).HumRat);
3451 19750 : QCoilReq = QZnReq - state.dataLoopNodes->Node(CCoilInAirNode).MassFlowRate * CpAirZn *
3452 19750 : (state.dataLoopNodes->Node(CCoilInAirNode).Temp - state.dataLoopNodes->Node(AirInNode).Temp);
3453 19750 : if (QCoilReq > -HVAC::SmallLoad) {
3454 8908 : QCoilReq = 0.0;
3455 8908 : mdot = 0.0;
3456 : }
3457 19750 : }
3458 :
3459 : } // namespace UnitVentilator
3460 :
3461 : } // namespace EnergyPlus
|