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