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