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