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/CurveManager.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataGenerators.hh>
59 : #include <EnergyPlus/DataGlobalConstants.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/DataLoopNode.hh>
62 : #include <EnergyPlus/GeneratorDynamicsManager.hh>
63 : #include <EnergyPlus/MicroCHPElectricGenerator.hh>
64 : #include <EnergyPlus/PlantUtilities.hh>
65 : #include <EnergyPlus/ScheduleManager.hh>
66 :
67 : namespace EnergyPlus {
68 :
69 : namespace GeneratorDynamicsManager {
70 :
71 : //_______________________________________________
72 : // Utility modules used by other generators.
73 : //
74 : // GeneratorDynamicsManager
75 : // reused among some generators to on/off state, transient limits, control implications etc.
76 :
77 : // Module containing the routines dealing with the management of dynamic constraints on Generator response
78 :
79 : // MODULE INFORMATION:
80 : // AUTHOR B. Griffith
81 : // DATE WRITTEN July 2006
82 :
83 : // PURPOSE OF THIS MODULE:
84 : // collect routines for managing generator states
85 : // reused by different generator models
86 : // determine response that generator is capable of providing
87 : // given load request data
88 : // models requiring calculations across timesteps
89 :
90 : using namespace DataGenerators;
91 10 : void SetupGeneratorControlStateManager(EnergyPlusData &state, int const GenNum) // index of generator to setup
92 : {
93 : // SUBROUTINE INFORMATION:
94 : // AUTHOR B. Griffith
95 : // DATE WRITTEN July 2006
96 :
97 : // PURPOSE OF THIS SUBROUTINE:
98 : // sets up data structures
99 :
100 : // METHODOLOGY EMPLOYED:
101 : // like a get input routine but feeds from
102 : // parent objects, could have its own input object someday
103 :
104 : // get the number of generators that might use this module
105 10 : int NumGensWDynamics = state.dataCHPElectGen->NumMicroCHPs; // TODO + NumFuelCellCGenerators
106 :
107 10 : if (!allocated(state.dataGenerator->GeneratorDynamics)) {
108 2 : state.dataGenerator->GeneratorDynamics.allocate(NumGensWDynamics);
109 : }
110 :
111 : // first populate with Micro CHP data
112 :
113 10 : state.dataGenerator->GeneratorDynamics(GenNum).Name = state.dataCHPElectGen->MicroCHP(GenNum).Name;
114 10 : state.dataGenerator->GeneratorDynamics(GenNum).PelMin = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MinElecPower;
115 10 : state.dataGenerator->GeneratorDynamics(GenNum).PelMax = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MaxElecPower;
116 10 : state.dataGenerator->GeneratorDynamics(GenNum).UpTranLimit = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaPelMax;
117 10 : state.dataGenerator->GeneratorDynamics(GenNum).DownTranLimit = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaPelMax;
118 10 : state.dataGenerator->GeneratorDynamics(GenNum).UpTranLimitFuel = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaFuelMdotMax;
119 10 : state.dataGenerator->GeneratorDynamics(GenNum).DownTranLimitFuel = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.DeltaFuelMdotMax;
120 10 : state.dataGenerator->GeneratorDynamics(GenNum).WarmUpByTimeDelay = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpByTimeDelay;
121 10 : state.dataGenerator->GeneratorDynamics(GenNum).WarmUpByEngineTemp = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpByEngineTemp;
122 10 : state.dataGenerator->GeneratorDynamics(GenNum).MandatoryFullCoolDown = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MandatoryFullCoolDown;
123 10 : state.dataGenerator->GeneratorDynamics(GenNum).WarmRestartOkay = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmRestartOkay;
124 10 : state.dataGenerator->GeneratorDynamics(GenNum).WarmUpDelay = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpDelay;
125 10 : state.dataGenerator->GeneratorDynamics(GenNum).CoolDownDelay =
126 10 : state.dataCHPElectGen->MicroCHP(GenNum).A42Model.CoolDownDelay / DataGlobalConstants::SecInHour; // seconds to hours
127 10 : state.dataGenerator->GeneratorDynamics(GenNum).PcoolDown = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.PcoolDown;
128 10 : state.dataGenerator->GeneratorDynamics(GenNum).Pstandby = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.Pstandby;
129 10 : state.dataGenerator->GeneratorDynamics(GenNum).MCeng = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MCeng;
130 10 : state.dataGenerator->GeneratorDynamics(GenNum).MCcw = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MCcw;
131 10 : state.dataGenerator->GeneratorDynamics(GenNum).kf = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.kf;
132 10 : state.dataGenerator->GeneratorDynamics(GenNum).TnomEngOp = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.TnomEngOp;
133 10 : state.dataGenerator->GeneratorDynamics(GenNum).kp = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.kp;
134 10 : state.dataGenerator->GeneratorDynamics(GenNum).AvailabilitySchedID = state.dataCHPElectGen->MicroCHP(GenNum).AvailabilitySchedID;
135 10 : state.dataGenerator->GeneratorDynamics(GenNum).StartUpTimeDelay =
136 10 : state.dataCHPElectGen->MicroCHP(GenNum).A42Model.WarmUpDelay / DataGlobalConstants::SecInHour; // seconds to hours
137 :
138 10 : state.dataGenerator->GeneratorDynamics(GenNum).ElectEffNom = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ElecEff;
139 10 : state.dataGenerator->GeneratorDynamics(GenNum).ThermEffNom = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ThermEff;
140 30 : state.dataGenerator->GeneratorDynamics(GenNum).QdotHXMax = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ThermEff *
141 20 : state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MaxElecPower /
142 10 : state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ElecEff;
143 30 : state.dataGenerator->GeneratorDynamics(GenNum).QdotHXMin = state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ThermEff *
144 20 : state.dataCHPElectGen->MicroCHP(GenNum).A42Model.MinElecPower /
145 10 : state.dataCHPElectGen->MicroCHP(GenNum).A42Model.ElecEff;
146 10 : state.dataGenerator->GeneratorDynamics(GenNum).QdotHXOpt = state.dataGenerator->GeneratorDynamics(GenNum).QdotHXMax;
147 10 : state.dataCHPElectGen->MicroCHP(GenNum).DynamicsControlID = GenNum;
148 10 : }
149 :
150 13932 : void ManageGeneratorControlState(EnergyPlusData &state,
151 : GeneratorType const GeneratorType, // type of Generator
152 : [[maybe_unused]] std::string const &GeneratorName, // user specified name of Generator
153 : int const GeneratorNum, // Generator number
154 : bool const RunFlagElectCenter, // TRUE when Generator operating per electric load center request
155 : bool const RunFlagPlant, // TRUE when generator operating per Plant request (always false)
156 : Real64 const ElecLoadRequest, // Generator Electrical power demand
157 : Real64 const ThermalLoadRequest, // cogenerator Thermal power demand
158 : Real64 &ElecLoadProvided, // power allowed
159 : DataGenerators::OperatingMode &OperatingMode, // operating mode
160 : Real64 &PLRforSubtimestepStartUp, // part load ratio for switch to normal from start up
161 : Real64 &PLRforSubtimestepShutDown, // part load ratio for switch from cool down to other
162 : [[maybe_unused]] bool const FirstHVACIteration // True is this is first HVAC iteration
163 : )
164 : {
165 :
166 : // SUBROUTINE INFORMATION:
167 : // AUTHOR B Griffith
168 : // DATE WRITTEN February-March 2007 (replaced July 2006 attempt)
169 : // MODIFIED Dec 2009, check and constrain with flow available from plant
170 : // RE-ENGINEERED na
171 :
172 : // PURPOSE OF THIS SUBROUTINE:
173 : // provide a service to other generators to make decisions, mostly temporal, or cross-timestep issues
174 : // used to model internal controlling issues within an individual generator model
175 : // This subroutine determines the current operating mode and returns the allowed power and
176 : // and part load ratio for certain sub-time step switching e.g. in and out of normal mode or cool down mode
177 :
178 : // METHODOLOGY EMPLOYED:
179 : // model controls-related issues, rules based algorithm
180 : // Control decision results include:
181 : // -- electrical load allowed/resulting/provided
182 : // -- new operating mode
183 : // -- part load this timestep for shift to normal mode occuring midway in timestep
184 : // -- part load this timestep for shift out of cool down mode
185 :
186 : // Input data used to make control decisions include:
187 : // -- Electrical load request
188 : // -- Thermal Load request
189 : // -- RunFlagElectricCenter
190 : // -- RunFlagPlant
191 : // -- previous timestep operating mode
192 : // -- previous timestep Power generated
193 : // -- availability schedule (off if not available)
194 : // -- Generator control parameter constants including
195 : // ** Start Up Time Delay (in hours)
196 : // ** Cool-down time delay (in hours)
197 : // -- Expected Plant flow rate
198 : // -- minimum cooling water flow rate
199 :
200 : // Algorithm summary
201 : // 1. examine calling run flags and refine electric load request to account for
202 : // thermal load requests (not yet ready for prime time)
203 : // 2. Determine states of various control inputs that change during simulation
204 : // 3. enter case statement based on previous operating mode.
205 : // -- decide on current operating mode
206 : // -- calculate part loads
207 :
208 : // 4. based on current operating mode determine allowed/provided electrical load
209 : // a. set allowed elec load by mode
210 : // b. set allowed elec load by constraints on rate of change
211 : // c. set allowed elec load by min and max
212 :
213 : // 5. Calculated part load ratios for special cases.
214 : // REFERENCES:
215 : // controls specifications in Annex 42 model specs.
216 : // Using/Aliasing
217 : using namespace DataGlobalConstants;
218 13932 : auto &SysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
219 13932 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
220 : using ScheduleManager::GetCurrentScheduleValue;
221 : using ScheduleManager::GetScheduleIndex;
222 :
223 : // Locals
224 : // SUBROUTINE ARGUMENT DEFINITIONS:
225 : // this is the part in normal mode
226 : // this is the part in cool down mode.
227 :
228 : // SUBROUTINE PARAMETER DEFINITIONS:
229 : // na
230 :
231 : // INTERFACE BLOCK SPECIFICATIONS:
232 : // na
233 :
234 : // DERIVED TYPE DEFINITIONS:
235 : // na
236 :
237 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
238 : bool RunFlag; // true if generator supposed to run
239 13932 : int DynaCntrlNum(0); // index in GeneratorDynamics structure for this generator ! na
240 : Real64 CurrentFractionalDay; // working var, time in decimal days
241 : Real64 EndingFractionalDay; // working var, time is decimal days
242 : Real64 LastSystemTimeStepFractionalDay; // working var, time is decimal days
243 : Real64 MaxPel; // working variable for max allowed by transient constraint
244 : Real64 MinPel; // working variabel for min allowed by transient constraint
245 : Real64 PelInput; // holds initial value of IN var
246 : Real64 Pel;
247 13932 : DataGenerators::OperatingMode newOpMode(DataGenerators::OperatingMode::Invalid);
248 : Real64 SchedVal;
249 : Real64 ElectLoadForThermalRequest;
250 : bool ConstrainedMaxP; // true if request was altered because of max power limit
251 : bool ConstrainedMinP; // true if request was altered because of min power limit
252 : bool ConstrainedIncreasingPdot; // true if request was altered because of power rate of change up
253 : bool ConstrainedDecreasingPdot; // true if request was altered because of power rate of change down
254 : bool ConstrainedByPlant; // true if request was altered because of cooling water problem
255 : bool PLRStartUp; // true if subtimestep issue involving startup
256 : bool PLRShutDown;
257 13932 : auto &InletCWnode = state.dataGenerator->InletCWnode;
258 13932 : auto &InternalFlowControl = state.dataGenerator->InternalFlowControl;
259 13932 : auto &TcwIn = state.dataGenerator->TcwIn;
260 13932 : auto &TrialMdotcw = state.dataGenerator->TrialMdotcw;
261 13932 : auto &LimitMinMdotcw = state.dataGenerator->LimitMinMdotcw;
262 :
263 : // inits
264 13932 : PLRforSubtimestepStartUp = 1.0;
265 13932 : PLRforSubtimestepShutDown = 0.0;
266 13932 : ConstrainedMaxP = false;
267 13932 : ConstrainedMinP = false;
268 13932 : ConstrainedIncreasingPdot = false;
269 13932 : ConstrainedDecreasingPdot = false;
270 13932 : ConstrainedByPlant = false;
271 13932 : PLRStartUp = false;
272 13932 : PLRShutDown = false;
273 13932 : InternalFlowControl = false;
274 :
275 : // get index for this generator in dynamics control structure
276 13932 : switch (GeneratorType) {
277 13932 : case GeneratorType::MicroCHP: {
278 13932 : DynaCntrlNum = state.dataCHPElectGen->MicroCHP(GeneratorNum).DynamicsControlID;
279 : // OutletCWnode = MicroCHPElectricGenerator::MicroCHP(GeneratorNum)%PlantOutletNodeID
280 13932 : InletCWnode = state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantInletNodeID;
281 13932 : TcwIn = state.dataLoopNodes->Node(state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantInletNodeID).Temp;
282 13932 : if (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.InternalFlowControl) {
283 7798 : InternalFlowControl = true;
284 : }
285 13932 : LimitMinMdotcw = state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.MinWaterMdot;
286 13932 : } break;
287 0 : case GeneratorType::FuelCell: {
288 : // not yet
289 0 : } break;
290 0 : default:
291 0 : break;
292 : }
293 :
294 13932 : PelInput = ElecLoadRequest;
295 13932 : ElectLoadForThermalRequest = 0.0;
296 13932 : if ((ThermalLoadRequest > 0.0) && RunFlagPlant) { // deal with possible thermal load following
297 : // Modify electric load request based on thermal load following signal using nominal efficiencies
298 0 : ElectLoadForThermalRequest = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).ThermEffNom * ThermalLoadRequest /
299 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).ElectEffNom;
300 0 : PelInput = max(PelInput, ElectLoadForThermalRequest);
301 : }
302 :
303 13932 : if ((RunFlagElectCenter) || (RunFlagPlant)) {
304 4335 : RunFlag = true;
305 : } else {
306 9597 : RunFlag = false;
307 : }
308 :
309 : // check availability schedule
310 13932 : SchedVal = GetCurrentScheduleValue(state, state.dataGenerator->GeneratorDynamics(DynaCntrlNum).AvailabilitySchedID);
311 :
312 13932 : Pel = PelInput;
313 :
314 : // get data to check if sufficient flow available from Plant
315 13932 : if (InternalFlowControl && (SchedVal > 0.0)) {
316 7798 : TrialMdotcw = FuncDetermineCWMdotForInternalFlowControl(state, GeneratorNum, Pel, TcwIn);
317 : } else {
318 6134 : TrialMdotcw = state.dataLoopNodes->Node(InletCWnode).MassFlowRate;
319 : }
320 :
321 : // determine current operating mode.
322 13932 : switch (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).LastOpMode) {
323 9870 : case DataGenerators::OperatingMode::Off:
324 : case DataGenerators::OperatingMode::Standby: {
325 : // possible future states {Off, Standby, WarmUp,Normal }
326 9870 : if (SchedVal == 0.0) {
327 5588 : newOpMode = DataGenerators::OperatingMode::Off;
328 :
329 4282 : } else if (((SchedVal != 0.0) && (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
330 3017 : newOpMode = DataGenerators::OperatingMode::Standby;
331 1265 : } else if ((SchedVal != 0.0) && (RunFlag)) {
332 :
333 1265 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByTimeDelay) {
334 :
335 1265 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay == 0.0) {
336 42 : newOpMode = DataGenerators::OperatingMode::Normal;
337 :
338 : // is startUp time delay longer than timestep?
339 1223 : } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay >= TimeStepSys) {
340 0 : newOpMode = DataGenerators::OperatingMode::WarmUp;
341 : // generator just started so set start time
342 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp =
343 0 : double(state.dataGlobal->DayOfSim) +
344 0 : (int(state.dataGlobal->CurrentTime) +
345 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime) - TimeStepSys))) /
346 : DataGlobalConstants::HoursInDay;
347 :
348 : } else { // warm up period is less than a single system time step
349 1223 : newOpMode = DataGenerators::OperatingMode::Normal;
350 1223 : PLRStartUp = true;
351 1223 : PLRforSubtimestepStartUp =
352 1223 : (TimeStepSys - state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay) / TimeStepSys;
353 : }
354 : }
355 1265 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByEngineTemp) {
356 0 : if (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng >=
357 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) {
358 0 : newOpMode = DataGenerators::OperatingMode::Normal;
359 : // assume linear interpolation for PLR
360 0 : PLRStartUp = true;
361 0 : if ((state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
362 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast) > 0.0) {
363 : // protect divide by zero or neg
364 0 : PLRforSubtimestepStartUp = (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
365 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) /
366 0 : (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
367 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast);
368 : } else {
369 0 : PLRforSubtimestepStartUp = 1.0;
370 : }
371 : } else {
372 0 : newOpMode = DataGenerators::OperatingMode::WarmUp;
373 : }
374 : }
375 : }
376 :
377 9870 : } break;
378 0 : case DataGenerators::OperatingMode::WarmUp: {
379 : // possible Future states {OFF, WarmUp, Normal, CoolDown }
380 : // check availability manager
381 0 : if (SchedVal == 0.0) {
382 : // to off unless cool down time period is needed
383 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay == 0.0) {
384 0 : newOpMode = DataGenerators::OperatingMode::Off;
385 : } else {
386 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > TimeStepSys) {
387 0 : newOpMode = DataGenerators::OperatingMode::CoolDown;
388 : // need to reset time of last shut down here
389 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
390 0 : double(state.dataGlobal->DayOfSim) +
391 0 : (int(state.dataGlobal->CurrentTime) +
392 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
393 : DataGlobalConstants::HoursInDay;
394 : } else {
395 0 : newOpMode = DataGenerators::OperatingMode::Off;
396 : }
397 : }
398 0 : } else if (((SchedVal != 0.0) && (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
399 : // to standby unless cool down time period is needed
400 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay == 0.0) {
401 0 : newOpMode = DataGenerators::OperatingMode::Standby;
402 : } else {
403 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > TimeStepSys) {
404 0 : newOpMode = DataGenerators::OperatingMode::CoolDown;
405 : // need to reset time of last shut down here
406 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
407 0 : double(state.dataGlobal->DayOfSim) +
408 0 : (int(state.dataGlobal->CurrentTime) +
409 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
410 : DataGlobalConstants::HoursInDay;
411 :
412 : } else {
413 0 : newOpMode = DataGenerators::OperatingMode::Standby;
414 : // assuming no PLR situation unless engine made to normal operation.
415 : }
416 : }
417 0 : } else if ((SchedVal != 0.0) && (RunFlag)) {
418 : // either warm up or normal
419 : // check if warm up completed, depends on type of warm up control time delay or reach nominal temperature
420 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByTimeDelay) {
421 : // compare current time to when warm up is over
422 : // calculate time for end of warmup period
423 0 : CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
424 0 : (int(state.dataGlobal->CurrentTime) +
425 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
426 : DataGlobalConstants::HoursInDay;
427 0 : EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp +
428 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay / DataGlobalConstants::HoursInDay;
429 0 : if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) || (CurrentFractionalDay > EndingFractionalDay)) {
430 0 : newOpMode = DataGenerators::OperatingMode::Normal;
431 0 : PLRStartUp = true;
432 0 : LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
433 0 : PLRforSubtimestepStartUp =
434 0 : ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
435 : } else {
436 0 : newOpMode = DataGenerators::OperatingMode::WarmUp;
437 : }
438 :
439 0 : } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByEngineTemp) {
440 0 : if (GeneratorType == GeneratorType::MicroCHP) {
441 : // only change to normal if this is result from completed timestep, not just an interation
442 0 : if (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast >=
443 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) {
444 0 : newOpMode = DataGenerators::OperatingMode::Normal;
445 : // assume linear interpolation for PLR
446 0 : PLRStartUp = true;
447 0 : if ((state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
448 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast) > 0.0) {
449 : // protect divide by zero or neg
450 0 : PLRforSubtimestepStartUp = (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
451 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).TnomEngOp) /
452 0 : (state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.Teng -
453 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.TengLast);
454 : } else {
455 0 : PLRforSubtimestepStartUp = 1.0;
456 : }
457 : } else {
458 0 : newOpMode = DataGenerators::OperatingMode::WarmUp;
459 : }
460 : }
461 : } else {
462 : // shouldn't come here
463 : // Write(*,*) 'problem with warm up type of control logical flags'
464 : }
465 : }
466 0 : } break;
467 4062 : case DataGenerators::OperatingMode::Normal: {
468 : // possible Future states {CoolDown, standby, off}
469 4062 : if (((SchedVal == 0.0) || (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
470 : // is cool down time delay longer than timestep?
471 1984 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay == 0.0) {
472 42 : if (SchedVal != 0.0) {
473 0 : newOpMode = DataGenerators::OperatingMode::Standby;
474 : } else {
475 42 : newOpMode = DataGenerators::OperatingMode::Off;
476 : }
477 950 : } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay >= TimeStepSys) {
478 0 : newOpMode = DataGenerators::OperatingMode::CoolDown;
479 : // also, generator just shut down so record shut down time
480 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
481 0 : double(state.dataGlobal->DayOfSim) +
482 0 : (int(state.dataGlobal->CurrentTime) +
483 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
484 : DataGlobalConstants::HoursInDay;
485 : } else { // cool down period is less than a single system time step
486 950 : if (SchedVal != 0.0) {
487 950 : newOpMode = DataGenerators::OperatingMode::Standby;
488 : } else {
489 0 : newOpMode = DataGenerators::OperatingMode::Off;
490 : }
491 950 : PLRShutDown = true;
492 950 : PLRforSubtimestepShutDown = (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay) / TimeStepSys;
493 :
494 : // also, generator just shut down so record shut down time
495 950 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown =
496 1900 : double(state.dataGlobal->DayOfSim) +
497 1900 : (int(state.dataGlobal->CurrentTime) +
498 1900 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
499 : DataGlobalConstants::HoursInDay;
500 : }
501 3070 : } else if ((SchedVal != 0.0) && (RunFlag)) {
502 :
503 3070 : newOpMode = DataGenerators::OperatingMode::Normal;
504 : }
505 4062 : } break;
506 0 : case DataGenerators::OperatingMode::CoolDown: {
507 : // possible Future States {Standby, OFF, WarmUp, Normal}
508 :
509 0 : if (SchedVal == 0.0) { // no longer available.
510 : // probably goes to off but could be stuck in cool down for awhile
511 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > 0.0) {
512 : // calculate time for end of cool down period
513 0 : CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
514 0 : (int(state.dataGlobal->CurrentTime) +
515 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
516 : DataGlobalConstants::HoursInDay;
517 0 : EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
518 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay -
519 0 : (TimeStepSys / DataGlobalConstants::HoursInDay);
520 0 : if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
521 : (CurrentFractionalDay > EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
522 0 : newOpMode = DataGenerators::OperatingMode::Off;
523 0 : PLRShutDown = true;
524 0 : LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
525 0 : PLRforSubtimestepShutDown =
526 0 : (EndingFractionalDay - LastSystemTimeStepFractionalDay) * DataGlobalConstants::HoursInDay / TimeStepSys;
527 : } else { // CurrentFractionalDay > EndingFractionalDay
528 0 : newOpMode = DataGenerators::OperatingMode::CoolDown;
529 : }
530 : } else {
531 0 : newOpMode = DataGenerators::OperatingMode::Off;
532 : }
533 0 : } else if (((SchedVal != 0.0) && (!RunFlag)) || (TrialMdotcw < LimitMinMdotcw)) {
534 : // probably goes to standby but could be stuck in cool down for awhile
535 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > 0.0) {
536 : // calculate time for end of cool down period
537 0 : CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
538 0 : (int(state.dataGlobal->CurrentTime) +
539 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
540 : DataGlobalConstants::HoursInDay;
541 0 : EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
542 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay -
543 0 : (TimeStepSys / DataGlobalConstants::HoursInDay);
544 0 : if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
545 : (CurrentFractionalDay > EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
546 0 : newOpMode = DataGenerators::OperatingMode::Standby;
547 0 : PLRShutDown = true;
548 0 : LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
549 0 : PLRforSubtimestepShutDown =
550 0 : (EndingFractionalDay - LastSystemTimeStepFractionalDay) * DataGlobalConstants::HoursInDay / TimeStepSys;
551 : } else { // CurrentFractionalDay < EndingFractionalDay
552 0 : newOpMode = DataGenerators::OperatingMode::CoolDown;
553 : }
554 : } else {
555 0 : newOpMode = DataGenerators::OperatingMode::Standby;
556 : }
557 0 : } else if ((SchedVal != 0.0) && (RunFlag)) {
558 : // was in cool down mode but is now being asked to restart
559 : // probably goes to warm up but could be stuck in cool down or jump to normal
560 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).MandatoryFullCoolDown) {
561 : // is cool down done or not?
562 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay > 0.0) {
563 : // calculate time for end of cool down period
564 0 : CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
565 0 : (int(state.dataGlobal->CurrentTime) +
566 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
567 : DataGlobalConstants::HoursInDay;
568 0 : EndingFractionalDay = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
569 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay -
570 0 : (TimeStepSys / DataGlobalConstants::HoursInDay);
571 0 : if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
572 : (CurrentFractionalDay < EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
573 :
574 0 : newOpMode = DataGenerators::OperatingMode::CoolDown;
575 : } else { // CurrentFractionalDay > EndingFractionalDay
576 : // could go to warm up or normal now
577 0 : PLRShutDown = true;
578 0 : LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
579 0 : PLRforSubtimestepShutDown =
580 0 : (EndingFractionalDay - LastSystemTimeStepFractionalDay) * DataGlobalConstants::HoursInDay / TimeStepSys;
581 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay == 0.0) {
582 0 : newOpMode = DataGenerators::OperatingMode::Normal;
583 : // possible PLR on start up.
584 0 : PLRStartUp = true;
585 0 : PLRforSubtimestepStartUp =
586 0 : ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
587 :
588 0 : } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay > 0.0) {
589 : // is remaining time enough?
590 0 : if ((CurrentFractionalDay - EndingFractionalDay) >
591 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay) {
592 0 : newOpMode = DataGenerators::OperatingMode::Normal;
593 : // possible PLR on start up.
594 0 : PLRStartUp = true;
595 0 : PLRforSubtimestepStartUp =
596 0 : ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
597 : } else {
598 0 : newOpMode = DataGenerators::OperatingMode::WarmUp;
599 : // generator just started so set start time
600 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp =
601 0 : double(state.dataGlobal->DayOfSim) +
602 0 : (int(state.dataGlobal->CurrentTime) +
603 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime) - TimeStepSys))) /
604 : DataGlobalConstants::HoursInDay;
605 : }
606 : }
607 : }
608 : } else {
609 :
610 0 : newOpMode = DataGenerators::OperatingMode::Standby;
611 : }
612 : } else { // not mandetory cool donw
613 : // likely to go into warm up but if no warm up then back to normal
614 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).WarmUpByTimeDelay) {
615 0 : if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay == 0.0) {
616 0 : newOpMode = DataGenerators::OperatingMode::Normal;
617 :
618 0 : } else if (state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay > 0.0) {
619 0 : CurrentFractionalDay = double(state.dataGlobal->DayOfSim) +
620 0 : (int(state.dataGlobal->CurrentTime) +
621 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime)))) /
622 : DataGlobalConstants::HoursInDay;
623 0 : EndingFractionalDay =
624 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastShutDown +
625 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CoolDownDelay / DataGlobalConstants::HoursInDay;
626 0 : if ((std::abs(CurrentFractionalDay - EndingFractionalDay) < 0.000001) ||
627 : (CurrentFractionalDay > EndingFractionalDay)) { // CurrentFractionalDay == EndingFractionalDay
628 0 : newOpMode = DataGenerators::OperatingMode::Normal;
629 : // possible PLR on start up.
630 0 : PLRStartUp = true;
631 0 : LastSystemTimeStepFractionalDay = CurrentFractionalDay - (TimeStepSys / DataGlobalConstants::HoursInDay);
632 0 : PLRforSubtimestepStartUp =
633 0 : ((CurrentFractionalDay - EndingFractionalDay) / (CurrentFractionalDay - LastSystemTimeStepFractionalDay));
634 : } else {
635 0 : newOpMode = DataGenerators::OperatingMode::WarmUp;
636 : // set start up time
637 : // generator just started so set start time
638 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FractionalDayofLastStartUp =
639 0 : double(state.dataGlobal->DayOfSim) +
640 0 : (int(state.dataGlobal->CurrentTime) +
641 0 : (SysTimeElapsed + (state.dataGlobal->CurrentTime - int(state.dataGlobal->CurrentTime) - TimeStepSys))) /
642 : DataGlobalConstants::HoursInDay;
643 : }
644 : }
645 : }
646 : }
647 : }
648 0 : } break;
649 0 : default:
650 0 : break;
651 : } // previous case
652 :
653 13932 : if (PLRforSubtimestepStartUp < 0.0) PLRforSubtimestepStartUp = 0.0;
654 13932 : if (PLRforSubtimestepStartUp > 1.0) PLRforSubtimestepStartUp = 1.0;
655 :
656 13932 : if (PLRforSubtimestepShutDown < 0.0) PLRforSubtimestepShutDown = 0.0;
657 13932 : if (PLRforSubtimestepShutDown > 1.0) PLRforSubtimestepShutDown = 1.0;
658 :
659 13932 : if (newOpMode == DataGenerators::OperatingMode::WarmUp) {
660 0 : switch (GeneratorType) {
661 0 : case GeneratorType::FuelCell: {
662 : // constant power level during start up (modeling artifact)
663 : //? hours or seconds here?
664 0 : Pel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpElectProd /
665 0 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).StartUpTimeDelay;
666 :
667 0 : } break;
668 0 : case GeneratorType::MicroCHP: {
669 0 : Pel = PelInput * PLRforSubtimestepStartUp;
670 0 : } break;
671 0 : default:
672 0 : break;
673 : }
674 : }
675 :
676 13932 : if (newOpMode == DataGenerators::OperatingMode::Normal) {
677 : // correct if switched to normal at sub timestep
678 4335 : Pel *= PLRforSubtimestepStartUp;
679 : // unit may have constraints from transient limits or operating ranges.
680 4335 : if (Pel > state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep) { // powering up
681 4958 : MaxPel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep +
682 2479 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).UpTranLimit * TimeStepSys * DataGlobalConstants::SecInHour;
683 2479 : if (MaxPel < Pel) {
684 0 : Pel = MaxPel;
685 : }
686 1856 : } else if (Pel < state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep) { // powering down
687 1116 : MinPel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelLastTimeStep -
688 558 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).DownTranLimit * TimeStepSys * DataGlobalConstants::SecInHour;
689 558 : if (Pel < MinPel) {
690 0 : Pel = MinPel;
691 : }
692 : }
693 : }
694 :
695 13932 : if (newOpMode == DataGenerators::OperatingMode::CoolDown) {
696 0 : Pel = 0.0; // assumes no power generated during shut down
697 : }
698 :
699 13932 : if (newOpMode == DataGenerators::OperatingMode::Off) {
700 5630 : Pel = 0.0; // assumes no power generated during OFF mode
701 : }
702 :
703 13932 : if (newOpMode == DataGenerators::OperatingMode::Standby) {
704 3967 : Pel = 0.0; // assumes no power generated during standby mode
705 : }
706 :
707 : // Control step 3: adjust for max and min limits on Pel
708 :
709 13932 : if (Pel < state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMin) {
710 0 : Pel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMin;
711 : }
712 13932 : if (Pel > state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMax) {
713 0 : Pel = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).PelMax;
714 : }
715 :
716 : // now do record keeping for amount of time spent in various operating modes
717 13932 : switch (GeneratorType) {
718 13932 : case GeneratorType::MicroCHP: {
719 : // first clear out values
720 13932 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime = 0.0;
721 13932 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime = 0.0;
722 13932 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = 0.0;
723 13932 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime = 0.0;
724 13932 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime = 0.0;
725 13932 : switch (newOpMode) {
726 5630 : case DataGenerators::OperatingMode::Off: {
727 5630 : if (PLRforSubtimestepShutDown == 0.0) {
728 5630 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
729 0 : } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
730 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
731 0 : TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
732 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime =
733 0 : TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
734 : } else {
735 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.OffModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
736 : }
737 5630 : } break;
738 3967 : case DataGenerators::OperatingMode::Standby: {
739 3967 : if (PLRforSubtimestepShutDown == 0.0) {
740 3017 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
741 950 : } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
742 950 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
743 950 : TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
744 950 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime =
745 950 : TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
746 : } else {
747 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.StandyByModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
748 : }
749 3967 : } break;
750 0 : case DataGenerators::OperatingMode::WarmUp: {
751 0 : if (PLRforSubtimestepShutDown == 0.0) {
752 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
753 0 : } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
754 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
755 0 : TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
756 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime =
757 0 : TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
758 : } else {
759 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
760 : }
761 0 : } break;
762 4335 : case DataGenerators::OperatingMode::Normal: {
763 4335 : if (PLRforSubtimestepStartUp == 0.0) {
764 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
765 :
766 4335 : } else if ((PLRforSubtimestepStartUp > 0.0) && (PLRforSubtimestepStartUp < 1.0)) {
767 1223 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WarmUpModeTime =
768 1223 : TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepStartUp);
769 1223 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime =
770 1223 : TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepStartUp);
771 : } else {
772 3112 : if (PLRforSubtimestepShutDown == 0.0) {
773 3112 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
774 0 : } else if ((PLRforSubtimestepShutDown > 0.0) && (PLRforSubtimestepShutDown < 1.0)) {
775 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime =
776 0 : TimeStepSys * DataGlobalConstants::SecInHour * (PLRforSubtimestepShutDown);
777 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime =
778 0 : TimeStepSys * DataGlobalConstants::SecInHour * (1.0 - PLRforSubtimestepShutDown);
779 : } else {
780 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.NormalModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
781 : }
782 : }
783 4335 : } break;
784 0 : case DataGenerators::OperatingMode::CoolDown: {
785 0 : state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.CoolDownModeTime = TimeStepSys * DataGlobalConstants::SecInHour;
786 0 : } break;
787 0 : default:
788 0 : break;
789 : }
790 13932 : } break;
791 0 : case GeneratorType::FuelCell: {
792 : // not yet using this control manager
793 0 : } break;
794 0 : default:
795 0 : break;
796 : }
797 :
798 13932 : ElecLoadProvided = Pel;
799 :
800 13932 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).CurrentOpMode = newOpMode;
801 13932 : OperatingMode = newOpMode;
802 13932 : }
803 :
804 4335 : void ManageGeneratorFuelFlow(EnergyPlusData &state,
805 : GeneratorType const GeneratorType, // type of Generator
806 : [[maybe_unused]] std::string const &GeneratorName, // user specified name of Generator
807 : int const GeneratorNum, // Generator number
808 : [[maybe_unused]] bool const RunFlag, // TRUE when Generator operating
809 : Real64 const FuelFlowRequest, // Generator demand mdot kg/ s
810 : Real64 &FuelFlowProvided, // allowed after constraints kg/s
811 : bool &ConstrainedIncreasingMdot, // true if request was altered because of fuel rate of change up
812 : bool &ConstrainedDecreasingMdot // true if request was altered because of fuel rate of change down
813 : )
814 : {
815 :
816 : // SUBROUTINE INFORMATION:
817 : // AUTHOR B. Griffith
818 : // DATE WRITTEN July 2006
819 : // MODIFIED na
820 : // RE-ENGINEERED na
821 :
822 : // PURPOSE OF THIS SUBROUTINE:
823 : // check if change in fuel flow rate is okay
824 :
825 : // Using/Aliasing
826 : using namespace DataGlobalConstants;
827 4335 : auto &TimeStepSys = state.dataHVACGlobal->TimeStepSys;
828 :
829 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
830 : Real64 MdotFuel;
831 : Real64 MaxMdot;
832 : Real64 MinMdot;
833 4335 : int DynaCntrlNum(0);
834 :
835 4335 : ConstrainedIncreasingMdot = false;
836 4335 : ConstrainedDecreasingMdot = false;
837 4335 : MdotFuel = FuelFlowRequest;
838 :
839 : // get index from GeneratorNum
840 4335 : switch (GeneratorType) {
841 4335 : case GeneratorType::MicroCHP: {
842 4335 : DynaCntrlNum = state.dataCHPElectGen->MicroCHP(GeneratorNum).DynamicsControlID;
843 4335 : } break;
844 0 : default:
845 0 : break;
846 : }
847 :
848 4335 : if (FuelFlowRequest > state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep) { // fuel flow is up
849 2926 : MaxMdot = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep +
850 1463 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).UpTranLimitFuel * TimeStepSys * DataGlobalConstants::SecInHour;
851 1463 : if (MaxMdot < FuelFlowRequest) {
852 0 : MdotFuel = MaxMdot;
853 0 : ConstrainedIncreasingMdot = true;
854 : }
855 :
856 2872 : } else if (FuelFlowRequest < state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep) { // fuel flow is down
857 1124 : MinMdot = state.dataGenerator->GeneratorDynamics(DynaCntrlNum).FuelMdotLastTimestep -
858 562 : state.dataGenerator->GeneratorDynamics(DynaCntrlNum).DownTranLimitFuel * TimeStepSys * DataGlobalConstants::SecInHour;
859 562 : if (FuelFlowRequest < MinMdot) {
860 0 : MdotFuel = MinMdot;
861 0 : ConstrainedDecreasingMdot = true;
862 : }
863 : } else {
864 : // do nothing
865 : }
866 :
867 4335 : FuelFlowProvided = MdotFuel;
868 4335 : }
869 :
870 11629 : Real64 FuncDetermineCWMdotForInternalFlowControl(EnergyPlusData &state,
871 : int const GeneratorNum, // ID of generator
872 : Real64 const Pnetss, // power net steady state
873 : Real64 const TcwIn // temperature of cooling water at inlet
874 : )
875 : {
876 :
877 : // FUNCTION INFORMATION:
878 : // AUTHOR B. Griffith
879 : // DATE WRITTEN Dec 2009
880 : // MODIFIED na
881 : // RE-ENGINEERED B. Griffith, Sept 2010, plant upgrade
882 :
883 : // PURPOSE OF THIS FUNCTION:
884 : // common place to figure flow rates with internal flow control
885 :
886 : // METHODOLOGY EMPLOYED:
887 : // apply contraints imposed by plant according to flow lock, first HVAC iteration etc.
888 :
889 : // REFERENCES:
890 : // na
891 :
892 : // Using/Aliasing
893 : using Curve::CurveValue;
894 : using PlantUtilities::SetComponentFlowRate;
895 :
896 : // Return value
897 : Real64 FuncDetermineCWMdotForInternalFlowControl;
898 :
899 : // Locals
900 : // FUNCTION ARGUMENT DEFINITIONS:
901 :
902 : // FUNCTION PARAMETER DEFINITIONS:
903 : // na
904 :
905 : // INTERFACE BLOCK SPECIFICATIONS:
906 : // na
907 :
908 : // DERIVED TYPE DEFINITIONS:
909 : // na
910 :
911 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
912 : Real64 MdotCW;
913 : int InletNode;
914 : int OutletNode;
915 :
916 11629 : InletNode = state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantInletNodeID;
917 11629 : OutletNode = state.dataCHPElectGen->MicroCHP(GeneratorNum).PlantOutletNodeID;
918 :
919 : // first evaluate curve
920 11629 : MdotCW = CurveValue(state, state.dataCHPElectGen->MicroCHP(GeneratorNum).A42Model.WaterFlowCurveID, Pnetss, TcwIn);
921 :
922 : // now apply constraints
923 11629 : MdotCW = max(0.0, MdotCW);
924 :
925 : // make sure plant can provide, utility call may change flow
926 11629 : if (state.dataCHPElectGen->MicroCHP(GeneratorNum).CWPlantLoc.loopNum > 0) { // protect early calls
927 11629 : SetComponentFlowRate(state, MdotCW, InletNode, OutletNode, state.dataCHPElectGen->MicroCHP(GeneratorNum).CWPlantLoc);
928 : }
929 :
930 11629 : FuncDetermineCWMdotForInternalFlowControl = MdotCW;
931 11629 : return FuncDetermineCWMdotForInternalFlowControl;
932 : }
933 :
934 : } // namespace GeneratorDynamicsManager
935 :
936 2313 : } // namespace EnergyPlus
|