LCOV - code coverage report
Current view: top level - EnergyPlus - Boilers.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 65.8 % 486 320
Test Date: 2025-06-02 12:03:30 Functions: 100.0 % 14 14

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

Generated by: LCOV version 2.0-1