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 <cassert>
50 : #include <cmath>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 :
56 : // EnergyPlus Headers
57 : #include <EnergyPlus/BranchNodeConnections.hh>
58 : #include <EnergyPlus/CurveManager.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataEnvironment.hh>
61 : #include <EnergyPlus/DataGenerators.hh>
62 : #include <EnergyPlus/DataHVACGlobals.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataLoopNode.hh>
66 : #include <EnergyPlus/FluidProperties.hh>
67 : #include <EnergyPlus/FuelCellElectricGenerator.hh>
68 : #include <EnergyPlus/General.hh>
69 : #include <EnergyPlus/GeneratorFuelSupply.hh>
70 : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
71 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
72 : #include <EnergyPlus/NodeInputManager.hh>
73 : #include <EnergyPlus/OutputProcessor.hh>
74 : #include <EnergyPlus/Plant/DataPlant.hh>
75 : #include <EnergyPlus/PlantUtilities.hh>
76 : #include <EnergyPlus/ScheduleManager.hh>
77 : #include <EnergyPlus/UtilityRoutines.hh>
78 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
79 :
80 : namespace EnergyPlus {
81 :
82 : namespace FuelCellElectricGenerator {
83 :
84 : // MODULE INFORMATION:
85 : // AUTHOR Brent Griffith
86 : // DATE WRITTEN August. 2005
87 :
88 : // PURPOSE OF THIS MODULE:
89 : // This module simulates the operation of Solid oxide fuel cell Generators.
90 :
91 : // METHODOLOGY EMPLOYED:
92 : // Once the ElectricPowerManager determines that the FuelCell Generator
93 : // is available to meet an electric load demand, it calls SimFuelCellGenerator
94 : // which in turn calls the FuelCell model.
95 : // See DataGenerators.cc for structures and variables
96 :
97 : // REFERENCES:
98 : // IEA/ECBCS Annex 42 model specification for Solid oxide and proton exchange membrane fuel cells
99 : constexpr std::array<std::string_view, (int)DataGenerators::CurveMode::Num> curveModeNamesUC = {"NORMALIZED", "ANNEX42"};
100 :
101 : constexpr std::array<std::string_view, (int)DataGenerators::SkinLoss::Num> skinLossNamesUC = {
102 : "CONSTANTRATE", "UAFORPROCESSGASTEMPERATURE", "QUADRATIC FUNCTION OF FUEL RATE"}; // Seriously with this last one?
103 :
104 : constexpr std::array<std::string_view, (int)DataGenerators::AirSupRateMode::Num> airSupRateModeNamesUC = {
105 : "QUADRATIC FUNCTION OF FUEL RATE", "AIRRATIOBYSTOICS", "QUADRATICFUNCTIONOFELECTRICPOWER"}; // Seriously with the first one?
106 :
107 : constexpr std::array<std::string_view, (int)DataGenerators::RecoverMode::Num> recoverModeNames = {"NoRecovery",
108 : "RecoverBurnerInverterStorage",
109 : "RecoverAuxiliaryBurner",
110 : "RecoverInverterandStorage",
111 : "RecoverInverter",
112 : "RecoverElectricalStorage"};
113 : constexpr std::array<std::string_view, (int)DataGenerators::RecoverMode::Num> recoverModeNamesUC = {"NORECOVERY",
114 : "RECOVERBURNERINVERTERSTORAGE",
115 : "RECOVERAUXILIARYBURNER",
116 : "RECOVERINVERTERANDSTORAGE",
117 : "RECOVERINVERTER",
118 : "RECOVERELECTRICALSTORAGE"};
119 :
120 : constexpr std::array<std::string_view, (int)DataGenerators::ConstituentMode::Num> constituentModeNames = {"AmbientAir",
121 : "UserDefinedConstituents"};
122 : constexpr std::array<std::string_view, (int)DataGenerators::ConstituentMode::Num> constituentModeNamesUC = {"AMBIENTAIR",
123 : "USERDEFINEDCONSTITUENTS"};
124 :
125 : constexpr std::array<std::string_view, (int)DataGenerators::WaterTempMode::Num> waterTempModeNames = {
126 : "MainsWaterTemperature", "TemperatureFromAirNode", "TemperatureFromWaterNode", "TemperatureFromSchedule"};
127 : constexpr std::array<std::string_view, (int)DataGenerators::WaterTempMode::Num> waterTempModeNamesUC = {
128 : "MAINSWATERTEMPERATURE", "TEMPERATUREFROMAIRNODE", "TEMPERATUREFROMWATERNODE", "TEMPERATUREFROMSCHEDULE"};
129 :
130 : constexpr std::array<std::string_view, (int)DataGenerators::InverterEfficiencyMode::Num> inverterEfficiencyModeNames = {"Constant", "Quadratic"};
131 : constexpr std::array<std::string_view, (int)DataGenerators::InverterEfficiencyMode::Num> inverterEfficiencyModeNamesUC = {"CONSTANT",
132 : "QUADRATIC"};
133 :
134 : constexpr std::array<std::string_view, (int)DataGenerators::ExhaustGasHX::Num> exhaustGasHXNames = {
135 : "FixedEffectiveness", "EmpiricalUAeff", "FundementalUAeff", "Condensing"};
136 : constexpr std::array<std::string_view, (int)DataGenerators::ExhaustGasHX::Num> exhaustGasHXNamesUC = {
137 : "FIXEDEFFECTIVENESS", "EMPIRICALUAEFF", "FUNDEMENTALUAEFF", "CONDENSING"};
138 :
139 : constexpr std::array<std::string_view, (int)DataGenerators::LossDestination::Num> lossDestinationNames = {"SurroundingZone",
140 : "AirInletForFuelCell"};
141 : constexpr std::array<std::string_view, (int)DataGenerators::LossDestination::Num> lossDestinationNamesUC = {"SURROUNDINGZONE",
142 : "AIRINLETFORFUELCELL"};
143 :
144 2237 : PlantComponent *FCDataStruct::factory(EnergyPlusData &state, std::string const &objectName)
145 : {
146 : // Process the input data
147 2237 : if (state.dataFuelCellElectGen->getFuelCellInputFlag) {
148 0 : getFuelCellInput(state);
149 0 : state.dataFuelCellElectGen->getFuelCellInputFlag = false;
150 : }
151 :
152 : // Now look for this object
153 2237 : auto thisObj = std::find_if(state.dataFuelCellElectGen->FuelCell.begin(),
154 2237 : state.dataFuelCellElectGen->FuelCell.end(),
155 2237 : [&objectName](const FCDataStruct &myObj) { return myObj.Name == objectName; });
156 2237 : if (thisObj != state.dataFuelCellElectGen->FuelCell.end()) return thisObj;
157 :
158 : // If we didn't find it, fatal
159 : ShowFatalError(state, format("LocalFuelCellGenFactory: Error getting inputs for object named: {}", objectName)); // LCOV_EXCL_LINE
160 : // Shut up the compiler
161 : return nullptr; // LCOV_EXCL_LINE
162 : }
163 :
164 1 : PlantComponent *FCDataStruct::factory_exhaust(EnergyPlusData &state, std::string const &objectName)
165 : {
166 : // Process the input data
167 1 : if (state.dataFuelCellElectGen->getFuelCellInputFlag) {
168 1 : getFuelCellInput(state);
169 1 : state.dataFuelCellElectGen->getFuelCellInputFlag = false;
170 : }
171 :
172 : // Now look for this object
173 1 : for (auto &thisFC : state.dataFuelCellElectGen->FuelCell) {
174 1 : if (Util::makeUPPER(thisFC.NameExhaustHX) == Util::makeUPPER(objectName)) {
175 1 : return &thisFC;
176 : }
177 : }
178 : // If we didn't find it, fatal
179 : ShowFatalError(state, format("LocalFuelCellGenFactory: Error getting inputs for object named: {}", objectName)); // LCOV_EXCL_LINE
180 : // Shut up the compiler
181 : return nullptr; // LCOV_EXCL_LINE
182 : }
183 :
184 2235 : void FCDataStruct::SimFuelCellGenerator(EnergyPlusData &state,
185 : bool const RunFlag, // simulate Generator when TRUE
186 : Real64 const MyLoad, // demand on electric generator
187 : bool const FirstHVACIteration)
188 : {
189 : // SUBROUTINE INFORMATION:
190 : // AUTHOR Brent Griffith
191 : // DATE WRITTEN March 2005
192 :
193 : // PURPOSE OF THIS SUBROUTINE: This is the Solid oxide fuel cell Generator model driver. It
194 : // gets the input for the models, initializes simulation variables, call
195 : // the appropriate model and sets up reporting variables.
196 :
197 2235 : this->initialize(state);
198 2235 : this->CalcFuelCellGeneratorModel(state, RunFlag, MyLoad, FirstHVACIteration);
199 2235 : this->CalcUpdateHeatRecovery(state, FirstHVACIteration);
200 2235 : this->UpdateFuelCellGeneratorRecords(state);
201 2235 : }
202 :
203 1 : void getFuelCellInput(EnergyPlusData &state)
204 : {
205 : // SUBROUTINE INFORMATION:
206 : // AUTHOR: Brent Griffith
207 : // DATE WRITTEN: April 2005
208 :
209 : // PURPOSE OF THIS SUBROUTINE:
210 : // This routine will get the input
211 : // required by the FuelCell Generator models.
212 :
213 : // METHODOLOGY EMPLOYED:
214 : // EnergyPlus input processor
215 : static constexpr std::string_view routineName = "getFuelCellInput";
216 :
217 : int NumAlphas; // Number of elements in the alpha array
218 : int NumNums; // Number of elements in the numeric array
219 : int IOStat; // IO Status when calling get input subroutine
220 1 : Array1D_string AlphArray(25); // character string data
221 1 : Array1D<Real64> NumArray(200); // numeric data TODO deal with allocatable for extensible
222 1 : bool ErrorsFound(false); // error flag
223 :
224 1 : auto &s_ipsc = state.dataIPShortCut;
225 :
226 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell";
227 2 : state.dataFuelCellElectGen->NumFuelCellGenerators =
228 1 : state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
229 :
230 1 : if (state.dataFuelCellElectGen->NumFuelCellGenerators <= 0) {
231 0 : ShowSevereError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
232 0 : ErrorsFound = true;
233 : }
234 :
235 : // ALLOCATE ARRAYS
236 1 : state.dataFuelCellElectGen->FuelCell.allocate(state.dataFuelCellElectGen->NumFuelCellGenerators); // inits handled in derived type definitions
237 :
238 : // first load in FuelCell names
239 2 : for (int GeneratorNum = 1; GeneratorNum <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++GeneratorNum) {
240 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
241 1 : s_ipsc->cCurrentModuleObject,
242 : GeneratorNum,
243 : AlphArray,
244 : NumAlphas,
245 : NumArray,
246 : NumNums,
247 : IOStat,
248 : _,
249 : _,
250 1 : s_ipsc->cAlphaFieldNames,
251 1 : s_ipsc->cNumericFieldNames);
252 :
253 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).Name = AlphArray(1);
254 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCPM = AlphArray(2);
255 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCAirSup = AlphArray(3);
256 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCFuelSup = AlphArray(4);
257 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCWaterSup = AlphArray(5);
258 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCAuxilHeat = AlphArray(6);
259 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameExhaustHX = AlphArray(7);
260 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameElecStorage = AlphArray(8);
261 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameInverter = AlphArray(9);
262 1 : if (NumAlphas == 10) {
263 0 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameStackCooler = AlphArray(10);
264 : }
265 : }
266 :
267 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:PowerModule";
268 1 : int NumFuelCellPMs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
269 :
270 1 : if (NumFuelCellPMs <= 0) {
271 0 : ShowSevereError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
272 0 : ErrorsFound = true;
273 : }
274 :
275 2 : for (int FCPMNum = 1; FCPMNum <= NumFuelCellPMs; ++FCPMNum) {
276 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
277 1 : s_ipsc->cCurrentModuleObject,
278 : FCPMNum,
279 : AlphArray,
280 : NumAlphas,
281 : NumArray,
282 : NumNums,
283 : IOStat,
284 : _,
285 1 : s_ipsc->lAlphaFieldBlanks,
286 1 : s_ipsc->cAlphaFieldNames,
287 1 : s_ipsc->cNumericFieldNames);
288 :
289 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
290 :
291 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameFCPM);
292 1 : if (fuelCellNum > 0) {
293 :
294 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
295 :
296 1 : fuelCell.FCPM.Name = AlphArray(1);
297 1 : fuelCell.FCPM.EffMode = static_cast<DataGenerators::CurveMode>(getEnumValue(curveModeNamesUC, AlphArray(2)));
298 1 : if (fuelCell.FCPM.EffMode == DataGenerators::CurveMode::Invalid) {
299 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), AlphArray(2));
300 0 : ErrorsFound = true;
301 : }
302 :
303 1 : if (s_ipsc->lAlphaFieldBlanks(3)) {
304 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
305 0 : ErrorsFound = true;
306 1 : } else if ((fuelCell.FCPM.EffCurve = Curve::GetCurve(state, AlphArray(3))) == nullptr) {
307 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), AlphArray(3));
308 0 : ErrorsFound = true;
309 : }
310 :
311 1 : fuelCell.FCPM.NomEff = NumArray(1);
312 1 : fuelCell.FCPM.NomPel = NumArray(2);
313 1 : fuelCell.FCPM.NumCyclesAtStart = NumArray(3);
314 1 : fuelCell.FCPM.NumCycles = fuelCell.FCPM.NumCyclesAtStart;
315 1 : fuelCell.FCPM.CyclingDegradRat = NumArray(4);
316 1 : fuelCell.FCPM.NumRunHours = NumArray(5);
317 1 : fuelCell.FCPM.OperateDegradRat = NumArray(6);
318 1 : fuelCell.FCPM.ThreshRunHours = NumArray(7);
319 1 : fuelCell.FCPM.UpTranLimit = NumArray(8);
320 1 : fuelCell.FCPM.DownTranLimit = NumArray(9);
321 1 : fuelCell.FCPM.StartUpTime = NumArray(10) / Constant::rSecsInHour; // convert to hours from seconds
322 1 : fuelCell.FCPM.StartUpFuel = NumArray(11);
323 1 : fuelCell.FCPM.StartUpElectConsum = NumArray(12);
324 1 : fuelCell.FCPM.StartUpElectProd = NumArray(13);
325 1 : fuelCell.FCPM.ShutDownTime = NumArray(14) / Constant::rSecsInHour; // convert to hours from seconds
326 1 : fuelCell.FCPM.ShutDownFuel = NumArray(15);
327 1 : fuelCell.FCPM.ShutDownElectConsum = NumArray(16);
328 1 : fuelCell.FCPM.ANC0 = NumArray(17);
329 1 : fuelCell.FCPM.ANC1 = NumArray(18);
330 1 : fuelCell.FCPM.SkinLossMode = static_cast<DataGenerators::SkinLoss>(getEnumValue(skinLossNamesUC, AlphArray(4)));
331 1 : if (fuelCell.FCPM.SkinLossMode == DataGenerators::SkinLoss::Invalid) {
332 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
333 0 : ErrorsFound = true;
334 : }
335 :
336 1 : fuelCell.FCPM.ZoneName = AlphArray(5);
337 1 : fuelCell.FCPM.ZoneID = Util::FindItemInList(fuelCell.FCPM.ZoneName, state.dataHeatBal->Zone);
338 1 : if (fuelCell.FCPM.ZoneID == 0 && !s_ipsc->lAlphaFieldBlanks(5)) {
339 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(5), AlphArray(5)));
340 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
341 0 : ShowContinueError(state, "Zone Name was not found ");
342 0 : ErrorsFound = true;
343 : }
344 :
345 1 : fuelCell.FCPM.RadiativeFract = NumArray(19);
346 1 : fuelCell.FCPM.QdotSkin = NumArray(20);
347 1 : fuelCell.FCPM.UAskin = NumArray(21);
348 :
349 1 : if (s_ipsc->lAlphaFieldBlanks(6)) {
350 0 : if (fuelCell.FCPM.SkinLossMode == DataGenerators::SkinLoss::QuadraticFuelNdot) {
351 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(6));
352 0 : ErrorsFound = true;
353 : }
354 1 : } else if ((fuelCell.FCPM.SkinLossCurve = Curve::GetCurve(state, AlphArray(6))) == nullptr) {
355 0 : if (fuelCell.FCPM.SkinLossMode == DataGenerators::SkinLoss::QuadraticFuelNdot) {
356 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), AlphArray(6));
357 0 : ErrorsFound = true;
358 : }
359 : }
360 :
361 1 : fuelCell.FCPM.NdotDilutionAir = NumArray(22);
362 1 : fuelCell.FCPM.StackHeatLossToDilution = NumArray(23);
363 1 : fuelCell.FCPM.DilutionInletNodeName = AlphArray(7);
364 1 : fuelCell.FCPM.DilutionInletNode =
365 1 : NodeInputManager::GetOnlySingleNode(state,
366 1 : AlphArray(7),
367 : ErrorsFound,
368 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellPowerModule,
369 1 : AlphArray(1),
370 : DataLoopNode::NodeFluidType::Air,
371 : DataLoopNode::ConnectionType::Inlet,
372 : NodeInputManager::CompFluidStream::Primary,
373 : DataLoopNode::ObjectIsNotParent);
374 1 : fuelCell.FCPM.DilutionExhaustNodeName = AlphArray(8);
375 1 : fuelCell.FCPM.DilutionExhaustNode =
376 1 : NodeInputManager::GetOnlySingleNode(state,
377 1 : AlphArray(8),
378 : ErrorsFound,
379 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellPowerModule,
380 1 : AlphArray(1),
381 : DataLoopNode::NodeFluidType::Air,
382 : DataLoopNode::ConnectionType::Outlet,
383 : NodeInputManager::CompFluidStream::Primary,
384 : DataLoopNode::ObjectIsNotParent);
385 :
386 1 : fuelCell.FCPM.PelMin = NumArray(24);
387 1 : fuelCell.FCPM.PelMax = NumArray(25);
388 :
389 : // check for other FuelCell using the same power module and fill
390 1 : for (int otherFuelCell = fuelCellNum + 1; otherFuelCell <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++otherFuelCell) {
391 0 : if (Util::SameString(state.dataFuelCellElectGen->FuelCell(otherFuelCell).FCPM.Name, fuelCell.FCPM.Name)) {
392 0 : state.dataFuelCellElectGen->FuelCell(otherFuelCell).FCPM = fuelCell.FCPM;
393 : }
394 : }
395 : } else { // throw warning, did not find power module input
396 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
397 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
398 0 : ErrorsFound = true;
399 : }
400 : } // loop over NumFuelCellPMs
401 :
402 1 : GeneratorFuelSupply::GetGeneratorFuelSupplyInput(state);
403 :
404 2 : for (int FuelSupNum = 1; FuelSupNum <= (int)state.dataGenerator->FuelSupply.size(); ++FuelSupNum) {
405 1 : GeneratorFuelSupply::SetupFuelConstituentData(state, FuelSupNum, ErrorsFound);
406 : }
407 :
408 : // set fuel supply ID in Fuel cell structure
409 2 : for (int GeneratorNum = 1; GeneratorNum <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++GeneratorNum) {
410 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).FuelSupNum = Util::FindItemInList(
411 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCFuelSup, state.dataGenerator->FuelSupply); // Fuel Supply ID
412 1 : if (state.dataFuelCellElectGen->FuelCell(GeneratorNum).FuelSupNum == 0) {
413 0 : ShowSevereError(state,
414 0 : format("Fuel Supply Name: {} not found in {}",
415 0 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).NameFCFuelSup,
416 0 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).Name));
417 0 : ErrorsFound = true;
418 : }
419 : }
420 :
421 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:AirSupply";
422 1 : int NumFuelCellAirSups = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
423 :
424 1 : if (NumFuelCellAirSups <= 0) { // Autodesk:Uninit thisFuelCell was possibly uninitialized past this condition
425 0 : ShowSevereError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
426 0 : ErrorsFound = true;
427 : }
428 :
429 2 : for (int FCAirSupNum = 1; FCAirSupNum <= NumFuelCellAirSups; ++FCAirSupNum) {
430 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
431 1 : s_ipsc->cCurrentModuleObject,
432 : FCAirSupNum,
433 : AlphArray,
434 : NumAlphas,
435 : NumArray,
436 : NumNums,
437 : IOStat,
438 : _,
439 1 : s_ipsc->lAlphaFieldBlanks,
440 1 : s_ipsc->cAlphaFieldNames,
441 1 : s_ipsc->cNumericFieldNames);
442 :
443 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
444 :
445 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameFCAirSup);
446 :
447 1 : if (fuelCellNum > 0) {
448 :
449 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
450 1 : fuelCell.AirSup.Name = AlphArray(1);
451 1 : fuelCell.AirSup.NodeName = AlphArray(2);
452 :
453 : // check the node connections
454 1 : fuelCell.AirSup.SupNodeNum = NodeInputManager::GetOnlySingleNode(state,
455 1 : AlphArray(2),
456 : ErrorsFound,
457 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellAirSupply,
458 1 : AlphArray(1),
459 : DataLoopNode::NodeFluidType::Air,
460 : DataLoopNode::ConnectionType::Inlet,
461 : NodeInputManager::CompFluidStream::Primary,
462 : DataLoopNode::ObjectIsNotParent);
463 :
464 1 : if (s_ipsc->lAlphaFieldBlanks(3)) {
465 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
466 0 : ErrorsFound = true;
467 1 : } else if ((fuelCell.AirSup.BlowerPowerCurve = Curve::GetCurve(state, AlphArray(3))) == nullptr) {
468 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), AlphArray(3));
469 0 : ErrorsFound = true;
470 : }
471 1 : fuelCell.AirSup.BlowerHeatLossFactor = NumArray(1);
472 :
473 1 : fuelCell.AirSup.AirSupRateMode = static_cast<DataGenerators::AirSupRateMode>(getEnumValue(airSupRateModeNamesUC, AlphArray(4)));
474 1 : if (fuelCell.AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::Invalid) {
475 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
476 0 : ErrorsFound = true;
477 : }
478 :
479 1 : fuelCell.AirSup.Stoics = NumArray(2) + 1.0;
480 :
481 1 : if (s_ipsc->lAlphaFieldBlanks(5)) {
482 0 : if (fuelCell.AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::QuadraticFuncofPel) {
483 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(5));
484 0 : ErrorsFound = true;
485 : }
486 1 : } else if ((fuelCell.AirSup.AirFuncPelCurve = Curve::GetCurve(state, AlphArray(5))) == nullptr) {
487 0 : if (fuelCell.AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::QuadraticFuncofPel) {
488 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(5), AlphArray(5));
489 0 : ErrorsFound = true;
490 : }
491 : }
492 :
493 1 : fuelCell.AirSup.AirTempCoeff = NumArray(3);
494 :
495 1 : if (s_ipsc->lAlphaFieldBlanks(6)) {
496 1 : if (fuelCell.AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::QuadraticFuncofNdot) {
497 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(6));
498 0 : ErrorsFound = true;
499 : }
500 0 : } else if ((fuelCell.AirSup.AirFuncNdotCurve = Curve::GetCurve(state, AlphArray(6))) == nullptr) {
501 0 : if (fuelCell.AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::QuadraticFuncofNdot) {
502 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), AlphArray(6));
503 0 : ErrorsFound = true;
504 : }
505 : }
506 :
507 1 : fuelCell.AirSup.IntakeRecoveryMode = static_cast<DataGenerators::RecoverMode>(getEnumValue(recoverModeNamesUC, AlphArray(7)));
508 1 : if (fuelCell.AirSup.IntakeRecoveryMode == DataGenerators::RecoverMode::Invalid) {
509 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(7), AlphArray(7));
510 0 : ErrorsFound = true;
511 : }
512 :
513 1 : fuelCell.AirSup.ConstituentMode = static_cast<DataGenerators::ConstituentMode>(getEnumValue(constituentModeNamesUC, AlphArray(8)));
514 1 : if (fuelCell.AirSup.ConstituentMode == DataGenerators::ConstituentMode::Invalid) {
515 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(8), AlphArray(8));
516 0 : ErrorsFound = true;
517 : }
518 :
519 : int NumAirConstit;
520 :
521 1 : if (fuelCell.AirSup.ConstituentMode == DataGenerators::ConstituentMode::UserDefinedConstituents) {
522 0 : NumAirConstit = NumArray(4);
523 0 : fuelCell.AirSup.NumConstituents = NumAirConstit;
524 :
525 0 : if (NumAirConstit > 5) {
526 0 : ShowSevereError(state, format("Invalid {}={:.2R}", s_ipsc->cNumericFieldNames(4), NumArray(4)));
527 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
528 0 : ShowContinueError(state, "Fuel Cell model not set up for more than 5 air constituents");
529 0 : ErrorsFound = true;
530 : }
531 :
532 0 : for (int ConstitNum = 1; ConstitNum <= NumAirConstit; ++ConstitNum) {
533 0 : fuelCell.AirSup.ConstitName(ConstitNum) = AlphArray(ConstitNum + 8);
534 0 : fuelCell.AirSup.ConstitMolalFract(ConstitNum) = NumArray(ConstitNum + 4);
535 : }
536 :
537 : } else { // regular air
538 1 : NumAirConstit = 5;
539 :
540 1 : fuelCell.AirSup.NumConstituents = NumAirConstit;
541 :
542 1 : fuelCell.AirSup.ConstitName(1) = "Nitrogen";
543 1 : fuelCell.AirSup.ConstitMolalFract(1) = 0.7728;
544 :
545 1 : fuelCell.AirSup.ConstitName(2) = "Oxygen";
546 1 : fuelCell.AirSup.ConstitMolalFract(2) = 0.2073;
547 :
548 1 : fuelCell.AirSup.ConstitName(3) = "Water";
549 1 : fuelCell.AirSup.ConstitMolalFract(3) = 0.0104;
550 :
551 1 : fuelCell.AirSup.ConstitName(4) = "Argon";
552 1 : fuelCell.AirSup.ConstitMolalFract(4) = 0.0092;
553 :
554 1 : fuelCell.AirSup.ConstitName(5) = "CarbonDioxide";
555 1 : fuelCell.AirSup.ConstitMolalFract(5) = 0.0003;
556 : }
557 :
558 : // check for molar fractions summing to 1.0.
559 1 : if (std::abs(sum(fuelCell.AirSup.ConstitMolalFract) - 1.0) > 0.0001) {
560 :
561 0 : ShowSevereError(state, format("{} molar fractions do not sum to 1.0", s_ipsc->cCurrentModuleObject));
562 0 : ShowContinueError(state, format("..Sum was={:.1R}", sum(fuelCell.AirSup.ConstitMolalFract)));
563 0 : ShowContinueError(state, format("Entered in {} = {}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
564 0 : ErrorsFound = true;
565 : }
566 :
567 : // check for other FuelCell using the same Air Supply module and fill
568 1 : for (int otherFuelCell = fuelCellNum + 1; otherFuelCell <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++otherFuelCell) {
569 0 : if (Util::SameString(state.dataFuelCellElectGen->FuelCell(otherFuelCell).AirSup.Name, fuelCell.AirSup.Name)) {
570 0 : state.dataFuelCellElectGen->FuelCell(otherFuelCell).AirSup = fuelCell.AirSup;
571 : }
572 : }
573 : } else {
574 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
575 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
576 0 : ErrorsFound = true;
577 : }
578 : }
579 :
580 2 : for (int GeneratorNum = 1; GeneratorNum <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++GeneratorNum) {
581 : // find molar fraction of oxygen in air supply
582 1 : int thisConstituent = Util::FindItem("Oxygen",
583 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.ConstitName,
584 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.NumConstituents);
585 1 : if (thisConstituent > 0)
586 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.O2fraction =
587 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.ConstitMolalFract(thisConstituent);
588 :
589 : // Loop over air constituents and do one-time setup
590 6 : for (int i = 1; i <= state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.NumConstituents; ++i) {
591 :
592 5 : std::string thisName = state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.ConstitName(i);
593 :
594 5 : int thisGasID = Util::FindItem(
595 5 : thisName, state.dataGenerator->GasPhaseThermoChemistryData, &DataGenerators::GasPropertyDataStruct::ConstituentName);
596 :
597 5 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).AirSup.GasLibID(i) = static_cast<GasID>(thisGasID);
598 5 : }
599 :
600 : // set up gas constituents for product gases
601 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).FCPM.GasLibID(1) = GasID::CarbonDioxide;
602 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).FCPM.GasLibID(2) = GasID::Nitrogen;
603 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).FCPM.GasLibID(3) = GasID::Oxygen;
604 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).FCPM.GasLibID(4) = GasID::Water;
605 1 : state.dataFuelCellElectGen->FuelCell(GeneratorNum).FCPM.GasLibID(5) = GasID::Argon;
606 : }
607 :
608 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:WaterSupply";
609 1 : int NumFCWaterSups = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
610 :
611 1 : if (NumFCWaterSups <= 0) {
612 0 : ShowSevereError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
613 0 : ErrorsFound = true;
614 : }
615 :
616 2 : for (int FCWaterSupNum = 1; FCWaterSupNum <= NumFCWaterSups; ++FCWaterSupNum) {
617 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
618 1 : s_ipsc->cCurrentModuleObject,
619 : FCWaterSupNum,
620 : AlphArray,
621 : NumAlphas,
622 : NumArray,
623 : NumNums,
624 : IOStat,
625 : _,
626 1 : s_ipsc->lAlphaFieldBlanks,
627 1 : s_ipsc->cAlphaFieldNames,
628 1 : s_ipsc->cNumericFieldNames);
629 :
630 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
631 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameFCWaterSup);
632 :
633 1 : if (fuelCellNum > 0) {
634 :
635 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
636 :
637 : // this is only the first instance of a FuelCell generator using this type of Water supply module
638 1 : fuelCell.WaterSup.Name = AlphArray(1);
639 :
640 1 : if (s_ipsc->lAlphaFieldBlanks(2)) {
641 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
642 0 : ErrorsFound = true;
643 1 : } else if ((fuelCell.WaterSup.WaterSupRateCurve = Curve::GetCurve(state, AlphArray(2))) == nullptr) {
644 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), AlphArray(2));
645 0 : ErrorsFound = true;
646 : }
647 :
648 1 : if (s_ipsc->lAlphaFieldBlanks(3)) {
649 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(2));
650 0 : ErrorsFound = true;
651 1 : } else if ((fuelCell.WaterSup.PmpPowerCurve = Curve::GetCurve(state, AlphArray(3))) == nullptr) {
652 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), AlphArray(3));
653 0 : ErrorsFound = true;
654 : }
655 :
656 1 : fuelCell.WaterSup.PmpPowerLossFactor = NumArray(1);
657 :
658 1 : fuelCell.WaterSup.waterTempMode = static_cast<DataGenerators::WaterTempMode>(getEnumValue(waterTempModeNamesUC, AlphArray(4)));
659 :
660 1 : if (fuelCell.WaterSup.waterTempMode == DataGenerators::WaterTempMode::Invalid) {
661 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
662 0 : ErrorsFound = true;
663 :
664 1 : } else if (fuelCell.WaterSup.waterTempMode == DataGenerators::WaterTempMode::AirNode) {
665 0 : fuelCell.WaterSup.NodeName = AlphArray(5);
666 0 : fuelCell.WaterSup.NodeNum = NodeInputManager::GetOnlySingleNode(state,
667 0 : AlphArray(5),
668 : ErrorsFound,
669 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellWaterSupply,
670 0 : AlphArray(1),
671 : DataLoopNode::NodeFluidType::Air,
672 : DataLoopNode::ConnectionType::Sensor,
673 : NodeInputManager::CompFluidStream::Primary,
674 : DataLoopNode::ObjectIsNotParent);
675 :
676 1 : } else if (fuelCell.WaterSup.waterTempMode == DataGenerators::WaterTempMode::WaterNode) {
677 0 : fuelCell.WaterSup.NodeName = AlphArray(5);
678 0 : fuelCell.WaterSup.NodeNum = NodeInputManager::GetOnlySingleNode(state,
679 0 : AlphArray(5),
680 : ErrorsFound,
681 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellWaterSupply,
682 0 : AlphArray(1),
683 : DataLoopNode::NodeFluidType::Water,
684 : DataLoopNode::ConnectionType::Sensor,
685 : NodeInputManager::CompFluidStream::Primary,
686 : DataLoopNode::ObjectIsNotParent);
687 : }
688 :
689 1 : fuelCell.WaterSup.sched = Sched::GetSchedule(state, AlphArray(6));
690 1 : if ((fuelCell.WaterSup.sched == nullptr) && (fuelCell.WaterSup.waterTempMode == DataGenerators::WaterTempMode::Schedule)) {
691 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), AlphArray(6));
692 0 : ErrorsFound = true;
693 : }
694 :
695 : // check for other FuelCell using the same Water Supply module and fill
696 1 : for (int otherFuelCell = fuelCellNum + 1; otherFuelCell <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++otherFuelCell) {
697 0 : if (Util::SameString(state.dataFuelCellElectGen->FuelCell(otherFuelCell).WaterSup.Name, fuelCell.WaterSup.Name)) {
698 0 : state.dataFuelCellElectGen->FuelCell(otherFuelCell).WaterSup = fuelCell.WaterSup;
699 : }
700 : }
701 : } else {
702 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
703 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
704 0 : ErrorsFound = true;
705 : }
706 : }
707 :
708 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:AuxiliaryHeater";
709 1 : int NumFuelCellAuxilHeaters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
710 :
711 1 : if (NumFuelCellAuxilHeaters <= 0) {
712 0 : ShowSevereError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
713 0 : ErrorsFound = true;
714 : }
715 :
716 2 : for (int FCAuxHeatNum = 1; FCAuxHeatNum <= NumFuelCellAuxilHeaters; ++FCAuxHeatNum) {
717 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
718 1 : s_ipsc->cCurrentModuleObject,
719 : FCAuxHeatNum,
720 : AlphArray,
721 : NumAlphas,
722 : NumArray,
723 : NumNums,
724 : IOStat,
725 : _,
726 1 : s_ipsc->lAlphaFieldBlanks,
727 1 : s_ipsc->cAlphaFieldNames,
728 1 : s_ipsc->cNumericFieldNames);
729 :
730 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
731 :
732 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameFCAuxilHeat);
733 :
734 1 : if (fuelCellNum > 0) {
735 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
736 :
737 1 : fuelCell.AuxilHeat.Name = AlphArray(1);
738 1 : fuelCell.AuxilHeat.ExcessAirRAT = NumArray(1);
739 1 : fuelCell.AuxilHeat.ANC0 = NumArray(2);
740 1 : fuelCell.AuxilHeat.ANC1 = NumArray(3);
741 1 : fuelCell.AuxilHeat.UASkin = NumArray(4);
742 :
743 1 : fuelCell.AuxilHeat.SkinLossDestination =
744 1 : static_cast<DataGenerators::LossDestination>(getEnumValue(lossDestinationNamesUC, AlphArray(2)));
745 1 : if (fuelCell.AuxilHeat.SkinLossDestination == DataGenerators::LossDestination::Invalid) {
746 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), AlphArray(2));
747 0 : ErrorsFound = true;
748 1 : } else if (fuelCell.AuxilHeat.SkinLossDestination == DataGenerators::LossDestination::SurroundingZone) {
749 0 : fuelCell.AuxilHeat.ZoneName = AlphArray(3);
750 0 : fuelCell.AuxilHeat.ZoneID = Util::FindItemInList(AlphArray(3), state.dataHeatBal->Zone);
751 0 : if (fuelCell.AuxilHeat.ZoneID == 0) {
752 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), AlphArray(3));
753 0 : ErrorsFound = true;
754 : }
755 : }
756 :
757 1 : fuelCell.AuxilHeat.MaxPowerW = NumArray(5);
758 1 : fuelCell.AuxilHeat.MinPowerW = NumArray(6);
759 1 : fuelCell.AuxilHeat.MaxPowerkmolperSec = NumArray(7);
760 1 : fuelCell.AuxilHeat.MinPowerkmolperSec = NumArray(8);
761 :
762 : // TODO finish Auxiliary heater
763 :
764 : // check for other FuelCell using the same Auxiliary Heating module and fill
765 1 : for (int otherFuelCell = fuelCellNum + 1; otherFuelCell <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++otherFuelCell) {
766 0 : if (Util::SameString(state.dataFuelCellElectGen->FuelCell(otherFuelCell).AuxilHeat.Name, fuelCell.AuxilHeat.Name)) {
767 0 : state.dataFuelCellElectGen->FuelCell(otherFuelCell).AuxilHeat = fuelCell.AuxilHeat;
768 : }
769 : }
770 : } else {
771 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
772 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
773 0 : ErrorsFound = true;
774 : }
775 : }
776 :
777 : // exhaust gas heat exchanger
778 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:ExhaustGasToWaterHeatExchanger";
779 1 : int NumFCExhaustGasHXs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
780 1 : if (NumFCExhaustGasHXs <= 0) {
781 0 : ShowWarningError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
782 0 : ShowContinueError(state, format("Fuel Cell model requires an {} object", s_ipsc->cCurrentModuleObject));
783 0 : ErrorsFound = true;
784 : }
785 :
786 2 : for (int FCHXNum = 1; FCHXNum <= NumFCExhaustGasHXs; ++FCHXNum) {
787 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
788 1 : s_ipsc->cCurrentModuleObject,
789 : FCHXNum,
790 : AlphArray,
791 : NumAlphas,
792 : NumArray,
793 : NumNums,
794 : IOStat,
795 : _,
796 1 : s_ipsc->lAlphaFieldBlanks,
797 1 : s_ipsc->cAlphaFieldNames,
798 1 : s_ipsc->cNumericFieldNames);
799 :
800 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
801 :
802 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameExhaustHX);
803 :
804 1 : if (fuelCellNum > 0) {
805 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
806 :
807 1 : fuelCell.Type = DataPlant::PlantEquipmentType::Generator_FCExhaust;
808 1 : fuelCell.ExhaustHX.Name = AlphArray(1);
809 1 : fuelCell.ExhaustHX.WaterInNodeName = AlphArray(2);
810 1 : fuelCell.ExhaustHX.WaterOutNodeName = AlphArray(3);
811 : // find node ids for water path
812 1 : fuelCell.ExhaustHX.WaterInNode =
813 1 : NodeInputManager::GetOnlySingleNode(state,
814 1 : AlphArray(2),
815 : ErrorsFound,
816 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellExhaustGasToWaterHeatExchanger,
817 1 : AlphArray(1),
818 : DataLoopNode::NodeFluidType::Water,
819 : DataLoopNode::ConnectionType::Inlet,
820 : NodeInputManager::CompFluidStream::Primary,
821 : DataLoopNode::ObjectIsNotParent);
822 1 : fuelCell.ExhaustHX.WaterOutNode =
823 2 : NodeInputManager::GetOnlySingleNode(state,
824 1 : AlphArray(3),
825 : ErrorsFound,
826 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellExhaustGasToWaterHeatExchanger,
827 1 : AlphArray(1),
828 : DataLoopNode::NodeFluidType::Water,
829 : DataLoopNode::ConnectionType::Outlet,
830 : NodeInputManager::CompFluidStream::Primary,
831 : DataLoopNode::ObjectIsNotParent);
832 2 : BranchNodeConnections::TestCompSet(
833 1 : state, s_ipsc->cCurrentModuleObject, AlphArray(1), AlphArray(2), AlphArray(3), "Heat Recovery Nodes");
834 :
835 1 : fuelCell.ExhaustHX.ExhaustOutNodeName = AlphArray(4);
836 1 : fuelCell.ExhaustHX.ExhaustOutNode =
837 1 : NodeInputManager::GetOnlySingleNode(state,
838 1 : AlphArray(4),
839 : ErrorsFound,
840 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellExhaustGasToWaterHeatExchanger,
841 1 : AlphArray(1),
842 : DataLoopNode::NodeFluidType::Air,
843 : DataLoopNode::ConnectionType::Outlet,
844 : NodeInputManager::CompFluidStream::Secondary,
845 : DataLoopNode::ObjectIsNotParent);
846 :
847 1 : fuelCell.ExhaustHX.HXmodelMode = static_cast<DataGenerators::ExhaustGasHX>(getEnumValue(exhaustGasHXNamesUC, AlphArray(5)));
848 1 : if (fuelCell.ExhaustHX.HXmodelMode == DataGenerators::ExhaustGasHX::Invalid) {
849 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), AlphArray(5));
850 0 : ErrorsFound = true;
851 : }
852 1 : fuelCell.ExhaustHX.WaterVolumeFlowMax = NumArray(1);
853 1 : fuelCell.ExhaustHX.HXEffect = NumArray(2);
854 1 : fuelCell.ExhaustHX.hxs0 = NumArray(3);
855 1 : fuelCell.ExhaustHX.hxs1 = NumArray(4);
856 1 : fuelCell.ExhaustHX.hxs2 = NumArray(5);
857 1 : fuelCell.ExhaustHX.hxs3 = NumArray(6);
858 1 : fuelCell.ExhaustHX.hxs4 = NumArray(7);
859 1 : fuelCell.ExhaustHX.h0gas = NumArray(8);
860 1 : fuelCell.ExhaustHX.NdotGasRef = NumArray(9);
861 1 : fuelCell.ExhaustHX.nCoeff = NumArray(10);
862 1 : fuelCell.ExhaustHX.AreaGas = NumArray(11);
863 1 : fuelCell.ExhaustHX.h0Water = NumArray(12);
864 1 : fuelCell.ExhaustHX.NdotWaterRef = NumArray(13);
865 1 : fuelCell.ExhaustHX.mCoeff = NumArray(14);
866 1 : fuelCell.ExhaustHX.AreaWater = NumArray(15);
867 1 : fuelCell.ExhaustHX.Fadjust = NumArray(16);
868 1 : fuelCell.ExhaustHX.l1Coeff = NumArray(17);
869 1 : fuelCell.ExhaustHX.l2Coeff = NumArray(18);
870 1 : fuelCell.ExhaustHX.CondensationThresholdTemp = NumArray(19);
871 :
872 : // store cooling water volume flow rate for autosizing system
873 1 : PlantUtilities::RegisterPlantCompDesignFlow(state, fuelCell.ExhaustHX.WaterInNode, fuelCell.ExhaustHX.WaterVolumeFlowMax);
874 : } else {
875 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
876 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
877 0 : ErrorsFound = true;
878 : }
879 : }
880 :
881 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:ElectricalStorage";
882 1 : int NumFCElecStorageUnits = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
883 :
884 1 : if (NumFCElecStorageUnits <= 0) {
885 0 : ShowWarningError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
886 0 : ShowContinueError(state, format("Fuel Cell model requires an {} object", s_ipsc->cCurrentModuleObject));
887 0 : ErrorsFound = true;
888 : }
889 :
890 2 : for (int StorageNum = 1; StorageNum <= NumFCElecStorageUnits; ++StorageNum) {
891 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
892 1 : s_ipsc->cCurrentModuleObject,
893 : StorageNum,
894 : AlphArray,
895 : NumAlphas,
896 : NumArray,
897 : NumNums,
898 : IOStat,
899 : _,
900 : _,
901 1 : s_ipsc->cAlphaFieldNames,
902 1 : s_ipsc->cNumericFieldNames);
903 :
904 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
905 :
906 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameElecStorage);
907 :
908 1 : if (fuelCellNum > 0) {
909 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
910 1 : fuelCell.ElecStorage.Name = AlphArray(1);
911 :
912 1 : if (Util::SameString(AlphArray(2), "SimpleEfficiencyWithConstraints")) {
913 1 : fuelCell.ElecStorage.StorageModelMode = DataGenerators::ElectricalStorage::SimpleEffConstraints;
914 : } else {
915 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(2), AlphArray(2)));
916 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
917 0 : ErrorsFound = true;
918 : }
919 :
920 1 : fuelCell.ElecStorage.EnergeticEfficCharge = NumArray(1);
921 1 : fuelCell.ElecStorage.EnergeticEfficDischarge = NumArray(2);
922 1 : fuelCell.ElecStorage.NominalEnergyCapacity = NumArray(3);
923 1 : fuelCell.ElecStorage.MaxPowerDraw = NumArray(4);
924 1 : fuelCell.ElecStorage.MaxPowerStore = NumArray(5);
925 1 : fuelCell.ElecStorage.StartingEnergyStored = NumArray(6);
926 :
927 : // check for other FuelCell using the same Electrical Storage and fill
928 1 : for (int otherFuelCell = fuelCellNum + 1; otherFuelCell <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++otherFuelCell) {
929 0 : if (Util::SameString(state.dataFuelCellElectGen->FuelCell(otherFuelCell).ElecStorage.Name, fuelCell.ElecStorage.Name)) {
930 0 : state.dataFuelCellElectGen->FuelCell(otherFuelCell).ElecStorage = fuelCell.ElecStorage;
931 : }
932 : }
933 : } else {
934 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
935 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
936 0 : ErrorsFound = true;
937 : }
938 : }
939 :
940 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:Inverter";
941 1 : int NumFCPowerCondUnits = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
942 :
943 1 : if (NumFCPowerCondUnits <= 0) {
944 0 : ShowWarningError(state, format("No {} equipment specified in input file", s_ipsc->cCurrentModuleObject));
945 0 : ShowContinueError(state, format("Fuel Cell model requires a {} object", s_ipsc->cCurrentModuleObject));
946 :
947 0 : ErrorsFound = true;
948 : }
949 :
950 2 : for (int FCPCUNum = 1; FCPCUNum <= NumFCPowerCondUnits; ++FCPCUNum) {
951 3 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
952 1 : s_ipsc->cCurrentModuleObject,
953 : FCPCUNum,
954 : AlphArray,
955 : NumAlphas,
956 : NumArray,
957 : NumNums,
958 : IOStat,
959 : _,
960 1 : s_ipsc->lAlphaFieldBlanks,
961 1 : s_ipsc->cAlphaFieldNames,
962 1 : s_ipsc->cNumericFieldNames);
963 :
964 1 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
965 :
966 1 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameInverter);
967 :
968 1 : if (fuelCellNum > 0) {
969 1 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
970 1 : fuelCell.Inverter.Name = AlphArray(1);
971 :
972 1 : fuelCell.Inverter.EffMode =
973 1 : static_cast<DataGenerators::InverterEfficiencyMode>(getEnumValue(inverterEfficiencyModeNamesUC, AlphArray(2)));
974 1 : if (fuelCell.Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Invalid) {
975 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(2), AlphArray(2));
976 0 : ErrorsFound = true;
977 : }
978 :
979 1 : fuelCell.Inverter.ConstEff = NumArray(1);
980 :
981 1 : if (s_ipsc->lAlphaFieldBlanks(3)) {
982 0 : if (fuelCell.Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Quadratic) {
983 0 : ShowSevereEmptyField(state, eoh, s_ipsc->cAlphaFieldNames(3));
984 0 : ErrorsFound = true;
985 : }
986 1 : } else if ((fuelCell.Inverter.EffQuadraticCurve = Curve::GetCurve(state, AlphArray(3))) == nullptr) {
987 0 : if (fuelCell.Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Quadratic) {
988 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), AlphArray(3));
989 0 : ErrorsFound = true;
990 : }
991 : }
992 :
993 : // check for other FuelCell using the same Inverter and fill
994 1 : for (int otherFuelCell = fuelCellNum + 1; otherFuelCell <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++otherFuelCell) {
995 0 : if (Util::SameString(state.dataFuelCellElectGen->FuelCell(otherFuelCell).Inverter.Name, fuelCell.Inverter.Name)) {
996 0 : state.dataFuelCellElectGen->FuelCell(otherFuelCell).Inverter = fuelCell.Inverter;
997 : }
998 : }
999 : } else {
1000 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
1001 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
1002 0 : ErrorsFound = true;
1003 : }
1004 : }
1005 :
1006 1 : s_ipsc->cCurrentModuleObject = "Generator:FuelCell:StackCooler";
1007 1 : int NumFCStackCoolers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
1008 :
1009 1 : if (NumFCStackCoolers > 0) { // get stack cooler input data
1010 0 : for (int FCScoolNum = 1; FCScoolNum <= NumFCStackCoolers; ++FCScoolNum) {
1011 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
1012 0 : s_ipsc->cCurrentModuleObject,
1013 : FCScoolNum,
1014 : AlphArray,
1015 : NumAlphas,
1016 : NumArray,
1017 : NumNums,
1018 : IOStat,
1019 : _,
1020 0 : s_ipsc->lAlphaFieldBlanks,
1021 0 : s_ipsc->cAlphaFieldNames,
1022 0 : s_ipsc->cNumericFieldNames);
1023 :
1024 0 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
1025 :
1026 0 : int fuelCellNum = Util::FindItemInList(AlphArray(1), state.dataFuelCellElectGen->FuelCell, &FCDataStruct::NameStackCooler);
1027 :
1028 0 : if (fuelCellNum > 0) {
1029 0 : auto &fuelCell = state.dataFuelCellElectGen->FuelCell(fuelCellNum);
1030 0 : fuelCell.Type = DataPlant::PlantEquipmentType::Generator_FCStackCooler;
1031 0 : fuelCell.StackCooler.Name = AlphArray(1);
1032 0 : fuelCell.StackCooler.WaterInNodeName = AlphArray(2);
1033 :
1034 0 : fuelCell.StackCooler.WaterOutNodeName = AlphArray(3);
1035 :
1036 0 : fuelCell.StackCooler.WaterInNode =
1037 0 : NodeInputManager::GetOnlySingleNode(state,
1038 0 : AlphArray(2),
1039 : ErrorsFound,
1040 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellStackCooler,
1041 0 : AlphArray(1),
1042 : DataLoopNode::NodeFluidType::Water,
1043 : DataLoopNode::ConnectionType::Inlet,
1044 : NodeInputManager::CompFluidStream::Primary,
1045 : DataLoopNode::ObjectIsNotParent);
1046 0 : fuelCell.StackCooler.WaterOutNode =
1047 0 : NodeInputManager::GetOnlySingleNode(state,
1048 0 : AlphArray(3),
1049 : ErrorsFound,
1050 : DataLoopNode::ConnectionObjectType::GeneratorFuelCellStackCooler,
1051 0 : AlphArray(1),
1052 : DataLoopNode::NodeFluidType::Water,
1053 : DataLoopNode::ConnectionType::Outlet,
1054 : NodeInputManager::CompFluidStream::Primary,
1055 : DataLoopNode::ObjectIsNotParent);
1056 0 : BranchNodeConnections::TestCompSet(
1057 0 : state, s_ipsc->cCurrentModuleObject, AlphArray(1), AlphArray(2), AlphArray(3), "Heat Recovery Nodes");
1058 :
1059 0 : fuelCell.StackCooler.TstackNom = NumArray(1);
1060 0 : fuelCell.StackCooler.TstackActual = NumArray(2);
1061 0 : fuelCell.StackCooler.r0 = NumArray(3);
1062 0 : fuelCell.StackCooler.r1 = NumArray(4);
1063 0 : fuelCell.StackCooler.r2 = NumArray(5);
1064 0 : fuelCell.StackCooler.r3 = NumArray(6);
1065 0 : fuelCell.StackCooler.MdotStackCoolant = NumArray(7);
1066 0 : fuelCell.StackCooler.UAs_cool = NumArray(8);
1067 0 : fuelCell.StackCooler.Fs_cogen = NumArray(9);
1068 0 : fuelCell.StackCooler.As_cogen = NumArray(10);
1069 0 : fuelCell.StackCooler.MdotCogenNom = NumArray(11);
1070 0 : fuelCell.StackCooler.hCogenNom = NumArray(12);
1071 0 : fuelCell.StackCooler.ns = NumArray(13);
1072 0 : fuelCell.StackCooler.PstackPumpEl = NumArray(14);
1073 0 : fuelCell.StackCooler.PmpPowerLossFactor = NumArray(15);
1074 0 : fuelCell.StackCooler.f0 = NumArray(16);
1075 0 : fuelCell.StackCooler.f1 = NumArray(17);
1076 0 : fuelCell.StackCooler.f1 = NumArray(18);
1077 :
1078 0 : fuelCell.StackCooler.StackCoolerPresent = true;
1079 :
1080 : } else {
1081 0 : ShowSevereError(state, format("Invalid, {} = {}", s_ipsc->cAlphaFieldNames(1), AlphArray(1)));
1082 0 : ShowContinueError(state, format("Entered in {}={}", s_ipsc->cCurrentModuleObject, AlphArray(1)));
1083 0 : ErrorsFound = true;
1084 : }
1085 : }
1086 : }
1087 :
1088 1 : if (ErrorsFound) {
1089 0 : ShowFatalError(state, "Errors found in getting input for fuel cell model ");
1090 : }
1091 :
1092 2 : for (int genNum = 1; genNum <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++genNum) {
1093 1 : auto &thisGen = state.dataFuelCellElectGen->FuelCell(genNum);
1094 1 : thisGen.setupOutputVars(state);
1095 : }
1096 1 : }
1097 :
1098 1 : void FCDataStruct::setupOutputVars(EnergyPlusData &state)
1099 : {
1100 2 : SetupOutputVariable(state,
1101 : "Generator Produced AC Electricity Rate",
1102 : Constant::Units::W,
1103 1 : this->Report.ACPowerGen,
1104 : OutputProcessor::TimeStepType::System,
1105 : OutputProcessor::StoreType::Average,
1106 1 : this->Name);
1107 :
1108 2 : SetupOutputVariable(state,
1109 : "Generator Produced AC Electricity Energy",
1110 : Constant::Units::J,
1111 1 : this->Report.ACEnergyGen,
1112 : OutputProcessor::TimeStepType::System,
1113 : OutputProcessor::StoreType::Sum,
1114 1 : this->Name,
1115 : Constant::eResource::ElectricityProduced,
1116 : OutputProcessor::Group::Plant,
1117 : OutputProcessor::EndUseCat::Cogeneration);
1118 :
1119 2 : SetupOutputVariable(state,
1120 : "Generator Produced Thermal Rate",
1121 : Constant::Units::W,
1122 1 : this->Report.qHX,
1123 : OutputProcessor::TimeStepType::System,
1124 : OutputProcessor::StoreType::Average,
1125 1 : this->Name);
1126 :
1127 2 : SetupOutputVariable(state,
1128 : "Generator Produced Thermal Energy",
1129 : Constant::Units::J,
1130 1 : this->Report.HXenergy,
1131 : OutputProcessor::TimeStepType::System,
1132 : OutputProcessor::StoreType::Sum,
1133 1 : this->Name,
1134 : Constant::eResource::EnergyTransfer,
1135 : OutputProcessor::Group::Plant,
1136 : OutputProcessor::EndUseCat::Cogeneration);
1137 :
1138 2 : SetupOutputVariable(state,
1139 : "Generator Fuel HHV Basis Energy",
1140 : Constant::Units::J,
1141 1 : this->Report.FuelEnergyHHV,
1142 : OutputProcessor::TimeStepType::System,
1143 : OutputProcessor::StoreType::Sum,
1144 1 : this->Name,
1145 : Constant::eResource::NaturalGas,
1146 : OutputProcessor::Group::Plant,
1147 : OutputProcessor::EndUseCat::Cogeneration);
1148 :
1149 2 : SetupOutputVariable(state,
1150 : "Generator Fuel HHV Basis Rate",
1151 : Constant::Units::W,
1152 1 : this->Report.FuelEnergyUseRateHHV,
1153 : OutputProcessor::TimeStepType::System,
1154 : OutputProcessor::StoreType::Average,
1155 1 : this->Name);
1156 :
1157 2 : SetupOutputVariable(state,
1158 : "Generator Zone Sensible Heat Transfer Rate",
1159 : Constant::Units::W,
1160 1 : this->Report.SkinLossPower,
1161 : OutputProcessor::TimeStepType::System,
1162 : OutputProcessor::StoreType::Average,
1163 1 : this->Name);
1164 :
1165 2 : SetupOutputVariable(state,
1166 : "Generator Zone Sensible Heat Transfer Energy",
1167 : Constant::Units::J,
1168 1 : this->Report.SkinLossEnergy,
1169 : OutputProcessor::TimeStepType::System,
1170 : OutputProcessor::StoreType::Sum,
1171 1 : this->Name);
1172 :
1173 2 : SetupOutputVariable(state,
1174 : "Generator Zone Convection Heat Transfer Rate",
1175 : Constant::Units::W,
1176 1 : this->Report.SkinLossConvect,
1177 : OutputProcessor::TimeStepType::System,
1178 : OutputProcessor::StoreType::Average,
1179 1 : this->Name);
1180 :
1181 2 : SetupOutputVariable(state,
1182 : "Generator Zone Radiation Heat Transfer Rate",
1183 : Constant::Units::W,
1184 1 : this->Report.SkinLossRadiat,
1185 : OutputProcessor::TimeStepType::System,
1186 : OutputProcessor::StoreType::Average,
1187 1 : this->Name);
1188 :
1189 1 : if (this->FCPM.ZoneID > 0) {
1190 1 : SetupZoneInternalGain(state,
1191 : this->FCPM.ZoneID,
1192 : this->Name,
1193 : DataHeatBalance::IntGainType::GeneratorFuelCell,
1194 : &this->Report.SkinLossConvect,
1195 : nullptr,
1196 : &this->Report.SkinLossRadiat);
1197 : }
1198 :
1199 1 : if (state.dataGlobal->DisplayAdvancedReportVariables) { // show extra data originally needed for detailed comparative testing
1200 0 : SetupOutputVariable(state,
1201 : "Generator Air Inlet Temperature",
1202 : Constant::Units::C,
1203 0 : this->Report.TairInlet,
1204 : OutputProcessor::TimeStepType::System,
1205 : OutputProcessor::StoreType::Average,
1206 0 : this->Name);
1207 :
1208 0 : SetupOutputVariable(state,
1209 : "Generator Power Module Entering Air Temperature",
1210 : Constant::Units::C,
1211 0 : this->Report.TairIntoFCPM,
1212 : OutputProcessor::TimeStepType::System,
1213 : OutputProcessor::StoreType::Average,
1214 0 : this->Name);
1215 :
1216 0 : SetupOutputVariable(state,
1217 : "Generator Air Molar Flow Rate",
1218 : Constant::Units::kmol_s,
1219 0 : this->Report.NdotAir,
1220 : OutputProcessor::TimeStepType::System,
1221 : OutputProcessor::StoreType::Average,
1222 0 : this->Name);
1223 :
1224 0 : SetupOutputVariable(state,
1225 : "Generator Power Module Entering Air Enthalpy",
1226 : Constant::Units::W,
1227 0 : this->Report.TotAirInEnthalpy,
1228 : OutputProcessor::TimeStepType::System,
1229 : OutputProcessor::StoreType::Average,
1230 0 : this->Name);
1231 :
1232 0 : SetupOutputVariable(state,
1233 : "Generator Blower Electricity Rate",
1234 : Constant::Units::W,
1235 0 : this->Report.BlowerPower,
1236 : OutputProcessor::TimeStepType::System,
1237 : OutputProcessor::StoreType::Average,
1238 0 : this->Name);
1239 :
1240 0 : SetupOutputVariable(state,
1241 : "Generator Blower Electricity Energy",
1242 : Constant::Units::J,
1243 0 : this->Report.BlowerEnergy,
1244 : OutputProcessor::TimeStepType::System,
1245 : OutputProcessor::StoreType::Sum,
1246 0 : this->Name);
1247 :
1248 0 : SetupOutputVariable(state,
1249 : "Generator Blower Skin Heat Loss Rate",
1250 : Constant::Units::W,
1251 0 : this->Report.BlowerSkinLoss,
1252 : OutputProcessor::TimeStepType::System,
1253 : OutputProcessor::StoreType::Average,
1254 0 : this->Name);
1255 :
1256 0 : SetupOutputVariable(state,
1257 : "Generator Fuel Inlet Temperature",
1258 : Constant::Units::C,
1259 0 : this->Report.TfuelInlet,
1260 : OutputProcessor::TimeStepType::System,
1261 : OutputProcessor::StoreType::Average,
1262 0 : this->Name);
1263 :
1264 0 : SetupOutputVariable(state,
1265 : "Generator Power Module Entering Fuel Temperature",
1266 : Constant::Units::C,
1267 0 : this->Report.TfuelIntoFCPM,
1268 : OutputProcessor::TimeStepType::System,
1269 : OutputProcessor::StoreType::Average,
1270 0 : this->Name);
1271 :
1272 0 : SetupOutputVariable(state,
1273 : "Generator Fuel Molar Flow Rate",
1274 : Constant::Units::kmol_s,
1275 0 : this->Report.NdotFuel,
1276 : OutputProcessor::TimeStepType::System,
1277 : OutputProcessor::StoreType::Average,
1278 0 : this->Name);
1279 :
1280 0 : SetupOutputVariable(state,
1281 : "Generator Fuel Consumption LHV Basis Energy",
1282 : Constant::Units::J,
1283 0 : this->Report.FuelEnergyLHV,
1284 : OutputProcessor::TimeStepType::System,
1285 : OutputProcessor::StoreType::Sum,
1286 0 : this->Name);
1287 :
1288 0 : SetupOutputVariable(state,
1289 : "Generator Fuel Consumption Rate LHV Basis",
1290 : Constant::Units::W,
1291 0 : this->Report.FuelEnergyUseRateLHV,
1292 : OutputProcessor::TimeStepType::System,
1293 : OutputProcessor::StoreType::Average,
1294 0 : this->Name);
1295 :
1296 0 : SetupOutputVariable(state,
1297 : "Generator Power Module Entering Fuel Enthalpy",
1298 : Constant::Units::W,
1299 0 : this->Report.TotFuelInEnthalpy,
1300 : OutputProcessor::TimeStepType::System,
1301 : OutputProcessor::StoreType::Average,
1302 0 : this->Name);
1303 :
1304 0 : SetupOutputVariable(state,
1305 : "Generator Fuel Compressor Electricity Rate",
1306 : Constant::Units::W,
1307 0 : this->Report.FuelCompressPower,
1308 : OutputProcessor::TimeStepType::System,
1309 : OutputProcessor::StoreType::Average,
1310 0 : this->Name);
1311 :
1312 0 : SetupOutputVariable(state,
1313 : "Generator Fuel Compressor Electricity Energy",
1314 : Constant::Units::J,
1315 0 : this->Report.FuelCompressEnergy,
1316 : OutputProcessor::TimeStepType::System,
1317 : OutputProcessor::StoreType::Sum,
1318 0 : this->Name);
1319 :
1320 0 : SetupOutputVariable(state,
1321 : "Generator Fuel Compressor Skin Heat Loss Rate",
1322 : Constant::Units::W,
1323 0 : this->Report.FuelCompressSkinLoss,
1324 : OutputProcessor::TimeStepType::System,
1325 : OutputProcessor::StoreType::Average,
1326 0 : this->Name);
1327 :
1328 0 : SetupOutputVariable(state,
1329 : "Generator Fuel Reformer Water Inlet Temperature",
1330 : Constant::Units::C,
1331 0 : this->Report.TwaterInlet,
1332 : OutputProcessor::TimeStepType::System,
1333 : OutputProcessor::StoreType::Average,
1334 0 : this->Name);
1335 :
1336 0 : SetupOutputVariable(state,
1337 : "Generator Power Module Entering Reforming Water Temperature",
1338 : Constant::Units::C,
1339 0 : this->Report.TwaterIntoFCPM,
1340 : OutputProcessor::TimeStepType::System,
1341 : OutputProcessor::StoreType::Average,
1342 0 : this->Name);
1343 :
1344 0 : SetupOutputVariable(state,
1345 : "Generator Fuel Reformer Water Molar Flow Rate",
1346 : Constant::Units::kmol_s,
1347 0 : this->Report.NdotWater,
1348 : OutputProcessor::TimeStepType::System,
1349 : OutputProcessor::StoreType::Average,
1350 0 : this->Name);
1351 :
1352 0 : SetupOutputVariable(state,
1353 : "Generator Fuel Reformer Water Pump Electricity Rate",
1354 : Constant::Units::W,
1355 0 : this->Report.WaterPumpPower,
1356 : OutputProcessor::TimeStepType::System,
1357 : OutputProcessor::StoreType::Average,
1358 0 : this->Name);
1359 :
1360 0 : SetupOutputVariable(state,
1361 : "Generator Fuel Reformer Water Pump Electricity Energy",
1362 : Constant::Units::J,
1363 0 : this->Report.WaterPumpEnergy,
1364 : OutputProcessor::TimeStepType::System,
1365 : OutputProcessor::StoreType::Sum,
1366 0 : this->Name);
1367 :
1368 0 : SetupOutputVariable(state,
1369 : "Generator Power Module Entering Reforming Water Enthalpy",
1370 : Constant::Units::W,
1371 0 : this->Report.WaterIntoFCPMEnthalpy,
1372 : OutputProcessor::TimeStepType::System,
1373 : OutputProcessor::StoreType::Average,
1374 0 : this->Name);
1375 :
1376 0 : SetupOutputVariable(state,
1377 : "Generator Product Gas Temperature",
1378 : Constant::Units::C,
1379 0 : this->Report.TprodGas,
1380 : OutputProcessor::TimeStepType::System,
1381 : OutputProcessor::StoreType::Average,
1382 0 : this->Name);
1383 :
1384 0 : SetupOutputVariable(state,
1385 : "Generator Product Gas Enthalpy",
1386 : Constant::Units::W,
1387 0 : this->Report.EnthalProdGas,
1388 : OutputProcessor::TimeStepType::System,
1389 : OutputProcessor::StoreType::Average,
1390 0 : this->Name);
1391 :
1392 0 : SetupOutputVariable(state,
1393 : "Generator Product Gas Molar Flow Rate",
1394 : Constant::Units::kmol_s,
1395 0 : this->Report.NdotProdGas,
1396 : OutputProcessor::TimeStepType::System,
1397 : OutputProcessor::StoreType::Average,
1398 0 : this->Name);
1399 :
1400 0 : SetupOutputVariable(state,
1401 : "Generator Product Gas Ar Molar Flow Rate",
1402 : Constant::Units::kmol_s,
1403 0 : this->Report.NdotProdAr,
1404 : OutputProcessor::TimeStepType::System,
1405 : OutputProcessor::StoreType::Average,
1406 0 : this->Name);
1407 :
1408 0 : SetupOutputVariable(state,
1409 : "Generator Product Gas CO2 Molar Flow Rate",
1410 : Constant::Units::kmol_s,
1411 0 : this->Report.NdotProdCO2,
1412 : OutputProcessor::TimeStepType::System,
1413 : OutputProcessor::StoreType::Average,
1414 0 : this->Name);
1415 :
1416 0 : SetupOutputVariable(state,
1417 : "Generator Product Gas H2O Vapor Molar Flow Rate",
1418 : Constant::Units::kmol_s,
1419 0 : this->Report.NdotProdH2O,
1420 : OutputProcessor::TimeStepType::System,
1421 : OutputProcessor::StoreType::Average,
1422 0 : this->Name);
1423 :
1424 0 : SetupOutputVariable(state,
1425 : "Generator Product Gas N2 Molar Flow Rate",
1426 : Constant::Units::kmol_s,
1427 0 : this->Report.NdotProdN2,
1428 : OutputProcessor::TimeStepType::System,
1429 : OutputProcessor::StoreType::Average,
1430 0 : this->Name);
1431 :
1432 0 : SetupOutputVariable(state,
1433 : "Generator Product Gas O2 Molar Flow Rate",
1434 : Constant::Units::kmol_s,
1435 0 : this->Report.NdotProdO2,
1436 : OutputProcessor::TimeStepType::System,
1437 : OutputProcessor::StoreType::Average,
1438 0 : this->Name);
1439 :
1440 0 : SetupOutputVariable(state,
1441 : "Generator Heat Recovery Exit Gas Temperature",
1442 : Constant::Units::C,
1443 0 : this->Report.THXexh,
1444 : OutputProcessor::TimeStepType::System,
1445 : OutputProcessor::StoreType::Average,
1446 0 : this->Name);
1447 :
1448 0 : SetupOutputVariable(state,
1449 : "Generator Heat Recovery Exit Gas H2O Vapor Fraction",
1450 : Constant::Units::None,
1451 0 : this->Report.WaterVaporFractExh,
1452 : OutputProcessor::TimeStepType::System,
1453 : OutputProcessor::StoreType::Average,
1454 0 : this->Name);
1455 :
1456 0 : SetupOutputVariable(state,
1457 : "Generator Heat Recovery Water Condensate Molar Flow Rate",
1458 : Constant::Units::kmol_s,
1459 0 : this->Report.CondensateRate,
1460 : OutputProcessor::TimeStepType::System,
1461 : OutputProcessor::StoreType::Average,
1462 0 : this->Name);
1463 :
1464 0 : SetupOutputVariable(state,
1465 : "Generator Inverter Loss Power",
1466 : Constant::Units::W,
1467 0 : this->Report.PCUlosses,
1468 : OutputProcessor::TimeStepType::System,
1469 : OutputProcessor::StoreType::Average,
1470 0 : this->Name);
1471 :
1472 0 : SetupOutputVariable(state,
1473 : "Generator Produced DC Electricity Rate",
1474 : Constant::Units::W,
1475 0 : this->Report.DCPowerGen,
1476 : OutputProcessor::TimeStepType::System,
1477 : OutputProcessor::StoreType::Average,
1478 0 : this->Name);
1479 :
1480 0 : SetupOutputVariable(state,
1481 : "Generator DC Power Efficiency",
1482 : Constant::Units::None,
1483 0 : this->Report.DCPowerEff,
1484 : OutputProcessor::TimeStepType::System,
1485 : OutputProcessor::StoreType::Average,
1486 0 : this->Name);
1487 :
1488 0 : SetupOutputVariable(state,
1489 : "Generator Electric Storage Charge State",
1490 : Constant::Units::J,
1491 0 : this->Report.ElectEnergyinStorage,
1492 : OutputProcessor::TimeStepType::System,
1493 : OutputProcessor::StoreType::Average,
1494 0 : this->Name);
1495 :
1496 0 : SetupOutputVariable(state,
1497 : "Generator DC Storage Charging Power",
1498 : Constant::Units::W,
1499 0 : this->Report.StoredPower,
1500 : OutputProcessor::TimeStepType::System,
1501 : OutputProcessor::StoreType::Average,
1502 0 : this->Name);
1503 :
1504 0 : SetupOutputVariable(state,
1505 : "Generator DC Storage Charging Energy",
1506 : Constant::Units::J,
1507 0 : this->Report.StoredEnergy,
1508 : OutputProcessor::TimeStepType::System,
1509 : OutputProcessor::StoreType::Sum,
1510 0 : this->Name);
1511 :
1512 0 : SetupOutputVariable(state,
1513 : "Generator DC Storage Discharging Power",
1514 : Constant::Units::W,
1515 0 : this->Report.DrawnPower,
1516 : OutputProcessor::TimeStepType::System,
1517 : OutputProcessor::StoreType::Average,
1518 0 : this->Name);
1519 :
1520 0 : SetupOutputVariable(state,
1521 : "Generator DC Storage Discharging Energy",
1522 : Constant::Units::J,
1523 0 : this->Report.DrawnEnergy,
1524 : OutputProcessor::TimeStepType::System,
1525 : OutputProcessor::StoreType::Sum,
1526 0 : this->Name);
1527 :
1528 0 : SetupOutputVariable(state,
1529 : "Generator Ancillary AC Electricity Rate",
1530 : Constant::Units::W,
1531 0 : this->Report.ACancillariesPower,
1532 : OutputProcessor::TimeStepType::System,
1533 : OutputProcessor::StoreType::Average,
1534 0 : this->Name);
1535 :
1536 0 : SetupOutputVariable(state,
1537 : "Generator Ancillary AC Electricity Energy",
1538 : Constant::Units::J,
1539 0 : this->Report.ACancillariesEnergy,
1540 : OutputProcessor::TimeStepType::System,
1541 : OutputProcessor::StoreType::Sum,
1542 0 : this->Name);
1543 :
1544 0 : SetupOutputVariable(state,
1545 : "Generator Fuel Cell Model Iteration Count",
1546 : Constant::Units::None,
1547 0 : this->Report.SeqSubstIterations,
1548 : OutputProcessor::TimeStepType::System,
1549 : OutputProcessor::StoreType::Sum,
1550 0 : this->Name);
1551 :
1552 0 : SetupOutputVariable(state,
1553 : "Generator Root Solver Iteration Count",
1554 : Constant::Units::None,
1555 0 : this->Report.RegulaFalsiIterations,
1556 : OutputProcessor::TimeStepType::System,
1557 : OutputProcessor::StoreType::Sum,
1558 0 : this->Name);
1559 :
1560 0 : SetupOutputVariable(state,
1561 : "Generator Number of Cycles",
1562 : Constant::Units::None,
1563 0 : this->Report.NumCycles,
1564 : OutputProcessor::TimeStepType::System,
1565 : OutputProcessor::StoreType::Average,
1566 0 : this->Name);
1567 :
1568 0 : SetupOutputVariable(state,
1569 : "Generator Power Module Skin Heat Loss Rate",
1570 : Constant::Units::W,
1571 0 : this->Report.FCPMSkinLoss,
1572 : OutputProcessor::TimeStepType::System,
1573 : OutputProcessor::StoreType::Average,
1574 0 : this->Name);
1575 : }
1576 1 : }
1577 :
1578 2235 : void FCDataStruct::CalcFuelCellGeneratorModel(EnergyPlusData &state,
1579 : bool const RunFlag,
1580 : Real64 const MyLoad,
1581 : [[maybe_unused]] bool const FirstHVACIteration)
1582 : {
1583 : // SUBROUTINE INFORMATION:
1584 : // AUTHOR Brent Griffith
1585 : // DATE WRITTEN Aug 2005
1586 :
1587 : // PURPOSE OF THIS SUBROUTINE:
1588 : // simulate a FuelCell generator using the Annex 42 model
1589 :
1590 : // METHODOLOGY EMPLOYED:
1591 : // curve fit of performance data:
1592 : // many subdomains such as fuel and air compressors, wa
1593 :
1594 : // REFERENCES: IEA/ECBCS Annex 42....
1595 :
1596 : // begin controls block to be moved out to GeneratorDynamics module
1597 : // If no loop demand or Generator OFF, return
1598 2235 : if (!RunFlag) {
1599 :
1600 : // TODO zero out terms as appropriate
1601 :
1602 0 : if (this->FCPM.HasBeenOn) {
1603 : // FuelCell just now beginning to shut down,
1604 :
1605 : // set Day and Time of Last Shut Down
1606 0 : this->FCPM.FractionalDayofLastShutDown =
1607 0 : double(state.dataGlobal->DayOfSim) +
1608 0 : (int(state.dataGlobal->CurrentTime) +
1609 0 : (state.dataHVACGlobal->SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
1610 : Constant::rHoursInDay;
1611 0 : this->FCPM.HasBeenOn = false;
1612 :
1613 0 : if (this->FCPM.ShutDownTime > 0.0) this->FCPM.DuringShutDown = true;
1614 : }
1615 :
1616 : // TODO check to see if still in shut down mode and using fuel.
1617 0 : if (this->FCPM.DuringShutDown) {
1618 : }
1619 :
1620 0 : return;
1621 : }
1622 :
1623 2235 : if (!this->FCPM.HasBeenOn) {
1624 : // fuel cell just turned on
1625 : // set Day and Time of Last STart Up
1626 :
1627 0 : this->FCPM.FractionalDayofLastStartUp =
1628 0 : double(state.dataGlobal->DayOfSim) +
1629 0 : (int(state.dataGlobal->CurrentTime) +
1630 0 : (state.dataHVACGlobal->SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
1631 : Constant::rHoursInDay;
1632 :
1633 0 : this->FCPM.HasBeenOn = true;
1634 0 : ++this->FCPM.NumCycles; // increment cycling counter
1635 :
1636 0 : if (this->FCPM.StartUpTime > 0.0) this->FCPM.DuringStartUp = true;
1637 : }
1638 :
1639 : // TODO deal with things when jump out if not running?
1640 2235 : if (!RunFlag) {
1641 0 : return;
1642 : }
1643 :
1644 : // Note: MyLoad (input) is Pdemand (electrical Power requested)
1645 2235 : Real64 Pdemand = MyLoad;
1646 2235 : Real64 PacAncillariesTotal = 0.0;
1647 2235 : Real64 PpcuLosses = 0.0;
1648 2235 : Real64 Pstorage = 0.0;
1649 2235 : Real64 PgridExtra = 0.0;
1650 2235 : Real64 PoutofInverter = 0.0;
1651 2235 : bool ConstrainedFCPM = false;
1652 : int SolverFlag;
1653 : int iter;
1654 : Real64 Pel;
1655 :
1656 : // BEGIN SEQUENTIAL SUBSTITUTION to handle a lot of inter-related calcs
1657 6705 : for (iter = 1; iter <= 20; ++iter) {
1658 6705 : if (iter > 1) {
1659 4470 : this->FigurePowerConditioningLosses(state, PoutofInverter, PpcuLosses);
1660 4470 : this->FigureACAncillaries(state, PacAncillariesTotal);
1661 4470 : Pdemand = MyLoad + PacAncillariesTotal + PpcuLosses;
1662 : } else {
1663 : // control Step 1a: Figure ancillary AC power draws
1664 2235 : this->FigureACAncillaries(state, PacAncillariesTotal);
1665 2235 : Pdemand = MyLoad + PacAncillariesTotal;
1666 : // Control Step 1b: Calculate losses associated with Power conditioning
1667 2235 : this->FigurePowerConditioningLosses(state, Pdemand, PpcuLosses);
1668 2235 : Pdemand += PpcuLosses;
1669 2235 : Pel = Pdemand;
1670 : }
1671 :
1672 6705 : this->Inverter.PCUlosses = PpcuLosses;
1673 :
1674 : // Control step 2: adjust for transient and startup/shut down constraints
1675 :
1676 : Real64 PelDiff;
1677 6705 : bool ConstrainedFCPMTrans = false;
1678 6705 : this->FigureTransientConstraints(state, Pel, ConstrainedFCPMTrans, PelDiff);
1679 :
1680 : // Control step 3: adjust for max and min limits on Pel
1681 :
1682 6705 : if (Pel < this->FCPM.PelMin) {
1683 27 : PelDiff += (this->FCPM.PelMin - Pel);
1684 27 : Pel = this->FCPM.PelMin;
1685 :
1686 27 : ConstrainedFCPM = true;
1687 : }
1688 6705 : if (Pel > this->FCPM.PelMax) {
1689 0 : PelDiff += (this->FCPM.PelMax - Pel);
1690 0 : Pel = this->FCPM.PelMax;
1691 0 : ConstrainedFCPM = true;
1692 : }
1693 : if (ConstrainedFCPM) {
1694 : }
1695 :
1696 6705 : this->FCPM.Pel = Pel;
1697 : // Now calculate FC models. return to controls and batter after
1698 :
1699 : // Calculation Step 1. Determine electrical Efficiency Eel
1700 :
1701 6705 : Real64 Eel = 0.0;
1702 6705 : if (this->FCPM.EffMode == DataGenerators::CurveMode::Normalized) {
1703 : // Equation (8) in FuelCell Spec modified for normalized curve
1704 :
1705 0 : Eel = this->FCPM.EffCurve->value(state, Pel / this->FCPM.NomPel) * this->FCPM.NomEff *
1706 0 : (1.0 - this->FCPM.NumCycles * this->FCPM.CyclingDegradRat) *
1707 0 : (1.0 - max((this->FCPM.NumRunHours - this->FCPM.ThreshRunHours), 0.0) * this->FCPM.OperateDegradRat);
1708 :
1709 6705 : } else if (this->FCPM.EffMode == DataGenerators::CurveMode::Direct) {
1710 : // Equation (8) in FuelCell Spec
1711 6705 : Eel = this->FCPM.EffCurve->value(state, Pel) * (1.0 - this->FCPM.NumCycles * this->FCPM.CyclingDegradRat) *
1712 6705 : (1.0 - max((this->FCPM.NumRunHours - this->FCPM.ThreshRunHours), 0.0) * this->FCPM.OperateDegradRat);
1713 : }
1714 :
1715 6705 : this->FCPM.Eel = Eel;
1716 : // Calculation Step 2. Determine fuel rate
1717 :
1718 : // fuel flow rate
1719 6705 : Real64 NdotFuel = Pel / (Eel * state.dataGenerator->FuelSupply(this->FuelSupNum).LHV * 1000000.0); // Eq. 10 solved for Ndot
1720 :
1721 6705 : this->FCPM.NdotFuel = NdotFuel;
1722 6705 : if (Pel <= 0.0) {
1723 : // TODO zero stuff before leaving
1724 0 : Pel = 0.0;
1725 0 : this->FCPM.Pel = 0.0;
1726 0 : return;
1727 : } else {
1728 :
1729 6705 : this->FCPM.Pel = Pel;
1730 : }
1731 :
1732 : // Calculation Step 3. Determine Air rate
1733 :
1734 6705 : if (this->AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::ConstantStoicsAirRat) { // MEthod 1
1735 : // molar rate coeff working variable
1736 6705 : Real64 NdotO2 = state.dataGenerator->FuelSupply(this->FuelSupNum).StoicOxygenRate * this->FCPM.NdotFuel * this->AirSup.Stoics;
1737 :
1738 6705 : this->FCPM.NdotAir = NdotO2 / this->AirSup.O2fraction;
1739 :
1740 0 : } else if (this->AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::QuadraticFuncofPel) { // MEthod 2
1741 :
1742 0 : this->FCPM.NdotAir = this->AirSup.AirFuncPelCurve->value(state, Pel) * (1 + this->AirSup.AirTempCoeff * this->AirSup.TairIntoFCPM);
1743 :
1744 0 : } else if (this->AirSup.AirSupRateMode == DataGenerators::AirSupRateMode::QuadraticFuncofNdot) { // method 3
1745 0 : this->FCPM.NdotAir =
1746 0 : this->AirSup.AirFuncNdotCurve->value(state, this->FCPM.NdotFuel) * (1 + this->AirSup.AirTempCoeff * this->AirSup.TairIntoFCPM);
1747 : }
1748 :
1749 : // Calculation Step 4. fuel compressor power
1750 :
1751 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl =
1752 6705 : Curve::CurveValue(state, state.dataGenerator->FuelSupply(this->FuelSupNum).CompPowerCurveID, this->FCPM.NdotFuel);
1753 :
1754 : // calculation Step 5, Fuel Compressor (need outlet temperature)
1755 :
1756 6705 : if (state.dataGenerator->FuelSupply(this->FuelSupNum).FuelTempMode == DataGenerators::FuelTemperatureMode::FuelInTempFromNode) {
1757 :
1758 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress =
1759 0 : state.dataLoopNodes->Node(state.dataGenerator->FuelSupply(this->FuelSupNum).NodeNum).Temp;
1760 :
1761 6705 : } else if (state.dataGenerator->FuelSupply(this->FuelSupNum).FuelTempMode == DataGenerators::FuelTemperatureMode::FuelInTempSchedule) {
1762 :
1763 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress =
1764 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).sched->getCurrentVal();
1765 : }
1766 :
1767 : // evaluate heat capacity at average temperature using shomate
1768 : Real64 Cp; // temp Heat Capacity, used in thermochemistry units of (J/mol K)
1769 6705 : Real64 Tavg = (state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress +
1770 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM) /
1771 6705 : 2.0;
1772 6705 : this->FigureFuelHeatCap(state, Tavg, Cp); // Cp in (J/mol K)
1773 :
1774 : // calculate a Temp of fuel out of compressor and into power module
1775 :
1776 6705 : if (this->FCPM.NdotFuel <= 0.0) { // just pass through, domain probably collapsed in modeling
1777 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM = state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress;
1778 : } else {
1779 6705 : if (state.dataGenerator->FuelSupply(this->FuelSupNum).NumConstituents == 0) {
1780 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM =
1781 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress;
1782 : } else {
1783 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM =
1784 6705 : ((1.0 - state.dataGenerator->FuelSupply(this->FuelSupNum).CompPowerLossFactor) *
1785 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl / (this->FCPM.NdotFuel * Cp * 1000.0)) +
1786 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress; // 1000 Cp units mol-> kmol
1787 : }
1788 : }
1789 : // calc skin losses from fuel compressor
1790 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).QskinLoss =
1791 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).CompPowerLossFactor * state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl;
1792 :
1793 6705 : if (state.dataGenerator->FuelSupply(this->FuelSupNum).QskinLoss < 0.0) {
1794 0 : ShowWarningError(state,
1795 0 : format("problem in FuelSupply.QskinLoss {:.3R}", state.dataGenerator->FuelSupply(this->FuelSupNum).QskinLoss));
1796 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).QskinLoss = 0.0;
1797 : }
1798 :
1799 : // calculate total fuel enthalpy coming into power module
1800 :
1801 : // (Hmolfuel in KJ/mol)
1802 : Real64 Hmolfuel; // temp enthalpy of fuel mixture in KJ/mol
1803 6705 : this->FigureFuelEnthalpy(state, state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM, Hmolfuel);
1804 :
1805 : // units, NdotFuel in kmol/sec. Hmolfule in KJ/mol ,
1806 : // factor of 1000's to get to J/s or watts
1807 6705 : this->FCPM.TotFuelInEnthalpy = Hmolfuel * 1000.0 * this->FCPM.NdotFuel * 1000.0;
1808 :
1809 : // Calculation Step 6, water compressor calculations
1810 :
1811 : // calculate water consumption
1812 :
1813 6705 : this->FCPM.NdotLiqwater = this->WaterSup.WaterSupRateCurve->value(state, this->FCPM.NdotFuel);
1814 :
1815 : // set inlet temp. (could move to init)
1816 :
1817 6705 : switch (this->WaterSup.waterTempMode) {
1818 0 : case DataGenerators::WaterTempMode::Mains: {
1819 0 : this->WaterSup.TwaterIntoCompress = state.dataEnvrn->WaterMainsTemp;
1820 0 : } break;
1821 0 : case DataGenerators::WaterTempMode::AirNode:
1822 : case DataGenerators::WaterTempMode::WaterNode: {
1823 0 : this->WaterSup.TwaterIntoCompress = state.dataLoopNodes->Node(this->WaterSup.NodeNum).Temp;
1824 0 : } break;
1825 6705 : case DataGenerators::WaterTempMode::Schedule: {
1826 6705 : this->WaterSup.TwaterIntoCompress = this->WaterSup.sched->getCurrentVal();
1827 6705 : } break;
1828 0 : default:
1829 0 : break;
1830 : }
1831 :
1832 6705 : this->WaterSup.PwaterCompEl = this->WaterSup.PmpPowerCurve->value(state, this->FCPM.NdotLiqwater);
1833 :
1834 : // 75.325 J/mol K Water at 0.1 MPa and 298 K, reference NIST WEBBOOK
1835 : Real64 CpWater; // heat capacity of water in molar units
1836 6705 : FigureLiquidWaterHeatCap(this->WaterSup.TwaterIntoCompress, CpWater);
1837 :
1838 6705 : if (this->FCPM.NdotLiqwater <= 0.0) { // just pass through, domain probably collapsed in modeling
1839 6705 : this->WaterSup.TwaterIntoFCPM = this->WaterSup.TwaterIntoCompress;
1840 : } else {
1841 :
1842 0 : this->WaterSup.TwaterIntoFCPM =
1843 0 : ((1 - this->WaterSup.PmpPowerLossFactor) * this->WaterSup.PwaterCompEl / (this->FCPM.NdotLiqwater * CpWater * 1000.0)) +
1844 0 : this->WaterSup.TwaterIntoCompress;
1845 : }
1846 :
1847 6705 : this->WaterSup.QskinLoss = this->WaterSup.PmpPowerLossFactor * this->WaterSup.PwaterCompEl;
1848 :
1849 6705 : if (this->WaterSup.QskinLoss < 0.0) {
1850 0 : this->WaterSup.QskinLoss = 0.0;
1851 : }
1852 :
1853 : Real64 HLiqWater; // temp enthalpy of liquid water in KJ/mol No Formation
1854 6705 : FigureLiquidWaterEnthalpy(this->WaterSup.TwaterIntoFCPM, HLiqWater); // HLiqWater in KJ/mol
1855 :
1856 6705 : this->FCPM.WaterInEnthalpy = this->FCPM.NdotLiqwater * HLiqWater * 1000.0 * 1000.0;
1857 :
1858 : // Calculation Step 7, Air compressor
1859 :
1860 6705 : this->AirSup.TairIntoBlower = state.dataLoopNodes->Node(this->AirSup.SupNodeNum).Temp;
1861 :
1862 6705 : this->AirSup.PairCompEl = this->AirSup.BlowerPowerCurve->value(state, this->FCPM.NdotAir);
1863 :
1864 6705 : Tavg = (this->AirSup.TairIntoBlower + this->AirSup.TairIntoFCPM) / 2.0;
1865 :
1866 6705 : this->FigureAirHeatCap(state, Tavg, Cp); // Cp in (J/mol K)
1867 :
1868 : // if PEMFC with stack cooler, then calculate stack cooler impacts
1869 6705 : if (this->StackCooler.StackCoolerPresent) {
1870 :
1871 0 : this->StackCooler.qs_cool =
1872 0 : (this->StackCooler.r0 + this->StackCooler.r1 * (this->StackCooler.TstackActual - this->StackCooler.TstackNom)) *
1873 0 : (1 + this->StackCooler.r2 * Pel + this->StackCooler.r3 * Pel * Pel) * Pel;
1874 :
1875 0 : this->FCPM.QdotStackCool = this->StackCooler.qs_cool;
1876 : }
1877 :
1878 : // Figure heat recovery from Electrical Storage, power conditioning, and auxiliary burner
1879 :
1880 6705 : switch (this->AirSup.IntakeRecoveryMode) {
1881 0 : case DataGenerators::RecoverMode::RecoverBurnInvertBatt: {
1882 0 : this->AirSup.QintakeRecovery = this->AuxilHeat.QairIntake + this->ElecStorage.QairIntake + this->Inverter.QairIntake;
1883 0 : } break;
1884 0 : case DataGenerators::RecoverMode::RecoverAuxiliaryBurner: {
1885 0 : this->AirSup.QintakeRecovery = this->AuxilHeat.QairIntake;
1886 0 : } break;
1887 0 : case DataGenerators::RecoverMode::RecoverInverterBatt: {
1888 0 : this->AirSup.QintakeRecovery = this->ElecStorage.QairIntake + this->Inverter.QairIntake;
1889 0 : } break;
1890 0 : case DataGenerators::RecoverMode::RecoverInverter: {
1891 0 : this->AirSup.QintakeRecovery = this->Inverter.QairIntake;
1892 0 : } break;
1893 0 : case DataGenerators::RecoverMode::RecoverBattery: {
1894 0 : this->AirSup.QintakeRecovery = this->ElecStorage.QairIntake;
1895 0 : } break;
1896 6705 : case DataGenerators::RecoverMode::NoRecoveryOnAirIntake: {
1897 6705 : this->AirSup.QintakeRecovery = 0.0;
1898 6705 : } break;
1899 0 : default:
1900 0 : break;
1901 : }
1902 :
1903 6705 : if (this->FCPM.NdotAir <= 0.0) { // just pass through, domain probably collapsed in modeling
1904 0 : this->AirSup.TairIntoFCPM = this->AirSup.TairIntoBlower;
1905 :
1906 : } else {
1907 6705 : this->AirSup.TairIntoFCPM = (((1 - this->AirSup.BlowerHeatLossFactor) * this->AirSup.PairCompEl + this->AirSup.QintakeRecovery) /
1908 6705 : (this->FCPM.NdotAir * Cp * 1000.0)) +
1909 6705 : this->AirSup.TairIntoBlower; // 1000 Cp units mol-> kmol
1910 : }
1911 :
1912 6705 : this->AirSup.QskinLoss = this->AirSup.BlowerHeatLossFactor * this->AirSup.PairCompEl;
1913 :
1914 6705 : if (this->AirSup.QskinLoss < 0.0) {
1915 0 : ShowWarningError(state, format("problem in AirSup.QskinLoss {:.3R}", this->AirSup.QskinLoss));
1916 0 : this->AirSup.QskinLoss = 0.0;
1917 : }
1918 :
1919 : Real64 Hmolair; // temp enthalpy of air mixture in KJ/mol
1920 6705 : this->FigureAirEnthalpy(state, this->AirSup.TairIntoFCPM, Hmolair); // (Hmolair in KJ/mol)
1921 :
1922 : // units, NdotAir in kmol/sec.; Hmolfuel in KJ/mol ,
1923 : // factor of 1000's to get to J/s or watts
1924 6705 : this->FCPM.TotAirInEnthalpy = Hmolair * 1000.0 * this->FCPM.NdotAir * 1000.0;
1925 :
1926 : // calculation Step 8, Figure Product Gases
1927 :
1928 : // figure stoic N dot for air
1929 6705 : Real64 NdotO2 = state.dataGenerator->FuelSupply(this->FuelSupNum).StoicOxygenRate * this->FCPM.NdotFuel;
1930 :
1931 : // Air in excess of match for fuel
1932 6705 : Real64 NdotStoicAir = NdotO2 / this->AirSup.O2fraction;
1933 :
1934 : // figure excess air rate
1935 :
1936 : // Air in excess of match for fuel
1937 6705 : Real64 NdotExcessAir = this->FCPM.NdotAir - NdotStoicAir;
1938 :
1939 6705 : if (NdotExcessAir < 0) { // can't meet stoichiometric fuel reaction
1940 :
1941 0 : ShowWarningError(state, "Air flow rate into fuel cell is too low for stoichiometric fuel reaction");
1942 0 : ShowContinueError(state, format("Increase air flow in GENERATOR:FC:AIR SUPPLY object:{}", this->AirSup.Name));
1943 : }
1944 :
1945 : // figure CO2 and Water rate from products (coefs setup during one-time processing in gas phase library )
1946 :
1947 : // CO2 from reaction
1948 6705 : Real64 NdotCO2ProdGas = this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).CO2ProductGasCoef;
1949 :
1950 : // Water from reaction
1951 6705 : Real64 NdotH2OProdGas = this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).H2OProductGasCoef;
1952 :
1953 : // set product gas constituent fractions (assume five usual components)
1954 6705 : Real64 NdotCO2 = 0.0; // temp CO2 molar rate coef product gas stream
1955 6705 : Real64 NdotN2 = 0.0; // temp Nitrogen rate coef product gas stream
1956 6705 : Real64 Ndot02 = 0.0; // temp Oxygen rate coef product gas stream
1957 6705 : Real64 NdotH2O = 0.0; // temp Water rate coef product gas stream
1958 6705 : Real64 NdotAr = 0.0; // temp Argon rate coef product gas stream
1959 :
1960 : // Product gas constituents are fixed (not a user defined thing)
1961 :
1962 40230 : for (int thisGas = 1; thisGas <= this->AirSup.NumConstituents; ++thisGas) {
1963 :
1964 33525 : switch (this->AirSup.GasLibID(thisGas)) {
1965 6705 : case GasID::CarbonDioxide: {
1966 : // all the CO2 coming in plus the new CO2 from reactions
1967 6705 : NdotCO2 = NdotCO2ProdGas + this->AirSup.ConstitMolalFract(thisGas) * this->FCPM.NdotAir;
1968 6705 : } break;
1969 6705 : case GasID::Nitrogen: {
1970 : // all the nitrogen coming in
1971 6705 : NdotN2 = this->FCPM.NdotAir * this->AirSup.ConstitMolalFract(thisGas);
1972 6705 : } break;
1973 6705 : case GasID::Oxygen: {
1974 : // all the oxygen in the excess air stream
1975 6705 : Ndot02 = NdotExcessAir * this->AirSup.ConstitMolalFract(thisGas);
1976 6705 : } break;
1977 6705 : case GasID::Water: {
1978 : // all the H2O coming in plus the new H2O from reactions and the H2O from water used in reforming
1979 6705 : NdotH2O = NdotH2OProdGas + this->AirSup.ConstitMolalFract(thisGas) * this->FCPM.NdotAir;
1980 :
1981 6705 : } break;
1982 6705 : case GasID::Argon: {
1983 : // all the argon coming in.
1984 6705 : NdotAr = this->FCPM.NdotAir * this->AirSup.ConstitMolalFract(thisGas);
1985 :
1986 6705 : } break;
1987 0 : default:
1988 0 : break;
1989 : }
1990 : }
1991 :
1992 6705 : this->FCPM.NdotProdGas = NdotCO2 + NdotN2 + Ndot02 + NdotH2O + NdotAr;
1993 :
1994 : // now that we have the total, figure molar fractions
1995 :
1996 6705 : this->FCPM.ConstitMolalFract(1) = NdotCO2 / this->FCPM.NdotProdGas;
1997 :
1998 : // all the nitrogen coming in
1999 6705 : this->FCPM.ConstitMolalFract(2) = NdotN2 / this->FCPM.NdotProdGas;
2000 :
2001 : // all the oxygen in the excess air stream
2002 6705 : this->FCPM.ConstitMolalFract(3) = Ndot02 / this->FCPM.NdotProdGas;
2003 :
2004 : // all the H2O coming in plus the new H2O from reactions and the H2O from water used in reforming
2005 6705 : this->FCPM.ConstitMolalFract(4) = NdotH2O / this->FCPM.NdotProdGas;
2006 :
2007 : // all the argon coming in.
2008 6705 : this->FCPM.ConstitMolalFract(5) = NdotAr / this->FCPM.NdotProdGas;
2009 :
2010 : // HmolProdGases KJ/mol)
2011 : Real64 HmolProdGases; // enthalpy of product gas mixture in KJ/mol
2012 6705 : this->FigureProductGasesEnthalpy(state, this->FCPM.TprodGasLeavingFCPM, HmolProdGases);
2013 :
2014 : // units, NdotProdGas in kmol/sec.; HmolProdGases in KJ/mol ,
2015 : // factor of 1000's to get to J/s or watts
2016 6705 : this->FCPM.TotProdGasEnthalpy = HmolProdGases * 1000.0 * this->FCPM.NdotProdGas * 1000.0;
2017 :
2018 : // calculation Step 9, Figure Skin losses
2019 :
2020 6705 : if (this->FCPM.SkinLossMode == DataGenerators::SkinLoss::ConstantRate) {
2021 : // do nothing just use QdotSkin
2022 :
2023 0 : } else if (this->FCPM.SkinLossMode == DataGenerators::SkinLoss::UADT) {
2024 :
2025 : // get zone air temp
2026 0 : if (this->FCPM.ZoneID > 0) {
2027 0 : this->FCPM.QdotSkin = this->FCPM.UAskin * (this->FCPM.TprodGasLeavingFCPM -
2028 0 : state.dataZoneTempPredictorCorrector->zoneHeatBalance(this->FCPM.ZoneID).ZT);
2029 : }
2030 :
2031 0 : } else if (this->FCPM.SkinLossMode == DataGenerators::SkinLoss::QuadraticFuelNdot) {
2032 :
2033 0 : this->FCPM.QdotSkin = this->FCPM.SkinLossCurve->value(state, this->FCPM.NdotFuel);
2034 : }
2035 :
2036 : // calculation Step 10, AC FCPM power ancillaries
2037 :
2038 6705 : this->FCPM.PelancillariesAC = this->FCPM.ANC0 + this->FCPM.ANC1 * this->FCPM.NdotFuel;
2039 :
2040 : // calculation Step 11, Dilution air
2041 6705 : this->FigureAirEnthalpy(state, this->AirSup.TairIntoBlower, Hmolair); // (Hmolair in KJ/mol)
2042 :
2043 : // units, NdotDilutionAir in kmol/sec.; Hmolair in KJ/mol ,
2044 : // factor of 1000's to get to J/s or watts
2045 6705 : this->FCPM.DilutionAirInEnthalpy = Hmolair * 1000.0 * this->FCPM.NdotDilutionAir * 1000.0;
2046 6705 : this->FCPM.DilutionAirOutEnthalpy = this->FCPM.DilutionAirInEnthalpy + this->FCPM.StackHeatLossToDilution;
2047 :
2048 : // calculation Step 12, Calculate Reforming water out enthalpy
2049 : Real64 HGasWater; // temp enthalpy of gaseous water in KJ/mol No Formation
2050 6705 : FigureGaseousWaterEnthalpy(this->FCPM.TprodGasLeavingFCPM, HGasWater);
2051 :
2052 6705 : this->FCPM.WaterOutEnthalpy = HGasWater * 1000.0 * this->FCPM.NdotLiqwater * 1000.0;
2053 :
2054 : // calculation Step 13, Calculate Heat balance
2055 : // move all terms in Equation 7 to RHS and calculate imbalance
2056 :
2057 6705 : Real64 MagofImbalance = -this->FCPM.TotFuelInEnthalpy - this->FCPM.TotAirInEnthalpy - this->FCPM.WaterInEnthalpy -
2058 13410 : this->FCPM.DilutionAirInEnthalpy -
2059 6705 : this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).LHV * 1000000.0 -
2060 6705 : this->FCPM.PelancillariesAC + this->FCPM.Pel + this->FCPM.TotProdGasEnthalpy + this->FCPM.WaterOutEnthalpy +
2061 6705 : this->FCPM.QdotStackCool + this->FCPM.QdotSkin + this->FCPM.DilutionAirOutEnthalpy;
2062 :
2063 : // Now find a new total prod Gas Enthalpy that would result in an energy balance
2064 : // TODO check signs...
2065 6705 : Real64 tmpTotProdGasEnthalpy = this->FCPM.TotProdGasEnthalpy - MagofImbalance;
2066 :
2067 : // solve for a new TprodGasLeavingFCPM using regula falsi method
2068 :
2069 6705 : Real64 Acc = 0.01; // guessing need to refine
2070 6705 : int MaxIter = 150; // guessing need to refine
2071 6705 : SolverFlag = 0; // init
2072 6705 : Real64 tmpTprodGas = this->FCPM.TprodGasLeavingFCPM;
2073 60345 : auto f = [&state, this, tmpTotProdGasEnthalpy](Real64 const TprodGas) {
2074 : Real64 thisHmolalProdGases;
2075 60345 : this->FigureProductGasesEnthalpy(state, TprodGas, thisHmolalProdGases);
2076 60345 : return (thisHmolalProdGases * this->FCPM.NdotProdGas * 1000000.0) - tmpTotProdGasEnthalpy;
2077 6705 : };
2078 6705 : General::SolveRoot(state, Acc, MaxIter, SolverFlag, tmpTprodGas, f, DataGenerators::MinProductGasTemp, DataGenerators::MaxProductGasTemp);
2079 :
2080 6705 : if (SolverFlag == -2) {
2081 0 : ++this->SolverErr_Type2_Iter;
2082 0 : if (this->SolverErr_Type2_Iter == 1) {
2083 0 : ShowWarningError(state, "CalcFuelCellGeneratorModel: Root Solver problem, flag = -2, check signs, all positive");
2084 0 : ShowRecurringWarningErrorAtEnd(state,
2085 : "CalcFuelCellGeneratorModel: Root Solver problem, flag = -2, check signs, all positive",
2086 0 : this->SolverErr_Type2_IterIndex);
2087 : } else {
2088 0 : ShowRecurringWarningErrorAtEnd(state,
2089 : "CalcFuelCellGeneratorModel: Root Solver problem, flag = -2, check signs, all positive",
2090 0 : this->SolverErr_Type2_IterIndex);
2091 : }
2092 : }
2093 6705 : if (SolverFlag == -1) {
2094 0 : ++this->SolverErr_Type1_Iter;
2095 0 : if (this->SolverErr_Type1_Iter == 1) {
2096 0 : ShowWarningError(state,
2097 : "CalcFuelCellGeneratorModel: Root Solver problem, flag = -1, check accuracy and iterations, did not converge");
2098 0 : ShowRecurringWarningErrorAtEnd(
2099 : state,
2100 : "CalcFuelCellGeneratorModel: Root Solver problem, flag = -1, check accuracy and iterations, did not converge",
2101 0 : this->SolverErr_Type1_IterIndex);
2102 : } else {
2103 0 : ShowRecurringWarningErrorAtEnd(
2104 : state,
2105 : "CalcFuelCellGeneratorModel: Root Solver problem, flag = -1, check accuracy and iterations, did not converge",
2106 0 : this->SolverErr_Type1_IterIndex);
2107 : }
2108 : }
2109 6705 : if (SolverFlag > 0) {
2110 6705 : this->FCPM.TprodGasLeavingFCPM = tmpTprodGas;
2111 : // write(*,*) 'Number of Root Solver iterations: ', solverFlag
2112 : }
2113 :
2114 : // Control Step 3 determine interaction with electrical storage
2115 : // How much power is really going into inverter?
2116 6705 : Real64 PintoInverter = Pel + Pstorage; // Back out so we can reapply
2117 : bool ConstrainedStorage;
2118 6705 : this->ManageElectStorInteractions(state, Pdemand, PpcuLosses, ConstrainedStorage, Pstorage, PgridExtra);
2119 6705 : PintoInverter = Pel - Pstorage;
2120 : // refine power conditioning losses with more current power production
2121 :
2122 6705 : if (this->Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Constant) {
2123 6705 : PpcuLosses = (1.0 - this->Inverter.ConstEff) * PintoInverter;
2124 0 : } else if (this->Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Quadratic) {
2125 0 : PpcuLosses = (1.0 - this->Inverter.EffQuadraticCurve->value(state, PintoInverter)) * PintoInverter;
2126 : }
2127 :
2128 6705 : PoutofInverter = PintoInverter - PpcuLosses;
2129 :
2130 13410 : this->ACPowerGen = PoutofInverter - this->FCPM.PelancillariesAC - this->AirSup.PairCompEl -
2131 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl - this->WaterSup.PwaterCompEl;
2132 6705 : this->Inverter.PCUlosses = PpcuLosses;
2133 : // model assumes air intake is drawn over power conditioner to recovery heat
2134 6705 : this->Inverter.QairIntake = this->Inverter.PCUlosses;
2135 :
2136 6705 : this->CalcFuelCellAuxHeater();
2137 :
2138 6705 : this->CalcFuelCellGenHeatRecovery(state);
2139 : // calculation Step 11, If imbalance below threshold, then exit out of do loop.
2140 :
2141 6705 : if ((std::abs(MagofImbalance) < std::abs(DataGenerators::ImBalanceTol * this->FCPM.Pel)) && (iter > 2)) {
2142 2235 : break;
2143 : }
2144 :
2145 : } // sequential substitution loop
2146 :
2147 2235 : this->FCPM.SeqSubstitIter = iter;
2148 2235 : this->FCPM.RegulaFalsiIter = SolverFlag;
2149 : }
2150 :
2151 6705 : void FCDataStruct::ManageElectStorInteractions(EnergyPlusData &state,
2152 : Real64 const Pdemand,
2153 : [[maybe_unused]] Real64 const PpcuLosses,
2154 : bool &Constrained,
2155 : Real64 &Pstorage,
2156 : Real64 &PgridOverage // electricity that can't be stored and needs to go out
2157 : )
2158 : {
2159 :
2160 : // SUBROUTINE INFORMATION:
2161 : // AUTHOR B. Griffith
2162 : // DATE WRITTEN Aug 2005
2163 :
2164 : // PURPOSE OF THIS SUBROUTINE:
2165 : // manage controls and calculations related to electrical storage in FuelCell model
2166 :
2167 6705 : Real64 tmpPdraw = 0.0;
2168 6705 : Real64 tmpPcharge = 0.0;
2169 6705 : bool drawing = false; // true if drawing power
2170 6705 : bool charging = false; // true if charging
2171 6705 : Constrained = false;
2172 :
2173 : // step 1 figure out what is desired of electrical storage system
2174 :
2175 6705 : if (this->FCPM.Pel < (Pdemand)) {
2176 : // draw from storage
2177 63 : tmpPdraw = (Pdemand) - this->FCPM.Pel;
2178 63 : drawing = true;
2179 : }
2180 :
2181 6705 : if (this->FCPM.Pel > (Pdemand)) {
2182 : // add to storage
2183 0 : tmpPcharge = this->FCPM.Pel - (Pdemand);
2184 0 : charging = true;
2185 : }
2186 :
2187 : // step 2, figure out what is possible for electrical storage draws/charges
2188 :
2189 6705 : if (charging) {
2190 :
2191 0 : if (this->ElecStorage.StorageModelMode == DataGenerators::ElectricalStorage::SimpleEffConstraints) {
2192 :
2193 0 : if (this->ElecStorage.LastTimeStepStateOfCharge >= this->ElecStorage.NominalEnergyCapacity) {
2194 : // storage full! no more allowed!
2195 0 : PgridOverage = tmpPcharge;
2196 0 : tmpPcharge = 0.0;
2197 0 : Constrained = true;
2198 : }
2199 0 : if (tmpPcharge > this->ElecStorage.MaxPowerStore) {
2200 0 : PgridOverage = tmpPcharge - this->ElecStorage.MaxPowerStore;
2201 0 : tmpPcharge = this->ElecStorage.MaxPowerStore;
2202 0 : Constrained = true;
2203 : }
2204 :
2205 : // now add energy to storage from charging
2206 0 : if ((this->ElecStorage.LastTimeStepStateOfCharge +
2207 0 : tmpPcharge * state.dataHVACGlobal->TimeStepSysSec * this->ElecStorage.EnergeticEfficCharge) <
2208 0 : this->ElecStorage.NominalEnergyCapacity) {
2209 :
2210 0 : this->ElecStorage.ThisTimeStepStateOfCharge =
2211 0 : this->ElecStorage.LastTimeStepStateOfCharge +
2212 0 : tmpPcharge * state.dataHVACGlobal->TimeStepSysSec * this->ElecStorage.EnergeticEfficCharge;
2213 : } else { // would over charge this time step
2214 :
2215 0 : tmpPcharge = (this->ElecStorage.NominalEnergyCapacity - this->ElecStorage.LastTimeStepStateOfCharge) /
2216 0 : (state.dataHVACGlobal->TimeStepSysSec * this->ElecStorage.EnergeticEfficCharge);
2217 0 : Constrained = true;
2218 0 : this->ElecStorage.ThisTimeStepStateOfCharge =
2219 0 : this->ElecStorage.LastTimeStepStateOfCharge +
2220 0 : tmpPcharge * state.dataHVACGlobal->TimeStepSysSec * this->ElecStorage.EnergeticEfficCharge;
2221 : }
2222 :
2223 : // losses go into QairIntake
2224 0 : this->ElecStorage.QairIntake = tmpPcharge * (1.0 - this->ElecStorage.EnergeticEfficCharge);
2225 :
2226 0 : } else if (this->ElecStorage.StorageModelMode == DataGenerators::ElectricalStorage::LeadAcidBatterManwellMcGowan) {
2227 0 : ShowWarningError(state, "ManageElectStorInteractions: Not yet implemented: Lead Acid Battery By Manwell and McGowan 1993 ");
2228 :
2229 0 : } else if (this->ElecStorage.StorageModelMode == DataGenerators::ElectricalStorage::LeadAcidBatterySaupe) {
2230 0 : ShowWarningError(state, "ManageElectStorInteractions: Not yet implemented: Lead Acid Battery By Saupe 1993 ");
2231 :
2232 : } else {
2233 :
2234 : // should not come here
2235 : }
2236 :
2237 0 : Pstorage = tmpPcharge;
2238 :
2239 : } // charging
2240 :
2241 6705 : if (drawing) {
2242 63 : if (this->ElecStorage.StorageModelMode == DataGenerators::ElectricalStorage::SimpleEffConstraints) {
2243 :
2244 63 : if (this->ElecStorage.LastTimeStepStateOfCharge <= 0.0) {
2245 : // storage empty no more allowed!
2246 63 : tmpPdraw = 0.0;
2247 63 : Constrained = true;
2248 63 : drawing = false;
2249 : }
2250 63 : if (tmpPdraw > this->ElecStorage.MaxPowerDraw) {
2251 0 : tmpPdraw = this->ElecStorage.MaxPowerDraw;
2252 0 : Constrained = true;
2253 : }
2254 :
2255 : // now take energy from storage by drawing (amplified by energetic effic)
2256 126 : if ((this->ElecStorage.LastTimeStepStateOfCharge -
2257 63 : tmpPdraw * state.dataHVACGlobal->TimeStepSysSec / this->ElecStorage.EnergeticEfficDischarge) > 0.0) {
2258 :
2259 0 : this->ElecStorage.ThisTimeStepStateOfCharge =
2260 0 : this->ElecStorage.LastTimeStepStateOfCharge -
2261 0 : tmpPdraw * state.dataHVACGlobal->TimeStepSysSec / this->ElecStorage.EnergeticEfficDischarge;
2262 : } else { // would over drain storage this timestep so reduce tmpPdraw
2263 126 : tmpPdraw = this->ElecStorage.LastTimeStepStateOfCharge * this->ElecStorage.EnergeticEfficDischarge /
2264 63 : (state.dataHVACGlobal->TimeStepSysSec);
2265 63 : this->ElecStorage.ThisTimeStepStateOfCharge =
2266 126 : this->ElecStorage.LastTimeStepStateOfCharge -
2267 63 : tmpPdraw * state.dataHVACGlobal->TimeStepSysSec / this->ElecStorage.EnergeticEfficDischarge;
2268 :
2269 63 : Constrained = true;
2270 : }
2271 : // losses go into QairIntake
2272 63 : this->ElecStorage.QairIntake = tmpPdraw * (1.0 / this->ElecStorage.EnergeticEfficDischarge - 1.0);
2273 0 : } else if (this->ElecStorage.StorageModelMode == DataGenerators::ElectricalStorage::LeadAcidBatterManwellMcGowan) {
2274 0 : ShowWarningError(state, "ManageElectStorInteractions: Not yet implemented: Lead Acid Battery By Manwell and McGowan 1993 ");
2275 :
2276 0 : } else if (this->ElecStorage.StorageModelMode == DataGenerators::ElectricalStorage::LeadAcidBatterySaupe) {
2277 0 : ShowWarningError(state, "ManageElectStorInteractions: Not yet implemented: Lead Acid Battery By Saupe 1993 ");
2278 :
2279 : } else {
2280 :
2281 : // should not come here
2282 : }
2283 :
2284 63 : Pstorage = -tmpPdraw;
2285 :
2286 : } // drawing
2287 :
2288 6705 : if ((!charging) && (!drawing)) {
2289 :
2290 6705 : this->ElecStorage.ThisTimeStepStateOfCharge = this->ElecStorage.LastTimeStepStateOfCharge;
2291 6705 : this->ElecStorage.PelNeedFromStorage = 0.0;
2292 6705 : this->ElecStorage.PelFromStorage = 0.0;
2293 6705 : this->ElecStorage.QairIntake = 0.0;
2294 : }
2295 :
2296 6705 : if (Pstorage >= 0.0) {
2297 :
2298 6705 : this->ElecStorage.PelIntoStorage = Pstorage;
2299 6705 : this->ElecStorage.PelFromStorage = 0.0;
2300 : }
2301 6705 : if (Pstorage < 0.0) {
2302 :
2303 0 : this->ElecStorage.PelIntoStorage = 0.0;
2304 0 : this->ElecStorage.PelFromStorage = -Pstorage;
2305 : }
2306 6705 : }
2307 :
2308 6705 : void FCDataStruct::FigureAirHeatCap(EnergyPlusData &state, Real64 const FluidTemp, Real64 &Cp)
2309 : {
2310 :
2311 : // SUBROUTINE INFORMATION:
2312 : // AUTHOR B Griffith
2313 : // DATE WRITTEN August 2005
2314 :
2315 : // PURPOSE OF THIS SUBROUTINE:
2316 : // calculate Cp from Shomate equations for fuel
2317 :
2318 : // METHODOLOGY EMPLOYED:
2319 : // sum by weighting molar fractions of all Air constituents.
2320 : // assumes mixture is sum of parts.
2321 :
2322 : // REFERENCES:
2323 : // NIST Webbook on gas phase thermochemistry
2324 :
2325 : Real64 A; // shomate coeff
2326 : Real64 B; // shomate coeff
2327 : Real64 C; // shomate coeff
2328 : Real64 D; // shomate coeff
2329 : Real64 E; // shomate coeff
2330 : Real64 A1; // NASA poly coeff
2331 : Real64 A2; // NASA poly coeff
2332 : Real64 A3; // NASA poly coeff
2333 : Real64 A4; // NASA poly coeff
2334 : Real64 A5; // NASA poly coeff
2335 :
2336 : // loop through fuel constituents and sum up Cp
2337 :
2338 : // two different thermodynamic curve fits might be used
2339 :
2340 6705 : Real64 tempCp = 0.0;
2341 :
2342 6705 : Real64 const Tkel = (FluidTemp + Constant::Kelvin); // temp for NASA eq. in Kelvin
2343 6705 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2344 :
2345 6705 : Real64 const pow_2_Tsho(pow_2(Tsho));
2346 6705 : Real64 const pow_3_Tsho(pow_3(Tsho));
2347 6705 : Real64 const pow_2_Tkel(pow_2(Tkel));
2348 6705 : Real64 const pow_3_Tkel(pow_3(Tkel));
2349 6705 : Real64 const pow_4_Tkel(pow_4(Tkel));
2350 :
2351 40230 : for (int thisConstit = 1; thisConstit <= this->AirSup.NumConstituents; ++thisConstit) {
2352 33525 : int gasID = static_cast<int>(this->AirSup.GasLibID(thisConstit));
2353 33525 : if (gasID > 0) {
2354 33525 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NISTShomate) {
2355 :
2356 33525 : A = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateA;
2357 33525 : B = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateB;
2358 33525 : C = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateC;
2359 33525 : D = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateD;
2360 33525 : E = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateE;
2361 :
2362 33525 : tempCp += ((A + B * Tsho + C * pow_2_Tsho + D * pow_3_Tsho + E / pow_2_Tsho) * this->AirSup.ConstitMolalFract(thisConstit));
2363 : }
2364 :
2365 33525 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NASAPolynomial) {
2366 :
2367 0 : A1 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A1;
2368 0 : A2 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A2;
2369 0 : A3 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A3;
2370 0 : A4 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A4;
2371 0 : A5 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A5;
2372 :
2373 0 : tempCp += (A1 + A2 * Tkel + A3 * pow_2_Tkel + A4 * pow_3_Tkel + A5 * pow_4_Tkel) * DataGenerators::RinKJperMolpK *
2374 0 : this->AirSup.ConstitMolalFract(thisConstit);
2375 : }
2376 : }
2377 : }
2378 :
2379 6705 : Cp = tempCp;
2380 6705 : }
2381 :
2382 13410 : void FCDataStruct::FigureAirEnthalpy(EnergyPlusData &state, Real64 const FluidTemp, Real64 &Hair)
2383 : {
2384 :
2385 : // SUBROUTINE INFORMATION:
2386 : // AUTHOR B Griffith
2387 : // DATE WRITTEN August 2005
2388 :
2389 : // PURPOSE OF THIS SUBROUTINE:
2390 : // calculate Enthalpy from Shomate equations for fuel
2391 :
2392 : // METHODOLOGY EMPLOYED:
2393 : // sum by weighting molar fractions of all fuel constituents.
2394 : // assumes mixture is sum of parts.
2395 :
2396 : // REFERENCES:
2397 : // NIST Webbook on gas phase thermochemistry
2398 :
2399 : Real64 A; // shomate coeff
2400 : Real64 B; // shomate coeff
2401 : Real64 C; // shomate coeff
2402 : Real64 D; // shomate coeff
2403 : Real64 E; // shomate coeff
2404 : Real64 F; // shomate coeff
2405 : Real64 H; // shomate coeff
2406 : Real64 A1; // NASA poly coeff
2407 : Real64 A2; // NASA poly coeff
2408 : Real64 A3; // NASA poly coeff
2409 : Real64 A4; // NASA poly coeff
2410 : Real64 A5; // NASA poly coeff
2411 : Real64 A6; // NASA poly coeff
2412 :
2413 13410 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2414 13410 : Real64 const Tkel = (FluidTemp + Constant::Kelvin); // temp for NASA eq. in Kelvin
2415 :
2416 : // loop through fuel constituents and sum up Cp
2417 :
2418 13410 : Real64 tempHair = 0.0;
2419 :
2420 13410 : Real64 const pow_2_Tsho(pow_2(Tsho));
2421 13410 : Real64 const pow_3_Tsho(pow_3(Tsho));
2422 13410 : Real64 const pow_4_Tsho(pow_4(Tsho));
2423 13410 : Real64 const pow_2_Tkel(pow_2(Tkel));
2424 13410 : Real64 const pow_3_Tkel(pow_3(Tkel));
2425 13410 : Real64 const pow_4_Tkel(pow_4(Tkel));
2426 :
2427 80460 : for (int thisConstit = 1; thisConstit <= this->AirSup.NumConstituents; ++thisConstit) {
2428 67050 : int gasID = static_cast<int>(this->AirSup.GasLibID(thisConstit));
2429 67050 : if (gasID > 0) {
2430 67050 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NISTShomate) {
2431 :
2432 67050 : A = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateA;
2433 67050 : B = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateB;
2434 67050 : C = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateC;
2435 67050 : D = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateD;
2436 67050 : E = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateE;
2437 67050 : F = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateF;
2438 67050 : H = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateH;
2439 :
2440 67050 : Real64 HairI = (A * Tsho + B * pow_2_Tsho / 2.0 + C * pow_3_Tsho / 3.0 + D * pow_4_Tsho / 4.0 - E / Tsho + F - H);
2441 :
2442 67050 : tempHair += HairI * this->AirSup.ConstitMolalFract(thisConstit);
2443 : }
2444 67050 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NASAPolynomial) {
2445 0 : A1 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A1;
2446 0 : A2 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A2;
2447 0 : A3 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A3;
2448 0 : A4 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A4;
2449 0 : A5 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A5;
2450 0 : A6 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A6;
2451 :
2452 0 : tempHair += (((A1 + A2 * Tkel / 2.0 + A3 * pow_2_Tkel / 3.0 + A4 * pow_3_Tkel / 4.0 + A5 * pow_4_Tkel / 5.0 + A6 / Tkel) *
2453 0 : DataGenerators::RinKJperMolpK * Tkel) -
2454 0 : state.dataGenerator->GasPhaseThermoChemistryData(gasID).StdRefMolarEnthOfForm) *
2455 0 : this->AirSup.ConstitMolalFract(thisConstit);
2456 : }
2457 : }
2458 : }
2459 :
2460 13410 : Hair = tempHair;
2461 13410 : }
2462 :
2463 6705 : void FCDataStruct::FigureFuelHeatCap(EnergyPlusData &state, Real64 const FluidTemp, Real64 &Cp) const
2464 : {
2465 :
2466 : // SUBROUTINE INFORMATION:
2467 : // AUTHOR B Griffith
2468 : // DATE WRITTEN August 2005
2469 :
2470 : // PURPOSE OF THIS SUBROUTINE:
2471 : // calculate Cp from Shomate equations for fuel
2472 :
2473 : // METHODOLOGY EMPLOYED:
2474 : // sum by weighting molar fractions of all fuel constituents.
2475 : // assumes mixture is sum of parts.
2476 :
2477 : // REFERENCES:
2478 : // NIST Webbook on gas phase thermochemistry
2479 :
2480 : Real64 A; // shomate coeff
2481 : Real64 B; // shomate coeff
2482 : Real64 C; // shomate coeff
2483 : Real64 D; // shomate coeff
2484 : Real64 E; // shomate coeff
2485 : Real64 A1; // NASA poly coeff
2486 : Real64 A2; // NASA poly coeff
2487 : Real64 A3; // NASA poly coeff
2488 : Real64 A4; // NASA poly coeff
2489 : Real64 A5; // NASA poly coeff
2490 :
2491 6705 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2492 6705 : Real64 const Tkel = (FluidTemp + Constant::Kelvin); // temp for NASA eq. in Kelvin
2493 :
2494 : // loop through fuel constituents and sum up Cp
2495 :
2496 6705 : Real64 tempCp = 0.0;
2497 :
2498 6705 : Real64 const pow_2_Tsho(pow_2(Tsho));
2499 6705 : Real64 const pow_3_Tsho(pow_3(Tsho));
2500 6705 : Real64 const pow_2_Tkel(pow_2(Tkel));
2501 6705 : Real64 const pow_3_Tkel(pow_3(Tkel));
2502 6705 : Real64 const pow_4_Tkel(pow_4(Tkel));
2503 :
2504 60345 : for (int thisConstit = 1; thisConstit <= state.dataGenerator->FuelSupply(this->FuelSupNum).NumConstituents; ++thisConstit) {
2505 53640 : int gasID = state.dataGenerator->FuelSupply(this->FuelSupNum).GasLibID(thisConstit);
2506 53640 : if (gasID > 0) {
2507 53640 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NISTShomate) {
2508 :
2509 53640 : A = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateA;
2510 53640 : B = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateB;
2511 53640 : C = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateC;
2512 53640 : D = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateD;
2513 53640 : E = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateE;
2514 :
2515 107280 : tempCp += ((A + B * Tsho + C * pow_2_Tsho + D * pow_3_Tsho + E / pow_2_Tsho) *
2516 53640 : state.dataGenerator->FuelSupply(this->FuelSupNum).ConstitMolalFract(thisConstit));
2517 : }
2518 :
2519 53640 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NASAPolynomial) {
2520 0 : A1 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A1;
2521 0 : A2 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A2;
2522 0 : A3 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A3;
2523 0 : A4 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A4;
2524 0 : A5 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A5;
2525 :
2526 0 : tempCp += (A1 + A2 * Tkel + A3 * pow_2_Tkel + A4 * pow_3_Tkel + A5 * pow_4_Tkel) * DataGenerators::RinKJperMolpK *
2527 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).ConstitMolalFract(thisConstit);
2528 : }
2529 : }
2530 : }
2531 :
2532 6705 : Cp = tempCp;
2533 6705 : }
2534 :
2535 6705 : void FCDataStruct::FigureFuelEnthalpy(EnergyPlusData &state, Real64 const FluidTemp, Real64 &Hfuel) const
2536 : {
2537 :
2538 : // SUBROUTINE INFORMATION:
2539 : // AUTHOR B Griffith
2540 : // DATE WRITTEN August 2005
2541 :
2542 : // PURPOSE OF THIS SUBROUTINE:
2543 : // calculate Enthalpy from Shomate equations for fuel
2544 :
2545 : // METHODOLOGY EMPLOYED:
2546 : // sum by weighting molar fractions of all fuel constituents.
2547 : // assumes mixture is sum of parts.
2548 :
2549 : // REFERENCES:
2550 : // NIST Webbook on gas phase thermochemistry
2551 :
2552 : Real64 A; // shomate coeff
2553 : Real64 B; // shomate coeff
2554 : Real64 C; // shomate coeff
2555 : Real64 D; // shomate coeff
2556 : Real64 E; // shomate coeff
2557 : Real64 F; // shomate coeff
2558 : Real64 H; // shomate coeff
2559 : Real64 A1; // NASA poly coeff
2560 : Real64 A2; // NASA poly coeff
2561 : Real64 A3; // NASA poly coeff
2562 : Real64 A4; // NASA poly coeff
2563 : Real64 A5; // NASA poly coeff
2564 : Real64 A6; // NASA poly coeff
2565 :
2566 6705 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2567 6705 : Real64 const Tkel = (FluidTemp + Constant::Kelvin); // temp for NASA eq. in Kelvin
2568 :
2569 : // loop through fuel constituents and sum up Cp
2570 :
2571 6705 : Real64 tempHfuel = 0.0;
2572 :
2573 6705 : Real64 const pow_2_Tsho(pow_2(Tsho));
2574 6705 : Real64 const pow_3_Tsho(pow_3(Tsho));
2575 6705 : Real64 const pow_4_Tsho(pow_4(Tsho));
2576 6705 : Real64 const pow_2_Tkel(pow_2(Tkel));
2577 6705 : Real64 const pow_3_Tkel(pow_3(Tkel));
2578 6705 : Real64 const pow_4_Tkel(pow_4(Tkel));
2579 :
2580 60345 : for (int thisConstit = 1; thisConstit <= state.dataGenerator->FuelSupply(this->FuelSupNum).NumConstituents; ++thisConstit) {
2581 53640 : int gasID = state.dataGenerator->FuelSupply(this->FuelSupNum).GasLibID(thisConstit);
2582 53640 : if (gasID > 0) {
2583 53640 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NISTShomate) {
2584 53640 : A = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateA;
2585 53640 : B = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateB;
2586 53640 : C = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateC;
2587 53640 : D = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateD;
2588 53640 : E = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateE;
2589 53640 : F = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateF;
2590 53640 : H = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateH;
2591 :
2592 53640 : Real64 HfuelI = (A * Tsho + B * pow_2_Tsho / 2.0 + C * pow_3_Tsho / 3.0 + D * pow_4_Tsho / 4.0 - E / Tsho + F - H);
2593 :
2594 53640 : tempHfuel += HfuelI * state.dataGenerator->FuelSupply(this->FuelSupNum).ConstitMolalFract(thisConstit);
2595 : }
2596 53640 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NASAPolynomial) {
2597 :
2598 0 : A1 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A1;
2599 0 : A2 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A2;
2600 0 : A3 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A3;
2601 0 : A4 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A4;
2602 0 : A5 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A5;
2603 0 : A6 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A6;
2604 :
2605 0 : tempHfuel += (((A1 + A2 * Tkel / 2.0 + A3 * pow_2_Tkel / 3.0 + A4 * pow_3_Tkel / 4.0 + A5 * pow_4_Tkel / 5.0 + A6 / Tkel) *
2606 0 : DataGenerators::RinKJperMolpK * Tkel) -
2607 0 : state.dataGenerator->GasPhaseThermoChemistryData(gasID).StdRefMolarEnthOfForm) *
2608 0 : state.dataGenerator->FuelSupply(this->FuelSupNum).ConstitMolalFract(thisConstit);
2609 : }
2610 : }
2611 : }
2612 :
2613 6705 : Hfuel = tempHfuel;
2614 6705 : }
2615 :
2616 67050 : void FCDataStruct::FigureProductGasesEnthalpy(EnergyPlusData &state, Real64 const FluidTemp, Real64 &HProdGases)
2617 : {
2618 :
2619 : // SUBROUTINE INFORMATION:
2620 : // AUTHOR B Griffith
2621 : // DATE WRITTEN August 2005
2622 :
2623 : // PURPOSE OF THIS SUBROUTINE:
2624 : // calculate Enthalpy from Shomate equations for gases
2625 :
2626 : // METHODOLOGY EMPLOYED:
2627 : // sum by weighting molar fractions of all product gas constituents.
2628 : // assumes mixture is sum of parts.
2629 :
2630 : // REFERENCES:
2631 : // NIST Webbook on gas phase thermochemistry
2632 :
2633 : Real64 A; // shomate coeff
2634 : Real64 B; // shomate coeff
2635 : Real64 C; // shomate coeff
2636 : Real64 D; // shomate coeff
2637 : Real64 E; // shomate coeff
2638 : Real64 F; // shomate coeff
2639 : Real64 H; // shomate coeff
2640 : Real64 A1; // NASA poly coeff
2641 : Real64 A2; // NASA poly coeff
2642 : Real64 A3; // NASA poly coeff
2643 : Real64 A4; // NASA poly coeff
2644 : Real64 A5; // NASA poly coeff
2645 : Real64 A6; // NASA poly coeff
2646 :
2647 67050 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2648 67050 : Real64 const Tkel = (FluidTemp + Constant::Kelvin); // temp for NASA eq. in Kelvin
2649 :
2650 : // loop through fuel constituents and sum up Cp
2651 :
2652 67050 : Real64 tempHprodGases = 0.0;
2653 :
2654 67050 : Real64 const pow_2_Tsho(pow_2(Tsho));
2655 67050 : Real64 const pow_3_Tsho(pow_3(Tsho));
2656 67050 : Real64 const pow_4_Tsho(pow_4(Tsho));
2657 67050 : Real64 const pow_2_Tkel(pow_2(Tkel));
2658 67050 : Real64 const pow_3_Tkel(pow_3(Tkel));
2659 67050 : Real64 const pow_4_Tkel(pow_4(Tkel));
2660 :
2661 402300 : for (int thisConstit = 1; thisConstit <= 5; ++thisConstit) {
2662 335250 : int gasID = static_cast<int>(this->FCPM.GasLibID(thisConstit));
2663 335250 : if (gasID > 0) {
2664 335250 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NISTShomate) {
2665 335250 : A = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateA;
2666 335250 : B = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateB;
2667 335250 : C = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateC;
2668 335250 : D = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateD;
2669 335250 : E = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateE;
2670 335250 : F = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateF;
2671 335250 : H = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateH;
2672 :
2673 670500 : tempHprodGases += ((A * Tsho + B * pow_2_Tsho / 2.0 + C * pow_3_Tsho / 3.0 + D * pow_4_Tsho / 4.0 - E / Tsho + F - H) *
2674 335250 : this->FCPM.ConstitMolalFract(thisConstit));
2675 : }
2676 335250 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NASAPolynomial) {
2677 0 : A1 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A1;
2678 0 : A2 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A2;
2679 0 : A3 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A3;
2680 0 : A4 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A4;
2681 0 : A5 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A5;
2682 0 : A6 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A6;
2683 :
2684 0 : tempHprodGases += (((A1 + A2 * Tkel / 2.0 + A3 * pow_2_Tkel / 3.0 + A4 * pow_3_Tkel / 4.0 + A5 * pow_4_Tkel / 5.0 + A6 / Tkel) *
2685 0 : DataGenerators::RinKJperMolpK * Tkel) -
2686 0 : state.dataGenerator->GasPhaseThermoChemistryData(gasID).StdRefMolarEnthOfForm) *
2687 0 : this->FCPM.ConstitMolalFract(thisConstit);
2688 : }
2689 : } // gasid > 0
2690 : }
2691 :
2692 67050 : HProdGases = tempHprodGases;
2693 67050 : }
2694 :
2695 6684 : void FCDataStruct::FigureAuxilHeatGasHeatCap(EnergyPlusData &state, Real64 const FluidTemp, Real64 &Cp)
2696 : {
2697 :
2698 : // SUBROUTINE INFORMATION:
2699 : // AUTHOR Brent Griffith
2700 : // DATE WRITTEN Aug. 2005
2701 :
2702 : Real64 tempCp;
2703 : Real64 A; // shomate coeff
2704 : Real64 B; // shomate coeff
2705 : Real64 C; // shomate coeff
2706 : Real64 D; // shomate coeff
2707 : Real64 E; // shomate coeff
2708 : Real64 A1; // NASA poly coeff
2709 : Real64 A2; // NASA poly coeff
2710 : Real64 A3; // NASA poly coeff
2711 : Real64 A4; // NASA poly coeff
2712 : Real64 A5; // NASA poly coeff
2713 :
2714 6684 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2715 6684 : Real64 const Tkel = (FluidTemp + Constant::Kelvin); // temp for NASA eq. in Kelvin
2716 :
2717 : // loop through fuel constituents and sum up Cp
2718 :
2719 6684 : tempCp = 0.0;
2720 :
2721 6684 : Real64 const pow_2_Tsho(pow_2(Tsho));
2722 6684 : Real64 const pow_3_Tsho(pow_3(Tsho));
2723 6684 : Real64 const pow_2_Tkel(pow_2(Tkel));
2724 6684 : Real64 const pow_3_Tkel(pow_3(Tkel));
2725 6684 : Real64 const pow_4_Tkel(pow_4(Tkel));
2726 :
2727 100260 : for (int thisConstit = 1; thisConstit <= isize(this->AuxilHeat.GasLibID); ++thisConstit) {
2728 93576 : int gasID = static_cast<int>(this->AuxilHeat.GasLibID(thisConstit));
2729 93576 : if (gasID > 0) {
2730 33420 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NISTShomate) {
2731 :
2732 33420 : A = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateA;
2733 33420 : B = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateB;
2734 33420 : C = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateC;
2735 33420 : D = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateD;
2736 33420 : E = state.dataGenerator->GasPhaseThermoChemistryData(gasID).ShomateE;
2737 :
2738 33420 : tempCp += ((A + B * Tsho + C * pow_2_Tsho + D * pow_3_Tsho + E / pow_2_Tsho) * this->AuxilHeat.ConstitMolalFract(thisConstit));
2739 : }
2740 :
2741 33420 : if (state.dataGenerator->GasPhaseThermoChemistryData(gasID).ThermoMode == DataGenerators::ThermodynamicMode::NASAPolynomial) {
2742 0 : A1 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A1;
2743 0 : A2 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A2;
2744 0 : A3 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A3;
2745 0 : A4 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A4;
2746 0 : A5 = state.dataGenerator->GasPhaseThermoChemistryData(gasID).NASA_A5;
2747 :
2748 0 : tempCp += (A1 + A2 * Tkel + A3 * pow_2_Tkel + A4 * pow_3_Tkel + A5 * pow_4_Tkel) * DataGenerators::RinKJperMolpK *
2749 0 : this->AuxilHeat.ConstitMolalFract(thisConstit);
2750 : }
2751 : }
2752 : }
2753 :
2754 6684 : Cp = tempCp;
2755 6684 : }
2756 :
2757 6705 : void FCDataStruct::FigureGaseousWaterEnthalpy(Real64 const FluidTemp, // degree C
2758 : Real64 &HGasWater // kJ/mol
2759 : )
2760 : {
2761 :
2762 : // SUBROUTINE INFORMATION:
2763 : // AUTHOR B Griffith
2764 : // DATE WRITTEN December 2005
2765 :
2766 : // PURPOSE OF THIS SUBROUTINE:
2767 : // calculate Enthalpy from Shomate equations for gaseous water
2768 : // No enthalpy of formation in this one.
2769 :
2770 : // REFERENCES:
2771 : // NIST Webbook on gas phase thermochemistry
2772 :
2773 6705 : Real64 constexpr A = 29.0373; // shomate coeff
2774 6705 : Real64 constexpr B = 10.2573; // shomate coeff
2775 6705 : Real64 constexpr C = 2.81048; // shomate coeff
2776 6705 : Real64 constexpr D = -0.95914; // shomate coeff
2777 6705 : Real64 constexpr E = 0.11725; // shomate coeff
2778 6705 : Real64 constexpr F = -250.569; // shomate coeff
2779 6705 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2780 :
2781 6705 : HGasWater = A * Tsho + B * pow_2(Tsho) / 2.0 + C * pow_3(Tsho) / 3.0 + D * pow_4(Tsho) / 4.0 - E / Tsho + F; //- H
2782 6705 : }
2783 :
2784 6705 : void FCDataStruct::FigureLiquidWaterEnthalpy(Real64 const FluidTemp, // degree C
2785 : Real64 &HLiqWater // kJ/mol
2786 : )
2787 : {
2788 :
2789 : // SUBROUTINE INFORMATION:
2790 : // AUTHOR B Griffith
2791 : // DATE WRITTEN December 2005
2792 :
2793 : // PURPOSE OF THIS SUBROUTINE:
2794 : // calculate Enthalpy from Shomate equations for liquid water
2795 : // No enthalpy of formation in this one
2796 :
2797 : // REFERENCES:
2798 : // NIST Webbook on gas phase thermochemistry
2799 :
2800 6705 : Real64 constexpr A = -203.606; // shomate coeff
2801 6705 : Real64 constexpr B = 1523.29; // shomate coeff
2802 6705 : Real64 constexpr C = -3196.413; // shomate coeff
2803 6705 : Real64 constexpr D = 2474.455; // shomate coeff
2804 6705 : Real64 constexpr E = 3.85533; // shomate coeff
2805 6705 : Real64 constexpr F = -256.5478; // shomate coeff
2806 :
2807 6705 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0; // temp for Shomate eq in (Kelvin/1000)
2808 :
2809 6705 : HLiqWater = A * Tsho + B * pow_2(Tsho) / 2.0 + C * pow_3(Tsho) / 3.0 + D * pow_4(Tsho) / 4.0 - E / Tsho + F; //- H
2810 6705 : }
2811 :
2812 13389 : void FCDataStruct::FigureLiquidWaterHeatCap(Real64 const FluidTemp, // degree C
2813 : Real64 &Cp // (J/mol*K)
2814 : )
2815 : {
2816 :
2817 : // SUBROUTINE INFORMATION:
2818 : // AUTHOR Brent Griffith
2819 : // DATE WRITTEN December 2005
2820 :
2821 : // PURPOSE OF THIS SUBROUTINE:
2822 : // calculate shomate eq. for pure liquid water
2823 :
2824 13389 : Real64 constexpr A = -203.606; // shomate coeff
2825 13389 : Real64 constexpr B = 1523.29; // shomate coeff
2826 13389 : Real64 constexpr C = -3196.413; // shomate coeff
2827 13389 : Real64 constexpr D = 2474.455; // shomate coeff
2828 13389 : Real64 constexpr E = 3.85533; // shomate coeff
2829 13389 : Real64 const Tsho = (FluidTemp + Constant::Kelvin) / 1000.0;
2830 :
2831 13389 : Cp = A + B * Tsho + C * pow_2(Tsho) + D * pow_3(Tsho) + E / pow_2(Tsho);
2832 13389 : }
2833 :
2834 6705 : void FCDataStruct::FigureACAncillaries(EnergyPlusData &state, Real64 &PacAncill)
2835 : {
2836 :
2837 : // SUBROUTINE INFORMATION:
2838 : // AUTHOR B Griffith
2839 : // DATE WRITTEN March 2006
2840 :
2841 : // PURPOSE OF THIS SUBROUTINE:
2842 : // Calculate the AC ancillaries to determine Pel
2843 :
2844 : // Using lagged values inside a sequential substitution loop
2845 6705 : PacAncill = 0.0;
2846 : // sect. 5.9
2847 6705 : this->FCPM.PelancillariesAC = this->FCPM.ANC0 + this->FCPM.ANC1 * this->FCPM.NdotFuel;
2848 :
2849 : // sect 6.0
2850 6705 : this->AirSup.PairCompEl = this->AirSup.BlowerPowerCurve->value(state, this->FCPM.NdotAir);
2851 : // sect 7.0
2852 6705 : state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl =
2853 6705 : Curve::CurveValue(state, state.dataGenerator->FuelSupply(this->FuelSupNum).CompPowerCurveID, this->FCPM.NdotFuel);
2854 :
2855 : // sect. 8.0
2856 6705 : this->WaterSup.PwaterCompEl = this->WaterSup.PmpPowerCurve->value(state, this->FCPM.NdotLiqwater);
2857 :
2858 6705 : PacAncill = this->FCPM.PelancillariesAC + this->AirSup.PairCompEl + state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl +
2859 6705 : this->WaterSup.PwaterCompEl;
2860 6705 : }
2861 :
2862 6705 : void FCDataStruct::FigurePowerConditioningLosses(EnergyPlusData &state, Real64 const Pdemand, Real64 &PpcuLosses) const
2863 : {
2864 :
2865 : // SUBROUTINE INFORMATION:
2866 : // AUTHOR B Griffith
2867 : // DATE WRITTEN Aug 2005
2868 :
2869 : // PURPOSE OF THIS SUBROUTINE:
2870 : // Calculate inverter losses
2871 :
2872 6705 : if (this->Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Constant) {
2873 6705 : PpcuLosses = Pdemand * (1 - this->Inverter.ConstEff) / this->Inverter.ConstEff;
2874 :
2875 0 : } else if (this->Inverter.EffMode == DataGenerators::InverterEfficiencyMode::Quadratic) {
2876 :
2877 : // first use Pdemand instead of Pel to get initial estimate
2878 : Real64 lastPpcuLosses =
2879 0 : Pdemand * (1.0 - this->Inverter.EffQuadraticCurve->value(state, Pdemand)) / this->Inverter.EffQuadraticCurve->value(state, Pdemand);
2880 :
2881 0 : for (int iter = 1; iter <= 20; ++iter) { // seems like need to iterate (??) Need to investigate number and convergence success here
2882 :
2883 0 : Real64 Pel = Pdemand + lastPpcuLosses;
2884 :
2885 0 : lastPpcuLosses = (1.0 - this->Inverter.EffQuadraticCurve->value(state, Pel)) * Pel;
2886 : }
2887 :
2888 0 : PpcuLosses = lastPpcuLosses;
2889 : }
2890 6705 : }
2891 :
2892 6705 : void FCDataStruct::FigureTransientConstraints(EnergyPlusData &state,
2893 : Real64 &Pel, // DC power control setting for power module
2894 : bool &Constrained, // true if transient constraints kick in
2895 : Real64 &PelDiff // if constrained then this is the difference, positive
2896 : )
2897 : {
2898 :
2899 : // SUBROUTINE INFORMATION:
2900 : // AUTHOR Brent Griffith
2901 : // DATE WRITTEN Aug 2005
2902 :
2903 6705 : Real64 PelInput = Pel; // hold initial value of input var
2904 :
2905 6705 : Real64 CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
2906 6705 : (int(state.dataGlobal->CurrentTime) + (state.dataHVACGlobal->SysTimeElapsed +
2907 6705 : (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
2908 6705 : Constant::rHoursInDay;
2909 :
2910 : // Check if in start up and if it still should be
2911 6705 : if (this->FCPM.DuringStartUp) {
2912 :
2913 : // calculate time for end of start up period
2914 0 : Real64 EndingFractionalDay = this->FCPM.FractionalDayofLastStartUp + this->FCPM.StartUpTime / Constant::rHoursInDay;
2915 :
2916 0 : if (CurrentFractionalDay > EndingFractionalDay) {
2917 : // start up period is now over
2918 0 : this->FCPM.DuringStartUp = false;
2919 : }
2920 : }
2921 :
2922 : // Check if in shut down up and if it still should be
2923 6705 : if (this->FCPM.DuringShutDown) {
2924 :
2925 : // calculate time for end of shut down period
2926 0 : Real64 EndingFractionalDay = this->FCPM.FractionalDayofLastShutDown + this->FCPM.ShutDownTime / Constant::rHoursInDay;
2927 :
2928 0 : if (CurrentFractionalDay > EndingFractionalDay) {
2929 : // start up period is now over
2930 0 : this->FCPM.DuringShutDown = false;
2931 : }
2932 : }
2933 : // compare
2934 :
2935 6705 : if (!(this->FCPM.DuringShutDown) && !(this->FCPM.DuringStartUp)) {
2936 : // unit is neither starting or stopping and the only constraints would come from transient limits
2937 6705 : if (Pel > this->FCPM.PelLastTimeStep) { // powering up
2938 : // working variable for max allowed by transient constraint
2939 90 : Real64 MaxPel = this->FCPM.PelLastTimeStep + this->FCPM.UpTranLimit * state.dataHVACGlobal->TimeStepSysSec;
2940 90 : if (MaxPel < Pel) {
2941 39 : Pel = MaxPel;
2942 39 : Constrained = true;
2943 : } else {
2944 51 : Constrained = false;
2945 : }
2946 6615 : } else if (Pel < this->FCPM.PelLastTimeStep) { // powering down
2947 : // working variable for min allowed by transient constraint
2948 0 : Real64 MinPel = this->FCPM.PelLastTimeStep - this->FCPM.DownTranLimit * state.dataHVACGlobal->TimeStepSysSec;
2949 0 : if (Pel < MinPel) {
2950 0 : Pel = MinPel;
2951 0 : Constrained = true;
2952 : } else {
2953 0 : Constrained = false;
2954 : }
2955 : } else { // the same
2956 : // do nothing
2957 6615 : Constrained = false;
2958 : }
2959 :
2960 : } // not in start up or shut down
2961 :
2962 6705 : if (this->FCPM.DuringStartUp) {
2963 : // constant during start up modeling artifact
2964 0 : Pel = this->FCPM.StartUpElectProd / this->FCPM.StartUpTime;
2965 0 : Constrained = true;
2966 : }
2967 :
2968 6705 : if (this->FCPM.DuringShutDown) {
2969 :
2970 0 : Pel = 0.0; // assumes no power generated during shut down
2971 0 : Constrained = true;
2972 : }
2973 :
2974 6705 : PelDiff = 0.0;
2975 6705 : if (Constrained) {
2976 39 : PelDiff = PelInput - Pel;
2977 : }
2978 6705 : }
2979 :
2980 6705 : void FCDataStruct::CalcFuelCellAuxHeater() // Generator number
2981 : {
2982 :
2983 : // not yet implemented, just pass product gases thru nul domain
2984 :
2985 6705 : this->AuxilHeat.TauxMix = this->FCPM.TprodGasLeavingFCPM;
2986 6705 : this->AuxilHeat.NdotAuxMix = this->FCPM.NdotProdGas;
2987 6705 : this->AuxilHeat.ConstitMolalFract = this->FCPM.ConstitMolalFract;
2988 6705 : this->AuxilHeat.GasLibID = this->FCPM.GasLibID;
2989 6705 : }
2990 :
2991 6705 : void FCDataStruct::CalcFuelCellGenHeatRecovery(EnergyPlusData &state) // Generator number
2992 : {
2993 : // SUBROUTINE INFORMATION:
2994 : // AUTHOR: Brent Griffith
2995 : // DATE WRITTEN: Aug. 2005
2996 :
2997 : // PURPOSE OF THIS SUBROUTINE:
2998 : // To perform heat recovery calculations and node updates
2999 :
3000 : // METHODOLOGY EMPLOYED:
3001 : // model exhaust gas to water heat exchanger
3002 :
3003 : // REFERENCES: Annex 42 model documentation
3004 :
3005 : static constexpr std::string_view RoutineName("CalcFuelCellGenHeatRecovery");
3006 :
3007 6705 : switch (this->ExhaustHX.HXmodelMode) {
3008 0 : case DataGenerators::ExhaustGasHX::FixedEffectiveness: { // Method 1
3009 :
3010 0 : Real64 eHX = this->ExhaustHX.HXEffect;
3011 :
3012 0 : Real64 MWwater = state.dataGenerator->GasPhaseThermoChemistryData(4).MolecularWeight;
3013 0 : Real64 NdotWater = this->ExhaustHX.WaterMassFlowRate / MWwater;
3014 0 : Real64 TwaterIn = this->ExhaustHX.WaterInletTemp;
3015 :
3016 : Real64 CpWaterMol;
3017 0 : FigureLiquidWaterHeatCap(TwaterIn, CpWaterMol);
3018 :
3019 0 : Real64 NdotGas = this->AuxilHeat.NdotAuxMix;
3020 0 : Real64 TprodGasIn = this->AuxilHeat.TauxMix;
3021 : Real64 CpProdGasMol;
3022 0 : this->FigureAuxilHeatGasHeatCap(state, TprodGasIn, CpProdGasMol); // Cp in (J/mol*K)
3023 : // factor of 1000.0 for kmol -> mol
3024 0 : Real64 NdotCp = min(NdotGas * CpProdGasMol * 1000.0, NdotWater * CpWaterMol * 1000.0);
3025 :
3026 0 : this->ExhaustHX.qHX = eHX * NdotCp * (TprodGasIn - TwaterIn);
3027 :
3028 0 : this->ExhaustHX.THXexh = TprodGasIn - this->ExhaustHX.qHX / (NdotGas * CpProdGasMol * 1000.0);
3029 :
3030 0 : Real64 Cp = state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).glycol->getSpecificHeat(state, TwaterIn, RoutineName);
3031 :
3032 0 : if (this->ExhaustHX.WaterMassFlowRate * Cp <= 0.0) {
3033 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn;
3034 : } else {
3035 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn + this->ExhaustHX.qHX / (this->ExhaustHX.WaterMassFlowRate * Cp);
3036 : }
3037 :
3038 0 : } break;
3039 0 : case DataGenerators::ExhaustGasHX::LMTDempiricalUAeff: { // method 2
3040 0 : Real64 MWwater = state.dataGenerator->GasPhaseThermoChemistryData(4).MolecularWeight;
3041 0 : Real64 NdotWater = this->ExhaustHX.WaterMassFlowRate / MWwater;
3042 0 : Real64 NdotGas = this->AuxilHeat.NdotAuxMix;
3043 :
3044 0 : Real64 UAeff = this->ExhaustHX.hxs0 + this->ExhaustHX.hxs1 * NdotWater + this->ExhaustHX.hxs2 * pow_2(NdotWater) +
3045 0 : this->ExhaustHX.hxs3 * NdotGas + this->ExhaustHX.hxs4 * pow_2(NdotGas);
3046 :
3047 0 : Real64 TauxMix = this->AuxilHeat.TauxMix;
3048 0 : Real64 TwaterIn = this->ExhaustHX.WaterInletTemp;
3049 : Real64 CpWaterMol;
3050 0 : FigureLiquidWaterHeatCap(TwaterIn, CpWaterMol);
3051 : // factor of 1000.0 for kmol -> mol
3052 0 : Real64 NdotCpWater = NdotWater * CpWaterMol * 1000.0;
3053 : Real64 CpProdGasMol;
3054 0 : this->FigureAuxilHeatGasHeatCap(state, TauxMix, CpProdGasMol); // Cp in (J/mol*K)
3055 0 : Real64 NdotCpAuxMix = NdotGas * CpProdGasMol * 1000.0;
3056 :
3057 0 : if ((NdotCpWater != 0.0) && (NdotCpAuxMix != 0.0)) { // trap divide by zero
3058 : // now evaluate Eq. 44
3059 0 : this->ExhaustHX.THXexh =
3060 0 : ((1.0 - NdotCpAuxMix / NdotCpWater) / (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) - NdotCpAuxMix / NdotCpWater)) *
3061 0 : TauxMix +
3062 0 : (std::expm1(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3063 0 : (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) - NdotCpAuxMix / NdotCpWater)) *
3064 : TwaterIn;
3065 :
3066 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn + (NdotCpAuxMix / NdotCpWater) * (TauxMix - this->ExhaustHX.THXexh); // Eq. 42
3067 :
3068 : } else {
3069 0 : this->ExhaustHX.THXexh = TauxMix;
3070 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn;
3071 : }
3072 : // ENDIF
3073 :
3074 0 : if ((this->ExhaustHX.THXexh - TwaterIn) != 0.0) { // trap divide by zero
3075 0 : this->ExhaustHX.qHX = UAeff * ((TauxMix - this->ExhaustHX.WaterOutletTemp) - (this->ExhaustHX.THXexh - TwaterIn)) /
3076 0 : std::log((TauxMix - this->ExhaustHX.WaterOutletTemp) / (this->ExhaustHX.THXexh - TwaterIn));
3077 : } else {
3078 0 : this->ExhaustHX.qHX = 0.0;
3079 : }
3080 :
3081 0 : } break;
3082 0 : case DataGenerators::ExhaustGasHX::LMTDfundementalUAeff: { // method 3
3083 0 : Real64 NdotGas = this->AuxilHeat.NdotAuxMix;
3084 0 : Real64 MWwater = state.dataGenerator->GasPhaseThermoChemistryData(4).MolecularWeight;
3085 0 : Real64 NdotWater = this->ExhaustHX.WaterMassFlowRate / MWwater;
3086 :
3087 0 : Real64 hgas = this->ExhaustHX.h0gas * std::pow(NdotGas / this->ExhaustHX.NdotGasRef, this->ExhaustHX.nCoeff); // Eq. 48
3088 0 : Real64 hwater = this->ExhaustHX.h0Water * std::pow(NdotWater / this->ExhaustHX.NdotWaterRef, this->ExhaustHX.mCoeff); // Eq. 48
3089 :
3090 : // now equation 47
3091 0 : Real64 UAeff = 1.0 / (1.0 / (hgas * this->ExhaustHX.AreaGas) + 1.0 / (hwater * this->ExhaustHX.AreaWater) + this->ExhaustHX.Fadjust);
3092 :
3093 0 : Real64 TauxMix = this->AuxilHeat.TauxMix;
3094 0 : Real64 TwaterIn = this->ExhaustHX.WaterInletTemp;
3095 : Real64 CpWaterMol;
3096 0 : FigureLiquidWaterHeatCap(TwaterIn, CpWaterMol);
3097 0 : Real64 NdotCpWater = NdotWater * CpWaterMol * 1000.0;
3098 : Real64 CpProdGasMol;
3099 0 : this->FigureAuxilHeatGasHeatCap(state, TauxMix, CpProdGasMol); // Cp in (J/mol*K)
3100 0 : Real64 NdotCpAuxMix = NdotGas * CpProdGasMol * 1000.0;
3101 :
3102 0 : if ((NdotCpWater != 0.0) && (NdotCpAuxMix != 0.0)) { // trap divide by zero
3103 : // now evaluate Eq. 44
3104 0 : this->ExhaustHX.THXexh =
3105 0 : ((1.0 - NdotCpAuxMix / NdotCpWater) / (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) - NdotCpAuxMix / NdotCpWater)) *
3106 0 : TauxMix +
3107 0 : (std::expm1(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3108 0 : (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) - NdotCpAuxMix / NdotCpWater)) *
3109 : TwaterIn;
3110 :
3111 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn + (NdotCpAuxMix / NdotCpWater) * (TauxMix - this->ExhaustHX.THXexh); // Eq. 42
3112 :
3113 : } else {
3114 0 : this->ExhaustHX.THXexh = TauxMix;
3115 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn;
3116 : }
3117 :
3118 0 : if ((this->ExhaustHX.THXexh - TwaterIn) != 0.0) { // trap divide by zero
3119 0 : this->ExhaustHX.qHX = UAeff * ((TauxMix - this->ExhaustHX.WaterOutletTemp) - (this->ExhaustHX.THXexh - TwaterIn)) /
3120 0 : std::log((TauxMix - this->ExhaustHX.WaterOutletTemp) / (this->ExhaustHX.THXexh - TwaterIn));
3121 : } else {
3122 0 : this->ExhaustHX.qHX = 0.0;
3123 : }
3124 :
3125 0 : } break;
3126 6705 : case DataGenerators::ExhaustGasHX::Condensing: { // method 4
3127 6705 : if (this->ExhaustHX.WaterMassFlowRate != 0.0) {
3128 :
3129 6684 : Real64 MWwater = state.dataGenerator->GasPhaseThermoChemistryData(4).MolecularWeight;
3130 6684 : Real64 NdotWater = this->ExhaustHX.WaterMassFlowRate / MWwater;
3131 6684 : Real64 NdotGas = this->AuxilHeat.NdotAuxMix;
3132 :
3133 6684 : Real64 UAeff = this->ExhaustHX.hxs0 + this->ExhaustHX.hxs1 * NdotWater + this->ExhaustHX.hxs2 * pow_2(NdotWater) +
3134 6684 : this->ExhaustHX.hxs3 * NdotGas + this->ExhaustHX.hxs4 * pow_2(NdotGas);
3135 :
3136 6684 : Real64 TauxMix = this->AuxilHeat.TauxMix;
3137 6684 : Real64 TwaterIn = this->ExhaustHX.WaterInletTemp;
3138 : Real64 CpWaterMol;
3139 6684 : FigureLiquidWaterHeatCap(TwaterIn, CpWaterMol);
3140 6684 : Real64 NdotCpWater = NdotWater * CpWaterMol * 1000.0;
3141 : Real64 CpProdGasMol;
3142 6684 : this->FigureAuxilHeatGasHeatCap(state, TauxMix, CpProdGasMol); // Cp in (J/mol*K)
3143 6684 : Real64 NdotCpAuxMix = NdotGas * CpProdGasMol * 1000.0;
3144 :
3145 : // find water fraction in incoming gas stream
3146 100260 : for (int i = 1; i <= isize(this->AuxilHeat.GasLibID); ++i) {
3147 93576 : if (this->AuxilHeat.GasLibID(i) == GasID::Water) this->ExhaustHX.WaterVaporFractExh = this->AuxilHeat.ConstitMolalFract(i);
3148 : }
3149 6684 : Real64 NdotWaterVapor = this->ExhaustHX.WaterVaporFractExh * NdotGas;
3150 :
3151 6684 : Real64 TcondThresh = this->ExhaustHX.CondensationThresholdTemp;
3152 6684 : Real64 hxl1 = this->ExhaustHX.l1Coeff;
3153 6684 : Real64 hxl2 = this->ExhaustHX.l2Coeff;
3154 :
3155 6684 : this->ExhaustHX.CondensateRate =
3156 6684 : (TcondThresh - TwaterIn) * (hxl1 * (NdotWaterVapor / NdotGas) + hxl2 * pow_2(NdotWaterVapor / NdotGas));
3157 :
3158 6684 : if (this->ExhaustHX.CondensateRate < 0.0) this->ExhaustHX.CondensateRate = 0.0;
3159 :
3160 6684 : Real64 hfpwater = 4.4004e+07; // molal heat of vaporization of water J/kmol)
3161 :
3162 6684 : if ((NdotCpWater != 0.0) && (NdotCpAuxMix != 0.0)) { // trap divide by zero
3163 :
3164 : // now evaluate Eq. 44
3165 6684 : this->ExhaustHX.THXexh = ((1.0 - NdotCpAuxMix / NdotCpWater) /
3166 6684 : (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) - NdotCpAuxMix / NdotCpWater)) *
3167 6684 : TauxMix +
3168 6684 : (std::expm1(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3169 6684 : (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) - NdotCpAuxMix / NdotCpWater)) *
3170 : TwaterIn;
3171 :
3172 6684 : this->ExhaustHX.WaterOutletTemp = TwaterIn + (NdotCpAuxMix / NdotCpWater) * (TauxMix - this->ExhaustHX.THXexh) +
3173 6684 : (this->ExhaustHX.CondensateRate * hfpwater) / NdotCpWater;
3174 :
3175 6684 : if (this->ExhaustHX.CondensateRate > 0) { // Eq. 44 is not correct. use its result as first guess for revised way...
3176 : // iterative solution because in condensing case THXexh is function of qSens and qLatent
3177 0 : for (int loop = 1; loop <= 5; ++loop) {
3178 :
3179 : Real64 qSens;
3180 : Real64 qLatent;
3181 :
3182 0 : if ((this->ExhaustHX.THXexh - TwaterIn) != 0.0 &&
3183 0 : ((TauxMix - this->ExhaustHX.WaterOutletTemp) / (this->ExhaustHX.THXexh - TwaterIn) >
3184 : 0.0001)) { // trap divide by zero and negative log
3185 0 : qSens = UAeff * ((TauxMix - this->ExhaustHX.WaterOutletTemp) - (this->ExhaustHX.THXexh - TwaterIn)) /
3186 0 : std::log((TauxMix - this->ExhaustHX.WaterOutletTemp) / (this->ExhaustHX.THXexh - TwaterIn));
3187 : } else {
3188 0 : qSens = 0.0;
3189 : }
3190 0 : qLatent = this->ExhaustHX.CondensateRate * hfpwater;
3191 0 : if (qSens > 0) {
3192 0 : this->ExhaustHX.THXexh =
3193 0 : TauxMix * ((1.0 - NdotCpAuxMix / NdotCpWater) / ((std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3194 0 : (std::exp((UAeff * qLatent) / (NdotCpWater * qSens)))) -
3195 0 : NdotCpAuxMix / NdotCpWater)) +
3196 0 : TwaterIn * ((std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3197 0 : (std::exp((UAeff * qLatent) / (NdotCpWater * qSens))) -
3198 0 : 1.0) /
3199 0 : (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3200 0 : (std::exp((UAeff * qLatent) / (NdotCpWater * qSens))) -
3201 0 : NdotCpAuxMix / NdotCpWater)) -
3202 0 : ((qLatent / NdotCpWater) / (std::exp(UAeff * (1.0 / NdotCpAuxMix - 1.0 / NdotCpWater)) /
3203 0 : (std::exp((UAeff * qLatent) / (NdotCpWater * qSens))) -
3204 0 : NdotCpAuxMix / NdotCpWater));
3205 : } else {
3206 0 : this->ExhaustHX.THXexh = TauxMix;
3207 : }
3208 :
3209 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn + (NdotCpAuxMix / NdotCpWater) * (TauxMix - this->ExhaustHX.THXexh) +
3210 0 : (this->ExhaustHX.CondensateRate * hfpwater) / NdotCpWater;
3211 : }
3212 : }
3213 :
3214 6684 : } else {
3215 0 : this->ExhaustHX.THXexh = TauxMix;
3216 0 : this->ExhaustHX.WaterOutletTemp = TwaterIn;
3217 : }
3218 :
3219 6684 : if ((this->ExhaustHX.THXexh - TwaterIn) != 0.0 && ((TauxMix - this->ExhaustHX.WaterOutletTemp) / (this->ExhaustHX.THXexh - TwaterIn) >
3220 : 0.0001)) { // trap divide by zero and negative log
3221 :
3222 6684 : this->ExhaustHX.qHX = UAeff * ((TauxMix - this->ExhaustHX.WaterOutletTemp) - (this->ExhaustHX.THXexh - TwaterIn)) /
3223 6684 : std::log((TauxMix - this->ExhaustHX.WaterOutletTemp) / (this->ExhaustHX.THXexh - TwaterIn)) +
3224 6684 : this->ExhaustHX.CondensateRate * hfpwater;
3225 : } else {
3226 0 : this->ExhaustHX.qHX = 0.0;
3227 : }
3228 : } else { // no cooling water flow, model will blow up.
3229 21 : this->ExhaustHX.qHX = 0.0;
3230 21 : this->ExhaustHX.THXexh = this->AuxilHeat.TauxMix;
3231 21 : this->ExhaustHX.WaterOutletTemp = this->ExhaustHX.WaterInletTemp;
3232 21 : this->ExhaustHX.CondensateRate = 0.0;
3233 21 : this->ExhaustHX.WaterVaporFractExh = -9999.0; // not defined
3234 : }
3235 6705 : } break;
3236 0 : default: {
3237 0 : assert(false); // Variables not set are used below
3238 : } break;
3239 : }
3240 :
3241 : // update results in data structure.
3242 6705 : this->ExhaustHX.WaterOutletEnthalpy = state.dataLoopNodes->Node(this->ExhaustHX.WaterInNode).Enthalpy + this->ExhaustHX.qHX;
3243 6705 : }
3244 :
3245 5 : void FCDataStruct::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
3246 : [[maybe_unused]] const PlantLocation &calledFromLocation,
3247 : Real64 &MaxLoad,
3248 : Real64 &MinLoad,
3249 : Real64 &OptLoad)
3250 : {
3251 5 : MaxLoad = 0.0;
3252 5 : MinLoad = 0.0;
3253 5 : OptLoad = 0.0;
3254 5 : }
3255 :
3256 5966 : void FCDataStruct::simulate(EnergyPlusData &state,
3257 : [[maybe_unused]] const PlantLocation &calledFromLocation,
3258 : bool FirstHVACIteration,
3259 : [[maybe_unused]] Real64 &CurLoad,
3260 : [[maybe_unused]] bool RunFlag)
3261 : {
3262 5966 : if (this->Type == DataPlant::PlantEquipmentType::Generator_FCStackCooler) {
3263 0 : PlantUtilities::UpdateComponentHeatRecoverySide(state,
3264 : this->CWPlantLoc.loopNum,
3265 : this->CWPlantLoc.loopSideNum,
3266 : DataPlant::PlantEquipmentType::Generator_FCStackCooler,
3267 : this->StackCooler.WaterInNode,
3268 : this->StackCooler.WaterOutNode,
3269 : this->Report.qHX,
3270 : this->Report.HeatRecInletTemp,
3271 : this->Report.HeatRecOutletTemp,
3272 : this->Report.HeatRecMdot,
3273 : FirstHVACIteration);
3274 5966 : } else if (this->Type == DataPlant::PlantEquipmentType::Generator_FCExhaust) {
3275 5966 : PlantUtilities::UpdateComponentHeatRecoverySide(state,
3276 : this->CWPlantLoc.loopNum,
3277 : this->CWPlantLoc.loopSideNum,
3278 : DataPlant::PlantEquipmentType::Generator_FCExhaust,
3279 : this->ExhaustHX.WaterInNode,
3280 : this->ExhaustHX.WaterOutNode,
3281 : this->ExhaustHX.qHX,
3282 : this->ExhaustHX.WaterInletTemp,
3283 : this->ExhaustHX.WaterOutletTemp,
3284 : this->ExhaustHX.WaterMassFlowRate,
3285 : FirstHVACIteration);
3286 : }
3287 5966 : }
3288 :
3289 2235 : void FCDataStruct::initialize(EnergyPlusData &state) // index to specific fuel cell generator
3290 : {
3291 :
3292 : // SUBROUTINE INFORMATION:
3293 : // AUTHOR Brent Griffith
3294 : // DATE WRITTEN Aug 2005
3295 : // RE-ENGINEERED B. Griffith Sept 2010, plant upgrades
3296 :
3297 : // PURPOSE OF THIS SUBROUTINE:
3298 : // This subroutine is for initializations of the FuelCell generators.
3299 :
3300 : // METHODOLOGY EMPLOYED:
3301 : // Uses the status flags to trigger initializations.
3302 :
3303 : static constexpr std::string_view RoutineName("InitFuelCellGenerators");
3304 :
3305 : // Do the Begin Environment initializations
3306 2235 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag_Init && !this->MyPlantScanFlag_Init) {
3307 :
3308 2 : state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl = 0.0;
3309 2 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM = 0.0;
3310 2 : state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress = 0.0;
3311 2 : state.dataGenerator->FuelSupply(this->FuelSupNum).QskinLoss = 0.0;
3312 :
3313 2 : this->AirSup.TairIntoFCPM = 0.0;
3314 2 : this->AirSup.PairCompEl = 0.0;
3315 2 : this->AirSup.TairIntoBlower = 0.0;
3316 2 : this->AirSup.QskinLoss = 0.0;
3317 2 : this->AirSup.QintakeRecovery = 0.0;
3318 2 : this->FCPM.NumCycles = this->FCPM.NumCyclesAtStart;
3319 2 : this->FCPM.Pel = 0.0;
3320 2 : this->FCPM.PelLastTimeStep = 0.0;
3321 2 : this->FCPM.Eel = 0.0;
3322 2 : this->FCPM.PelancillariesAC = 0.0;
3323 2 : this->FCPM.NdotFuel = 0.0;
3324 2 : this->FCPM.TotFuelInEnthalpy = 0.0;
3325 2 : this->FCPM.NdotProdGas = 0.0;
3326 2 : this->FCPM.TprodGasLeavingFCPM = 0.0;
3327 2 : this->FCPM.TotProdGasEnthalpy = 0.0;
3328 2 : this->FCPM.NdotAir = 0.0;
3329 2 : this->FCPM.TotAirInEnthalpy = 0.0;
3330 2 : this->FCPM.NdotLiqwater = 0.0;
3331 2 : this->FCPM.TwaterInlet = 0.0;
3332 2 : this->FCPM.WaterInEnthalpy = 0.0;
3333 2 : this->FCPM.TprodGasLeavingFCPM = 200.0;
3334 2 : this->FCPM.FractionalDayofLastStartUp = 0.0;
3335 2 : this->FCPM.FractionalDayofLastShutDown = 0.0;
3336 2 : this->FCPM.HasBeenOn = true;
3337 2 : this->FCPM.DuringShutDown = false;
3338 2 : this->FCPM.DuringStartUp = false;
3339 2 : this->WaterSup.TwaterIntoCompress = 0.0;
3340 2 : this->WaterSup.TwaterIntoFCPM = 0.0;
3341 2 : this->WaterSup.PwaterCompEl = 0.0;
3342 2 : this->WaterSup.QskinLoss = 0.0;
3343 2 : this->AuxilHeat.TauxMix = 0.0;
3344 2 : this->AuxilHeat.NdotAuxMix = 0.0;
3345 2 : this->AuxilHeat.QskinLoss = 0.0;
3346 2 : this->AuxilHeat.QairIntake = 0.0;
3347 2 : this->ExhaustHX.NdotHXleaving = 0.0;
3348 2 : this->ExhaustHX.WaterOutletTemp = 0.0;
3349 2 : this->ExhaustHX.WaterOutletEnthalpy = 0.0;
3350 2 : this->ElecStorage.LastTimeStepStateOfCharge = this->ElecStorage.StartingEnergyStored;
3351 2 : this->ElecStorage.ThisTimeStepStateOfCharge = this->ElecStorage.StartingEnergyStored;
3352 2 : this->ElecStorage.PelNeedFromStorage = 0.0;
3353 2 : this->ElecStorage.IdesiredDischargeCurrent = 0.0;
3354 2 : this->ElecStorage.PelFromStorage = 0.0;
3355 2 : this->ElecStorage.IfromStorage = 0.0;
3356 2 : this->ElecStorage.PelIntoStorage = 0.0;
3357 2 : this->ElecStorage.QairIntake = 0.0;
3358 :
3359 2 : this->Inverter.PCUlosses = 0.0;
3360 2 : this->Inverter.QairIntake = 0.0;
3361 :
3362 2 : Real64 rho = state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).glycol->getDensity(state, DataGenerators::InitHRTemp, RoutineName);
3363 :
3364 2 : this->ExhaustHX.WaterMassFlowRateDesign = this->ExhaustHX.WaterVolumeFlowMax * rho;
3365 2 : this->ExhaustHX.WaterMassFlowRate = this->ExhaustHX.WaterMassFlowRateDesign;
3366 2 : state.dataLoopNodes->Node(this->ExhaustHX.WaterInNode).Temp = DataGenerators::InitHRTemp;
3367 2 : state.dataLoopNodes->Node(this->ExhaustHX.WaterOutNode).Temp = DataGenerators::InitHRTemp;
3368 :
3369 2 : PlantUtilities::InitComponentNodes(
3370 : state, 0.0, this->ExhaustHX.WaterMassFlowRateDesign, this->ExhaustHX.WaterInNode, this->ExhaustHX.WaterOutNode);
3371 :
3372 2 : this->MyEnvrnFlag_Init = false;
3373 2 : this->MyWarmupFlag_Init = true;
3374 : } // end environmental inits
3375 :
3376 2235 : if (!state.dataGlobal->BeginEnvrnFlag) {
3377 2229 : this->MyEnvrnFlag_Init = true;
3378 : }
3379 :
3380 2235 : if (this->MyWarmupFlag_Init && (!state.dataGlobal->WarmupFlag)) {
3381 : // need to reset initial state of charge at beginning of environment but after warm up is complete
3382 1 : this->ElecStorage.LastTimeStepStateOfCharge = this->ElecStorage.StartingEnergyStored;
3383 1 : this->ElecStorage.ThisTimeStepStateOfCharge = this->ElecStorage.StartingEnergyStored;
3384 1 : this->MyWarmupFlag_Init = false;
3385 : }
3386 :
3387 : // using and elapsed time method rather than FirstHVACIteration here
3388 : Real64 timeElapsed =
3389 2235 : state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
3390 2235 : if (this->TimeElapsed != timeElapsed) {
3391 :
3392 725 : this->ElecStorage.LastTimeStepStateOfCharge = this->ElecStorage.ThisTimeStepStateOfCharge;
3393 725 : this->FCPM.PelLastTimeStep = this->FCPM.Pel;
3394 :
3395 : // initialize flow rate in water loop, this is "requesting" flow
3396 725 : Real64 mdot = this->ExhaustHX.WaterMassFlowRateDesign;
3397 :
3398 725 : PlantUtilities::SetComponentFlowRate(state, mdot, this->ExhaustHX.WaterInNode, this->ExhaustHX.WaterOutNode, this->CWPlantLoc);
3399 :
3400 725 : this->ExhaustHX.WaterMassFlowRate = mdot;
3401 725 : this->ExhaustHX.WaterInletTemp = state.dataLoopNodes->Node(this->ExhaustHX.WaterInNode).Temp;
3402 725 : this->TimeElapsed = timeElapsed;
3403 : } else {
3404 :
3405 1510 : PlantUtilities::SetComponentFlowRate(
3406 1510 : state, this->ExhaustHX.WaterMassFlowRate, this->ExhaustHX.WaterInNode, this->ExhaustHX.WaterOutNode, this->CWPlantLoc);
3407 :
3408 1510 : this->ExhaustHX.WaterInletTemp = state.dataLoopNodes->Node(this->ExhaustHX.WaterInNode).Temp;
3409 : }
3410 2235 : }
3411 :
3412 249958 : void FigureFuelCellZoneGains(EnergyPlusData &state)
3413 : {
3414 :
3415 : // SUBROUTINE INFORMATION:
3416 : // AUTHOR B. Griffith
3417 : // DATE WRITTEN Aug 2005
3418 : // MODIFIED BG March 2007
3419 :
3420 : // PURPOSE OF THIS SUBROUTINE:
3421 : // Couple equipment skin losses to the Zone Heat Balance
3422 : // calculate skin losses from different subsystems and set the value
3423 :
3424 : // METHODOLOGY EMPLOYED:
3425 : // This routine adds up the various skin losses and then
3426 : // sets the values in the ZoneIntGain structure
3427 :
3428 249958 : if (state.dataFuelCellElectGen->NumFuelCellGenerators == 0) return;
3429 :
3430 674 : if (state.dataGlobal->BeginEnvrnFlag && state.dataFuelCellElectGen->MyEnvrnFlag) {
3431 2 : for (auto &e : state.dataGenerator->FuelSupply)
3432 1 : e.QskinLoss = 0.0;
3433 1 : state.dataFuelCellElectGen->MyEnvrnFlag = false;
3434 2 : for (int i = state.dataFuelCellElectGen->FuelCell.l(), e = state.dataFuelCellElectGen->FuelCell.u(); i <= e; ++i) {
3435 1 : auto &cell(state.dataFuelCellElectGen->FuelCell(i));
3436 1 : cell.FCPM.HasBeenOn = false;
3437 1 : cell.AirSup.PairCompEl = 0.0;
3438 1 : cell.QconvZone = 0.0;
3439 1 : cell.QradZone = 0.0;
3440 1 : cell.AirSup.QskinLoss = 0.0;
3441 1 : cell.WaterSup.QskinLoss = 0.0;
3442 1 : cell.AuxilHeat.QskinLoss = 0.0;
3443 1 : if (cell.FCPM.SkinLossMode != DataGenerators::SkinLoss::ConstantRate) {
3444 : // If Constant Skin Loss Rate, then do not zero out
3445 0 : cell.FCPM.QdotSkin = 0.0;
3446 : }
3447 1 : cell.Report.SkinLossConvect = 0.0;
3448 1 : cell.Report.SkinLossRadiat = 0.0;
3449 1 : cell.AuxilHeat.QairIntake = 0.0;
3450 1 : cell.ElecStorage.QairIntake = 0.0;
3451 1 : cell.Inverter.QairIntake = 0.0;
3452 : }
3453 : }
3454 :
3455 674 : if (!state.dataGlobal->BeginEnvrnFlag) state.dataFuelCellElectGen->MyEnvrnFlag = true;
3456 :
3457 : // this routine needs to do something for zone gains during sizing
3458 :
3459 : // first collect skin losses from different subsystems
3460 1348 : for (int FCnum = 1; FCnum <= state.dataFuelCellElectGen->NumFuelCellGenerators; ++FCnum) {
3461 674 : auto &thisFC = state.dataFuelCellElectGen->FuelCell(FCnum);
3462 674 : Real64 TotalZoneHeatGain = thisFC.AirSup.QskinLoss + state.dataGenerator->FuelSupply(thisFC.FuelSupNum).QskinLoss +
3463 674 : thisFC.WaterSup.QskinLoss + thisFC.AuxilHeat.QskinLoss +
3464 674 : thisFC.FCPM.QdotSkin; // intake Blower losses to zone | fuel compressor losses to
3465 : // zone | water pump losses to zone | auxil burner losses to
3466 : // zone | power module (stack and reformer) losses to zone
3467 :
3468 : // now account for other subsystems that may or may not have air intake recovery
3469 674 : switch (thisFC.AirSup.IntakeRecoveryMode) {
3470 674 : case DataGenerators::RecoverMode::NoRecoveryOnAirIntake: { // then the heat has to go into zone
3471 674 : TotalZoneHeatGain += thisFC.AuxilHeat.QairIntake + thisFC.ElecStorage.QairIntake + thisFC.Inverter.QairIntake;
3472 674 : } break;
3473 0 : case DataGenerators::RecoverMode::RecoverAuxiliaryBurner: {
3474 0 : TotalZoneHeatGain += thisFC.ElecStorage.QairIntake + thisFC.Inverter.QairIntake;
3475 0 : } break;
3476 0 : case DataGenerators::RecoverMode::RecoverInverterBatt: {
3477 0 : TotalZoneHeatGain += thisFC.AuxilHeat.QairIntake;
3478 0 : } break;
3479 0 : case DataGenerators::RecoverMode::RecoverInverter: {
3480 0 : TotalZoneHeatGain += thisFC.AuxilHeat.QairIntake + thisFC.ElecStorage.QairIntake;
3481 0 : } break;
3482 0 : case DataGenerators::RecoverMode::RecoverBattery: {
3483 0 : TotalZoneHeatGain += thisFC.AuxilHeat.QairIntake + thisFC.Inverter.QairIntake;
3484 0 : } break;
3485 0 : case DataGenerators::RecoverMode::RecoverBurnInvertBatt: {
3486 : // do nothing
3487 0 : } break;
3488 0 : default:
3489 0 : break;
3490 : }
3491 :
3492 674 : thisFC.QconvZone = TotalZoneHeatGain * (1 - thisFC.FCPM.RadiativeFract);
3493 674 : thisFC.Report.SkinLossConvect = thisFC.QconvZone;
3494 674 : thisFC.QradZone = TotalZoneHeatGain * thisFC.FCPM.RadiativeFract;
3495 674 : thisFC.Report.SkinLossRadiat = thisFC.QradZone;
3496 :
3497 : } // over number of Fuel cells
3498 : }
3499 :
3500 2235 : void FCDataStruct::CalcUpdateHeatRecovery(EnergyPlusData &state, [[maybe_unused]] bool const FirstHVACIteration) const
3501 : {
3502 :
3503 : // SUBROUTINE INFORMATION:
3504 : // AUTHOR B Griffith
3505 : // DATE WRITTEN March 2008
3506 :
3507 : // PURPOSE OF THIS SUBROUTINE:
3508 : // update plant loop interactions, do any calcs needed
3509 :
3510 : // now update water outlet node Changing to Kg/s!
3511 2235 : PlantUtilities::SafeCopyPlantNode(state, this->ExhaustHX.WaterInNode, this->ExhaustHX.WaterOutNode);
3512 :
3513 2235 : state.dataLoopNodes->Node(this->ExhaustHX.WaterOutNode).Temp = this->ExhaustHX.WaterOutletTemp;
3514 2235 : state.dataLoopNodes->Node(this->ExhaustHX.WaterOutNode).Enthalpy = this->ExhaustHX.WaterOutletEnthalpy;
3515 2235 : }
3516 :
3517 2235 : void FCDataStruct::UpdateFuelCellGeneratorRecords(EnergyPlusData &state)
3518 : {
3519 :
3520 2235 : this->Report.ACPowerGen = this->ACPowerGen; // electrical power produced [W]
3521 2235 : this->Report.ACEnergyGen = this->ACPowerGen * state.dataHVACGlobal->TimeStepSysSec; // energy produced (J)
3522 2235 : this->Report.QdotExhaust = 0.0; // reporting: exhaust gas heat recovered (W)
3523 2235 : this->Report.TotalHeatEnergyRec = 0.0; // reporting: total heat recovered (J)
3524 2235 : this->Report.ExhaustEnergyRec = 0.0; // reporting: exhaust gas heat recovered (J)
3525 :
3526 2235 : this->Report.HeatRecInletTemp = 0.0; // reporting: Heat Recovery Loop Inlet Temperature (C)
3527 2235 : this->Report.HeatRecOutletTemp = 0.0; // reporting: Heat Recovery Loop Outlet Temperature (C)
3528 2235 : this->Report.HeatRecMdot = 0.0; // reporting: Heat Recovery Loop Mass flow rate (kg/s)
3529 :
3530 2235 : this->Report.ElectEfficiency = 0.0;
3531 2235 : this->Report.ThermalEfficiency = 0.0;
3532 2235 : this->Report.OverallEfficiency = 0.0;
3533 2235 : this->Report.ExergyEfficiency = 0.0;
3534 :
3535 2235 : this->Report.TairInlet = this->AirSup.TairIntoBlower; // State point 1
3536 2235 : this->Report.TairIntoFCPM = this->AirSup.TairIntoFCPM; // State point 4
3537 2235 : this->Report.NdotAir = this->FCPM.NdotAir; // air flow in kmol/sec
3538 2235 : this->Report.TotAirInEnthalpy = this->FCPM.TotAirInEnthalpy; // State point 4
3539 2235 : this->Report.BlowerPower = this->AirSup.PairCompEl; // electrical power used by air supply blower
3540 2235 : this->Report.BlowerEnergy = this->AirSup.PairCompEl * state.dataHVACGlobal->TimeStepSysSec; // electrical energy
3541 2235 : this->Report.BlowerSkinLoss = this->AirSup.QskinLoss; // heat rate of losses by blower
3542 :
3543 2235 : this->Report.TfuelInlet = state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoCompress; // State point 2
3544 2235 : this->Report.TfuelIntoFCPM = state.dataGenerator->FuelSupply(this->FuelSupNum).TfuelIntoFCPM; // TEmperature state point 5 [C]
3545 2235 : this->Report.NdotFuel = this->FCPM.NdotFuel; // fuel flow in kmol/sec
3546 2235 : this->Report.TotFuelInEnthalpy = this->FCPM.TotFuelInEnthalpy; // enthalpy at state point 5 [W]
3547 2235 : this->Report.FuelCompressPower = state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl;
3548 : // electrical power used by fuel supply compressor [W]
3549 2235 : this->Report.FuelCompressEnergy =
3550 2235 : state.dataGenerator->FuelSupply(this->FuelSupNum).PfuelCompEl * state.dataHVACGlobal->TimeStepSys * Constant::rSecsInHour; // elect energy
3551 2235 : this->Report.FuelCompressSkinLoss = state.dataGenerator->FuelSupply(this->FuelSupNum).QskinLoss;
3552 : // heat rate of losses.by fuel supply compressor [W]
3553 2235 : this->Report.FuelEnergyLHV = this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).LHV * 1000000.0 *
3554 2235 : state.dataHVACGlobal->TimeStepSysSec; // reporting: Fuel Energy used (J)
3555 2235 : this->Report.FuelEnergyUseRateLHV =
3556 2235 : this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).LHV * 1000000.0; // reporting: Fuel Energy used (W)
3557 2235 : this->Report.FuelEnergyHHV = this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).HHV *
3558 2235 : state.dataGenerator->FuelSupply(this->FuelSupNum).KmolPerSecToKgPerSec * state.dataHVACGlobal->TimeStepSys *
3559 : Constant::rSecsInHour;
3560 :
3561 2235 : this->Report.FuelEnergyUseRateHHV = this->FCPM.NdotFuel * state.dataGenerator->FuelSupply(this->FuelSupNum).HHV *
3562 2235 : state.dataGenerator->FuelSupply(this->FuelSupNum).KmolPerSecToKgPerSec;
3563 :
3564 2235 : this->Report.FuelRateMdot = 0.0; // (Kg/s)
3565 :
3566 2235 : this->Report.TwaterInlet = this->WaterSup.TwaterIntoCompress;
3567 2235 : this->Report.TwaterIntoFCPM = this->WaterSup.TwaterIntoFCPM;
3568 2235 : this->Report.NdotWater = this->FCPM.NdotLiqwater; // water flow in kmol/sec (reformer water)
3569 2235 : this->Report.WaterPumpPower = this->WaterSup.PwaterCompEl;
3570 2235 : this->Report.WaterPumpEnergy = this->WaterSup.PwaterCompEl * state.dataHVACGlobal->TimeStepSysSec; // electrical energy
3571 2235 : this->Report.WaterIntoFCPMEnthalpy = this->FCPM.WaterInEnthalpy;
3572 :
3573 2235 : this->Report.TprodGas = this->FCPM.TprodGasLeavingFCPM; // temperature at State point 7
3574 2235 : this->Report.EnthalProdGas = this->FCPM.TotProdGasEnthalpy; // enthalpy at State point 7
3575 2235 : this->Report.NdotProdGas = this->FCPM.NdotProdGas; // flow rate at point 7 [kmol/sec]
3576 2235 : this->Report.NdotProdAr = this->FCPM.ConstitMolalFract(5) * this->FCPM.NdotProdGas;
3577 2235 : this->Report.NdotProdCO2 = this->FCPM.ConstitMolalFract(1) * this->FCPM.NdotProdGas;
3578 2235 : this->Report.NdotProdH2O = this->FCPM.ConstitMolalFract(4) * this->FCPM.NdotProdGas;
3579 2235 : this->Report.NdotProdN2 = this->FCPM.ConstitMolalFract(2) * this->FCPM.NdotProdGas;
3580 2235 : this->Report.NdotProdO2 = this->FCPM.ConstitMolalFract(3) * this->FCPM.NdotProdGas;
3581 :
3582 2235 : this->Report.qHX = this->ExhaustHX.qHX;
3583 2235 : this->Report.HXenergy = this->ExhaustHX.qHX * state.dataHVACGlobal->TimeStepSysSec;
3584 2235 : this->Report.THXexh = this->ExhaustHX.THXexh;
3585 2235 : this->Report.WaterVaporFractExh = this->ExhaustHX.WaterVaporFractExh;
3586 2235 : this->Report.CondensateRate = this->ExhaustHX.CondensateRate;
3587 :
3588 2235 : this->Report.SeqSubstIterations = this->FCPM.SeqSubstitIter; // number of iterations in FuelCell loop
3589 2235 : this->Report.RegulaFalsiIterations = this->FCPM.RegulaFalsiIter; // number of iterations in Tproduct gas solving
3590 2235 : this->Report.NumCycles = this->FCPM.NumCycles; // number of start-stop cycles
3591 2235 : this->Report.FCPMSkinLoss = this->FCPM.QdotSkin; // Skin loss of power module
3592 :
3593 2235 : this->Report.ACancillariesPower = this->FCPM.PelancillariesAC;
3594 2235 : this->Report.ACancillariesEnergy = this->FCPM.PelancillariesAC * state.dataHVACGlobal->TimeStepSysSec;
3595 :
3596 2235 : this->Report.PCUlosses = this->Inverter.PCUlosses; // inverter losses
3597 2235 : this->Report.DCPowerGen = this->FCPM.Pel; // DC power out of FCPM.
3598 2235 : this->Report.DCPowerEff = this->FCPM.Eel; // FCPM efficiency Eel.
3599 2235 : this->Report.ElectEnergyinStorage = this->ElecStorage.ThisTimeStepStateOfCharge;
3600 2235 : this->Report.StoredPower = this->ElecStorage.PelIntoStorage;
3601 2235 : this->Report.StoredEnergy = this->ElecStorage.PelIntoStorage * state.dataHVACGlobal->TimeStepSysSec;
3602 2235 : this->Report.DrawnPower = this->ElecStorage.PelFromStorage;
3603 2235 : this->Report.DrawnEnergy = this->ElecStorage.PelFromStorage * state.dataHVACGlobal->TimeStepSysSec;
3604 :
3605 2235 : this->Report.SkinLossPower = this->QconvZone + this->QradZone;
3606 2235 : this->Report.SkinLossEnergy = (this->QconvZone + this->QradZone) * state.dataHVACGlobal->TimeStepSysSec;
3607 2235 : this->Report.SkinLossConvect = this->QconvZone;
3608 2235 : this->Report.SkinLossRadiat = this->QradZone;
3609 2235 : }
3610 1 : void FCDataStruct::oneTimeInit_new(EnergyPlusData &state)
3611 : {
3612 :
3613 1 : if (this->MyPlantScanFlag_Init && allocated(state.dataPlnt->PlantLoop)) {
3614 1 : bool errFlag = false;
3615 :
3616 2 : PlantUtilities::ScanPlantLoopsForObject(
3617 1 : state, this->NameExhaustHX, DataPlant::PlantEquipmentType::Generator_FCExhaust, this->CWPlantLoc, errFlag, _, _, _, _, _);
3618 :
3619 : // if there is a stack cooler option it might be connected to plant as well
3620 :
3621 1 : if (errFlag) {
3622 0 : ShowFatalError(state, "InitFuelCellGenerators: Program terminated due to previous condition(s).");
3623 : }
3624 1 : this->MyPlantScanFlag_Init = false;
3625 : }
3626 1 : }
3627 :
3628 0 : void FCDataStruct::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
3629 : {
3630 0 : }
3631 :
3632 : } // namespace FuelCellElectricGenerator
3633 :
3634 : } // namespace EnergyPlus
|