Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/AirTerminalUnit.hh>
57 : #include <EnergyPlus/Autosizing/Base.hh>
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/CurveManager.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataContaminantBalance.hh>
62 : #include <EnergyPlus/DataDefineEquip.hh>
63 : #include <EnergyPlus/DataEnvironment.hh>
64 : #include <EnergyPlus/DataHVACGlobals.hh>
65 : #include <EnergyPlus/DataIPShortCuts.hh>
66 : #include <EnergyPlus/DataLoopNode.hh>
67 : #include <EnergyPlus/DataSizing.hh>
68 : #include <EnergyPlus/DataZoneEnergyDemands.hh>
69 : #include <EnergyPlus/DataZoneEquipment.hh>
70 : #include <EnergyPlus/FluidProperties.hh>
71 : #include <EnergyPlus/General.hh>
72 : #include <EnergyPlus/GeneralRoutines.hh>
73 : #include <EnergyPlus/HVACFourPipeBeam.hh>
74 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
75 : #include <EnergyPlus/NodeInputManager.hh>
76 : #include <EnergyPlus/OutputProcessor.hh>
77 : #include <EnergyPlus/OutputReportPredefined.hh>
78 : #include <EnergyPlus/Plant/DataPlant.hh>
79 : #include <EnergyPlus/PlantUtilities.hh>
80 : #include <EnergyPlus/Psychrometrics.hh>
81 : #include <EnergyPlus/ScheduleManager.hh>
82 : #include <EnergyPlus/UtilityRoutines.hh>
83 :
84 : namespace EnergyPlus {
85 :
86 : namespace FourPipeBeam {
87 :
88 : // HVACFourPipeBeam::HVACFourPipeBeam(){}
89 : ///// Note use of shared_ptr here is not a good pattern, not to be replicated without further discussion.
90 4 : std::shared_ptr<AirTerminalUnit> HVACFourPipeBeam::fourPipeBeamFactory(EnergyPlusData &state, std::string objectName)
91 : {
92 :
93 : using BranchNodeConnections::TestCompSet;
94 : using DataLoopNode::ObjectIsNotParent;
95 : using DataLoopNode::ObjectIsParent;
96 : using NodeInputManager::GetOnlySingleNode;
97 : using namespace DataSizing;
98 : using Curve::GetCurveIndex;
99 : static constexpr std::string_view routineName("FourPipeBeamFactory "); // include trailing blank space
100 :
101 : int beamIndex; // loop index
102 :
103 : // certain object in the input file
104 4 : bool errFlag = false;
105 4 : bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
106 4 : bool found = false;
107 : bool airNodeFound;
108 : int aDUIndex;
109 :
110 : ///// Note use of shared_ptr here is not a good pattern, not to be replicated without further discussion.
111 4 : std::shared_ptr<HVACFourPipeBeam> thisBeam(new HVACFourPipeBeam());
112 4 : auto &cCurrentModuleObject = state.dataIPShortCut->cCurrentModuleObject;
113 : // find the number of cooled beam units
114 4 : cCurrentModuleObject = "AirTerminal:SingleDuct:ConstantVolume:FourPipeBeam";
115 :
116 : // find beam index from name
117 4 : beamIndex = state.dataInputProcessing->inputProcessor->getObjectItemNum(state, cCurrentModuleObject, objectName);
118 4 : if (beamIndex > 0) {
119 : int IOStatus;
120 4 : int NumAlphas = 16;
121 4 : int NumNumbers = 11;
122 8 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
123 : cCurrentModuleObject,
124 : beamIndex,
125 4 : state.dataIPShortCut->cAlphaArgs,
126 : NumAlphas,
127 4 : state.dataIPShortCut->rNumericArgs,
128 : NumNumbers,
129 : IOStatus,
130 4 : state.dataIPShortCut->lNumericFieldBlanks,
131 4 : state.dataIPShortCut->lAlphaFieldBlanks,
132 4 : state.dataIPShortCut->cAlphaFieldNames,
133 4 : state.dataIPShortCut->cNumericFieldNames);
134 4 : found = true;
135 : } else {
136 0 : ErrorsFound = true;
137 : }
138 :
139 4 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)};
140 :
141 4 : errFlag = false;
142 4 : GlobalNames::VerifyUniqueADUName(state, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1), errFlag, cCurrentModuleObject + " Name");
143 4 : if (errFlag) {
144 0 : ErrorsFound = true;
145 : }
146 4 : thisBeam->name = state.dataIPShortCut->cAlphaArgs(1);
147 4 : thisBeam->unitType = cCurrentModuleObject;
148 :
149 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
150 1 : thisBeam->airAvailSched = Sched::GetScheduleAlwaysOn(state);
151 3 : } else if ((thisBeam->airAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(2))) == nullptr) {
152 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2));
153 0 : ErrorsFound = true;
154 : }
155 :
156 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(3)) {
157 1 : thisBeam->coolingAvailSched = Sched::GetScheduleAlwaysOn(state);
158 3 : } else if ((thisBeam->coolingAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(3))) == nullptr) {
159 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(3), state.dataIPShortCut->cAlphaArgs(3));
160 0 : ErrorsFound = true;
161 : }
162 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(4)) {
163 1 : thisBeam->heatingAvailSched = Sched::GetScheduleAlwaysOn(state);
164 3 : } else if ((thisBeam->heatingAvailSched = Sched::GetSchedule(state, state.dataIPShortCut->cAlphaArgs(4))) == nullptr) {
165 0 : ShowSevereItemNotFound(state, eoh, state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4));
166 0 : ErrorsFound = true;
167 : }
168 :
169 4 : thisBeam->airInNodeNum = GetOnlySingleNode(state,
170 4 : state.dataIPShortCut->cAlphaArgs(5),
171 : ErrorsFound,
172 : DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctConstantVolumeFourPipeBeam,
173 4 : state.dataIPShortCut->cAlphaArgs(1),
174 : DataLoopNode::NodeFluidType::Air,
175 : DataLoopNode::ConnectionType::Inlet,
176 : NodeInputManager::CompFluidStream::Primary,
177 : ObjectIsNotParent,
178 4 : state.dataIPShortCut->cAlphaFieldNames(5));
179 4 : thisBeam->airOutNodeNum = GetOnlySingleNode(state,
180 4 : state.dataIPShortCut->cAlphaArgs(6),
181 : ErrorsFound,
182 : DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctConstantVolumeFourPipeBeam,
183 4 : state.dataIPShortCut->cAlphaArgs(1),
184 : DataLoopNode::NodeFluidType::Air,
185 : DataLoopNode::ConnectionType::Outlet,
186 : NodeInputManager::CompFluidStream::Primary,
187 : ObjectIsNotParent,
188 4 : state.dataIPShortCut->cAlphaFieldNames(6));
189 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(7) && state.dataIPShortCut->lAlphaFieldBlanks(8)) { // no chilled water nodes, no beam cooling
190 0 : thisBeam->beamCoolingPresent = false;
191 4 : } else if (state.dataIPShortCut->lAlphaFieldBlanks(7) &&
192 0 : !state.dataIPShortCut->lAlphaFieldBlanks(8)) { // outlet node but no inlet node for chilled water
193 0 : thisBeam->beamCoolingPresent = false;
194 0 : ShowWarningError(state,
195 0 : format("{}{}: missing {} for {}={}, simulation continues with no beam cooling",
196 : routineName,
197 : cCurrentModuleObject,
198 0 : state.dataIPShortCut->cAlphaFieldNames(7),
199 0 : state.dataIPShortCut->cAlphaFieldNames(1),
200 0 : state.dataIPShortCut->cAlphaArgs(1)));
201 8 : } else if (!state.dataIPShortCut->lAlphaFieldBlanks(7) &&
202 4 : state.dataIPShortCut->lAlphaFieldBlanks(8)) { // inlet node but no outlet node for chilled water
203 0 : thisBeam->beamCoolingPresent = false;
204 0 : ShowWarningError(state,
205 0 : format("{}{}: missing {} for {}={}, simulation continues with no beam cooling",
206 : routineName,
207 : cCurrentModuleObject,
208 0 : state.dataIPShortCut->cAlphaFieldNames(8),
209 0 : state.dataIPShortCut->cAlphaFieldNames(1),
210 0 : state.dataIPShortCut->cAlphaArgs(1)));
211 : } else {
212 4 : thisBeam->beamCoolingPresent = true;
213 4 : thisBeam->cWInNodeNum = GetOnlySingleNode(state,
214 4 : state.dataIPShortCut->cAlphaArgs(7),
215 : ErrorsFound,
216 : DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctConstantVolumeFourPipeBeam,
217 4 : state.dataIPShortCut->cAlphaArgs(1),
218 : DataLoopNode::NodeFluidType::Water,
219 : DataLoopNode::ConnectionType::Inlet,
220 : NodeInputManager::CompFluidStream::Secondary,
221 : ObjectIsParent,
222 4 : state.dataIPShortCut->cAlphaFieldNames(7));
223 4 : thisBeam->cWOutNodeNum = GetOnlySingleNode(state,
224 4 : state.dataIPShortCut->cAlphaArgs(8),
225 : ErrorsFound,
226 : DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctConstantVolumeFourPipeBeam,
227 4 : state.dataIPShortCut->cAlphaArgs(1),
228 : DataLoopNode::NodeFluidType::Water,
229 : DataLoopNode::ConnectionType::Outlet,
230 : NodeInputManager::CompFluidStream::Secondary,
231 : ObjectIsParent,
232 4 : state.dataIPShortCut->cAlphaFieldNames(8));
233 : }
234 4 : if (state.dataIPShortCut->lAlphaFieldBlanks(9) && state.dataIPShortCut->lAlphaFieldBlanks(10)) { // no hot water nodes, no beam heating
235 0 : thisBeam->beamHeatingPresent = false;
236 4 : } else if (state.dataIPShortCut->lAlphaFieldBlanks(9) &&
237 0 : !state.dataIPShortCut->lAlphaFieldBlanks(10)) { // outlet node but no inlet node for hot water
238 0 : thisBeam->beamHeatingPresent = false;
239 0 : ShowWarningError(state,
240 0 : format("{}{}: missing {} for {}={}, simulation continues with no beam heating",
241 : routineName,
242 : cCurrentModuleObject,
243 0 : state.dataIPShortCut->cAlphaFieldNames(9),
244 0 : state.dataIPShortCut->cAlphaFieldNames(1),
245 0 : state.dataIPShortCut->cAlphaArgs(1)));
246 8 : } else if (!state.dataIPShortCut->lAlphaFieldBlanks(9) &&
247 4 : state.dataIPShortCut->lAlphaFieldBlanks(10)) { // inlet node but no outlet node for hot water
248 0 : thisBeam->beamHeatingPresent = false;
249 0 : ShowWarningError(state,
250 0 : format("{}{}: missing {} for {}={}, simulation continues with no beam heating",
251 : routineName,
252 : cCurrentModuleObject,
253 0 : state.dataIPShortCut->cAlphaFieldNames(10),
254 0 : state.dataIPShortCut->cAlphaFieldNames(1),
255 0 : state.dataIPShortCut->cAlphaArgs(1)));
256 : } else {
257 4 : thisBeam->beamHeatingPresent = true;
258 4 : thisBeam->hWInNodeNum = GetOnlySingleNode(state,
259 4 : state.dataIPShortCut->cAlphaArgs(9),
260 : ErrorsFound,
261 : DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctConstantVolumeFourPipeBeam,
262 4 : state.dataIPShortCut->cAlphaArgs(1),
263 : DataLoopNode::NodeFluidType::Water,
264 : DataLoopNode::ConnectionType::Inlet,
265 : NodeInputManager::CompFluidStream::Secondary,
266 : ObjectIsParent,
267 4 : state.dataIPShortCut->cAlphaFieldNames(9));
268 4 : thisBeam->hWOutNodeNum = GetOnlySingleNode(state,
269 4 : state.dataIPShortCut->cAlphaArgs(10),
270 : ErrorsFound,
271 : DataLoopNode::ConnectionObjectType::AirTerminalSingleDuctConstantVolumeFourPipeBeam,
272 4 : state.dataIPShortCut->cAlphaArgs(1),
273 : DataLoopNode::NodeFluidType::Water,
274 : DataLoopNode::ConnectionType::Outlet,
275 : NodeInputManager::CompFluidStream::Secondary,
276 : ObjectIsParent,
277 4 : state.dataIPShortCut->cAlphaFieldNames(10));
278 : }
279 4 : thisBeam->vDotDesignPrimAir = state.dataIPShortCut->rNumericArgs(1);
280 4 : if (thisBeam->vDotDesignPrimAir == AutoSize) {
281 4 : thisBeam->vDotDesignPrimAirWasAutosized = true;
282 : }
283 4 : thisBeam->vDotDesignCW = state.dataIPShortCut->rNumericArgs(2);
284 4 : if (thisBeam->vDotDesignCW == AutoSize && thisBeam->beamCoolingPresent) {
285 4 : thisBeam->vDotDesignCWWasAutosized = true;
286 : }
287 4 : thisBeam->vDotDesignHW = state.dataIPShortCut->rNumericArgs(3);
288 4 : if (thisBeam->vDotDesignHW == AutoSize && thisBeam->beamHeatingPresent) {
289 4 : thisBeam->vDotDesignHWWasAutosized = true;
290 : }
291 4 : thisBeam->totBeamLength = state.dataIPShortCut->rNumericArgs(4);
292 4 : if (thisBeam->totBeamLength == AutoSize) {
293 4 : thisBeam->totBeamLengthWasAutosized = true;
294 : }
295 4 : thisBeam->vDotNormRatedPrimAir = state.dataIPShortCut->rNumericArgs(5);
296 4 : thisBeam->qDotNormRatedCooling = state.dataIPShortCut->rNumericArgs(6);
297 4 : thisBeam->deltaTempRatedCooling = state.dataIPShortCut->rNumericArgs(7);
298 4 : thisBeam->vDotNormRatedCW = state.dataIPShortCut->rNumericArgs(8);
299 :
300 4 : thisBeam->modCoolingQdotDeltaTFuncNum = GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(11));
301 4 : if (thisBeam->modCoolingQdotDeltaTFuncNum == 0 && thisBeam->beamCoolingPresent) {
302 0 : ShowSevereError(state, format("{}{}=\"{}\"", routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
303 0 : ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(11), state.dataIPShortCut->cAlphaArgs(11)));
304 0 : ErrorsFound = true;
305 : }
306 4 : thisBeam->modCoolingQdotAirFlowFuncNum = GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(12));
307 4 : if (thisBeam->modCoolingQdotAirFlowFuncNum == 0 && thisBeam->beamCoolingPresent) {
308 0 : ShowSevereError(state, format("{}{}=\"{}\"", routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
309 0 : ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(12), state.dataIPShortCut->cAlphaArgs(12)));
310 0 : ErrorsFound = true;
311 : }
312 4 : thisBeam->modCoolingQdotCWFlowFuncNum = GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(13));
313 4 : if (thisBeam->modCoolingQdotCWFlowFuncNum == 0 && thisBeam->beamCoolingPresent) {
314 0 : ShowSevereError(state, format("{}{}=\"{}\"", routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
315 0 : ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(13), state.dataIPShortCut->cAlphaArgs(13)));
316 0 : ErrorsFound = true;
317 : }
318 4 : thisBeam->qDotNormRatedHeating = state.dataIPShortCut->rNumericArgs(9);
319 4 : thisBeam->deltaTempRatedHeating = state.dataIPShortCut->rNumericArgs(10);
320 4 : thisBeam->vDotNormRatedHW = state.dataIPShortCut->rNumericArgs(11);
321 4 : thisBeam->modHeatingQdotDeltaTFuncNum = GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(14));
322 4 : if (thisBeam->modHeatingQdotDeltaTFuncNum == 0 && thisBeam->beamHeatingPresent) {
323 0 : ShowSevereError(state, format("{}{}=\"{}\"", routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
324 0 : ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(14), state.dataIPShortCut->cAlphaArgs(14)));
325 0 : ErrorsFound = true;
326 : }
327 4 : thisBeam->modHeatingQdotAirFlowFuncNum = GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(15));
328 4 : if (thisBeam->modHeatingQdotAirFlowFuncNum == 0 && thisBeam->beamHeatingPresent) {
329 0 : ShowSevereError(state, format("{}{}=\"{}\"", routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
330 0 : ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(15), state.dataIPShortCut->cAlphaArgs(15)));
331 0 : ErrorsFound = true;
332 : }
333 4 : thisBeam->modHeatingQdotHWFlowFuncNum = GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(16));
334 4 : if (thisBeam->modHeatingQdotHWFlowFuncNum == 0 && thisBeam->beamHeatingPresent) {
335 0 : ShowSevereError(state, format("{}{}=\"{}\"", routineName, cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
336 0 : ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(16), state.dataIPShortCut->cAlphaArgs(16)));
337 0 : ErrorsFound = true;
338 : }
339 : // Register component set data
340 8 : TestCompSet(state,
341 : cCurrentModuleObject,
342 4 : thisBeam->name,
343 4 : state.dataLoopNodes->NodeID(thisBeam->airInNodeNum),
344 4 : state.dataLoopNodes->NodeID(thisBeam->airOutNodeNum),
345 : "Air Nodes");
346 4 : if (thisBeam->beamCoolingPresent) {
347 12 : TestCompSet(state,
348 : cCurrentModuleObject,
349 4 : thisBeam->name,
350 4 : state.dataLoopNodes->NodeID(thisBeam->cWInNodeNum),
351 4 : state.dataLoopNodes->NodeID(thisBeam->cWOutNodeNum),
352 : "Chilled Water Nodes");
353 : }
354 4 : if (thisBeam->beamHeatingPresent) {
355 12 : TestCompSet(state,
356 : cCurrentModuleObject,
357 4 : thisBeam->name,
358 4 : state.dataLoopNodes->NodeID(thisBeam->hWInNodeNum),
359 4 : state.dataLoopNodes->NodeID(thisBeam->hWOutNodeNum),
360 : "Hot Water Nodes");
361 : }
362 :
363 : // Setup the Cooled Beam reporting variables
364 4 : if (thisBeam->beamCoolingPresent) {
365 8 : SetupOutputVariable(state,
366 : "Zone Air Terminal Beam Sensible Cooling Energy",
367 : Constant::Units::J,
368 4 : thisBeam->beamCoolingEnergy,
369 : OutputProcessor::TimeStepType::System,
370 : OutputProcessor::StoreType::Sum,
371 4 : thisBeam->name,
372 : Constant::eResource::EnergyTransfer,
373 : OutputProcessor::Group::HVAC,
374 : OutputProcessor::EndUseCat::CoolingCoils);
375 8 : SetupOutputVariable(state,
376 : "Zone Air Terminal Beam Sensible Cooling Rate",
377 : Constant::Units::W,
378 4 : thisBeam->beamCoolingRate,
379 : OutputProcessor::TimeStepType::System,
380 : OutputProcessor::StoreType::Average,
381 4 : thisBeam->name);
382 : }
383 4 : if (thisBeam->beamHeatingPresent) {
384 8 : SetupOutputVariable(state,
385 : "Zone Air Terminal Beam Sensible Heating Energy",
386 : Constant::Units::J,
387 4 : thisBeam->beamHeatingEnergy,
388 : OutputProcessor::TimeStepType::System,
389 : OutputProcessor::StoreType::Sum,
390 4 : thisBeam->name,
391 : Constant::eResource::EnergyTransfer,
392 : OutputProcessor::Group::HVAC,
393 : OutputProcessor::EndUseCat::HeatingCoils);
394 8 : SetupOutputVariable(state,
395 : "Zone Air Terminal Beam Sensible Heating Rate",
396 : Constant::Units::W,
397 4 : thisBeam->beamHeatingRate,
398 : OutputProcessor::TimeStepType::System,
399 : OutputProcessor::StoreType::Average,
400 4 : thisBeam->name);
401 : }
402 8 : SetupOutputVariable(state,
403 : "Zone Air Terminal Primary Air Sensible Cooling Energy",
404 : Constant::Units::J,
405 4 : thisBeam->supAirCoolingEnergy,
406 : OutputProcessor::TimeStepType::System,
407 : OutputProcessor::StoreType::Sum,
408 4 : thisBeam->name);
409 8 : SetupOutputVariable(state,
410 : "Zone Air Terminal Primary Air Sensible Cooling Rate",
411 : Constant::Units::W,
412 4 : thisBeam->supAirCoolingRate,
413 : OutputProcessor::TimeStepType::System,
414 : OutputProcessor::StoreType::Average,
415 4 : thisBeam->name);
416 8 : SetupOutputVariable(state,
417 : "Zone Air Terminal Primary Air Sensible Heating Energy",
418 : Constant::Units::J,
419 4 : thisBeam->supAirHeatingEnergy,
420 : OutputProcessor::TimeStepType::System,
421 : OutputProcessor::StoreType::Sum,
422 4 : thisBeam->name);
423 8 : SetupOutputVariable(state,
424 : "Zone Air Terminal Primary Air Sensible Heating Rate",
425 : Constant::Units::W,
426 4 : thisBeam->supAirHeatingRate,
427 : OutputProcessor::TimeStepType::System,
428 : OutputProcessor::StoreType::Average,
429 4 : thisBeam->name);
430 8 : SetupOutputVariable(state,
431 : "Zone Air Terminal Primary Air Flow Rate",
432 : Constant::Units::m3_s,
433 4 : thisBeam->primAirFlow,
434 : OutputProcessor::TimeStepType::System,
435 : OutputProcessor::StoreType::Average,
436 4 : thisBeam->name);
437 :
438 8 : SetupOutputVariable(state,
439 : "Zone Air Terminal Outdoor Air Volume Flow Rate",
440 : Constant::Units::m3_s,
441 4 : thisBeam->OutdoorAirFlowRate,
442 : OutputProcessor::TimeStepType::System,
443 : OutputProcessor::StoreType::Average,
444 4 : thisBeam->name);
445 :
446 4 : airNodeFound = false;
447 8 : for (aDUIndex = 1; aDUIndex <= (int)state.dataDefineEquipment->AirDistUnit.size(); ++aDUIndex) {
448 4 : if (thisBeam->airOutNodeNum == state.dataDefineEquipment->AirDistUnit(aDUIndex).OutletNodeNum) {
449 4 : thisBeam->aDUNum = aDUIndex;
450 4 : state.dataDefineEquipment->AirDistUnit(aDUIndex).InletNodeNum = thisBeam->airInNodeNum;
451 : }
452 : }
453 : // assumes if there isn't one assigned, it's an error
454 4 : if (thisBeam->aDUNum == 0) {
455 0 : ShowSevereError(state,
456 0 : format("{}No matching Air Distribution Unit, for Unit = [{},{}].", routineName, cCurrentModuleObject, thisBeam->name));
457 0 : ShowContinueError(state, format("...should have outlet node={}", state.dataLoopNodes->NodeID(thisBeam->airOutNodeNum)));
458 0 : ErrorsFound = true;
459 : } else {
460 :
461 : // Fill the Zone Equipment data with the supply air inlet node number of this unit.
462 8 : for (int ctrlZone = 1; ctrlZone <= state.dataGlobal->NumOfZones; ++ctrlZone) {
463 4 : if (!state.dataZoneEquip->ZoneEquipConfig(ctrlZone).IsControlled) continue;
464 4 : for (int supAirIn = 1; supAirIn <= state.dataZoneEquip->ZoneEquipConfig(ctrlZone).NumInletNodes; ++supAirIn) {
465 4 : if (thisBeam->airOutNodeNum == state.dataZoneEquip->ZoneEquipConfig(ctrlZone).InletNode(supAirIn)) {
466 4 : thisBeam->zoneIndex = ctrlZone;
467 4 : thisBeam->zoneNodeIndex = state.dataZoneEquip->ZoneEquipConfig(ctrlZone).ZoneNode;
468 4 : thisBeam->ctrlZoneInNodeIndex = supAirIn;
469 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitCool(supAirIn).InNode = thisBeam->airInNodeNum;
470 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitCool(supAirIn).OutNode = thisBeam->airOutNodeNum;
471 4 : state.dataDefineEquipment->AirDistUnit(thisBeam->aDUNum).TermUnitSizingNum =
472 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitCool(supAirIn).TermUnitSizingIndex;
473 4 : thisBeam->termUnitSizingNum = state.dataDefineEquipment->AirDistUnit(thisBeam->aDUNum).TermUnitSizingNum;
474 4 : state.dataDefineEquipment->AirDistUnit(thisBeam->aDUNum).ZoneEqNum = ctrlZone;
475 4 : if (thisBeam->beamHeatingPresent) {
476 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitHeat(supAirIn).InNode = thisBeam->airInNodeNum;
477 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitHeat(supAirIn).OutNode = thisBeam->airOutNodeNum;
478 : }
479 4 : airNodeFound = true;
480 4 : break;
481 : }
482 : }
483 : }
484 : }
485 4 : if (!airNodeFound) {
486 0 : ShowSevereError(state, format("The outlet air node from the {} = {}", cCurrentModuleObject, thisBeam->name));
487 0 : ShowContinueError(state, format("did not have a matching Zone Equipment Inlet Node, Node ={}", state.dataIPShortCut->cAlphaArgs(5)));
488 0 : ErrorsFound = true;
489 : }
490 :
491 4 : if (found && !ErrorsFound) {
492 4 : state.dataFourPipeBeam->FourPipeBeams.push_back(thisBeam);
493 4 : return thisBeam;
494 : } else {
495 0 : ShowFatalError(state, format("{}Errors found in getting input. Preceding conditions cause termination.", routineName));
496 0 : return nullptr;
497 : }
498 4 : }
499 :
500 0 : int HVACFourPipeBeam::getAirLoopNum()
501 : {
502 0 : return airLoopNum;
503 : }
504 :
505 0 : int HVACFourPipeBeam::getZoneIndex()
506 : {
507 0 : return zoneIndex;
508 : }
509 :
510 1 : Real64 HVACFourPipeBeam::getPrimAirDesignVolFlow()
511 : {
512 1 : return vDotDesignPrimAir;
513 : }
514 :
515 0 : int HVACFourPipeBeam::getTermUnitSizingIndex()
516 : {
517 0 : return termUnitSizingNum;
518 : }
519 :
520 37 : void HVACFourPipeBeam::simulate(EnergyPlusData &state,
521 : bool const FirstHVACIteration, // TRUE if first HVAC iteration in time step
522 : Real64 &NonAirSysOutput // convective cooling by the beam system [W]
523 : )
524 : {
525 :
526 : // initialize the unit
527 37 : this->init(state, FirstHVACIteration);
528 :
529 : // control and simulate the beam
530 36 : if (!this->mySizeFlag) {
531 33 : this->control(state, FirstHVACIteration, NonAirSysOutput);
532 :
533 : // Update the current unit's outlet nodes.
534 33 : this->update(state);
535 :
536 : // Fill the report variables.
537 33 : this->report(state);
538 : }
539 36 : }
540 :
541 37 : void HVACFourPipeBeam::init(EnergyPlusData &state,
542 : bool const FirstHVACIteration // TRUE if first air loop solution this HVAC step
543 : )
544 : {
545 :
546 : // Using
547 : using DataZoneEquipment::CheckZoneEquipmentList;
548 : using PlantUtilities::InitComponentNodes;
549 : using PlantUtilities::ScanPlantLoopsForObject;
550 : using PlantUtilities::SetComponentFlowRate;
551 :
552 : static constexpr std::string_view routineName("HVACFourPipeBeam::init");
553 :
554 37 : if (this->plantLoopScanFlag && allocated(state.dataPlnt->PlantLoop)) {
555 3 : bool errFlag = false;
556 3 : if (this->beamCoolingPresent) {
557 9 : ScanPlantLoopsForObject(state,
558 : this->name,
559 : DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal,
560 3 : this->cWplantLoc,
561 : errFlag,
562 : _,
563 : _,
564 : _,
565 3 : this->cWInNodeNum,
566 : _);
567 3 : if (errFlag) {
568 0 : ShowFatalError(state, format("{} Program terminated for previous conditions.", routineName));
569 : }
570 : }
571 3 : if (this->beamHeatingPresent) {
572 9 : ScanPlantLoopsForObject(state,
573 : this->name,
574 : DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal,
575 3 : this->hWplantLoc,
576 : errFlag,
577 : _,
578 : _,
579 : _,
580 3 : this->hWInNodeNum,
581 : _);
582 3 : if (errFlag) {
583 0 : ShowFatalError(state, format("{} Program terminated for previous conditions.", routineName));
584 : }
585 : }
586 3 : this->plantLoopScanFlag = false;
587 : }
588 :
589 37 : if (!this->zoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
590 : // Check to see if there is a Air Distribution Unit on the Zone Equipment List
591 3 : if (this->aDUNum != 0) {
592 3 : if (!CheckZoneEquipmentList(state, "ZONEHVAC:AIRDISTRIBUTIONUNIT", state.dataDefineEquipment->AirDistUnit(this->aDUNum).Name)) {
593 0 : ShowSevereError(state,
594 0 : format("{}: ADU=[Air Distribution Unit,{}] is not on any ZoneHVAC:EquipmentList.",
595 : routineName,
596 0 : state.dataDefineEquipment->AirDistUnit(this->aDUNum).Name));
597 0 : ShowContinueError(state, format("...Unit=[{},{}] will not be simulated.", this->unitType, this->name));
598 : }
599 3 : this->zoneEquipmentListChecked = true;
600 : }
601 : }
602 :
603 37 : if (!state.dataGlobal->SysSizingCalc && this->mySizeFlag && !this->plantLoopScanFlag) {
604 : // if ( SysSizingCalc && this->mySizeFlag && ! this->plantLoopScanFlag ) {
605 3 : this->airLoopNum = state.dataZoneEquip->ZoneEquipConfig(this->zoneIndex).InletNodeAirLoopNum(this->ctrlZoneInNodeIndex);
606 3 : state.dataDefineEquipment->AirDistUnit(this->aDUNum).AirLoopNum = this->airLoopNum;
607 3 : this->set_size(state); // calculate autosize values (in any) and convert volume flow rates to mass flow rates
608 2 : if (this->beamCoolingPresent) { // initialize chilled water design mass flow rate in plant routines
609 2 : InitComponentNodes(state, 0.0, this->mDotDesignCW, this->cWInNodeNum, this->cWOutNodeNum);
610 : }
611 2 : if (this->beamHeatingPresent) { // initialize hot water design mass flow rate in plant routines
612 2 : InitComponentNodes(state, 0.0, this->mDotDesignHW, this->hWInNodeNum, this->hWOutNodeNum);
613 : }
614 2 : this->mySizeFlag = false;
615 : }
616 :
617 : // Do the Begin Environment initializations
618 36 : if (state.dataGlobal->BeginEnvrnFlag && this->myEnvrnFlag) {
619 :
620 4 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMax = this->mDotDesignPrimAir;
621 4 : state.dataLoopNodes->Node(this->airOutNodeNum).MassFlowRateMax = this->mDotDesignPrimAir;
622 4 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMin = 0.0;
623 4 : state.dataLoopNodes->Node(this->airOutNodeNum).MassFlowRateMin = 0.0;
624 :
625 4 : if (this->beamCoolingPresent) { // initialize chilled water design mass flow rate in plant routines
626 4 : InitComponentNodes(state, 0.0, this->mDotDesignCW, this->cWInNodeNum, this->cWOutNodeNum);
627 : }
628 4 : if (this->beamHeatingPresent) { // initialize hot water design mass flow rate in plant routines
629 4 : InitComponentNodes(state, 0.0, this->mDotDesignHW, this->hWInNodeNum, this->hWOutNodeNum);
630 : }
631 :
632 4 : if (this->airLoopNum == 0) { // fill air loop index
633 0 : if (this->zoneIndex > 0 && this->ctrlZoneInNodeIndex > 0) {
634 0 : this->airLoopNum = state.dataZoneEquip->ZoneEquipConfig(this->zoneIndex).InletNodeAirLoopNum(this->ctrlZoneInNodeIndex);
635 : }
636 : }
637 :
638 4 : this->myEnvrnFlag = false;
639 : } // end one time inits
640 :
641 36 : if (!state.dataGlobal->BeginEnvrnFlag) {
642 24 : this->myEnvrnFlag = true;
643 : }
644 :
645 : // Do the start of HVAC time step initializations
646 36 : if (FirstHVACIteration) {
647 : // check availability schedules and set flags
648 24 : this->airAvailable = (this->airAvailSched->getCurrentVal() > 0.0);
649 24 : this->coolingAvailable = (this->airAvailable && beamCoolingPresent && (this->coolingAvailSched->getCurrentVal() > 0.0));
650 24 : this->heatingAvailable = (this->airAvailable && beamHeatingPresent && (this->heatingAvailSched->getCurrentVal() > 0.0));
651 :
652 : // check for upstream zero flow. If nonzero and air available, set primary flow to max
653 24 : if (this->airAvailable && state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate > 0.0) {
654 19 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate = this->mDotDesignPrimAir;
655 : } else {
656 5 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate = 0.0;
657 : }
658 : // reset the max and min avail flows
659 24 : if (this->airAvailable && state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail > 0.0) {
660 19 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail = this->mDotDesignPrimAir;
661 19 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMinAvail = this->mDotDesignPrimAir;
662 : } else {
663 5 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail = 0.0;
664 5 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMinAvail = 0.0;
665 : }
666 : }
667 :
668 : // do these initializations every time step
669 36 : if (beamCoolingPresent) {
670 36 : this->cWTempIn = state.dataLoopNodes->Node(this->cWInNodeNum).Temp;
671 36 : this->cWTempOut = this->cWTempIn;
672 : }
673 36 : if (beamHeatingPresent) {
674 36 : this->hWTempIn = state.dataLoopNodes->Node(this->hWInNodeNum).Temp;
675 36 : this->hWTempOut = this->hWTempIn;
676 : }
677 36 : this->mDotSystemAir = state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail;
678 36 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate = this->mDotSystemAir;
679 36 : this->tDBZoneAirTemp = state.dataLoopNodes->Node(this->zoneNodeIndex).Temp;
680 36 : this->tDBSystemAir = state.dataLoopNodes->Node(this->airInNodeNum).Temp;
681 36 : this->cpZoneAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(this->zoneNodeIndex).HumRat);
682 36 : this->cpSystemAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(this->airInNodeNum).HumRat);
683 36 : this->qDotBeamCooling = 0.0;
684 36 : this->qDotBeamHeating = 0.0;
685 36 : this->supAirCoolingRate = 0.0;
686 36 : this->supAirHeatingRate = 0.0;
687 36 : this->beamCoolingRate = 0.0;
688 36 : this->beamHeatingRate = 0.0;
689 36 : this->primAirFlow = 0.0;
690 :
691 36 : } // init
692 :
693 3 : void HVACFourPipeBeam::set_size(EnergyPlusData &state)
694 : {
695 :
696 : // Using
697 : using namespace DataSizing;
698 : using PlantUtilities::MyPlantSizingIndex;
699 : using PlantUtilities::RegisterPlantCompDesignFlow;
700 : using Psychrometrics::PsyCpAirFnW;
701 : using namespace std::placeholders;
702 :
703 : static constexpr std::string_view routineName("HVACFourPipeBeam::set_size ");
704 :
705 3 : bool ErrorsFound = false;
706 : Real64 rho; // local fluid density
707 : bool noHardSizeAnchorAvailable; // aid for complex logic surrounding mix of hard size and autosizes
708 3 : Real64 cpAir = 0.0;
709 3 : Real64 ErrTolerance = 0.001;
710 :
711 3 : Real64 mDotAirSolutionHeating = 0.0;
712 3 : Real64 mDotAirSolutionCooling = 0.0;
713 3 : Real64 originalTermUnitSizeMaxVDot = 0.0;
714 3 : Real64 originalTermUnitSizeCoolVDot = 0.0;
715 3 : Real64 originalTermUnitSizeHeatVDot = 0.0;
716 :
717 : // convert rated primary flow rate to mass flow rate using standard pressure and dry air at 20.0
718 3 : this->mDotNormRatedPrimAir = this->vDotNormRatedPrimAir * state.dataEnvrn->rhoAirSTP;
719 :
720 3 : noHardSizeAnchorAvailable = false;
721 :
722 3 : if (state.dataSize->CurTermUnitSizingNum > 0) {
723 3 : originalTermUnitSizeMaxVDot = std::max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow,
724 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow);
725 3 : originalTermUnitSizeCoolVDot = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow;
726 3 : originalTermUnitSizeHeatVDot = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow;
727 : }
728 :
729 3 : if (this->totBeamLengthWasAutosized && this->vDotDesignPrimAirWasAutosized && this->vDotDesignCWWasAutosized &&
730 3 : this->vDotDesignHWWasAutosized) {
731 3 : noHardSizeAnchorAvailable = true;
732 0 : } else if (this->totBeamLengthWasAutosized && this->vDotDesignPrimAirWasAutosized && this->vDotDesignCWWasAutosized && !beamHeatingPresent) {
733 0 : noHardSizeAnchorAvailable = true;
734 0 : } else if (this->totBeamLengthWasAutosized && this->vDotDesignPrimAirWasAutosized && !this->beamCoolingPresent &&
735 0 : this->vDotDesignHWWasAutosized) {
736 0 : noHardSizeAnchorAvailable = true;
737 0 : } else if (!this->totBeamLengthWasAutosized) { // the simplest case is where length is not autosized
738 : // use the normalized rated values (likely defaulted ) with length to calculate any that are autosized
739 0 : if (this->vDotDesignPrimAirWasAutosized) {
740 0 : this->vDotDesignPrimAir = this->vDotNormRatedPrimAir * this->totBeamLength;
741 : }
742 0 : if (this->vDotDesignCWWasAutosized) {
743 0 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
744 : }
745 0 : if (vDotDesignHWWasAutosized) {
746 0 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
747 : }
748 : } else { // need to find beam length
749 : // the next simplest case is if the supply air rate is given
750 0 : if (!this->vDotDesignPrimAirWasAutosized) { //
751 : // find length from air flow rate and then proceed
752 0 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
753 0 : if (this->vDotDesignCWWasAutosized) {
754 0 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
755 : }
756 0 : if (vDotDesignHWWasAutosized) {
757 0 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
758 : }
759 : } else { // both air and length are autosized
760 0 : if (this->beamCoolingPresent && !this->vDotDesignCWWasAutosized) { // we have a chilled water flow rate to use
761 0 : this->totBeamLength = this->vDotDesignCW / this->vDotNormRatedCW;
762 0 : this->vDotDesignPrimAir = this->vDotNormRatedPrimAir * this->totBeamLength;
763 0 : if (vDotDesignHWWasAutosized) {
764 0 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
765 : }
766 0 : } else if (this->beamHeatingPresent && !this->vDotDesignHWWasAutosized) { // we have a hot water flow rate to use
767 0 : this->totBeamLength = this->vDotDesignHW / this->vDotNormRatedHW;
768 0 : this->vDotDesignPrimAir = this->vDotNormRatedPrimAir * this->totBeamLength;
769 0 : if (this->vDotDesignCWWasAutosized) { // don't think it can come here but...
770 0 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
771 : }
772 : } else {
773 : // should not come here, developer exception
774 : }
775 : } // no air flow rate
776 : } // no beam length
777 :
778 6 : if (noHardSizeAnchorAvailable && (state.dataSize->CurZoneEqNum > 0) &&
779 3 : (state.dataSize->CurTermUnitSizingNum > 0)) { // need to use central sizing results to calculate
780 :
781 : // set up for solver
782 :
783 3 : CheckZoneSizing(state, this->unitType, this->name);
784 : // minimum flow rate is from air flow rate on the terminal unit final zone size ( typically ventilation minimum and may be too low)
785 3 : Real64 minFlow(0.0);
786 3 : Real64 maxFlowCool(0.0);
787 3 : minFlow = std::min(state.dataEnvrn->StdRhoAir * originalTermUnitSizeMaxVDot,
788 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).MinOA * state.dataEnvrn->StdRhoAir);
789 3 : minFlow = std::max(0.0, minFlow);
790 : // max flow is as if the air supply was sufficient to provide all the conditioning
791 :
792 3 : if (beamCoolingPresent) {
793 3 : cpAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInHumRatTU);
794 :
795 3 : if ((state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtCoolPeak -
796 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInTempTU) >
797 : 2.0) { // avoid div by zero and blow up
798 3 : maxFlowCool = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad /
799 3 : (cpAir * (state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtCoolPeak -
800 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInTempTU));
801 : } else {
802 0 : maxFlowCool = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad / (cpAir * 2.0);
803 : }
804 3 : if (minFlow * 3.0 >= maxFlowCool) {
805 2 : minFlow = maxFlowCool / 3.0; // make sure min is significantly lower than max.
806 : }
807 :
808 3 : int pltSizCoolNum = MyPlantSizingIndex(state, "four pipe beam unit", this->name, this->cWInNodeNum, this->cWOutNodeNum, ErrorsFound);
809 3 : if (pltSizCoolNum == 0) {
810 0 : ShowSevereError(state, "Autosizing of water flow requires a cooling loop Sizing:Plant object");
811 0 : ShowContinueError(state, format("Occurs in {} Object={}", this->unitType, this->name));
812 0 : ErrorsFound = true;
813 : } else {
814 3 : this->cWTempIn = state.dataSize->PlantSizData(pltSizCoolNum).ExitTemp;
815 : }
816 3 : this->mDotHW = 0.0;
817 3 : this->tDBZoneAirTemp = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtCoolPeak;
818 3 : this->tDBSystemAir = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInTempTU;
819 3 : this->cpZoneAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneHumRatAtCoolPeak);
820 3 : this->cpSystemAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInHumRatTU);
821 3 : this->qDotZoneReq = -1.0 * state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad;
822 3 : this->qDotZoneToCoolSetPt = -1.0 * state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad;
823 3 : this->airAvailable = true;
824 3 : this->coolingAvailable = true;
825 3 : this->heatingAvailable = false;
826 9 : auto f = [&state, this](Real64 const airFlow) {
827 : static constexpr std::string_view routineName("Real64 HVACFourPipeBeam::residualSizing ");
828 9 : this->mDotSystemAir = airFlow;
829 9 : this->vDotDesignPrimAir = this->mDotSystemAir / state.dataEnvrn->StdRhoAir;
830 9 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
831 9 : if (this->vDotDesignCWWasAutosized) {
832 9 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
833 : Real64 const rho =
834 9 : state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, routineName);
835 9 : this->mDotNormRatedCW = this->vDotNormRatedCW * rho;
836 9 : this->mDotCW = this->vDotDesignCW * rho;
837 9 : if (this->beamCoolingPresent) {
838 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum);
839 : }
840 : }
841 9 : if (vDotDesignHWWasAutosized) {
842 9 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
843 : Real64 const rho =
844 9 : state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, routineName);
845 9 : this->mDotNormRatedHW = this->vDotNormRatedHW * rho;
846 9 : this->mDotHW = this->vDotDesignHW * rho;
847 9 : if (this->beamHeatingPresent) {
848 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum);
849 : }
850 : }
851 9 : this->calc(state);
852 9 : if (this->qDotZoneReq != 0.0) {
853 9 : return ((this->qDotZoneReq - this->qDotTotalDelivered) / this->qDotZoneReq);
854 : } else {
855 0 : return 1.0;
856 : }
857 3 : };
858 3 : int SolFlag = 0;
859 3 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, mDotAirSolutionCooling, f, minFlow, maxFlowCool);
860 3 : if (SolFlag == -1) {
861 0 : ShowWarningError(state, format("Cooling load sizing search failed in four pipe beam unit called {}", this->name));
862 0 : ShowContinueError(state, " Iteration limit exceeded in calculating size for design cooling load");
863 3 : } else if (SolFlag == -2) {
864 0 : ShowWarningError(state, format("Cooling load sizing search failed in four pipe beam unit called {}", this->name));
865 0 : ShowContinueError(state, " Bad size limits");
866 : }
867 : }
868 :
869 3 : if (beamHeatingPresent) {
870 3 : cpAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInHumRatTU);
871 3 : Real64 maxFlowHeat = 0.0;
872 3 : if ((state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInTempTU -
873 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtHeatPeak) >
874 : 2.0) { // avoid div by zero and blow up
875 0 : maxFlowHeat = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad /
876 0 : (cpAir * (state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInTempTU -
877 0 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtHeatPeak));
878 : } else {
879 3 : maxFlowHeat = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad / (cpAir * 2.0);
880 : }
881 :
882 3 : int pltSizHeatNum = MyPlantSizingIndex(state, "four pipe beam unit", this->name, this->hWInNodeNum, this->hWOutNodeNum, ErrorsFound);
883 3 : if (pltSizHeatNum == 0) {
884 0 : ShowSevereError(state, "Autosizing of water flow requires a heating loop Sizing:Plant object");
885 0 : ShowContinueError(state, format("Occurs in {} Object={}", this->unitType, this->name));
886 0 : ErrorsFound = true;
887 : } else {
888 3 : this->hWTempIn = state.dataSize->PlantSizData(pltSizHeatNum).ExitTemp;
889 : }
890 3 : this->mDotCW = 0.0;
891 3 : this->tDBZoneAirTemp = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtHeatPeak;
892 3 : this->tDBSystemAir = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInTempTU;
893 3 : this->cpZoneAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneHumRatAtHeatPeak);
894 3 : this->cpSystemAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInHumRatTU);
895 3 : this->qDotZoneReq = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad;
896 3 : this->qDotZoneToHeatSetPt = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad;
897 3 : this->airAvailable = true;
898 3 : this->heatingAvailable = true;
899 3 : this->coolingAvailable = false;
900 9 : auto f = [&state, this](Real64 const airFlow) {
901 : static constexpr std::string_view routineName("Real64 HVACFourPipeBeam::residualSizing ");
902 9 : this->mDotSystemAir = airFlow;
903 9 : this->vDotDesignPrimAir = this->mDotSystemAir / state.dataEnvrn->StdRhoAir;
904 9 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
905 9 : if (this->vDotDesignCWWasAutosized) {
906 9 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
907 : Real64 const rho =
908 9 : state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, routineName);
909 9 : this->mDotNormRatedCW = this->vDotNormRatedCW * rho;
910 9 : this->mDotCW = this->vDotDesignCW * rho;
911 9 : if (this->beamCoolingPresent) {
912 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum);
913 : }
914 : }
915 9 : if (vDotDesignHWWasAutosized) {
916 9 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
917 : Real64 const rho =
918 9 : state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, routineName);
919 9 : this->mDotNormRatedHW = this->vDotNormRatedHW * rho;
920 9 : this->mDotHW = this->vDotDesignHW * rho;
921 9 : if (this->beamHeatingPresent) {
922 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum);
923 : }
924 : }
925 9 : this->calc(state);
926 9 : if (this->qDotZoneReq != 0.0) {
927 9 : return ((this->qDotZoneReq - this->qDotTotalDelivered) / this->qDotZoneReq);
928 : } else {
929 0 : return 1.0;
930 : }
931 3 : };
932 3 : int SolFlag = 0;
933 3 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, mDotAirSolutionHeating, f, 0.0, maxFlowHeat);
934 3 : if (SolFlag == -1) {
935 0 : ShowWarningError(state, format("Heating load sizing search failed in four pipe beam unit called {}", this->name));
936 0 : ShowContinueError(state, " Iteration limit exceeded in calculating size for design heating load");
937 3 : } else if (SolFlag == -2) {
938 0 : ShowWarningError(state, format("Heating load sizing search failed in four pipe beam unit called {}", this->name));
939 0 : ShowContinueError(state, " Bad size limits");
940 : }
941 : }
942 :
943 : // take the larger of heating and cooling
944 3 : this->mDotDesignPrimAir = std::max(mDotAirSolutionHeating, mDotAirSolutionCooling);
945 : // make sure this is higher than the zone OA requirement
946 3 : this->mDotDesignPrimAir =
947 6 : std::max(this->mDotDesignPrimAir,
948 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).MinOA * state.dataEnvrn->StdRhoAir);
949 3 : this->vDotDesignPrimAir = this->mDotDesignPrimAir / state.dataEnvrn->StdRhoAir;
950 3 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
951 3 : if (this->vDotDesignCWWasAutosized) {
952 3 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
953 : }
954 3 : if (vDotDesignHWWasAutosized) {
955 3 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
956 : }
957 : }
958 : // fill in mass flow rate versions of working variables (regardless of autosizing )
959 3 : this->mDotDesignPrimAir = this->vDotDesignPrimAir * state.dataEnvrn->StdRhoAir;
960 :
961 3 : if ((originalTermUnitSizeMaxVDot > 0.0) && (originalTermUnitSizeMaxVDot != this->vDotDesignPrimAir) && (state.dataSize->CurZoneEqNum > 0)) {
962 3 : if ((state.dataSize->SysSizingRunDone) && (this->airLoopNum > 0)) {
963 : // perturb system size to handle change in system size calculated without knowing about 4 pipe beam
964 : // Note that this approach is not necessarily appropriate for coincident system design option
965 : // and it might be moved to make such adjustments in SizingManager::ManageSystemSizingAdjustments()
966 2 : state.dataSize->FinalSysSizing(this->airLoopNum).DesMainVolFlow += (this->vDotDesignPrimAir - originalTermUnitSizeMaxVDot);
967 2 : state.dataSize->FinalSysSizing(this->airLoopNum).DesCoolVolFlow += (this->vDotDesignPrimAir - originalTermUnitSizeCoolVDot);
968 2 : state.dataSize->FinalSysSizing(this->airLoopNum).DesHeatVolFlow += (this->vDotDesignPrimAir - originalTermUnitSizeHeatVDot);
969 2 : state.dataSize->FinalSysSizing(this->airLoopNum).MassFlowAtCoolPeak +=
970 2 : (this->vDotDesignPrimAir - originalTermUnitSizeCoolVDot) * state.dataEnvrn->StdRhoAir;
971 :
972 4 : BaseSizer::reportSizerOutput(state,
973 : this->unitType,
974 : this->name,
975 : "AirLoopHVAC Design Supply Air Flow Rate Adjustment [m3/s]",
976 2 : (this->vDotDesignPrimAir - originalTermUnitSizeMaxVDot));
977 : } else {
978 2 : ShowSevereError(state, "Four pipe beam requires system sizing. Turn on system sizing.");
979 3 : ShowFatalError(state, "Program terminating due to previous errors");
980 : }
981 : }
982 :
983 2 : if (this->beamCoolingPresent) {
984 2 : rho = state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, routineName);
985 2 : this->mDotNormRatedCW = this->vDotNormRatedCW * rho;
986 2 : this->mDotDesignCW = this->vDotDesignCW * rho;
987 2 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotDesignCW, this->cWInNodeNum, this->cWOutNodeNum);
988 : }
989 2 : if (this->beamHeatingPresent) {
990 2 : rho = state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, routineName);
991 2 : this->mDotNormRatedHW = this->vDotNormRatedHW * rho;
992 2 : this->mDotDesignHW = this->vDotDesignHW * rho;
993 2 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotDesignHW, this->hWInNodeNum, this->hWOutNodeNum);
994 : }
995 :
996 : // report final sizes if autosized
997 2 : if (this->vDotDesignPrimAirWasAutosized) {
998 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Supply Air Flow Rate [m3/s]", this->vDotDesignPrimAir);
999 : }
1000 2 : if (this->vDotDesignCWWasAutosized) {
1001 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Maximum Total Chilled Water Flow Rate [m3/s]", this->vDotDesignCW);
1002 : }
1003 2 : if (this->vDotDesignHWWasAutosized) {
1004 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Maximum Total Hot Water Flow Rate [m3/s]", this->vDotDesignHW);
1005 : }
1006 2 : if (this->totBeamLengthWasAutosized) {
1007 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Zone Total Beam Length [m]", this->totBeamLength);
1008 : }
1009 : // save the design water volume flow rate for use by the water loop sizing algorithms
1010 2 : if (this->vDotDesignCW > 0.0 && this->beamCoolingPresent) {
1011 2 : RegisterPlantCompDesignFlow(state, this->cWInNodeNum, this->vDotDesignCW);
1012 : }
1013 2 : if (this->vDotDesignHW > 0.0 && this->beamHeatingPresent) {
1014 2 : RegisterPlantCompDesignFlow(state, this->hWInNodeNum, this->vDotDesignHW);
1015 : }
1016 2 : if (ErrorsFound) {
1017 0 : ShowFatalError(state, "Preceding four pipe beam sizing errors cause program termination");
1018 : }
1019 :
1020 2 : } // set_size
1021 :
1022 33 : void HVACFourPipeBeam::control(EnergyPlusData &state,
1023 : [[maybe_unused]] bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
1024 : Real64 &NonAirSysOutput // convective cooling by the beam system [W]
1025 : )
1026 : {
1027 :
1028 : // Using/Aliasing
1029 : using namespace DataZoneEnergyDemands;
1030 : using PlantUtilities::SetComponentFlowRate;
1031 : using namespace std::placeholders;
1032 :
1033 : int SolFlag;
1034 : Real64 ErrTolerance;
1035 :
1036 33 : NonAirSysOutput = 0.0; // initialize
1037 :
1038 33 : if (this->mDotSystemAir < HVAC::VerySmallMassFlow ||
1039 31 : (!this->airAvailable && !this->coolingAvailable && !this->heatingAvailable)) { // unit is off
1040 2 : this->mDotHW = 0.0;
1041 2 : if (this->beamHeatingPresent) {
1042 2 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1043 : }
1044 2 : this->hWTempOut = this->hWTempIn;
1045 : // assume if there is still flow that unit has an internal bypass and convector does not still heat
1046 2 : this->mDotCW = 0.0;
1047 2 : this->cWTempOut = this->cWTempIn;
1048 2 : if (this->beamCoolingPresent) {
1049 2 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1050 : }
1051 : // assume if there is still flow that unit has an internal bypass and convector does not still cool
1052 : // don't even need to run calc
1053 2 : return;
1054 : }
1055 :
1056 31 : if (this->airAvailable && this->mDotSystemAir > HVAC::VerySmallMassFlow && !this->coolingAvailable && !this->heatingAvailable) {
1057 0 : this->mDotHW = 0.0;
1058 0 : if (this->beamHeatingPresent) {
1059 0 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1060 : }
1061 : // assume if there is still flow that unit has an internal bypass and convector does not still heat
1062 0 : this->hWTempOut = this->hWTempIn;
1063 0 : this->mDotCW = 0.0;
1064 0 : if (this->beamCoolingPresent) {
1065 0 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1066 : }
1067 : // assume if there is still flow that unit has an internal bypass and convector does not still cool
1068 0 : this->cWTempOut = this->cWTempIn;
1069 0 : this->calc(state);
1070 :
1071 0 : return;
1072 : }
1073 :
1074 : // get zone loads
1075 31 : this->qDotZoneReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->zoneIndex).RemainingOutputRequired;
1076 31 : this->qDotZoneToHeatSetPt = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->zoneIndex).RemainingOutputReqToHeatSP;
1077 31 : this->qDotZoneToCoolSetPt = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->zoneIndex).RemainingOutputReqToCoolSP;
1078 :
1079 : // decide if beam is in heating or cooling
1080 :
1081 31 : this->qDotSystemAir = this->mDotSystemAir * ((this->cpSystemAir * this->tDBSystemAir) - (this->cpZoneAir * this->tDBZoneAirTemp));
1082 :
1083 31 : this->qDotBeamReq = this->qDotZoneReq - this->qDotSystemAir;
1084 :
1085 31 : if (this->qDotBeamReq < -HVAC::SmallLoad && this->coolingAvailable) { // beam cooling needed
1086 : // first calc with max chilled water flow
1087 10 : this->mDotHW = 0.0;
1088 10 : if (this->beamHeatingPresent) {
1089 10 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1090 : }
1091 10 : this->hWTempOut = this->hWTempIn;
1092 10 : this->mDotCW = this->mDotDesignCW;
1093 10 : this->calc(state);
1094 10 : if (this->qDotBeamCooling < (qDotBeamReq - HVAC::SmallLoad)) {
1095 : // can overcool, modulate chilled water flow rate to meet load
1096 8 : this->qDotBeamCoolingMax = this->qDotBeamCooling;
1097 8 : ErrTolerance = 0.01;
1098 28 : auto f = [&state, this](Real64 const cWFlow) {
1099 28 : this->mDotHW = 0.0;
1100 28 : this->mDotCW = cWFlow;
1101 28 : this->calc(state);
1102 28 : if (this->qDotBeamCoolingMax != 0.0) {
1103 28 : return (((this->qDotZoneToCoolSetPt - this->qDotSystemAir) - this->qDotBeamCooling) / this->qDotBeamCoolingMax);
1104 : } else {
1105 0 : return 1.0;
1106 : }
1107 8 : };
1108 8 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, this->mDotCW, f, 0.0, this->mDotDesignCW);
1109 8 : if (SolFlag == -1) {
1110 : // ShowWarningError( "Cold water control failed in four pipe beam unit called " + this->name );
1111 : // ShowContinueError(state, " Iteration limit exceeded in calculating cold water mass flow rate" );
1112 8 : } else if (SolFlag == -2) {
1113 : // ShowWarningError( "Cold water control failed in four pipe beam unit called " + this->name );
1114 : // ShowContinueError(state, " Bad cold water flow limits" );
1115 : }
1116 8 : this->calc(state);
1117 8 : NonAirSysOutput = this->qDotBeamCooling;
1118 8 : return;
1119 : } else { // can run flat out without overcooling, which we just did
1120 2 : NonAirSysOutput = this->qDotBeamCooling;
1121 2 : return;
1122 : }
1123 :
1124 21 : } else if (qDotBeamReq > HVAC::SmallLoad && this->heatingAvailable) { // beam heating needed
1125 : // first calc with max hot water flow
1126 19 : this->mDotCW = 0.0;
1127 19 : if (this->beamCoolingPresent) {
1128 19 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1129 : }
1130 19 : this->cWTempOut = this->cWTempIn;
1131 19 : this->mDotHW = this->mDotDesignHW;
1132 19 : this->calc(state);
1133 19 : if (this->qDotBeamHeating > (qDotBeamReq + HVAC::SmallLoad)) {
1134 15 : this->qDotBeamHeatingMax = this->qDotBeamHeating;
1135 : // can overheat, modulate hot water flow to meet load
1136 15 : ErrTolerance = 0.01;
1137 54 : auto f = [&state, this](Real64 const hWFlow) {
1138 54 : this->mDotHW = hWFlow;
1139 54 : this->mDotCW = 0.0;
1140 54 : this->calc(state);
1141 54 : if (this->qDotBeamHeatingMax != 0.0) {
1142 54 : return (((this->qDotZoneToHeatSetPt - this->qDotSystemAir) - this->qDotBeamHeating) / this->qDotBeamHeatingMax);
1143 : } else {
1144 0 : return 1.0;
1145 : }
1146 15 : };
1147 15 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, this->mDotHW, f, 0.0, this->mDotDesignHW);
1148 15 : if (SolFlag == -1) {
1149 : // ShowWarningError( "Hot water control failed in four pipe beam unit called " + this->name );
1150 : // ShowContinueError(state, " Iteration limit exceeded in calculating hot water mass flow rate" );
1151 15 : } else if (SolFlag == -2) {
1152 : // ShowWarningError( "Hot water control failed in four pipe beam called " + this->name );
1153 : // ShowContinueError(state, " Bad hot water flow limits" );
1154 : }
1155 15 : this->calc(state);
1156 15 : NonAirSysOutput = this->qDotBeamHeating;
1157 15 : return;
1158 :
1159 : } else { // can run flat out without overheating, which we just did
1160 4 : NonAirSysOutput = this->qDotBeamHeating;
1161 4 : return;
1162 : }
1163 :
1164 : } else {
1165 2 : this->mDotHW = 0.0;
1166 2 : if (this->beamHeatingPresent) {
1167 2 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1168 : }
1169 2 : this->hWTempOut = this->hWTempIn;
1170 : // assume if there is still flow that unit has an internal bypass and convector does not still heat
1171 2 : this->mDotCW = 0.0;
1172 2 : this->cWTempOut = this->cWTempIn;
1173 2 : if (this->beamCoolingPresent) {
1174 2 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1175 : }
1176 : // assume if there is still flow that unit has an internal bypass and convector does not still cool
1177 : // don't even need to run calc
1178 2 : return;
1179 : }
1180 : }
1181 :
1182 152 : void HVACFourPipeBeam::calc(EnergyPlusData &state)
1183 : {
1184 :
1185 : // Using/Aliasing
1186 : using PlantUtilities::SetComponentFlowRate;
1187 :
1188 : // Locals
1189 : // SUBROUTINE ARGUMENT DEFINITIONS:
1190 :
1191 : // SUBROUTINE PARAMETER DEFINITIONS:
1192 : static constexpr std::string_view routineName("HVACFourPipeBeam::calc ");
1193 :
1194 : // INTERFACE BLOCK SPECIFICATIONS
1195 : // na
1196 :
1197 : // DERIVED TYPE DEFINITIONS
1198 : // na
1199 :
1200 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1201 : Real64 fModCoolCWMdot; // Cooling capacity modification factor function of chilled water flow rate
1202 : Real64 fModCoolDeltaT; // Cooling capacity modification factor function of air-water temperature difference
1203 : Real64 fModCoolAirMdot; // Cooling capacity modification factor function of primary air flow rate
1204 : Real64 fModHeatHWMdot; // Heating capacity modification factor function of hot water flow rate
1205 : Real64 fModHeatDeltaT; // Heating capacity modification factor function of water - air temperature difference
1206 : Real64 fModHeatAirMdot; // Heating capacity modification factor function of primary air flow rate
1207 : Real64 cp; // local fluid specific heat
1208 :
1209 152 : this->qDotBeamHeating = 0.0;
1210 152 : this->qDotBeamCooling = 0.0;
1211 152 : this->qDotSystemAir = this->mDotSystemAir * ((this->cpSystemAir * this->tDBSystemAir) - (this->cpZoneAir * this->tDBZoneAirTemp));
1212 :
1213 152 : if (this->coolingAvailable && this->mDotCW > HVAC::VerySmallMassFlow) {
1214 : // test chilled water flow against plant, it might not all be available
1215 41 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1216 : fModCoolCWMdot =
1217 41 : Curve::CurveValue(state, this->modCoolingQdotCWFlowFuncNum, ((this->mDotCW / this->totBeamLength) / this->mDotNormRatedCW));
1218 : fModCoolDeltaT =
1219 41 : Curve::CurveValue(state, this->modCoolingQdotDeltaTFuncNum, ((this->tDBZoneAirTemp - this->cWTempIn) / this->deltaTempRatedCooling));
1220 82 : fModCoolAirMdot = Curve::CurveValue(
1221 41 : state, this->modCoolingQdotAirFlowFuncNum, ((this->mDotSystemAir / this->totBeamLength) / this->mDotNormRatedPrimAir));
1222 41 : this->qDotBeamCooling = -1.0 * this->qDotNormRatedCooling * fModCoolDeltaT * fModCoolAirMdot * fModCoolCWMdot * this->totBeamLength;
1223 41 : cp = state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getSpecificHeat(state, this->cWTempIn, routineName);
1224 41 : if (this->mDotCW > 0.0) {
1225 39 : this->cWTempOut = this->cWTempIn - (this->qDotBeamCooling / (this->mDotCW * cp));
1226 : } else {
1227 2 : this->cWTempOut = this->cWTempIn;
1228 : }
1229 : // check if non physical temperature rise, can't be warmer than air
1230 41 : if (this->cWTempOut > (std::max(this->tDBSystemAir, this->tDBZoneAirTemp) - 1.0)) {
1231 : // throw recurring warning as this indicates a problem in beam model input
1232 0 : ShowRecurringWarningErrorAtEnd(state,
1233 0 : std::string{routineName} + " four pipe beam name " + this->name +
1234 : ", chilled water outlet temperature is too warm. Capacity was limited. check beam capacity input ",
1235 0 : this->cWTempOutErrorCount,
1236 0 : this->cWTempOut,
1237 0 : this->cWTempOut);
1238 : // restrict it within 1.0 C of warmest air and recalculate cooling
1239 0 : this->cWTempOut = (std::max(this->tDBSystemAir, this->tDBZoneAirTemp) - 1.0);
1240 0 : this->qDotBeamCooling = this->mDotCW * cp * (this->cWTempIn - this->cWTempOut);
1241 : }
1242 : } else {
1243 111 : this->mDotCW = 0.0;
1244 111 : if (this->beamCoolingPresent) {
1245 111 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1246 : }
1247 111 : this->cWTempOut = this->cWTempIn;
1248 111 : this->qDotBeamCooling = 0.0;
1249 : }
1250 152 : if (this->heatingAvailable && this->mDotHW > HVAC::VerySmallMassFlow) {
1251 : // test hot water flow against plant, it might not all be available
1252 71 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1253 : fModHeatHWMdot =
1254 71 : Curve::CurveValue(state, this->modHeatingQdotHWFlowFuncNum, ((this->mDotHW / this->totBeamLength) / this->mDotNormRatedHW));
1255 : fModHeatDeltaT =
1256 71 : Curve::CurveValue(state, this->modHeatingQdotDeltaTFuncNum, ((this->hWTempIn - this->tDBZoneAirTemp) / this->deltaTempRatedHeating));
1257 142 : fModHeatAirMdot = Curve::CurveValue(
1258 71 : state, this->modHeatingQdotAirFlowFuncNum, ((this->mDotSystemAir / this->totBeamLength) / this->mDotNormRatedPrimAir));
1259 71 : this->qDotBeamHeating = this->qDotNormRatedHeating * fModHeatDeltaT * fModHeatAirMdot * fModHeatHWMdot * this->totBeamLength;
1260 71 : cp = state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getSpecificHeat(state, this->hWTempIn, routineName);
1261 71 : if (this->mDotHW > 0.0) {
1262 67 : this->hWTempOut = this->hWTempIn - (this->qDotBeamHeating / (this->mDotHW * cp));
1263 : } else {
1264 4 : this->hWTempOut = this->hWTempIn;
1265 : }
1266 : // check if non physical temperature drop, can't be cooler than air
1267 71 : if (this->hWTempOut < (std::min(this->tDBSystemAir, this->tDBZoneAirTemp) + 1.0)) {
1268 : // throw recurring warning as this indicates a problem in beam model input
1269 0 : ShowRecurringWarningErrorAtEnd(state,
1270 0 : std::string{routineName} + " four pipe beam name " + this->name +
1271 : ", hot water outlet temperature is too cool. Capacity was limited. check beam capacity input ",
1272 0 : this->hWTempOutErrorCount,
1273 0 : this->hWTempOut,
1274 0 : this->hWTempOut);
1275 : // restrict it within 1.0 C of warmest air and recalculate cooling
1276 0 : this->hWTempOut = (std::min(this->tDBSystemAir, this->tDBZoneAirTemp) + 1.0);
1277 0 : this->qDotBeamHeating = this->mDotHW * cp * (this->hWTempIn - this->hWTempOut);
1278 : }
1279 : } else {
1280 81 : this->mDotHW = 0.0;
1281 81 : if (this->beamHeatingPresent) {
1282 81 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1283 : }
1284 81 : this->hWTempOut = this->hWTempIn;
1285 81 : this->qDotBeamHeating = 0.0;
1286 : }
1287 :
1288 152 : this->qDotTotalDelivered = this->qDotSystemAir + this->qDotBeamCooling + this->qDotBeamHeating;
1289 152 : }
1290 :
1291 33 : void HVACFourPipeBeam::update(EnergyPlusData &state) const // update node date elsewhere in EnergyPlus, does not change state of this
1292 : {
1293 33 : auto &Node(state.dataLoopNodes->Node);
1294 :
1295 : using PlantUtilities::SafeCopyPlantNode;
1296 :
1297 : // Set the outlet air nodes of the unit; note that all quantities are unchanged from inlet to outlet
1298 33 : Node(this->airOutNodeNum).MassFlowRate = Node(this->airInNodeNum).MassFlowRate;
1299 33 : Node(this->airOutNodeNum).Temp = Node(this->airInNodeNum).Temp;
1300 33 : Node(this->airOutNodeNum).HumRat = Node(this->airInNodeNum).HumRat;
1301 33 : Node(this->airOutNodeNum).Enthalpy = Node(this->airInNodeNum).Enthalpy;
1302 33 : Node(this->airOutNodeNum).Quality = Node(this->airInNodeNum).Quality;
1303 33 : Node(this->airOutNodeNum).Press = Node(this->airInNodeNum).Press;
1304 33 : Node(this->airOutNodeNum).MassFlowRateMin = Node(this->airInNodeNum).MassFlowRateMin;
1305 33 : Node(this->airOutNodeNum).MassFlowRateMax = Node(this->airInNodeNum).MassFlowRateMax;
1306 33 : Node(this->airOutNodeNum).MassFlowRateMinAvail = Node(this->airInNodeNum).MassFlowRateMinAvail;
1307 33 : Node(this->airOutNodeNum).MassFlowRateMaxAvail = Node(this->airInNodeNum).MassFlowRateMaxAvail;
1308 :
1309 33 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1310 0 : Node(this->airOutNodeNum).CO2 = Node(this->airInNodeNum).CO2;
1311 : }
1312 :
1313 33 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1314 0 : Node(this->airOutNodeNum).GenContam = Node(this->airInNodeNum).GenContam;
1315 : }
1316 :
1317 : // Set the outlet water nodes for the unit
1318 :
1319 33 : if (this->beamCoolingPresent) {
1320 33 : SafeCopyPlantNode(state, this->cWInNodeNum, this->cWOutNodeNum);
1321 33 : Node(this->cWOutNodeNum).Temp = this->cWTempOut;
1322 : }
1323 33 : if (this->beamHeatingPresent) {
1324 33 : SafeCopyPlantNode(state, this->hWInNodeNum, this->hWOutNodeNum);
1325 33 : Node(this->hWOutNodeNum).Temp = this->hWTempOut;
1326 : }
1327 33 : }
1328 :
1329 33 : void HVACFourPipeBeam::report(EnergyPlusData &state) // fill out local output variables for reporting
1330 : {
1331 :
1332 : Real64 ReportingConstant;
1333 :
1334 33 : ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
1335 :
1336 33 : if (this->beamCoolingPresent) {
1337 33 : this->beamCoolingRate = std::abs(this->qDotBeamCooling); // report var has positive sign convention
1338 33 : this->beamCoolingEnergy = this->beamCoolingRate * ReportingConstant;
1339 : }
1340 33 : if (this->beamHeatingPresent) {
1341 33 : this->beamHeatingRate = this->qDotBeamHeating;
1342 33 : this->beamHeatingEnergy = this->beamHeatingRate * ReportingConstant;
1343 : }
1344 33 : if (qDotSystemAir <= 0.0) { // cooling
1345 20 : this->supAirCoolingRate = std::abs(this->qDotSystemAir);
1346 20 : this->supAirHeatingRate = 0.0;
1347 : } else {
1348 13 : this->supAirHeatingRate = this->qDotSystemAir;
1349 13 : this->supAirCoolingRate = 0.0;
1350 : }
1351 33 : this->supAirCoolingEnergy = this->supAirCoolingRate * ReportingConstant;
1352 33 : this->supAirHeatingEnergy = this->supAirHeatingRate * ReportingConstant;
1353 :
1354 33 : this->primAirFlow = this->mDotSystemAir / state.dataEnvrn->StdRhoAir;
1355 :
1356 33 : this->CalcOutdoorAirVolumeFlowRate(state);
1357 33 : }
1358 :
1359 33 : void HVACFourPipeBeam::CalcOutdoorAirVolumeFlowRate(EnergyPlusData &state)
1360 : {
1361 : // calculates zone outdoor air volume flow rate using the supply air flow rate and OA fraction
1362 33 : if (this->airLoopNum > 0) {
1363 33 : this->OutdoorAirFlowRate = (state.dataLoopNodes->Node(this->airOutNodeNum).MassFlowRate / state.dataEnvrn->StdRhoAir) *
1364 33 : state.dataAirLoop->AirLoopFlow(this->airLoopNum).OAFrac;
1365 : } else {
1366 0 : this->OutdoorAirFlowRate = 0.0;
1367 : }
1368 33 : }
1369 :
1370 0 : void HVACFourPipeBeam::reportTerminalUnit(EnergyPlusData &state)
1371 : {
1372 : // populate the predefined equipment summary report related to air terminals
1373 0 : auto &orp = state.dataOutRptPredefined;
1374 0 : auto &adu = state.dataDefineEquipment->AirDistUnit(this->aDUNum);
1375 0 : if (!state.dataSize->TermUnitFinalZoneSizing.empty()) {
1376 0 : auto &sizing = state.dataSize->TermUnitFinalZoneSizing(adu.TermUnitSizingNum);
1377 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlow, adu.Name, sizing.DesCoolVolFlowMin);
1378 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOutdoorFlow, adu.Name, sizing.MinOA);
1379 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupCoolingSP, adu.Name, sizing.CoolDesTemp);
1380 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupHeatingSP, adu.Name, sizing.HeatDesTemp);
1381 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatingCap, adu.Name, sizing.DesHeatLoad);
1382 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolingCap, adu.Name, sizing.DesCoolLoad);
1383 : }
1384 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermTypeInp, adu.Name, "AirTerminal:SingleDuct:ConstantVolume:FourPipeBeam");
1385 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermPrimFlow, adu.Name, this->vDotNormRatedPrimAir);
1386 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSecdFlow, adu.Name, "n/a");
1387 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, "n/a");
1388 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMaxFlowReh, adu.Name, "n/a");
1389 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOAflowSch, adu.Name, "n/a");
1390 0 : if (this->beamHeatingPresent) {
1391 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, "Included");
1392 : } else {
1393 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, "None");
1394 : }
1395 :
1396 0 : if (this->beamCoolingPresent) {
1397 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "Included");
1398 : } else {
1399 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "None");
1400 : }
1401 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanType, adu.Name, "n/a");
1402 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanName, adu.Name, "n/a");
1403 0 : }
1404 :
1405 : } // namespace FourPipeBeam
1406 :
1407 : } // namespace EnergyPlus
|