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) {
464 0 : continue;
465 : }
466 4 : for (int supAirIn = 1; supAirIn <= state.dataZoneEquip->ZoneEquipConfig(ctrlZone).NumInletNodes; ++supAirIn) {
467 4 : if (thisBeam->airOutNodeNum == state.dataZoneEquip->ZoneEquipConfig(ctrlZone).InletNode(supAirIn)) {
468 4 : thisBeam->zoneIndex = ctrlZone;
469 4 : thisBeam->zoneNodeIndex = state.dataZoneEquip->ZoneEquipConfig(ctrlZone).ZoneNode;
470 4 : thisBeam->ctrlZoneInNodeIndex = supAirIn;
471 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitCool(supAirIn).InNode = thisBeam->airInNodeNum;
472 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitCool(supAirIn).OutNode = thisBeam->airOutNodeNum;
473 4 : state.dataDefineEquipment->AirDistUnit(thisBeam->aDUNum).TermUnitSizingNum =
474 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitCool(supAirIn).TermUnitSizingIndex;
475 4 : thisBeam->termUnitSizingNum = state.dataDefineEquipment->AirDistUnit(thisBeam->aDUNum).TermUnitSizingNum;
476 4 : state.dataDefineEquipment->AirDistUnit(thisBeam->aDUNum).ZoneEqNum = ctrlZone;
477 4 : if (thisBeam->beamHeatingPresent) {
478 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitHeat(supAirIn).InNode = thisBeam->airInNodeNum;
479 4 : state.dataZoneEquip->ZoneEquipConfig(ctrlZone).AirDistUnitHeat(supAirIn).OutNode = thisBeam->airOutNodeNum;
480 : }
481 4 : airNodeFound = true;
482 4 : break;
483 : }
484 : }
485 : }
486 : }
487 4 : if (!airNodeFound) {
488 0 : ShowSevereError(state, format("The outlet air node from the {} = {}", cCurrentModuleObject, thisBeam->name));
489 0 : ShowContinueError(state, format("did not have a matching Zone Equipment Inlet Node, Node ={}", state.dataIPShortCut->cAlphaArgs(5)));
490 0 : ErrorsFound = true;
491 : }
492 :
493 4 : if (found && !ErrorsFound) {
494 4 : state.dataFourPipeBeam->FourPipeBeams.push_back(thisBeam);
495 4 : return thisBeam;
496 : } else {
497 0 : ShowFatalError(state, format("{}Errors found in getting input. Preceding conditions cause termination.", routineName));
498 0 : return nullptr;
499 : }
500 4 : }
501 :
502 0 : int HVACFourPipeBeam::getAirLoopNum()
503 : {
504 0 : return airLoopNum;
505 : }
506 :
507 0 : int HVACFourPipeBeam::getZoneIndex()
508 : {
509 0 : return zoneIndex;
510 : }
511 :
512 1 : Real64 HVACFourPipeBeam::getPrimAirDesignVolFlow()
513 : {
514 1 : return vDotDesignPrimAir;
515 : }
516 :
517 0 : int HVACFourPipeBeam::getTermUnitSizingIndex()
518 : {
519 0 : return termUnitSizingNum;
520 : }
521 :
522 37 : void HVACFourPipeBeam::simulate(EnergyPlusData &state,
523 : bool const FirstHVACIteration, // TRUE if first HVAC iteration in time step
524 : Real64 &NonAirSysOutput // convective cooling by the beam system [W]
525 : )
526 : {
527 :
528 : // initialize the unit
529 37 : this->init(state, FirstHVACIteration);
530 :
531 : // control and simulate the beam
532 36 : if (!this->mySizeFlag) {
533 33 : this->control(state, FirstHVACIteration, NonAirSysOutput);
534 :
535 : // Update the current unit's outlet nodes.
536 33 : this->update(state);
537 :
538 : // Fill the report variables.
539 33 : this->report(state);
540 : }
541 36 : }
542 :
543 37 : void HVACFourPipeBeam::init(EnergyPlusData &state,
544 : bool const FirstHVACIteration // TRUE if first air loop solution this HVAC step
545 : )
546 : {
547 :
548 : // Using
549 : using DataZoneEquipment::CheckZoneEquipmentList;
550 : using PlantUtilities::InitComponentNodes;
551 : using PlantUtilities::ScanPlantLoopsForObject;
552 : using PlantUtilities::SetComponentFlowRate;
553 :
554 : static constexpr std::string_view routineName("HVACFourPipeBeam::init");
555 :
556 37 : if (this->plantLoopScanFlag && allocated(state.dataPlnt->PlantLoop)) {
557 3 : bool errFlag = false;
558 3 : if (this->beamCoolingPresent) {
559 9 : ScanPlantLoopsForObject(state,
560 : this->name,
561 : DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal,
562 3 : this->cWplantLoc,
563 : errFlag,
564 : _,
565 : _,
566 : _,
567 3 : this->cWInNodeNum,
568 : _);
569 3 : if (errFlag) {
570 0 : ShowFatalError(state, format("{} Program terminated for previous conditions.", routineName));
571 : }
572 : }
573 3 : if (this->beamHeatingPresent) {
574 9 : ScanPlantLoopsForObject(state,
575 : this->name,
576 : DataPlant::PlantEquipmentType::FourPipeBeamAirTerminal,
577 3 : this->hWplantLoc,
578 : errFlag,
579 : _,
580 : _,
581 : _,
582 3 : this->hWInNodeNum,
583 : _);
584 3 : if (errFlag) {
585 0 : ShowFatalError(state, format("{} Program terminated for previous conditions.", routineName));
586 : }
587 : }
588 3 : this->plantLoopScanFlag = false;
589 : }
590 :
591 37 : if (!this->zoneEquipmentListChecked && state.dataZoneEquip->ZoneEquipInputsFilled) {
592 : // Check to see if there is a Air Distribution Unit on the Zone Equipment List
593 3 : if (this->aDUNum != 0) {
594 3 : if (!CheckZoneEquipmentList(state, "ZONEHVAC:AIRDISTRIBUTIONUNIT", state.dataDefineEquipment->AirDistUnit(this->aDUNum).Name)) {
595 0 : ShowSevereError(state,
596 0 : format("{}: ADU=[Air Distribution Unit,{}] is not on any ZoneHVAC:EquipmentList.",
597 : routineName,
598 0 : state.dataDefineEquipment->AirDistUnit(this->aDUNum).Name));
599 0 : ShowContinueError(state, format("...Unit=[{},{}] will not be simulated.", this->unitType, this->name));
600 : }
601 3 : this->zoneEquipmentListChecked = true;
602 : }
603 : }
604 :
605 37 : if (!state.dataGlobal->SysSizingCalc && this->mySizeFlag && !this->plantLoopScanFlag) {
606 : // if ( SysSizingCalc && this->mySizeFlag && ! this->plantLoopScanFlag ) {
607 3 : this->airLoopNum = state.dataZoneEquip->ZoneEquipConfig(this->zoneIndex).InletNodeAirLoopNum(this->ctrlZoneInNodeIndex);
608 3 : state.dataDefineEquipment->AirDistUnit(this->aDUNum).AirLoopNum = this->airLoopNum;
609 3 : this->set_size(state); // calculate autosize values (in any) and convert volume flow rates to mass flow rates
610 2 : if (this->beamCoolingPresent) { // initialize chilled water design mass flow rate in plant routines
611 2 : InitComponentNodes(state, 0.0, this->mDotDesignCW, this->cWInNodeNum, this->cWOutNodeNum);
612 : }
613 2 : if (this->beamHeatingPresent) { // initialize hot water design mass flow rate in plant routines
614 2 : InitComponentNodes(state, 0.0, this->mDotDesignHW, this->hWInNodeNum, this->hWOutNodeNum);
615 : }
616 2 : this->mySizeFlag = false;
617 : }
618 :
619 : // Do the Begin Environment initializations
620 36 : if (state.dataGlobal->BeginEnvrnFlag && this->myEnvrnFlag) {
621 :
622 4 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMax = this->mDotDesignPrimAir;
623 4 : state.dataLoopNodes->Node(this->airOutNodeNum).MassFlowRateMax = this->mDotDesignPrimAir;
624 4 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMin = 0.0;
625 4 : state.dataLoopNodes->Node(this->airOutNodeNum).MassFlowRateMin = 0.0;
626 :
627 4 : if (this->beamCoolingPresent) { // initialize chilled water design mass flow rate in plant routines
628 4 : InitComponentNodes(state, 0.0, this->mDotDesignCW, this->cWInNodeNum, this->cWOutNodeNum);
629 : }
630 4 : if (this->beamHeatingPresent) { // initialize hot water design mass flow rate in plant routines
631 4 : InitComponentNodes(state, 0.0, this->mDotDesignHW, this->hWInNodeNum, this->hWOutNodeNum);
632 : }
633 :
634 4 : if (this->airLoopNum == 0) { // fill air loop index
635 0 : if (this->zoneIndex > 0 && this->ctrlZoneInNodeIndex > 0) {
636 0 : this->airLoopNum = state.dataZoneEquip->ZoneEquipConfig(this->zoneIndex).InletNodeAirLoopNum(this->ctrlZoneInNodeIndex);
637 : }
638 : }
639 :
640 4 : this->myEnvrnFlag = false;
641 : } // end one time inits
642 :
643 36 : if (!state.dataGlobal->BeginEnvrnFlag) {
644 24 : this->myEnvrnFlag = true;
645 : }
646 :
647 : // Do the start of HVAC time step initializations
648 36 : if (FirstHVACIteration) {
649 : // check availability schedules and set flags
650 24 : this->airAvailable = (this->airAvailSched->getCurrentVal() > 0.0);
651 24 : this->coolingAvailable = (this->airAvailable && beamCoolingPresent && (this->coolingAvailSched->getCurrentVal() > 0.0));
652 24 : this->heatingAvailable = (this->airAvailable && beamHeatingPresent && (this->heatingAvailSched->getCurrentVal() > 0.0));
653 :
654 : // check for upstream zero flow. If nonzero and air available, set primary flow to max
655 24 : if (this->airAvailable && state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate > 0.0) {
656 19 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate = this->mDotDesignPrimAir;
657 : } else {
658 5 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate = 0.0;
659 : }
660 : // reset the max and min avail flows
661 24 : if (this->airAvailable && state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail > 0.0) {
662 19 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail = this->mDotDesignPrimAir;
663 19 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMinAvail = this->mDotDesignPrimAir;
664 : } else {
665 5 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail = 0.0;
666 5 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMinAvail = 0.0;
667 : }
668 : }
669 :
670 : // do these initializations every time step
671 36 : if (beamCoolingPresent) {
672 36 : this->cWTempIn = state.dataLoopNodes->Node(this->cWInNodeNum).Temp;
673 36 : this->cWTempOut = this->cWTempIn;
674 : }
675 36 : if (beamHeatingPresent) {
676 36 : this->hWTempIn = state.dataLoopNodes->Node(this->hWInNodeNum).Temp;
677 36 : this->hWTempOut = this->hWTempIn;
678 : }
679 36 : this->mDotSystemAir = state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRateMaxAvail;
680 36 : state.dataLoopNodes->Node(this->airInNodeNum).MassFlowRate = this->mDotSystemAir;
681 36 : this->tDBZoneAirTemp = state.dataLoopNodes->Node(this->zoneNodeIndex).Temp;
682 36 : this->tDBSystemAir = state.dataLoopNodes->Node(this->airInNodeNum).Temp;
683 36 : this->cpZoneAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(this->zoneNodeIndex).HumRat);
684 36 : this->cpSystemAir = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(this->airInNodeNum).HumRat);
685 36 : this->qDotBeamCooling = 0.0;
686 36 : this->qDotBeamHeating = 0.0;
687 36 : this->supAirCoolingRate = 0.0;
688 36 : this->supAirHeatingRate = 0.0;
689 36 : this->beamCoolingRate = 0.0;
690 36 : this->beamHeatingRate = 0.0;
691 36 : this->primAirFlow = 0.0;
692 :
693 36 : } // init
694 :
695 3 : void HVACFourPipeBeam::set_size(EnergyPlusData &state)
696 : {
697 :
698 : // Using
699 : using namespace DataSizing;
700 : using PlantUtilities::MyPlantSizingIndex;
701 : using PlantUtilities::RegisterPlantCompDesignFlow;
702 : using Psychrometrics::PsyCpAirFnW;
703 : using namespace std::placeholders;
704 :
705 : static constexpr std::string_view routineName("HVACFourPipeBeam::set_size ");
706 :
707 3 : bool ErrorsFound = false;
708 : Real64 rho; // local fluid density
709 : bool noHardSizeAnchorAvailable; // aid for complex logic surrounding mix of hard size and autosizes
710 3 : Real64 cpAir = 0.0;
711 3 : Real64 ErrTolerance = 0.001;
712 :
713 3 : Real64 mDotAirSolutionHeating = 0.0;
714 3 : Real64 mDotAirSolutionCooling = 0.0;
715 3 : Real64 originalTermUnitSizeMaxVDot = 0.0;
716 3 : Real64 originalTermUnitSizeCoolVDot = 0.0;
717 3 : Real64 originalTermUnitSizeHeatVDot = 0.0;
718 :
719 : // convert rated primary flow rate to mass flow rate using standard pressure and dry air at 20.0
720 3 : this->mDotNormRatedPrimAir = this->vDotNormRatedPrimAir * state.dataEnvrn->rhoAirSTP;
721 :
722 3 : noHardSizeAnchorAvailable = false;
723 :
724 3 : if (state.dataSize->CurTermUnitSizingNum > 0) {
725 3 : originalTermUnitSizeMaxVDot = std::max(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow,
726 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow);
727 3 : originalTermUnitSizeCoolVDot = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolVolFlow;
728 3 : originalTermUnitSizeHeatVDot = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatVolFlow;
729 : }
730 :
731 3 : if (this->totBeamLengthWasAutosized && this->vDotDesignPrimAirWasAutosized && this->vDotDesignCWWasAutosized &&
732 3 : this->vDotDesignHWWasAutosized) {
733 3 : noHardSizeAnchorAvailable = true;
734 0 : } else if (this->totBeamLengthWasAutosized && this->vDotDesignPrimAirWasAutosized && this->vDotDesignCWWasAutosized && !beamHeatingPresent) {
735 0 : noHardSizeAnchorAvailable = true;
736 0 : } else if (this->totBeamLengthWasAutosized && this->vDotDesignPrimAirWasAutosized && !this->beamCoolingPresent &&
737 0 : this->vDotDesignHWWasAutosized) {
738 0 : noHardSizeAnchorAvailable = true;
739 0 : } else if (!this->totBeamLengthWasAutosized) { // the simplest case is where length is not autosized
740 : // use the normalized rated values (likely defaulted ) with length to calculate any that are autosized
741 0 : if (this->vDotDesignPrimAirWasAutosized) {
742 0 : this->vDotDesignPrimAir = this->vDotNormRatedPrimAir * this->totBeamLength;
743 : }
744 0 : if (this->vDotDesignCWWasAutosized) {
745 0 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
746 : }
747 0 : if (vDotDesignHWWasAutosized) {
748 0 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
749 : }
750 : } else { // need to find beam length
751 : // the next simplest case is if the supply air rate is given
752 0 : if (!this->vDotDesignPrimAirWasAutosized) { //
753 : // find length from air flow rate and then proceed
754 0 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
755 0 : if (this->vDotDesignCWWasAutosized) {
756 0 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
757 : }
758 0 : if (vDotDesignHWWasAutosized) {
759 0 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
760 : }
761 : } else { // both air and length are autosized
762 0 : if (this->beamCoolingPresent && !this->vDotDesignCWWasAutosized) { // we have a chilled water flow rate to use
763 0 : this->totBeamLength = this->vDotDesignCW / this->vDotNormRatedCW;
764 0 : this->vDotDesignPrimAir = this->vDotNormRatedPrimAir * this->totBeamLength;
765 0 : if (vDotDesignHWWasAutosized) {
766 0 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
767 : }
768 0 : } else if (this->beamHeatingPresent && !this->vDotDesignHWWasAutosized) { // we have a hot water flow rate to use
769 0 : this->totBeamLength = this->vDotDesignHW / this->vDotNormRatedHW;
770 0 : this->vDotDesignPrimAir = this->vDotNormRatedPrimAir * this->totBeamLength;
771 0 : if (this->vDotDesignCWWasAutosized) { // don't think it can come here but...
772 0 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
773 : }
774 : } else {
775 : // should not come here, developer exception
776 : }
777 : } // no air flow rate
778 : } // no beam length
779 :
780 6 : if (noHardSizeAnchorAvailable && (state.dataSize->CurZoneEqNum > 0) &&
781 3 : (state.dataSize->CurTermUnitSizingNum > 0)) { // need to use central sizing results to calculate
782 :
783 : // set up for solver
784 :
785 3 : CheckZoneSizing(state, this->unitType, this->name);
786 : // minimum flow rate is from air flow rate on the terminal unit final zone size ( typically ventilation minimum and may be too low)
787 3 : Real64 minFlow(0.0);
788 3 : Real64 maxFlowCool(0.0);
789 3 : minFlow = std::min(state.dataEnvrn->StdRhoAir * originalTermUnitSizeMaxVDot,
790 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).MinOA * state.dataEnvrn->StdRhoAir);
791 3 : minFlow = std::max(0.0, minFlow);
792 : // max flow is as if the air supply was sufficient to provide all the conditioning
793 :
794 3 : if (beamCoolingPresent) {
795 3 : cpAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInHumRatTU);
796 :
797 3 : if ((state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtCoolPeak -
798 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInTempTU) >
799 : 2.0) { // avoid div by zero and blow up
800 3 : maxFlowCool = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad /
801 3 : (cpAir * (state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtCoolPeak -
802 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInTempTU));
803 : } else {
804 0 : maxFlowCool = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad / (cpAir * 2.0);
805 : }
806 3 : if (minFlow * 3.0 >= maxFlowCool) {
807 2 : minFlow = maxFlowCool / 3.0; // make sure min is significantly lower than max.
808 : }
809 :
810 3 : int pltSizCoolNum = MyPlantSizingIndex(state, "four pipe beam unit", this->name, this->cWInNodeNum, this->cWOutNodeNum, ErrorsFound);
811 3 : if (pltSizCoolNum == 0) {
812 0 : ShowSevereError(state, "Autosizing of water flow requires a cooling loop Sizing:Plant object");
813 0 : ShowContinueError(state, format("Occurs in {} Object={}", this->unitType, this->name));
814 0 : ErrorsFound = true;
815 : } else {
816 3 : this->cWTempIn = state.dataSize->PlantSizData(pltSizCoolNum).ExitTemp;
817 : }
818 3 : this->mDotHW = 0.0;
819 3 : this->tDBZoneAirTemp = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtCoolPeak;
820 3 : this->tDBSystemAir = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInTempTU;
821 3 : this->cpZoneAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneHumRatAtCoolPeak);
822 3 : this->cpSystemAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolCoilInHumRatTU);
823 3 : this->qDotZoneReq = -1.0 * state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad;
824 3 : this->qDotZoneToCoolSetPt = -1.0 * state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesCoolLoad;
825 3 : this->airAvailable = true;
826 3 : this->coolingAvailable = true;
827 3 : this->heatingAvailable = false;
828 9 : auto f = [&state, this](Real64 const airFlow) {
829 : static constexpr std::string_view routineName("Real64 HVACFourPipeBeam::residualSizing ");
830 9 : this->mDotSystemAir = airFlow;
831 9 : this->vDotDesignPrimAir = this->mDotSystemAir / state.dataEnvrn->StdRhoAir;
832 9 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
833 9 : if (this->vDotDesignCWWasAutosized) {
834 9 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
835 : Real64 const rho =
836 9 : state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, routineName);
837 9 : this->mDotNormRatedCW = this->vDotNormRatedCW * rho;
838 9 : this->mDotCW = this->vDotDesignCW * rho;
839 9 : if (this->beamCoolingPresent) {
840 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum);
841 : }
842 : }
843 9 : if (vDotDesignHWWasAutosized) {
844 9 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
845 : Real64 const rho =
846 9 : state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, routineName);
847 9 : this->mDotNormRatedHW = this->vDotNormRatedHW * rho;
848 9 : this->mDotHW = this->vDotDesignHW * rho;
849 9 : if (this->beamHeatingPresent) {
850 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum);
851 : }
852 : }
853 9 : this->calc(state);
854 9 : if (this->qDotZoneReq != 0.0) {
855 9 : return ((this->qDotZoneReq - this->qDotTotalDelivered) / this->qDotZoneReq);
856 : } else {
857 0 : return 1.0;
858 : }
859 3 : };
860 3 : int SolFlag = 0;
861 3 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, mDotAirSolutionCooling, f, minFlow, maxFlowCool);
862 3 : if (SolFlag == -1) {
863 0 : ShowWarningError(state, format("Cooling load sizing search failed in four pipe beam unit called {}", this->name));
864 0 : ShowContinueError(state, " Iteration limit exceeded in calculating size for design cooling load");
865 3 : } else if (SolFlag == -2) {
866 0 : ShowWarningError(state, format("Cooling load sizing search failed in four pipe beam unit called {}", this->name));
867 0 : ShowContinueError(state, " Bad size limits");
868 : }
869 : }
870 :
871 3 : if (beamHeatingPresent) {
872 3 : cpAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInHumRatTU);
873 3 : Real64 maxFlowHeat = 0.0;
874 3 : if ((state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInTempTU -
875 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtHeatPeak) >
876 : 2.0) { // avoid div by zero and blow up
877 0 : maxFlowHeat = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad /
878 0 : (cpAir * (state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInTempTU -
879 0 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtHeatPeak));
880 : } else {
881 3 : maxFlowHeat = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad / (cpAir * 2.0);
882 : }
883 :
884 3 : int pltSizHeatNum = MyPlantSizingIndex(state, "four pipe beam unit", this->name, this->hWInNodeNum, this->hWOutNodeNum, ErrorsFound);
885 3 : if (pltSizHeatNum == 0) {
886 0 : ShowSevereError(state, "Autosizing of water flow requires a heating loop Sizing:Plant object");
887 0 : ShowContinueError(state, format("Occurs in {} Object={}", this->unitType, this->name));
888 0 : ErrorsFound = true;
889 : } else {
890 3 : this->hWTempIn = state.dataSize->PlantSizData(pltSizHeatNum).ExitTemp;
891 : }
892 3 : this->mDotCW = 0.0;
893 3 : this->tDBZoneAirTemp = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneTempAtHeatPeak;
894 3 : this->tDBSystemAir = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInTempTU;
895 3 : this->cpZoneAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).ZoneHumRatAtHeatPeak);
896 3 : this->cpSystemAir = PsyCpAirFnW(state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatCoilInHumRatTU);
897 3 : this->qDotZoneReq = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad;
898 3 : this->qDotZoneToHeatSetPt = state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).DesHeatLoad;
899 3 : this->airAvailable = true;
900 3 : this->heatingAvailable = true;
901 3 : this->coolingAvailable = false;
902 9 : auto f = [&state, this](Real64 const airFlow) {
903 : static constexpr std::string_view routineName("Real64 HVACFourPipeBeam::residualSizing ");
904 9 : this->mDotSystemAir = airFlow;
905 9 : this->vDotDesignPrimAir = this->mDotSystemAir / state.dataEnvrn->StdRhoAir;
906 9 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
907 9 : if (this->vDotDesignCWWasAutosized) {
908 9 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
909 : Real64 const rho =
910 9 : state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, routineName);
911 9 : this->mDotNormRatedCW = this->vDotNormRatedCW * rho;
912 9 : this->mDotCW = this->vDotDesignCW * rho;
913 9 : if (this->beamCoolingPresent) {
914 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum);
915 : }
916 : }
917 9 : if (vDotDesignHWWasAutosized) {
918 9 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
919 : Real64 const rho =
920 9 : state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, routineName);
921 9 : this->mDotNormRatedHW = this->vDotNormRatedHW * rho;
922 9 : this->mDotHW = this->vDotDesignHW * rho;
923 9 : if (this->beamHeatingPresent) {
924 9 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum);
925 : }
926 : }
927 9 : this->calc(state);
928 9 : if (this->qDotZoneReq != 0.0) {
929 9 : return ((this->qDotZoneReq - this->qDotTotalDelivered) / this->qDotZoneReq);
930 : } else {
931 0 : return 1.0;
932 : }
933 3 : };
934 3 : int SolFlag = 0;
935 3 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, mDotAirSolutionHeating, f, 0.0, maxFlowHeat);
936 3 : if (SolFlag == -1) {
937 0 : ShowWarningError(state, format("Heating load sizing search failed in four pipe beam unit called {}", this->name));
938 0 : ShowContinueError(state, " Iteration limit exceeded in calculating size for design heating load");
939 3 : } else if (SolFlag == -2) {
940 0 : ShowWarningError(state, format("Heating load sizing search failed in four pipe beam unit called {}", this->name));
941 0 : ShowContinueError(state, " Bad size limits");
942 : }
943 : }
944 :
945 : // take the larger of heating and cooling
946 3 : this->mDotDesignPrimAir = std::max(mDotAirSolutionHeating, mDotAirSolutionCooling);
947 : // make sure this is higher than the zone OA requirement
948 3 : this->mDotDesignPrimAir =
949 6 : std::max(this->mDotDesignPrimAir,
950 3 : state.dataSize->TermUnitFinalZoneSizing(state.dataSize->CurTermUnitSizingNum).MinOA * state.dataEnvrn->StdRhoAir);
951 3 : this->vDotDesignPrimAir = this->mDotDesignPrimAir / state.dataEnvrn->StdRhoAir;
952 3 : this->totBeamLength = this->vDotDesignPrimAir / this->vDotNormRatedPrimAir;
953 3 : if (this->vDotDesignCWWasAutosized) {
954 3 : this->vDotDesignCW = this->vDotNormRatedCW * this->totBeamLength;
955 : }
956 3 : if (vDotDesignHWWasAutosized) {
957 3 : this->vDotDesignHW = this->vDotNormRatedHW * this->totBeamLength;
958 : }
959 : }
960 : // fill in mass flow rate versions of working variables (regardless of autosizing )
961 3 : this->mDotDesignPrimAir = this->vDotDesignPrimAir * state.dataEnvrn->StdRhoAir;
962 :
963 3 : if ((originalTermUnitSizeMaxVDot > 0.0) && (originalTermUnitSizeMaxVDot != this->vDotDesignPrimAir) && (state.dataSize->CurZoneEqNum > 0)) {
964 3 : if ((state.dataSize->SysSizingRunDone) && (this->airLoopNum > 0)) {
965 : // perturb system size to handle change in system size calculated without knowing about 4 pipe beam
966 : // Note that this approach is not necessarily appropriate for coincident system design option
967 : // and it might be moved to make such adjustments in SizingManager::ManageSystemSizingAdjustments()
968 2 : state.dataSize->FinalSysSizing(this->airLoopNum).DesMainVolFlow += (this->vDotDesignPrimAir - originalTermUnitSizeMaxVDot);
969 2 : state.dataSize->FinalSysSizing(this->airLoopNum).DesCoolVolFlow += (this->vDotDesignPrimAir - originalTermUnitSizeCoolVDot);
970 2 : state.dataSize->FinalSysSizing(this->airLoopNum).DesHeatVolFlow += (this->vDotDesignPrimAir - originalTermUnitSizeHeatVDot);
971 2 : state.dataSize->FinalSysSizing(this->airLoopNum).MassFlowAtCoolPeak +=
972 2 : (this->vDotDesignPrimAir - originalTermUnitSizeCoolVDot) * state.dataEnvrn->StdRhoAir;
973 :
974 4 : BaseSizer::reportSizerOutput(state,
975 : this->unitType,
976 : this->name,
977 : "AirLoopHVAC Design Supply Air Flow Rate Adjustment [m3/s]",
978 2 : (this->vDotDesignPrimAir - originalTermUnitSizeMaxVDot));
979 : } else {
980 2 : ShowSevereError(state, "Four pipe beam requires system sizing. Turn on system sizing.");
981 3 : ShowFatalError(state, "Program terminating due to previous errors");
982 : }
983 : }
984 :
985 2 : if (this->beamCoolingPresent) {
986 2 : rho = state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, routineName);
987 2 : this->mDotNormRatedCW = this->vDotNormRatedCW * rho;
988 2 : this->mDotDesignCW = this->vDotDesignCW * rho;
989 2 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotDesignCW, this->cWInNodeNum, this->cWOutNodeNum);
990 : }
991 2 : if (this->beamHeatingPresent) {
992 2 : rho = state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, routineName);
993 2 : this->mDotNormRatedHW = this->vDotNormRatedHW * rho;
994 2 : this->mDotDesignHW = this->vDotDesignHW * rho;
995 2 : PlantUtilities::InitComponentNodes(state, 0.0, this->mDotDesignHW, this->hWInNodeNum, this->hWOutNodeNum);
996 : }
997 :
998 : // report final sizes if autosized
999 2 : if (this->vDotDesignPrimAirWasAutosized) {
1000 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Supply Air Flow Rate [m3/s]", this->vDotDesignPrimAir);
1001 : }
1002 2 : if (this->vDotDesignCWWasAutosized) {
1003 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Maximum Total Chilled Water Flow Rate [m3/s]", this->vDotDesignCW);
1004 : }
1005 2 : if (this->vDotDesignHWWasAutosized) {
1006 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Maximum Total Hot Water Flow Rate [m3/s]", this->vDotDesignHW);
1007 : }
1008 2 : if (this->totBeamLengthWasAutosized) {
1009 2 : BaseSizer::reportSizerOutput(state, this->unitType, this->name, "Zone Total Beam Length [m]", this->totBeamLength);
1010 : }
1011 : // save the design water volume flow rate for use by the water loop sizing algorithms
1012 2 : if (this->vDotDesignCW > 0.0 && this->beamCoolingPresent) {
1013 2 : RegisterPlantCompDesignFlow(state, this->cWInNodeNum, this->vDotDesignCW);
1014 : }
1015 2 : if (this->vDotDesignHW > 0.0 && this->beamHeatingPresent) {
1016 2 : RegisterPlantCompDesignFlow(state, this->hWInNodeNum, this->vDotDesignHW);
1017 : }
1018 2 : if (ErrorsFound) {
1019 0 : ShowFatalError(state, "Preceding four pipe beam sizing errors cause program termination");
1020 : }
1021 :
1022 2 : } // set_size
1023 :
1024 33 : void HVACFourPipeBeam::control(EnergyPlusData &state,
1025 : [[maybe_unused]] bool const FirstHVACIteration, // TRUE if 1st HVAC simulation of system timestep
1026 : Real64 &NonAirSysOutput // convective cooling by the beam system [W]
1027 : )
1028 : {
1029 :
1030 : // Using/Aliasing
1031 : using namespace DataZoneEnergyDemands;
1032 : using PlantUtilities::SetComponentFlowRate;
1033 : using namespace std::placeholders;
1034 :
1035 : int SolFlag;
1036 : Real64 ErrTolerance;
1037 :
1038 33 : NonAirSysOutput = 0.0; // initialize
1039 :
1040 33 : if (this->mDotSystemAir < HVAC::VerySmallMassFlow ||
1041 31 : (!this->airAvailable && !this->coolingAvailable && !this->heatingAvailable)) { // unit is off
1042 2 : this->mDotHW = 0.0;
1043 2 : if (this->beamHeatingPresent) {
1044 2 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1045 : }
1046 2 : this->hWTempOut = this->hWTempIn;
1047 : // assume if there is still flow that unit has an internal bypass and convector does not still heat
1048 2 : this->mDotCW = 0.0;
1049 2 : this->cWTempOut = this->cWTempIn;
1050 2 : if (this->beamCoolingPresent) {
1051 2 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1052 : }
1053 : // assume if there is still flow that unit has an internal bypass and convector does not still cool
1054 : // don't even need to run calc
1055 2 : return;
1056 : }
1057 :
1058 31 : if (this->airAvailable && this->mDotSystemAir > HVAC::VerySmallMassFlow && !this->coolingAvailable && !this->heatingAvailable) {
1059 0 : this->mDotHW = 0.0;
1060 0 : if (this->beamHeatingPresent) {
1061 0 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1062 : }
1063 : // assume if there is still flow that unit has an internal bypass and convector does not still heat
1064 0 : this->hWTempOut = this->hWTempIn;
1065 0 : this->mDotCW = 0.0;
1066 0 : if (this->beamCoolingPresent) {
1067 0 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1068 : }
1069 : // assume if there is still flow that unit has an internal bypass and convector does not still cool
1070 0 : this->cWTempOut = this->cWTempIn;
1071 0 : this->calc(state);
1072 :
1073 0 : return;
1074 : }
1075 :
1076 : // get zone loads
1077 31 : this->qDotZoneReq = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->zoneIndex).RemainingOutputRequired;
1078 31 : this->qDotZoneToHeatSetPt = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->zoneIndex).RemainingOutputReqToHeatSP;
1079 31 : this->qDotZoneToCoolSetPt = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(this->zoneIndex).RemainingOutputReqToCoolSP;
1080 :
1081 : // decide if beam is in heating or cooling
1082 :
1083 31 : this->qDotSystemAir = this->mDotSystemAir * ((this->cpSystemAir * this->tDBSystemAir) - (this->cpZoneAir * this->tDBZoneAirTemp));
1084 :
1085 31 : this->qDotBeamReq = this->qDotZoneReq - this->qDotSystemAir;
1086 :
1087 31 : if (this->qDotBeamReq < -HVAC::SmallLoad && this->coolingAvailable) { // beam cooling needed
1088 : // first calc with max chilled water flow
1089 10 : this->mDotHW = 0.0;
1090 10 : if (this->beamHeatingPresent) {
1091 10 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1092 : }
1093 10 : this->hWTempOut = this->hWTempIn;
1094 10 : this->mDotCW = this->mDotDesignCW;
1095 10 : this->calc(state);
1096 10 : if (this->qDotBeamCooling < (qDotBeamReq - HVAC::SmallLoad)) {
1097 : // can overcool, modulate chilled water flow rate to meet load
1098 8 : this->qDotBeamCoolingMax = this->qDotBeamCooling;
1099 8 : ErrTolerance = 0.01;
1100 28 : auto f = [&state, this](Real64 const cWFlow) {
1101 28 : this->mDotHW = 0.0;
1102 28 : this->mDotCW = cWFlow;
1103 28 : this->calc(state);
1104 28 : if (this->qDotBeamCoolingMax != 0.0) {
1105 28 : return (((this->qDotZoneToCoolSetPt - this->qDotSystemAir) - this->qDotBeamCooling) / this->qDotBeamCoolingMax);
1106 : } else {
1107 0 : return 1.0;
1108 : }
1109 8 : };
1110 8 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, this->mDotCW, f, 0.0, this->mDotDesignCW);
1111 8 : if (SolFlag == -1) {
1112 : // ShowWarningError( "Cold water control failed in four pipe beam unit called " + this->name );
1113 : // ShowContinueError(state, " Iteration limit exceeded in calculating cold water mass flow rate" );
1114 8 : } else if (SolFlag == -2) {
1115 : // ShowWarningError( "Cold water control failed in four pipe beam unit called " + this->name );
1116 : // ShowContinueError(state, " Bad cold water flow limits" );
1117 : }
1118 8 : this->calc(state);
1119 8 : NonAirSysOutput = this->qDotBeamCooling;
1120 8 : return;
1121 : } else { // can run flat out without overcooling, which we just did
1122 2 : NonAirSysOutput = this->qDotBeamCooling;
1123 2 : return;
1124 : }
1125 :
1126 21 : } else if (qDotBeamReq > HVAC::SmallLoad && this->heatingAvailable) { // beam heating needed
1127 : // first calc with max hot water flow
1128 19 : this->mDotCW = 0.0;
1129 19 : if (this->beamCoolingPresent) {
1130 19 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1131 : }
1132 19 : this->cWTempOut = this->cWTempIn;
1133 19 : this->mDotHW = this->mDotDesignHW;
1134 19 : this->calc(state);
1135 19 : if (this->qDotBeamHeating > (qDotBeamReq + HVAC::SmallLoad)) {
1136 15 : this->qDotBeamHeatingMax = this->qDotBeamHeating;
1137 : // can overheat, modulate hot water flow to meet load
1138 15 : ErrTolerance = 0.01;
1139 54 : auto f = [&state, this](Real64 const hWFlow) {
1140 54 : this->mDotHW = hWFlow;
1141 54 : this->mDotCW = 0.0;
1142 54 : this->calc(state);
1143 54 : if (this->qDotBeamHeatingMax != 0.0) {
1144 54 : return (((this->qDotZoneToHeatSetPt - this->qDotSystemAir) - this->qDotBeamHeating) / this->qDotBeamHeatingMax);
1145 : } else {
1146 0 : return 1.0;
1147 : }
1148 15 : };
1149 15 : General::SolveRoot(state, ErrTolerance, 50, SolFlag, this->mDotHW, f, 0.0, this->mDotDesignHW);
1150 15 : if (SolFlag == -1) {
1151 : // ShowWarningError( "Hot water control failed in four pipe beam unit called " + this->name );
1152 : // ShowContinueError(state, " Iteration limit exceeded in calculating hot water mass flow rate" );
1153 15 : } else if (SolFlag == -2) {
1154 : // ShowWarningError( "Hot water control failed in four pipe beam called " + this->name );
1155 : // ShowContinueError(state, " Bad hot water flow limits" );
1156 : }
1157 15 : this->calc(state);
1158 15 : NonAirSysOutput = this->qDotBeamHeating;
1159 15 : return;
1160 :
1161 : } else { // can run flat out without overheating, which we just did
1162 4 : NonAirSysOutput = this->qDotBeamHeating;
1163 4 : return;
1164 : }
1165 :
1166 : } else {
1167 2 : this->mDotHW = 0.0;
1168 2 : if (this->beamHeatingPresent) {
1169 2 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1170 : }
1171 2 : this->hWTempOut = this->hWTempIn;
1172 : // assume if there is still flow that unit has an internal bypass and convector does not still heat
1173 2 : this->mDotCW = 0.0;
1174 2 : this->cWTempOut = this->cWTempIn;
1175 2 : if (this->beamCoolingPresent) {
1176 2 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1177 : }
1178 : // assume if there is still flow that unit has an internal bypass and convector does not still cool
1179 : // don't even need to run calc
1180 2 : return;
1181 : }
1182 : }
1183 :
1184 152 : void HVACFourPipeBeam::calc(EnergyPlusData &state)
1185 : {
1186 :
1187 : // Using/Aliasing
1188 : using PlantUtilities::SetComponentFlowRate;
1189 :
1190 : // Locals
1191 : // SUBROUTINE ARGUMENT DEFINITIONS:
1192 :
1193 : // SUBROUTINE PARAMETER DEFINITIONS:
1194 : static constexpr std::string_view routineName("HVACFourPipeBeam::calc ");
1195 :
1196 : // INTERFACE BLOCK SPECIFICATIONS
1197 : // na
1198 :
1199 : // DERIVED TYPE DEFINITIONS
1200 : // na
1201 :
1202 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1203 : Real64 fModCoolCWMdot; // Cooling capacity modification factor function of chilled water flow rate
1204 : Real64 fModCoolDeltaT; // Cooling capacity modification factor function of air-water temperature difference
1205 : Real64 fModCoolAirMdot; // Cooling capacity modification factor function of primary air flow rate
1206 : Real64 fModHeatHWMdot; // Heating capacity modification factor function of hot water flow rate
1207 : Real64 fModHeatDeltaT; // Heating capacity modification factor function of water - air temperature difference
1208 : Real64 fModHeatAirMdot; // Heating capacity modification factor function of primary air flow rate
1209 : Real64 cp; // local fluid specific heat
1210 :
1211 152 : this->qDotBeamHeating = 0.0;
1212 152 : this->qDotBeamCooling = 0.0;
1213 152 : this->qDotSystemAir = this->mDotSystemAir * ((this->cpSystemAir * this->tDBSystemAir) - (this->cpZoneAir * this->tDBZoneAirTemp));
1214 :
1215 152 : if (this->coolingAvailable && this->mDotCW > HVAC::VerySmallMassFlow) {
1216 : // test chilled water flow against plant, it might not all be available
1217 41 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1218 : fModCoolCWMdot =
1219 41 : Curve::CurveValue(state, this->modCoolingQdotCWFlowFuncNum, ((this->mDotCW / this->totBeamLength) / this->mDotNormRatedCW));
1220 : fModCoolDeltaT =
1221 41 : Curve::CurveValue(state, this->modCoolingQdotDeltaTFuncNum, ((this->tDBZoneAirTemp - this->cWTempIn) / this->deltaTempRatedCooling));
1222 82 : fModCoolAirMdot = Curve::CurveValue(
1223 41 : state, this->modCoolingQdotAirFlowFuncNum, ((this->mDotSystemAir / this->totBeamLength) / this->mDotNormRatedPrimAir));
1224 41 : this->qDotBeamCooling = -1.0 * this->qDotNormRatedCooling * fModCoolDeltaT * fModCoolAirMdot * fModCoolCWMdot * this->totBeamLength;
1225 41 : cp = state.dataPlnt->PlantLoop(this->cWplantLoc.loopNum).glycol->getSpecificHeat(state, this->cWTempIn, routineName);
1226 41 : if (this->mDotCW > 0.0) {
1227 39 : this->cWTempOut = this->cWTempIn - (this->qDotBeamCooling / (this->mDotCW * cp));
1228 : } else {
1229 2 : this->cWTempOut = this->cWTempIn;
1230 : }
1231 : // check if non physical temperature rise, can't be warmer than air
1232 41 : if (this->cWTempOut > (std::max(this->tDBSystemAir, this->tDBZoneAirTemp) - 1.0)) {
1233 : // throw recurring warning as this indicates a problem in beam model input
1234 0 : ShowRecurringWarningErrorAtEnd(state,
1235 0 : std::string{routineName} + " four pipe beam name " + this->name +
1236 : ", chilled water outlet temperature is too warm. Capacity was limited. check beam capacity input ",
1237 0 : this->cWTempOutErrorCount,
1238 0 : this->cWTempOut,
1239 0 : this->cWTempOut);
1240 : // restrict it within 1.0 C of warmest air and recalculate cooling
1241 0 : this->cWTempOut = (std::max(this->tDBSystemAir, this->tDBZoneAirTemp) - 1.0);
1242 0 : this->qDotBeamCooling = this->mDotCW * cp * (this->cWTempIn - this->cWTempOut);
1243 : }
1244 : } else {
1245 111 : this->mDotCW = 0.0;
1246 111 : if (this->beamCoolingPresent) {
1247 111 : SetComponentFlowRate(state, this->mDotCW, this->cWInNodeNum, this->cWOutNodeNum, this->cWplantLoc);
1248 : }
1249 111 : this->cWTempOut = this->cWTempIn;
1250 111 : this->qDotBeamCooling = 0.0;
1251 : }
1252 152 : if (this->heatingAvailable && this->mDotHW > HVAC::VerySmallMassFlow) {
1253 : // test hot water flow against plant, it might not all be available
1254 71 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1255 : fModHeatHWMdot =
1256 71 : Curve::CurveValue(state, this->modHeatingQdotHWFlowFuncNum, ((this->mDotHW / this->totBeamLength) / this->mDotNormRatedHW));
1257 : fModHeatDeltaT =
1258 71 : Curve::CurveValue(state, this->modHeatingQdotDeltaTFuncNum, ((this->hWTempIn - this->tDBZoneAirTemp) / this->deltaTempRatedHeating));
1259 142 : fModHeatAirMdot = Curve::CurveValue(
1260 71 : state, this->modHeatingQdotAirFlowFuncNum, ((this->mDotSystemAir / this->totBeamLength) / this->mDotNormRatedPrimAir));
1261 71 : this->qDotBeamHeating = this->qDotNormRatedHeating * fModHeatDeltaT * fModHeatAirMdot * fModHeatHWMdot * this->totBeamLength;
1262 71 : cp = state.dataPlnt->PlantLoop(this->hWplantLoc.loopNum).glycol->getSpecificHeat(state, this->hWTempIn, routineName);
1263 71 : if (this->mDotHW > 0.0) {
1264 67 : this->hWTempOut = this->hWTempIn - (this->qDotBeamHeating / (this->mDotHW * cp));
1265 : } else {
1266 4 : this->hWTempOut = this->hWTempIn;
1267 : }
1268 : // check if non physical temperature drop, can't be cooler than air
1269 71 : if (this->hWTempOut < (std::min(this->tDBSystemAir, this->tDBZoneAirTemp) + 1.0)) {
1270 : // throw recurring warning as this indicates a problem in beam model input
1271 0 : ShowRecurringWarningErrorAtEnd(state,
1272 0 : std::string{routineName} + " four pipe beam name " + this->name +
1273 : ", hot water outlet temperature is too cool. Capacity was limited. check beam capacity input ",
1274 0 : this->hWTempOutErrorCount,
1275 0 : this->hWTempOut,
1276 0 : this->hWTempOut);
1277 : // restrict it within 1.0 C of warmest air and recalculate cooling
1278 0 : this->hWTempOut = (std::min(this->tDBSystemAir, this->tDBZoneAirTemp) + 1.0);
1279 0 : this->qDotBeamHeating = this->mDotHW * cp * (this->hWTempIn - this->hWTempOut);
1280 : }
1281 : } else {
1282 81 : this->mDotHW = 0.0;
1283 81 : if (this->beamHeatingPresent) {
1284 81 : SetComponentFlowRate(state, this->mDotHW, this->hWInNodeNum, this->hWOutNodeNum, this->hWplantLoc);
1285 : }
1286 81 : this->hWTempOut = this->hWTempIn;
1287 81 : this->qDotBeamHeating = 0.0;
1288 : }
1289 :
1290 152 : this->qDotTotalDelivered = this->qDotSystemAir + this->qDotBeamCooling + this->qDotBeamHeating;
1291 152 : }
1292 :
1293 33 : void HVACFourPipeBeam::update(EnergyPlusData &state) const // update node date elsewhere in EnergyPlus, does not change state of this
1294 : {
1295 33 : auto &Node(state.dataLoopNodes->Node);
1296 :
1297 : using PlantUtilities::SafeCopyPlantNode;
1298 :
1299 : // Set the outlet air nodes of the unit; note that all quantities are unchanged from inlet to outlet
1300 33 : Node(this->airOutNodeNum).MassFlowRate = Node(this->airInNodeNum).MassFlowRate;
1301 33 : Node(this->airOutNodeNum).Temp = Node(this->airInNodeNum).Temp;
1302 33 : Node(this->airOutNodeNum).HumRat = Node(this->airInNodeNum).HumRat;
1303 33 : Node(this->airOutNodeNum).Enthalpy = Node(this->airInNodeNum).Enthalpy;
1304 33 : Node(this->airOutNodeNum).Quality = Node(this->airInNodeNum).Quality;
1305 33 : Node(this->airOutNodeNum).Press = Node(this->airInNodeNum).Press;
1306 33 : Node(this->airOutNodeNum).MassFlowRateMin = Node(this->airInNodeNum).MassFlowRateMin;
1307 33 : Node(this->airOutNodeNum).MassFlowRateMax = Node(this->airInNodeNum).MassFlowRateMax;
1308 33 : Node(this->airOutNodeNum).MassFlowRateMinAvail = Node(this->airInNodeNum).MassFlowRateMinAvail;
1309 33 : Node(this->airOutNodeNum).MassFlowRateMaxAvail = Node(this->airInNodeNum).MassFlowRateMaxAvail;
1310 :
1311 33 : if (state.dataContaminantBalance->Contaminant.CO2Simulation) {
1312 0 : Node(this->airOutNodeNum).CO2 = Node(this->airInNodeNum).CO2;
1313 : }
1314 :
1315 33 : if (state.dataContaminantBalance->Contaminant.GenericContamSimulation) {
1316 0 : Node(this->airOutNodeNum).GenContam = Node(this->airInNodeNum).GenContam;
1317 : }
1318 :
1319 : // Set the outlet water nodes for the unit
1320 :
1321 33 : if (this->beamCoolingPresent) {
1322 33 : SafeCopyPlantNode(state, this->cWInNodeNum, this->cWOutNodeNum);
1323 33 : Node(this->cWOutNodeNum).Temp = this->cWTempOut;
1324 : }
1325 33 : if (this->beamHeatingPresent) {
1326 33 : SafeCopyPlantNode(state, this->hWInNodeNum, this->hWOutNodeNum);
1327 33 : Node(this->hWOutNodeNum).Temp = this->hWTempOut;
1328 : }
1329 33 : }
1330 :
1331 33 : void HVACFourPipeBeam::report(EnergyPlusData &state) // fill out local output variables for reporting
1332 : {
1333 :
1334 : Real64 ReportingConstant;
1335 :
1336 33 : ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
1337 :
1338 33 : if (this->beamCoolingPresent) {
1339 33 : this->beamCoolingRate = std::abs(this->qDotBeamCooling); // report var has positive sign convention
1340 33 : this->beamCoolingEnergy = this->beamCoolingRate * ReportingConstant;
1341 : }
1342 33 : if (this->beamHeatingPresent) {
1343 33 : this->beamHeatingRate = this->qDotBeamHeating;
1344 33 : this->beamHeatingEnergy = this->beamHeatingRate * ReportingConstant;
1345 : }
1346 33 : if (qDotSystemAir <= 0.0) { // cooling
1347 20 : this->supAirCoolingRate = std::abs(this->qDotSystemAir);
1348 20 : this->supAirHeatingRate = 0.0;
1349 : } else {
1350 13 : this->supAirHeatingRate = this->qDotSystemAir;
1351 13 : this->supAirCoolingRate = 0.0;
1352 : }
1353 33 : this->supAirCoolingEnergy = this->supAirCoolingRate * ReportingConstant;
1354 33 : this->supAirHeatingEnergy = this->supAirHeatingRate * ReportingConstant;
1355 :
1356 33 : this->primAirFlow = this->mDotSystemAir / state.dataEnvrn->StdRhoAir;
1357 :
1358 33 : this->CalcOutdoorAirVolumeFlowRate(state);
1359 33 : }
1360 :
1361 33 : void HVACFourPipeBeam::CalcOutdoorAirVolumeFlowRate(EnergyPlusData &state)
1362 : {
1363 : // calculates zone outdoor air volume flow rate using the supply air flow rate and OA fraction
1364 33 : if (this->airLoopNum > 0) {
1365 33 : this->OutdoorAirFlowRate = (state.dataLoopNodes->Node(this->airOutNodeNum).MassFlowRate / state.dataEnvrn->StdRhoAir) *
1366 33 : state.dataAirLoop->AirLoopFlow(this->airLoopNum).OAFrac;
1367 : } else {
1368 0 : this->OutdoorAirFlowRate = 0.0;
1369 : }
1370 33 : }
1371 :
1372 0 : void HVACFourPipeBeam::reportTerminalUnit(EnergyPlusData &state)
1373 : {
1374 : // populate the predefined equipment summary report related to air terminals
1375 0 : auto &orp = state.dataOutRptPredefined;
1376 0 : auto &adu = state.dataDefineEquipment->AirDistUnit(this->aDUNum);
1377 0 : if (!state.dataSize->TermUnitFinalZoneSizing.empty()) {
1378 0 : auto &sizing = state.dataSize->TermUnitFinalZoneSizing(adu.TermUnitSizingNum);
1379 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlow, adu.Name, sizing.DesCoolVolFlowMin);
1380 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOutdoorFlow, adu.Name, sizing.MinOA);
1381 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupCoolingSP, adu.Name, sizing.CoolDesTemp);
1382 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSupHeatingSP, adu.Name, sizing.HeatDesTemp);
1383 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatingCap, adu.Name, sizing.DesHeatLoad);
1384 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolingCap, adu.Name, sizing.DesCoolLoad);
1385 : }
1386 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermTypeInp, adu.Name, "AirTerminal:SingleDuct:ConstantVolume:FourPipeBeam");
1387 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermPrimFlow, adu.Name, this->vDotNormRatedPrimAir);
1388 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermSecdFlow, adu.Name, "n/a");
1389 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinFlowSch, adu.Name, "n/a");
1390 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMaxFlowReh, adu.Name, "n/a");
1391 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermMinOAflowSch, adu.Name, "n/a");
1392 0 : if (this->beamHeatingPresent) {
1393 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, "Included");
1394 : } else {
1395 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermHeatCoilType, adu.Name, "None");
1396 : }
1397 :
1398 0 : if (this->beamCoolingPresent) {
1399 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "Included");
1400 : } else {
1401 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermCoolCoilType, adu.Name, "None");
1402 : }
1403 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanType, adu.Name, "n/a");
1404 0 : OutputReportPredefined::PreDefTableEntry(state, orp->pdchAirTermFanName, adu.Name, "n/a");
1405 0 : }
1406 :
1407 : } // namespace FourPipeBeam
1408 :
1409 : } // namespace EnergyPlus
|