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