Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Autosizing/Base.hh>
57 : #include <EnergyPlus/BranchNodeConnections.hh>
58 : #include <EnergyPlus/Data/EnergyPlusData.hh>
59 : #include <EnergyPlus/DataDefineEquip.hh>
60 : #include <EnergyPlus/DataEnvironment.hh>
61 : #include <EnergyPlus/DataHeatBalFanSys.hh>
62 : #include <EnergyPlus/DataIPShortCuts.hh>
63 : #include <EnergyPlus/DataLoopNode.hh>
64 : #include <EnergyPlus/DataPrecisionGlobals.hh>
65 : #include <EnergyPlus/DataSizing.hh>
66 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
67 : #include <EnergyPlus/DataZoneEquipment.hh>
68 : #include <EnergyPlus/Fans.hh>
69 : #include <EnergyPlus/FluidProperties.hh>
70 : #include <EnergyPlus/General.hh>
71 : #include <EnergyPlus/GeneralRoutines.hh>
72 : #include <EnergyPlus/GlobalNames.hh>
73 : #include <EnergyPlus/HeatingCoils.hh>
74 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
75 : #include <EnergyPlus/MixerComponent.hh>
76 : #include <EnergyPlus/NodeInputManager.hh>
77 : #include <EnergyPlus/OutputProcessor.hh>
78 : #include <EnergyPlus/OutputReportPredefined.hh>
79 : #include <EnergyPlus/Plant/DataPlant.hh>
80 : #include <EnergyPlus/PlantUtilities.hh>
81 : #include <EnergyPlus/PoweredInductionUnits.hh>
82 : #include <EnergyPlus/Psychrometrics.hh>
83 : #include <EnergyPlus/ScheduleManager.hh>
84 : #include <EnergyPlus/SteamCoils.hh>
85 : #include <EnergyPlus/UtilityRoutines.hh>
86 : #include <EnergyPlus/WaterCoils.hh>
87 : #include <EnergyPlus/ZoneAirLoopEquipmentManager.hh>
88 :
89 : namespace EnergyPlus::PoweredInductionUnits {
90 :
91 : // Module containing routines dealing with Series and Parallel fan powered terminal boxes
92 :
93 : // MODULE INFORMATION:
94 : // AUTHOR Fred Buhl
95 : // DATE WRITTEN August 2000
96 : // MODIFIED Brent Griffith, Sept 2010, plant upgrades, fluid properties
97 : // RE-ENGINEERED na
98 :
99 : // PURPOSE OF THIS MODULE:
100 : // To encapsulate the data and algorithms needed to simulate Series and Parallel
101 : // fan powered induction terminal boxes.
102 :
103 : // METHODOLOGY EMPLOYED:
104 : // The terminal boxes are modeled as a collection of components: air mixer,
105 : // fan, and heating coil plus an integrated control
106 : // algorithm that adjusts the primary air flow and the heating coil output
107 : // to meet the zone load.
108 :
109 : // Using/Aliasing
110 : using namespace DataLoopNode;
111 : using HVAC::SmallAirVolFlow;
112 : using HVAC::SmallLoad;
113 : using HVAC::SmallMassFlow;
114 : using HVAC::SmallTempDiff;
115 : using namespace ScheduleManager;
116 : using Psychrometrics::PsyCpAirFnW;
117 : using Psychrometrics::PsyHFnTdbW;
118 : using SteamCoils::SimulateSteamCoilComponents;
119 :
120 : constexpr const char *fluidNameSteam("STEAM");
121 : constexpr const char *fluidNameWater("WATER");
122 :
123 308748 : void SimPIU(EnergyPlusData &state,
124 : std::string_view CompName, // name of the PIU
125 : bool const FirstHVACIteration, // TRUE if first HVAC iteration in time step
126 : int const ZoneNum, // index of zone served by PIU
127 : int const ZoneNodeNum, // zone node number of zone served by PIU
128 : int &CompIndex // PIU Index in PIU names
129 : )
130 : {
131 :
132 : // SUBROUTINE INFORMATION:
133 : // AUTHOR Fred Buhl
134 : // DATE WRITTEN March 2000
135 : // MODIFIED na
136 : // RE-ENGINEERED na
137 :
138 : // PURPOSE OF THIS SUBROUTINE:
139 : // Manages the simulation of a fan powered induction terminal unit.
140 : // Called from SimZoneAirLoopEquipmentin module ZoneAirLoopEquipmentManager.
141 :
142 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
143 308748 : int PIUNum = 0; // index of powered induction unit being simulated
144 :
145 : // First time SimPIU is called, get the input for all the fan coil units
146 308748 : if (state.dataPowerInductionUnits->GetPIUInputFlag) {
147 14 : GetPIUs(state);
148 14 : state.dataPowerInductionUnits->GetPIUInputFlag = false;
149 : }
150 :
151 : // Get the powered induction unit index
152 308748 : if (CompIndex == 0) {
153 57 : PIUNum = Util::FindItemInList(CompName, state.dataPowerInductionUnits->PIU);
154 57 : if (PIUNum == 0) {
155 0 : ShowFatalError(state, format("SimPIU: PIU Unit not found={}", CompName));
156 : }
157 57 : CompIndex = PIUNum;
158 : } else {
159 308691 : PIUNum = CompIndex;
160 308691 : if (PIUNum > state.dataPowerInductionUnits->NumPIUs || PIUNum < 1) {
161 0 : ShowFatalError(state,
162 0 : format("SimPIU: Invalid CompIndex passed={}, Number of PIU Units={}, PIU Unit name={}",
163 : CompIndex,
164 0 : state.dataPowerInductionUnits->NumPIUs,
165 : CompName));
166 : }
167 308691 : if (state.dataPowerInductionUnits->CheckEquipName(PIUNum)) {
168 57 : if (CompName != state.dataPowerInductionUnits->PIU(PIUNum).Name) {
169 0 : ShowFatalError(state,
170 0 : format("SimPIU: Invalid CompIndex passed={}, PIU Unit name={}, stored PIU Unit Name for that index={}",
171 : CompIndex,
172 : CompName,
173 0 : state.dataPowerInductionUnits->PIU(PIUNum).Name));
174 : }
175 57 : state.dataPowerInductionUnits->CheckEquipName(PIUNum) = false;
176 : }
177 : }
178 :
179 617496 : state.dataSize->CurTermUnitSizingNum =
180 308748 : state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(PIUNum).ADUNum).TermUnitSizingNum;
181 : // initialize the unit
182 308748 : InitPIU(state, PIUNum, FirstHVACIteration);
183 :
184 308748 : state.dataSize->TermUnitPIU = true;
185 :
186 : // Select the correct unit type
187 308748 : switch (state.dataPowerInductionUnits->PIU(PIUNum).UnitType_Num) {
188 :
189 287202 : case DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat: { // 'AirTerminal:SingleDuct:SeriesPIU:Reheat'
190 :
191 287202 : CalcSeriesPIU(state, PIUNum, ZoneNum, ZoneNodeNum, FirstHVACIteration);
192 287202 : break;
193 : }
194 21546 : case DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat: { // 'AirTerminal:SingleDuct:ParallelPIU:Reheat'
195 :
196 21546 : CalcParallelPIU(state, PIUNum, ZoneNum, ZoneNodeNum, FirstHVACIteration);
197 21546 : break;
198 : }
199 0 : default:
200 0 : ShowSevereError(state, format("Illegal PI Unit Type used={}", state.dataPowerInductionUnits->PIU(PIUNum).UnitType));
201 0 : ShowContinueError(state, format("Occurs in PI Unit={}", state.dataPowerInductionUnits->PIU(PIUNum).Name));
202 0 : ShowFatalError(state, "Preceding condition causes termination.");
203 0 : break;
204 : }
205 :
206 308748 : state.dataSize->TermUnitPIU = false;
207 :
208 : // Update the current unit's outlet nodes
209 : // no update needed: reheat coil updates outlet node; inlet nodes' mass flow rate set by Calc routine
210 :
211 : // Fill the report variables
212 308748 : ReportPIU(state, PIUNum);
213 308748 : }
214 :
215 18 : void GetPIUs(EnergyPlusData &state)
216 : {
217 :
218 : // SUBROUTINE INFORMATION:
219 : // AUTHOR Fred Buhl
220 : // DATE WRITTEN August 2000
221 : // MODIFIED na
222 : // RE-ENGINEERED na
223 :
224 : // PURPOSE OF THIS SUBROUTINE:
225 : // Obtains input data for powered induction unit terminal boxes and stores it
226 : // in PIU data structures
227 :
228 : // METHODOLOGY EMPLOYED:
229 : // Uses "Get" routines to read in data.
230 :
231 : // Using/Aliasing
232 : using BranchNodeConnections::SetUpCompSets;
233 : using BranchNodeConnections::TestCompSet;
234 :
235 : using NodeInputManager::GetOnlySingleNode;
236 : using SteamCoils::GetCoilSteamInletNode;
237 : using WaterCoils::GetCoilWaterInletNode;
238 :
239 : static constexpr std::string_view routineName = "GetPIUs";
240 :
241 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
242 18 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
243 : static constexpr std::string_view RoutineName("GetPIUs: "); // include trailing blank space
244 18 : bool SteamMessageNeeded = true;
245 :
246 : // find the number of each type of fan coil unit
247 36 : state.dataPowerInductionUnits->NumSeriesPIUs =
248 18 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirTerminal:SingleDuct:SeriesPIU:Reheat");
249 36 : state.dataPowerInductionUnits->NumParallelPIUs =
250 18 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "AirTerminal:SingleDuct:ParallelPIU:Reheat");
251 18 : state.dataPowerInductionUnits->NumPIUs = state.dataPowerInductionUnits->NumSeriesPIUs + state.dataPowerInductionUnits->NumParallelPIUs;
252 :
253 18 : if (state.dataPowerInductionUnits->NumPIUs > 0) {
254 : // GetZonePlenumInput might call this routine before the AirDistUnit has been populated
255 14 : if (state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag) {
256 0 : ZoneAirLoopEquipmentManager::GetZoneAirLoopEquipment(state);
257 0 : state.dataZoneAirLoopEquipmentManager->GetAirDistUnitsFlag = false;
258 : }
259 : }
260 :
261 : // allocate the data structures
262 18 : state.dataPowerInductionUnits->PIU.allocate(state.dataPowerInductionUnits->NumPIUs);
263 18 : state.dataPowerInductionUnits->PiuUniqueNames.reserve(static_cast<unsigned>(state.dataPowerInductionUnits->NumPIUs));
264 18 : state.dataPowerInductionUnits->CheckEquipName.dimension(state.dataPowerInductionUnits->NumPIUs, true);
265 :
266 18 : int PIUNum{0};
267 18 : auto &ip = state.dataInputProcessing->inputProcessor;
268 : // loop over Series PIUs; get and load the input data
269 54 : for (const std::string cCurrentModuleObject : {"AirTerminal:SingleDuct:SeriesPIU:Reheat", "AirTerminal:SingleDuct:ParallelPIU:Reheat"}) {
270 36 : auto const &objectSchemaProps = ip->getObjectSchemaProps(state, cCurrentModuleObject);
271 36 : auto const &PIUsInstances = ip->epJSON.find(cCurrentModuleObject);
272 36 : if (PIUsInstances != ip->epJSON.end()) {
273 18 : auto &PIUInstances = PIUsInstances.value();
274 75 : for (auto instance = PIUInstances.begin(); instance != PIUInstances.end(); ++instance) {
275 57 : ++PIUNum;
276 57 : auto const &fields = instance.value();
277 :
278 57 : GlobalNames::VerifyUniqueInterObjectName(
279 57 : state, state.dataPowerInductionUnits->PiuUniqueNames, Util::makeUPPER(instance.key()), cCurrentModuleObject, "Name", ErrorsFound);
280 57 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
281 57 : thisPIU.Name = Util::makeUPPER(instance.key());
282 57 : thisPIU.UnitType = cCurrentModuleObject;
283 57 : ip->markObjectAsUsed(cCurrentModuleObject, instance.key());
284 57 : if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
285 51 : thisPIU.UnitType_Num = DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat;
286 6 : } else if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
287 6 : thisPIU.UnitType_Num = DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat;
288 : }
289 57 : thisPIU.Sched = ip->getAlphaFieldValue(fields, objectSchemaProps, "availability_schedule_name");
290 57 : if (!thisPIU.Sched.empty()) {
291 53 : thisPIU.SchedPtr = ScheduleManager::GetScheduleIndex(state, thisPIU.Sched);
292 53 : if (thisPIU.SchedPtr == 0) {
293 0 : ShowWarningError(
294 : state,
295 0 : format("GetPIUs {}=\"{}\", invalid Availability Schedule Name = {}", cCurrentModuleObject, thisPIU.Name, thisPIU.Sched));
296 0 : ShowContinueError(state, "Set the default as Always On. Simulation continues.");
297 0 : thisPIU.SchedPtr = ScheduleManager::ScheduleAlwaysOn;
298 : }
299 : } else {
300 4 : thisPIU.SchedPtr = ScheduleManager::ScheduleAlwaysOn;
301 : }
302 57 : if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
303 51 : thisPIU.MaxTotAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_air_flow_rate");
304 : }
305 57 : if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
306 6 : thisPIU.MaxSecAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_secondary_air_flow_rate");
307 : }
308 57 : thisPIU.MaxPriAirVolFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_primary_air_flow_rate");
309 57 : thisPIU.MinPriAirFlowFrac = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_primary_air_flow_fraction");
310 57 : if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
311 6 : thisPIU.FanOnFlowFrac = ip->getRealFieldValue(fields, objectSchemaProps, "fan_on_flow_fraction");
312 : }
313 57 : thisPIU.HCoilType = static_cast<HtgCoilType>(
314 57 : getEnumValue(HCoilNamesUC, Util::makeUPPER(ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"))));
315 57 : switch (thisPIU.HCoilType) {
316 56 : case HtgCoilType::SimpleHeating: {
317 56 : thisPIU.HCoil_PlantType = DataPlant::PlantEquipmentType::CoilWaterSimpleHeating;
318 56 : break;
319 : }
320 1 : case HtgCoilType::Electric:
321 : case HtgCoilType::Gas: {
322 1 : break;
323 : }
324 0 : case HtgCoilType::SteamAirHeating: {
325 0 : thisPIU.HCoil_PlantType = DataPlant::PlantEquipmentType::CoilSteamAirHeating;
326 0 : thisPIU.HCoil_FluidIndex = FluidProperties::GetRefrigNum(state, "STEAM");
327 0 : if (thisPIU.HCoil_FluidIndex == 0) {
328 0 : ShowSevereError(state, format("{} Steam Properties for {} not found.", RoutineName, thisPIU.Name));
329 0 : if (SteamMessageNeeded) {
330 0 : ShowContinueError(state, "Steam Fluid Properties should have been included in the input file.");
331 : }
332 0 : ErrorsFound = true;
333 0 : SteamMessageNeeded = false;
334 : }
335 0 : break;
336 : }
337 0 : default: {
338 0 : ShowSevereError(state, format("Illegal Reheat Coil Type = {}", thisPIU.HCoilType));
339 0 : ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
340 0 : ErrorsFound = true;
341 : }
342 : }
343 :
344 57 : auto connectionType = DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctSeriesPIUReheat;
345 57 : if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
346 6 : connectionType = DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctParallelPIUReheat;
347 : }
348 114 : thisPIU.PriAirInNode = GetOnlySingleNode(state,
349 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "supply_air_inlet_node_name"),
350 : ErrorsFound,
351 : connectionType,
352 57 : thisPIU.Name,
353 : DataLoopNode::NodeFluidType::Air,
354 : DataLoopNode::ConnectionType::Inlet,
355 : NodeInputManager::CompFluidStream::Primary,
356 : ObjectIsParent,
357 : "Supply Air Inlet Node Name");
358 :
359 114 : thisPIU.SecAirInNode = GetOnlySingleNode(state,
360 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "secondary_air_inlet_node_name"),
361 : ErrorsFound,
362 : connectionType,
363 57 : thisPIU.Name,
364 : DataLoopNode::NodeFluidType::Air,
365 : DataLoopNode::ConnectionType::Inlet,
366 : NodeInputManager::CompFluidStream::Primary,
367 : ObjectIsParent,
368 : "Secondary Air Inlet Node Name");
369 :
370 114 : thisPIU.OutAirNode = GetOnlySingleNode(state,
371 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "outlet_node_name"),
372 : ErrorsFound,
373 : connectionType,
374 57 : thisPIU.Name,
375 : DataLoopNode::NodeFluidType::Air,
376 : DataLoopNode::ConnectionType::Outlet,
377 : NodeInputManager::CompFluidStream::Primary,
378 : ObjectIsParent,
379 : "Outlet Node Name");
380 :
381 114 : thisPIU.HCoilInAirNode = GetOnlySingleNode(state,
382 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"),
383 : ErrorsFound,
384 : connectionType,
385 57 : thisPIU.Name,
386 : DataLoopNode::NodeFluidType::Air,
387 : DataLoopNode::ConnectionType::Internal,
388 : NodeInputManager::CompFluidStream::Primary,
389 : ObjectIsParent,
390 : "Reheat Coil Air Inlet Node Name");
391 : // The reheat coil control node is necessary for hot water reheat, but not necessary for
392 : // electric or gas reheat.
393 57 : if (thisPIU.HCoilType == HtgCoilType::SimpleHeating) {
394 56 : thisPIU.HotControlNode = GetCoilWaterInletNode(state,
395 112 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
396 112 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
397 : ErrorsFound);
398 : }
399 57 : if (thisPIU.HCoilType == HtgCoilType::SteamAirHeating) {
400 0 : thisPIU.HotControlNode = GetCoilSteamInletNode(state,
401 0 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
402 0 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
403 : ErrorsFound);
404 : }
405 57 : thisPIU.MixerName = ip->getAlphaFieldValue(fields, objectSchemaProps, "zone_mixer_name");
406 57 : thisPIU.FanName = ip->getAlphaFieldValue(fields, objectSchemaProps, "fan_name");
407 :
408 : // find fan type
409 : // test if Fan:SystemModel fan of this name exists
410 57 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(8)};
411 57 : if ((thisPIU.Fan_Index = Fans::GetFanIndex(state, thisPIU.FanName)) == 0) {
412 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(8), thisPIU.FanName);
413 0 : ErrorsFound = true;
414 : } else {
415 : // Assert that this is a constant volume fan?
416 57 : auto *fan = state.dataFans->fans(thisPIU.Fan_Index);
417 57 : thisPIU.fanType = fan->type;
418 57 : thisPIU.FanAvailSchedPtr = fan->availSchedNum;
419 : }
420 :
421 57 : thisPIU.HCoil = ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name");
422 57 : bool IsNotOK = false;
423 114 : ValidateComponent(
424 171 : state, HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)], thisPIU.HCoil, IsNotOK, cCurrentModuleObject + " - Heating Coil");
425 57 : if (IsNotOK) {
426 0 : ShowContinueError(state, format("In {} = {}", cCurrentModuleObject, thisPIU.Name));
427 0 : ErrorsFound = true;
428 : }
429 57 : thisPIU.MaxVolHotWaterFlow = ip->getRealFieldValue(fields, objectSchemaProps, "maximum_hot_water_or_steam_flow_rate");
430 57 : thisPIU.MinVolHotWaterFlow = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_hot_water_or_steam_flow_rate");
431 57 : thisPIU.HotControlOffset = ip->getRealFieldValue(fields, objectSchemaProps, "convergence_tolerance");
432 : // Set default convergence tolerance
433 57 : if (thisPIU.HotControlOffset <= 0.0) {
434 0 : thisPIU.HotControlOffset = 0.001;
435 : }
436 :
437 : // Variable speed fan inputs
438 114 : std::string fan_control_type = "ConstantSpeed";
439 57 : fan_control_type = ip->getAlphaFieldValue(fields, objectSchemaProps, "fan_control_type");
440 57 : thisPIU.fanControlType = FanCntrlType::ConstantSpeedFan;
441 57 : if (Util::SameString(fan_control_type, "VariableSpeed")) {
442 4 : thisPIU.fanControlType = FanCntrlType::VariableSpeedFan;
443 4 : if (thisPIU.fanType != HVAC::FanType::SystemModel) {
444 0 : ErrorsFound = true;
445 0 : ShowSevereError(state, format("Fan type must be Fan:SystemModel when Fan Control Type = {}", fan_control_type));
446 0 : ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
447 : }
448 53 : } else if (Util::SameString(fan_control_type, "ConstantSpeed")) {
449 53 : thisPIU.fanControlType = FanCntrlType::ConstantSpeedFan;
450 : } else {
451 0 : ShowSevereError(state, format("Illegal Fan Control Type = {}", fan_control_type));
452 0 : ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
453 0 : ErrorsFound = true;
454 : }
455 :
456 171 : std::string const heating_control_type = ip->getAlphaFieldValue(fields, objectSchemaProps, "heating_control_type");
457 57 : thisPIU.heatingControlType = HeatCntrlBehaviorType::Invalid;
458 57 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
459 4 : if (Util::SameString(heating_control_type, "Staged")) {
460 2 : thisPIU.heatingControlType = HeatCntrlBehaviorType::StagedHeaterBehavior;
461 2 : } else if (Util::SameString(heating_control_type, "Modulated")) {
462 2 : thisPIU.heatingControlType = HeatCntrlBehaviorType::ModulatedHeaterBehavior;
463 : } else {
464 0 : ShowSevereError(state, format("Illegal Heating Control Type = {}", heating_control_type));
465 0 : ShowContinueError(state, format("Occurs in {} = {}", cCurrentModuleObject, thisPIU.Name));
466 0 : ErrorsFound = true;
467 : }
468 : }
469 :
470 57 : thisPIU.MinFanTurnDownRatio = ip->getRealFieldValue(fields, objectSchemaProps, "minimum_fan_turn_down_ratio");
471 57 : thisPIU.designHeatingDAT = ip->getRealFieldValue(fields, objectSchemaProps, "design_heating_discharge_air_temperature");
472 57 : thisPIU.highLimitDAT = ip->getRealFieldValue(fields, objectSchemaProps, "high_limit_heating_discharge_air_temperature");
473 :
474 : // Add fan to component sets array
475 57 : if (cCurrentModuleObject == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
476 102 : SetUpCompSets(state,
477 : thisPIU.UnitType,
478 : thisPIU.Name,
479 : "UNDEFINED",
480 : thisPIU.FanName,
481 : "UNDEFINED",
482 102 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"));
483 6 : } else if (cCurrentModuleObject == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
484 12 : SetUpCompSets(state,
485 : thisPIU.UnitType,
486 : thisPIU.Name,
487 : "UNDEFINED",
488 : thisPIU.FanName,
489 12 : ip->getAlphaFieldValue(fields, objectSchemaProps, "secondary_air_inlet_node_name"),
490 : "UNDEFINED");
491 : }
492 :
493 : // Add reheat coil to component sets array
494 285 : SetUpCompSets(state,
495 : thisPIU.UnitType,
496 : thisPIU.Name,
497 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_object_type"),
498 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_name"),
499 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "reheat_coil_air_inlet_node_name"),
500 114 : ip->getAlphaFieldValue(fields, objectSchemaProps, "outlet_node_name"));
501 :
502 : // Register component set data
503 114 : TestCompSet(state,
504 : thisPIU.UnitType,
505 : thisPIU.Name,
506 57 : state.dataLoopNodes->NodeID(thisPIU.PriAirInNode),
507 57 : state.dataLoopNodes->NodeID(thisPIU.OutAirNode),
508 : "Air Nodes");
509 :
510 499 : for (int ADUNum = 1; ADUNum <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++ADUNum) {
511 442 : if (thisPIU.OutAirNode == state.dataDefineEquipment->AirDistUnit(ADUNum).OutletNodeNum) {
512 57 : thisPIU.ADUNum = ADUNum;
513 57 : state.dataDefineEquipment->AirDistUnit(ADUNum).InletNodeNum = thisPIU.PriAirInNode;
514 : }
515 : }
516 : // one assumes if there isn't one assigned, it's an error?
517 57 : if (thisPIU.ADUNum == 0) {
518 0 : ShowSevereError(state,
519 0 : format("{}No matching Air Distribution Unit, for PIU = [{},{}].", RoutineName, thisPIU.UnitType, thisPIU.Name));
520 0 : ShowContinueError(state, format("...should have outlet node = {}", state.dataLoopNodes->NodeID(thisPIU.OutAirNode)));
521 0 : ErrorsFound = true;
522 : } else {
523 :
524 57 : bool AirNodeFound = false;
525 : // Fill the Zone Equipment data with the supply air inlet node number of this unit.
526 580 : for (int CtrlZone = 1; CtrlZone <= state.dataGlobal->NumOfZones; ++CtrlZone) {
527 523 : if (!state.dataZoneEquip->ZoneEquipConfig(CtrlZone).IsControlled) {
528 81 : continue;
529 : }
530 1037 : for (int SupAirIn = 1; SupAirIn <= state.dataZoneEquip->ZoneEquipConfig(CtrlZone).NumInletNodes; ++SupAirIn) {
531 652 : if (thisPIU.OutAirNode == state.dataZoneEquip->ZoneEquipConfig(CtrlZone).InletNode(SupAirIn)) {
532 57 : state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).InNode = thisPIU.PriAirInNode;
533 57 : state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).OutNode = thisPIU.OutAirNode;
534 57 : state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).TermUnitSizingNum =
535 57 : state.dataZoneEquip->ZoneEquipConfig(CtrlZone).AirDistUnitCool(SupAirIn).TermUnitSizingIndex;
536 57 : state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).ZoneEqNum = CtrlZone;
537 57 : AirNodeFound = true;
538 57 : thisPIU.CtrlZoneNum = CtrlZone; // fill index for later use in finding air loop index
539 57 : thisPIU.ctrlZoneInNodeIndex = SupAirIn;
540 57 : break;
541 : }
542 : }
543 : }
544 57 : if (!AirNodeFound) {
545 0 : ShowSevereError(state, format("The outlet air node from the {} Unit = {}", cCurrentModuleObject, thisPIU.Name));
546 0 : ShowContinueError(
547 0 : state, format("did not have a matching Zone Equipment Inlet Node, Node = {}", state.dataIPShortCut->cAlphaArgs(5)));
548 0 : ErrorsFound = true;
549 : }
550 : }
551 18 : }
552 : }
553 36 : }
554 :
555 18 : if (ErrorsFound) {
556 0 : ShowFatalError(state, format("{} Errors found in getting input. Preceding conditions cause termination.", RoutineName));
557 : }
558 :
559 75 : for (int PIUNum = 1; PIUNum <= state.dataPowerInductionUnits->NumPIUs; ++PIUNum) {
560 57 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
561 :
562 : // Setup Report variables for the PIUs
563 114 : SetupOutputVariable(state,
564 : "Zone Air Terminal Primary Damper Position",
565 : Constant::Units::None,
566 57 : thisPIU.PriDamperPosition,
567 : OutputProcessor::TimeStepType::System,
568 : OutputProcessor::StoreType::Average,
569 57 : thisPIU.Name);
570 114 : SetupOutputVariable(state,
571 : "Zone Air Terminal Heating Rate",
572 : Constant::Units::W,
573 57 : thisPIU.HeatingRate,
574 : OutputProcessor::TimeStepType::System,
575 : OutputProcessor::StoreType::Average,
576 57 : thisPIU.Name);
577 114 : SetupOutputVariable(state,
578 : "Zone Air Terminal Heating Energy",
579 : Constant::Units::J,
580 57 : thisPIU.HeatingEnergy,
581 : OutputProcessor::TimeStepType::System,
582 : OutputProcessor::StoreType::Sum,
583 57 : thisPIU.Name);
584 114 : SetupOutputVariable(state,
585 : "Zone Air Terminal Sensible Cooling Rate",
586 : Constant::Units::W,
587 57 : thisPIU.SensCoolRate,
588 : OutputProcessor::TimeStepType::System,
589 : OutputProcessor::StoreType::Average,
590 57 : thisPIU.Name);
591 114 : SetupOutputVariable(state,
592 : "Zone Air Terminal Sensible Cooling Energy",
593 : Constant::Units::J,
594 57 : thisPIU.SensCoolEnergy,
595 : OutputProcessor::TimeStepType::System,
596 : OutputProcessor::StoreType::Sum,
597 57 : thisPIU.Name);
598 114 : SetupOutputVariable(state,
599 : "Zone Air Terminal Outdoor Air Volume Flow Rate",
600 : Constant::Units::m3_s,
601 57 : thisPIU.OutdoorAirFlowRate,
602 : OutputProcessor::TimeStepType::System,
603 : OutputProcessor::StoreType::Average,
604 57 : thisPIU.Name);
605 114 : SetupOutputVariable(state,
606 : "Zone Air Terminal Total Air Mass Flow Rate",
607 : Constant::Units::kg_s,
608 57 : thisPIU.TotMassFlowRate,
609 : OutputProcessor::TimeStepType::System,
610 : OutputProcessor::StoreType::Average,
611 57 : state.dataPowerInductionUnits->PIU(PIUNum).Name);
612 114 : SetupOutputVariable(state,
613 : "Zone Air Terminal Primary Air Mass Flow Rate",
614 : Constant::Units::kg_s,
615 57 : thisPIU.PriMassFlowRate,
616 : OutputProcessor::TimeStepType::System,
617 : OutputProcessor::StoreType::Average,
618 57 : state.dataPowerInductionUnits->PIU(PIUNum).Name);
619 114 : SetupOutputVariable(state,
620 : "Zone Air Terminal Secondary Air Mass Flow Rate",
621 : Constant::Units::kg_s,
622 57 : thisPIU.SecMassFlowRate,
623 : OutputProcessor::TimeStepType::System,
624 : OutputProcessor::StoreType::Average,
625 57 : state.dataPowerInductionUnits->PIU(PIUNum).Name);
626 114 : SetupOutputVariable(state,
627 : "Zone Air Terminal Outlet Discharge Air Temperature",
628 : Constant::Units::C,
629 57 : thisPIU.DischargeAirTemp,
630 : OutputProcessor::TimeStepType::System,
631 : OutputProcessor::StoreType::Average,
632 57 : state.dataPowerInductionUnits->PIU(PIUNum).Name);
633 57 : SetupOutputVariable(state,
634 : "Zone Air Terminal Current Operation Control Stage",
635 : Constant::Units::unknown,
636 57 : thisPIU.CurOperationControlStage,
637 : OutputProcessor::TimeStepType::System,
638 : OutputProcessor::StoreType::Average,
639 57 : state.dataPowerInductionUnits->PIU(PIUNum).Name);
640 : }
641 18 : }
642 :
643 308748 : void InitPIU(EnergyPlusData &state,
644 : int const PIUNum, // number of the current fan coil unit being simulated
645 : bool const FirstHVACIteration // TRUE if first zone equip this HVAC step
646 : )
647 : {
648 :
649 : // SUBROUTINE INFORMATION:
650 : // AUTHOR Fred Buhl
651 : // DATE WRITTEN August 2000
652 : // MODIFIED na
653 : // RE-ENGINEERED na
654 :
655 : // PURPOSE OF THIS SUBROUTINE:
656 : // This subroutine is for initializations of the powered induction unit
657 : // terminal boxe.
658 :
659 : // METHODOLOGY EMPLOYED:
660 : // Uses the status flags to trigger initializations.
661 :
662 : // Using/Aliasing
663 :
664 : using DataZoneEquipment::CheckZoneEquipmentList;
665 : using PlantUtilities::InitComponentNodes;
666 : using PlantUtilities::ScanPlantLoopsForObject;
667 :
668 308748 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
669 : // SUBROUTINE PARAMETER DEFINITIONS:
670 : static constexpr std::string_view RoutineName("InitPIU");
671 :
672 : // Do the one time initializations
673 308748 : if (state.dataPowerInductionUnits->MyOneTimeFlag) {
674 14 : state.dataPowerInductionUnits->MyEnvrnFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
675 14 : state.dataPowerInductionUnits->MySizeFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
676 14 : state.dataPowerInductionUnits->MyPlantScanFlag.dimension(state.dataPowerInductionUnits->NumPIUs, true);
677 14 : state.dataPowerInductionUnits->MyOneTimeFlag = false;
678 : }
679 :
680 308748 : if (state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) && allocated(state.dataPlnt->PlantLoop)) {
681 57 : if ((thisPIU.HCoil_PlantType == DataPlant::PlantEquipmentType::CoilWaterSimpleHeating) ||
682 1 : (thisPIU.HCoil_PlantType == DataPlant::PlantEquipmentType::CoilSteamAirHeating)) {
683 56 : bool errFlag = false;
684 56 : ScanPlantLoopsForObject(state, thisPIU.HCoil, thisPIU.HCoil_PlantType, thisPIU.HWplantLoc, errFlag, _, _, _, _, _);
685 56 : if (errFlag) {
686 0 : ShowFatalError(state, "InitPIU: Program terminated due to previous condition(s).");
687 : }
688 56 : thisPIU.HotCoilOutNodeNum = DataPlant::CompData::getPlantComponent(state, thisPIU.HWplantLoc).NodeNumOut;
689 : }
690 57 : state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) = false;
691 308691 : } else if (state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) && !state.dataGlobal->AnyPlantInModel) {
692 0 : state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum) = false;
693 : }
694 :
695 308748 : if (!state.dataPowerInductionUnits->ZoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
696 14 : state.dataPowerInductionUnits->ZoneEquipmentListChecked = true;
697 : // Check to see if there is a Air Distribution Unit on the Zone Equipment List
698 71 : for (int Loop = 1; Loop <= state.dataPowerInductionUnits->NumPIUs; ++Loop) {
699 57 : if (state.dataPowerInductionUnits->PIU(Loop).ADUNum == 0) {
700 0 : continue;
701 : }
702 114 : if (CheckZoneEquipmentList(state,
703 : "ZoneHVAC:AirDistributionUnit",
704 57 : state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(Loop).ADUNum).Name)) {
705 57 : continue;
706 : }
707 0 : ShowSevereError(state,
708 0 : format("InitPIU: ADU=[Air Distribution Unit,{}] is not on any ZoneHVAC:EquipmentList.",
709 0 : state.dataDefineEquipment->AirDistUnit(state.dataPowerInductionUnits->PIU(Loop).ADUNum).Name));
710 0 : ShowContinueError(state,
711 0 : format("...PIU=[{},{}] will not be simulated.",
712 0 : state.dataPowerInductionUnits->PIU(Loop).UnitType,
713 0 : state.dataPowerInductionUnits->PIU(Loop).Name));
714 : }
715 : }
716 :
717 308805 : if (!state.dataGlobal->SysSizingCalc && state.dataPowerInductionUnits->MySizeFlag(PIUNum) &&
718 57 : !state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum)) {
719 :
720 57 : SizePIU(state, PIUNum);
721 :
722 : // If there's a hot water control node number defined in PIU
723 57 : if (thisPIU.HotControlNode > 0) {
724 : // plant upgrade note? why no separate handling of steam coil? add it ?
725 : // local plant fluid density
726 56 : Real64 const rho = FluidProperties::GetDensityGlycol(state,
727 56 : state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).FluidName,
728 : Constant::HWInitConvTemp,
729 56 : state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).FluidIndex,
730 : RoutineName);
731 :
732 56 : thisPIU.MaxHotWaterFlow = rho * thisPIU.MaxVolHotWaterFlow;
733 56 : thisPIU.MinHotWaterFlow = rho * thisPIU.MinVolHotWaterFlow;
734 56 : InitComponentNodes(state, thisPIU.MinHotWaterFlow, thisPIU.MaxHotWaterFlow, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum);
735 : }
736 :
737 57 : state.dataPowerInductionUnits->MySizeFlag(PIUNum) = false;
738 : }
739 :
740 308748 : int const PriNode = thisPIU.PriAirInNode;
741 308748 : int const SecNode = thisPIU.SecAirInNode;
742 :
743 : // Do the Begin Environment initializations
744 308748 : if (state.dataGlobal->BeginEnvrnFlag && state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum)) {
745 321 : Real64 const RhoAir = state.dataEnvrn->StdRhoAir;
746 321 : int const OutletNode = thisPIU.OutAirNode;
747 : // set the mass flow rates from the input volume flow rates
748 321 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
749 287 : thisPIU.MaxTotAirMassFlow = RhoAir * thisPIU.MaxTotAirVolFlow;
750 287 : thisPIU.MaxPriAirMassFlow = RhoAir * thisPIU.MaxPriAirVolFlow;
751 287 : thisPIU.MinPriAirMassFlow = RhoAir * thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
752 287 : state.dataLoopNodes->Node(PriNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
753 287 : state.dataLoopNodes->Node(PriNode).MassFlowRateMin = thisPIU.MinPriAirMassFlow;
754 287 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = thisPIU.MaxTotAirMassFlow;
755 : } else {
756 : // parallel
757 34 : thisPIU.MaxPriAirMassFlow = RhoAir * thisPIU.MaxPriAirVolFlow;
758 34 : thisPIU.MinPriAirMassFlow = RhoAir * thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
759 34 : thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
760 34 : thisPIU.FanOnAirMassFlow = RhoAir * thisPIU.FanOnFlowFrac * thisPIU.MaxPriAirVolFlow;
761 34 : state.dataLoopNodes->Node(PriNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
762 34 : state.dataLoopNodes->Node(PriNode).MassFlowRateMin = thisPIU.MinPriAirMassFlow;
763 34 : state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
764 : }
765 321 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
766 20 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
767 10 : thisPIU.MinTotAirMassFlow = thisPIU.MaxTotAirMassFlow * thisPIU.MinFanTurnDownRatio;
768 10 : thisPIU.MaxSecAirVolFlow = thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow;
769 10 : thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
770 10 : thisPIU.MinSecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
771 : } else {
772 10 : thisPIU.MaxSecAirMassFlow = RhoAir * thisPIU.MaxSecAirVolFlow;
773 10 : thisPIU.MinSecAirMassFlow = max(0.0, thisPIU.MaxSecAirMassFlow * thisPIU.MinFanTurnDownRatio);
774 10 : thisPIU.MinTotAirMassFlow = thisPIU.MinSecAirMassFlow + thisPIU.MinPriAirMassFlow;
775 : }
776 : }
777 :
778 637 : if (((thisPIU.HCoilType == HtgCoilType::SimpleHeating) || (thisPIU.HCoilType == HtgCoilType::SteamAirHeating)) &&
779 316 : !state.dataPowerInductionUnits->MyPlantScanFlag(PIUNum)) {
780 316 : InitComponentNodes(state, thisPIU.MinHotWaterFlow, thisPIU.MaxHotWaterFlow, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum);
781 : }
782 :
783 321 : if (thisPIU.AirLoopNum == 0) { // fill air loop index
784 57 : if (thisPIU.CtrlZoneNum > 0 && thisPIU.ctrlZoneInNodeIndex > 0) {
785 57 : thisPIU.AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(thisPIU.CtrlZoneNum).InletNodeAirLoopNum(thisPIU.ctrlZoneInNodeIndex);
786 57 : state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).AirLoopNum = thisPIU.AirLoopNum;
787 : }
788 : }
789 :
790 321 : state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum) = false;
791 : } // end one time inits
792 :
793 308748 : if (!state.dataGlobal->BeginEnvrnFlag) {
794 305926 : state.dataPowerInductionUnits->MyEnvrnFlag(PIUNum) = true;
795 : }
796 :
797 : // Do the start of HVAC time step initializations
798 308748 : if (FirstHVACIteration) {
799 : // check for upstream zero flow. If nonzero and schedule ON, set primary flow to max
800 144051 : if (GetCurrentScheduleValue(state, thisPIU.SchedPtr) > 0.0 && state.dataLoopNodes->Node(PriNode).MassFlowRate > 0.0) {
801 113738 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
802 103002 : state.dataLoopNodes->Node(PriNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
803 103002 : state.dataLoopNodes->Node(SecNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MaxPriAirMassFlow);
804 : } else {
805 10736 : state.dataLoopNodes->Node(PriNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
806 10736 : state.dataLoopNodes->Node(SecNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
807 : }
808 : } else {
809 30313 : state.dataLoopNodes->Node(PriNode).MassFlowRate = 0.0;
810 30313 : state.dataLoopNodes->Node(SecNode).MassFlowRate = 0.0;
811 : }
812 : // reset the max and min avail flows
813 144051 : if (GetCurrentScheduleValue(state, thisPIU.SchedPtr) > 0.0 && state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail > 0.0) {
814 113738 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
815 103002 : state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = thisPIU.MaxPriAirMassFlow;
816 103002 : state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = thisPIU.MinPriAirMassFlow;
817 103002 : state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
818 103002 : state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MaxPriAirMassFlow);
819 : } else {
820 10736 : state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = thisPIU.MaxPriAirMassFlow;
821 10736 : state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = thisPIU.MinPriAirMassFlow;
822 10736 : state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = thisPIU.MaxSecAirMassFlow;
823 10736 : state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = 0.0;
824 : }
825 : } else {
826 30313 : state.dataLoopNodes->Node(PriNode).MassFlowRateMaxAvail = 0.0;
827 30313 : state.dataLoopNodes->Node(PriNode).MassFlowRateMinAvail = 0.0;
828 30313 : state.dataLoopNodes->Node(SecNode).MassFlowRateMaxAvail = 0.0;
829 30313 : state.dataLoopNodes->Node(SecNode).MassFlowRateMinAvail = 0.0;
830 : }
831 : }
832 :
833 : // Do the following initializations every time step
834 :
835 : // None needed
836 308748 : }
837 :
838 57 : void SizePIU(EnergyPlusData &state, int const PIUNum)
839 : {
840 :
841 : // SUBROUTINE INFORMATION:
842 : // AUTHOR Fred Buhl
843 : // DATE WRITTEN January 2002
844 : // MODIFIED August 2013 Daeho Kang, add component sizing table entries
845 : // RE-ENGINEERED na
846 :
847 : // PURPOSE OF THIS SUBROUTINE:
848 : // This subroutine is for sizing PIU terminal units for which flow rates have not been
849 : // specified in the input.
850 :
851 : // METHODOLOGY EMPLOYED:
852 : // Obtains flow rates from the zone or system sizing arrays.
853 :
854 : // Using/Aliasing
855 : using namespace DataSizing;
856 : using FluidProperties::GetDensityGlycol;
857 : using FluidProperties::GetSpecificHeatGlycol;
858 : using SteamCoils::GetCoilSteamInletNode;
859 : using SteamCoils::GetCoilSteamOutletNode;
860 : using WaterCoils::GetCoilWaterInletNode;
861 : using WaterCoils::GetCoilWaterOutletNode;
862 : using WaterCoils::SetCoilDesFlow;
863 :
864 : using PlantUtilities::MyPlantSizingIndex;
865 :
866 : // SUBROUTINE PARAMETER DEFINITIONS:
867 : static constexpr std::string_view RoutineName("SizePIU");
868 :
869 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
870 57 : bool IsAutoSize = false; // Indicator to autosize
871 57 : bool IsMaxPriFlowAutoSize = false; // Indicate if the maximum terminal flow is autosize
872 57 : int SysSizNum = 0; // System sizing number
873 57 : Real64 DesCoilLoad = 0.0;
874 57 : bool ErrorsFound = false;
875 :
876 57 : auto &TermUnitSizing(state.dataSize->TermUnitSizing);
877 57 : auto &CurTermUnitSizingNum(state.dataSize->CurTermUnitSizingNum);
878 57 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
879 :
880 57 : if (thisPIU.MaxPriAirVolFlow == AutoSize) {
881 57 : IsAutoSize = true;
882 : }
883 57 : if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
884 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
885 0 : if (thisPIU.MaxPriAirVolFlow > 0.0) {
886 0 : BaseSizer::reportSizerOutput(
887 : state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Primary Air Flow Rate [m3/s]", thisPIU.MaxPriAirVolFlow);
888 : }
889 : } else {
890 57 : CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
891 : // Autosized maximum primary air flow for reporting
892 57 : Real64 MaxPriAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
893 57 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
894 57 : if (MaxPriAirVolFlowDes < SmallAirVolFlow) {
895 0 : MaxPriAirVolFlowDes = 0.0;
896 : }
897 :
898 57 : if (IsAutoSize) {
899 57 : thisPIU.MaxPriAirVolFlow = MaxPriAirVolFlowDes;
900 57 : IsMaxPriFlowAutoSize = true;
901 57 : BaseSizer::reportSizerOutput(
902 : state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Primary Air Flow Rate [m3/s]", MaxPriAirVolFlowDes);
903 : } else {
904 0 : if (thisPIU.MaxPriAirVolFlow > 0.0 && MaxPriAirVolFlowDes > 0.0) {
905 : // Hardsized maximum primary air flow for reporting
906 0 : Real64 const MaxPriAirVolFlowUser = thisPIU.MaxPriAirVolFlow;
907 0 : BaseSizer::reportSizerOutput(state,
908 : thisPIU.UnitType,
909 : thisPIU.Name,
910 : "Design Size Maximum Primary Air Flow Rate [m3/s]",
911 : MaxPriAirVolFlowDes,
912 : "User-Specified Maximum Primary Air Flow Rate [m3/s]",
913 : MaxPriAirVolFlowUser);
914 0 : if (state.dataGlobal->DisplayExtraWarnings) {
915 0 : if ((std::abs(MaxPriAirVolFlowDes - MaxPriAirVolFlowUser) / MaxPriAirVolFlowUser) >
916 0 : state.dataSize->AutoVsHardSizingThreshold) {
917 0 : ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
918 0 : ShowContinueError(state, format("User-Specified Primary Air Flow Rate of {:.5R} [m3/s]", MaxPriAirVolFlowUser));
919 0 : ShowContinueError(state, format("differs from Design Size Primary Air Flow Rate of {:.5R} [m3/s]", MaxPriAirVolFlowDes));
920 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
921 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
922 : }
923 : }
924 : }
925 : }
926 : }
927 : }
928 :
929 57 : IsAutoSize = false;
930 57 : if (thisPIU.MaxTotAirVolFlow == AutoSize) {
931 51 : IsAutoSize = true;
932 : }
933 57 : if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
934 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
935 0 : if (thisPIU.MaxTotAirVolFlow > 0.0) {
936 0 : BaseSizer::reportSizerOutput(
937 : state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Air Flow Rate [m3/s]", thisPIU.MaxTotAirVolFlow);
938 : }
939 : } else {
940 57 : CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
941 : // Autosized maximum air flow for reporting
942 57 : Real64 MaxTotAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
943 57 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
944 57 : if (MaxTotAirVolFlowDes < SmallAirVolFlow) {
945 0 : MaxTotAirVolFlowDes = 0.0;
946 : }
947 57 : if (IsAutoSize) {
948 51 : thisPIU.MaxTotAirVolFlow = MaxTotAirVolFlowDes;
949 51 : BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Air Flow Rate [m3/s]", MaxTotAirVolFlowDes);
950 : } else {
951 6 : if (thisPIU.MaxTotAirVolFlow > 0.0 && MaxTotAirVolFlowDes > 0.0) {
952 : // Hardsized maximum air flow for reporting
953 0 : Real64 const MaxTotAirVolFlowUser = thisPIU.MaxTotAirVolFlow;
954 0 : BaseSizer::reportSizerOutput(state,
955 : thisPIU.UnitType,
956 : thisPIU.Name,
957 : "Design Size Maximum Air Flow Rate [m3/s]",
958 : MaxTotAirVolFlowDes,
959 : "User-Specified Maximum Air Flow Rate [m3/s]",
960 : MaxTotAirVolFlowUser);
961 0 : if (state.dataGlobal->DisplayExtraWarnings) {
962 0 : if ((std::abs(MaxTotAirVolFlowDes - MaxTotAirVolFlowUser) / MaxTotAirVolFlowUser) >
963 0 : state.dataSize->AutoVsHardSizingThreshold) {
964 0 : ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
965 0 : ShowContinueError(state, format("User-Specified Maximum Air Flow Rate of {:.5R} [m3/s]", MaxTotAirVolFlowUser));
966 0 : ShowContinueError(state, format("differs from Design Size Maximum Air Flow Rate of {:.5R} [m3/s]", MaxTotAirVolFlowDes));
967 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
968 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
969 : }
970 : }
971 : }
972 : }
973 : }
974 : }
975 :
976 : // if a sizing run has been done, check if system sizing has been done for this system
977 57 : bool SizingDesRunThisAirSys = false;
978 57 : if (state.dataSize->SysSizingRunDone) {
979 57 : int const AirLoopNum = state.dataZoneEquip->ZoneEquipConfig(thisPIU.CtrlZoneNum).InletNodeAirLoopNum(thisPIU.ctrlZoneInNodeIndex);
980 57 : if (AirLoopNum > 0) {
981 57 : CheckThisAirSystemForSizing(state, AirLoopNum, SizingDesRunThisAirSys);
982 : }
983 :
984 : // get system sizing id if a sizing run has been done for this system
985 57 : if (SizingDesRunThisAirSys) {
986 57 : SysSizNum = Util::FindItemInList(
987 57 : state.dataSize->FinalSysSizing(AirLoopNum).AirPriLoopName, state.dataSize->SysSizInput, &SystemSizingInputData::AirPriLoopName);
988 57 : if (SysSizNum == 0) {
989 0 : SysSizNum = 1; // use first when none applicable
990 : }
991 : }
992 : }
993 :
994 57 : IsAutoSize = false;
995 57 : if (thisPIU.MaxSecAirVolFlow == AutoSize) {
996 6 : IsAutoSize = true;
997 : }
998 57 : if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
999 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1000 0 : if (thisPIU.MaxSecAirVolFlow > 0.0) {
1001 0 : BaseSizer::reportSizerOutput(
1002 : state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Secondary Air Flow Rate [m3/s]", thisPIU.MaxSecAirVolFlow);
1003 : }
1004 : } else {
1005 57 : CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
1006 : // Autosized maximum secondary air flow for reporting
1007 57 : Real64 MaxSecAirVolFlowDes = max(state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesCoolVolFlow,
1008 57 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatVolFlow);
1009 57 : if (MaxSecAirVolFlowDes < SmallAirVolFlow) {
1010 0 : MaxSecAirVolFlowDes = 0.0;
1011 : }
1012 57 : if (IsAutoSize) {
1013 6 : thisPIU.MaxSecAirVolFlow = MaxSecAirVolFlowDes;
1014 6 : BaseSizer::reportSizerOutput(
1015 : state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Secondary Air Flow Rate [m3/s]", MaxSecAirVolFlowDes);
1016 : } else {
1017 51 : if (thisPIU.MaxSecAirVolFlow > 0.0 && MaxSecAirVolFlowDes > 0.0) {
1018 : // Harsized maximum secondary air flow for reporting
1019 0 : Real64 const MaxSecAirVolFlowUser = thisPIU.MaxSecAirVolFlow;
1020 0 : BaseSizer::reportSizerOutput(state,
1021 : thisPIU.UnitType,
1022 : thisPIU.Name,
1023 : "Design Size Maximum Secondary Air Flow Rate [m3/s]",
1024 : MaxSecAirVolFlowDes,
1025 : "User-Specified Maximum Secondary Air Flow Rate [m3/s]",
1026 : MaxSecAirVolFlowUser);
1027 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1028 0 : if ((std::abs(MaxSecAirVolFlowDes - MaxSecAirVolFlowUser) / MaxSecAirVolFlowUser) >
1029 0 : state.dataSize->AutoVsHardSizingThreshold) {
1030 0 : ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
1031 0 : ShowContinueError(state, format("User-Specified Maximum Secondary Air Flow Rate of {:.5R} [m3/s]", MaxSecAirVolFlowUser));
1032 0 : ShowContinueError(
1033 0 : state, format("differs from Design Size Maximum Secondary Air Flow Rate of {:.5R} [m3/s]", MaxSecAirVolFlowDes));
1034 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1035 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1036 : }
1037 : }
1038 : }
1039 : }
1040 : }
1041 : }
1042 :
1043 57 : IsAutoSize = false;
1044 57 : if (thisPIU.MinPriAirFlowFrac == AutoSize) {
1045 57 : IsAutoSize = true;
1046 : }
1047 57 : if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
1048 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1049 0 : if (thisPIU.MinPriAirFlowFrac > 0.0) {
1050 0 : BaseSizer::reportSizerOutput(
1051 : state, thisPIU.UnitType, thisPIU.Name, "User-Specified Minimum Primary Air Flow Fraction", thisPIU.MinPriAirFlowFrac);
1052 : }
1053 : } else {
1054 57 : CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
1055 : // Autosized minimum primary air flow fraction for reporting
1056 57 : Real64 MinPriAirFlowFracDes = 0.0;
1057 114 : if (thisPIU.MaxPriAirVolFlow >= SmallAirVolFlow &&
1058 57 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).MinOA >= SmallAirVolFlow) {
1059 57 : MinPriAirFlowFracDes = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).MinOA / thisPIU.MaxPriAirVolFlow;
1060 : }
1061 57 : if (SizingDesRunThisAirSys) {
1062 57 : if (state.dataSize->SysSizInput(SysSizNum).SystemOAMethod == SysOAMethod::SP) { // 62.1 simplified procedure
1063 2 : if (thisPIU.MaxPriAirVolFlow > 0.0) {
1064 2 : MinPriAirFlowFracDes = 1.5 *
1065 2 : max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VozClgByZone,
1066 2 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VozHtgByZone) /
1067 2 : thisPIU.MaxPriAirVolFlow;
1068 :
1069 : // adjust maximum flow rate
1070 2 : if (MinPriAirFlowFracDes > 1.0 && IsMaxPriFlowAutoSize) {
1071 0 : thisPIU.MaxPriAirVolFlow *= MinPriAirFlowFracDes;
1072 0 : MinPriAirFlowFracDes = 1.0;
1073 0 : ShowWarningError(state,
1074 0 : format("SingleDuctSystem:SizeSys: Autosized maximum air flow rate for {} was increased to meet the zone "
1075 : "primary air flow determined according to the ASHRAE Standard 62.1 Simplified Procedure.",
1076 0 : thisPIU.Name));
1077 2 : } else if (MinPriAirFlowFracDes > 1.0) {
1078 0 : ShowWarningError(
1079 : state,
1080 0 : format("SingleDuctSystem:SizeSys: Maximum primary air flow rate for {} is potentially too low.", thisPIU.Name));
1081 0 : ShowContinueError(state,
1082 : "The flow is lower than the minimum primary air flow rate calculated following the ASHRAE Standard "
1083 : "62.1 Simplified Procedure:");
1084 0 : ShowContinueError(state, format(" User-specified maximum primary air flow rate: {:.3R} m3/s.", thisPIU.MaxPriAirVolFlow));
1085 0 : ShowContinueError(
1086 : state,
1087 0 : format(" Calculated minimum primary air flow rate: {:.3R} m3/s.", thisPIU.MaxPriAirVolFlow * MinPriAirFlowFracDes));
1088 0 : MinPriAirFlowFracDes = 1.0;
1089 : }
1090 : }
1091 : }
1092 : }
1093 57 : if (IsAutoSize) {
1094 57 : if (SizingDesRunThisAirSys) {
1095 57 : if (state.dataSize->SysSizInput(SysSizNum).SystemOAMethod == SysOAMethod::SP) {
1096 2 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).VpzMinByZoneSPSized = true;
1097 : }
1098 : }
1099 57 : thisPIU.MinPriAirFlowFrac = MinPriAirFlowFracDes;
1100 57 : BaseSizer::reportSizerOutput(
1101 : state, thisPIU.UnitType, thisPIU.Name, "Design Size Minimum Primary Air Flow Fraction", MinPriAirFlowFracDes);
1102 : } else {
1103 0 : if (thisPIU.MinPriAirFlowFrac > 0.0 && MinPriAirFlowFracDes > 0.0) {
1104 : // Hardsized minimum primary air flow fraction for reporting
1105 0 : Real64 const MinPriAirFlowFracUser = thisPIU.MinPriAirFlowFrac;
1106 0 : BaseSizer::reportSizerOutput(state,
1107 : thisPIU.UnitType,
1108 : thisPIU.Name,
1109 : "Design Size Minimum Primary Air Flow Fraction",
1110 : MinPriAirFlowFracDes,
1111 : "User-Specified Minimum Primary Air Flow Fraction",
1112 : MinPriAirFlowFracUser);
1113 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1114 0 : if ((std::abs(MinPriAirFlowFracDes - MinPriAirFlowFracUser) / MinPriAirFlowFracUser) >
1115 0 : state.dataSize->AutoVsHardSizingThreshold) {
1116 0 : ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
1117 0 : ShowContinueError(state, format("User-Specified Minimum Primary Air Flow Fraction of {:.1R}", MinPriAirFlowFracUser));
1118 0 : ShowContinueError(state,
1119 0 : format("differs from Design Size Minimum Primary Air Flow Fraction of {:.1R}", MinPriAirFlowFracDes));
1120 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1121 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1122 : }
1123 : }
1124 : }
1125 : }
1126 : }
1127 : }
1128 :
1129 57 : if (CurTermUnitSizingNum > 0) {
1130 :
1131 57 : if (thisPIU.UnitType_Num == DataDefineEquip::ZnAirLoopEquipType::SingleDuct_SeriesPIU_Reheat) {
1132 51 : TermUnitSizing(CurTermUnitSizingNum).AirVolFlow = thisPIU.MaxTotAirVolFlow;
1133 6 : } else if (thisPIU.UnitType_Num == DataDefineEquip::ZnAirLoopEquipType::SingleDuct_ParallelPIU_Reheat) {
1134 6 : TermUnitSizing(CurTermUnitSizingNum).AirVolFlow = thisPIU.MaxSecAirVolFlow + thisPIU.MinPriAirFlowFrac * thisPIU.MaxPriAirVolFlow;
1135 : }
1136 : }
1137 :
1138 57 : IsAutoSize = false;
1139 57 : if (thisPIU.FanOnFlowFrac == AutoSize) {
1140 4 : IsAutoSize = true;
1141 : }
1142 57 : if (state.dataSize->CurZoneEqNum > 0) {
1143 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1144 0 : if (thisPIU.FanOnFlowFrac > 0.0) {
1145 0 : BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "User-Specified Fan On Flow Fraction", thisPIU.FanOnFlowFrac);
1146 : }
1147 : } else {
1148 57 : CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
1149 : // Autosized fan on flow fraction for reporting
1150 57 : Real64 FanOnFlowFracDes = thisPIU.MinPriAirFlowFrac;
1151 57 : if (IsAutoSize) {
1152 4 : thisPIU.FanOnFlowFrac = FanOnFlowFracDes;
1153 4 : BaseSizer::reportSizerOutput(state, thisPIU.UnitType, thisPIU.Name, "Design Size Fan On Flow Fraction", FanOnFlowFracDes);
1154 : } else {
1155 53 : if (thisPIU.FanOnFlowFrac > 0.0 && FanOnFlowFracDes > 0.0) {
1156 : // Hardsized fan on flow fraction for reporting
1157 0 : Real64 const FanOnFlowFracUser = thisPIU.FanOnFlowFrac;
1158 0 : BaseSizer::reportSizerOutput(state,
1159 : thisPIU.UnitType,
1160 : thisPIU.Name,
1161 : "Design Size Fan On Flow Fraction",
1162 : FanOnFlowFracDes,
1163 : "User-Specified Fan On Flow Fraction",
1164 : FanOnFlowFracUser);
1165 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1166 0 : if ((std::abs(FanOnFlowFracDes - FanOnFlowFracUser) / FanOnFlowFracUser) > state.dataSize->AutoVsHardSizingThreshold) {
1167 0 : ShowMessage(state, format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
1168 0 : ShowContinueError(state, format("User-Specified Fan On Flow Fraction of {:.1R}", FanOnFlowFracUser));
1169 0 : ShowContinueError(state, format("differs from Design Size Fan On Flow Fraction of {:.1R}", FanOnFlowFracDes));
1170 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1171 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1172 : }
1173 : }
1174 : }
1175 : }
1176 : }
1177 : }
1178 :
1179 57 : IsAutoSize = false;
1180 57 : if (thisPIU.MaxVolHotWaterFlow == AutoSize) { //.or.()) THEN
1181 57 : IsAutoSize = true;
1182 : }
1183 57 : if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
1184 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1185 0 : if (thisPIU.MaxVolHotWaterFlow > 0.0) {
1186 0 : BaseSizer::reportSizerOutput(
1187 : state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Reheat Water Flow Rate [m3/s]", thisPIU.MaxVolHotWaterFlow);
1188 : }
1189 : } else {
1190 57 : CheckZoneSizing(state, thisPIU.UnitType, thisPIU.Name);
1191 57 : if (Util::SameString(HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)], "Coil:Heating:Water")) {
1192 :
1193 56 : int const CoilWaterInletNode = GetCoilWaterInletNode(state, "Coil:Heating:Water", thisPIU.HCoil, ErrorsFound);
1194 56 : int const CoilWaterOutletNode = GetCoilWaterOutletNode(state, "Coil:Heating:Water", thisPIU.HCoil, ErrorsFound);
1195 :
1196 : // Autosized maximum hot water flow for reporting
1197 56 : Real64 MaxVolHotWaterFlowDes = 0.0;
1198 :
1199 56 : if (IsAutoSize) {
1200 : int const PltSizHeatNum =
1201 56 : MyPlantSizingIndex(state, "Coil:Heating:Water", thisPIU.HCoil, CoilWaterInletNode, CoilWaterOutletNode, ErrorsFound);
1202 56 : if (PltSizHeatNum > 0) {
1203 :
1204 56 : if (state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatMassFlow >= SmallAirVolFlow) {
1205 : Real64 const CoilInTemp =
1206 56 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU * thisPIU.MinPriAirFlowFrac +
1207 56 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).ZoneTempAtHeatPeak * (1.0 - thisPIU.MinPriAirFlowFrac);
1208 56 : Real64 const CoilOutTemp = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesTemp;
1209 56 : Real64 const CoilOutHumRat = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesHumRat;
1210 56 : Real64 const DesMassFlow = state.dataEnvrn->StdRhoAir * TermUnitSizing(CurTermUnitSizingNum).AirVolFlow;
1211 56 : DesCoilLoad = PsyCpAirFnW(CoilOutHumRat) * DesMassFlow * (CoilOutTemp - CoilInTemp);
1212 :
1213 56 : Real64 const rho = GetDensityGlycol(state,
1214 56 : state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).FluidName,
1215 : Constant::HWInitConvTemp,
1216 56 : state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).FluidIndex,
1217 : RoutineName);
1218 56 : Real64 const Cp = GetSpecificHeatGlycol(state,
1219 56 : state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).FluidName,
1220 : Constant::HWInitConvTemp,
1221 56 : state.dataPlnt->PlantLoop(thisPIU.HWplantLoc.loopNum).FluidIndex,
1222 : RoutineName);
1223 :
1224 56 : MaxVolHotWaterFlowDes = DesCoilLoad / (state.dataSize->PlantSizData(PltSizHeatNum).DeltaT * Cp * rho);
1225 : } else {
1226 0 : MaxVolHotWaterFlowDes = 0.0;
1227 : }
1228 : } else {
1229 0 : ShowSevereError(state, "Autosizing of water flow requires a heating loop Sizing:Plant object");
1230 0 : ShowContinueError(state, format("Occurs in{} Object={}", thisPIU.UnitType, thisPIU.Name));
1231 0 : ErrorsFound = true;
1232 : }
1233 : }
1234 56 : if (IsAutoSize) {
1235 56 : thisPIU.MaxVolHotWaterFlow = MaxVolHotWaterFlowDes;
1236 56 : BaseSizer::reportSizerOutput(
1237 : state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Reheat Water Flow Rate [m3/s]", MaxVolHotWaterFlowDes);
1238 112 : BaseSizer::reportSizerOutput(state,
1239 : thisPIU.UnitType,
1240 : thisPIU.Name,
1241 : "Design Size Reheat Coil Inlet Air Temperature [C]",
1242 56 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU);
1243 112 : BaseSizer::reportSizerOutput(state,
1244 : thisPIU.UnitType,
1245 : thisPIU.Name,
1246 : "Design Size Reheat Coil Inlet Air Humidity Ratio [kgWater/kgDryAir]",
1247 56 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInHumRatTU);
1248 : } else { // Hardsize with sizing data
1249 0 : if (thisPIU.MaxVolHotWaterFlow > 0.0 && MaxVolHotWaterFlowDes > 0.0) {
1250 : // Hardsized maximum hot water flow for reporting
1251 0 : Real64 const MaxVolHotWaterFlowUser = thisPIU.MaxVolHotWaterFlow;
1252 0 : BaseSizer::reportSizerOutput(state,
1253 : thisPIU.UnitType,
1254 : thisPIU.Name,
1255 : "Design Size Maximum Reheat Water Flow Rate [m3/s]",
1256 : MaxVolHotWaterFlowDes,
1257 : "User-Specified Maximum Reheat Water Flow Rate [m3/s]",
1258 : MaxVolHotWaterFlowUser);
1259 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1260 0 : if ((std::abs(MaxVolHotWaterFlowDes - MaxVolHotWaterFlowUser) / MaxVolHotWaterFlowUser) >
1261 0 : state.dataSize->AutoVsHardSizingThreshold) {
1262 0 : ShowMessage(state,
1263 0 : format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
1264 0 : ShowContinueError(state,
1265 0 : format("User-Specified Maximum Reheat Water Flow Rate of {:.5R} [m3/s]", MaxVolHotWaterFlowUser));
1266 0 : ShowContinueError(
1267 0 : state, format("differs from Design Size Maximum Reheat Water Flow Rate of {:.5R} [m3/s]", MaxVolHotWaterFlowDes));
1268 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1269 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1270 : }
1271 : }
1272 : }
1273 : }
1274 : } else {
1275 1 : thisPIU.MaxVolHotWaterFlow = 0.0;
1276 : }
1277 : }
1278 : }
1279 :
1280 57 : IsAutoSize = false;
1281 57 : if (thisPIU.MaxVolHotSteamFlow == AutoSize) {
1282 0 : IsAutoSize = true;
1283 : }
1284 57 : if ((state.dataSize->CurZoneEqNum > 0) && (CurTermUnitSizingNum > 0)) {
1285 57 : if (!IsAutoSize && !state.dataSize->ZoneSizingRunDone) { // Simulation continue
1286 0 : if (thisPIU.MaxVolHotWaterFlow > 0.0) {
1287 0 : BaseSizer::reportSizerOutput(
1288 : state, thisPIU.UnitType, thisPIU.Name, "User-Specified Maximum Reheat Steam Flow Rate [m3/s]", thisPIU.MaxVolHotWaterFlow);
1289 : }
1290 : } else {
1291 57 : if (Util::SameString(HCoilNames[static_cast<int>(thisPIU.HCoilType)], "Coil:Heating:Steam")) {
1292 :
1293 0 : int const CoilSteamInletNode = GetCoilSteamInletNode(state, "Coil:Heating:Steam", thisPIU.HCoil, ErrorsFound);
1294 0 : int const CoilSteamOutletNode = GetCoilSteamOutletNode(state, "Coil:Heating:Steam", thisPIU.HCoil, ErrorsFound);
1295 0 : Real64 MaxVolHotSteamFlowDes = 0.0; // Autosized maximum hot steam flow for reporting
1296 :
1297 0 : if (IsAutoSize) {
1298 : int const PltSizHeatNum =
1299 0 : MyPlantSizingIndex(state, "Coil:Heating:Steam", thisPIU.HCoil, CoilSteamInletNode, CoilSteamOutletNode, ErrorsFound);
1300 0 : if (PltSizHeatNum > 0) {
1301 0 : if (state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatMassFlow >= SmallAirVolFlow) {
1302 : Real64 const CoilInTemp =
1303 0 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).DesHeatCoilInTempTU * thisPIU.MinPriAirFlowFrac +
1304 0 : state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).ZoneTempAtHeatPeak * (1.0 - thisPIU.MinPriAirFlowFrac);
1305 0 : Real64 const CoilOutTemp = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesTemp;
1306 0 : Real64 const CoilOutHumRat = state.dataSize->TermUnitFinalZoneSizing(CurTermUnitSizingNum).HeatDesHumRat;
1307 0 : Real64 const DesMassFlow = state.dataEnvrn->StdRhoAir * TermUnitSizing(CurTermUnitSizingNum).AirVolFlow;
1308 0 : DesCoilLoad = PsyCpAirFnW(CoilOutHumRat) * DesMassFlow * (CoilOutTemp - CoilInTemp);
1309 0 : Real64 constexpr TempSteamIn = 100.00;
1310 : Real64 const EnthSteamInDry =
1311 0 : FluidProperties::GetSatEnthalpyRefrig(state, fluidNameSteam, TempSteamIn, 1.0, thisPIU.HCoil_FluidIndex, RoutineName);
1312 : Real64 const EnthSteamOutWet =
1313 0 : FluidProperties::GetSatEnthalpyRefrig(state, fluidNameSteam, TempSteamIn, 0.0, thisPIU.HCoil_FluidIndex, RoutineName);
1314 0 : Real64 const LatentHeatSteam = EnthSteamInDry - EnthSteamOutWet;
1315 : Real64 const SteamDensity =
1316 0 : FluidProperties::GetSatDensityRefrig(state, fluidNameSteam, TempSteamIn, 1.0, thisPIU.HCoil_FluidIndex, RoutineName);
1317 0 : int DummyWaterIndex = 1;
1318 0 : Real64 const Cp = GetSpecificHeatGlycol(
1319 0 : state, fluidNameWater, state.dataSize->PlantSizData(PltSizHeatNum).ExitTemp, DummyWaterIndex, RoutineName);
1320 0 : MaxVolHotSteamFlowDes =
1321 0 : DesCoilLoad / (SteamDensity * (LatentHeatSteam + state.dataSize->PlantSizData(PltSizHeatNum).DeltaT * Cp));
1322 : } else {
1323 0 : MaxVolHotSteamFlowDes = 0.0;
1324 : }
1325 : } else {
1326 0 : ShowSevereError(state, "Autosizing of Steam flow requires a heating loop Sizing:Plant object");
1327 0 : ShowContinueError(state, format("Occurs in{} Object={}", thisPIU.UnitType, thisPIU.Name));
1328 0 : ErrorsFound = true;
1329 : }
1330 : }
1331 0 : if (IsAutoSize) {
1332 0 : thisPIU.MaxVolHotSteamFlow = MaxVolHotSteamFlowDes;
1333 0 : BaseSizer::reportSizerOutput(
1334 : state, thisPIU.UnitType, thisPIU.Name, "Design Size Maximum Reheat Steam Flow [m3/s]", MaxVolHotSteamFlowDes);
1335 : } else {
1336 0 : if (thisPIU.MaxVolHotSteamFlow > 0.0 && MaxVolHotSteamFlowDes > 0.0) {
1337 0 : Real64 const MaxVolHotSteamFlowUser = thisPIU.MaxVolHotSteamFlow;
1338 0 : BaseSizer::reportSizerOutput(state,
1339 : thisPIU.UnitType,
1340 : thisPIU.Name,
1341 : "Design Size Maximum Reheat Steam Flow [m3/s]",
1342 : MaxVolHotSteamFlowDes,
1343 : "User-Specified Maximum Reheat Steam Flow [m3/s]",
1344 : MaxVolHotSteamFlowUser);
1345 0 : if (state.dataGlobal->DisplayExtraWarnings) {
1346 0 : if ((std::abs(MaxVolHotSteamFlowDes - MaxVolHotSteamFlowUser) / MaxVolHotSteamFlowUser) >
1347 0 : state.dataSize->AutoVsHardSizingThreshold) {
1348 0 : ShowMessage(state,
1349 0 : format("SizePIU: Potential issue with equipment sizing for {} {}", thisPIU.UnitType, thisPIU.Name));
1350 0 : ShowContinueError(state, format("User-Specified Maximum Reheat Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowUser));
1351 0 : ShowContinueError(
1352 0 : state, format("differs from Design Size Maximum Reheat Steam Flow of {:.5R} [m3/s]", MaxVolHotSteamFlowDes));
1353 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
1354 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
1355 : }
1356 : }
1357 : }
1358 : }
1359 : } else {
1360 57 : thisPIU.MaxVolHotSteamFlow = 0.0;
1361 : }
1362 : }
1363 : }
1364 :
1365 57 : if (CurTermUnitSizingNum > 0) {
1366 57 : TermUnitSizing(CurTermUnitSizingNum).MinPriFlowFrac = thisPIU.MinPriAirFlowFrac;
1367 57 : TermUnitSizing(CurTermUnitSizingNum).plenumIndex = thisPIU.plenumIndex;
1368 57 : TermUnitSizing(CurTermUnitSizingNum).MaxHWVolFlow = thisPIU.MaxVolHotWaterFlow;
1369 57 : TermUnitSizing(CurTermUnitSizingNum).MaxSTVolFlow = thisPIU.MaxVolHotSteamFlow;
1370 57 : TermUnitSizing(CurTermUnitSizingNum).DesHeatingLoad = DesCoilLoad; // coil report
1371 57 : TermUnitSizing(CurTermUnitSizingNum).InducesPlenumAir = thisPIU.InducesPlenumAir;
1372 57 : if (thisPIU.HCoilType == HtgCoilType::SimpleHeating) {
1373 56 : SetCoilDesFlow(state,
1374 56 : HCoilNamesUC[static_cast<int>(thisPIU.HCoilType)],
1375 56 : thisPIU.HCoil,
1376 56 : TermUnitSizing(CurTermUnitSizingNum).AirVolFlow,
1377 : ErrorsFound);
1378 : }
1379 : }
1380 :
1381 57 : if (ErrorsFound) {
1382 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
1383 : }
1384 57 : }
1385 :
1386 287202 : void CalcSeriesPIU(EnergyPlusData &state,
1387 : int const PIUNum, // number of the current PIU being simulated
1388 : int const ZoneNum, // number of zone being served
1389 : int const ZoneNode, // zone node number
1390 : bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
1391 : )
1392 : {
1393 :
1394 : // SUBROUTINE INFORMATION:
1395 : // AUTHOR Fred Buhl
1396 : // DATE WRITTEN August 2000
1397 : // MODIFIED na
1398 : // RE-ENGINEERED na
1399 :
1400 : // PURPOSE OF THIS SUBROUTINE:
1401 : // Simulate a series powered induction unit; adjust its primary air flow
1402 : // and reheat coil output to match the zone load.
1403 :
1404 : // METHODOLOGY EMPLOYED:
1405 : // If unit is on and there is a cooling load:
1406 : // (1) simulates mixer and fan at max secondary air flow and heating coil
1407 : // off. Obtains fan temperature increase.
1408 : // (2) Calculates primary and secomdary air flow to meet zone load and
1409 : // resimulates mixer, fan, and (off) coil.
1410 : // If unit is on and there is a heating load
1411 : // (1) sets primary air flow to a minimum.
1412 : // (2) simulates mixer and fan
1413 : // (3) if reheat is hot water, calls ControlCompOutput to simulate hot
1414 : // water coil and adjust water flow to match coil output to the zone load.
1415 : // (4) if reheat is electric or gas calls SimulateHeatingCoilComponents to
1416 : // simulate coil at coil output that matches the zone load
1417 :
1418 : // REFERENCES:
1419 : // na
1420 :
1421 : // Using/Aliasing
1422 : using namespace DataZoneEnergyDemands;
1423 : using FluidProperties::GetDensityGlycol;
1424 : using FluidProperties::GetSpecificHeatGlycol;
1425 : using HeatingCoils::SimulateHeatingCoilComponents;
1426 : using MixerComponent::SimAirMixer;
1427 : using PlantUtilities::SetComponentFlowRate;
1428 : using SteamCoils::SimulateSteamCoilComponents;
1429 : using WaterCoils::SimulateWaterCoilComponents;
1430 :
1431 : // Locals
1432 : // SUBROUTINE ARGUMENT DEFINITIONS:
1433 :
1434 : // SUBROUTINE PARAMETER DEFINITIONS:
1435 :
1436 : // INTERFACE BLOCK SPECIFICATIONS
1437 :
1438 : // DERIVED TYPE DEFINITIONS
1439 : // na
1440 :
1441 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1442 287202 : bool UnitOn(true); // TRUE if unit is on
1443 287202 : bool PriOn(true); // TRUE if primary air available
1444 :
1445 287202 : Real64 QCoilReq = 0.0; // required heating coil outlet to meet zone load
1446 287202 : Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/s]
1447 287202 : Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/s]
1448 :
1449 : // initialize local variables
1450 287202 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
1451 :
1452 287202 : Real64 const PriAirMassFlowMax = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMaxAvail; // max primary air mass flow rate [kg/s]
1453 287202 : Real64 const PriAirMassFlowMin = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMinAvail; // min primary air mass flow rate [kg/s]
1454 : Real64 const QZnReq =
1455 287202 : state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // heating or cooling needed by zone [Watts]
1456 : Real64 const QToHeatSetPt =
1457 287202 : state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // [W] remaining load to heating setpoint
1458 287202 : Real64 const CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat); // zone air specific heat [J/kg-C]
1459 287202 : thisPIU.PriAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate; // primary air mass flow rate [kg/s]
1460 287202 : thisPIU.SecAirMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate; // secondary air mass flow rate [kg/s]
1461 287202 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1462 7366 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
1463 : } else {
1464 279836 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1465 : }
1466 287202 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
1467 :
1468 : // On the first HVAC iteration the system values are given to the controller, but after that
1469 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
1470 287202 : if (thisPIU.HotControlNode > 0) {
1471 276483 : if (FirstHVACIteration) {
1472 128437 : MaxWaterFlow = thisPIU.MaxHotWaterFlow;
1473 128437 : MinWaterFlow = thisPIU.MinHotWaterFlow;
1474 : } else {
1475 148046 : MaxWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMaxAvail;
1476 148046 : MinWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMinAvail;
1477 : }
1478 : }
1479 287202 : if (GetCurrentScheduleValue(state, thisPIU.SchedPtr) <= 0.0) {
1480 14 : UnitOn = false;
1481 : }
1482 287202 : if ((GetCurrentScheduleValue(state, thisPIU.FanAvailSchedPtr) <= 0.0 || state.dataHVACGlobal->TurnFansOff) && !state.dataHVACGlobal->TurnFansOn) {
1483 56933 : UnitOn = false;
1484 : }
1485 287202 : if (thisPIU.PriAirMassFlow <= SmallMassFlow || PriAirMassFlowMax <= SmallMassFlow) {
1486 61409 : PriOn = false;
1487 : }
1488 : // Set the mass flow rates
1489 287202 : if (UnitOn) {
1490 : // unit is on
1491 230269 : if (!PriOn) {
1492 : // no primary air flow
1493 4497 : thisPIU.PriAirMassFlow = 0.0;
1494 : // PIU fan off if there is no heating load, also reset fan flag if fan should be off
1495 4497 : if (QZnReq <= SmallLoad) {
1496 2095 : thisPIU.SecAirMassFlow = 0.0;
1497 2095 : state.dataHVACGlobal->TurnFansOn = false;
1498 : } else {
1499 2402 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1500 6 : thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
1501 3 : CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1502 2399 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1503 3 : thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
1504 3 : CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1505 2396 : } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1506 2396 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1507 2396 : thisPIU.SecAirMassFlow = thisPIU.MaxTotAirMassFlow;
1508 : }
1509 : }
1510 225772 : } else if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum) || std::abs(QZnReq) < SmallLoad) {
1511 : // in deadband or very small load: set primary air flow to the minimum
1512 44030 : thisPIU.PriAirMassFlow = PriAirMassFlowMin;
1513 44030 : if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1514 41168 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1515 41168 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
1516 2862 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1517 2862 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.PriAirMassFlow);
1518 : }
1519 181742 : } else if (QZnReq > SmallLoad) {
1520 : // heating: set primary air flow to the minimum
1521 116368 : thisPIU.PriAirMassFlow = PriAirMassFlowMin;
1522 : // determine secondary flow rate
1523 116368 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1524 2702 : thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
1525 1352 : CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1526 115016 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1527 1350 : thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
1528 1350 : CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1529 113666 : } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1530 113666 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1531 113666 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
1532 : }
1533 : } else {
1534 65374 : if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1535 : // cooling: set the primary air flow rate to meet the load.
1536 : // First calculate the fan temperature rise
1537 : // use only secondary air for this calculation
1538 63582 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
1539 63582 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
1540 63582 : SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num); // fire the mixer
1541 63582 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
1542 :
1543 : // fan temperature rise [C]
1544 : Real64 const FanDeltaTemp =
1545 63582 : state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp;
1546 :
1547 : // using the required zone load, calculate the air temperature needed to meet the load
1548 63582 : Real64 const OutletTempNeeded = state.dataLoopNodes->Node(ZoneNode).Temp + QZnReq / (thisPIU.MaxTotAirMassFlow * CpAirZn);
1549 :
1550 : // mixer outlet temperature needed to meet cooling load
1551 63582 : Real64 const MixTempNeeded = OutletTempNeeded - FanDeltaTemp;
1552 :
1553 63582 : if (MixTempNeeded <= state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp) { //
1554 19746 : thisPIU.PriAirMassFlow = PriAirMassFlowMax;
1555 87672 : } else if (MixTempNeeded >= state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp &&
1556 43836 : MixTempNeeded >= state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp) {
1557 462 : thisPIU.PriAirMassFlow = PriAirMassFlowMin;
1558 : } else {
1559 43374 : thisPIU.PriAirMassFlow =
1560 43374 : thisPIU.MaxTotAirMassFlow * (state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp - MixTempNeeded) /
1561 43374 : max(SmallTempDiff,
1562 43374 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp - state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp);
1563 43374 : thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
1564 : }
1565 63582 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.PriAirMassFlow);
1566 63582 : if (QZnReq < 0) {
1567 63582 : thisPIU.coolingOperatingMode = CoolOpModeType::ConstantVolumeCool;
1568 : }
1569 1792 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1570 1792 : CalcVariableSpeedPIUCoolingBehavior(state, PIUNum, ZoneNode, QZnReq, QToHeatSetPt, PriAirMassFlowMin, PriAirMassFlowMax);
1571 : }
1572 : }
1573 : } else {
1574 : // unit is off ; no flow
1575 56933 : thisPIU.PriAirMassFlow = 0.0;
1576 56933 : thisPIU.SecAirMassFlow = 0.0;
1577 : }
1578 : // set inlet node flowrates
1579 287202 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.PriAirMassFlow;
1580 287202 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.SecAirMassFlow;
1581 287202 : if (PriAirMassFlowMax == 0) {
1582 61409 : thisPIU.PriDamperPosition = 0;
1583 : } else {
1584 225793 : thisPIU.PriDamperPosition = thisPIU.PriAirMassFlow / PriAirMassFlowMax;
1585 : }
1586 :
1587 : // now that inlet airflows have been set, the terminal components can be simulated.
1588 : // fire the mixer
1589 287202 : SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
1590 :
1591 : // fire the fan
1592 287202 : if (thisPIU.fanType == HVAC::FanType::SystemModel) {
1593 175093 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1594 : // calculate fan speed ratio
1595 7366 : Real64 fanFlowRatio(1.0);
1596 7366 : if (thisPIU.MaxTotAirMassFlow > 0.0) {
1597 7364 : fanFlowRatio = (thisPIU.PriAirMassFlow + thisPIU.SecAirMassFlow) / thisPIU.MaxTotAirMassFlow;
1598 : }
1599 7366 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, fanFlowRatio, _);
1600 : } else {
1601 167727 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
1602 : }
1603 112109 : } else if (thisPIU.fanType == HVAC::FanType::Constant) {
1604 112109 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
1605 : }
1606 :
1607 : // the heating load seen by the reheat coil [W]
1608 287202 : Real64 QActualHeating = QToHeatSetPt - state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * CpAirZn *
1609 287202 : (state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp);
1610 :
1611 : // check if heating coil is off
1612 230269 : if (((!UnitOn) || (QActualHeating < SmallLoad) || (state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::ThermostatType::SingleCooling) ||
1613 574404 : (thisPIU.PriAirMassFlow > PriAirMassFlowMin)) &&
1614 168042 : (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
1615 168042 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
1616 : }
1617 :
1618 : // determine what is required of heater for current operating stage
1619 287202 : if (thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) {
1620 168044 : QCoilReq = 0.0;
1621 119158 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage) {
1622 0 : QCoilReq = 0.0;
1623 119158 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ConstantVolumeHeat) {
1624 116464 : QCoilReq = QActualHeating;
1625 2694 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatSecondStage) {
1626 1348 : QCoilReq = QActualHeating;
1627 1346 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatFirstStage) {
1628 1346 : QCoilReq = QActualHeating;
1629 0 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatSecondStage) {
1630 : // find heater power to deliver design discharge air temperature
1631 0 : Real64 targetDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.designHeatingDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
1632 : Real64 mixAirEnthalpy =
1633 0 : Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
1634 0 : QCoilReq = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (targetDATEnthalpy - mixAirEnthalpy);
1635 0 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatThirdStage) {
1636 : // find heater power to deliver maximum discharge air temperature
1637 0 : Real64 HiLimitDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.highLimitDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
1638 : Real64 mixAirEnthalpy =
1639 0 : Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
1640 0 : Real64 QcoilLimit = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (HiLimitDATEnthalpy - mixAirEnthalpy);
1641 0 : if (QcoilLimit < QActualHeating) { // if requried power is too high use limit of coil discharge
1642 0 : QCoilReq = QcoilLimit;
1643 : } else {
1644 0 : QCoilReq = QActualHeating;
1645 : }
1646 : } else {
1647 0 : ShowSevereError(state, "Incorrect series PIU heating operation.");
1648 0 : ShowFatalError(state, format("Series PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
1649 : }
1650 287202 : if ((QCoilReq < SmallLoad) &&
1651 168044 : (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
1652 168044 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
1653 168044 : QCoilReq = 0.0;
1654 : }
1655 :
1656 : // fire the heating coil
1657 287202 : switch (thisPIU.HCoilType) {
1658 276483 : case HtgCoilType::SimpleHeating: { // COIL:WATER:SIMPLEHEATING
1659 276483 : if ((thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) || (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage)) {
1660 : // call the reheat coil with the NO FLOW condition
1661 161631 : Real64 mdot = 0.0;
1662 161631 : SetComponentFlowRate(state, mdot, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum, thisPIU.HWplantLoc);
1663 :
1664 161631 : SimulateWaterCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index);
1665 161631 : } else {
1666 : // control water flow to obtain output matching QZnReq
1667 344556 : ControlCompOutput(state,
1668 114852 : thisPIU.HCoil,
1669 114852 : thisPIU.UnitType,
1670 114852 : thisPIU.HCoil_Index,
1671 : FirstHVACIteration,
1672 : QCoilReq,
1673 : thisPIU.HotControlNode,
1674 : MaxWaterFlow,
1675 : MinWaterFlow,
1676 : thisPIU.HotControlOffset,
1677 114852 : thisPIU.ControlCompTypeNum,
1678 114852 : thisPIU.CompErrIndex,
1679 114852 : thisPIU.HCoilInAirNode,
1680 114852 : thisPIU.OutAirNode,
1681 : _,
1682 : _,
1683 : _,
1684 114852 : thisPIU.HWplantLoc);
1685 : }
1686 276483 : break;
1687 : }
1688 0 : case HtgCoilType::SteamAirHeating: { // COIL:STEAM:AIRHEATING
1689 0 : SimulateSteamCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index, QCoilReq);
1690 0 : break;
1691 : }
1692 10719 : case HtgCoilType::Electric: { // COIL:ELECTRIC:HEATING
1693 10719 : SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
1694 10719 : break;
1695 : }
1696 0 : case HtgCoilType::Gas: { // COIL:GAS:HEATING
1697 0 : SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
1698 0 : break;
1699 : }
1700 0 : default:
1701 0 : break;
1702 : }
1703 :
1704 : // Power supplied
1705 287202 : Real64 PowerMet = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate *
1706 287202 : (PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat) -
1707 287202 : PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat));
1708 287202 : thisPIU.HeatingRate = max(0.0, PowerMet);
1709 287202 : thisPIU.SensCoolRate = std::abs(min(DataPrecisionGlobals::constant_zero, PowerMet));
1710 287202 : thisPIU.TotMassFlowRate = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate;
1711 287202 : thisPIU.SecMassFlowRate = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
1712 287202 : thisPIU.PriMassFlowRate = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;
1713 287202 : thisPIU.DischargeAirTemp = state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp;
1714 287202 : if (state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate == 0.0) {
1715 59032 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
1716 59032 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = 0.0;
1717 : }
1718 287202 : if (thisPIU.InducesPlenumAir) {
1719 168070 : state.dataHVACGlobal->PlenumInducedMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
1720 : } else {
1721 119132 : state.dataHVACGlobal->PlenumInducedMassFlow = 0.0;
1722 : }
1723 287202 : state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).MassFlowRatePlenInd = state.dataHVACGlobal->PlenumInducedMassFlow;
1724 287202 : state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRateMax = thisPIU.MaxTotAirMassFlow;
1725 :
1726 287202 : ReportCurOperatingControlStage(state, PIUNum, UnitOn, thisPIU.heatingOperatingMode, thisPIU.coolingOperatingMode);
1727 287202 : }
1728 :
1729 21546 : void CalcParallelPIU(EnergyPlusData &state,
1730 : int const PIUNum, // number of the current PIU being simulated
1731 : int const ZoneNum, // number of zone being served
1732 : int const ZoneNode, // zone node number
1733 : bool const FirstHVACIteration // TRUE if 1st HVAC simulation of system timestep
1734 : )
1735 : {
1736 :
1737 : // SUBROUTINE INFORMATION:
1738 : // AUTHOR Fred Buhl
1739 : // DATE WRITTEN August 2000
1740 : // MODIFIED September 2016, March 2017
1741 :
1742 : // PURPOSE OF THIS SUBROUTINE:
1743 : // Simulate a parallel powered induction unit; adjust its primary air flow
1744 : // and reheat coil output to match the zone load.
1745 :
1746 : // METHODOLOGY EMPLOYED:
1747 : // If unit is on and there is a cooling load:
1748 : // (1) simulate fan at max secondary air flow and heating coil
1749 : // off. Obtains fan temperature increase.
1750 : // (2) Calculates primary and secomdary air flow to meet zone load.
1751 : // (a) Assume fan is off and calculate primary air flow to meet cooling load.
1752 : // (b1) If calculated primary air flow is above the fan turn on ratio, fan is off.
1753 : // Otherwise fan is on; calculate mixed secondary and primary air flow that
1754 : // will meet the zone load
1755 : // (b2) If the fan turn on ratio is zero, then the fan is on only if reheat is needed.
1756 : // (3) Simulate fan, mixer, and (off) heating coil to obtain zone inlet conditions.
1757 : // If unit is on and there is a heating load
1758 : // (1) sets primary air flow to a minimum.
1759 : // (2) simulates fan and mixer
1760 : // (3) if reheat is hot water, calls ControlCompOutput to simulate hot
1761 : // water coil and adjust water flow to match coil output to the zone load.
1762 : // (4) if reheat is electric or gas calls SimulateHeatingCoilComponents to
1763 : // simulate coil at coil output that matches the zone load
1764 :
1765 : using namespace DataZoneEnergyDemands;
1766 : using HeatingCoils::SimulateHeatingCoilComponents;
1767 : using MixerComponent::SimAirMixer;
1768 : using PlantUtilities::SetComponentFlowRate;
1769 : using SteamCoils::SimulateSteamCoilComponents;
1770 : using WaterCoils::SimulateWaterCoilComponents;
1771 :
1772 21546 : bool UnitOn(true); // TRUE if unit is on
1773 21546 : bool PriOn(true); // TRUE if primary air available
1774 :
1775 21546 : Real64 QCoilReq = 0.0; // required heating coil outlet to meet zone load
1776 21546 : Real64 MaxWaterFlow = 0.0; // maximum water flow for heating or cooling [kg/s]
1777 21546 : Real64 MinWaterFlow = 0.0; // minimum water flow for heating or cooling [kg/s]
1778 :
1779 : // initialize local variables
1780 21546 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
1781 :
1782 21546 : Real64 const PriAirMassFlowMax = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMaxAvail; // max primary air mass flow rate [kg/s]
1783 21546 : Real64 const PriAirMassFlowMin = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRateMinAvail; // min primary air mass flow rate [kg/s]
1784 21546 : thisPIU.PriAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate; // primary air mass flow rate [kg/s]
1785 21546 : thisPIU.SecAirMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate; // secondary air mass flow rate [kg/s]
1786 : Real64 const QZnReq =
1787 21546 : state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputRequired; // heating or cooling needed by zone [Watts]
1788 : Real64 const QToHeatSetPt =
1789 21546 : state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).RemainingOutputReqToHeatSP; // [W] remaining load to heating setpoint
1790 21546 : Real64 const CpAirZn = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat); // zone air specific heat [J/kg-C]
1791 21546 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
1792 21546 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
1793 :
1794 : // On the first HVAC iteration the system values are given to the controller, but after that
1795 : // the demand limits are in place and there needs to be feedback to the Zone Equipment
1796 21546 : if (thisPIU.HotControlNode > 0) {
1797 21546 : if (FirstHVACIteration) {
1798 10789 : MaxWaterFlow = thisPIU.MaxHotWaterFlow;
1799 10789 : MinWaterFlow = thisPIU.MinHotWaterFlow;
1800 : } else {
1801 10757 : MaxWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMaxAvail;
1802 10757 : MinWaterFlow = state.dataLoopNodes->Node(thisPIU.HotControlNode).MassFlowRateMinAvail;
1803 : }
1804 : }
1805 21546 : if (GetCurrentScheduleValue(state, thisPIU.SchedPtr) <= 0.0) {
1806 14 : UnitOn = false;
1807 : }
1808 21546 : if (thisPIU.PriAirMassFlow <= SmallMassFlow || PriAirMassFlowMax <= SmallMassFlow) {
1809 788 : PriOn = false;
1810 : }
1811 : // Set the mass flow rates
1812 21546 : if (UnitOn) {
1813 : // unit is on
1814 : // Calculate if reheat is needed
1815 21532 : bool ReheatRequired = false;
1816 : Real64 const qMinPrimary =
1817 : PriAirMassFlowMin *
1818 21532 : (CpAirZn * min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
1819 21532 : if (qMinPrimary < QToHeatSetPt) {
1820 14997 : ReheatRequired = true;
1821 : }
1822 :
1823 21532 : if (!PriOn) {
1824 : // no primary air flow
1825 774 : thisPIU.PriAirMassFlow = 0.0;
1826 : // PIU fan off if there is no heating load, also reset fan flag if fan should be off
1827 774 : if (QZnReq <= SmallLoad) {
1828 49 : thisPIU.SecAirMassFlow = 0.0;
1829 49 : state.dataHVACGlobal->TurnFansOn = false;
1830 : } else {
1831 725 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1832 6 : thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
1833 3 : CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1834 722 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1835 3 : thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
1836 3 : CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1837 719 : } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1838 719 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1839 719 : thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
1840 : }
1841 : }
1842 20758 : } else if (state.dataZoneEnergyDemand->CurDeadBandOrSetback(ZoneNum) || std::abs(QZnReq) < SmallLoad) {
1843 : // in deadband or very small load: set primary air flow to the minimum
1844 5161 : thisPIU.PriAirMassFlow = PriAirMassFlowMin;
1845 : // PIU fan off if reheat is not needed, also reset fan flag if fan should be off
1846 5161 : if (ReheatRequired) {
1847 5011 : state.dataHVACGlobal->TurnFansOn = true;
1848 5011 : if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1849 2493 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1850 2493 : thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
1851 2518 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1852 2518 : if (thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
1853 1387 : thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatFirstStage;
1854 : } else {
1855 1131 : thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatFirstStage;
1856 : }
1857 2518 : thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
1858 : }
1859 : } else {
1860 150 : thisPIU.SecAirMassFlow = 0.0;
1861 150 : state.dataHVACGlobal->TurnFansOn = false;
1862 150 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
1863 : }
1864 15597 : } else if (QZnReq > SmallLoad) {
1865 : // heating
1866 : // set primary air flow to the minimum
1867 8199 : thisPIU.PriAirMassFlow = PriAirMassFlowMin;
1868 : // determine secondary flow rate
1869 8199 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1870 2706 : thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
1871 1356 : CalcVariableSpeedPIUStagedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1872 6843 : } else if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan &&
1873 1350 : thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
1874 1350 : CalcVariableSpeedPIUModulatedHeatingBehavior(state, PIUNum, ZoneNode, QZnReq, PriOn, thisPIU.PriAirMassFlow);
1875 5493 : } else if (thisPIU.fanControlType == FanCntrlType::ConstantSpeedFan) {
1876 5493 : thisPIU.heatingOperatingMode = HeatOpModeType::ConstantVolumeHeat;
1877 5493 : thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
1878 : }
1879 : } else {
1880 : // cooling: set the primary air flow rate to meet the load.
1881 : // First calculate the fan temperature rise
1882 7398 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
1883 7398 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRateMaxAvail = thisPIU.MaxSecAirMassFlow;
1884 7398 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
1885 :
1886 7398 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
1887 :
1888 7398 : SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num); // fire the mixer
1889 : // fan temperature rise [C]
1890 7398 : Real64 const FanDeltaTemp = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp;
1891 : // Assuming the fan is off, calculate the primary air flow needed to meet the zone cooling demand.
1892 7398 : thisPIU.PriAirMassFlow =
1893 7398 : QZnReq /
1894 7398 : (CpAirZn * min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
1895 7398 : thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
1896 : // check for fan on or off
1897 7398 : if ((thisPIU.PriAirMassFlow > thisPIU.FanOnAirMassFlow) && !ReheatRequired) {
1898 6336 : thisPIU.SecAirMassFlow = 0.0; // Fan is off unless reheat is required; no secondary air; also reset fan flag
1899 6336 : state.dataHVACGlobal->TurnFansOn = false;
1900 : } else {
1901 : // fan is on; recalc primary air flow
1902 1062 : thisPIU.PriAirMassFlow =
1903 2124 : (QZnReq - CpAirZn * thisPIU.SecAirMassFlow *
1904 1062 : (state.dataLoopNodes->Node(thisPIU.SecAirInNode).Temp + FanDeltaTemp - state.dataLoopNodes->Node(ZoneNode).Temp)) /
1905 1062 : (CpAirZn *
1906 1062 : min(-SmallTempDiff, (state.dataLoopNodes->Node(thisPIU.PriAirInNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp)));
1907 1062 : thisPIU.PriAirMassFlow = min(max(thisPIU.PriAirMassFlow, PriAirMassFlowMin), PriAirMassFlowMax);
1908 1062 : thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
1909 : }
1910 7398 : if (QZnReq < 0) {
1911 7398 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1912 2076 : if (thisPIU.PriAirMassFlow == PriAirMassFlowMax) {
1913 0 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolSecondStage;
1914 : } else {
1915 2076 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolFirstStage;
1916 : }
1917 : } else {
1918 5322 : thisPIU.coolingOperatingMode = CoolOpModeType::ConstantVolumeCool;
1919 : }
1920 : }
1921 : }
1922 : } else {
1923 : // unit is off; no flow
1924 14 : thisPIU.PriAirMassFlow = 0.0;
1925 14 : thisPIU.SecAirMassFlow = 0.0;
1926 : }
1927 : // set inlet node flowrates
1928 21546 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.PriAirMassFlow;
1929 21546 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.SecAirMassFlow;
1930 21546 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRateMaxAvail = thisPIU.SecAirMassFlow;
1931 21546 : if (PriAirMassFlowMax == 0) {
1932 787 : thisPIU.PriDamperPosition = 0;
1933 : } else {
1934 20759 : thisPIU.PriDamperPosition = thisPIU.PriAirMassFlow / PriAirMassFlowMax;
1935 : }
1936 :
1937 : // now that inlet airflows have been set, the terminal box components can be simulated.
1938 : // fire the fan
1939 21546 : if (thisPIU.fanType == HVAC::FanType::SystemModel) {
1940 14308 : if (thisPIU.fanControlType == FanCntrlType::VariableSpeedFan) {
1941 : // calculate fan speed ratio
1942 7366 : Real64 fanFlowRatio(1.0);
1943 7366 : if (thisPIU.MaxSecAirMassFlow > 0.0) {
1944 7364 : fanFlowRatio = thisPIU.SecAirMassFlow / thisPIU.MaxSecAirMassFlow;
1945 : }
1946 7366 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, fanFlowRatio, _);
1947 : } else {
1948 6942 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
1949 : }
1950 7238 : } else if (thisPIU.fanType == HVAC::FanType::Constant) {
1951 7238 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, FirstHVACIteration, _, _);
1952 : }
1953 :
1954 : // fire the mixer
1955 21546 : SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
1956 :
1957 : // the heating load seen by the reheat coil [W]
1958 21546 : Real64 QActualHeating = QToHeatSetPt - state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * CpAirZn *
1959 21546 : (state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp - state.dataLoopNodes->Node(ZoneNode).Temp);
1960 :
1961 : // check if heating coil is off
1962 21532 : if (((!UnitOn) || (QActualHeating < SmallLoad) || (state.dataHeatBalFanSys->TempControlType(ZoneNum) == HVAC::ThermostatType::SingleCooling) ||
1963 43092 : (thisPIU.PriAirMassFlow > PriAirMassFlowMin)) &&
1964 12659 : (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
1965 11261 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
1966 : }
1967 :
1968 : // determine what is required of heater for current operating stage
1969 21546 : if (thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) {
1970 11271 : QCoilReq = 0.0;
1971 10275 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage) {
1972 1400 : QCoilReq = 0.0;
1973 8875 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ConstantVolumeHeat) {
1974 6199 : QCoilReq = QActualHeating;
1975 2676 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatSecondStage) {
1976 1330 : QCoilReq = QActualHeating;
1977 1346 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatFirstStage) {
1978 1346 : QCoilReq = QActualHeating;
1979 0 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatSecondStage) {
1980 : // find heater power to deliver design discharge air temperature
1981 0 : Real64 targetDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.designHeatingDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
1982 : Real64 mixAirEnthalpy =
1983 0 : Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
1984 0 : QCoilReq = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (targetDATEnthalpy - mixAirEnthalpy);
1985 0 : } else if (thisPIU.heatingOperatingMode == HeatOpModeType::ModulatedHeatThirdStage) {
1986 : // find heater power to deliver maximum discharge air temperature
1987 0 : Real64 HiLimitDATEnthalpy = Psychrometrics::PsyHFnTdbW(thisPIU.highLimitDAT, state.dataLoopNodes->Node(ZoneNode).HumRat);
1988 : Real64 mixAirEnthalpy =
1989 0 : Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
1990 0 : Real64 QcoilLimit = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).MassFlowRate * (HiLimitDATEnthalpy - mixAirEnthalpy);
1991 0 : if (QcoilLimit < QActualHeating) { // if requried power is too high use limit of coil discharge
1992 0 : QCoilReq = QcoilLimit;
1993 : } else {
1994 0 : QCoilReq = QActualHeating;
1995 : }
1996 : } else {
1997 0 : ShowSevereError(state, "Incorrect parallel PIU heating operation.");
1998 0 : ShowFatalError(state, format("Parallel PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
1999 : }
2000 21546 : if ((QCoilReq < SmallLoad) &&
2001 12671 : (thisPIU.heatingOperatingMode != HeatOpModeType::StagedHeatFirstStage)) { // reheat is off during the first stage of heating
2002 11271 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
2003 11271 : QCoilReq = 0.0;
2004 : }
2005 :
2006 : // fire the heating coil
2007 21546 : switch (thisPIU.HCoilType) {
2008 21546 : case HtgCoilType::SimpleHeating: { // COIL:WATER:SIMPLEHEATING
2009 21546 : if ((thisPIU.heatingOperatingMode == HeatOpModeType::HeaterOff) || (thisPIU.heatingOperatingMode == HeatOpModeType::StagedHeatFirstStage)) {
2010 : // call the reheat coil with the NO FLOW condition
2011 12671 : Real64 mdot = 0.0;
2012 12671 : SetComponentFlowRate(state, mdot, thisPIU.HotControlNode, thisPIU.HotCoilOutNodeNum, thisPIU.HWplantLoc);
2013 12671 : SimulateWaterCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index);
2014 12671 : } else {
2015 : // control water flow to obtain output matching QZnReq
2016 26625 : ControlCompOutput(state,
2017 8875 : thisPIU.HCoil,
2018 8875 : thisPIU.UnitType,
2019 8875 : thisPIU.HCoil_Index,
2020 : FirstHVACIteration,
2021 : QCoilReq,
2022 : thisPIU.HotControlNode,
2023 : MaxWaterFlow,
2024 : MinWaterFlow,
2025 : thisPIU.HotControlOffset,
2026 8875 : thisPIU.ControlCompTypeNum,
2027 8875 : thisPIU.CompErrIndex,
2028 8875 : thisPIU.HCoilInAirNode,
2029 8875 : thisPIU.OutAirNode,
2030 : _,
2031 : _,
2032 : _,
2033 8875 : thisPIU.HWplantLoc);
2034 : }
2035 21546 : break;
2036 : }
2037 0 : case HtgCoilType::SteamAirHeating: { // COIL:STEAM:AIRHEATING
2038 0 : SimulateSteamCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, thisPIU.HCoil_Index, QCoilReq);
2039 0 : break;
2040 : }
2041 0 : case HtgCoilType::Electric: { // COIL:ELECTRIC:HEATING
2042 0 : SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
2043 0 : break;
2044 : }
2045 0 : case HtgCoilType::Gas: { // COIL:GAS:HEATING
2046 0 : SimulateHeatingCoilComponents(state, thisPIU.HCoil, FirstHVACIteration, QCoilReq, thisPIU.HCoil_Index);
2047 0 : break;
2048 : }
2049 0 : default:
2050 0 : break;
2051 : }
2052 : // Power supplied
2053 21546 : Real64 PowerMet = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate *
2054 21546 : (PsyHFnTdbW(state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat) -
2055 21546 : PsyHFnTdbW(state.dataLoopNodes->Node(ZoneNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat));
2056 21546 : thisPIU.HeatingRate = max(0.0, PowerMet);
2057 21546 : thisPIU.SensCoolRate = std::abs(min(DataPrecisionGlobals::constant_zero, PowerMet));
2058 21546 : thisPIU.TotMassFlowRate = state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate;
2059 21546 : thisPIU.SecMassFlowRate = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
2060 21546 : thisPIU.PriMassFlowRate = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate;
2061 21546 : thisPIU.DischargeAirTemp = state.dataLoopNodes->Node(thisPIU.OutAirNode).Temp;
2062 21546 : if (state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRate == 0.0) {
2063 74 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2064 74 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = 0.0;
2065 : }
2066 21546 : if (thisPIU.InducesPlenumAir) {
2067 7300 : state.dataHVACGlobal->PlenumInducedMassFlow = state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
2068 : } else {
2069 14246 : state.dataHVACGlobal->PlenumInducedMassFlow = 0.0;
2070 : }
2071 21546 : state.dataDefineEquipment->AirDistUnit(thisPIU.ADUNum).MassFlowRatePlenInd = state.dataHVACGlobal->PlenumInducedMassFlow;
2072 21546 : state.dataLoopNodes->Node(thisPIU.OutAirNode).MassFlowRateMax = thisPIU.MaxPriAirMassFlow;
2073 :
2074 21546 : ReportCurOperatingControlStage(state, PIUNum, UnitOn, thisPIU.heatingOperatingMode, thisPIU.coolingOperatingMode);
2075 21546 : }
2076 :
2077 308748 : void ReportCurOperatingControlStage(EnergyPlusData &state, int const piuNum, bool const unitOn, HeatOpModeType heaterMode, CoolOpModeType coolingMode)
2078 : {
2079 308748 : int undetermined(-1);
2080 308748 : int off(0);
2081 308748 : int constantVolumeCooling(1);
2082 308748 : int constantVolumeHeating(2);
2083 308748 : int deadband(3);
2084 308748 : int variableSpeedFirstStageCooling(4);
2085 308748 : int variableSpeedSecondStageCooling(5);
2086 308748 : int variableSpeedStagedHeatFirstStageHeating(6);
2087 308748 : int variableSpeedStagedHeatSecondStageHeating(7);
2088 308748 : int variableSpeedModulatedHeatFirstStageHeating(8);
2089 308748 : int variableSpeedModulatedHeatSecondStageHeating(9);
2090 308748 : int variableSpeedModulatedHeatThirdStageHeating(10);
2091 :
2092 308748 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = undetermined;
2093 :
2094 308748 : if (!unitOn) {
2095 56947 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = off;
2096 : } else {
2097 251801 : if (state.dataPowerInductionUnits->PIU(piuNum).fanControlType == FanCntrlType::ConstantSpeedFan) {
2098 237069 : if (heaterMode != HeatOpModeType::HeaterOff && coolingMode == CoolOpModeType::CoolerOff) {
2099 122663 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = constantVolumeHeating;
2100 114406 : } else if (coolingMode != CoolOpModeType::CoolerOff && heaterMode == HeatOpModeType::HeaterOff) {
2101 68904 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = constantVolumeCooling;
2102 : } else {
2103 45502 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = deadband;
2104 : }
2105 : }
2106 251801 : if (state.dataPowerInductionUnits->PIU(piuNum).fanControlType == FanCntrlType::VariableSpeedFan) {
2107 14732 : if (heaterMode != HeatOpModeType::HeaterOff) {
2108 6770 : if (state.dataPowerInductionUnits->PIU(piuNum).heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
2109 4078 : if (heaterMode == HeatOpModeType::StagedHeatFirstStage) {
2110 1400 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedStagedHeatFirstStageHeating;
2111 2678 : } else if (heaterMode == HeatOpModeType::StagedHeatSecondStage) {
2112 2678 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedStagedHeatSecondStageHeating;
2113 : }
2114 2692 : } else if (state.dataPowerInductionUnits->PIU(piuNum).heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
2115 2692 : if (heaterMode == HeatOpModeType::ModulatedHeatFirstStage) {
2116 2692 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatFirstStageHeating;
2117 0 : } else if (heaterMode == HeatOpModeType::ModulatedHeatSecondStage) {
2118 0 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatSecondStageHeating;
2119 0 : } else if (heaterMode == HeatOpModeType::ModulatedHeatThirdStage) {
2120 0 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedModulatedHeatThirdStageHeating;
2121 : }
2122 : }
2123 7962 : } else if (coolingMode == CoolOpModeType::CoolFirstStage) {
2124 3492 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedFirstStageCooling;
2125 4470 : } else if (coolingMode == CoolOpModeType::CoolSecondStage) {
2126 0 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = variableSpeedSecondStageCooling;
2127 4470 : } else if (heaterMode == HeatOpModeType::HeaterOff && coolingMode == CoolOpModeType::CoolerOff) {
2128 4470 : state.dataPowerInductionUnits->PIU(piuNum).CurOperationControlStage = deadband;
2129 : }
2130 : }
2131 : }
2132 308748 : }
2133 :
2134 1792 : void CalcVariableSpeedPIUCoolingBehavior(EnergyPlusData &state,
2135 : int const piuNum, // number of the current PIU being simulated
2136 : int const zoneNode, // zone node number
2137 : Real64 const zoneLoad,
2138 : Real64 const loadToHeatSetPt,
2139 : Real64 const priAirMassFlowMin,
2140 : Real64 const priAirMassFlowMax)
2141 : {
2142 1792 : auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
2143 1792 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolerOff;
2144 :
2145 : // set min primary flow and low secondary
2146 1792 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = priAirMassFlowMin;
2147 1792 : Real64 TotAirMassFlow = thisPIU.MinTotAirMassFlow;
2148 1792 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - priAirMassFlowMin);
2149 :
2150 : // calculate cooling provided to zone at minimum fan speed and minimum primary air mass flow
2151 1792 : Real64 qdotDelivMinPrim = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
2152 :
2153 1792 : if (qdotDelivMinPrim <= zoneLoad) { // will provide more cooling than required at minimum primary flow
2154 376 : thisPIU.PriAirMassFlow = priAirMassFlowMin;
2155 376 : if (qdotDelivMinPrim >=
2156 : loadToHeatSetPt) { // will provide more cooling than required but not enough to drop below the heating thermostat setpoint
2157 0 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.PriAirMassFlow);
2158 0 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
2159 : } else {
2160 376 : if (thisPIU.heatingControlType == HeatCntrlBehaviorType::StagedHeaterBehavior) {
2161 208 : CalcVariableSpeedPIUStagedHeatingBehavior(state, piuNum, zoneNode, loadToHeatSetPt, true, thisPIU.PriAirMassFlow);
2162 168 : } else if (thisPIU.heatingControlType == HeatCntrlBehaviorType::ModulatedHeaterBehavior) {
2163 168 : CalcVariableSpeedPIUModulatedHeatingBehavior(state, piuNum, zoneNode, loadToHeatSetPt, true, thisPIU.PriAirMassFlow);
2164 : }
2165 : }
2166 : } else {
2167 : // check how much cooling provided at max fan and primary air
2168 1416 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MaxPriAirMassFlow;
2169 1416 : Real64 TotAirMassFlow = thisPIU.MaxTotAirMassFlow;
2170 1416 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - thisPIU.MaxPriAirMassFlow);
2171 1416 : Real64 qdotDelivMaxFan = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, 1.0);
2172 :
2173 1416 : if (zoneLoad <= qdotDelivMaxFan) { // not going to make it just run at max
2174 0 : thisPIU.PriAirMassFlow = thisPIU.PriAirMassFlow;
2175 0 : Real64 TotAirMassFlow = thisPIU.MaxTotAirMassFlow;
2176 0 : thisPIU.SecAirMassFlow = max(0.0, TotAirMassFlow - thisPIU.PriAirMassFlow);
2177 0 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
2178 0 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolSecondStage;
2179 : } else {
2180 : // call regula falsi solver, vary a coooling control signal for fan speed and primary air flow together from min to max.
2181 1416 : int constexpr MaxIte(500); // Maximum number of iterations
2182 1416 : Real64 constexpr Acc(0.0001); // Accuracy of result
2183 1416 : int SolFla(0); // Flag of solver
2184 1416 : Real64 coolSignal = 0.5; // starting value
2185 7001 : auto f = [&state, piuNum, zoneLoad, zoneNode](Real64 const coolSignal) {
2186 7001 : return CalcVariableSpeedPIUCoolingResidual(state, coolSignal, piuNum, zoneLoad, zoneNode);
2187 1416 : };
2188 :
2189 1416 : General::SolveRoot(state, Acc, MaxIte, SolFla, coolSignal, f, 0.0, 1.0);
2190 :
2191 1416 : if (SolFla == -1) {
2192 0 : ShowSevereError(state, "Iteration limit exceeded in calculating variable speed fan powered box cooling signal");
2193 0 : ShowContinueErrorTimeStamp(state, "");
2194 0 : ShowFatalError(state, format("Series PIU control failed for {}:{} ", thisPIU.UnitType, thisPIU.Name));
2195 1416 : } else if (SolFla == -2) {
2196 0 : ShowSevereError(state, "Bad starting values for in calculating variable speed fan powered box cooling signal");
2197 0 : ShowContinueError(state, format("Zone Load to Cooling Setpoint = {:.2R} [W]", zoneLoad));
2198 0 : ShowContinueError(state, format("Load Delivered to Zone at Minimum Fan Speed = {:.2R} [W]", qdotDelivMinPrim));
2199 0 : ShowContinueErrorTimeStamp(state, "");
2200 0 : ShowFatalError(state, format("Series PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
2201 : } else {
2202 1416 : thisPIU.PriAirMassFlow = coolSignal * (thisPIU.MaxPriAirMassFlow - thisPIU.MinPriAirMassFlow) + thisPIU.MinPriAirMassFlow;
2203 1416 : Real64 TotAirMassFlow = coolSignal * (thisPIU.MaxTotAirMassFlow - thisPIU.MinTotAirMassFlow) + thisPIU.MinTotAirMassFlow;
2204 1416 : thisPIU.SecAirMassFlow = max(0.0, TotAirMassFlow - thisPIU.PriAirMassFlow);
2205 1416 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
2206 1416 : thisPIU.coolingOperatingMode = CoolOpModeType::CoolFirstStage;
2207 : }
2208 : }
2209 : }
2210 1792 : }
2211 :
2212 2922 : void CalcVariableSpeedPIUStagedHeatingBehavior(EnergyPlusData &state,
2213 : int const piuNum, // number of the current PIU being simulated
2214 : int const zoneNode, // zone node number
2215 : Real64 const zoneLoad,
2216 : bool const pri,
2217 : Real64 const primaryAirMassFlow)
2218 : {
2219 2922 : auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
2220 :
2221 : // Calculate heating provided to zone with no coil at the maximum secondary flow rate: "1st stage, max fan"
2222 2922 : if (pri) {
2223 2916 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2224 1560 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2225 1560 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
2226 1356 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2227 1356 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2228 1356 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
2229 : }
2230 : } else {
2231 6 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2232 3 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2233 3 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
2234 3 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2235 3 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2236 3 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
2237 : }
2238 : }
2239 : Real64 TotAirMassFlow =
2240 2922 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
2241 2922 : Real64 qdotDelivered1stStageMaxFan = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, 1.0);
2242 :
2243 : // Calculate heating provided to zone with no coil at the minimum secondary flow rate: "1st stage, min fan"
2244 2922 : if (pri) {
2245 2916 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2246 1560 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2247 1560 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
2248 1356 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2249 1356 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2250 1356 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
2251 : }
2252 : } else {
2253 6 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2254 3 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2255 3 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinTotAirMassFlow;
2256 3 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2257 3 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2258 3 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
2259 : }
2260 : }
2261 2922 : TotAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
2262 : Real64 qdotDelivered1stStageMinFan =
2263 2922 : CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, false, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
2264 :
2265 2922 : if (qdotDelivered1stStageMinFan <= zoneLoad && qdotDelivered1stStageMaxFan >= zoneLoad) { // 1st of heating (no coil) can meet the load
2266 : // Find fan speed/flow that meets the load through iteration
2267 13 : thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatFirstStage;
2268 13 : int constexpr MaxIte(500); // Maximum number of iterations
2269 13 : Real64 constexpr Acc(0.001); // Accuracy of result
2270 13 : int SolFla(0); // Flag of solver
2271 13 : Real64 fanSignal = 0.0;
2272 13 : fanSignal = (1.0 - thisPIU.MinFanTurnDownRatio) * 0.5 + thisPIU.MinFanTurnDownRatio; // average speed as the initial value
2273 67 : auto f = [&state, piuNum, zoneLoad, zoneNode, primaryAirMassFlow](Real64 const fanSignal) {
2274 67 : return CalcVariableSpeedPIUHeatingResidual(state, fanSignal, piuNum, zoneLoad, zoneNode, primaryAirMassFlow, false, fanSignal);
2275 13 : };
2276 :
2277 13 : General::SolveRoot(state, Acc, MaxIte, SolFla, fanSignal, f, thisPIU.MinFanTurnDownRatio, 1.0);
2278 :
2279 13 : if (SolFla == -1) {
2280 0 : ShowSevereError(state, "Iteration limit exceeded in calculating variable speed fan powered box 1st stage heating fan speed");
2281 0 : ShowContinueErrorTimeStamp(state, "");
2282 0 : ShowFatalError(state, format("PIU control failed for {}:{} ", thisPIU.UnitType, thisPIU.Name));
2283 13 : } else if (SolFla == -2) {
2284 0 : ShowSevereError(state, "Bad starting values in calculating variable speed fan powered box 1st stage heating fan speed");
2285 0 : ShowContinueErrorTimeStamp(state, "");
2286 0 : ShowFatalError(state, format("PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
2287 : } else {
2288 13 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2289 0 : thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
2290 13 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2291 13 : thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxSecAirMassFlow);
2292 : }
2293 : }
2294 2922 : } else if (qdotDelivered1stStageMaxFan < zoneLoad) {
2295 2892 : thisPIU.heatingOperatingMode = HeatOpModeType::StagedHeatSecondStage;
2296 2892 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2297 1559 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
2298 1333 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2299 1333 : thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
2300 : }
2301 17 : } else if (qdotDelivered1stStageMinFan > zoneLoad) {
2302 17 : thisPIU.heatingOperatingMode = HeatOpModeType::HeaterOff;
2303 17 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2304 4 : thisPIU.SecAirMassFlow = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
2305 13 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2306 13 : thisPIU.SecAirMassFlow = 0.0;
2307 : }
2308 : }
2309 2922 : }
2310 :
2311 18996 : Real64 CalcVariableSpeedPIUQdotDelivered(EnergyPlusData &state,
2312 : int const piuNum, // number of the current PIU being simulated
2313 : int const zoneNode, // zone node number
2314 : bool const useDAT,
2315 : Real64 const totAirMassFlow,
2316 : Real64 const fanTurnDown)
2317 : {
2318 18996 : Real64 qdotDelivered = 0.0;
2319 18996 : auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
2320 18996 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2321 14857 : MixerComponent::SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
2322 14857 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, false, fanTurnDown, _);
2323 : } else {
2324 4139 : state.dataFans->fans(thisPIU.Fan_Index)->simulate(state, false, fanTurnDown, _);
2325 4139 : MixerComponent::SimAirMixer(state, thisPIU.MixerName, thisPIU.Mixer_Num);
2326 : }
2327 18996 : Real64 zoneEnthalpy = Psychrometrics::PsyHFnTdbW(state.dataLoopNodes->Node(zoneNode).Temp, state.dataLoopNodes->Node(zoneNode).HumRat);
2328 18996 : Real64 piuTemp = 0.0;
2329 18996 : if (useDAT) {
2330 2876 : piuTemp = thisPIU.designHeatingDAT;
2331 : } else {
2332 16120 : piuTemp = state.dataLoopNodes->Node(thisPIU.HCoilInAirNode).Temp;
2333 : }
2334 18996 : Real64 piuEnthalpy = Psychrometrics::PsyHFnTdbW(piuTemp, state.dataLoopNodes->Node(zoneNode).HumRat);
2335 18996 : qdotDelivered = totAirMassFlow * (piuEnthalpy - zoneEnthalpy);
2336 18996 : return qdotDelivered;
2337 : }
2338 :
2339 2874 : void CalcVariableSpeedPIUModulatedHeatingBehavior(EnergyPlusData &state,
2340 : int const piuNum, // number of the current PIU being simulated
2341 : int const zoneNode, // zone node number
2342 : Real64 const zoneLoad,
2343 : bool const pri,
2344 : Real64 const primaryAirMassFlow)
2345 : {
2346 2874 : auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
2347 :
2348 : // Calculate heating provided to zone with no coil at the minimum secondary flow rate: "1st stage, min fan"
2349 2874 : if (pri) {
2350 2868 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2351 1518 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2352 1518 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MinTotAirMassFlow - thisPIU.MinPriAirMassFlow);
2353 1350 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2354 1350 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2355 1350 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
2356 : }
2357 : } else {
2358 6 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2359 3 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2360 3 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinTotAirMassFlow;
2361 3 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2362 3 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2363 3 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MinSecAirMassFlow;
2364 : }
2365 : }
2366 : Real64 TotAirMassFlow =
2367 2874 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
2368 2874 : Real64 qdotDeliveredEnd1stStage = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, true, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
2369 2874 : if (qdotDeliveredEnd1stStage >= zoneLoad) { // 1st stage, find heating power at minimum fan speed
2370 2872 : thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatFirstStage;
2371 2872 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2372 1520 : thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
2373 1352 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2374 1352 : thisPIU.SecAirMassFlow = thisPIU.MinSecAirMassFlow;
2375 : }
2376 : } else {
2377 2 : if (pri) {
2378 0 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2379 0 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2380 0 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow);
2381 0 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2382 0 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = thisPIU.MinPriAirMassFlow;
2383 0 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
2384 : }
2385 : } else {
2386 2 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2387 1 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2388 1 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxTotAirMassFlow;
2389 1 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2390 1 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = 0.0;
2391 1 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = thisPIU.MaxSecAirMassFlow;
2392 : }
2393 : }
2394 2 : TotAirMassFlow = state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate + state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate;
2395 : Real64 qdotDeliveredEnd2ndStage =
2396 2 : CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNode, true, TotAirMassFlow, thisPIU.MinFanTurnDownRatio);
2397 2 : if (qdotDeliveredEnd2ndStage > zoneLoad) { // 2nd stage
2398 0 : thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatSecondStage;
2399 : // Find fan speed that meets zone heating load
2400 0 : int constexpr MaxIte(500); // Maximum number of iterations
2401 0 : Real64 constexpr Acc(0.0001); // Accuracy of result
2402 0 : int SolFla(0); // Flag of solver
2403 0 : Real64 fanSignal = (1.0 - thisPIU.MinFanTurnDownRatio) * 0.5 + thisPIU.MinFanTurnDownRatio; // starting value in middle
2404 0 : auto f = [&state, piuNum, zoneLoad, zoneNode, primaryAirMassFlow](Real64 const fanSignal) {
2405 0 : return CalcVariableSpeedPIUHeatingResidual(state, fanSignal, piuNum, zoneLoad, zoneNode, primaryAirMassFlow, true, fanSignal);
2406 0 : };
2407 :
2408 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, fanSignal, f, thisPIU.MinFanTurnDownRatio, 1.0);
2409 :
2410 0 : if (SolFla == -1) {
2411 0 : ShowSevereError(state, "Iteration limit exceeded in calculating variable speed fan powered box 2nd stage heating fan speed");
2412 0 : ShowContinueErrorTimeStamp(state, "");
2413 0 : ShowFatalError(state, format("PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
2414 0 : } else if (SolFla == -2) {
2415 0 : ShowSevereError(state, "Bad starting values for in calculating variable speed fan powered box 2nd stage heating fan speed");
2416 0 : ShowContinueErrorTimeStamp(state, "");
2417 0 : ShowFatalError(state, format("PIU control failed for {}:{}", thisPIU.UnitType, thisPIU.Name));
2418 : } else {
2419 0 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2420 0 : thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxTotAirMassFlow - primaryAirMassFlow);
2421 0 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2422 0 : thisPIU.SecAirMassFlow = max(0.0, fanSignal * thisPIU.MaxSecAirMassFlow);
2423 : }
2424 : }
2425 : } else { // 3rd stage, full fan speed
2426 2 : thisPIU.heatingOperatingMode = HeatOpModeType::ModulatedHeatThirdStage;
2427 2 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2428 1 : thisPIU.SecAirMassFlow = thisPIU.MaxTotAirMassFlow - thisPIU.MinPriAirMassFlow;
2429 1 : } else if (thisPIU.UnitType == "AirTerminal:SingleDuct:ParallelPIU:Reheat") {
2430 1 : thisPIU.SecAirMassFlow = thisPIU.MaxSecAirMassFlow;
2431 : }
2432 : }
2433 : }
2434 2874 : }
2435 :
2436 67 : Real64 CalcVariableSpeedPIUHeatingResidual(EnergyPlusData &state,
2437 : Real64 const fanSignal,
2438 : int const piuNum,
2439 : Real64 const targetQznReq,
2440 : int const zoneNodeNum,
2441 : Real64 const primaryMassFlow,
2442 : bool useDAT,
2443 : Real64 const fanTurnDown)
2444 :
2445 : {
2446 : // used to find a fan speed to meet load to heating setpoint with no heater power
2447 : // 1st stage heating for staged heat, also used for undershoot case where cooling at min primary flow would push below heating
2448 : // setpoint.
2449 67 : auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
2450 67 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = primaryMassFlow;
2451 67 : Real64 TotAirMassFlow = 0.0;
2452 67 : if (thisPIU.UnitType == "AirTerminal:SingleDuct:SeriesPIU:Reheat") {
2453 0 : TotAirMassFlow = fanSignal * thisPIU.MaxTotAirMassFlow;
2454 0 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = max(0.0, TotAirMassFlow - primaryMassFlow);
2455 : } else {
2456 : // parallel
2457 67 : TotAirMassFlow = fanSignal * thisPIU.MaxSecAirMassFlow + primaryMassFlow;
2458 67 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = fanSignal * thisPIU.MaxSecAirMassFlow;
2459 : }
2460 :
2461 : // calculate heating provided to zone
2462 67 : Real64 qdotDelivered = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNodeNum, useDAT, TotAirMassFlow, fanTurnDown);
2463 : // formulate residual and return
2464 67 : Real64 Residuum = (targetQznReq - qdotDelivered);
2465 67 : return Residuum;
2466 : }
2467 :
2468 7001 : Real64 CalcVariableSpeedPIUCoolingResidual(EnergyPlusData &state, Real64 const coolSignal, int piuNum, Real64 targetQznReq, int zoneNodeNum)
2469 : {
2470 : // used for cooling control with VS fan. Simultaneous control of fan speed and primary air damper
2471 : // given trial cooling signal, calculate the cooling provided and a residual that compares what is delivered vs what the zone
2472 : // needs. set the flows, controller acts on fan and damper simultaneously
2473 7001 : auto &thisPIU = state.dataPowerInductionUnits->PIU(piuNum);
2474 7001 : Real64 PriAirMassFlow = coolSignal * (thisPIU.MaxPriAirMassFlow - thisPIU.MinPriAirMassFlow) + thisPIU.MinPriAirMassFlow;
2475 7001 : Real64 TotAirMassFlow = coolSignal * (thisPIU.MaxTotAirMassFlow - thisPIU.MinTotAirMassFlow) + thisPIU.MinTotAirMassFlow;
2476 7001 : Real64 SecAirMassFlow = max(0.0, TotAirMassFlow - PriAirMassFlow);
2477 7001 : state.dataLoopNodes->Node(thisPIU.PriAirInNode).MassFlowRate = PriAirMassFlow;
2478 7001 : state.dataLoopNodes->Node(thisPIU.SecAirInNode).MassFlowRate = SecAirMassFlow;
2479 :
2480 7001 : Real64 fanTurnDown = coolSignal * (1.0 - thisPIU.MinFanTurnDownRatio) + thisPIU.MinFanTurnDownRatio;
2481 7001 : Real64 qdotDelivered = CalcVariableSpeedPIUQdotDelivered(state, piuNum, zoneNodeNum, false, TotAirMassFlow, fanTurnDown);
2482 : // formulate residual and return
2483 7001 : Real64 Residuum = (targetQznReq - qdotDelivered);
2484 7001 : return Residuum;
2485 : }
2486 :
2487 308748 : void ReportPIU(EnergyPlusData &state, int const PIUNum) // number of the current fan coil unit being simulated
2488 : {
2489 :
2490 : // SUBROUTINE INFORMATION:
2491 : // AUTHOR Fred Buhl
2492 : // DATE WRITTEN August 2000
2493 : // MODIFIED na
2494 : // RE-ENGINEERED na
2495 :
2496 : // PURPOSE OF THIS SUBROUTINE:
2497 : // Fills some report variables for the PIU terminal boxes
2498 :
2499 : // Using/Aliasing
2500 308748 : Real64 TimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
2501 :
2502 308748 : auto &thisPIU = state.dataPowerInductionUnits->PIU(PIUNum);
2503 308748 : thisPIU.HeatingEnergy = thisPIU.HeatingRate * TimeStepSysSec;
2504 308748 : thisPIU.SensCoolEnergy = thisPIU.SensCoolRate * TimeStepSysSec;
2505 :
2506 : // set zone OA Volume flow rate
2507 308748 : thisPIU.CalcOutdoorAirVolumeFlowRate(state);
2508 308748 : }
2509 :
2510 : // ===================== Utilities =====================================
2511 :
2512 63 : bool PIUnitHasMixer(EnergyPlusData &state, std::string_view CompName) // component (mixer) name
2513 : {
2514 :
2515 : // FUNCTION INFORMATION:
2516 : // AUTHOR Linda Lawrie
2517 : // DATE WRITTEN September 2011
2518 : // MODIFIED na
2519 : // RE-ENGINEERED na
2520 :
2521 : // PURPOSE OF THIS FUNCTION:
2522 : // Given a mixer name, this routine determines if that mixer is found on
2523 : // PIUnits.
2524 :
2525 : // Return value
2526 63 : bool YesNo = false; // True if found
2527 :
2528 63 : if (state.dataPowerInductionUnits->GetPIUInputFlag) {
2529 2 : GetPIUs(state);
2530 2 : state.dataPowerInductionUnits->GetPIUInputFlag = false;
2531 : }
2532 :
2533 63 : if (state.dataPowerInductionUnits->NumPIUs > 0) {
2534 57 : int const ItemNum = Util::FindItemInList(CompName, state.dataPowerInductionUnits->PIU, &PowIndUnitData::MixerName);
2535 57 : if (ItemNum > 0) {
2536 57 : YesNo = true;
2537 : }
2538 : }
2539 :
2540 63 : return YesNo;
2541 : }
2542 :
2543 26 : void PIUInducesPlenumAir(EnergyPlusData &state, int const NodeNum, int const plenumNum) // induced air node number
2544 : {
2545 :
2546 : // SUBROUTINE INFORMATION:
2547 : // AUTHOR Fred Buhl
2548 : // DATE WRITTEN January 2012
2549 : // MODIFIED na
2550 : // RE-ENGINEERED na
2551 :
2552 : // PURPOSE OF THIS FUNCTION:
2553 : // Marks a PIU air terminal unit as obtaining its induced air from
2554 : // a plenum.
2555 :
2556 26 : if (state.dataPowerInductionUnits->GetPIUInputFlag) {
2557 2 : GetPIUs(state);
2558 2 : state.dataPowerInductionUnits->GetPIUInputFlag = false;
2559 : }
2560 :
2561 137 : for (int PIUIndex = 1; PIUIndex <= state.dataPowerInductionUnits->NumPIUs; ++PIUIndex) {
2562 130 : if (NodeNum == state.dataPowerInductionUnits->PIU(PIUIndex).SecAirInNode) {
2563 19 : state.dataPowerInductionUnits->PIU(PIUIndex).InducesPlenumAir = true;
2564 19 : state.dataPowerInductionUnits->PIU(PIUIndex).plenumIndex = plenumNum;
2565 19 : break;
2566 : }
2567 : }
2568 26 : }
2569 :
2570 308748 : void PowIndUnitData::CalcOutdoorAirVolumeFlowRate(EnergyPlusData &state)
2571 : {
2572 : // calculates zone outdoor air volume flow rate using the supply air flow rate and OA fraction
2573 308748 : if (this->AirLoopNum > 0) {
2574 308691 : this->OutdoorAirFlowRate = (state.dataLoopNodes->Node(this->PriAirInNode).MassFlowRate / state.dataEnvrn->StdRhoAir) *
2575 308691 : state.dataAirLoop->AirLoopFlow(this->AirLoopNum).OAFrac;
2576 : } else {
2577 57 : this->OutdoorAirFlowRate = 0.0;
2578 : }
2579 308748 : }
2580 :
2581 57 : void PowIndUnitData::reportTerminalUnit(EnergyPlusData &state)
2582 : {
2583 : // populate the predefined equipment summary report related to air terminals
2584 57 : auto &orp = state.dataOutRptPredefined;
2585 57 : auto &adu = state.dataDefineEquipment->AirDistUnit(this->ADUNum);
2586 57 : if (!state.dataSize->TermUnitFinalZoneSizing.empty()) {
2587 57 : auto &sizing = state.dataSize->TermUnitFinalZoneSizing(adu.TermUnitSizingNum);
2588 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlow, adu.Name, sizing.DesCoolVolFlowMin);
2589 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOutdoorFlow, adu.Name, sizing.MinOA);
2590 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupCoolingSP, adu.Name, sizing.CoolDesTemp);
2591 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupHeatingSP, adu.Name, sizing.HeatDesTemp);
2592 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatingCap, adu.Name, sizing.DesHeatLoad);
2593 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolingCap, adu.Name, sizing.DesCoolLoad);
2594 : }
2595 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermTypeInp, adu.Name, this->UnitType);
2596 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermPrimFlow, adu.Name, this->MaxPriAirVolFlow);
2597 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSecdFlow, adu.Name, this->MaxSecAirVolFlow);
2598 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, "n/a");
2599 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMaxFlowReh, adu.Name, "n/a");
2600 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOAflowSch, adu.Name, "n/a");
2601 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, HCoilNamesUC[(int)this->HCoilType]);
2602 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "n/a");
2603 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanType, adu.Name, HVAC::fanTypeNames[(int)this->fanType]);
2604 57 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanName, adu.Name, this->FanName);
2605 57 : }
2606 :
2607 : } // namespace EnergyPlus::PoweredInductionUnits
|