LCOV - code coverage report
Current view: top level - EnergyPlus - Boilers.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 73.7 % 486 358
Test Date: 2025-06-02 07:23:51 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          211 : 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          211 :     if (state.dataBoilers->getBoilerInputFlag) {
      97          203 :         GetBoilerInput(state);
      98          203 :         state.dataBoilers->getBoilerInputFlag = false;
      99              :     }
     100              :     // Now look for this particular boiler in the list
     101          211 :     auto myBoiler = std::find_if(state.dataBoilers->Boiler.begin(), state.dataBoilers->Boiler.end(), [&objectName](const BoilerSpecs &boiler) {
     102          219 :         return boiler.Name == objectName;
     103              :     });
     104          211 :     if (myBoiler != state.dataBoilers->Boiler.end()) {
     105          211 :         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      7478785 : 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      7478785 :     auto &sim_component(DataPlant::CompData::getPlantComponent(state, this->plantLoc));
     121      7478785 :     this->InitBoiler(state);
     122      7478785 :     this->CalcBoilerModel(state, CurLoad, RunFlag, sim_component.FlowCtrl);
     123      7478785 :     this->UpdateBoilerRecords(state, CurLoad, RunFlag);
     124      7478785 : }
     125              : 
     126         1077 : void BoilerSpecs::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
     127              :                                       [[maybe_unused]] const PlantLocation &calledFromLocation,
     128              :                                       Real64 &MaxLoad,
     129              :                                       Real64 &MinLoad,
     130              :                                       Real64 &OptLoad)
     131              : {
     132         1077 :     MinLoad = this->NomCap * this->MinPartLoadRat;
     133         1077 :     MaxLoad = this->NomCap * this->MaxPartLoadRat;
     134         1077 :     OptLoad = this->NomCap * this->OptPartLoadRat;
     135         1077 : }
     136              : 
     137          211 : void BoilerSpecs::getSizingFactor(Real64 &SizFactor)
     138              : {
     139          211 :     SizFactor = this->SizFac;
     140          211 : }
     141              : 
     142         1077 : void BoilerSpecs::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
     143              : {
     144         1077 :     this->InitBoiler(state);
     145         1077 :     this->SizeBoiler(state);
     146         1077 : }
     147              : 
     148          203 : 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          203 :     auto &s_ipsc = state.dataIPShortCut;
     166              : 
     167              :     // LOCAL VARIABLES
     168          203 :     bool ErrorsFound(false); // Flag to show errors were found during GetInput
     169              : 
     170              :     // GET NUMBER OF ALL EQUIPMENT
     171          203 :     s_ipsc->cCurrentModuleObject = "Boiler:HotWater";
     172          203 :     int numBoilers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, s_ipsc->cCurrentModuleObject);
     173              : 
     174          203 :     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          203 :     if (allocated(state.dataBoilers->Boiler)) {
     181            0 :         return;
     182              :     }
     183              : 
     184          203 :     state.dataBoilers->Boiler.allocate(numBoilers);
     185              : 
     186              :     // LOAD ARRAYS WITH CURVE FIT Boiler DATA
     187              : 
     188          414 :     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          422 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
     193          211 :                                                                  s_ipsc->cCurrentModuleObject,
     194              :                                                                  BoilerNum,
     195          211 :                                                                  s_ipsc->cAlphaArgs,
     196              :                                                                  NumAlphas,
     197          211 :                                                                  s_ipsc->rNumericArgs,
     198              :                                                                  NumNums,
     199              :                                                                  IOStat,
     200          211 :                                                                  s_ipsc->lNumericFieldBlanks,
     201          211 :                                                                  s_ipsc->lAlphaFieldBlanks,
     202          211 :                                                                  s_ipsc->cAlphaFieldNames,
     203          211 :                                                                  s_ipsc->cNumericFieldNames);
     204              : 
     205          211 :         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          211 :         GlobalNames::VerifyUniqueBoilerName(
     209          211 :             state, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1), ErrorsFound, s_ipsc->cCurrentModuleObject + " Name");
     210          211 :         auto &thisBoiler = state.dataBoilers->Boiler(BoilerNum);
     211          211 :         thisBoiler.Name = s_ipsc->cAlphaArgs(1);
     212          211 :         thisBoiler.Type = DataPlant::PlantEquipmentType::Boiler_Simple;
     213              : 
     214              :         // Validate fuel type input
     215          211 :         thisBoiler.FuelType = static_cast<Constant::eFuel>(getEnumValue(Constant::eFuelNamesUC, s_ipsc->cAlphaArgs(2)));
     216              : 
     217          211 :         thisBoiler.NomCap = s_ipsc->rNumericArgs(1);
     218          211 :         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          211 :         if (thisBoiler.NomCap == DataSizing::AutoSize) {
     225          187 :             thisBoiler.NomCapWasAutoSized = true;
     226              :         }
     227              : 
     228          211 :         thisBoiler.NomEffic = s_ipsc->rNumericArgs(2);
     229          211 :         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          211 :         } 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          211 :         if (s_ipsc->cAlphaArgs(3) == "ENTERINGBOILER") {
     244            1 :             thisBoiler.CurveTempMode = TempMode::ENTERINGBOILERTEMP;
     245          210 :         } else if (s_ipsc->cAlphaArgs(3) == "LEAVINGBOILER") {
     246          207 :             thisBoiler.CurveTempMode = TempMode::LEAVINGBOILERTEMP;
     247              :         } else {
     248            3 :             thisBoiler.CurveTempMode = TempMode::NOTSET;
     249              :         }
     250              : 
     251          211 :         if (s_ipsc->lAlphaFieldBlanks(4)) {
     252              :             // Ok if this is empty?
     253          174 :         } 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          174 :         } 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          174 :             if (thisBoiler.EfficiencyCurve->numDims == 2) {         // curve uses water temperature
     262            2 :                 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          211 :         thisBoiler.VolFlowRate = s_ipsc->rNumericArgs(3);
     283          211 :         if (thisBoiler.VolFlowRate == DataSizing::AutoSize) {
     284          203 :             thisBoiler.VolFlowRateWasAutoSized = true;
     285              :         }
     286          211 :         thisBoiler.MinPartLoadRat = s_ipsc->rNumericArgs(4);
     287          211 :         thisBoiler.MaxPartLoadRat = s_ipsc->rNumericArgs(5);
     288          211 :         thisBoiler.OptPartLoadRat = s_ipsc->rNumericArgs(6);
     289              : 
     290          211 :         thisBoiler.TempUpLimitBoilerOut = s_ipsc->rNumericArgs(7);
     291              :         // default to 99.9C if upper temperature limit is left blank.
     292          211 :         if (thisBoiler.TempUpLimitBoilerOut <= 0.0) {
     293            0 :             thisBoiler.TempUpLimitBoilerOut = 99.9;
     294              :         }
     295              : 
     296          211 :         thisBoiler.ParasiticElecLoad = s_ipsc->rNumericArgs(8);
     297          211 :         thisBoiler.ParasiticFuelCapacity = s_ipsc->rNumericArgs(10);
     298          211 :         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          211 :         thisBoiler.SizFac = s_ipsc->rNumericArgs(9);
     306          211 :         if (thisBoiler.SizFac == 0.0) {
     307            0 :             thisBoiler.SizFac = 1.0;
     308              :         }
     309              : 
     310          211 :         thisBoiler.BoilerInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     311          211 :                                                                             s_ipsc->cAlphaArgs(5),
     312              :                                                                             ErrorsFound,
     313              :                                                                             DataLoopNode::ConnectionObjectType::BoilerHotWater,
     314          211 :                                                                             s_ipsc->cAlphaArgs(1),
     315              :                                                                             DataLoopNode::NodeFluidType::Water,
     316              :                                                                             DataLoopNode::ConnectionType::Inlet,
     317              :                                                                             NodeInputManager::CompFluidStream::Primary,
     318              :                                                                             DataLoopNode::ObjectIsNotParent);
     319          422 :         thisBoiler.BoilerOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
     320          211 :                                                                              s_ipsc->cAlphaArgs(6),
     321              :                                                                              ErrorsFound,
     322              :                                                                              DataLoopNode::ConnectionObjectType::BoilerHotWater,
     323          211 :                                                                              s_ipsc->cAlphaArgs(1),
     324              :                                                                              DataLoopNode::NodeFluidType::Water,
     325              :                                                                              DataLoopNode::ConnectionType::Outlet,
     326              :                                                                              NodeInputManager::CompFluidStream::Primary,
     327              :                                                                              DataLoopNode::ObjectIsNotParent);
     328          422 :         BranchNodeConnections::TestCompSet(
     329          211 :             state, s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaArgs(1), s_ipsc->cAlphaArgs(5), s_ipsc->cAlphaArgs(6), "Hot Water Nodes");
     330              : 
     331          211 :         if (s_ipsc->cAlphaArgs(7) == "CONSTANTFLOW") {
     332           38 :             thisBoiler.FlowMode = DataPlant::FlowMode::Constant;
     333          173 :         } else if (s_ipsc->cAlphaArgs(7) == "LEAVINGSETPOINTMODULATED") {
     334          173 :             thisBoiler.FlowMode = DataPlant::FlowMode::LeavingSetpointModulated;
     335            0 :         } else if (s_ipsc->cAlphaArgs(7) == "NOTMODULATED") {
     336            0 :             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          211 :         if (NumAlphas > 7) {
     347            4 :             thisBoiler.EndUseSubcategory = s_ipsc->cAlphaArgs(8);
     348              :         } else {
     349          207 :             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          203 :     if (ErrorsFound) {
     355            0 :         ShowFatalError(state, format("{}{}", RoutineName, "Errors found in processing " + s_ipsc->cCurrentModuleObject + " input."));
     356              :     }
     357              : }
     358              : 
     359          211 : void BoilerSpecs::SetupOutputVars(EnergyPlusData &state)
     360              : {
     361          211 :     std::string_view const sFuelType = Constant::eFuelNames[static_cast<int>(this->FuelType)];
     362          422 :     SetupOutputVariable(state,
     363              :                         "Boiler Heating Rate",
     364              :                         Constant::Units::W,
     365          211 :                         this->BoilerLoad,
     366              :                         OutputProcessor::TimeStepType::System,
     367              :                         OutputProcessor::StoreType::Average,
     368          211 :                         this->Name);
     369          422 :     SetupOutputVariable(state,
     370              :                         "Boiler Heating Energy",
     371              :                         Constant::Units::J,
     372          211 :                         this->BoilerEnergy,
     373              :                         OutputProcessor::TimeStepType::System,
     374              :                         OutputProcessor::StoreType::Sum,
     375          211 :                         this->Name,
     376              :                         Constant::eResource::EnergyTransfer,
     377              :                         OutputProcessor::Group::Plant,
     378              :                         OutputProcessor::EndUseCat::Boilers);
     379          633 :     SetupOutputVariable(state,
     380          422 :                         format("Boiler {} Rate", sFuelType),
     381              :                         Constant::Units::W,
     382          211 :                         this->FuelUsed,
     383              :                         OutputProcessor::TimeStepType::System,
     384              :                         OutputProcessor::StoreType::Average,
     385          211 :                         this->Name);
     386          633 :     SetupOutputVariable(state,
     387          422 :                         format("Boiler {} Energy", sFuelType),
     388              :                         Constant::Units::J,
     389          211 :                         this->FuelConsumed,
     390              :                         OutputProcessor::TimeStepType::System,
     391              :                         OutputProcessor::StoreType::Sum,
     392          211 :                         this->Name,
     393          211 :                         Constant::eFuel2eResource[(int)this->FuelType],
     394              :                         OutputProcessor::Group::Plant,
     395              :                         OutputProcessor::EndUseCat::Heating,
     396              :                         this->EndUseSubcategory);
     397          422 :     SetupOutputVariable(state,
     398              :                         "Boiler Inlet Temperature",
     399              :                         Constant::Units::C,
     400          211 :                         this->BoilerInletTemp,
     401              :                         OutputProcessor::TimeStepType::System,
     402              :                         OutputProcessor::StoreType::Average,
     403          211 :                         this->Name);
     404          422 :     SetupOutputVariable(state,
     405              :                         "Boiler Outlet Temperature",
     406              :                         Constant::Units::C,
     407          211 :                         this->BoilerOutletTemp,
     408              :                         OutputProcessor::TimeStepType::System,
     409              :                         OutputProcessor::StoreType::Average,
     410          211 :                         this->Name);
     411          422 :     SetupOutputVariable(state,
     412              :                         "Boiler Mass Flow Rate",
     413              :                         Constant::Units::kg_s,
     414          211 :                         this->BoilerMassFlowRate,
     415              :                         OutputProcessor::TimeStepType::System,
     416              :                         OutputProcessor::StoreType::Average,
     417          211 :                         this->Name);
     418          422 :     SetupOutputVariable(state,
     419              :                         "Boiler Ancillary Electricity Rate",
     420              :                         Constant::Units::W,
     421          211 :                         this->ParasiticElecPower,
     422              :                         OutputProcessor::TimeStepType::System,
     423              :                         OutputProcessor::StoreType::Average,
     424          211 :                         this->Name);
     425          422 :     SetupOutputVariable(state,
     426              :                         "Boiler Ancillary Electricity Energy",
     427              :                         Constant::Units::J,
     428          211 :                         this->ParasiticElecConsumption,
     429              :                         OutputProcessor::TimeStepType::System,
     430              :                         OutputProcessor::StoreType::Sum,
     431          211 :                         this->Name,
     432              :                         Constant::eResource::Electricity,
     433              :                         OutputProcessor::Group::Plant,
     434              :                         OutputProcessor::EndUseCat::Heating,
     435              :                         "Boiler Parasitic");
     436          211 :     if (this->FuelType != Constant::eFuel::Electricity) {
     437          630 :         SetupOutputVariable(state,
     438          420 :                             format("Boiler Ancillary {} Rate", sFuelType),
     439              :                             Constant::Units::W,
     440          210 :                             this->ParasiticFuelRate,
     441              :                             OutputProcessor::TimeStepType::System,
     442              :                             OutputProcessor::StoreType::Average,
     443          210 :                             this->Name);
     444          630 :         SetupOutputVariable(state,
     445          420 :                             format("Boiler Ancillary {} Energy", sFuelType),
     446              :                             Constant::Units::J,
     447          210 :                             this->ParasiticFuelConsumption,
     448              :                             OutputProcessor::TimeStepType::System,
     449              :                             OutputProcessor::StoreType::Sum,
     450          210 :                             this->Name,
     451          210 :                             Constant::eFuel2eResource[(int)this->FuelType],
     452              :                             OutputProcessor::Group::Plant,
     453              :                             OutputProcessor::EndUseCat::Heating,
     454              :                             "Boiler Parasitic");
     455              :     }
     456          422 :     SetupOutputVariable(state,
     457              :                         "Boiler Part Load Ratio",
     458              :                         Constant::Units::None,
     459          211 :                         this->BoilerPLR,
     460              :                         OutputProcessor::TimeStepType::System,
     461              :                         OutputProcessor::StoreType::Average,
     462          211 :                         this->Name);
     463          422 :     SetupOutputVariable(state,
     464              :                         "Boiler Efficiency",
     465              :                         Constant::Units::None,
     466          211 :                         this->BoilerEff,
     467              :                         OutputProcessor::TimeStepType::System,
     468              :                         OutputProcessor::StoreType::Average,
     469          211 :                         this->Name);
     470          211 :     if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
     471           32 :         SetupEMSInternalVariable(state, "Boiler Nominal Capacity", this->Name, "[W]", this->NomCap);
     472              :     }
     473          211 : }
     474              : 
     475          211 : void BoilerSpecs::oneTimeInit(EnergyPlusData &state)
     476              : {
     477              :     // Locate the boilers on the plant loops for later usage
     478          211 :     bool errFlag = false;
     479          633 :     PlantUtilities::ScanPlantLoopsForObject(
     480          422 :         state, this->Name, DataPlant::PlantEquipmentType::Boiler_Simple, this->plantLoc, errFlag, _, this->TempUpLimitBoilerOut, _, _, _);
     481          211 :     if (errFlag) {
     482            0 :         ShowFatalError(state, "InitBoiler: Program terminated due to previous condition(s).");
     483              :     }
     484              : 
     485          211 :     if ((this->FlowMode == DataPlant::FlowMode::LeavingSetpointModulated) || (this->FlowMode == DataPlant::FlowMode::Constant)) {
     486              :         // reset flow priority
     487          211 :         DataPlant::CompData::getPlantComponent(state, this->plantLoc).FlowPriority = DataPlant::LoopFlowStatus::NeedyIfLoopOn;
     488              :     }
     489          211 : }
     490              : 
     491         1326 : void BoilerSpecs::initEachEnvironment(EnergyPlusData &state)
     492              : {
     493              :     static constexpr std::string_view RoutineName("BoilerSpecs::initEachEnvironment");
     494         1326 :     Real64 const rho = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
     495         1326 :     this->DesMassFlowRate = this->VolFlowRate * rho;
     496              : 
     497         1326 :     PlantUtilities::InitComponentNodes(state, 0.0, this->DesMassFlowRate, this->BoilerInletNodeNum, this->BoilerOutletNodeNum);
     498              : 
     499         1326 :     if (this->FlowMode == DataPlant::FlowMode::LeavingSetpointModulated) { // check if setpoint on outlet node
     500         1117 :         if ((state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint == DataLoopNode::SensedNodeFlagValue) &&
     501           11 :             (state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPointLo == DataLoopNode::SensedNodeFlagValue)) {
     502           11 :             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           11 :                 bool FatalError = false; // but not really fatal yet, but should be.
     513           11 :                 EMSManager::CheckIfNodeSetPointManagedByEMS(state, this->BoilerOutletNodeNum, HVAC::CtrlVarType::Temp, FatalError);
     514           11 :                 state.dataLoopNodes->NodeSetpointCheck(this->BoilerOutletNodeNum).needsSetpointChecking = false;
     515           11 :                 if (FatalError) {
     516            1 :                     if (!this->ModulatedFlowErrDone) {
     517            1 :                         ShowWarningError(state, format("Missing temperature setpoint for LeavingSetpointModulated mode Boiler named {}", this->Name));
     518            2 :                         ShowContinueError(state, "  A temperature setpoint is needed at the outlet node of a boiler in variable flow mode");
     519            2 :                         ShowContinueError(state, "  use a Setpoint Manager to establish a setpoint at the boiler outlet node ");
     520            2 :                         ShowContinueError(state, "  or use an EMS actuator to establish a setpoint at the boiler outlet node ");
     521            2 :                         ShowContinueError(state, "  The overall loop setpoint will be assumed for Boiler. The simulation continues ... ");
     522            1 :                         this->ModulatedFlowErrDone = true;
     523              :                     }
     524              :                 }
     525              :             }
     526           11 :             this->ModulatedFlowSetToLoop = true; // this is for backward compatibility and could be removed
     527              :         }
     528              :     }
     529         1326 : }
     530              : 
     531      7479862 : 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      7479862 :     if (this->MyFlag) {
     547          211 :         this->SetupOutputVars(state);
     548          211 :         this->oneTimeInit(state);
     549          211 :         this->MyFlag = false;
     550              :     }
     551              : 
     552      7479862 :     if (this->MyEnvrnFlag && state.dataGlobal->BeginEnvrnFlag && (state.dataPlnt->PlantFirstSizesOkayToFinalize)) {
     553         1326 :         this->initEachEnvironment(state);
     554         1326 :         this->MyEnvrnFlag = false;
     555              :     }
     556              : 
     557      7479862 :     if (!state.dataGlobal->BeginEnvrnFlag) {
     558      7433432 :         this->MyEnvrnFlag = true;
     559              :     }
     560              : 
     561              :     // every iteration inits.  (most in calc routine)
     562              : 
     563      7479862 :     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       125652 :         if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     567       125652 :             state.dataLoopNodes->Node(this->BoilerOutletNodeNum).TempSetPoint =
     568       125652 :                 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      7479862 : }
     575              : 
     576         1077 : 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         1077 :     bool ErrorsFound(false); // If errors detected in input
     597              : 
     598              :     // grab some initial values for capacity and flow rate
     599         1077 :     Real64 tmpNomCap = this->NomCap;                 // local nominal capacity cooling power
     600         1077 :     Real64 tmpBoilerVolFlowRate = this->VolFlowRate; // local boiler design volume flow rate
     601              : 
     602         1077 :     int const PltSizNum = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).PlantSizNum; // Plant Sizing index corresponding to CurLoopNum
     603              : 
     604         1077 :     if (PltSizNum > 0) {
     605         1037 :         if (state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
     606              : 
     607          833 :             Real64 const rho = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).glycol->getDensity(state, Constant::HWInitConvTemp, RoutineName);
     608          833 :             Real64 const Cp = state.dataPlnt->PlantLoop(this->plantLoc.loopNum).glycol->getSpecificHeat(state, Constant::HWInitConvTemp, RoutineName);
     609          833 :             tmpNomCap =
     610          833 :                 Cp * rho * this->SizFac * state.dataSize->PlantSizData(PltSizNum).DeltaT * state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate;
     611              :         } else {
     612          204 :             if (this->NomCapWasAutoSized) {
     613          188 :                 tmpNomCap = 0.0;
     614              :             }
     615              :         }
     616         1037 :         if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     617          225 :             if (this->NomCapWasAutoSized) {
     618          209 :                 this->NomCap = tmpNomCap;
     619          209 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     620          187 :                     BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "Design Size Nominal Capacity [W]", tmpNomCap);
     621              :                 }
     622          209 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     623           11 :                     BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "Initial Design Size Nominal Capacity [W]", tmpNomCap);
     624              :                 }
     625              :             } else { // Hard-sized with sizing data
     626           16 :                 if (this->NomCap > 0.0 && tmpNomCap > 0.0) {
     627           16 :                     Real64 const NomCapUser = this->NomCap; // Hardsized nominal capacity for reporting
     628           16 :                     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     629           16 :                         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           16 :                         if (state.dataGlobal->DisplayExtraWarnings) {
     637            0 :                             if ((std::abs(tmpNomCap - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
     638            0 :                                 ShowMessage(state, format("SizeBoilerHotWater: Potential issue with equipment sizing for {}", this->Name));
     639            0 :                                 ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", NomCapUser));
     640            0 :                                 ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpNomCap));
     641            0 :                                 ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     642            0 :                                 ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     643              :                             }
     644              :                         }
     645              :                     }
     646              :                 }
     647              :             }
     648              :         }
     649              :     } else {
     650           40 :         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           40 :         if (!this->NomCapWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport && (this->NomCap > 0.0)) { // Hard-sized with no sizing data
     656            8 :             BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "User-Specified Nominal Capacity [W]", this->NomCap);
     657              :         }
     658              :     }
     659              : 
     660         1077 :     if (PltSizNum > 0) {
     661         1037 :         if (state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
     662          833 :             tmpBoilerVolFlowRate = state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate * this->SizFac;
     663              :         } else {
     664          204 :             if (this->VolFlowRateWasAutoSized) {
     665          204 :                 tmpBoilerVolFlowRate = 0.0;
     666              :             }
     667              :         }
     668         1037 :         if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     669          225 :             if (this->VolFlowRateWasAutoSized) {
     670          225 :                 this->VolFlowRate = tmpBoilerVolFlowRate;
     671          225 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     672          203 :                     BaseSizer::reportSizerOutput(
     673              :                         state, "Boiler:HotWater", this->Name, "Design Size Design Water Flow Rate [m3/s]", tmpBoilerVolFlowRate);
     674              :                 }
     675          225 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     676           11 :                     BaseSizer::reportSizerOutput(
     677              :                         state, "Boiler:HotWater", this->Name, "Initial Design Size Design Water Flow Rate [m3/s]", tmpBoilerVolFlowRate);
     678              :                 }
     679              :             } else {
     680            0 :                 if (this->VolFlowRate > 0.0 && tmpBoilerVolFlowRate > 0.0) {
     681            0 :                     Real64 VolFlowRateUser = this->VolFlowRate; // Hardsized volume flow for reporting
     682            0 :                     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            0 :                     tmpBoilerVolFlowRate = VolFlowRateUser;
     702              :                 }
     703              :             }
     704              :         }
     705              :     } else {
     706           40 :         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           48 :         if (!this->VolFlowRateWasAutoSized && state.dataPlnt->PlantFinalSizesOkayToReport &&
     712            8 :             (this->VolFlowRate > 0.0)) { // Hard-sized with no sizing data
     713            8 :             BaseSizer::reportSizerOutput(state, "Boiler:HotWater", this->Name, "User-Specified Design Water Flow Rate [m3/s]", this->VolFlowRate);
     714              :         }
     715              :     }
     716              : 
     717         1077 :     PlantUtilities::RegisterPlantCompDesignFlow(state, this->BoilerInletNodeNum, tmpBoilerVolFlowRate);
     718              : 
     719         1077 :     if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     720              :         // create predefined report
     721          211 :         std::string const equipName = this->Name;
     722          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechType, equipName, "Boiler:HotWater");
     723          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomEff, equipName, this->NomEffic);
     724          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, equipName, this->NomCap);
     725              : 
     726              :         // Std 229 Boilers new report table
     727          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerType, equipName, "Boiler:HotWater");
     728          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRefCap, equipName, this->NomCap);
     729          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRefEff, equipName, this->NomEffic);
     730          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRatedCap, equipName, this->NomCap);
     731          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerRatedEff, equipName, this->NomEffic);
     732          422 :         OutputReportPredefined::PreDefTableEntry(state,
     733          211 :                                                  state.dataOutRptPredefined->pdchBoilerPlantloopName,
     734              :                                                  equipName,
     735          422 :                                                  this->plantLoc.loopNum > 0 ? state.dataPlnt->PlantLoop(this->plantLoc.loopNum).Name : "N/A");
     736          422 :         OutputReportPredefined::PreDefTableEntry(
     737              :             state,
     738          211 :             state.dataOutRptPredefined->pdchBoilerPlantloopBranchName,
     739              :             equipName,
     740          211 :             this->plantLoc.loopNum > 0
     741          422 :                 ? state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopSide(this->plantLoc.loopSideNum).Branch(this->plantLoc.branchNum).Name
     742              :                 : "N/A");
     743          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerMinPLR, equipName, this->MinPartLoadRat);
     744          422 :         OutputReportPredefined::PreDefTableEntry(
     745          422 :             state, state.dataOutRptPredefined->pdchBoilerFuelType, equipName, Constant::eFuelNames[static_cast<int>(this->FuelType)]);
     746          211 :         OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchBoilerParaElecLoad, equipName, this->ParasiticElecLoad);
     747          211 :     }
     748              : 
     749         1077 :     if (ErrorsFound) {
     750            0 :         ShowFatalError(state, "Preceding sizing errors cause program termination");
     751              :     }
     752         1077 : }
     753              : 
     754      7478785 : 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      7478785 :     this->BoilerLoad = 0.0;
     782      7478785 :     this->ParasiticElecPower = 0.0;
     783      7478785 :     this->BoilerMassFlowRate = 0.0;
     784              : 
     785      7478785 :     int const BoilerInletNode = this->BoilerInletNodeNum;
     786      7478785 :     int const BoilerOutletNode = this->BoilerOutletNodeNum;
     787      7478785 :     Real64 BoilerNomCap = this->NomCap;                         // W - boiler nominal capacity
     788      7478785 :     Real64 const BoilerMaxPLR = this->MaxPartLoadRat;           // boiler maximum part load ratio
     789      7478785 :     Real64 const BoilerMinPLR = this->MinPartLoadRat;           // boiler minimum part load ratio
     790      7478785 :     Real64 BoilerNomEff = this->NomEffic;                       // boiler efficiency
     791      7478785 :     Real64 const TempUpLimitBout = this->TempUpLimitBoilerOut;  // C - boiler high temperature limit
     792      7478785 :     Real64 const BoilerMassFlowRateMax = this->DesMassFlowRate; // Max Design Boiler Mass Flow Rate converted from Volume Flow Rate
     793              : 
     794      7478785 :     Real64 Cp = state.dataPlnt->PlantLoop(this->plantLoc.loopNum)
     795      7478785 :                     .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      7478785 :     if (MyLoad <= 0.0 || !RunFlag) {
     801      3959446 :         if (EquipFlowCtrl == DataBranchAirLoopPlant::ControlType::SeriesActive) {
     802        70311 :             this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerInletNode).MassFlowRate;
     803              :         }
     804      3959446 :         return;
     805              :     }
     806              : 
     807              :     // If there is a fault of boiler fouling
     808      3526179 :     if (this->FaultyBoilerFoulingFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
     809         6840 :         (!state.dataGlobal->KickOffSimulation)) {
     810         6840 :         int FaultIndex = this->FaultyBoilerFoulingIndex;
     811         6840 :         Real64 NomCap_ff = BoilerNomCap;
     812         6840 :         Real64 BoilerNomEff_ff = BoilerNomEff;
     813              : 
     814              :         // calculate the Faulty Boiler Fouling Factor using fault information
     815         6840 :         this->FaultyBoilerFoulingFactor = state.dataFaultsMgr->FaultsBoilerFouling(FaultIndex).CalFoulingFactor(state);
     816              : 
     817              :         // update the boiler nominal capacity at faulty cases
     818         6840 :         BoilerNomCap = NomCap_ff * this->FaultyBoilerFoulingFactor;
     819         6840 :         BoilerNomEff = BoilerNomEff_ff * this->FaultyBoilerFoulingFactor;
     820              :     }
     821              : 
     822              :     // Set the current load equal to the boiler load
     823      3519339 :     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      3519339 :     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      1759512 :         if ((this->FlowMode == DataPlant::FlowMode::Constant) || (this->FlowMode == DataPlant::FlowMode::NotModulated)) {
     831              :             // Then find the flow rate and outlet temp
     832       208206 :             this->BoilerMassFlowRate = BoilerMassFlowRateMax;
     833       208206 :             PlantUtilities::SetComponentFlowRate(state, this->BoilerMassFlowRate, BoilerInletNode, BoilerOutletNode, this->plantLoc);
     834              : 
     835       208206 :             if ((this->BoilerMassFlowRate != 0.0) && (MyLoad > 0.0)) {
     836       208206 :                 BoilerDeltaTemp = this->BoilerLoad / this->BoilerMassFlowRate / Cp;
     837              :             } else {
     838            0 :                 BoilerDeltaTemp = 0.0;
     839              :             }
     840       208206 :             this->BoilerOutletTemp = BoilerDeltaTemp + state.dataLoopNodes->Node(BoilerInletNode).Temp;
     841              : 
     842      1551306 :         } 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      1551306 :             if (state.dataPlnt->PlantLoop(this->plantLoc.loopNum).LoopDemandCalcScheme == DataPlant::LoopDemandCalcScheme::SingleSetPoint) {
     847      1514403 :                 BoilerDeltaTemp = state.dataLoopNodes->Node(BoilerOutletNode).TempSetPoint - state.dataLoopNodes->Node(BoilerInletNode).Temp;
     848              :             } else { // DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand
     849        36903 :                 BoilerDeltaTemp = state.dataLoopNodes->Node(BoilerOutletNode).TempSetPointLo - state.dataLoopNodes->Node(BoilerInletNode).Temp;
     850              :             }
     851              : 
     852      1551306 :             this->BoilerOutletTemp = BoilerDeltaTemp + state.dataLoopNodes->Node(BoilerInletNode).Temp;
     853              : 
     854      1551306 :             if ((BoilerDeltaTemp > 0.0) && (this->BoilerLoad > 0.0)) {
     855      1551053 :                 this->BoilerMassFlowRate = this->BoilerLoad / Cp / BoilerDeltaTemp;
     856      1551053 :                 this->BoilerMassFlowRate = std::min(BoilerMassFlowRateMax, this->BoilerMassFlowRate);
     857              :             } else {
     858          253 :                 this->BoilerMassFlowRate = 0.0;
     859              :             }
     860      1551306 :             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      1759827 :         this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerInletNode).MassFlowRate;
     867              : 
     868      1759827 :         if ((MyLoad > 0.0) && (this->BoilerMassFlowRate > 0.0)) { // this boiler has a heat load
     869      1757141 :             this->BoilerLoad = MyLoad;
     870      1757141 :             if (this->BoilerLoad > BoilerNomCap * BoilerMaxPLR) {
     871          278 :                 this->BoilerLoad = BoilerNomCap * BoilerMaxPLR;
     872              :             }
     873      1757141 :             if (this->BoilerLoad < BoilerNomCap * BoilerMinPLR) {
     874          224 :                 this->BoilerLoad = BoilerNomCap * BoilerMinPLR;
     875              :             }
     876      1757141 :             this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp + this->BoilerLoad / (this->BoilerMassFlowRate * Cp);
     877              :         } else {
     878         2686 :             this->BoilerLoad = 0.0;
     879         2686 :             this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
     880              :         }
     881              :     }
     882              : 
     883              :     // Limit BoilerOutletTemp.  If > max temp, trip boiler off
     884      3519339 :     if (this->BoilerOutletTemp > TempUpLimitBout) {
     885          225 :         this->BoilerLoad = 0.0;
     886          225 :         this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
     887              :     }
     888      3519339 :     this->BoilerPLR = this->BoilerLoad / BoilerNomCap; // operating part load ratio
     889      3519339 :     this->BoilerPLR = std::min(this->BoilerPLR, BoilerMaxPLR);
     890      3519339 :     this->BoilerPLR = std::max(this->BoilerPLR, BoilerMinPLR);
     891              : 
     892              :     // calculate theoretical fuel use based on nominal thermal efficiency
     893      3519339 :     Real64 const TheorFuelUse = this->BoilerLoad / BoilerNomEff; // Theoretical (stoichiometric) fuel use
     894      3519339 :     Real64 EffCurveOutput = 1.0;                                 // Output of boiler efficiency curve
     895              : 
     896              :     // calculate normalized efficiency based on curve object type
     897      3519339 :     if (this->EfficiencyCurve != nullptr) {
     898      2630309 :         if (this->EfficiencyCurve->numDims == 2) {
     899         9240 :             if (this->CurveTempMode == TempMode::ENTERINGBOILERTEMP) {
     900         8496 :                 EffCurveOutput = this->EfficiencyCurve->value(state, this->BoilerPLR, state.dataLoopNodes->Node(BoilerInletNode).Temp);
     901          744 :             } else if (this->CurveTempMode == TempMode::LEAVINGBOILERTEMP) {
     902          744 :                 EffCurveOutput = this->EfficiencyCurve->value(state, this->BoilerPLR, this->BoilerOutletTemp);
     903              :             }
     904              :         } else {
     905      2621069 :             EffCurveOutput = this->EfficiencyCurve->value(state, this->BoilerPLR);
     906              :         }
     907              :     }
     908      3519339 :     BoilerEff = EffCurveOutput * BoilerNomEff;
     909              : 
     910              :     // warn if efficiency curve produces zero or negative results
     911      3519339 :     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      3519339 :     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      3519339 :     this->FuelUsed = TheorFuelUse / EffCurveOutput;
     980      3519339 :     if (this->BoilerLoad > 0.0) {
     981      3516428 :         this->ParasiticElecPower = this->ParasiticElecLoad * this->BoilerPLR;
     982              :     }
     983      3519339 :     this->ParasiticFuelRate = this->ParasiticFuelCapacity * (1.0 - this->BoilerPLR);
     984              : }
     985              : 
     986      7478785 : 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      7478785 :     Real64 const ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
     999      7478785 :     int const BoilerInletNode = this->BoilerInletNodeNum;
    1000      7478785 :     int const BoilerOutletNode = this->BoilerOutletNodeNum;
    1001              : 
    1002      7478785 :     if (MyLoad <= 0 || !RunFlag) {
    1003      3959446 :         PlantUtilities::SafeCopyPlantNode(state, BoilerInletNode, BoilerOutletNode);
    1004      3959446 :         state.dataLoopNodes->Node(BoilerOutletNode).Temp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
    1005      3959446 :         this->BoilerOutletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
    1006      3959446 :         this->BoilerLoad = 0.0;
    1007      3959446 :         this->FuelUsed = 0.0;
    1008      3959446 :         this->ParasiticElecPower = 0.0;
    1009      3959446 :         this->BoilerPLR = 0.0;
    1010      3959446 :         this->BoilerEff = 0.0;
    1011              :     } else {
    1012      3519339 :         PlantUtilities::SafeCopyPlantNode(state, BoilerInletNode, BoilerOutletNode);
    1013      3519339 :         state.dataLoopNodes->Node(BoilerOutletNode).Temp = this->BoilerOutletTemp;
    1014              :     }
    1015              : 
    1016      7478785 :     this->BoilerInletTemp = state.dataLoopNodes->Node(BoilerInletNode).Temp;
    1017      7478785 :     this->BoilerMassFlowRate = state.dataLoopNodes->Node(BoilerOutletNode).MassFlowRate;
    1018      7478785 :     this->BoilerEnergy = this->BoilerLoad * ReportingConstant;
    1019      7478785 :     this->FuelConsumed = this->FuelUsed * ReportingConstant;
    1020      7478785 :     this->ParasiticElecConsumption = this->ParasiticElecPower * ReportingConstant;
    1021      7478785 :     this->ParasiticFuelConsumption = this->ParasiticFuelRate * ReportingConstant;
    1022      7478785 : }
    1023              : 
    1024              : } // namespace EnergyPlus::Boilers
        

Generated by: LCOV version 2.0-1