LCOV - code coverage report
Current view: top level - EnergyPlus - Boilers.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 320 457 70.0 %
Date: 2023-01-17 19:17:23 Functions: 15 15 100.0 %

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

Generated by: LCOV version 1.13