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

Generated by: LCOV version 2.0-1