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 <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 : // #include <ObjexxFCL/Fmath.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/Autosizing/Base.hh>
57 : #include <EnergyPlus/BoilerSteam.hh>
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/Data/EnergyPlusData.hh>
60 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
61 : #include <EnergyPlus/DataGlobalConstants.hh>
62 : #include <EnergyPlus/DataHVACGlobals.hh>
63 : #include <EnergyPlus/DataIPShortCuts.hh>
64 : #include <EnergyPlus/DataLoopNode.hh>
65 : #include <EnergyPlus/DataSizing.hh>
66 : #include <EnergyPlus/EMSManager.hh>
67 : #include <EnergyPlus/FluidProperties.hh>
68 : #include <EnergyPlus/GlobalNames.hh>
69 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
70 : #include <EnergyPlus/NodeInputManager.hh>
71 : #include <EnergyPlus/OutputProcessor.hh>
72 : #include <EnergyPlus/OutputReportPredefined.hh>
73 : #include <EnergyPlus/Plant/DataPlant.hh>
74 : #include <EnergyPlus/PlantUtilities.hh>
75 : #include <EnergyPlus/UtilityRoutines.hh>
76 :
77 : namespace EnergyPlus {
78 :
79 : namespace BoilerSteam {
80 :
81 : // Module containing the routines dealing with the Boilers
82 :
83 : // MODULE INFORMATION:
84 : // AUTHOR Rahul Chillar
85 : // DATE WRITTEN Dec 2004
86 :
87 : // PURPOSE OF THIS MODULE:
88 : // Performs steam boiler simulation for plant simulation
89 :
90 8 : BoilerSpecs *BoilerSpecs::factory(EnergyPlusData &state, std::string const &objectName)
91 : {
92 : // Process the input data for boilers if it hasn't been done already
93 8 : if (state.dataBoilerSteam->getSteamBoilerInput) {
94 8 : GetBoilerInput(state);
95 8 : state.dataBoilerSteam->getSteamBoilerInput = false;
96 : }
97 :
98 : // Now look for this particular pipe in the list
99 8 : auto myBoiler = std::find_if(state.dataBoilerSteam->Boiler.begin(),
100 8 : state.dataBoilerSteam->Boiler.end(),
101 8 : [&objectName](const BoilerSpecs &boiler) { return boiler.Name == objectName; });
102 8 : if (myBoiler != state.dataBoilerSteam->Boiler.end()) {
103 8 : return myBoiler;
104 : }
105 :
106 : // If we didn't find it, fatal
107 : ShowFatalError(state, format("LocalBoilerSteamFactory: Error getting inputs for steam boiler named: {}", objectName)); // LCOV_EXCL_LINE
108 : // Shut up the compiler
109 : return nullptr; // LCOV_EXCL_LINE
110 : }
111 :
112 107780 : void BoilerSpecs::simulate(
113 : EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation, bool FirstHVACIteration, Real64 &CurLoad, bool RunFlag)
114 : {
115 107780 : this->initialize(state);
116 107780 : auto &sim_component(DataPlant::CompData::getPlantComponent(state, this->plantLoc));
117 107780 : this->calculate(state, CurLoad, RunFlag, sim_component.FlowCtrl);
118 107780 : this->update(state, CurLoad, RunFlag, FirstHVACIteration);
119 107780 : }
120 :
121 : void
122 40 : BoilerSpecs::getDesignCapacities([[maybe_unused]] EnergyPlusData &state, const PlantLocation &, Real64 &MaxLoad, Real64 &MinLoad, Real64 &OptLoad)
123 : {
124 40 : MinLoad = this->NomCap * this->MinPartLoadRat;
125 40 : MaxLoad = this->NomCap * this->MaxPartLoadRat;
126 40 : OptLoad = this->NomCap * this->OptPartLoadRat;
127 40 : }
128 :
129 8 : void BoilerSpecs::getSizingFactor(Real64 &sizFac)
130 : {
131 8 : sizFac = this->SizFac;
132 8 : }
133 :
134 40 : void BoilerSpecs::onInitLoopEquip(EnergyPlusData &state, const PlantLocation &)
135 : {
136 40 : this->initialize(state);
137 40 : this->autosize(state);
138 40 : }
139 :
140 8 : void GetBoilerInput(EnergyPlusData &state)
141 : {
142 : // SUBROUTINE INFORMATION:
143 : // AUTHOR Rahul Chillar
144 : // DATE WRITTEN Dec 2004
145 :
146 : // PURPOSE OF THIS SUBROUTINE:
147 : // Get all boiler data from input file
148 :
149 : // Locals
150 : static constexpr std::string_view RoutineName("GetBoilerInput: ");
151 : static constexpr std::string_view routineName = "GetBoilerInput";
152 :
153 : // LOCAL VARIABLES
154 : int BoilerNum; // boiler identifier
155 : int NumAlphas; // Number of elements in the alpha array
156 : int NumNums; // Number of elements in the numeric array
157 : int IOStat; // IO Status when calling get input subroutine
158 8 : bool ErrorsFound(false);
159 :
160 8 : state.dataIPShortCut->cCurrentModuleObject = "Boiler:Steam";
161 8 : int numBoilers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
162 :
163 8 : if (numBoilers <= 0) {
164 0 : ShowSevereError(state, format("No {} equipment specified in input file", state.dataIPShortCut->cCurrentModuleObject));
165 0 : ErrorsFound = true;
166 : }
167 :
168 : // See if load distribution manager has already gotten the input
169 8 : if (allocated(state.dataBoilerSteam->Boiler)) {
170 0 : return;
171 : }
172 :
173 : // Boiler will have fuel input to it , that is it !
174 8 : state.dataBoilerSteam->Boiler.allocate(numBoilers);
175 :
176 : // LOAD ARRAYS WITH CURVE FIT Boiler DATA
177 16 : for (BoilerNum = 1; BoilerNum <= numBoilers; ++BoilerNum) {
178 24 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
179 8 : state.dataIPShortCut->cCurrentModuleObject,
180 : BoilerNum,
181 8 : state.dataIPShortCut->cAlphaArgs,
182 : NumAlphas,
183 8 : state.dataIPShortCut->rNumericArgs,
184 : NumNums,
185 : IOStat,
186 : _,
187 : _,
188 8 : state.dataIPShortCut->cAlphaFieldNames,
189 8 : state.dataIPShortCut->cNumericFieldNames);
190 :
191 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
192 8 : GlobalNames::VerifyUniqueBoilerName(state,
193 8 : state.dataIPShortCut->cCurrentModuleObject,
194 8 : state.dataIPShortCut->cAlphaArgs(1),
195 : ErrorsFound,
196 16 : state.dataIPShortCut->cCurrentModuleObject + " Name");
197 8 : auto &thisBoiler = state.dataBoilerSteam->Boiler(BoilerNum);
198 8 : thisBoiler.Name = state.dataIPShortCut->cAlphaArgs(1);
199 :
200 : // Validate fuel type input
201 8 : thisBoiler.FuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, state.dataIPShortCut->cAlphaArgs(2)));
202 :
203 : // INPUTS from the IDF file
204 8 : thisBoiler.BoilerMaxOperPress = state.dataIPShortCut->rNumericArgs(1);
205 8 : if (thisBoiler.BoilerMaxOperPress < 1e5) {
206 0 : ShowWarningMessage(state, format("{}=\"{}\"", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
207 0 : ShowContinueError(state, "Field: Maximum Operation Pressure units are Pa. Verify units.");
208 : }
209 8 : thisBoiler.NomEffic = state.dataIPShortCut->rNumericArgs(2);
210 8 : thisBoiler.TempUpLimitBoilerOut = state.dataIPShortCut->rNumericArgs(3);
211 8 : thisBoiler.NomCap = state.dataIPShortCut->rNumericArgs(4);
212 8 : if (thisBoiler.NomCap == DataSizing::AutoSize) {
213 8 : thisBoiler.NomCapWasAutoSized = true;
214 : }
215 8 : thisBoiler.MinPartLoadRat = state.dataIPShortCut->rNumericArgs(5);
216 8 : thisBoiler.MaxPartLoadRat = state.dataIPShortCut->rNumericArgs(6);
217 8 : thisBoiler.OptPartLoadRat = state.dataIPShortCut->rNumericArgs(7);
218 8 : thisBoiler.FullLoadCoef[0] = state.dataIPShortCut->rNumericArgs(8);
219 8 : thisBoiler.FullLoadCoef[1] = state.dataIPShortCut->rNumericArgs(9);
220 8 : thisBoiler.FullLoadCoef[2] = state.dataIPShortCut->rNumericArgs(10);
221 8 : thisBoiler.SizFac = state.dataIPShortCut->rNumericArgs(11);
222 8 : if (thisBoiler.SizFac <= 0.0) {
223 0 : thisBoiler.SizFac = 1.0;
224 : }
225 :
226 8 : if ((state.dataIPShortCut->rNumericArgs(8) + state.dataIPShortCut->rNumericArgs(9) + state.dataIPShortCut->rNumericArgs(10)) == 0.0) {
227 0 : ShowSevereError(state,
228 0 : format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
229 0 : ShowContinueError(state, " Sum of fuel use curve coefficients = 0.0");
230 0 : ErrorsFound = true;
231 : }
232 :
233 8 : if (state.dataIPShortCut->rNumericArgs(5) < 0.0) {
234 0 : ShowSevereError(state,
235 0 : format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
236 0 : ShowContinueError(state,
237 0 : format("Invalid {}={:.3R}", state.dataIPShortCut->cNumericFieldNames(5), state.dataIPShortCut->rNumericArgs(5)));
238 0 : ErrorsFound = true;
239 : }
240 :
241 8 : if (state.dataIPShortCut->rNumericArgs(3) == 0.0) {
242 0 : ShowSevereError(state,
243 0 : format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
244 0 : ShowContinueError(state,
245 0 : format("Invalid {}={:.3R}", state.dataIPShortCut->cNumericFieldNames(3), state.dataIPShortCut->rNumericArgs(3)));
246 0 : ErrorsFound = true;
247 : }
248 8 : thisBoiler.BoilerInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
249 8 : state.dataIPShortCut->cAlphaArgs(3),
250 : ErrorsFound,
251 : DataLoopNode::ConnectionObjectType::BoilerSteam,
252 8 : state.dataIPShortCut->cAlphaArgs(1),
253 : DataLoopNode::NodeFluidType::Steam,
254 : DataLoopNode::ConnectionType::Inlet,
255 : NodeInputManager::CompFluidStream::Primary,
256 : DataLoopNode::ObjectIsNotParent);
257 16 : thisBoiler.BoilerOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
258 8 : state.dataIPShortCut->cAlphaArgs(4),
259 : ErrorsFound,
260 : DataLoopNode::ConnectionObjectType::BoilerSteam,
261 8 : state.dataIPShortCut->cAlphaArgs(1),
262 : DataLoopNode::NodeFluidType::Steam,
263 : DataLoopNode::ConnectionType::Outlet,
264 : NodeInputManager::CompFluidStream::Primary,
265 : DataLoopNode::ObjectIsNotParent);
266 16 : BranchNodeConnections::TestCompSet(state,
267 8 : state.dataIPShortCut->cCurrentModuleObject,
268 8 : state.dataIPShortCut->cAlphaArgs(1),
269 8 : state.dataIPShortCut->cAlphaArgs(3),
270 8 : state.dataIPShortCut->cAlphaArgs(4),
271 : "Hot Steam Nodes");
272 :
273 8 : thisBoiler.fluid = Fluid::GetSteam(state);
274 8 : if (thisBoiler.fluid == nullptr && BoilerNum == 1) {
275 0 : ShowSevereError(state, "Fluid Properties for STEAM not found.");
276 0 : ErrorsFound = true;
277 : }
278 :
279 8 : if (NumAlphas > 4) {
280 0 : thisBoiler.EndUseSubcategory = state.dataIPShortCut->cAlphaArgs(5);
281 : } else {
282 8 : thisBoiler.EndUseSubcategory = "General";
283 : }
284 : }
285 :
286 8 : if (ErrorsFound) {
287 0 : ShowFatalError(state, format("{}Errors found in processing {} input.", RoutineName, state.dataIPShortCut->cCurrentModuleObject));
288 : }
289 : }
290 :
291 8 : void BoilerSpecs::oneTimeInit(EnergyPlusData &state)
292 : {
293 8 : bool errFlag = false;
294 16 : PlantUtilities::ScanPlantLoopsForObject(
295 8 : state, this->Name, DataPlant::PlantEquipmentType::Boiler_Steam, this->plantLoc, errFlag, _, _, _, _, _);
296 8 : if (errFlag) {
297 0 : ShowFatalError(state, "InitBoiler: Program terminated due to previous condition(s).");
298 : }
299 8 : }
300 :
301 47 : void BoilerSpecs::initEachEnvironment(EnergyPlusData &state)
302 : {
303 : static constexpr std::string_view RoutineName("BoilerSpecs::initEachEnvironment");
304 :
305 47 : int BoilerInletNode = this->BoilerInletNodeNum;
306 :
307 47 : Real64 EnthSteamOutDry = this->fluid->getSatEnthalpy(state, this->TempUpLimitBoilerOut, 1.0, RoutineName);
308 47 : Real64 EnthSteamOutWet = this->fluid->getSatEnthalpy(state, this->TempUpLimitBoilerOut, 0.0, RoutineName);
309 47 : Real64 LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
310 :
311 47 : Real64 CpWater = this->fluid->getSatSpecificHeat(state, this->TempUpLimitBoilerOut, 0.0, RoutineName);
312 :
313 47 : this->DesMassFlowRate =
314 47 : this->NomCap / (LatentEnthSteam + CpWater * (this->TempUpLimitBoilerOut - state.dataLoopNodes->Node(BoilerInletNode).Temp));
315 :
316 47 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesMassFlowRate, this->BoilerInletNodeNum, this->BoilerOutletNodeNum);
317 :
318 47 : this->BoilerPressCheck = 0.0;
319 47 : this->FuelUsed = 0.0;
320 47 : this->BoilerLoad = 0.0;
321 47 : this->BoilerEff = 0.0;
322 47 : this->BoilerOutletTemp = 0.0;
323 :
324 50 : if ((state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint == DataLoopNode::SensedNodeFlagValue) &&
325 3 : (state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo == DataLoopNode::SensedNodeFlagValue)) {
326 3 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
327 3 : if (!this->MissingSetPointErrDone) {
328 1 : ShowWarningError(state, format("Missing temperature setpoint for Boiler:Steam = {}", this->Name));
329 2 : ShowContinueError(state, " A temperature setpoint is needed at the outlet node of the boiler, use a SetpointManager");
330 2 : ShowContinueError(state, " The overall loop setpoint will be assumed for this boiler. The simulation continues ...");
331 1 : this->MissingSetPointErrDone = true;
332 : }
333 : } else {
334 : // need call to EMS to check node
335 0 : bool FatalError = false; // but not really fatal yet, but should be.
336 0 : EMSManager::CheckIfNodeSetPointManagedByEMS(state, this->BoilerOutletNodeNum, HVAC::CtrlVarType::Temp, FatalError);
337 0 : state.dataLoopNodes->NodeSetpointCheck(this->BoilerOutletNodeNum).needsSetpointChecking = false;
338 0 : if (FatalError) {
339 0 : if (!this->MissingSetPointErrDone) {
340 0 : ShowWarningError(state, format("Missing temperature setpoint for LeavingSetpointModulated mode Boiler named {}", this->Name));
341 0 : ShowContinueError(state, " A temperature setpoint is needed at the outlet node of the boiler.");
342 0 : ShowContinueError(state, " Use a Setpoint Manager to establish a setpoint at the boiler outlet node ");
343 0 : ShowContinueError(state, " or use an EMS actuator to establish a setpoint at the boiler outlet node.");
344 0 : ShowContinueError(state, " The overall loop setpoint will be assumed for this boiler. The simulation continues...");
345 0 : this->MissingSetPointErrDone = true;
346 : }
347 : }
348 : }
349 3 : this->UseLoopSetPoint = true; // this is for backward compatibility and could be removed
350 : }
351 47 : }
352 :
353 107820 : void BoilerSpecs::initialize(EnergyPlusData &state) // number of the current electric chiller being simulated
354 : {
355 : // SUBROUTINE INFORMATION:
356 : // AUTHOR Rahul Chillar
357 : // DATE WRITTEN Dec 2004
358 : // RE-ENGINEERED D. Shirey, rework for plant upgrade
359 :
360 : // PURPOSE OF THIS SUBROUTINE:
361 : // This subroutine is for initializations of the Boiler components
362 :
363 : // METHODOLOGY EMPLOYED:
364 : // Uses the status flags to trigger initializations.
365 :
366 : // Init more variables
367 107820 : if (this->myFlag) {
368 8 : this->setupOutputVars(state);
369 8 : this->oneTimeInit(state);
370 8 : this->myFlag = false;
371 : }
372 :
373 107820 : if (state.dataGlobal->BeginEnvrnFlag && this->myEnvrnFlag && (state.dataPlnt->PlantFirstSizesOkayToFinalize)) {
374 47 : this->initEachEnvironment(state);
375 47 : this->myEnvrnFlag = false;
376 : }
377 107820 : if (!state.dataGlobal->BeginEnvrnFlag) {
378 106868 : this->myEnvrnFlag = true;
379 : }
380 :
381 107820 : if (this->UseLoopSetPoint) {
382 : // At some point, need to circle back and get from plant data structure instead of node
383 : // fix for clumsy old input that worked because loop setpoint was spread.
384 : // could be removed with transition, testing , model change, period of being obsolete.
385 5366 : int BoilerOutletNode = this->BoilerOutletNodeNum;
386 5366 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
387 :
388 5366 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint:
389 5366 : state.dataLoopNodes->Node(BoilerOutletNode).TempSetPoint =
390 5366 : state.dataLoopNodes->Node(this->plantLoc.loop->TempSetPointNodeNum).TempSetPoint;
391 5366 : break;
392 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand:
393 0 : state.dataLoopNodes->Node(BoilerOutletNode).TempSetPointLo =
394 0 : state.dataLoopNodes->Node(this->plantLoc.loop->TempSetPointNodeNum).TempSetPointLo;
395 0 : break;
396 0 : default:
397 0 : break;
398 : }
399 : }
400 107820 : }
401 :
402 8 : void BoilerSpecs::setupOutputVars(EnergyPlusData &state)
403 : {
404 8 : std::string_view sFuelType = Constant::eFuelNames[static_cast<int>(this->FuelType)];
405 16 : SetupOutputVariable(state,
406 : "Boiler Heating Rate",
407 : Constant::Units::W,
408 8 : this->BoilerLoad,
409 : OutputProcessor::TimeStepType::System,
410 : OutputProcessor::StoreType::Average,
411 8 : this->Name);
412 :
413 16 : SetupOutputVariable(state,
414 : "Boiler Heating Energy",
415 : Constant::Units::J,
416 8 : this->BoilerEnergy,
417 : OutputProcessor::TimeStepType::System,
418 : OutputProcessor::StoreType::Sum,
419 8 : this->Name,
420 : Constant::eResource::EnergyTransfer,
421 : OutputProcessor::Group::Plant,
422 : OutputProcessor::EndUseCat::Boilers);
423 24 : SetupOutputVariable(state,
424 16 : format("Boiler {} Rate", sFuelType),
425 : Constant::Units::W,
426 8 : this->FuelUsed,
427 : OutputProcessor::TimeStepType::System,
428 : OutputProcessor::StoreType::Average,
429 8 : this->Name);
430 24 : SetupOutputVariable(state,
431 16 : format("Boiler {} Energy", sFuelType),
432 : Constant::Units::J,
433 8 : this->FuelConsumed,
434 : OutputProcessor::TimeStepType::System,
435 : OutputProcessor::StoreType::Sum,
436 8 : this->Name,
437 8 : Constant::eFuel2eResource[(int)this->FuelType],
438 : OutputProcessor::Group::Plant,
439 : OutputProcessor::EndUseCat::Heating,
440 : this->EndUseSubcategory);
441 16 : SetupOutputVariable(state,
442 : "Boiler Steam Efficiency",
443 : Constant::Units::None,
444 8 : this->BoilerEff,
445 : OutputProcessor::TimeStepType::System,
446 : OutputProcessor::StoreType::Average,
447 8 : this->Name);
448 16 : SetupOutputVariable(state,
449 : "Boiler Steam Inlet Temperature",
450 : Constant::Units::C,
451 8 : this->BoilerInletTemp,
452 : OutputProcessor::TimeStepType::System,
453 : OutputProcessor::StoreType::Average,
454 8 : this->Name);
455 16 : SetupOutputVariable(state,
456 : "Boiler Steam Outlet Temperature",
457 : Constant::Units::C,
458 8 : this->BoilerOutletTemp,
459 : OutputProcessor::TimeStepType::System,
460 : OutputProcessor::StoreType::Average,
461 8 : this->Name);
462 16 : SetupOutputVariable(state,
463 : "Boiler Steam Mass Flow Rate",
464 : Constant::Units::kg_s,
465 8 : this->BoilerMassFlowRate,
466 : OutputProcessor::TimeStepType::System,
467 : OutputProcessor::StoreType::Average,
468 8 : this->Name);
469 8 : }
470 :
471 40 : void BoilerSpecs::autosize(EnergyPlusData &state)
472 : {
473 :
474 : // SUBROUTINE INFORMATION:
475 : // AUTHOR Rahul Chillar
476 : // DATE WRITTEN Dec 2004
477 : // MODIFIED November 2013 Daeho Kang, add component sizing table entries
478 :
479 : // PURPOSE OF THIS SUBROUTINE:
480 : // This subroutine is for sizing Boiler Components for which capacities and flow rates
481 : // have not been specified in the input.
482 :
483 : // METHODOLOGY EMPLOYED:
484 : // Obtains Steam flow rate from the plant sizing array. Calculates nominal capacity from
485 : // the hot water flow rate and the hot water loop design delta T.
486 :
487 : // SUBROUTINE PARAMETER DEFINITIONS:
488 : static constexpr std::string_view RoutineName("SizeBoiler");
489 :
490 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
491 40 : bool ErrorsFound(false); // If errors detected in input
492 40 : Real64 tmpNomCap = this->NomCap;
493 40 : int PltSizNum = this->plantLoc.loop->PlantSizNum;
494 :
495 40 : if (PltSizNum > 0) {
496 40 : if (state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
497 32 : Real64 SizingTemp = this->TempUpLimitBoilerOut;
498 32 : Real64 SteamDensity = this->fluid->getSatDensity(state, SizingTemp, 1.0, RoutineName);
499 32 : Real64 EnthSteamOutDry = this->fluid->getSatEnthalpy(state, SizingTemp, 1.0, RoutineName);
500 32 : Real64 EnthSteamOutWet = this->fluid->getSatEnthalpy(state, SizingTemp, 0.0, RoutineName);
501 32 : Real64 LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
502 32 : Real64 CpWater = this->fluid->getSatSpecificHeat(state, SizingTemp, 0.0, RoutineName);
503 32 : tmpNomCap = (CpWater * SteamDensity * this->SizFac * state.dataSize->PlantSizData(PltSizNum).DeltaT *
504 32 : state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate +
505 32 : state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate * SteamDensity * LatentEnthSteam);
506 : } else {
507 8 : if (this->NomCapWasAutoSized) {
508 8 : tmpNomCap = 0.0;
509 : }
510 : }
511 40 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
512 8 : if (this->NomCapWasAutoSized) {
513 8 : this->NomCap = tmpNomCap;
514 8 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
515 8 : BaseSizer::reportSizerOutput(state, "Boiler:Steam", this->Name, "Design Size Nominal Capacity [W]", tmpNomCap);
516 :
517 : // Std 229 Boilers new report table
518 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerType, this->Name, "Boiler:Steam");
519 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRefCap, this->Name, this->NomCap);
520 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRefEff, this->Name, this->NomEffic);
521 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRatedCap, this->Name, this->NomCap);
522 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRatedEff, this->Name, this->NomEffic);
523 16 : OutputReportPredefined::PreDefTableEntry(state,
524 8 : state.dataOutRptPredefined->pdchBoilerPlantloopName,
525 : this->Name,
526 16 : (this->plantLoc.loop != nullptr) ? this->plantLoc.loop->Name : "N/A");
527 16 : OutputReportPredefined::PreDefTableEntry(state,
528 8 : state.dataOutRptPredefined->pdchBoilerPlantloopBranchName,
529 : this->Name,
530 16 : (this->plantLoc.branch != nullptr) ? this->plantLoc.branch->Name : "N/A");
531 16 : OutputReportPredefined::PreDefTableEntry(
532 8 : state, state.dataOutRptPredefined->pdchBoilerMinPLR, this->Name, this->MinPartLoadRat);
533 16 : OutputReportPredefined::PreDefTableEntry(state,
534 8 : state.dataOutRptPredefined->pdchBoilerFuelType,
535 : this->Name,
536 8 : Constant::eFuelNames[static_cast<int>(this->FuelType)]);
537 16 : OutputReportPredefined::PreDefTableEntry(
538 8 : state, state.dataOutRptPredefined->pdchBoilerParaElecLoad, this->Name, "Not Applicable");
539 : }
540 8 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
541 0 : BaseSizer::reportSizerOutput(state, "Boiler:Steam", this->Name, "Initial Design Size Nominal Capacity [W]", tmpNomCap);
542 : }
543 : } else { // Hard-sized with sizing data
544 0 : if (this->NomCap > 0.0 && tmpNomCap > 0.0) {
545 0 : Real64 NomCapUser = this->NomCap;
546 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
547 0 : BaseSizer::reportSizerOutput(state,
548 : "Boiler:Steam",
549 : this->Name,
550 : "Design Size Nominal Capacity [W]",
551 : tmpNomCap,
552 : "User-Specified Nominal Capacity [W]",
553 : NomCapUser);
554 :
555 : // Std 229 Boilers new report table
556 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerType, this->Name, "Boiler:Steam");
557 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRefCap, this->Name, this->NomCap);
558 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRefEff, this->Name, this->NomEffic);
559 0 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRatedCap, this->Name, this->NomCap);
560 0 : OutputReportPredefined::PreDefTableEntry(
561 0 : state, state.dataOutRptPredefined->pdchBoilerRatedEff, this->Name, this->NomEffic);
562 0 : OutputReportPredefined::PreDefTableEntry(state,
563 0 : state.dataOutRptPredefined->pdchBoilerPlantloopName,
564 : this->Name,
565 0 : (this->plantLoc.loop != nullptr) ? this->plantLoc.loop->Name : "N/A");
566 0 : OutputReportPredefined::PreDefTableEntry(state,
567 0 : state.dataOutRptPredefined->pdchBoilerPlantloopBranchName,
568 : this->Name,
569 0 : (this->plantLoc.branch != nullptr) ? this->plantLoc.branch->Name : "N/A");
570 0 : OutputReportPredefined::PreDefTableEntry(
571 0 : state, state.dataOutRptPredefined->pdchBoilerMinPLR, this->Name, this->MinPartLoadRat);
572 0 : OutputReportPredefined::PreDefTableEntry(state,
573 0 : state.dataOutRptPredefined->pdchBoilerFuelType,
574 : this->Name,
575 0 : Constant::eFuelNames[static_cast<int>(this->FuelType)]);
576 0 : OutputReportPredefined::PreDefTableEntry(
577 0 : state, state.dataOutRptPredefined->pdchBoilerParaElecLoad, this->Name, "Not Applicable");
578 :
579 0 : if (state.dataGlobal->DisplayExtraWarnings) {
580 0 : if ((std::abs(tmpNomCap - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
581 0 : ShowMessage(state, format("SizePump: Potential issue with equipment sizing for {}", this->Name));
582 0 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", NomCapUser));
583 0 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpNomCap));
584 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
585 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
586 : }
587 : }
588 : }
589 : }
590 : }
591 : }
592 : } else {
593 0 : if (this->NomCapWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
594 0 : ShowSevereError(state, "Autosizing of Boiler nominal capacity requires a loop Sizing:Plant object");
595 0 : ShowContinueError(state, format("Occurs in Boiler:Steam object={}", this->Name));
596 0 : ErrorsFound = true;
597 : }
598 0 : if (!this->NomCapWasAutoSized && this->NomCap > 0.0 && state.dataPlnt->PlantFinalSizesOkayToReport) {
599 0 : BaseSizer::reportSizerOutput(state, "Boiler:Steam", this->Name, "User-Specified Nominal Capacity [W]", this->NomCap);
600 : }
601 : }
602 :
603 40 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
604 : // create predefined report
605 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, this->Name, "Boiler:Steam");
606 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, this->Name, this->NomEffic);
607 8 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->Name, this->NomCap);
608 : }
609 :
610 40 : if (ErrorsFound) {
611 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
612 : }
613 40 : }
614 :
615 107780 : void BoilerSpecs::calculate(EnergyPlusData &state,
616 : Real64 &MyLoad, // W - hot water demand to be met by boiler
617 : bool const RunFlag, // TRUE if boiler operating
618 : DataBranchAirLoopPlant::ControlType const EquipFlowCtrl // Flow control mode for the equipment
619 : )
620 : {
621 : // SUBROUTINE INFORMATION:
622 : // AUTHOR Rahul Chillar
623 : // DATE WRITTEN Dec 2004
624 :
625 : // PURPOSE OF THIS SUBROUTINE:
626 : // This subroutine calculates the boiler fuel consumption and the associated
627 : // hot water demand met by the boiler
628 :
629 : // METHODOLOGY EMPLOYED:
630 : // The model is based on a single combustion efficiency (=1 for electric)
631 : // and a second order polynomial fit of performance data to obtain part
632 : // load performance
633 :
634 : // SUBROUTINE PARAMETER DEFINITIONS:
635 : static constexpr std::string_view RoutineName("CalcBoilerModel");
636 :
637 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
638 107780 : Real64 BoilerDeltaTemp(0.0); // C - boiler inlet to outlet temperature difference
639 : Real64 CpWater; // Heat capacity of condensed steam
640 :
641 : // Loading the variables derived type in to local variables
642 107780 : this->BoilerLoad = 0.0;
643 107780 : this->BoilerMassFlowRate = 0.0;
644 :
645 107780 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
646 107780 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
647 107780 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint;
648 107780 : } break;
649 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
650 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo;
651 0 : } break;
652 0 : default:
653 0 : break;
654 : }
655 : // If the specified load is 0.0 or the boiler should not run then we leave this subroutine.Before leaving
656 : // if the component control is SERIESACTIVE we set the component flow to inlet flow so that flow resolver
657 : // will not shut down the branch
658 107780 : if (MyLoad <= 0.0 || !RunFlag) {
659 74576 : if (EquipFlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
660 0 : this->BoilerMassFlowRate = state.dataLoopNodes->Node(this->BoilerInletNodeNum).MassFlowRate;
661 : }
662 74576 : return;
663 : }
664 :
665 : // Set the current load equal to the boiler load
666 33204 : this->BoilerLoad = MyLoad;
667 :
668 33204 : this->BoilerPressCheck = this->fluid->getSatPressure(state, this->BoilerOutletTemp, RoutineName);
669 :
670 33204 : if ((this->BoilerPressCheck) > this->BoilerMaxOperPress) {
671 0 : if (this->PressErrIndex == 0) {
672 0 : ShowSevereError(state, format("Boiler:Steam=\"{}\", Saturation Pressure is greater than Maximum Operating Pressure,", this->Name));
673 0 : ShowContinueError(state, "Lower Input Temperature");
674 0 : ShowContinueError(state, format("Steam temperature=[{:.2R}] C", this->BoilerOutletTemp));
675 0 : ShowContinueError(state, format("Refrigerant Saturation Pressure =[{:.0R}] Pa", this->BoilerPressCheck));
676 : }
677 0 : ShowRecurringSevereErrorAtEnd(state,
678 0 : "Boiler:Steam=\"" + this->Name +
679 : "\", Saturation Pressure is greater than Maximum Operating Pressure..continues",
680 0 : this->PressErrIndex,
681 0 : this->BoilerPressCheck,
682 0 : this->BoilerPressCheck,
683 : _,
684 : "[Pa]",
685 : "[Pa]");
686 : }
687 :
688 33204 : CpWater = this->fluid->getSatSpecificHeat(state, state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp, 0.0, RoutineName);
689 :
690 33204 : if (this->plantLoc.side->FlowLock == DataPlant::FlowLock::Unlocked) { // TODO: Components shouldn't check FlowLock
691 : // Calculate the flow for the boiler
692 :
693 16602 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
694 16602 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
695 16602 : BoilerDeltaTemp =
696 16602 : state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint - state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
697 16602 : } break;
698 0 : default: { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
699 0 : BoilerDeltaTemp =
700 0 : state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo - state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
701 0 : } break;
702 : }
703 16602 : this->BoilerOutletTemp = BoilerDeltaTemp + state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
704 :
705 16602 : Real64 const EnthSteamOutDry = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 1.0, RoutineName);
706 16602 : Real64 const EnthSteamOutWet = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 0.0, RoutineName);
707 16602 : Real64 const LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
708 16602 : this->BoilerMassFlowRate = this->BoilerLoad / (LatentEnthSteam + (CpWater * BoilerDeltaTemp));
709 :
710 16602 : PlantUtilities::SetComponentFlowRate(
711 16602 : state, this->BoilerMassFlowRate, this->BoilerInletNodeNum, this->BoilerOutletNodeNum, this->plantLoc);
712 :
713 : } else { // If FlowLock is True
714 : // Set the boiler flow rate from inlet node and then check performance
715 16602 : this->BoilerMassFlowRate = state.dataLoopNodes->Node(this->BoilerInletNodeNum).MassFlowRate;
716 : // Assume that it can meet the setpoint
717 16602 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
718 16602 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
719 16602 : BoilerDeltaTemp =
720 16602 : state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint - state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
721 16602 : } break;
722 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
723 0 : BoilerDeltaTemp =
724 0 : state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo - state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
725 0 : } break;
726 0 : default:
727 0 : break;
728 : }
729 : // If boiler outlet temp is already greater than setpoint than it does not need to operate this iteration
730 16602 : if (BoilerDeltaTemp < 0.0) {
731 2 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
732 2 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
733 2 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint;
734 2 : } break;
735 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
736 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo;
737 0 : } break;
738 0 : default:
739 0 : break;
740 : }
741 2 : Real64 const EnthSteamOutDry = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 1.0, RoutineName);
742 2 : Real64 const EnthSteamOutWet = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 0.0, RoutineName);
743 2 : Real64 const LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
744 2 : this->BoilerLoad = (this->BoilerMassFlowRate * LatentEnthSteam);
745 :
746 : } else {
747 16600 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
748 16600 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
749 16600 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint;
750 16600 : } break;
751 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
752 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo;
753 0 : } break;
754 0 : default:
755 0 : break;
756 : }
757 :
758 16600 : Real64 const EnthSteamOutDry = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 1.0, RoutineName);
759 16600 : Real64 const EnthSteamOutWet = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 0.0, RoutineName);
760 16600 : Real64 const LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
761 16600 : this->BoilerLoad =
762 16600 : std::abs(this->BoilerMassFlowRate * LatentEnthSteam) + std::abs(this->BoilerMassFlowRate * CpWater * BoilerDeltaTemp);
763 : }
764 :
765 : // If load exceeds the distributed load set to the distributed load
766 16602 : if (this->BoilerLoad > MyLoad) {
767 2 : this->BoilerLoad = MyLoad;
768 :
769 : // Reset later , here just for calculating latent heat
770 2 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
771 2 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
772 2 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint;
773 2 : } break;
774 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
775 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo;
776 0 : } break;
777 0 : default:
778 0 : break;
779 : }
780 :
781 2 : Real64 const EnthSteamOutDry = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 1.0, RoutineName);
782 2 : Real64 const EnthSteamOutWet = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 0.0, RoutineName);
783 2 : Real64 const LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
784 2 : BoilerDeltaTemp = this->BoilerOutletTemp - state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
785 2 : this->BoilerMassFlowRate = this->BoilerLoad / (LatentEnthSteam + CpWater * BoilerDeltaTemp);
786 :
787 2 : PlantUtilities::SetComponentFlowRate(
788 2 : state, this->BoilerMassFlowRate, this->BoilerInletNodeNum, this->BoilerOutletNodeNum, this->plantLoc);
789 : }
790 :
791 : // Checks Boiler Load on the basis of the machine limits.
792 16602 : if (this->BoilerLoad > this->NomCap) {
793 0 : if (this->BoilerMassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
794 0 : this->BoilerLoad = this->NomCap;
795 :
796 0 : Real64 const EnthSteamOutDry = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 1.0, RoutineName);
797 0 : Real64 const EnthSteamOutWet = this->fluid->getSatEnthalpy(state, this->BoilerOutletTemp, 0.0, RoutineName);
798 0 : Real64 const LatentEnthSteam = EnthSteamOutDry - EnthSteamOutWet;
799 0 : BoilerDeltaTemp = this->BoilerOutletTemp - state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
800 0 : this->BoilerMassFlowRate = this->BoilerLoad / (LatentEnthSteam + CpWater * BoilerDeltaTemp);
801 :
802 0 : PlantUtilities::SetComponentFlowRate(
803 0 : state, this->BoilerMassFlowRate, this->BoilerInletNodeNum, this->BoilerOutletNodeNum, this->plantLoc);
804 : } else {
805 0 : this->BoilerLoad = 0.0;
806 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
807 : }
808 : }
809 :
810 : } // End of the FlowLock If block
811 :
812 : // Limit BoilerOutletTemp. If > max temp, trip boiler.
813 33204 : if (this->BoilerOutletTemp > this->TempUpLimitBoilerOut) {
814 0 : this->BoilerLoad = 0.0;
815 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(this->BoilerInletNodeNum).Temp;
816 : // Does BoilerMassFlowRate need to be set????
817 : }
818 :
819 33204 : Real64 OperPLR = this->BoilerLoad / this->NomCap;
820 33204 : OperPLR = min(OperPLR, this->MaxPartLoadRat);
821 33204 : OperPLR = max(OperPLR, this->MinPartLoadRat);
822 33204 : Real64 TheorFuelUse = this->BoilerLoad / this->NomEffic;
823 :
824 : // Calculate fuel used
825 33204 : this->FuelUsed = TheorFuelUse / (this->FullLoadCoef[0] + this->FullLoadCoef[1] * OperPLR + this->FullLoadCoef[2] * pow_2(OperPLR));
826 : // Calculate boiler efficiency
827 33204 : this->BoilerEff = this->BoilerLoad / this->FuelUsed;
828 : }
829 :
830 : // Beginning of Record Keeping subroutines for the BOILER:SIMPLE Module
831 :
832 107780 : void BoilerSpecs::update(EnergyPlusData &state,
833 : Real64 const MyLoad, // boiler operating load
834 : bool const RunFlag, // boiler on when TRUE
835 : [[maybe_unused]] bool const FirstHVACIteration // TRUE if First iteration of simulation
836 : )
837 : {
838 : // SUBROUTINE INFORMATION:
839 : // AUTHOR Rahul Chillar
840 : // DATE WRITTEN Dec 2004
841 :
842 : // PURPOSE OF THIS SUBROUTINE:
843 : // Boiler simulation reporting
844 :
845 107780 : Real64 ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
846 107780 : int BoilerInletNode = this->BoilerInletNodeNum;
847 107780 : int BoilerOutletNode = this->BoilerOutletNodeNum;
848 :
849 107780 : if (MyLoad <= 0.0 || !RunFlag) {
850 : // set node temperatures
851 74576 : PlantUtilities::SafeCopyPlantNode(state, BoilerInletNode, BoilerOutletNode);
852 74576 : state.dataLoopNodes->Node(BoilerOutletNode).Temp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
853 74576 : this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
854 74576 : this->BoilerLoad = 0.0;
855 74576 : this->FuelUsed = 0.0;
856 74576 : this->BoilerEff = 0.0;
857 74576 : state.dataLoopNodes->Node(BoilerInletNode).Press = this->BoilerPressCheck;
858 74576 : state.dataLoopNodes->Node(BoilerOutletNode).Press = state.dataLoopNodes->Node(BoilerInletNode).Press;
859 74576 : state.dataLoopNodes->Node(BoilerInletNode).Quality = 0.0;
860 74576 : state.dataLoopNodes->Node(BoilerOutletNode).Quality = state.dataLoopNodes->Node(BoilerInletNode).Quality;
861 :
862 : } else {
863 : // set node temperatures
864 33204 : PlantUtilities::SafeCopyPlantNode(state, BoilerInletNode, BoilerOutletNode);
865 33204 : state.dataLoopNodes->Node(BoilerOutletNode).Temp = this->BoilerOutletTemp;
866 33204 : state.dataLoopNodes->Node(BoilerInletNode).Press = this->BoilerPressCheck; //???
867 33204 : state.dataLoopNodes->Node(BoilerOutletNode).Press = state.dataLoopNodes->Node(BoilerInletNode).Press;
868 33204 : state.dataLoopNodes->Node(BoilerOutletNode).Quality = 1.0; // Model assumes saturated steam exiting the boiler
869 : }
870 :
871 107780 : this->BoilerInletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
872 107780 : this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerOutletNode).MassFlowRate;
873 107780 : this->BoilerEnergy = this->BoilerLoad * ReportingConstant;
874 107780 : this->FuelConsumed = this->FuelUsed * ReportingConstant;
875 107780 : }
876 :
877 : } // namespace BoilerSteam
878 :
879 : } // namespace EnergyPlus
|