Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/BranchNodeConnections.hh>
57 : #include <EnergyPlus/CTElectricGenerator.hh>
58 : #include <EnergyPlus/CurveManager.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataEnvironment.hh>
61 : #include <EnergyPlus/DataHVACGlobals.hh>
62 : #include <EnergyPlus/DataIPShortCuts.hh>
63 : #include <EnergyPlus/DataLoopNode.hh>
64 : #include <EnergyPlus/FluidProperties.hh>
65 : #include <EnergyPlus/General.hh>
66 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
67 : #include <EnergyPlus/NodeInputManager.hh>
68 : #include <EnergyPlus/OutAirNodeManager.hh>
69 : #include <EnergyPlus/OutputProcessor.hh>
70 : #include <EnergyPlus/Plant/DataPlant.hh>
71 : #include <EnergyPlus/Plant/PlantLocation.hh>
72 : #include <EnergyPlus/PlantUtilities.hh>
73 : #include <EnergyPlus/UtilityRoutines.hh>
74 :
75 : namespace EnergyPlus {
76 :
77 : namespace CTElectricGenerator {
78 :
79 : //__________________________________________________________________________;
80 : // BLAST inherited generators:
81 : // CTElectricGenerator (COMBUSTION Turbine)
82 :
83 : // MODULE INFORMATION:
84 : // AUTHOR Dan Fisher
85 : // DATE WRITTEN Sept 2000
86 : // MODIFIED na
87 : // RE-ENGINEERED na
88 :
89 : // PURPOSE OF THIS MODULE:
90 : // This module simulates the performance of the COMBUSTION turbine
91 : // Generators.
92 :
93 : // METHODOLOGY EMPLOYED:
94 : // Once the Electric power manager determines that the CT Generator
95 : // is available, it calls SimCTGenerator which in turn calls the
96 : // appropriate COMBUSTION turbine Generator model.
97 : // All CT Generator models are based on a polynomial fit of Generator
98 : // performance data.
99 :
100 54893 : PlantComponent *CTGeneratorData::factory(EnergyPlusData &state, std::string const &objectName)
101 : {
102 : // Process the input data for generators if it hasn't been done already
103 54893 : if (state.dataCTElectricGenerator->getCTInputFlag) {
104 6 : GetCTGeneratorInput(state);
105 6 : state.dataCTElectricGenerator->getCTInputFlag = false;
106 : }
107 :
108 : // Now look for this particular generator in the list
109 54893 : for (auto &CTGen : state.dataCTElectricGenerator->CTGenerator) {
110 54893 : if (CTGen.Name == objectName) {
111 54893 : return &CTGen;
112 : }
113 : }
114 : // If we didn't find it, fatal
115 0 : ShowFatalError(state,
116 0 : "LocalCombustionTurbineGeneratorFactory: Error getting inputs for combustion turbine generator named: " +
117 : objectName); // LCOV_EXCL_LINE
118 : // Shut up the compiler
119 : return nullptr; // LCOV_EXCL_LINE
120 : }
121 :
122 162561 : void CTGeneratorData::simulate([[maybe_unused]] EnergyPlusData &state,
123 : [[maybe_unused]] const EnergyPlus::PlantLocation &calledFromLocation,
124 : [[maybe_unused]] bool FirstHVACIteration,
125 : [[maybe_unused]] Real64 &CurLoad,
126 : [[maybe_unused]] bool RunFlag)
127 : {
128 : // SUBROUTINE INFORMATION:
129 : // AUTHOR Dan Fisher
130 : // DATE WRITTEN Sept. 2000
131 : // MODIFIED na
132 : // RE-ENGINEERED na
133 :
134 : // PURPOSE OF THIS SUBROUTINE: This is the CT Generator driver. It
135 : // gets the input for the models, initializes simulation variables, call
136 : // the appropriate model and sets up reporting variables.
137 :
138 : // empty function to emulate current behavior as of conversion to using the PlantComponent calling structure.
139 : // calls from the plant side... do nothing.
140 : // calls from the ElectricPowerServiceManger call the init and calculation worker functions directly.
141 162561 : }
142 :
143 6 : void GetCTGeneratorInput(EnergyPlusData &state)
144 : {
145 : // SUBROUTINE INFORMATION:
146 : // AUTHOR: Dan Fisher
147 : // DATE WRITTEN: April 2000
148 :
149 : // PURPOSE OF THIS SUBROUTINE:
150 : // This routine will get the input
151 : // required by the CT Generator models.
152 :
153 : int NumAlphas; // Number of elements in the alpha array
154 : int NumNums; // Number of elements in the numeric array
155 : int IOStat; // IO Status when calling get input subroutine
156 12 : Array1D_string AlphArray(12); // character string data
157 12 : Array1D<Real64> NumArray(12); // numeric data
158 6 : bool ErrorsFound(false); // error flag
159 :
160 6 : state.dataIPShortCut->cCurrentModuleObject = "Generator:CombustionTurbine";
161 6 : int NumCTGenerators = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
162 :
163 6 : if (NumCTGenerators <= 0) {
164 0 : ShowSevereError(state, "No " + state.dataIPShortCut->cCurrentModuleObject + " equipment specified in input file");
165 0 : ErrorsFound = true;
166 : }
167 :
168 : // ALLOCATE ARRAYS
169 6 : state.dataCTElectricGenerator->CTGenerator.allocate(NumCTGenerators);
170 :
171 : // LOAD ARRAYS WITH CT CURVE FIT Generator DATA
172 12 : for (int genNum = 1; genNum <= NumCTGenerators; ++genNum) {
173 30 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
174 6 : state.dataIPShortCut->cCurrentModuleObject,
175 : genNum,
176 : AlphArray,
177 : NumAlphas,
178 : NumArray,
179 : NumNums,
180 : IOStat,
181 : _,
182 6 : state.dataIPShortCut->lAlphaFieldBlanks,
183 6 : state.dataIPShortCut->cAlphaFieldNames,
184 6 : state.dataIPShortCut->cNumericFieldNames);
185 6 : UtilityRoutines::IsNameEmpty(state, AlphArray(1), state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
186 :
187 6 : state.dataCTElectricGenerator->CTGenerator(genNum).Name = AlphArray(1);
188 :
189 6 : state.dataCTElectricGenerator->CTGenerator(genNum).RatedPowerOutput = NumArray(1);
190 6 : if (NumArray(1) == 0.0) {
191 0 : ShowSevereError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(1), NumArray(1)));
192 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
193 0 : ErrorsFound = true;
194 : }
195 :
196 : // Not sure what to do with electric nodes, so do not use optional arguments
197 6 : state.dataCTElectricGenerator->CTGenerator(genNum).ElectricCircuitNode =
198 12 : NodeInputManager::GetOnlySingleNode(state,
199 6 : AlphArray(2),
200 : ErrorsFound,
201 : DataLoopNode::ConnectionObjectType::GeneratorCombustionTurbine,
202 6 : AlphArray(1),
203 : DataLoopNode::NodeFluidType::Electric,
204 : DataLoopNode::ConnectionType::Electric,
205 : NodeInputManager::CompFluidStream::Primary,
206 6 : DataLoopNode::ObjectIsNotParent);
207 :
208 6 : state.dataCTElectricGenerator->CTGenerator(genNum).MinPartLoadRat = NumArray(2);
209 6 : state.dataCTElectricGenerator->CTGenerator(genNum).MaxPartLoadRat = NumArray(3);
210 6 : state.dataCTElectricGenerator->CTGenerator(genNum).OptPartLoadRat = NumArray(4);
211 :
212 : // Load Special CT Generator Input
213 :
214 6 : state.dataCTElectricGenerator->CTGenerator(genNum).PLBasedFuelInputCurve =
215 6 : Curve::GetCurveIndex(state, AlphArray(3)); // convert curve name to number
216 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).PLBasedFuelInputCurve == 0) {
217 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + '=' + AlphArray(3));
218 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
219 0 : ErrorsFound = true;
220 : }
221 :
222 6 : state.dataCTElectricGenerator->CTGenerator(genNum).TempBasedFuelInputCurve =
223 6 : Curve::GetCurveIndex(state, AlphArray(4)); // convert curve name to number
224 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).TempBasedFuelInputCurve == 0) {
225 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(4) + '=' + AlphArray(4));
226 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
227 0 : ErrorsFound = true;
228 : }
229 :
230 6 : state.dataCTElectricGenerator->CTGenerator(genNum).ExhaustFlowCurve =
231 6 : Curve::GetCurveIndex(state, AlphArray(5)); // convert curve name to number
232 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).ExhaustFlowCurve == 0) {
233 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(5) + '=' + AlphArray(5));
234 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
235 0 : ErrorsFound = true;
236 : }
237 :
238 6 : state.dataCTElectricGenerator->CTGenerator(genNum).PLBasedExhaustTempCurve =
239 6 : Curve::GetCurveIndex(state, AlphArray(6)); // convert curve name to number
240 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).PLBasedExhaustTempCurve == 0) {
241 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(6) + '=' + AlphArray(6));
242 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
243 0 : ErrorsFound = true;
244 : }
245 :
246 6 : state.dataCTElectricGenerator->CTGenerator(genNum).TempBasedExhaustTempCurve =
247 6 : Curve::GetCurveIndex(state, AlphArray(7)); // convert curve name to number
248 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).TempBasedExhaustTempCurve == 0) {
249 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(7) + '=' + AlphArray(7));
250 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
251 0 : ErrorsFound = true;
252 : }
253 :
254 6 : state.dataCTElectricGenerator->CTGenerator(genNum).QLubeOilRecoveredCurve =
255 6 : Curve::GetCurveIndex(state, AlphArray(8)); // convert curve name to number
256 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).QLubeOilRecoveredCurve == 0) {
257 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(8) + '=' + AlphArray(8));
258 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
259 0 : ErrorsFound = true;
260 : }
261 :
262 6 : state.dataCTElectricGenerator->CTGenerator(genNum).UACoef[0] = NumArray(5);
263 6 : state.dataCTElectricGenerator->CTGenerator(genNum).UACoef[1] = NumArray(6);
264 :
265 6 : state.dataCTElectricGenerator->CTGenerator(genNum).MaxExhaustperCTPower = NumArray(7);
266 6 : state.dataCTElectricGenerator->CTGenerator(genNum).DesignMinExitGasTemp = NumArray(8);
267 6 : state.dataCTElectricGenerator->CTGenerator(genNum).DesignAirInletTemp = NumArray(9);
268 6 : state.dataCTElectricGenerator->CTGenerator(genNum).FuelHeatingValue = NumArray(10);
269 6 : state.dataCTElectricGenerator->CTGenerator(genNum).DesignHeatRecVolFlowRate = NumArray(11);
270 :
271 6 : if (state.dataCTElectricGenerator->CTGenerator(genNum).DesignHeatRecVolFlowRate > 0.0) {
272 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecActive = true;
273 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecInletNodeNum =
274 6 : NodeInputManager::GetOnlySingleNode(state,
275 3 : AlphArray(9),
276 : ErrorsFound,
277 : DataLoopNode::ConnectionObjectType::GeneratorCombustionTurbine,
278 3 : AlphArray(1),
279 : DataLoopNode::NodeFluidType::Water,
280 : DataLoopNode::ConnectionType::Inlet,
281 : NodeInputManager::CompFluidStream::Primary,
282 3 : DataLoopNode::ObjectIsNotParent);
283 3 : if (state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecInletNodeNum == 0) {
284 0 : ShowSevereError(state,
285 0 : "Missing Node Name, Heat Recovery Inlet, for " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
286 0 : ErrorsFound = true;
287 : }
288 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecOutletNodeNum =
289 6 : NodeInputManager::GetOnlySingleNode(state,
290 3 : AlphArray(10),
291 : ErrorsFound,
292 : DataLoopNode::ConnectionObjectType::GeneratorCombustionTurbine,
293 3 : AlphArray(1),
294 : DataLoopNode::NodeFluidType::Water,
295 : DataLoopNode::ConnectionType::Outlet,
296 : NodeInputManager::CompFluidStream::Primary,
297 3 : DataLoopNode::ObjectIsNotParent);
298 3 : if (state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecOutletNodeNum == 0) {
299 0 : ShowSevereError(
300 0 : state, "Missing Node Name, Heat Recovery Outlet, for " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
301 0 : ErrorsFound = true;
302 : }
303 9 : BranchNodeConnections::TestCompSet(
304 6 : state, state.dataIPShortCut->cCurrentModuleObject, AlphArray(1), AlphArray(9), AlphArray(10), "Heat Recovery Nodes");
305 6 : PlantUtilities::RegisterPlantCompDesignFlow(state,
306 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecInletNodeNum,
307 3 : state.dataCTElectricGenerator->CTGenerator(genNum).DesignHeatRecVolFlowRate);
308 : } else {
309 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecActive = false;
310 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecInletNodeNum = 0;
311 3 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecOutletNodeNum = 0;
312 3 : if (!state.dataIPShortCut->lAlphaFieldBlanks(9) || !state.dataIPShortCut->lAlphaFieldBlanks(10)) {
313 0 : ShowWarningError(state,
314 0 : "Since Design Heat Flow Rate = 0.0, Heat Recovery inactive for " + state.dataIPShortCut->cCurrentModuleObject +
315 0 : '=' + AlphArray(1));
316 0 : ShowContinueError(state, "However, Node names were specified for Heat Recovery inlet or outlet nodes");
317 : }
318 : }
319 :
320 : // Validate fuel type input
321 6 : bool FuelTypeError(false);
322 6 : UtilityRoutines::ValidateFuelType(state, AlphArray(11), state.dataCTElectricGenerator->CTGenerator(genNum).FuelType, FuelTypeError);
323 6 : if (FuelTypeError) {
324 0 : ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(11) + '=' + AlphArray(11));
325 0 : ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + '=' + AlphArray(1));
326 0 : ErrorsFound = true;
327 0 : FuelTypeError = false;
328 : }
329 :
330 6 : state.dataCTElectricGenerator->CTGenerator(genNum).HeatRecMaxTemp = NumArray(12);
331 :
332 : // begin CR7021
333 6 : if (state.dataIPShortCut->lAlphaFieldBlanks(12)) {
334 4 : state.dataCTElectricGenerator->CTGenerator(genNum).OAInletNode = 0;
335 : } else {
336 2 : state.dataCTElectricGenerator->CTGenerator(genNum).OAInletNode =
337 4 : NodeInputManager::GetOnlySingleNode(state,
338 2 : AlphArray(12),
339 : ErrorsFound,
340 : DataLoopNode::ConnectionObjectType::GeneratorCombustionTurbine,
341 2 : AlphArray(1),
342 : DataLoopNode::NodeFluidType::Air,
343 : DataLoopNode::ConnectionType::OutsideAirReference,
344 : NodeInputManager::CompFluidStream::Primary,
345 2 : DataLoopNode::ObjectIsNotParent);
346 2 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, state.dataCTElectricGenerator->CTGenerator(genNum).OAInletNode)) {
347 0 : ShowSevereError(state,
348 0 : state.dataIPShortCut->cCurrentModuleObject + ", \"" + state.dataCTElectricGenerator->CTGenerator(genNum).Name +
349 0 : "\" Outdoor Air Inlet Node Name not valid Outdoor Air Node= " + AlphArray(12));
350 0 : ShowContinueError(state, "...does not appear in an OutdoorAir:NodeList or as an OutdoorAir:Node.");
351 0 : ErrorsFound = true;
352 : }
353 : }
354 : }
355 :
356 6 : if (ErrorsFound) {
357 0 : ShowFatalError(state, "Errors found in processing input for " + state.dataIPShortCut->cCurrentModuleObject);
358 : }
359 6 : }
360 :
361 6 : void CTGeneratorData::setupOutputVars(EnergyPlusData &state)
362 : {
363 12 : SetupOutputVariable(state,
364 : "Generator Produced AC Electricity Rate",
365 : OutputProcessor::Unit::W,
366 : this->ElecPowerGenerated,
367 : OutputProcessor::SOVTimeStepType::System,
368 : OutputProcessor::SOVStoreType::Average,
369 6 : this->Name);
370 :
371 12 : SetupOutputVariable(state,
372 : "Generator Produced AC Electricity Energy",
373 : OutputProcessor::Unit::J,
374 : this->ElecEnergyGenerated,
375 : OutputProcessor::SOVTimeStepType::System,
376 : OutputProcessor::SOVStoreType::Summed,
377 : this->Name,
378 : _,
379 : "ElectricityProduced",
380 : "COGENERATION",
381 : _,
382 6 : "Plant");
383 :
384 18 : SetupOutputVariable(state,
385 12 : "Generator " + this->FuelType + " Rate",
386 : OutputProcessor::Unit::W,
387 : this->FuelEnergyUseRate,
388 : OutputProcessor::SOVTimeStepType::System,
389 : OutputProcessor::SOVStoreType::Average,
390 : this->Name);
391 :
392 18 : SetupOutputVariable(state,
393 12 : "Generator " + this->FuelType + " Energy",
394 : OutputProcessor::Unit::J,
395 : this->FuelEnergy,
396 : OutputProcessor::SOVTimeStepType::System,
397 : OutputProcessor::SOVStoreType::Summed,
398 : this->Name,
399 : _,
400 : this->FuelType,
401 : "COGENERATION",
402 : _,
403 : "Plant");
404 :
405 : // general fuel use report (to match other generators)
406 12 : SetupOutputVariable(state,
407 : "Generator Fuel HHV Basis Rate",
408 : OutputProcessor::Unit::W,
409 : this->FuelEnergyUseRate,
410 : OutputProcessor::SOVTimeStepType::System,
411 : OutputProcessor::SOVStoreType::Average,
412 6 : this->Name);
413 :
414 12 : SetupOutputVariable(state,
415 : "Generator Fuel HHV Basis Energy",
416 : OutputProcessor::Unit::J,
417 : this->FuelEnergy,
418 : OutputProcessor::SOVTimeStepType::System,
419 : OutputProcessor::SOVStoreType::Summed,
420 6 : this->Name);
421 :
422 18 : SetupOutputVariable(state,
423 12 : "Generator " + this->FuelType + " Mass Flow Rate",
424 : OutputProcessor::Unit::kg_s,
425 : this->FuelMdot,
426 : OutputProcessor::SOVTimeStepType::System,
427 : OutputProcessor::SOVStoreType::Average,
428 : this->Name);
429 :
430 12 : SetupOutputVariable(state,
431 : "Generator Exhaust Air Temperature",
432 : OutputProcessor::Unit::C,
433 : this->ExhaustStackTemp,
434 : OutputProcessor::SOVTimeStepType::System,
435 : OutputProcessor::SOVStoreType::Average,
436 6 : this->Name);
437 :
438 6 : if (this->HeatRecActive) {
439 6 : SetupOutputVariable(state,
440 : "Generator Exhaust Heat Recovery Rate",
441 : OutputProcessor::Unit::W,
442 : this->QExhaustRecovered,
443 : OutputProcessor::SOVTimeStepType::System,
444 : OutputProcessor::SOVStoreType::Average,
445 3 : this->Name);
446 :
447 6 : SetupOutputVariable(state,
448 : "Generator Exhaust Heat Recovery Energy",
449 : OutputProcessor::Unit::J,
450 : this->ExhaustEnergyRec,
451 : OutputProcessor::SOVTimeStepType::System,
452 : OutputProcessor::SOVStoreType::Summed,
453 : this->Name,
454 : _,
455 : "ENERGYTRANSFER",
456 : "HEATRECOVERY",
457 : _,
458 3 : "Plant");
459 :
460 6 : SetupOutputVariable(state,
461 : "Generator Lube Heat Recovery Rate",
462 : OutputProcessor::Unit::W,
463 : this->QLubeOilRecovered,
464 : OutputProcessor::SOVTimeStepType::System,
465 : OutputProcessor::SOVStoreType::Average,
466 3 : this->Name);
467 :
468 6 : SetupOutputVariable(state,
469 : "Generator Lube Heat Recovery Energy",
470 : OutputProcessor::Unit::J,
471 : this->LubeOilEnergyRec,
472 : OutputProcessor::SOVTimeStepType::System,
473 : OutputProcessor::SOVStoreType::Summed,
474 : this->Name,
475 : _,
476 : "ENERGYTRANSFER",
477 : "HEATRECOVERY",
478 : _,
479 3 : "Plant");
480 :
481 6 : SetupOutputVariable(state,
482 : "Generator Produced Thermal Rate",
483 : OutputProcessor::Unit::W,
484 : this->QTotalHeatRecovered,
485 : OutputProcessor::SOVTimeStepType::System,
486 : OutputProcessor::SOVStoreType::Average,
487 3 : this->Name);
488 :
489 6 : SetupOutputVariable(state,
490 : "Generator Produced Thermal Energy",
491 : OutputProcessor::Unit::J,
492 : this->TotalHeatEnergyRec,
493 : OutputProcessor::SOVTimeStepType::System,
494 : OutputProcessor::SOVStoreType::Summed,
495 3 : this->Name);
496 :
497 6 : SetupOutputVariable(state,
498 : "Generator Heat Recovery Inlet Temperature",
499 : OutputProcessor::Unit::C,
500 : this->HeatRecInletTemp,
501 : OutputProcessor::SOVTimeStepType::System,
502 : OutputProcessor::SOVStoreType::Average,
503 3 : this->Name);
504 :
505 6 : SetupOutputVariable(state,
506 : "Generator Heat Recovery Outlet Temperature",
507 : OutputProcessor::Unit::C,
508 : this->HeatRecOutletTemp,
509 : OutputProcessor::SOVTimeStepType::System,
510 : OutputProcessor::SOVStoreType::Average,
511 3 : this->Name);
512 :
513 6 : SetupOutputVariable(state,
514 : "Generator Heat Recovery Mass Flow Rate",
515 : OutputProcessor::Unit::kg_s,
516 : this->HeatRecMdot,
517 : OutputProcessor::SOVTimeStepType::System,
518 : OutputProcessor::SOVStoreType::Average,
519 3 : this->Name);
520 : }
521 6 : }
522 :
523 54890 : void CTGeneratorData::CalcCTGeneratorModel(EnergyPlusData &state,
524 : bool const RunFlag, // TRUE when Generator operating
525 : Real64 const MyLoad, // Generator demand
526 : bool const FirstHVACIteration)
527 : {
528 : // SUBROUTINE INFORMATION:
529 : // AUTHOR Dan Fisher
530 : // DATE WRITTEN Sept. 2000
531 : // MODIFIED na
532 : // RE-ENGINEERED na
533 :
534 : // PURPOSE OF THIS SUBROUTINE:
535 : // simulate a vapor compression Generator using the CT model
536 :
537 : // METHODOLOGY EMPLOYED:
538 : // curve fit of performance data. This model was originally
539 : // developed by Dale Herron for the BLAST program
540 :
541 54890 : Real64 constexpr exhaustCp(1.047); // Exhaust Gas Specific Heat (J/kg-K)
542 54890 : Real64 constexpr KJtoJ(1000.0); // convert Kjoules to joules
543 : static constexpr std::string_view RoutineName("CalcCTGeneratorModel");
544 :
545 : // min allowed operating frac full load
546 54890 : Real64 minPartLoadRat = this->MinPartLoadRat;
547 :
548 : // max allowed operating frac full load
549 54890 : Real64 maxPartLoadRat = this->MaxPartLoadRat;
550 :
551 : // Generator nominal capacity (W)
552 54890 : Real64 ratedPowerOutput = this->RatedPowerOutput;
553 :
554 : // MAX EXHAUST FLOW PER W POWER OUTPUT COEFF
555 54890 : Real64 maxExhaustperCTPower = this->MaxExhaustperCTPower;
556 :
557 : // design turbine inlet temperature (C)
558 54890 : Real64 designAirInletTemp = this->DesignAirInletTemp;
559 :
560 : int heatRecInNode; // Heat Recovery Fluid Inlet Node Num
561 : Real64 heatRecInTemp; // Heat Recovery Fluid Inlet Temperature (C)
562 :
563 : Real64 heatRecMdot; // Heat Recovery Fluid Mass FlowRate (kg/s)
564 : Real64 heatRecCp; // Specific Heat of the Heat Recovery Fluid (J/kg-K)
565 :
566 54890 : if (this->HeatRecActive) {
567 27609 : heatRecInNode = this->HeatRecInletNodeNum;
568 27609 : heatRecInTemp = state.dataLoopNodes->Node(heatRecInNode).Temp;
569 :
570 55218 : heatRecCp = FluidProperties::GetSpecificHeatGlycol(state,
571 27609 : state.dataPlnt->PlantLoop(this->HRPlantLoc.loopNum).FluidName,
572 : heatRecInTemp,
573 27609 : state.dataPlnt->PlantLoop(this->HRPlantLoc.loopNum).FluidIndex,
574 : RoutineName);
575 27609 : if (FirstHVACIteration && RunFlag) {
576 11086 : heatRecMdot = this->DesignHeatRecMassFlowRate;
577 : } else {
578 16523 : heatRecMdot = state.dataLoopNodes->Node(heatRecInNode).MassFlowRate;
579 : }
580 : } else {
581 27281 : heatRecInTemp = 0.0;
582 27281 : heatRecCp = 0.0;
583 27281 : heatRecMdot = 0.0;
584 : }
585 :
586 : // If no loop demand or Generator OFF, return
587 54890 : if (!RunFlag) {
588 25568 : this->ElecPowerGenerated = 0.0;
589 25568 : this->ElecEnergyGenerated = 0.0;
590 25568 : this->HeatRecInletTemp = heatRecInTemp;
591 25568 : this->HeatRecOutletTemp = heatRecInTemp;
592 25568 : this->HeatRecMdot = 0.0;
593 25568 : this->QLubeOilRecovered = 0.0;
594 25568 : this->QExhaustRecovered = 0.0;
595 25568 : this->QTotalHeatRecovered = 0.0;
596 25568 : this->LubeOilEnergyRec = 0.0;
597 25568 : this->ExhaustEnergyRec = 0.0;
598 25568 : this->TotalHeatEnergyRec = 0.0;
599 25568 : this->FuelEnergyUseRate = 0.0;
600 25568 : this->FuelEnergy = 0.0;
601 25568 : this->FuelMdot = 0.0;
602 25568 : this->ExhaustStackTemp = 0.0;
603 25568 : return;
604 : }
605 :
606 : // CALCULATE POWER GENERATED AND PLR
607 : // Generator output (W)
608 29322 : Real64 elecPowerGenerated = min(MyLoad, ratedPowerOutput);
609 29322 : elecPowerGenerated = max(elecPowerGenerated, 0.0);
610 :
611 : // Generator operating part load ratio
612 29322 : Real64 PLR = min(elecPowerGenerated / ratedPowerOutput, maxPartLoadRat);
613 29322 : PLR = max(PLR, minPartLoadRat);
614 29322 : elecPowerGenerated = PLR * ratedPowerOutput;
615 :
616 : // SET OFF-DESIGN AIR TEMPERATURE DIFFERENCE
617 : // (ATAIR) Difference between ambient actual and ambient design temperatures
618 : Real64 ambientDeltaT;
619 29322 : if (this->OAInletNode == 0) {
620 26813 : ambientDeltaT = state.dataEnvrn->OutDryBulbTemp - designAirInletTemp;
621 : } else {
622 2509 : ambientDeltaT = state.dataLoopNodes->Node(this->OAInletNode).Temp - designAirInletTemp;
623 : }
624 :
625 : // Use Curve fit to determine Fuel Energy Input. For electric power generated in Watts, the fuel
626 : // energy input is calculated in J/s. The PLBasedFuelInputCurve selects ratio of fuel flow (J/s)/power generated (J/s).
627 : // The TempBasedFuelInputCurve is a correction based on deviation from design inlet air temperature conditions.
628 : // The first coefficient of this fit should be 1.0 to ensure that no correction is made at design conditions.
629 : // (EFUEL) rate of Fuel Energy Required to run COMBUSTION turbine (W)
630 58644 : Real64 FuelUseRate = elecPowerGenerated * Curve::CurveValue(state, this->PLBasedFuelInputCurve, PLR) *
631 29322 : Curve::CurveValue(state, this->TempBasedFuelInputCurve, ambientDeltaT);
632 :
633 : // Use Curve fit to determine Exhaust Flow. This curve shows the ratio of exhaust gas flow (kg/s) to electric power
634 : // output (J/s). The units on ExhaustFlowCurve are (kg/J). When multiplied by the rated power of the unit,
635 : // it gives the exhaust flow rate in kg/s
636 : // (FEX) Exhaust Gas Flow Rate cubic meters per second???
637 29322 : Real64 exhaustFlow = ratedPowerOutput * Curve::CurveValue(state, this->ExhaustFlowCurve, ambientDeltaT);
638 :
639 : // Use Curve fit to determine Exhaust Temperature. This curve calculates the exhaust temperature (C) by
640 : // multiplying the exhaust temperature (C) for a particular part load as given by PLBasedExhaustTempCurve
641 : // a correction factor based on the deviation from design temperature, TempBasedExhaustTempCurve
642 :
643 : Real64 QExhaustRec; // recovered exhaust heat (W)
644 : Real64 exhaustStackTemp; // turbine stack temp. (C)
645 29322 : if ((PLR > 0.0) && ((exhaustFlow > 0.0) || (maxExhaustperCTPower > 0.0))) {
646 :
647 : // (TEX) Exhaust Gas Temperature in C
648 38870 : Real64 exhaustTemp = Curve::CurveValue(state, this->PLBasedExhaustTempCurve, PLR) *
649 19435 : Curve::CurveValue(state, this->TempBasedExhaustTempCurve, ambientDeltaT);
650 :
651 : // (UACGC) Heat Exchanger UA to Capacity
652 19435 : Real64 UA_loc = this->UACoef[0] * std::pow(ratedPowerOutput, this->UACoef[1]);
653 :
654 : // design engine stack saturated steam temp. (C)
655 19435 : Real64 designMinExitGasTemp = this->DesignMinExitGasTemp;
656 :
657 38870 : exhaustStackTemp = designMinExitGasTemp + (exhaustTemp - designMinExitGasTemp) /
658 19435 : std::exp(UA_loc / (max(exhaustFlow, maxExhaustperCTPower * ratedPowerOutput) * exhaustCp));
659 :
660 19435 : QExhaustRec = max(exhaustFlow * exhaustCp * (exhaustTemp - exhaustStackTemp), 0.0);
661 : } else {
662 9887 : exhaustStackTemp = this->DesignMinExitGasTemp;
663 9887 : QExhaustRec = 0.0;
664 : }
665 :
666 : // Use Curve fit to determine Heat Recovered Lubricant heat. This curve calculates the lube heat recovered (J/s) by
667 : // multiplying the total power generated by the fraction of that power that could be recovered in the lube oil at that
668 : // particular part load.
669 : // recovered lube oil heat (W)
670 29322 : Real64 QLubeOilRec = elecPowerGenerated * Curve::CurveValue(state, this->QLubeOilRecoveredCurve, PLR);
671 :
672 : // Check for divide by zero
673 : Real64 HeatRecOutTemp; // Heat Recovery Fluid Outlet Temperature (C)
674 29322 : if ((heatRecMdot > 0.0) && (heatRecCp > 0.0)) {
675 16917 : HeatRecOutTemp = (QExhaustRec + QLubeOilRec) / (heatRecMdot * heatRecCp) + heatRecInTemp;
676 : } else {
677 12405 : heatRecMdot = 0.0;
678 12405 : HeatRecOutTemp = heatRecInTemp;
679 12405 : QExhaustRec = 0.0;
680 12405 : QLubeOilRec = 0.0;
681 : }
682 :
683 : // Now verify the maximum temperature was not exceeded
684 : // Heat Recovery Flow Rate if minimal heat recovery is accomplished
685 29322 : Real64 MinHeatRecMdot = 0.0;
686 :
687 : Real64 HRecRatio; // When Max Temp is reached the amount of recovered heat has to be reduced.
688 :
689 29322 : if (HeatRecOutTemp > this->HeatRecMaxTemp) {
690 12512 : if (this->HeatRecMaxTemp != heatRecInTemp) {
691 12512 : MinHeatRecMdot = (QExhaustRec + QLubeOilRec) / (heatRecCp * (this->HeatRecMaxTemp - heatRecInTemp));
692 12512 : if (MinHeatRecMdot < 0.0) MinHeatRecMdot = 0.0;
693 : }
694 :
695 : // Recalculate Outlet Temperature, with adjusted flowrate
696 12512 : if ((MinHeatRecMdot > 0.0) && (heatRecCp > 0.0)) {
697 12512 : HeatRecOutTemp = (QExhaustRec + QLubeOilRec) / (MinHeatRecMdot * heatRecCp) + heatRecInTemp;
698 12512 : HRecRatio = heatRecMdot / MinHeatRecMdot;
699 : } else {
700 0 : HeatRecOutTemp = heatRecInTemp;
701 0 : HRecRatio = 0.0;
702 : }
703 12512 : QLubeOilRec *= HRecRatio;
704 12512 : QExhaustRec *= HRecRatio;
705 : }
706 :
707 : // Calculate Energy
708 : // Generator output (J)
709 29322 : Real64 ElectricEnergyGen = elecPowerGenerated * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
710 :
711 : // Amount of Fuel Energy Required to run COMBUSTION turbine (J)
712 29322 : Real64 FuelEnergyUsed = FuelUseRate * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
713 :
714 : // recovered lube oil heat (J)
715 29322 : Real64 lubeOilEnergyRec = QLubeOilRec * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
716 :
717 : // recovered exhaust heat (J)
718 29322 : Real64 exhaustEnergyRec = QExhaustRec * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
719 :
720 29322 : this->ElecPowerGenerated = elecPowerGenerated;
721 29322 : this->ElecEnergyGenerated = ElectricEnergyGen;
722 :
723 29322 : this->HeatRecInletTemp = heatRecInTemp;
724 29322 : this->HeatRecOutletTemp = HeatRecOutTemp;
725 :
726 29322 : this->HeatRecMdot = heatRecMdot;
727 29322 : this->QExhaustRecovered = QExhaustRec;
728 29322 : this->QLubeOilRecovered = QLubeOilRec;
729 29322 : this->QTotalHeatRecovered = QExhaustRec + QLubeOilRec;
730 29322 : this->FuelEnergyUseRate = std::abs(FuelUseRate);
731 29322 : this->ExhaustEnergyRec = exhaustEnergyRec;
732 29322 : this->LubeOilEnergyRec = lubeOilEnergyRec;
733 29322 : this->TotalHeatEnergyRec = exhaustEnergyRec + lubeOilEnergyRec;
734 29322 : this->FuelEnergy = std::abs(FuelEnergyUsed);
735 :
736 : // Heating Value of Fuel in (kJ/kg)
737 29322 : Real64 fuelHeatingValue = this->FuelHeatingValue;
738 :
739 29322 : this->FuelMdot = std::abs(FuelUseRate) / (fuelHeatingValue * KJtoJ);
740 :
741 29322 : this->ExhaustStackTemp = exhaustStackTemp;
742 :
743 29322 : if (this->HeatRecActive) {
744 16926 : int HeatRecOutletNode = this->HeatRecOutletNodeNum;
745 16926 : state.dataLoopNodes->Node(HeatRecOutletNode).Temp = this->HeatRecOutletTemp;
746 : }
747 : }
748 :
749 54890 : void CTGeneratorData::InitCTGenerators(EnergyPlusData &state,
750 : bool const RunFlag, // TRUE when Generator operating
751 : bool const FirstHVACIteration)
752 : {
753 :
754 : // SUBROUTINE INFORMATION:
755 : // AUTHOR Dan Fisher
756 : // DATE WRITTEN Oct 2000
757 : // MODIFIED na
758 : // RE-ENGINEERED Brent Griffith, Sept 2010 plant upgrades, generalize fluid props
759 :
760 : // PURPOSE OF THIS SUBROUTINE:
761 : // This subroutine is for initializations of the CT generators.
762 :
763 54890 : this->oneTimeInit(state); // Do one-time inits
764 :
765 : // Do the Begin Environment initializations
766 54890 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlag && this->HeatRecActive) {
767 22 : int HeatRecInletNode = this->HeatRecInletNodeNum;
768 22 : int HeatRecOutletNode = this->HeatRecOutletNodeNum;
769 : // set the node Temperature, assuming freeze control
770 22 : state.dataLoopNodes->Node(HeatRecInletNode).Temp = 20.0;
771 22 : state.dataLoopNodes->Node(HeatRecOutletNode).Temp = 20.0;
772 : // set the node max and min mass flow rates
773 22 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesignHeatRecMassFlowRate, HeatRecInletNode, HeatRecOutletNode);
774 :
775 22 : this->MyEnvrnFlag = false;
776 : } // end environmental inits
777 :
778 54890 : if (!state.dataGlobal->BeginEnvrnFlag) {
779 54348 : this->MyEnvrnFlag = true;
780 : }
781 :
782 54890 : if (this->HeatRecActive) {
783 27609 : if (FirstHVACIteration) {
784 : Real64 mdot;
785 17956 : if (RunFlag) {
786 11086 : mdot = this->DesignHeatRecMassFlowRate;
787 : } else {
788 6870 : mdot = 0.0;
789 : }
790 17956 : PlantUtilities::SetComponentFlowRate(state, mdot, this->HeatRecInletNodeNum, this->HeatRecOutletNodeNum, this->HRPlantLoc);
791 :
792 : } else {
793 9653 : PlantUtilities::SetComponentFlowRate(
794 : state, this->HeatRecMdot, this->HeatRecInletNodeNum, this->HeatRecOutletNodeNum, this->HRPlantLoc);
795 : }
796 : }
797 54890 : }
798 :
799 54890 : void CTGeneratorData::oneTimeInit(EnergyPlusData &state)
800 : {
801 54890 : auto constexpr RoutineName("InitICEngineGenerators");
802 : bool errFlag;
803 :
804 54890 : if (this->MyPlantScanFlag) { // this flag to be removed
805 6 : if (allocated(state.dataPlnt->PlantLoop) && this->HeatRecActive) {
806 3 : errFlag = false;
807 3 : PlantUtilities::ScanPlantLoopsForObject(
808 : state, this->Name, DataPlant::PlantEquipmentType::Generator_CTurbine, this->HRPlantLoc, errFlag, _, _, _, _, _);
809 3 : if (errFlag) {
810 0 : ShowFatalError(state, "InitCTGenerators: Program terminated due to previous condition(s).");
811 : }
812 : }
813 :
814 6 : this->MyPlantScanFlag = false;
815 : }
816 :
817 54890 : if (this->MyFlag) {
818 6 : this->setupOutputVars(state);
819 6 : this->MyFlag = false;
820 : }
821 :
822 54890 : if (this->MySizeAndNodeInitFlag && (!this->MyPlantScanFlag) && this->HeatRecActive) {
823 3 : int HeatRecInletNode = this->HeatRecInletNodeNum;
824 3 : int HeatRecOutletNode = this->HeatRecOutletNodeNum;
825 :
826 : // size mass flow rate
827 9 : Real64 rho = FluidProperties::GetDensityGlycol(state,
828 3 : state.dataPlnt->PlantLoop(this->HRPlantLoc.loopNum).FluidName,
829 : DataGlobalConstants::InitConvTemp,
830 3 : state.dataPlnt->PlantLoop(this->HRPlantLoc.loopNum).FluidIndex,
831 6 : RoutineName);
832 :
833 3 : this->DesignHeatRecMassFlowRate = rho * this->DesignHeatRecVolFlowRate;
834 :
835 3 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesignHeatRecMassFlowRate, HeatRecInletNode, HeatRecOutletNode);
836 :
837 3 : this->MySizeAndNodeInitFlag = false;
838 : }
839 54890 : }
840 :
841 : } // namespace CTElectricGenerator
842 :
843 2313 : } // namespace EnergyPlus
|