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/Autosizing/Base.hh>
57 : #include <EnergyPlus/Boilers.hh>
58 : #include <EnergyPlus/BranchNodeConnections.hh>
59 : #include <EnergyPlus/CurveManager.hh>
60 : #include <EnergyPlus/Data/EnergyPlusData.hh>
61 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
62 : #include <EnergyPlus/DataGlobalConstants.hh>
63 : #include <EnergyPlus/DataHVACGlobals.hh>
64 : #include <EnergyPlus/DataIPShortCuts.hh>
65 : #include <EnergyPlus/DataLoopNode.hh>
66 : #include <EnergyPlus/DataSizing.hh>
67 : #include <EnergyPlus/EMSManager.hh>
68 : #include <EnergyPlus/FaultsManager.hh>
69 : #include <EnergyPlus/FluidProperties.hh>
70 : #include <EnergyPlus/GlobalNames.hh>
71 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
72 : #include <EnergyPlus/NodeInputManager.hh>
73 : #include <EnergyPlus/OutputProcessor.hh>
74 : #include <EnergyPlus/OutputReportPredefined.hh>
75 : #include <EnergyPlus/Plant/DataPlant.hh>
76 : #include <EnergyPlus/Plant/PlantLocation.hh>
77 : #include <EnergyPlus/PlantUtilities.hh>
78 : #include <EnergyPlus/UtilityRoutines.hh>
79 :
80 : namespace EnergyPlus::Boilers {
81 :
82 : // Module containing the routines dealing with the Boilers
83 :
84 : // MODULE INFORMATION:
85 : // AUTHOR Dan Fisher, Taecheol Kim
86 : // DATE WRITTEN 1998, 2000
87 : // MODIFIED na
88 : // RE-ENGINEERED na
89 :
90 : // PURPOSE OF THIS MODULE:
91 : // Perform boiler simulation for plant simulation
92 :
93 : // METHODOLOGY EMPLOYED:
94 : // The BLAST/DOE-2 empirical model based on mfg. data
95 :
96 198 : PlantComponent *BoilerSpecs::factory(EnergyPlusData &state, std::string const &objectName)
97 : {
98 : // Process the input data for boilers if it hasn't been done already
99 198 : if (state.dataBoilers->getBoilerInputFlag) {
100 192 : GetBoilerInput(state);
101 192 : state.dataBoilers->getBoilerInputFlag = false;
102 : }
103 : // Now look for this particular pipe in the list
104 204 : for (auto &boiler : state.dataBoilers->Boiler) {
105 204 : if (boiler.Name == objectName) {
106 198 : return &boiler;
107 : }
108 : }
109 : // If we didn't find it, fatal
110 : ShowFatalError(state, "LocalBoilerFactory: Error getting inputs for boiler named: " + objectName); // LCOV_EXCL_LINE
111 : // Shut up the compiler
112 : return nullptr; // LCOV_EXCL_LINE
113 : }
114 :
115 6817372 : void BoilerSpecs::simulate(EnergyPlusData &state,
116 : [[maybe_unused]] const PlantLocation &calledFromLocation,
117 : [[maybe_unused]] bool const FirstHVACIteration,
118 : Real64 &CurLoad,
119 : bool const RunFlag)
120 : {
121 6817372 : auto &sim_component(DataPlant::CompData::getPlantComponent(state, this->plantLoc));
122 6817372 : this->InitBoiler(state);
123 6817372 : this->CalcBoilerModel(state, CurLoad, RunFlag, sim_component.FlowCtrl);
124 6817372 : this->UpdateBoilerRecords(state, CurLoad, RunFlag);
125 6817372 : }
126 :
127 1012 : void BoilerSpecs::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
128 : [[maybe_unused]] const PlantLocation &calledFromLocation,
129 : Real64 &MaxLoad,
130 : Real64 &MinLoad,
131 : Real64 &OptLoad)
132 : {
133 1012 : MinLoad = this->NomCap * this->MinPartLoadRat;
134 1012 : MaxLoad = this->NomCap * this->MaxPartLoadRat;
135 1012 : OptLoad = this->NomCap * this->OptPartLoadRat;
136 1012 : }
137 :
138 198 : void BoilerSpecs::getSizingFactor(Real64 &SizFactor)
139 : {
140 198 : SizFactor = this->SizFac;
141 198 : }
142 :
143 1012 : void BoilerSpecs::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
144 : {
145 1012 : this->InitBoiler(state);
146 1012 : this->SizeBoiler(state);
147 1012 : }
148 :
149 192 : void GetBoilerInput(EnergyPlusData &state)
150 : {
151 : // SUBROUTINE INFORMATION:
152 : // AUTHOR: Dan Fisher
153 : // DATE WRITTEN: April 1998
154 : // MODIFIED: R. Raustad - FSEC, June 2008: added boiler efficiency curve object
155 : // RE-ENGINEERED: na
156 :
157 : // PURPOSE OF THIS SUBROUTINE:
158 : // get all boiler data from input file
159 :
160 : // METHODOLOGY EMPLOYED:
161 : // standard EnergyPlus input retrieval using input Processor
162 :
163 : // Locals
164 : static constexpr std::string_view RoutineName("GetBoilerInput: ");
165 :
166 : // LOCAL VARIABLES
167 192 : bool ErrorsFound(false); // Flag to show errors were found during GetInput
168 :
169 : // GET NUMBER OF ALL EQUIPMENT
170 192 : state.dataIPShortCut->cCurrentModuleObject = "Boiler:HotWater";
171 192 : int numBoilers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
172 :
173 192 : if (numBoilers <= 0) {
174 0 : ShowSevereError(state, "No " + state.dataIPShortCut->cCurrentModuleObject + " Equipment specified in input file");
175 0 : ErrorsFound = true;
176 : }
177 :
178 : // See if load distribution manager has already gotten the input
179 192 : if (allocated(state.dataBoilers->Boiler)) return;
180 :
181 192 : state.dataBoilers->Boiler.allocate(numBoilers);
182 :
183 : // LOAD ARRAYS WITH CURVE FIT Boiler DATA
184 :
185 390 : for (int BoilerNum = 1; BoilerNum <= numBoilers; ++BoilerNum) {
186 : int NumAlphas; // Number of elements in the alpha array
187 : int NumNums; // Number of elements in the numeric array
188 : int IOStat; // IO Status when calling get input subroutine
189 1584 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
190 198 : state.dataIPShortCut->cCurrentModuleObject,
191 : BoilerNum,
192 198 : state.dataIPShortCut->cAlphaArgs,
193 : NumAlphas,
194 198 : state.dataIPShortCut->rNumericArgs,
195 : NumNums,
196 : IOStat,
197 198 : state.dataIPShortCut->lNumericFieldBlanks,
198 198 : state.dataIPShortCut->lAlphaFieldBlanks,
199 198 : state.dataIPShortCut->cAlphaFieldNames,
200 198 : state.dataIPShortCut->cNumericFieldNames);
201 198 : UtilityRoutines::IsNameEmpty(state, state.dataIPShortCut->cAlphaArgs(1), state.dataIPShortCut->cCurrentModuleObject, ErrorsFound);
202 : // ErrorsFound will be set to True if problem was found, left untouched otherwise
203 198 : GlobalNames::VerifyUniqueBoilerName(state,
204 198 : state.dataIPShortCut->cCurrentModuleObject,
205 198 : state.dataIPShortCut->cAlphaArgs(1),
206 : ErrorsFound,
207 396 : state.dataIPShortCut->cCurrentModuleObject + " Name");
208 198 : auto &thisBoiler = state.dataBoilers->Boiler(BoilerNum);
209 198 : thisBoiler.Name = state.dataIPShortCut->cAlphaArgs(1);
210 198 : thisBoiler.Type = DataPlant::PlantEquipmentType::Boiler_Simple;
211 :
212 : // Validate fuel type input
213 198 : bool FuelTypeError(false);
214 396 : UtilityRoutines::ValidateFuelTypeWithAssignResourceTypeNum(
215 198 : state.dataIPShortCut->cAlphaArgs(2), thisBoiler.BoilerFuelTypeForOutputVariable, thisBoiler.FuelType, FuelTypeError);
216 198 : if (FuelTypeError) {
217 0 : ShowSevereError(
218 0 : state, fmt::format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
219 0 : ShowContinueError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + '=' + state.dataIPShortCut->cAlphaArgs(2));
220 : // Set to Electric to avoid errors when setting up output variables
221 0 : thisBoiler.BoilerFuelTypeForOutputVariable = "Electricity";
222 0 : thisBoiler.FuelType = DataGlobalConstants::AssignResourceTypeNum("ELECTRICITY");
223 0 : ErrorsFound = true;
224 0 : FuelTypeError = false;
225 : }
226 :
227 198 : thisBoiler.NomCap = state.dataIPShortCut->rNumericArgs(1);
228 198 : if (state.dataIPShortCut->rNumericArgs(1) == 0.0) {
229 0 : ShowSevereError(
230 0 : state, fmt::format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
231 0 : ShowContinueError(state, format("Invalid {}={:.2R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
232 0 : ShowContinueError(state, "..." + state.dataIPShortCut->cNumericFieldNames(1) + " must be greater than 0.0");
233 0 : ErrorsFound = true;
234 : }
235 198 : if (thisBoiler.NomCap == DataSizing::AutoSize) {
236 175 : thisBoiler.NomCapWasAutoSized = true;
237 : }
238 :
239 198 : thisBoiler.NomEffic = state.dataIPShortCut->rNumericArgs(2);
240 198 : if (state.dataIPShortCut->rNumericArgs(2) == 0.0) {
241 0 : ShowSevereError(
242 0 : state, fmt::format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
243 0 : ShowContinueError(state, format("Invalid {}={:.3R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
244 0 : ShowSevereError(state, "..." + state.dataIPShortCut->cNumericFieldNames(2) + " must be greater than 0.0");
245 0 : ErrorsFound = true;
246 : }
247 :
248 198 : if (state.dataIPShortCut->cAlphaArgs(3) == "ENTERINGBOILER") {
249 1 : thisBoiler.CurveTempMode = TempMode::ENTERINGBOILERTEMP;
250 197 : } else if (state.dataIPShortCut->cAlphaArgs(3) == "LEAVINGBOILER") {
251 195 : thisBoiler.CurveTempMode = TempMode::LEAVINGBOILERTEMP;
252 : } else {
253 2 : thisBoiler.CurveTempMode = TempMode::NOTSET;
254 : }
255 :
256 198 : thisBoiler.EfficiencyCurvePtr = Curve::GetCurveIndex(state, state.dataIPShortCut->cAlphaArgs(4));
257 198 : if (thisBoiler.EfficiencyCurvePtr > 0) {
258 332 : ErrorsFound |= Curve::CheckCurveDims(state,
259 : thisBoiler.EfficiencyCurvePtr, // Curve index
260 : {1, 2}, // Valid dimensions
261 : RoutineName, // Routine name
262 166 : state.dataIPShortCut->cCurrentModuleObject, // Object Type
263 : thisBoiler.Name, // Object Name
264 166 : state.dataIPShortCut->cAlphaFieldNames(4)); // Field Name
265 :
266 : // if curve uses temperature, make sure water temp mode has been set
267 166 : if (state.dataCurveManager->PerfCurve(thisBoiler.EfficiencyCurvePtr).numDims == 2) { // curve uses water temperature
268 1 : if (thisBoiler.CurveTempMode == TempMode::NOTSET) { // throw error
269 0 : if (!state.dataIPShortCut->lAlphaFieldBlanks(3)) {
270 0 : ShowSevereError(
271 : state,
272 0 : fmt::format("{}{}=\"{}\"", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
273 0 : ShowContinueError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + '=' + state.dataIPShortCut->cAlphaArgs(3));
274 0 : ShowContinueError(
275 : state,
276 0 : format("boilers.Boiler using curve type of {} must specify {}",
277 0 : Curve::objectNames[static_cast<int>(state.dataCurveManager->PerfCurve(thisBoiler.EfficiencyCurvePtr).curveType)],
278 0 : state.dataIPShortCut->cAlphaFieldNames(3)));
279 0 : ShowContinueError(state, "Available choices are EnteringBoiler or LeavingBoiler");
280 : } else {
281 0 : ShowSevereError(
282 : state,
283 0 : fmt::format("{}{}=\"{}\"", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
284 0 : ShowContinueError(state, "Field " + state.dataIPShortCut->cAlphaFieldNames(3) + " is blank");
285 0 : ShowContinueError(
286 : state,
287 0 : format("boilers.Boiler using curve type of {} must specify either EnteringBoiler or LeavingBoiler",
288 0 : Curve::objectNames[static_cast<int>(state.dataCurveManager->PerfCurve(thisBoiler.EfficiencyCurvePtr).curveType)]));
289 : }
290 0 : ErrorsFound = true;
291 : }
292 : }
293 :
294 32 : } else if (!state.dataIPShortCut->lAlphaFieldBlanks(4)) {
295 0 : ShowSevereError(state,
296 0 : fmt::format("{}{}=\"{}\"", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
297 0 : ShowContinueError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(4) + '=' + state.dataIPShortCut->cAlphaArgs(4));
298 0 : ShowSevereError(state, "..." + state.dataIPShortCut->cAlphaFieldNames(4) + " not found.");
299 0 : ErrorsFound = true;
300 : }
301 198 : thisBoiler.VolFlowRate = state.dataIPShortCut->rNumericArgs(3);
302 198 : if (thisBoiler.VolFlowRate == DataSizing::AutoSize) {
303 190 : thisBoiler.VolFlowRateWasAutoSized = true;
304 : }
305 198 : thisBoiler.MinPartLoadRat = state.dataIPShortCut->rNumericArgs(4);
306 198 : thisBoiler.MaxPartLoadRat = state.dataIPShortCut->rNumericArgs(5);
307 198 : thisBoiler.OptPartLoadRat = state.dataIPShortCut->rNumericArgs(6);
308 :
309 198 : thisBoiler.TempUpLimitBoilerOut = state.dataIPShortCut->rNumericArgs(7);
310 : // default to 99.9C if upper temperature limit is left blank.
311 198 : if (thisBoiler.TempUpLimitBoilerOut <= 0.0) {
312 0 : thisBoiler.TempUpLimitBoilerOut = 99.9;
313 : }
314 :
315 198 : thisBoiler.ParasiticElecLoad = state.dataIPShortCut->rNumericArgs(8);
316 198 : thisBoiler.SizFac = state.dataIPShortCut->rNumericArgs(9);
317 198 : if (thisBoiler.SizFac == 0.0) thisBoiler.SizFac = 1.0;
318 :
319 198 : thisBoiler.BoilerInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
320 198 : state.dataIPShortCut->cAlphaArgs(5),
321 : ErrorsFound,
322 : DataLoopNode::ConnectionObjectType::BoilerHotWater,
323 198 : state.dataIPShortCut->cAlphaArgs(1),
324 : DataLoopNode::NodeFluidType::Water,
325 : DataLoopNode::ConnectionType::Inlet,
326 : NodeInputManager::CompFluidStream::Primary,
327 198 : DataLoopNode::ObjectIsNotParent);
328 198 : thisBoiler.BoilerOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
329 198 : state.dataIPShortCut->cAlphaArgs(6),
330 : ErrorsFound,
331 : DataLoopNode::ConnectionObjectType::BoilerHotWater,
332 198 : state.dataIPShortCut->cAlphaArgs(1),
333 : DataLoopNode::NodeFluidType::Water,
334 : DataLoopNode::ConnectionType::Outlet,
335 : NodeInputManager::CompFluidStream::Primary,
336 198 : DataLoopNode::ObjectIsNotParent);
337 594 : BranchNodeConnections::TestCompSet(state,
338 198 : state.dataIPShortCut->cCurrentModuleObject,
339 198 : state.dataIPShortCut->cAlphaArgs(1),
340 198 : state.dataIPShortCut->cAlphaArgs(5),
341 198 : state.dataIPShortCut->cAlphaArgs(6),
342 : "Hot Water Nodes");
343 :
344 198 : if (state.dataIPShortCut->cAlphaArgs(7) == "CONSTANTFLOW") {
345 36 : thisBoiler.FlowMode = DataPlant::FlowMode::Constant;
346 162 : } else if (state.dataIPShortCut->cAlphaArgs(7) == "LEAVINGSETPOINTMODULATED") {
347 162 : thisBoiler.FlowMode = DataPlant::FlowMode::LeavingSetpointModulated;
348 0 : } else if (state.dataIPShortCut->cAlphaArgs(7) == "NOTMODULATED") {
349 0 : thisBoiler.FlowMode = DataPlant::FlowMode::NotModulated;
350 : } else {
351 0 : ShowSevereError(state,
352 0 : fmt::format("{}{}=\"{}\"", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
353 0 : ShowContinueError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(7) + '=' + state.dataIPShortCut->cAlphaArgs(7));
354 0 : ShowContinueError(state, "Available choices are ConstantFlow, NotModulated, or LeavingSetpointModulated");
355 0 : ShowContinueError(state, "Flow mode NotModulated is assumed and the simulation continues.");
356 : // We will assume variable flow if not specified
357 0 : thisBoiler.FlowMode = DataPlant::FlowMode::NotModulated;
358 : }
359 :
360 198 : if (NumAlphas > 7) {
361 1 : thisBoiler.EndUseSubcategory = state.dataIPShortCut->cAlphaArgs(8);
362 : } else {
363 197 : thisBoiler.EndUseSubcategory = "Boiler"; // leave this as "boiler" instead of "general" like other end use subcategories since
364 : // it appears this way in existing output files.
365 : }
366 : }
367 :
368 192 : if (ErrorsFound) {
369 0 : ShowFatalError(state, format("{}{}", RoutineName, "Errors found in processing " + state.dataIPShortCut->cCurrentModuleObject + " input."));
370 : }
371 : }
372 :
373 198 : void BoilerSpecs::SetupOutputVars(EnergyPlusData &state)
374 : {
375 396 : SetupOutputVariable(state,
376 : "Boiler Heating Rate",
377 : OutputProcessor::Unit::W,
378 : this->BoilerLoad,
379 : OutputProcessor::SOVTimeStepType::System,
380 : OutputProcessor::SOVStoreType::Average,
381 198 : this->Name);
382 396 : SetupOutputVariable(state,
383 : "Boiler Heating Energy",
384 : OutputProcessor::Unit::J,
385 : this->BoilerEnergy,
386 : OutputProcessor::SOVTimeStepType::System,
387 : OutputProcessor::SOVStoreType::Summed,
388 : this->Name,
389 : _,
390 : "ENERGYTRANSFER",
391 : "BOILERS",
392 : _,
393 198 : "Plant");
394 594 : SetupOutputVariable(state,
395 396 : "Boiler " + this->BoilerFuelTypeForOutputVariable + " Rate",
396 : OutputProcessor::Unit::W,
397 : this->FuelUsed,
398 : OutputProcessor::SOVTimeStepType::System,
399 : OutputProcessor::SOVStoreType::Average,
400 : this->Name);
401 594 : SetupOutputVariable(state,
402 396 : "Boiler " + this->BoilerFuelTypeForOutputVariable + " Energy",
403 : OutputProcessor::Unit::J,
404 : this->FuelConsumed,
405 : OutputProcessor::SOVTimeStepType::System,
406 : OutputProcessor::SOVStoreType::Summed,
407 : this->Name,
408 : _,
409 : this->BoilerFuelTypeForOutputVariable,
410 : "Heating",
411 : this->EndUseSubcategory,
412 : "Plant");
413 396 : SetupOutputVariable(state,
414 : "Boiler Inlet Temperature",
415 : OutputProcessor::Unit::C,
416 : this->BoilerInletTemp,
417 : OutputProcessor::SOVTimeStepType::System,
418 : OutputProcessor::SOVStoreType::Average,
419 198 : this->Name);
420 396 : SetupOutputVariable(state,
421 : "Boiler Outlet Temperature",
422 : OutputProcessor::Unit::C,
423 : this->BoilerOutletTemp,
424 : OutputProcessor::SOVTimeStepType::System,
425 : OutputProcessor::SOVStoreType::Average,
426 198 : this->Name);
427 396 : SetupOutputVariable(state,
428 : "Boiler Mass Flow Rate",
429 : OutputProcessor::Unit::kg_s,
430 : this->BoilerMassFlowRate,
431 : OutputProcessor::SOVTimeStepType::System,
432 : OutputProcessor::SOVStoreType::Average,
433 198 : this->Name);
434 396 : SetupOutputVariable(state,
435 : "Boiler Ancillary Electricity Rate",
436 : OutputProcessor::Unit::W,
437 : this->ParasiticElecPower,
438 : OutputProcessor::SOVTimeStepType::System,
439 : OutputProcessor::SOVStoreType::Average,
440 198 : this->Name);
441 396 : SetupOutputVariable(state,
442 : "Boiler Ancillary Electricity Energy",
443 : OutputProcessor::Unit::J,
444 : this->ParasiticElecConsumption,
445 : OutputProcessor::SOVTimeStepType::System,
446 : OutputProcessor::SOVStoreType::Summed,
447 : this->Name,
448 : _,
449 : "ELECTRICITY",
450 : "Heating",
451 : "Boiler Parasitic",
452 198 : "Plant");
453 396 : SetupOutputVariable(state,
454 : "Boiler Part Load Ratio",
455 : OutputProcessor::Unit::None,
456 : this->BoilerPLR,
457 : OutputProcessor::SOVTimeStepType::System,
458 : OutputProcessor::SOVStoreType::Average,
459 198 : this->Name);
460 396 : SetupOutputVariable(state,
461 : "Boiler Efficiency",
462 : OutputProcessor::Unit::None,
463 : this->BoilerEff,
464 : OutputProcessor::SOVTimeStepType::System,
465 : OutputProcessor::SOVStoreType::Average,
466 198 : this->Name);
467 198 : if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
468 28 : SetupEMSInternalVariable(state, "Boiler Nominal Capacity", this->Name, "[W]", this->NomCap);
469 : }
470 198 : }
471 :
472 198 : void BoilerSpecs::oneTimeInit(EnergyPlusData &state)
473 : {
474 : // Locate the boilers on the plant loops for later usage
475 198 : bool errFlag = false;
476 198 : PlantUtilities::ScanPlantLoopsForObject(
477 : state, this->Name, DataPlant::PlantEquipmentType::Boiler_Simple, this->plantLoc, errFlag, _, this->TempUpLimitBoilerOut, _, _, _);
478 198 : if (errFlag) {
479 0 : ShowFatalError(state, "InitBoiler: Program terminated due to previous condition(s).");
480 : }
481 :
482 198 : if ((this->FlowMode == DataPlant::FlowMode::LeavingSetpointModulated) || (this->FlowMode == DataPlant::FlowMode::Constant)) {
483 : // reset flow priority
484 198 : DataPlant::CompData::getPlantComponent(state, this->plantLoc).FlowPriority = DataPlant::LoopFlowStatus::NeedyIfLoopOn;
485 : }
486 198 : }
487 :
488 1251 : void BoilerSpecs::initEachEnvironment(EnergyPlusData &state)
489 : {
490 : static constexpr std::string_view RoutineName("BoilerSpecs::initEachEnvironment");
491 2502 : Real64 const rho = FluidProperties::GetDensityGlycol(state,
492 1251 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
493 : DataGlobalConstants::HWInitConvTemp,
494 1251 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
495 1251 : RoutineName);
496 1251 : this->DesMassFlowRate = this->VolFlowRate * rho;
497 :
498 1251 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesMassFlowRate, this->BoilerInletNodeNum, this->BoilerOutletNodeNum);
499 :
500 1251 : if (this->FlowMode == DataPlant::FlowMode::LeavingSetpointModulated) { // check if setpoint on outlet node
501 1050 : if ((state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint == DataLoopNode::SensedNodeFlagValue) &&
502 8 : (state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo == DataLoopNode::SensedNodeFlagValue)) {
503 8 : if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
504 0 : if (!this->ModulatedFlowErrDone) {
505 0 : ShowWarningError(state, "Missing temperature setpoint for LeavingSetpointModulated mode Boiler named " + this->Name);
506 0 : ShowContinueError(
507 : state, " A temperature setpoint is needed at the outlet node of a boiler in variable flow mode, use a SetpointManager");
508 0 : ShowContinueError(state, " The overall loop setpoint will be assumed for Boiler. The simulation continues ... ");
509 0 : this->ModulatedFlowErrDone = true;
510 : }
511 : } else {
512 : // need call to EMS to check node
513 8 : bool FatalError = false; // but not really fatal yet, but should be.
514 8 : EMSManager::CheckIfNodeSetPointManagedByEMS(
515 : state, this->BoilerOutletNodeNum, EMSManager::SPControlType::TemperatureSetPoint, FatalError);
516 8 : state.dataLoopNodes->NodeSetpointCheck(this->BoilerOutletNodeNum).needsSetpointChecking = false;
517 8 : if (FatalError) {
518 0 : if (!this->ModulatedFlowErrDone) {
519 0 : ShowWarningError(state, "Missing temperature setpoint for LeavingSetpointModulated mode Boiler named " + this->Name);
520 0 : ShowContinueError(state, " A temperature setpoint is needed at the outlet node of a boiler in variable flow mode");
521 0 : ShowContinueError(state, " use a Setpoint Manager to establish a setpoint at the boiler outlet node ");
522 0 : ShowContinueError(state, " or use an EMS actuator to establish a setpoint at the boiler outlet node ");
523 0 : ShowContinueError(state, " The overall loop setpoint will be assumed for Boiler. The simulation continues ... ");
524 0 : this->ModulatedFlowErrDone = true;
525 : }
526 : }
527 : }
528 8 : this->ModulatedFlowSetToLoop = true; // this is for backward compatibility and could be removed
529 : }
530 : }
531 1251 : }
532 :
533 6818384 : void BoilerSpecs::InitBoiler(EnergyPlusData &state) // number of the current boiler being simulated
534 : {
535 :
536 : // SUBROUTINE INFORMATION:
537 : // AUTHOR Fred Buhl
538 : // DATE WRITTEN April 2002
539 : // MODIFIED na
540 : // RE-ENGINEERED Brent Griffith, rework for plant upgrade
541 :
542 : // PURPOSE OF THIS SUBROUTINE:
543 : // This subroutine is for initializations of the Boiler components
544 :
545 : // METHODOLOGY EMPLOYED:
546 : // Uses the status flags to trigger initializations.
547 :
548 : // Init more variables
549 6818384 : if (this->MyFlag) {
550 198 : this->SetupOutputVars(state);
551 198 : this->oneTimeInit(state);
552 198 : this->MyFlag = false;
553 : }
554 :
555 6818384 : if (this->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag && (state.dataPlnt->PlantFirstSizesOkayToFinalize)) {
556 1251 : this->initEachEnvironment(state);
557 1251 : this->MyEnvrnFlag = false;
558 : }
559 :
560 6818384 : if (!state.dataGlobal->BeginEnvrnFlag) {
561 6778906 : this->MyEnvrnFlag = true;
562 : }
563 :
564 : // every iteration inits. (most in calc routine)
565 :
566 6818384 : if ((this->FlowMode == DataPlant::FlowMode::LeavingSetpointModulated) && this->ModulatedFlowSetToLoop) {
567 : // fix for clumsy old input that worked because loop setpoint was spread.
568 : // could be removed with transition, testing , model change, period of being obsolete.
569 130382 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
570 130382 : state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint =
571 130382 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(this->plantLoc.loopNum).TempSetPointNodeNum).TempSetPoint;
572 : } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
573 0 : state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo =
574 0 : state.dataLoopNodes->Node(state.dataPlnt->PlantLoop(this->plantLoc.loopNum).TempSetPointNodeNum).TempSetPointLo;
575 : }
576 : }
577 6818384 : }
578 :
579 1012 : void BoilerSpecs::SizeBoiler(EnergyPlusData &state)
580 : {
581 :
582 : // SUBROUTINE INFORMATION:
583 : // AUTHOR Fred Buhl
584 : // DATE WRITTEN April 2002
585 : // MODIFIED November 2013 Daeho Kang, add component sizing table entries
586 : // RE-ENGINEERED na
587 :
588 : // PURPOSE OF THIS SUBROUTINE:
589 : // This subroutine is for sizing Boiler Components for which capacities and flow rates
590 : // have not been specified in the input.
591 :
592 : // METHODOLOGY EMPLOYED:
593 : // Obtains hot water flow rate from the plant sizing array. Calculates nominal capacity from
594 : // the hot water flow rate and the hot water loop design delta T.
595 :
596 : // SUBROUTINE PARAMETER DEFINITIONS:
597 : static constexpr std::string_view RoutineName("SizeBoiler");
598 :
599 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
600 1012 : bool ErrorsFound(false); // If errors detected in input
601 :
602 : // grab some initial values for capacity and flow rate
603 1012 : Real64 tmpNomCap = this->NomCap; // local nominal capacity cooling power
604 1012 : Real64 tmpBoilerVolFlowRate = this->VolFlowRate; // local boiler design volume flow rate
605 :
606 1012 : int const PltSizNum = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).PlantSizNum; // Plant Sizing index corresponding to CurLoopNum
607 :
608 1012 : if (PltSizNum > 0) {
609 972 : if (state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate >= DataHVACGlobals::SmallWaterVolFlow) {
610 :
611 1564 : Real64 const rho = FluidProperties::GetDensityGlycol(state,
612 782 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
613 : DataGlobalConstants::HWInitConvTemp,
614 782 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
615 782 : RoutineName);
616 1564 : Real64 const Cp = FluidProperties::GetSpecificHeatGlycol(state,
617 782 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
618 : DataGlobalConstants::HWInitConvTemp,
619 782 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
620 782 : RoutineName);
621 782 : tmpNomCap =
622 782 : Cp * rho * this->SizFac * state.dataSize->PlantSizData(PltSizNum).DeltaT * state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate;
623 : } else {
624 190 : if (this->NomCapWasAutoSized) tmpNomCap = 0.0;
625 : }
626 972 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
627 212 : if (this->NomCapWasAutoSized) {
628 197 : this->NomCap = tmpNomCap;
629 197 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
630 175 : BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "Design Size Nominal Capacity [W]", tmpNomCap);
631 : }
632 197 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
633 11 : BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "Initial Design Size Nominal Capacity [W]", tmpNomCap);
634 : }
635 : } else { // Hard-sized with sizing data
636 15 : if (this->NomCap > 0.0 && tmpNomCap > 0.0) {
637 15 : Real64 const NomCapUser = this->NomCap; // Hardsized nominal capacity for reporting
638 15 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
639 45 : BaseSizer::reportSizerOutput(state,
640 : "Boiler:HotWater",
641 : this->Name,
642 : "Design Size Nominal Capacity [W]",
643 : tmpNomCap,
644 : "User-Specified Nominal Capacity [W]",
645 30 : NomCapUser);
646 15 : if (state.dataGlobal->DisplayExtraWarnings) {
647 0 : if ((std::abs(tmpNomCap - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
648 0 : ShowMessage(state, "SizeBoilerHotWater: Potential issue with equipment sizing for " + this->Name);
649 0 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", NomCapUser));
650 0 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpNomCap));
651 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
652 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
653 : }
654 : }
655 : }
656 : }
657 : }
658 : }
659 : } else {
660 40 : if (this->NomCapWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
661 0 : ShowSevereError(state, "Autosizing of Boiler nominal capacity requires a loop Sizing:Plant object");
662 0 : ShowContinueError(state, "Occurs in Boiler object=" + this->Name);
663 0 : ErrorsFound = true;
664 : }
665 40 : if (!this->NomCapWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport && (this->NomCap > 0.0)) { // Hard-sized with no sizing data
666 8 : BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "User-Specified Nominal Capacity [W]", this->NomCap);
667 : }
668 : }
669 :
670 1012 : if (PltSizNum > 0) {
671 972 : if (state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate >= DataHVACGlobals::SmallWaterVolFlow) {
672 782 : tmpBoilerVolFlowRate = state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate * this->SizFac;
673 : } else {
674 190 : if (this->VolFlowRateWasAutoSized) tmpBoilerVolFlowRate = 0.0;
675 : }
676 972 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
677 212 : if (this->VolFlowRateWasAutoSized) {
678 212 : this->VolFlowRate = tmpBoilerVolFlowRate;
679 212 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
680 570 : BaseSizer::reportSizerOutput(
681 380 : state, "Boiler:HotWater", this->Name, "Design Size Design Water Flow Rate [m3/s]", tmpBoilerVolFlowRate);
682 : }
683 212 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
684 33 : BaseSizer::reportSizerOutput(
685 22 : state, "Boiler:HotWater", this->Name, "Initial Design Size Design Water Flow Rate [m3/s]", tmpBoilerVolFlowRate);
686 : }
687 : } else {
688 0 : if (this->VolFlowRate > 0.0 && tmpBoilerVolFlowRate > 0.0) {
689 0 : Real64 VolFlowRateUser = this->VolFlowRate; // Hardsized volume flow for reporting
690 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
691 0 : BaseSizer::reportSizerOutput(state,
692 : "Boiler:HotWater",
693 : this->Name,
694 : "Design Size Design Water Flow Rate [m3/s]",
695 : tmpBoilerVolFlowRate,
696 : "User-Specified Design Water Flow Rate [m3/s]",
697 0 : VolFlowRateUser);
698 0 : if (state.dataGlobal->DisplayExtraWarnings) {
699 0 : if ((std::abs(tmpBoilerVolFlowRate - VolFlowRateUser) / VolFlowRateUser) > state.dataSize->AutoVsHardSizingThreshold) {
700 0 : ShowMessage(state, "SizeBoilerHotWater: Potential issue with equipment sizing for " + this->Name);
701 0 : ShowContinueError(state, format("User-Specified Design Water Flow Rate of {:.2R} [m3/s]", VolFlowRateUser));
702 0 : ShowContinueError(state,
703 0 : format("differs from Design Size Design Water Flow Rate of {:.2R} [m3/s]", tmpBoilerVolFlowRate));
704 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
705 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
706 : }
707 : }
708 : }
709 0 : tmpBoilerVolFlowRate = VolFlowRateUser;
710 : }
711 : }
712 : }
713 : } else {
714 40 : if (this->VolFlowRateWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
715 0 : ShowSevereError(state, "Autosizing of Boiler design flow rate requires a loop Sizing:Plant object");
716 0 : ShowContinueError(state, "Occurs in Boiler object=" + this->Name);
717 0 : ErrorsFound = true;
718 : }
719 48 : if (!this->VolFlowRateWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport &&
720 8 : (this->VolFlowRate > 0.0)) { // Hard-sized with no sizing data
721 8 : BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "User-Specified Design Water Flow Rate [m3/s]", this->VolFlowRate);
722 : }
723 : }
724 :
725 1012 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->BoilerInletNodeNum, tmpBoilerVolFlowRate);
726 :
727 1012 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
728 : // create predefined report
729 396 : std::string const equipName = this->Name;
730 198 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, equipName, "Boiler:HotWater");
731 198 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, equipName, this->NomEffic);
732 198 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, equipName, this->NomCap);
733 : }
734 :
735 1012 : if (ErrorsFound) {
736 0 : ShowFatalError(state, "Preceding sizing errors cause program termination");
737 : }
738 1012 : }
739 :
740 6817372 : void BoilerSpecs::CalcBoilerModel(EnergyPlusData &state,
741 : Real64 const MyLoad, // W - hot water demand to be met by boiler
742 : bool const RunFlag, // TRUE if boiler operating
743 : DataBranchAirLoopPlant::ControlType const EquipFlowCtrl // Flow control mode for the equipment
744 : )
745 : {
746 : // SUBROUTINE INFORMATION:
747 : // AUTHOR Dan Fisher
748 : // DATE WRITTEN April 1999
749 : // MODIFIED Taecheol Kim,May 2000
750 : // Jun. 2008, R. Raustad, FSEC. Added boiler efficiency curve object
751 : // Aug. 2011, B. Griffith, NREL. Added switch for temperature to use in curve
752 : // Nov. 2016, R. Zhang, LBNL. Applied the boiler fouling fault model
753 : // RE-ENGINEERED na
754 :
755 : // PURPOSE OF THIS SUBROUTINE:
756 : // This subroutine calculates the boiler fuel consumption and the associated
757 : // hot water demand met by the boiler
758 :
759 : // METHODOLOGY EMPLOYED:
760 : // The model is based on a single combustion efficiency (=1 for electric)
761 : // and a second order polynomial fit of performance data to obtain part
762 : // load performance
763 :
764 : // SUBROUTINE PARAMETER DEFINITIONS:
765 : static constexpr std::string_view RoutineName("CalcBoilerModel");
766 :
767 : // clean up some operating conditions, may not be necessary
768 6817372 : this->BoilerLoad = 0.0;
769 6817372 : this->ParasiticElecPower = 0.0;
770 6817372 : this->BoilerMassFlowRate = 0.0;
771 :
772 6817372 : int const BoilerInletNode = this->BoilerInletNodeNum;
773 6817372 : int const BoilerOutletNode = this->BoilerOutletNodeNum;
774 6817372 : Real64 BoilerNomCap = this->NomCap; // W - boiler nominal capacity
775 6817372 : Real64 const BoilerMaxPLR = this->MaxPartLoadRat; // boiler maximum part load ratio
776 6817372 : Real64 const BoilerMinPLR = this->MinPartLoadRat; // boiler minimum part load ratio
777 6817372 : Real64 BoilerNomEff = this->NomEffic; // boiler efficiency
778 6817372 : Real64 const TempUpLimitBout = this->TempUpLimitBoilerOut; // C - boiler high temperature limit
779 6817372 : Real64 const BoilerMassFlowRateMax = this->DesMassFlowRate; // Max Design Boiler Mass Flow Rate converted from Volume Flow Rate
780 :
781 20452116 : Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state,
782 6817372 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
783 6817372 : state.dataLoopNodes->Node(BoilerInletNode).Temp,
784 6817372 : state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
785 6817372 : RoutineName);
786 :
787 : // If the specified load is 0.0 or the boiler should not run then we leave this subroutine. Before leaving
788 : // if the component control is SERIESACTIVE we set the component flow to inlet flow so that flow resolver
789 : // will not shut down the branch
790 6817372 : if (MyLoad <= 0.0 || !RunFlag) {
791 3597124 : if (EquipFlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive)
792 13994 : this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerInletNode).MassFlowRate;
793 3597124 : return;
794 : }
795 :
796 : // If there is a fault of boiler fouling
797 3227088 : if (this->FaultyBoilerFoulingFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
798 6840 : (!state.dataGlobal->KickOffSimulation)) {
799 6840 : int FaultIndex = this->FaultyBoilerFoulingIndex;
800 6840 : Real64 NomCap_ff = BoilerNomCap;
801 6840 : Real64 BoilerNomEff_ff = BoilerNomEff;
802 :
803 : // calculate the Faulty Boiler Fouling Factor using fault information
804 6840 : this->FaultyBoilerFoulingFactor = state.dataFaultsMgr->FaultsBoilerFouling(FaultIndex).CalFoulingFactor(state);
805 :
806 : // update the boiler nominal capacity at faulty cases
807 6840 : BoilerNomCap = NomCap_ff * this->FaultyBoilerFoulingFactor;
808 6840 : BoilerNomEff = BoilerNomEff_ff * this->FaultyBoilerFoulingFactor;
809 : }
810 :
811 : // Set the current load equal to the boiler load
812 3220248 : this->BoilerLoad = MyLoad;
813 :
814 : // Initialize the delta temperature to zero
815 : Real64 BoilerDeltaTemp; // C - boiler inlet to outlet temperature difference, set in all necessary code paths so no initialization required
816 :
817 3220248 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopSide(this->plantLoc.loopSideNum).FlowLock == DataPlant::FlowLock::Unlocked) {
818 : // Either set the flow to the Constant value or calculate the flow for the variable volume
819 1610124 : if ((this->FlowMode == DataPlant::FlowMode::Constant) || (this->FlowMode == DataPlant::FlowMode::NotModulated)) {
820 : // Then find the flow rate and outlet temp
821 166473 : this->BoilerMassFlowRate = BoilerMassFlowRateMax;
822 166473 : PlantUtilities::SetComponentFlowRate(state, this->BoilerMassFlowRate, BoilerInletNode, BoilerOutletNode, this->plantLoc);
823 :
824 166473 : if ((this->BoilerMassFlowRate != 0.0) && (MyLoad > 0.0)) {
825 166473 : BoilerDeltaTemp = this->BoilerLoad / this->BoilerMassFlowRate / Cp;
826 : } else {
827 0 : BoilerDeltaTemp = 0.0;
828 : }
829 166473 : this->BoilerOutletTemp = BoilerDeltaTemp + state.dataLoopNodes->Node(BoilerInletNode).Temp;
830 :
831 1443651 : } else if (this->FlowMode == DataPlant::FlowMode::LeavingSetpointModulated) {
832 : // Calculate the Delta Temp from the inlet temp to the boiler outlet setpoint
833 : // Then find the flow rate and outlet temp
834 :
835 1443651 : if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
836 1406773 : BoilerDeltaTemp = state.dataLoopNodes->Node(BoilerOutletNode).TempSetPoint - state.dataLoopNodes->Node(BoilerInletNode).Temp;
837 : } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
838 36878 : BoilerDeltaTemp = state.dataLoopNodes->Node(BoilerOutletNode).TempSetPointLo - state.dataLoopNodes->Node(BoilerInletNode).Temp;
839 : }
840 :
841 1443651 : this->BoilerOutletTemp = BoilerDeltaTemp + state.dataLoopNodes->Node(BoilerInletNode).Temp;
842 :
843 1443651 : if ((BoilerDeltaTemp > 0.0) && (this->BoilerLoad > 0.0)) {
844 1443651 : this->BoilerMassFlowRate = this->BoilerLoad / Cp / BoilerDeltaTemp;
845 1443651 : this->BoilerMassFlowRate = min(BoilerMassFlowRateMax, this->BoilerMassFlowRate);
846 : } else {
847 0 : this->BoilerMassFlowRate = 0.0;
848 : }
849 1443651 : PlantUtilities::SetComponentFlowRate(state, this->BoilerMassFlowRate, BoilerInletNode, BoilerOutletNode, this->plantLoc);
850 :
851 : } // End of Constant/Variable Flow If Block
852 :
853 : } else { // If FlowLock is True
854 : // Set the boiler flow rate from inlet node and then check performance
855 1610124 : this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerInletNode).MassFlowRate;
856 :
857 1610124 : if ((MyLoad > 0.0) && (this->BoilerMassFlowRate > 0.0)) { // this boiler has a heat load
858 1610124 : this->BoilerLoad = MyLoad;
859 1610124 : if (this->BoilerLoad > BoilerNomCap * BoilerMaxPLR) this->BoilerLoad = BoilerNomCap * BoilerMaxPLR;
860 1610124 : if (this->BoilerLoad < BoilerNomCap * BoilerMinPLR) this->BoilerLoad = BoilerNomCap * BoilerMinPLR;
861 1610124 : this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp + this->BoilerLoad / (this->BoilerMassFlowRate * Cp);
862 : } else {
863 0 : this->BoilerLoad = 0.0;
864 0 : this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
865 : }
866 : }
867 :
868 : // Limit BoilerOutletTemp. If > max temp, trip boiler off
869 3220248 : if (this->BoilerOutletTemp > TempUpLimitBout) {
870 76 : this->BoilerLoad = 0.0;
871 76 : this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
872 : }
873 3220248 : this->BoilerPLR = this->BoilerLoad / BoilerNomCap; // operating part load ratio
874 3220248 : this->BoilerPLR = min(this->BoilerPLR, BoilerMaxPLR);
875 3220248 : this->BoilerPLR = max(this->BoilerPLR, BoilerMinPLR);
876 :
877 : // calculate theoretical fuel use based on nominal thermal efficiency
878 3220248 : Real64 const TheorFuelUse = this->BoilerLoad / BoilerNomEff; // Theoretical (stoichiometric) fuel use
879 3220248 : Real64 EffCurveOutput = 1.0; // Output of boiler efficiency curve
880 :
881 : // calculate normalized efficiency based on curve object type
882 3220248 : if (this->EfficiencyCurvePtr > 0) {
883 2510130 : if (state.dataCurveManager->PerfCurve(this->EfficiencyCurvePtr).numDims == 2) {
884 8508 : if (this->CurveTempMode == TempMode::ENTERINGBOILERTEMP) {
885 8508 : EffCurveOutput = Curve::CurveValue(state, this->EfficiencyCurvePtr, this->BoilerPLR, state.dataLoopNodes->Node(BoilerInletNode).Temp);
886 0 : } else if (this->CurveTempMode == TempMode::LEAVINGBOILERTEMP) {
887 0 : EffCurveOutput = Curve::CurveValue(state, this->EfficiencyCurvePtr, this->BoilerPLR, this->BoilerOutletTemp);
888 : }
889 : } else {
890 2501622 : EffCurveOutput = Curve::CurveValue(state, this->EfficiencyCurvePtr, this->BoilerPLR);
891 : }
892 : }
893 3220248 : BoilerEff = EffCurveOutput * BoilerNomEff;
894 :
895 : // warn if efficiency curve produces zero or negative results
896 3220248 : if (!state.dataGlobal->WarmupFlag && EffCurveOutput <= 0.0) {
897 0 : if (this->BoilerLoad > 0.0) {
898 0 : if (this->EffCurveOutputError < 1) {
899 0 : ++this->EffCurveOutputError;
900 0 : ShowWarningError(state, "Boiler:HotWater \"" + this->Name + "\"");
901 0 : ShowContinueError(state, "...Normalized Boiler Efficiency Curve output is less than or equal to 0.");
902 0 : ShowContinueError(state, format("...Curve input x value (PLR) = {:.5T}", this->BoilerPLR));
903 0 : if (state.dataCurveManager->PerfCurve(this->EfficiencyCurvePtr).numDims == 2) {
904 0 : if (this->CurveTempMode == TempMode::ENTERINGBOILERTEMP) {
905 0 : ShowContinueError(state, format("...Curve input y value (Tinlet) = {:.2T}", state.dataLoopNodes->Node(BoilerInletNode).Temp));
906 0 : } else if (this->CurveTempMode == TempMode::LEAVINGBOILERTEMP) {
907 0 : ShowContinueError(state, format("...Curve input y value (Toutlet) = {:.2T}", this->BoilerOutletTemp));
908 : }
909 : }
910 0 : ShowContinueError(state, format("...Curve output (normalized eff) = {:.5T}", EffCurveOutput));
911 0 : ShowContinueError(state,
912 0 : format("...Calculated Boiler efficiency = {:.5T} (Boiler efficiency = Nominal Thermal Efficiency * Normalized "
913 : "Boiler Efficiency Curve output)",
914 0 : BoilerEff));
915 0 : ShowContinueErrorTimeStamp(state, "...Curve output reset to 0.01 and simulation continues.");
916 : } else {
917 0 : ShowRecurringWarningErrorAtEnd(state,
918 0 : "Boiler:HotWater \"" + this->Name +
919 : "\": Boiler Efficiency Curve output is less than or equal to 0 warning continues...",
920 : this->EffCurveOutputIndex,
921 : EffCurveOutput,
922 : EffCurveOutput);
923 : }
924 : }
925 0 : EffCurveOutput = 0.01;
926 : }
927 :
928 : // warn if overall efficiency greater than 1.1
929 3220248 : if (!state.dataGlobal->WarmupFlag && BoilerEff > 1.1) {
930 0 : if (this->BoilerLoad > 0.0 && this->EfficiencyCurvePtr > 0) {
931 0 : if (this->CalculatedEffError < 1) {
932 0 : ++this->CalculatedEffError;
933 0 : ShowWarningError(state, "Boiler:HotWater \"" + this->Name + "\"");
934 0 : ShowContinueError(state, "...Calculated Boiler Efficiency is greater than 1.1.");
935 0 : ShowContinueError(state, "...Boiler Efficiency calculations shown below.");
936 0 : ShowContinueError(state, format("...Curve input x value (PLR) = {:.5T}", this->BoilerPLR));
937 0 : if (state.dataCurveManager->PerfCurve(this->EfficiencyCurvePtr).numDims == 2) {
938 0 : if (this->CurveTempMode == TempMode::ENTERINGBOILERTEMP) {
939 0 : ShowContinueError(state, format("...Curve input y value (Tinlet) = {:.2T}", state.dataLoopNodes->Node(BoilerInletNode).Temp));
940 0 : } else if (this->CurveTempMode == TempMode::LEAVINGBOILERTEMP) {
941 0 : ShowContinueError(state, format("...Curve input y value (Toutlet) = {:.2T}", this->BoilerOutletTemp));
942 : }
943 : }
944 0 : ShowContinueError(state, format("...Curve output (normalized eff) = {:.5T}", EffCurveOutput));
945 0 : ShowContinueError(state,
946 0 : format("...Calculated Boiler efficiency = {:.5T} (Boiler efficiency = Nominal Thermal Efficiency * Normalized "
947 : "Boiler Efficiency Curve output)",
948 0 : BoilerEff));
949 0 : ShowContinueErrorTimeStamp(state, "...Curve output reset to 1.1 and simulation continues.");
950 : } else {
951 0 : ShowRecurringWarningErrorAtEnd(state,
952 0 : "Boiler:HotWater \"" + this->Name +
953 : "\": Calculated Boiler Efficiency is greater than 1.1 warning continues...",
954 : this->CalculatedEffIndex,
955 : BoilerEff,
956 : BoilerEff);
957 : }
958 : }
959 0 : EffCurveOutput = 1.1;
960 : }
961 :
962 : // calculate fuel used based on normalized boiler efficiency curve (=1 when no curve used)
963 3220248 : this->FuelUsed = TheorFuelUse / EffCurveOutput;
964 3220248 : if (this->BoilerLoad > 0.0) this->ParasiticElecPower = this->ParasiticElecLoad * this->BoilerPLR;
965 : }
966 :
967 6817372 : void BoilerSpecs::UpdateBoilerRecords(EnergyPlusData &state,
968 : Real64 const MyLoad, // boiler operating load
969 : bool const RunFlag // boiler on when TRUE
970 : )
971 : {
972 : // SUBROUTINE INFORMATION:
973 : // AUTHOR: Dan Fisher
974 : // DATE WRITTEN: October 1998
975 :
976 : // PURPOSE OF THIS SUBROUTINE:
977 : // boiler simulation reporting
978 :
979 6817372 : Real64 const ReportingConstant = state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
980 6817372 : int const BoilerInletNode = this->BoilerInletNodeNum;
981 6817372 : int const BoilerOutletNode = this->BoilerOutletNodeNum;
982 :
983 6817372 : if (MyLoad <= 0 || !RunFlag) {
984 3597124 : PlantUtilities::SafeCopyPlantNode(state, BoilerInletNode, BoilerOutletNode);
985 3597124 : state.dataLoopNodes->Node(BoilerOutletNode).Temp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
986 3597124 : this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
987 3597124 : this->BoilerLoad = 0.0;
988 3597124 : this->FuelUsed = 0.0;
989 3597124 : this->ParasiticElecPower = 0.0;
990 3597124 : this->BoilerPLR = 0.0;
991 3597124 : this->BoilerEff = 0.0;
992 : } else {
993 3220248 : PlantUtilities::SafeCopyPlantNode(state, BoilerInletNode, BoilerOutletNode);
994 3220248 : state.dataLoopNodes->Node(BoilerOutletNode).Temp = this->BoilerOutletTemp;
995 : }
996 :
997 6817372 : this->BoilerInletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
998 6817372 : this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerOutletNode).MassFlowRate;
999 6817372 : this->BoilerEnergy = this->BoilerLoad * ReportingConstant;
1000 6817372 : this->FuelConsumed = this->FuelUsed * ReportingConstant;
1001 6817372 : this->ParasiticElecConsumption = this->ParasiticElecPower * ReportingConstant;
1002 6817372 : }
1003 :
1004 2313 : } // namespace EnergyPlus::Boilers
|