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