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