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 <string>
50 :
51 : // EnergyPlus Headers
52 : #include <EnergyPlus/AirLoopHVACDOAS.hh>
53 : #include <EnergyPlus/Autosizing/Base.hh>
54 : #include <EnergyPlus/BranchNodeConnections.hh>
55 : #include <EnergyPlus/Data/EnergyPlusData.hh>
56 : #include <EnergyPlus/DataAirLoop.hh>
57 : #include <EnergyPlus/DataAirSystems.hh>
58 : #include <EnergyPlus/DataEnvironment.hh>
59 : #include <EnergyPlus/DataLoopNode.hh>
60 : #include <EnergyPlus/DataSizing.hh>
61 : #include <EnergyPlus/DesiccantDehumidifiers.hh>
62 : #include <EnergyPlus/EvaporativeCoolers.hh>
63 : #include <EnergyPlus/Fans.hh>
64 : #include <EnergyPlus/FluidProperties.hh>
65 : #include <EnergyPlus/HVACDXHeatPumpSystem.hh>
66 : #include <EnergyPlus/HVACHXAssistedCoolingCoil.hh>
67 : #include <EnergyPlus/HVACVariableRefrigerantFlow.hh>
68 : #include <EnergyPlus/HeatRecovery.hh>
69 : #include <EnergyPlus/HeatingCoils.hh>
70 : #include <EnergyPlus/Humidifiers.hh>
71 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
72 : #include <EnergyPlus/MixedAir.hh>
73 : #include <EnergyPlus/NodeInputManager.hh>
74 : #include <EnergyPlus/OutAirNodeManager.hh>
75 : #include <EnergyPlus/PhotovoltaicThermalCollectors.hh>
76 : #include <EnergyPlus/PlantUtilities.hh>
77 : #include <EnergyPlus/Psychrometrics.hh>
78 : #include <EnergyPlus/ScheduleManager.hh>
79 : #include <EnergyPlus/SimAirServingZones.hh>
80 : #include <EnergyPlus/SteamCoils.hh>
81 : #include <EnergyPlus/TranspiredCollector.hh>
82 : #include <EnergyPlus/UnitarySystem.hh>
83 : #include <EnergyPlus/UtilityRoutines.hh>
84 : #include <EnergyPlus/WaterCoils.hh>
85 : #include <EnergyPlus/WeatherManager.hh>
86 :
87 : namespace EnergyPlus {
88 :
89 : namespace AirLoopHVACDOAS {
90 :
91 : // the equipment list object has its own subset of E+ components that are valid, this covers that list
92 : enum class ValidEquipListType
93 : {
94 : Invalid = -1,
95 : OutdoorAirMixer,
96 : FanConstantVolume,
97 : FanVariableVolume,
98 : FanSystemModel,
99 : FanComponentModel,
100 : CoilCoolingWater,
101 : CoilHeatingWater,
102 : CoilHeatingSteam,
103 : CoilCoolingWaterDetailedGeometry,
104 : CoilHeatingElectric,
105 : CoilHeatingFuel,
106 : CoilSystemCoolingWaterHeatExchangerAssisted,
107 : CoilSystemCoolingDX,
108 : CoilSystemHeatingDX,
109 : AirLoopHVACUnitarySystem,
110 : CoilUserDefined,
111 : HeatExchangerAirToAirFlatPlate,
112 : HeatExchangerAirToAirSensibleAndLatent,
113 : HeatExchangerDesiccantBalancedFlow,
114 : DehumidifierDesiccantNoFans,
115 : DehumidifierDesiccantSystem,
116 : HumidifierSteamElectric,
117 : HumidifierSteamGas,
118 : SolarCollectorUnglazedTranspired,
119 : SolarCollectorFlatPlatePhotovoltaicThermal,
120 : EvaporativeCoolerDirectCeldekPad,
121 : EvaporativeCoolerIndirectCeldekPad,
122 : EvaporativeCoolerIndirectWetCoil,
123 : EvaporativeCoolerIndirectResearchSpecial,
124 : EvaporativeCoolerDirectResearchSpecial,
125 : ZoneHVACTerminalUnitVariableRefrigerantFlow,
126 : Num
127 : };
128 : constexpr std::array<std::string_view, static_cast<int>(ValidEquipListType::Num)> validEquipNamesUC = {
129 : "OUTDOORAIR:MIXER",
130 : "FAN:CONSTANTVOLUME",
131 : "FAN:VARIABLEVOLUME",
132 : "FAN:SYSTEMMODEL",
133 : "FAN:COMPONENTMODEL",
134 : "COIL:COOLING:WATER",
135 : "COIL:HEATING:WATER",
136 : "COIL:HEATING:STEAM",
137 : "COIL:COOLING:WATER:DETAILEDGEOMETRY",
138 : "COIL:HEATING:ELECTRIC",
139 : "COIL:HEATING:FUEL",
140 : "COILSYSTEM:COOLING:WATER:HEATEXCHANGERASSISTED",
141 : "COILSYSTEM:COOLING:DX",
142 : "COILSYSTEM:HEATING:DX",
143 : "AIRLOOPHVAC:UNITARYSYSTEM",
144 : "COIL:USERDEFINED",
145 : "HEATEXCHANGER:AIRTOAIR:FLATPLATE",
146 : "HEATEXCHANGER:AIRTOAIR:SENSIBLEANDLATENT",
147 : "HEATEXCHANGER:DESICCANT:BALANCEDFLOW",
148 : "DEHUMIDIFIER:DESICCANT:NOFANS",
149 : "DEHUMIDIFIER:DESICCANT:SYSTEM",
150 : "HUMIDIFIER:STEAM:ELECTRIC",
151 : "HUMIDIFIER:STEAM:GAS",
152 : "SOLARCOLLECTOR:UNGLAZEDTRANSPIRED",
153 : "SOLARCOLLECTOR:FLATPLATE:PHOTOVOLTAICTHERMAL",
154 : "EVAPORATIVECOOLER:DIRECT:CELDEKPAD",
155 : "EVAPORATIVECOOLER:INDIRECT:CELDEKPAD",
156 : "EVAPORATIVECOOLER:INDIRECT:WETCOIL",
157 : "EVAPORATIVECOOLER:INDIRECT:RESEARCHSPECIAL",
158 : "EVAPORATIVECOOLER:DIRECT:RESEARCHSPECIAL",
159 : "ZONEHVAC:TERMINALUNIT:VARIABLEREFRIGERANTFLOW",
160 : };
161 :
162 14568 : void AirLoopDOAS::SimAirLoopHVACDOAS(EnergyPlusData &state, bool const FirstHVACIteration, int &CompIndex)
163 : {
164 :
165 : // Obtains and Allocates unitary system related parameters from input file
166 14568 : if (state.dataAirLoopHVACDOAS->GetInputOnceFlag) {
167 : // Get the AirLoopHVACDOAS input
168 0 : getAirLoopDOASInput(state);
169 0 : state.dataAirLoopHVACDOAS->GetInputOnceFlag = false;
170 : }
171 :
172 14568 : if (CompIndex == -1) {
173 0 : CompIndex = this->m_AirLoopDOASNum;
174 : }
175 :
176 14568 : if (this->SizingOnceFlag) {
177 2 : this->SizingAirLoopDOAS(state);
178 2 : this->SizingOnceFlag = false;
179 : }
180 :
181 14568 : this->initAirLoopDOAS(state, FirstHVACIteration);
182 :
183 14568 : if (this->SumMassFlowRate == 0.0 && !state.dataGlobal->BeginEnvrnFlag) {
184 14529 : state.dataLoopNodes->Node(this->m_CompPointerAirLoopMixer->OutletNodeNum).MassFlowRate = 0.0;
185 : }
186 :
187 14568 : this->CalcAirLoopDOAS(state, FirstHVACIteration);
188 14568 : }
189 :
190 7 : AirLoopMixer *AirLoopMixer::factory(EnergyPlusData &state, int object_num, std::string const &objectName)
191 : {
192 :
193 7 : if (state.dataAirLoopHVACDOAS->getAirLoopMixerInputOnceFlag) {
194 0 : AirLoopMixer::getAirLoopMixer(state);
195 0 : state.dataAirLoopHVACDOAS->getAirLoopMixerInputOnceFlag = false;
196 : }
197 :
198 7 : int MixerNum = -1;
199 7 : for (auto &dSpec : state.dataAirLoopHVACDOAS->airloopMixer) {
200 7 : ++MixerNum;
201 7 : if (Util::SameString(dSpec.name, objectName) && dSpec.m_AirLoopMixer_Num == object_num) {
202 7 : return &dSpec;
203 : }
204 : }
205 :
206 0 : ShowSevereError(state, format("AirLoopMixer factory: Error getting inputs for system named: {}", objectName));
207 0 : return nullptr;
208 : }
209 :
210 7 : void AirLoopMixer::getAirLoopMixer(EnergyPlusData &state)
211 : {
212 :
213 7 : std::string const cCurrentModuleObject = "AirLoopHVAC:Mixer";
214 :
215 7 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
216 7 : if (instances != state.dataInputProcessing->inputProcessor->epJSON.end()) {
217 7 : bool errorsFound(false);
218 7 : std::string cFieldName;
219 7 : int AirLoopMixerNum = 0;
220 7 : auto &instancesValue = instances.value();
221 14 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
222 :
223 7 : auto const &fields = instance.value();
224 7 : std::string const &thisObjectName = instance.key();
225 7 : state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
226 7 : ++AirLoopMixerNum;
227 7 : AirLoopMixer thisMixer;
228 :
229 7 : thisMixer.name = Util::makeUPPER(thisObjectName);
230 7 : thisMixer.OutletNodeName = Util::makeUPPER(fields.at("outlet_node_name").get<std::string>());
231 7 : thisMixer.m_AirLoopMixer_Num = AirLoopMixerNum - 1;
232 7 : thisMixer.OutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
233 : thisMixer.OutletNodeName,
234 : errorsFound,
235 : DataLoopNode::ConnectionObjectType::AirLoopHVACMixer,
236 : thisObjectName,
237 : DataLoopNode::NodeFluidType::Air,
238 : DataLoopNode::ConnectionType::Outlet,
239 : NodeInputManager::CompFluidStream::Primary,
240 : DataLoopNode::ObjectIsParent);
241 :
242 7 : auto NodeNames = fields.find("nodes");
243 7 : if (NodeNames != fields.end()) {
244 7 : auto const &NodeArray = NodeNames.value();
245 7 : thisMixer.numOfInletNodes = NodeArray.size();
246 7 : int num = 0;
247 34 : for (auto const &NodeDOASName : NodeArray) {
248 27 : num += 1;
249 27 : std::string name = Util::makeUPPER(NodeDOASName.at("inlet_node_name").get<std::string>());
250 27 : int NodeNum = NodeInputManager::GetOnlySingleNode(state,
251 : name,
252 : errorsFound,
253 : DataLoopNode::ConnectionObjectType::AirLoopHVACMixer,
254 : thisObjectName,
255 : DataLoopNode::NodeFluidType::Air,
256 : DataLoopNode::ConnectionType::Inlet,
257 : NodeInputManager::CompFluidStream::Primary,
258 27 : DataLoopNode::ObjectIsParent);
259 27 : if (NodeNum > 0 && num <= thisMixer.numOfInletNodes) {
260 27 : thisMixer.InletNodeName.push_back(name);
261 27 : thisMixer.InletNodeNum.push_back(NodeNum);
262 : } else {
263 0 : cFieldName = "Inlet Node Name";
264 0 : ShowSevereError(state, format("{}, \"{}\" {} not found: {}", cCurrentModuleObject, thisMixer.name, name, cFieldName));
265 0 : errorsFound = true;
266 : }
267 27 : }
268 : }
269 :
270 7 : state.dataAirLoopHVACDOAS->airloopMixer.push_back(thisMixer);
271 7 : }
272 7 : if (errorsFound) {
273 0 : ShowFatalError(state, "getAirLoopMixer: Previous errors cause termination.");
274 : }
275 7 : }
276 7 : } // namespace AirLoopMixer
277 :
278 14568 : void AirLoopMixer::CalcAirLoopMixer(EnergyPlusData &state)
279 : {
280 14568 : Real64 outletTemp = 0.0;
281 14568 : Real64 outletHumRat = 0.0;
282 14568 : Real64 massSum = 0.0;
283 :
284 29148 : for (int i = 1; i <= this->numOfInletNodes; i++) {
285 14580 : int InletNum = this->InletNodeNum[i - 1];
286 14580 : massSum += state.dataLoopNodes->Node(InletNum).MassFlowRate;
287 14580 : outletTemp += state.dataLoopNodes->Node(InletNum).MassFlowRate * state.dataLoopNodes->Node(InletNum).Temp;
288 14580 : outletHumRat += state.dataLoopNodes->Node(InletNum).MassFlowRate * state.dataLoopNodes->Node(InletNum).HumRat;
289 : }
290 14568 : if (massSum > 0.0) {
291 3 : state.dataLoopNodes->Node(this->OutletNodeNum).Temp = outletTemp / massSum;
292 3 : state.dataLoopNodes->Node(this->OutletNodeNum).HumRat = outletHumRat / massSum;
293 3 : state.dataLoopNodes->Node(this->OutletNodeNum).MassFlowRate = massSum;
294 3 : state.dataLoopNodes->Node(this->OutletNodeNum).Enthalpy = Psychrometrics::PsyHFnTdbW(outletTemp / massSum, outletHumRat / massSum);
295 3 : this->OutletTemp = state.dataLoopNodes->Node(this->OutletNodeNum).Temp;
296 : } else {
297 14565 : state.dataLoopNodes->Node(this->OutletNodeNum).Temp = state.dataLoopNodes->Node(this->InletNodeNum[0]).Temp;
298 14565 : state.dataLoopNodes->Node(this->OutletNodeNum).HumRat = state.dataLoopNodes->Node(this->InletNodeNum[0]).HumRat;
299 14565 : state.dataLoopNodes->Node(this->OutletNodeNum).MassFlowRate = 0.0;
300 14565 : state.dataLoopNodes->Node(this->OutletNodeNum).Enthalpy = state.dataLoopNodes->Node(this->InletNodeNum[0]).Enthalpy;
301 14565 : this->OutletTemp = state.dataLoopNodes->Node(this->InletNodeNum[0]).Temp;
302 : }
303 14568 : }
304 :
305 7 : int getAirLoopMixerIndex(EnergyPlusData &state, std::string const &objectName)
306 : {
307 7 : if (state.dataAirLoopHVACDOAS->getAirLoopMixerInputOnceFlag) {
308 7 : AirLoopMixer::getAirLoopMixer(state);
309 7 : state.dataAirLoopHVACDOAS->getAirLoopMixerInputOnceFlag = false;
310 : }
311 :
312 7 : int index = -1;
313 7 : for (std::size_t loop = 0; loop < state.dataAirLoopHVACDOAS->airloopMixer.size(); ++loop) {
314 7 : AirLoopMixer *thisAirLoopMixerObjec = &state.dataAirLoopHVACDOAS->airloopMixer[loop];
315 7 : if (Util::SameString(objectName, thisAirLoopMixerObjec->name)) {
316 7 : index = loop;
317 7 : return index;
318 : }
319 : }
320 0 : ShowSevereError(state, format("getAirLoopMixer: did not find AirLoopHVAC:Mixer name ={}. Check inputs", objectName));
321 0 : return index;
322 : }
323 :
324 7 : AirLoopSplitter *AirLoopSplitter::factory(EnergyPlusData &state, int object_num, std::string const &objectName)
325 : {
326 :
327 7 : if (state.dataAirLoopHVACDOAS->getAirLoopSplitterInputOnceFlag) {
328 0 : AirLoopSplitter::getAirLoopSplitter(state);
329 0 : state.dataAirLoopHVACDOAS->getAirLoopSplitterInputOnceFlag = false;
330 : }
331 :
332 7 : int SplitterNum = -1;
333 7 : for (auto &dSpec : state.dataAirLoopHVACDOAS->airloopSplitter) {
334 7 : SplitterNum++;
335 7 : if (Util::SameString(dSpec.name, objectName) && dSpec.m_AirLoopSplitter_Num == object_num) {
336 7 : return &dSpec;
337 : }
338 : }
339 0 : ShowSevereError(state, format("AirLoopSplitter factory: Error getting inputs for system named: {}", objectName));
340 0 : return nullptr;
341 : }
342 :
343 14568 : void AirLoopSplitter::CalcAirLoopSplitter(EnergyPlusData &state, Real64 Temp, Real64 HumRat)
344 : {
345 29148 : for (int i = 0; i < this->numOfOutletNodes; i++) {
346 14580 : state.dataLoopNodes->Node(this->OutletNodeNum[i]).Temp = Temp;
347 14580 : state.dataLoopNodes->Node(this->OutletNodeNum[i]).HumRat = HumRat;
348 14580 : state.dataLoopNodes->Node(this->OutletNodeNum[i]).Enthalpy = Psychrometrics::PsyHFnTdbW(Temp, HumRat);
349 : }
350 14568 : this->InletTemp = Temp;
351 14568 : }
352 :
353 7 : int getAirLoopSplitterIndex(EnergyPlusData &state, std::string const &objectName)
354 : {
355 7 : if (state.dataAirLoopHVACDOAS->getAirLoopSplitterInputOnceFlag) {
356 7 : AirLoopSplitter::getAirLoopSplitter(state);
357 7 : state.dataAirLoopHVACDOAS->getAirLoopSplitterInputOnceFlag = false;
358 : }
359 :
360 7 : int index = -1;
361 7 : for (std::size_t loop = 0; loop < state.dataAirLoopHVACDOAS->airloopSplitter.size(); ++loop) {
362 7 : AirLoopSplitter *thisAirLoopSplitterObjec = &state.dataAirLoopHVACDOAS->airloopSplitter[loop];
363 7 : if (Util::SameString(objectName, thisAirLoopSplitterObjec->name)) {
364 7 : index = loop;
365 7 : return index;
366 : }
367 : }
368 0 : ShowSevereError(state, format("getAirLoopSplitter: did not find AirLoopSplitter name ={}. Check inputs", objectName));
369 0 : return index;
370 : }
371 :
372 7 : void AirLoopSplitter::getAirLoopSplitter(EnergyPlusData &state)
373 : {
374 :
375 7 : std::string const cCurrentModuleObject = "AirLoopHVAC:Splitter";
376 :
377 7 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
378 7 : if (instances != state.dataInputProcessing->inputProcessor->epJSON.end()) {
379 7 : bool errorsFound(false);
380 7 : std::string cFieldName;
381 7 : int AirLoopSplitterNum = 0;
382 7 : auto &instancesValue = instances.value();
383 14 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
384 :
385 7 : auto const &fields = instance.value();
386 7 : std::string const &thisObjectName = instance.key();
387 7 : state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
388 :
389 7 : ++AirLoopSplitterNum;
390 7 : AirLoopSplitter thisSplitter;
391 :
392 7 : thisSplitter.name = Util::makeUPPER(thisObjectName);
393 7 : thisSplitter.InletNodeName = Util::makeUPPER(fields.at("inlet_node_name").get<std::string>());
394 7 : thisSplitter.InletNodeNum = NodeInputManager::GetOnlySingleNode(state,
395 : thisSplitter.InletNodeName,
396 : errorsFound,
397 : DataLoopNode::ConnectionObjectType::AirLoopHVACSplitter,
398 : thisObjectName,
399 : DataLoopNode::NodeFluidType::Air,
400 : DataLoopNode::ConnectionType::Inlet,
401 : NodeInputManager::CompFluidStream::Primary,
402 : DataLoopNode::ObjectIsParent);
403 7 : thisSplitter.m_AirLoopSplitter_Num = AirLoopSplitterNum - 1;
404 :
405 7 : auto NodeNames = fields.find("nodes");
406 7 : if (NodeNames != fields.end()) {
407 7 : auto const &NodeArray = NodeNames.value();
408 7 : thisSplitter.numOfOutletNodes = NodeArray.size();
409 7 : int num = 0;
410 34 : for (auto const &NodeDOASName : NodeArray) {
411 27 : num += 1;
412 :
413 27 : std::string name = Util::makeUPPER(NodeDOASName.at("outlet_node_name").get<std::string>());
414 27 : int NodeNum = NodeInputManager::GetOnlySingleNode(state,
415 : name,
416 : errorsFound,
417 : DataLoopNode::ConnectionObjectType::AirLoopHVACSplitter,
418 : thisObjectName,
419 : DataLoopNode::NodeFluidType::Air,
420 : DataLoopNode::ConnectionType::Inlet,
421 : NodeInputManager::CompFluidStream::Primary,
422 27 : DataLoopNode::ObjectIsParent);
423 27 : if (NodeNum > 0 && num <= thisSplitter.numOfOutletNodes) {
424 27 : thisSplitter.OutletNodeName.push_back(name);
425 27 : thisSplitter.OutletNodeNum.push_back(NodeNum);
426 : } else {
427 0 : cFieldName = "Outlet Node Name";
428 0 : ShowSevereError(state, format("{}, \"{}\"{} not found: {}", cCurrentModuleObject, thisSplitter.name, cFieldName, name));
429 0 : errorsFound = true;
430 : }
431 27 : }
432 : }
433 :
434 7 : state.dataAirLoopHVACDOAS->airloopSplitter.push_back(thisSplitter);
435 7 : }
436 7 : if (errorsFound) {
437 0 : ShowFatalError(state, "getAirLoopSplitter: Previous errors cause termination.");
438 : }
439 7 : }
440 7 : } // namespace AirLoopSplitter
441 :
442 7 : void AirLoopDOAS::getAirLoopDOASInput(EnergyPlusData &state)
443 : {
444 7 : constexpr std::string_view routineName = "AirLoopDOAS::getAirLoopDOASInput";
445 7 : std::string const cCurrentModuleObject = "AirLoopHVAC:DedicatedOutdoorAirSystem";
446 :
447 7 : auto const instances = state.dataInputProcessing->inputProcessor->epJSON.find(cCurrentModuleObject);
448 7 : if (instances != state.dataInputProcessing->inputProcessor->epJSON.end()) {
449 7 : bool errorsFound(false);
450 7 : std::string cFieldName;
451 7 : int AirLoopDOASNum = 0;
452 7 : auto &instancesValue = instances.value();
453 14 : for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
454 :
455 7 : auto const &fields = instance.value();
456 7 : std::string const &thisObjectName = instance.key();
457 7 : state.dataInputProcessing->inputProcessor->markObjectAsUsed(cCurrentModuleObject, thisObjectName);
458 7 : ++AirLoopDOASNum;
459 7 : AirLoopDOAS thisDOAS;
460 :
461 7 : ErrorObjectHeader eoh{routineName, cCurrentModuleObject, thisObjectName};
462 :
463 7 : thisDOAS.Name = Util::makeUPPER(thisObjectName);
464 : // get OA and avail num
465 7 : thisDOAS.OASystemName = Util::makeUPPER(fields.at("airloophvac_outdoorairsystem_name").get<std::string>());
466 7 : thisDOAS.m_OASystemNum = Util::FindItemInList(thisDOAS.OASystemName, state.dataAirLoop->OutsideAirSys);
467 :
468 7 : if (thisDOAS.m_OASystemNum == 0) {
469 0 : cFieldName = "AirLoopHVAC:OutdoorAirSystem Name";
470 0 : ShowSevereError(state,
471 0 : format("{}, \"{}\", {} not found: {}\n", cCurrentModuleObject, thisDOAS.Name, cFieldName, thisDOAS.OASystemName));
472 0 : errorsFound = true;
473 : }
474 : // Check controller type
475 7 : std::string_view CurrentModuleObject = "AirLoopHVAC:OutdoorAirSystem";
476 7 : auto &thisOutsideAirSys = state.dataAirLoop->OutsideAirSys(thisDOAS.m_OASystemNum);
477 13 : for (int InListNum = 1; InListNum <= thisOutsideAirSys.NumControllers; ++InListNum) {
478 6 : if (Util::SameString(thisOutsideAirSys.ControllerType(InListNum), "Controller:OutdoorAir")) {
479 0 : ShowSevereError(state,
480 0 : format("When {} = {} is used in AirLoopHVAC:DedicatedOutdoorAirSystem,",
481 : CurrentModuleObject,
482 : thisOutsideAirSys.ControllerName(InListNum)));
483 0 : ShowContinueError(state, "The Controller:OutdoorAir can not be used as a controller. Please remove it");
484 0 : errorsFound = true;
485 : }
486 : }
487 :
488 : // get inlet and outlet node number from equipment list
489 7 : CurrentModuleObject = "AirLoopHVAC:OutdoorAirSystem:EquipmentList";
490 7 : int CoolingCoilOrder = 0;
491 7 : int FanOrder = 0;
492 25 : for (int CompNum = 1; CompNum <= thisOutsideAirSys.NumComponents; ++CompNum) {
493 18 : std::string &CompType = thisOutsideAirSys.ComponentType(CompNum);
494 18 : std::string &CompName = thisOutsideAirSys.ComponentName(CompNum);
495 :
496 18 : bool InletNodeErrFlag = false;
497 18 : bool OutletNodeErrFlag = false;
498 :
499 18 : const std::string typeNameUC = Util::makeUPPER(thisOutsideAirSys.ComponentType(CompNum));
500 18 : ValidEquipListType foundType = static_cast<ValidEquipListType>(getEnumValue(validEquipNamesUC, typeNameUC));
501 :
502 18 : switch (foundType) {
503 0 : case ValidEquipListType::OutdoorAirMixer:
504 0 : ShowSevereError(state,
505 0 : format("When {} = {} is used in AirLoopHVAC:DedicatedOutdoorAirSystem,", CurrentModuleObject, CompName));
506 0 : ShowContinueError(state, " the OUTDOORAIR:MIXER can not be used as a component. Please remove it");
507 0 : errorsFound = true;
508 0 : break;
509 :
510 0 : case ValidEquipListType::FanConstantVolume:
511 0 : ShowSevereError(state,
512 0 : format("When {} = {} is used in AirLoopHVAC:DedicatedOutdoorAirSystem,", CurrentModuleObject, CompName));
513 0 : ShowContinueError(state,
514 : " the FAN:CONSTANTVOLUME can not be used as a component. The allowed fan types are FAN:SYSTEMMODEL and "
515 : "FAN:COMPONENTMODEL. Please change it");
516 0 : errorsFound = true;
517 0 : break;
518 :
519 0 : case ValidEquipListType::FanVariableVolume:
520 0 : ShowSevereError(state,
521 0 : format("When {} = {} is used in AirLoopHVAC:DedicatedOutdoorAirSystem,", CurrentModuleObject, CompName));
522 0 : ShowContinueError(state,
523 : " the FAN:VARIABLEVOLUME can not be used as a component. The allowed fan types are FAN:SYSTEMMODEL and "
524 : "FAN:COMPONENTMODEL. Please change it");
525 0 : errorsFound = true;
526 0 : break;
527 :
528 5 : case ValidEquipListType::FanSystemModel:
529 5 : thisDOAS.FanName = CompName;
530 5 : thisDOAS.m_FanTypeNum = SimAirServingZones::CompType::Fan_System_Object;
531 5 : thisDOAS.m_FanIndex = Fans::GetFanIndex(state, CompName);
532 5 : thisOutsideAirSys.InletNodeNum(CompNum) = state.dataFans->fans(thisDOAS.m_FanIndex)->inletNodeNum;
533 5 : if (thisOutsideAirSys.InletNodeNum(CompNum) == 0) {
534 0 : InletNodeErrFlag = true;
535 : }
536 5 : thisOutsideAirSys.OutletNodeNum(CompNum) = state.dataFans->fans(thisDOAS.m_FanIndex)->outletNodeNum;
537 5 : if (thisOutsideAirSys.OutletNodeNum(CompNum) == 0) {
538 0 : OutletNodeErrFlag = true;
539 : }
540 5 : thisDOAS.m_FanInletNodeNum = thisOutsideAirSys.InletNodeNum(CompNum);
541 5 : thisDOAS.m_FanOutletNodeNum = thisOutsideAirSys.OutletNodeNum(CompNum);
542 5 : if (CompNum == 1) {
543 2 : thisDOAS.FanBeforeCoolingCoilFlag = true;
544 : }
545 5 : FanOrder = CompNum;
546 5 : break;
547 :
548 0 : case ValidEquipListType::FanComponentModel:
549 0 : thisDOAS.m_FanTypeNum = SimAirServingZones::CompType::Fan_ComponentModel;
550 0 : thisDOAS.m_FanIndex = Fans::GetFanIndex(state, CompName);
551 0 : thisDOAS.FanName = CompName;
552 0 : if (CompNum == 1) {
553 0 : thisDOAS.FanBeforeCoolingCoilFlag = true;
554 : }
555 0 : thisOutsideAirSys.InletNodeNum(CompNum) = state.dataFans->fans(thisDOAS.m_FanIndex)->inletNodeNum;
556 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = state.dataFans->fans(thisDOAS.m_FanIndex)->outletNodeNum;
557 0 : thisDOAS.m_FanInletNodeNum = thisOutsideAirSys.InletNodeNum(CompNum);
558 0 : thisDOAS.m_FanOutletNodeNum = thisOutsideAirSys.OutletNodeNum(CompNum);
559 0 : FanOrder = CompNum;
560 0 : break;
561 :
562 0 : case ValidEquipListType::CoilCoolingWater:
563 0 : thisOutsideAirSys.InletNodeNum(CompNum) = WaterCoils::GetCoilInletNode(state, typeNameUC, CompName, InletNodeErrFlag);
564 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = WaterCoils::GetCoilOutletNode(state, typeNameUC, CompName, OutletNodeErrFlag);
565 0 : thisDOAS.CWCtrlNodeNum = WaterCoils::GetCoilWaterInletNode(state, "COIL:COOLING:WATER", CompName, errorsFound);
566 0 : if (errorsFound) {
567 0 : ShowContinueError(state, format("The control node number is not found in {} = {}", CurrentModuleObject, CompName));
568 : }
569 0 : PlantUtilities::ScanPlantLoopsForObject(
570 : state, CompName, DataPlant::PlantEquipmentType::CoilWaterCooling, thisDOAS.CWPlantLoc, errorsFound, _, _, _, _, _);
571 0 : if (errorsFound) { // is this really needed here, program fatals out later on when errorsFound = true
572 0 : ShowFatalError(state, "GetAirLoopDOASInput: Program terminated for previous conditions.");
573 : }
574 0 : CoolingCoilOrder = CompNum;
575 0 : break;
576 :
577 0 : case ValidEquipListType::CoilHeatingWater:
578 0 : thisOutsideAirSys.InletNodeNum(CompNum) = WaterCoils::GetCoilInletNode(state, typeNameUC, CompName, InletNodeErrFlag);
579 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = WaterCoils::GetCoilOutletNode(state, typeNameUC, CompName, OutletNodeErrFlag);
580 0 : thisDOAS.HWCtrlNodeNum = WaterCoils::GetCoilWaterInletNode(state, "Coil:Heating:Water", CompName, errorsFound);
581 0 : if (errorsFound) {
582 0 : ShowContinueError(state, format("The control node number is not found in {} = {}", CurrentModuleObject, CompName));
583 : }
584 0 : PlantUtilities::ScanPlantLoopsForObject(
585 : state, CompName, DataPlant::PlantEquipmentType::CoilWaterSimpleHeating, thisDOAS.HWPlantLoc, errorsFound, _, _, _, _, _);
586 0 : if (errorsFound) { // is this really needed here, program fatals out later on when errorsFound = true
587 0 : ShowFatalError(state, "GetAirLoopDOASInput: Program terminated for previous conditions.");
588 : }
589 0 : break;
590 :
591 0 : case ValidEquipListType::CoilHeatingSteam:
592 0 : thisOutsideAirSys.InletNodeNum(CompNum) = SteamCoils::GetCoilSteamInletNode(state, CompType, CompName, InletNodeErrFlag);
593 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = SteamCoils::GetCoilSteamOutletNode(state, CompType, CompName, OutletNodeErrFlag);
594 0 : break;
595 :
596 0 : case ValidEquipListType::CoilCoolingWaterDetailedGeometry:
597 0 : thisOutsideAirSys.InletNodeNum(CompNum) = WaterCoils::GetCoilInletNode(state, typeNameUC, CompName, InletNodeErrFlag);
598 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = WaterCoils::GetCoilOutletNode(state, typeNameUC, CompName, OutletNodeErrFlag);
599 0 : thisDOAS.CWCtrlNodeNum =
600 0 : WaterCoils::GetCoilWaterInletNode(state, "Coil:Cooling:Water:DetailedGeometry", CompName, errorsFound);
601 0 : if (errorsFound) {
602 0 : ShowContinueError(state, format("The control node number is not found in {} = {}", CurrentModuleObject, CompName));
603 : }
604 0 : PlantUtilities::ScanPlantLoopsForObject(state,
605 : CompName,
606 : DataPlant::PlantEquipmentType::CoilWaterDetailedFlatCooling,
607 : thisDOAS.CWPlantLoc,
608 : errorsFound,
609 : _,
610 : _,
611 : _,
612 : _,
613 : _);
614 0 : if (errorsFound) { // is this really needed here, program fatals out later on when errorsFound = true
615 0 : ShowFatalError(state, "GetAirLoopDOASInput: Program terminated for previous conditions.");
616 : }
617 0 : CoolingCoilOrder = CompNum;
618 0 : break;
619 :
620 4 : case ValidEquipListType::CoilHeatingElectric:
621 : case ValidEquipListType::CoilHeatingFuel:
622 4 : thisOutsideAirSys.InletNodeNum(CompNum) = HeatingCoils::GetCoilInletNode(state, typeNameUC, CompName, InletNodeErrFlag);
623 4 : thisOutsideAirSys.OutletNodeNum(CompNum) = HeatingCoils::GetCoilOutletNode(state, typeNameUC, CompName, OutletNodeErrFlag);
624 4 : break;
625 :
626 0 : case ValidEquipListType::CoilSystemCoolingWaterHeatExchangerAssisted:
627 0 : thisOutsideAirSys.InletNodeNum(CompNum) =
628 0 : HVACHXAssistedCoolingCoil::GetCoilInletNode(state, CompType, CompName, InletNodeErrFlag);
629 0 : thisOutsideAirSys.OutletNodeNum(CompNum) =
630 0 : HVACHXAssistedCoolingCoil::GetCoilOutletNode(state, CompType, CompName, OutletNodeErrFlag);
631 0 : break;
632 :
633 4 : case ValidEquipListType::CoilSystemCoolingDX:
634 : case ValidEquipListType::AirLoopHVACUnitarySystem:
635 4 : if (thisOutsideAirSys.compPointer[CompNum] == nullptr) {
636 0 : UnitarySystems::UnitarySys thisSys;
637 0 : thisOutsideAirSys.compPointer[CompNum] =
638 0 : UnitarySystems::UnitarySys::factory(state, HVAC::UnitarySysType::Unitary_AnyCoilType, CompName, false, 0);
639 0 : }
640 8 : thisOutsideAirSys.InletNodeNum(CompNum) =
641 4 : thisOutsideAirSys.compPointer[CompNum]->getAirInNode(state, CompName, 0, InletNodeErrFlag);
642 8 : thisOutsideAirSys.OutletNodeNum(CompNum) =
643 4 : thisOutsideAirSys.compPointer[CompNum]->getAirOutNode(state, CompName, 0, OutletNodeErrFlag);
644 4 : CoolingCoilOrder = CompNum;
645 4 : break;
646 :
647 0 : case ValidEquipListType::CoilSystemHeatingDX:
648 0 : thisOutsideAirSys.InletNodeNum(CompNum) = HVACDXHeatPumpSystem::GetHeatingCoilInletNodeNum(state, CompName, InletNodeErrFlag);
649 0 : thisOutsideAirSys.OutletNodeNum(CompNum) =
650 0 : HVACDXHeatPumpSystem::GetHeatingCoilOutletNodeNum(state, CompName, OutletNodeErrFlag);
651 0 : break;
652 :
653 0 : case ValidEquipListType::CoilUserDefined:
654 0 : ShowSevereError(state,
655 0 : format("When {} = {} is used in AirLoopHVAC:DedicatedOutdoorAirSystem,", CurrentModuleObject, CompName));
656 0 : ShowContinueError(state, " the COIL:USERDEFINED can not be used as a component.");
657 0 : errorsFound = true;
658 0 : break;
659 :
660 2 : case ValidEquipListType::HeatExchangerAirToAirFlatPlate:
661 : case ValidEquipListType::HeatExchangerAirToAirSensibleAndLatent:
662 : case ValidEquipListType::HeatExchangerDesiccantBalancedFlow:
663 2 : thisOutsideAirSys.HeatExchangerFlag = true;
664 2 : thisOutsideAirSys.InletNodeNum(CompNum) = HeatRecovery::GetSupplyInletNode(state, CompName, InletNodeErrFlag);
665 2 : thisOutsideAirSys.OutletNodeNum(CompNum) = HeatRecovery::GetSupplyOutletNode(state, CompName, OutletNodeErrFlag);
666 2 : break;
667 :
668 0 : case ValidEquipListType::DehumidifierDesiccantNoFans:
669 : case ValidEquipListType::DehumidifierDesiccantSystem:
670 0 : thisOutsideAirSys.InletNodeNum(CompNum) = DesiccantDehumidifiers::GetProcAirInletNodeNum(state, CompName, InletNodeErrFlag);
671 0 : thisOutsideAirSys.OutletNodeNum(CompNum) =
672 0 : DesiccantDehumidifiers::GetProcAirOutletNodeNum(state, CompName, OutletNodeErrFlag);
673 0 : break;
674 :
675 3 : case ValidEquipListType::HumidifierSteamElectric:
676 : case ValidEquipListType::HumidifierSteamGas:
677 3 : thisOutsideAirSys.InletNodeNum(CompNum) = Humidifiers::GetAirInletNodeNum(state, CompName, InletNodeErrFlag);
678 3 : thisOutsideAirSys.OutletNodeNum(CompNum) = Humidifiers::GetAirOutletNodeNum(state, CompName, OutletNodeErrFlag);
679 3 : break;
680 :
681 0 : case ValidEquipListType::SolarCollectorUnglazedTranspired:
682 0 : thisOutsideAirSys.InletNodeNum(CompNum) = TranspiredCollector::GetAirInletNodeNum(state, CompName, InletNodeErrFlag);
683 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = TranspiredCollector::GetAirOutletNodeNum(state, CompName, OutletNodeErrFlag);
684 0 : break;
685 :
686 0 : case ValidEquipListType::SolarCollectorFlatPlatePhotovoltaicThermal:
687 0 : thisOutsideAirSys.InletNodeNum(CompNum) =
688 0 : PhotovoltaicThermalCollectors::GetAirInletNodeNum(state, CompName, InletNodeErrFlag);
689 0 : thisOutsideAirSys.OutletNodeNum(CompNum) =
690 0 : PhotovoltaicThermalCollectors::GetAirOutletNodeNum(state, CompName, OutletNodeErrFlag);
691 0 : break;
692 :
693 0 : case ValidEquipListType::EvaporativeCoolerDirectCeldekPad:
694 : case ValidEquipListType::EvaporativeCoolerIndirectCeldekPad:
695 : case ValidEquipListType::EvaporativeCoolerIndirectWetCoil:
696 : case ValidEquipListType::EvaporativeCoolerIndirectResearchSpecial:
697 : case ValidEquipListType::EvaporativeCoolerDirectResearchSpecial:
698 0 : thisOutsideAirSys.InletNodeNum(CompNum) = EvaporativeCoolers::GetInletNodeNum(state, CompName, InletNodeErrFlag);
699 0 : thisOutsideAirSys.OutletNodeNum(CompNum) = EvaporativeCoolers::GetOutletNodeNum(state, CompName, OutletNodeErrFlag);
700 0 : break;
701 :
702 0 : case ValidEquipListType::ZoneHVACTerminalUnitVariableRefrigerantFlow:
703 0 : thisOutsideAirSys.InletNodeNum(CompNum) =
704 0 : HVACVariableRefrigerantFlow::GetVRFTUInAirNodeFromName(state, CompName, InletNodeErrFlag);
705 0 : thisOutsideAirSys.OutletNodeNum(CompNum) =
706 0 : HVACVariableRefrigerantFlow::GetVRFTUOutAirNodeFromName(state, CompName, OutletNodeErrFlag);
707 0 : break;
708 :
709 0 : default:
710 0 : ShowSevereError(state,
711 0 : format(R"({} = "{}" invalid Outside Air Component="{}".)",
712 : CurrentModuleObject,
713 : CompName,
714 : thisOutsideAirSys.ComponentType(CompNum)));
715 0 : errorsFound = true;
716 : }
717 18 : if (CoolingCoilOrder > FanOrder && !thisDOAS.FanBeforeCoolingCoilFlag) {
718 3 : thisDOAS.FanBeforeCoolingCoilFlag = true;
719 : }
720 18 : if (InletNodeErrFlag) {
721 0 : ShowSevereError(state, format("Inlet node number is not found in {} = {}", CurrentModuleObject, CompName));
722 0 : errorsFound = true;
723 : }
724 18 : if (OutletNodeErrFlag) {
725 0 : ShowSevereError(state, format("Outlet node number is not found in {} = {}", CurrentModuleObject, CompName));
726 0 : errorsFound = true;
727 : }
728 : // Check node connection to ensure that the outlet node of the previous component is the inlet node of the current component
729 18 : if (CompNum > 1) {
730 11 : if (thisOutsideAirSys.InletNodeNum(CompNum) != thisOutsideAirSys.OutletNodeNum(CompNum - 1)) {
731 2 : ShowSevereError(state,
732 2 : format("getAirLoopMixer: Node Connection Error in AirLoopHVAC:DedicatedOutdoorAirSystem = {}. Inlet node "
733 : "of {} as current component is not same as the outlet node of "
734 : "{} as previous component",
735 : thisDOAS.Name,
736 : thisOutsideAirSys.ComponentName(CompNum),
737 : thisOutsideAirSys.ComponentName(CompNum - 1)));
738 2 : ShowContinueError(state,
739 2 : format("The inlet node name = {}, and the outlet node name = {}.",
740 1 : state.dataLoopNodes->NodeID(thisOutsideAirSys.InletNodeNum(CompNum)),
741 1 : state.dataLoopNodes->NodeID(thisOutsideAirSys.OutletNodeNum(CompNum - 1))));
742 1 : errorsFound = true;
743 : }
744 : }
745 18 : }
746 :
747 7 : thisDOAS.m_InletNodeNum = thisOutsideAirSys.InletNodeNum(1);
748 7 : thisDOAS.m_OutletNodeNum = thisOutsideAirSys.OutletNodeNum(thisOutsideAirSys.NumComponents);
749 7 : thisOutsideAirSys.AirLoopDOASNum = AirLoopDOASNum - 1;
750 : // Set up parent-child connection
751 14 : BranchNodeConnections::SetUpCompSets(state,
752 : cCurrentModuleObject,
753 : thisDOAS.Name,
754 : "AIRLOOPHVAC:OUTDOORAIRSYSTEM",
755 : thisDOAS.OASystemName,
756 7 : state.dataLoopNodes->NodeID(thisDOAS.m_InletNodeNum),
757 7 : state.dataLoopNodes->NodeID(thisDOAS.m_OutletNodeNum));
758 :
759 7 : if (thisOutsideAirSys.HeatExchangerFlag) {
760 2 : thisDOAS.m_HeatExchangerFlag = true;
761 : }
762 :
763 7 : thisDOAS.AvailManagerSchedName = Util::makeUPPER(fields.at("availability_schedule_name").get<std::string>());
764 :
765 7 : if ((thisDOAS.m_AvailManagerSched = Sched::GetSchedule(state, thisDOAS.AvailManagerSchedName)) == nullptr) {
766 0 : ShowSevereItemNotFound(state, eoh, "Availability Schedule Name", thisDOAS.AvailManagerSchedName);
767 0 : errorsFound = true;
768 : }
769 :
770 7 : thisDOAS.AirLoopMixerName = Util::makeUPPER(fields.at("airloophvac_mixer_name").get<std::string>()); //
771 7 : thisDOAS.m_AirLoopMixerIndex = getAirLoopMixerIndex(state, thisDOAS.AirLoopMixerName);
772 7 : if (thisDOAS.m_AirLoopMixerIndex < 0) {
773 0 : cFieldName = "AirLoopHVAC:Mixer Name";
774 0 : ShowSevereError(
775 0 : state, format("{}, \"{}\" {} not found: {}", cCurrentModuleObject, thisDOAS.Name, cFieldName, thisDOAS.AirLoopMixerName));
776 0 : errorsFound = true;
777 : }
778 14 : AirLoopMixer thisAirLoopMixer;
779 7 : thisDOAS.m_CompPointerAirLoopMixer = thisAirLoopMixer.factory(state, thisDOAS.m_AirLoopMixerIndex, thisDOAS.AirLoopMixerName);
780 7 : thisDOAS.AirLoopSplitterName = Util::makeUPPER(fields.at("airloophvac_splitter_name").get<std::string>()); //
781 7 : thisDOAS.m_AirLoopSplitterIndex = getAirLoopSplitterIndex(state, thisDOAS.AirLoopSplitterName);
782 7 : if (thisDOAS.m_AirLoopSplitterIndex < 0) {
783 0 : cFieldName = "AirLoopHVAC:Splitter Name";
784 0 : ShowSevereError(
785 0 : state, format("{}, \"{}\" {} not found: {}", cCurrentModuleObject, thisDOAS.Name, cFieldName, thisDOAS.AirLoopSplitterName));
786 0 : errorsFound = true;
787 : }
788 14 : AirLoopSplitter thisAirLoopSplitter;
789 7 : thisDOAS.m_CompPointerAirLoopSplitter =
790 7 : thisAirLoopSplitter.factory(state, thisDOAS.m_AirLoopSplitterIndex, thisDOAS.AirLoopSplitterName);
791 :
792 : // get pretreated design conditions
793 14 : thisDOAS.PreheatTemp = fields.at("preheat_design_temperature").get<Real64>();
794 14 : thisDOAS.PreheatHumRat = fields.at("preheat_design_humidity_ratio").get<Real64>();
795 14 : thisDOAS.PrecoolTemp = fields.at("precool_design_temperature").get<Real64>();
796 14 : thisDOAS.PrecoolHumRat = fields.at("precool_design_humidity_ratio").get<Real64>();
797 :
798 : // get info on AirLoops
799 7 : thisDOAS.NumOfAirLoops = fields.at("number_of_airloophvac").get<int>(); //
800 7 : if (thisDOAS.NumOfAirLoops < 1) {
801 0 : cFieldName = "Number of AirLoopHVAC";
802 0 : ShowSevereError(state,
803 0 : fmt::format("{}, \"{}\" {} = {}", cCurrentModuleObject, thisDOAS.Name, cFieldName, thisDOAS.NumOfAirLoops));
804 0 : ShowContinueError(state, " The minimum value should be 1.");
805 0 : errorsFound = true;
806 : }
807 :
808 7 : auto AirLoopNames = fields.find("airloophvacs");
809 7 : if (AirLoopNames != fields.end()) {
810 7 : auto const &AirLoopArray = AirLoopNames.value();
811 7 : int num = 0;
812 34 : for (auto const &AirLoopHVACName : AirLoopArray) {
813 27 : std::string name = Util::makeUPPER(AirLoopHVACName.at("airloophvac_name").get<std::string>());
814 27 : int LoopNum = Util::FindItemInList(name, state.dataAirSystemsData->PrimaryAirSystems);
815 :
816 27 : num += 1;
817 27 : if (LoopNum > 0 && num <= thisDOAS.NumOfAirLoops) {
818 27 : thisDOAS.AirLoopName.push_back(name);
819 27 : thisDOAS.m_AirLoopNum.push_back(LoopNum);
820 : } else {
821 0 : cFieldName = "AirLoopHVAC Name";
822 0 : ShowSevereError(state, format("{}, \"{}\" {} not found: {}", cCurrentModuleObject, thisDOAS.Name, cFieldName, name));
823 0 : errorsFound = true;
824 : }
825 27 : }
826 : }
827 :
828 7 : thisDOAS.m_AirLoopDOASNum = AirLoopDOASNum - 1;
829 7 : state.dataAirLoopHVACDOAS->airloopDOAS.push_back(thisDOAS);
830 :
831 7 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, thisDOAS.m_InletNodeNum)) {
832 2 : ShowSevereError(state,
833 2 : format("Inlet node ({}) is not one of OutdoorAir:Node in {} = {}",
834 1 : state.dataLoopNodes->NodeID(thisDOAS.m_InletNodeNum),
835 : CurrentModuleObject,
836 : thisDOAS.Name));
837 1 : errorsFound = true;
838 : }
839 :
840 : // Ensure the outlet node is the splitter inlet node, otherwise issue a severe error
841 7 : if (thisDOAS.m_OutletNodeNum != thisDOAS.m_CompPointerAirLoopSplitter->InletNodeNum) {
842 2 : ShowSevereError(
843 : state,
844 2 : format("The outlet node is not the inlet node of AirLoopHVAC:Splitter in {} = {}", CurrentModuleObject, thisDOAS.Name));
845 2 : ShowContinueError(state,
846 2 : format("The outlet node name is {}, and the inlet node name of AirLoopHVAC:Splitter is {}",
847 1 : state.dataLoopNodes->NodeID(thisDOAS.m_OutletNodeNum),
848 1 : state.dataLoopNodes->NodeID(thisDOAS.m_CompPointerAirLoopSplitter->InletNodeNum)));
849 1 : errorsFound = true;
850 : }
851 7 : }
852 :
853 : // Check valid OA controller
854 26 : for (int OASysNum = 1; OASysNum <= state.dataAirLoop->NumOASystems; OASysNum++) {
855 19 : if (Util::SameString(state.dataAirLoop->OutsideAirSys(OASysNum).ControllerListName, "")) {
856 4 : if (state.dataAirLoop->OutsideAirSys(OASysNum).AirLoopDOASNum == -1) {
857 0 : ShowSevereError(state,
858 0 : format("AirLoopHVAC:OutdoorAirSystem = \"{}\" invalid Controller List Name = \" not found.",
859 0 : state.dataAirLoop->OutsideAirSys(OASysNum).Name));
860 0 : errorsFound = true;
861 : }
862 : }
863 : }
864 7 : if (errorsFound) {
865 3 : ShowFatalError(state, "getAirLoopHVACDOAS: Previous errors cause termination.");
866 : }
867 7 : }
868 7 : }
869 :
870 14568 : void AirLoopDOAS::initAirLoopDOAS(EnergyPlusData &state, bool const FirstHVACIteration)
871 : {
872 : int LoopOA;
873 : Real64 SchAvailValue;
874 : static constexpr std::string_view RoutineName = "AirLoopDOAS::initAirLoopDOAS";
875 :
876 14568 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag) {
877 8 : bool ErrorsFound = false;
878 : Real64 rho;
879 32 : for (int CompNum = 1; CompNum <= state.dataAirLoop->OutsideAirSys(this->m_OASystemNum).NumComponents; ++CompNum) {
880 24 : std::string const &CompType = state.dataAirLoop->OutsideAirSys(this->m_OASystemNum).ComponentType(CompNum);
881 24 : std::string const &CompName = state.dataAirLoop->OutsideAirSys(this->m_OASystemNum).ComponentName(CompNum);
882 24 : if (Util::SameString(CompType, "FAN:SYSTEMMODEL")) {
883 8 : state.dataFans->fans(this->m_FanIndex)->simulate(state, FirstHVACIteration);
884 : }
885 24 : if (Util::SameString(CompType, "FAN:COMPONENTMODEL")) {
886 0 : state.dataFans->fans(this->m_FanIndex)->simulate(state, FirstHVACIteration);
887 : }
888 :
889 24 : if (Util::SameString(CompType, "COIL:HEATING:WATER")) {
890 0 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, this->m_HeatCoilNum);
891 0 : Real64 CoilMaxVolFlowRate = WaterCoils::GetCoilMaxWaterFlowRate(state, "Coil:Heating:Water", CompName, ErrorsFound);
892 0 : rho = state.dataPlnt->PlantLoop(this->HWPlantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
893 0 : PlantUtilities::InitComponentNodes(state,
894 : 0.0,
895 : CoilMaxVolFlowRate * rho,
896 : this->HWCtrlNodeNum,
897 0 : state.dataAirLoop->OutsideAirSys(this->m_OASystemNum).OutletNodeNum(CompNum));
898 : }
899 24 : if (Util::SameString(CompType, "COIL:COOLING:WATER")) {
900 0 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, this->m_CoolCoilNum);
901 0 : Real64 CoilMaxVolFlowRate = WaterCoils::GetCoilMaxWaterFlowRate(state, "Coil:Cooling:Water", CompName, ErrorsFound);
902 0 : rho = state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, RoutineName);
903 0 : PlantUtilities::InitComponentNodes(state,
904 : 0.0,
905 : CoilMaxVolFlowRate * rho,
906 : this->CWCtrlNodeNum,
907 0 : state.dataAirLoop->OutsideAirSys(this->m_OASystemNum).OutletNodeNum(CompNum));
908 : }
909 24 : if (Util::SameString(CompType, "COIL:COOLING:WATER:DETAILEDGEOMETRY")) {
910 0 : WaterCoils::SimulateWaterCoilComponents(state, CompName, FirstHVACIteration, this->m_CoolCoilNum);
911 : Real64 CoilMaxVolFlowRate =
912 0 : WaterCoils::GetCoilMaxWaterFlowRate(state, "Coil:Cooling:Water:DetailedGeometry", CompName, ErrorsFound);
913 0 : rho = state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).glycol->getDensity(state, Constant::CWInitConvTemp, RoutineName);
914 0 : PlantUtilities::InitComponentNodes(state,
915 : 0.0,
916 : CoilMaxVolFlowRate * rho,
917 : this->CWCtrlNodeNum,
918 0 : state.dataAirLoop->OutsideAirSys(this->m_OASystemNum).OutletNodeNum(CompNum));
919 : }
920 : }
921 :
922 8 : this->MyEnvrnFlag = false;
923 8 : if (ErrorsFound) {
924 0 : ShowFatalError(state, "initAirLoopDOAS: Previous errors cause termination.");
925 : }
926 : }
927 :
928 14568 : if (!state.dataGlobal->BeginEnvrnFlag) {
929 14532 : this->MyEnvrnFlag = true;
930 : }
931 :
932 14568 : this->SumMassFlowRate = 0.0;
933 :
934 29148 : for (LoopOA = 0; LoopOA < this->m_CompPointerAirLoopSplitter->numOfOutletNodes; LoopOA++) {
935 14580 : int NodeNum = this->m_CompPointerAirLoopSplitter->OutletNodeNum[LoopOA];
936 14580 : this->SumMassFlowRate += state.dataLoopNodes->Node(NodeNum).MassFlowRate;
937 : }
938 :
939 14568 : SchAvailValue = this->m_AvailManagerSched->getCurrentVal();
940 14568 : if (SchAvailValue < 1.0) {
941 0 : this->SumMassFlowRate = 0.0;
942 : }
943 14568 : state.dataLoopNodes->Node(this->m_InletNodeNum).MassFlowRate = this->SumMassFlowRate;
944 14568 : }
945 :
946 14568 : void AirLoopDOAS::CalcAirLoopDOAS(EnergyPlusData &state, bool const FirstHVACIteration)
947 : {
948 : using MixedAir::ManageOutsideAirSystem;
949 :
950 14568 : this->m_CompPointerAirLoopMixer->CalcAirLoopMixer(state);
951 14568 : if (this->m_FanIndex > 0) {
952 14565 : if (this->m_FanInletNodeNum == this->m_InletNodeNum) {
953 7282 : state.dataLoopNodes->Node(this->m_FanInletNodeNum).MassFlowRateMaxAvail = this->SumMassFlowRate;
954 7282 : state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMaxAvail = this->SumMassFlowRate;
955 7282 : state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMax = this->SumMassFlowRate;
956 : } else {
957 7283 : state.dataLoopNodes->Node(this->m_InletNodeNum).MassFlowRateMax = this->SumMassFlowRate;
958 7283 : state.dataLoopNodes->Node(this->m_InletNodeNum).MassFlowRateMaxAvail = this->SumMassFlowRate;
959 : }
960 : }
961 14568 : ManageOutsideAirSystem(state, this->OASystemName, FirstHVACIteration, 0, this->m_OASystemNum);
962 14568 : Real64 Temp = state.dataLoopNodes->Node(this->m_OutletNodeNum).Temp;
963 14568 : Real64 HumRat = state.dataLoopNodes->Node(this->m_OutletNodeNum).HumRat;
964 14568 : state.dataLoopNodes->Node(this->m_OutletNodeNum).Enthalpy = Psychrometrics::PsyHFnTdbW(Temp, HumRat);
965 :
966 14568 : this->m_CompPointerAirLoopSplitter->CalcAirLoopSplitter(state, Temp, HumRat);
967 14568 : }
968 :
969 2 : void AirLoopDOAS::SizingAirLoopDOAS(EnergyPlusData &state)
970 : {
971 2 : Real64 sizingVolumeFlow = 0;
972 :
973 4 : for (int AirLoop = 1; AirLoop <= this->NumOfAirLoops; AirLoop++) {
974 2 : int AirLoopNum = this->m_AirLoopNum[AirLoop - 1];
975 2 : this->m_OACtrlNum.push_back(state.dataAirLoop->AirLoopControlInfo(AirLoopNum).OACtrlNum);
976 :
977 2 : if (this->m_OACtrlNum[AirLoop - 1] > 0) {
978 2 : sizingVolumeFlow +=
979 2 : state.dataMixedAir->OAController(this->m_OACtrlNum[AirLoop - 1]).MaxOA; // this is a volume flow rate not a mass flow rate
980 : }
981 : }
982 2 : this->SizingMassFlow = sizingVolumeFlow * state.dataEnvrn->StdRhoAir;
983 :
984 2 : BaseSizer::reportSizerOutput(state, "AirLoopHVAC:DedicatedOutdoorAirSystem", this->Name, "Design Volume Flow Rate [m3/s]", sizingVolumeFlow);
985 2 : this->GetDesignDayConditions(state);
986 :
987 2 : if (this->m_FanIndex > 0 && this->m_FanTypeNum == SimAirServingZones::CompType::Fan_System_Object) {
988 2 : state.dataFans->fans(this->m_FanIndex)->maxAirFlowRate = sizingVolumeFlow;
989 2 : state.dataLoopNodes->Node(this->m_FanInletNodeNum).MassFlowRateMaxAvail = this->SizingMassFlow;
990 2 : state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMaxAvail = this->SizingMassFlow;
991 2 : state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMax = this->SizingMassFlow;
992 : }
993 2 : if (this->m_FanIndex > 0 && this->m_FanTypeNum == SimAirServingZones::CompType::Fan_ComponentModel) {
994 0 : state.dataFans->fans(this->m_FanIndex)->maxAirFlowRate = sizingVolumeFlow;
995 0 : state.dataFans->fans(this->m_FanIndex)->minAirFlowRate = 0.0;
996 0 : state.dataFans->fans(this->m_FanIndex)->maxAirMassFlowRate = this->SizingMassFlow;
997 0 : state.dataLoopNodes->Node(this->m_FanInletNodeNum).MassFlowRateMaxAvail = this->SizingMassFlow;
998 0 : state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMaxAvail = this->SizingMassFlow;
999 0 : state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMax = this->SizingMassFlow;
1000 : }
1001 :
1002 2 : state.dataSize->CurSysNum = state.dataHVACGlobal->NumPrimaryAirSys + this->m_AirLoopDOASNum + 1;
1003 2 : state.dataSize->CurOASysNum = this->m_OASystemNum;
1004 2 : }
1005 :
1006 4 : void getAirLoopHVACDOASInput(EnergyPlusData &state)
1007 : {
1008 4 : if (state.dataAirLoopHVACDOAS->GetInputOnceFlag) {
1009 4 : AirLoopDOAS::getAirLoopDOASInput(state);
1010 4 : state.dataAirLoopHVACDOAS->GetInputOnceFlag = false;
1011 : }
1012 4 : }
1013 :
1014 6 : void AirLoopDOAS::GetDesignDayConditions(EnergyPlusData &state)
1015 : {
1016 28 : for (auto &env : state.dataWeather->Environment) {
1017 22 : if (env.KindOfEnvrn != Constant::KindOfSim::DesignDay && env.KindOfEnvrn != Constant::KindOfSim::RunPeriodDesign) continue;
1018 20 : if (env.maxCoolingOATSizing > this->SizingCoolOATemp) {
1019 12 : this->SizingCoolOATemp = env.maxCoolingOATSizing;
1020 : // DesignDayNum = 0 for KindOfSim == RunPeriodDesign
1021 12 : if (env.KindOfEnvrn == Constant::KindOfSim::DesignDay && state.dataWeather->DesDayInput(env.DesignDayNum).PressureEntered) {
1022 5 : this->SizingCoolOAHumRat =
1023 5 : Psychrometrics::PsyWFnTdpPb(state, env.maxCoolingOADPSizing, state.dataWeather->DesDayInput(env.DesignDayNum).PressBarom);
1024 : } else {
1025 7 : this->SizingCoolOAHumRat = Psychrometrics::PsyWFnTdpPb(state, env.maxCoolingOADPSizing, state.dataEnvrn->StdBaroPress);
1026 : }
1027 : }
1028 20 : if (env.minHeatingOATSizing < this->HeatOutTemp) {
1029 14 : this->HeatOutTemp = env.minHeatingOATSizing;
1030 14 : if (env.KindOfEnvrn == Constant::KindOfSim::DesignDay && state.dataWeather->DesDayInput(env.DesignDayNum).PressureEntered) {
1031 3 : this->HeatOutHumRat =
1032 3 : Psychrometrics::PsyWFnTdpPb(state, env.minHeatingOADPSizing, state.dataWeather->DesDayInput(env.DesignDayNum).PressBarom);
1033 : } else {
1034 11 : this->HeatOutHumRat = Psychrometrics::PsyWFnTdpPb(state, env.minHeatingOADPSizing, state.dataEnvrn->StdBaroPress);
1035 : }
1036 : }
1037 : }
1038 :
1039 6 : BaseSizer::reportSizerOutput(
1040 : state, "AirLoopHVAC:DedicatedOutdoorAirSystem", this->Name, "Design Cooling Outdoor Air Temperature [C]", this->SizingCoolOATemp);
1041 6 : BaseSizer::reportSizerOutput(state,
1042 : "AirLoopHVAC:DedicatedOutdoorAirSystem",
1043 : this->Name,
1044 : "Design Cooling Outdoor Air Humidity Ratio [kgWater/kgDryAir]",
1045 : this->SizingCoolOAHumRat);
1046 6 : BaseSizer::reportSizerOutput(
1047 : state, "AirLoopHVAC:DedicatedOutdoorAirSystem", this->Name, "Design Heating Outdoor Air Temperature [C]", this->HeatOutTemp);
1048 6 : BaseSizer::reportSizerOutput(state,
1049 : "AirLoopHVAC:DedicatedOutdoorAirSystem",
1050 : this->Name,
1051 : "Design Heating Outdoor Air Humidity Ratio [kgWater/kgDryAir]",
1052 : this->HeatOutHumRat);
1053 6 : }
1054 :
1055 0 : void CheckConvergence(EnergyPlusData &state)
1056 : {
1057 :
1058 : Real64 maxDiff;
1059 : Real64 Diff;
1060 : Real64 OldTemp;
1061 0 : for (auto &loop : state.dataAirLoopHVACDOAS->airloopDOAS) {
1062 0 : maxDiff = 0.0;
1063 0 : Diff = std::abs(loop.m_CompPointerAirLoopSplitter->InletTemp -
1064 0 : state.dataLoopNodes->Node(loop.m_CompPointerAirLoopSplitter->OutletNodeNum[0]).Temp);
1065 0 : if (Diff > maxDiff) {
1066 0 : maxDiff = Diff;
1067 : }
1068 0 : if (loop.m_HeatExchangerFlag) {
1069 0 : OldTemp = loop.m_CompPointerAirLoopMixer->OutletTemp;
1070 0 : loop.m_CompPointerAirLoopMixer->CalcAirLoopMixer(state);
1071 0 : Diff = std::abs(OldTemp - loop.m_CompPointerAirLoopMixer->OutletTemp);
1072 0 : if (Diff > maxDiff) {
1073 0 : maxDiff = Diff;
1074 : }
1075 : }
1076 0 : if (maxDiff > 1.0e-6) {
1077 0 : if (loop.ConveCount == 0) {
1078 0 : ++loop.ConveCount;
1079 0 : ShowWarningError(state, format("Convergence limit is above 1.0e-6 for unit={}", loop.Name));
1080 0 : ShowContinueErrorTimeStamp(
1081 0 : state, format("The max difference of node temperatures between AirLoopDOAS outlet and OA mixer inlet ={:.6R}", maxDiff));
1082 : } else {
1083 0 : ++loop.ConveCount;
1084 0 : ShowRecurringWarningErrorAtEnd(state,
1085 0 : loop.Name + "\": The max difference of node temperatures exceeding 1.0e-6 continues...",
1086 0 : loop.ConveIndex,
1087 : maxDiff,
1088 : maxDiff);
1089 : }
1090 : }
1091 : }
1092 0 : }
1093 :
1094 : } // namespace AirLoopHVACDOAS
1095 : } // namespace EnergyPlus
|