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