LCOV - code coverage report
Current view: top level - EnergyPlus - Boilers.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 357 500 71.4 %
Date: 2024-08-23 23:50:59 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.14