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