LCOV - code coverage report
Current view: top level - EnergyPlus - SolarCollectors.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 742 998 74.3 %
Date: 2024-08-24 18:31:18 Functions: 18 19 94.7 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <cmath>
      50             : 
      51             : // ObjexxFCL Headers
      52             : #include <ObjexxFCL/Array.functions.hh>
      53             : #include <ObjexxFCL/Fmath.hh>
      54             : 
      55             : // EnergyPlus Headers
      56             : #include <EnergyPlus/BranchNodeConnections.hh>
      57             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      58             : #include <EnergyPlus/DataEnvironment.hh>
      59             : #include <EnergyPlus/DataHVACGlobals.hh>
      60             : #include <EnergyPlus/DataHeatBalance.hh>
      61             : #include <EnergyPlus/DataIPShortCuts.hh>
      62             : #include <EnergyPlus/DataLoopNode.hh>
      63             : #include <EnergyPlus/DataSurfaces.hh>
      64             : #include <EnergyPlus/FluidProperties.hh>
      65             : #include <EnergyPlus/General.hh>
      66             : #include <EnergyPlus/GlobalNames.hh>
      67             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      68             : #include <EnergyPlus/NodeInputManager.hh>
      69             : #include <EnergyPlus/OutputProcessor.hh>
      70             : #include <EnergyPlus/Plant/DataPlant.hh>
      71             : #include <EnergyPlus/PlantUtilities.hh>
      72             : #include <EnergyPlus/Psychrometrics.hh>
      73             : #include <EnergyPlus/SolarCollectors.hh>
      74             : #include <EnergyPlus/UtilityRoutines.hh>
      75             : 
      76             : namespace EnergyPlus {
      77             : 
      78             : namespace SolarCollectors {
      79             : 
      80             :     // MODULE INFORMATION:
      81             :     //       AUTHOR         Peter Graham Ellis
      82             :     //       DATE WRITTEN   December 2003
      83             :     //       MODIFIED       B. Nigusse, FSEC/UCF, March 2012, added ICS Collector
      84             :     //       RE-ENGINEERED  Brent Griffith, for plant upgrade, general fluid props
      85             : 
      86             :     // PURPOSE OF THIS MODULE:
      87             :     // Simulates solar collectors as a component on the plant loop.  Currently only flat-plate collectors (glazed and
      88             :     // unglazed) are implemented.
      89             : 
      90             :     // METHODOLOGY EMPLOYED:
      91             :     // Solar collectors are called as non-zone equipment on the demand side of the plant loop.  The collector object
      92             :     // must be connected to a WATER HEATER object on the supply side of the plant loop.  Water is assumed to be
      93             :     // the heat transfer fluid.
      94             : 
      95             :     static constexpr std::string_view fluidNameWater("WATER");
      96             : 
      97          10 :     PlantComponent *CollectorData::factory(EnergyPlusData &state, std::string const &objectName)
      98             :     {
      99             :         // Process the input data
     100          10 :         if (state.dataSolarCollectors->GetInputFlag) {
     101           3 :             GetSolarCollectorInput(state);
     102           3 :             state.dataSolarCollectors->GetInputFlag = false;
     103             :         }
     104             :         // Now look for this particular object
     105          27 :         for (auto &thisSC : state.dataSolarCollectors->Collector) {
     106          27 :             if (thisSC.Name == objectName) {
     107          10 :                 return &thisSC;
     108             :             }
     109          20 :         }
     110             :         // If we didn't find it, fatal
     111             :         ShowFatalError(state, format("LocalSolarCollectorFactory: Error getting inputs for object named: {}", objectName)); // LCOV_EXCL_LINE
     112             :         // Shut up the compiler
     113             :         return nullptr; // LCOV_EXCL_LINE
     114             :     }
     115             : 
     116           3 :     void GetSolarCollectorInput(EnergyPlusData &state)
     117             :     {
     118             : 
     119             :         // SUBROUTINE INFORMATION:
     120             :         //       AUTHOR         Peter Graham Ellis
     121             :         //       DATE WRITTEN   December 2003
     122             :         //       MODIFIED       na
     123             :         //       RE-ENGINEERED  na
     124             : 
     125             :         // PURPOSE OF THIS SUBROUTINE:
     126             :         // Gets the solar collector input from the input file and sets up the parameters and collector objects.
     127             : 
     128           3 :         bool ErrorsFound(false);              // Set to true if errors in input, fatal at end of routine
     129             :         int IOStatus;                         // Used in GetObjectItem
     130             :         int NumAlphas;                        // Number of Alphas for each GetObjectItem call
     131             :         int NumNumbers;                       // Number of Numbers for each GetObjectItem call
     132           3 :         std::string CurrentModuleObject;      // for ease in renaming.
     133           3 :         std::string CurrentModuleParamObject; // for ease in renaming.
     134             : 
     135             :         int NumFields;  // Total number of fields in object
     136             :         int MaxAlphas;  // Maximum number of alpha fields in all objects
     137             :         int MaxNumbers; // Maximum number of numeric fields in all objects
     138             : 
     139           3 :         Array1D<Real64> Numbers;       // Numeric data
     140           3 :         Array1D_string Alphas;         // Alpha data
     141           3 :         Array1D_string cAlphaFields;   // Alpha field names
     142           3 :         Array1D_string cNumericFields; // Numeric field names
     143           3 :         Array1D_bool lAlphaBlanks;     // Logical array, alpha field input BLANK = .TRUE.
     144           3 :         Array1D_bool lNumericBlanks;   // Logical array, numeric field input BLANK = .TRUE.
     145             : 
     146           3 :         MaxNumbers = 0;
     147           3 :         MaxAlphas = 0;
     148             : 
     149           3 :         CurrentModuleParamObject = "SolarCollectorPerformance:FlatPlate";
     150           3 :         int NumOfFlatPlateParam = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleParamObject);
     151           3 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleParamObject, NumFields, NumAlphas, NumNumbers);
     152           3 :         MaxNumbers = max(MaxNumbers, NumNumbers);
     153           3 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     154             : 
     155           3 :         CurrentModuleObject = "SolarCollector:FlatPlate:Water";
     156           3 :         int NumFlatPlateUnits = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
     157           3 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNumbers);
     158           3 :         MaxNumbers = max(MaxNumbers, NumNumbers);
     159           3 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     160             : 
     161           3 :         CurrentModuleParamObject = "SolarCollectorPerformance:IntegralCollectorStorage";
     162           3 :         int NumOfICSParam = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleParamObject);
     163           3 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleParamObject, NumFields, NumAlphas, NumNumbers);
     164           3 :         MaxNumbers = max(MaxNumbers, NumNumbers);
     165           3 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     166             : 
     167           3 :         CurrentModuleObject = "SolarCollector:IntegralCollectorStorage";
     168           3 :         int NumOfICSUnits = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
     169           3 :         state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNumbers);
     170           3 :         MaxNumbers = max(MaxNumbers, NumNumbers);
     171           3 :         MaxAlphas = max(MaxAlphas, NumAlphas);
     172             : 
     173           3 :         Alphas.allocate(MaxAlphas);
     174           3 :         Numbers.dimension(MaxNumbers, 0.0);
     175           3 :         cAlphaFields.allocate(MaxAlphas);
     176           3 :         cNumericFields.allocate(MaxNumbers);
     177           3 :         lAlphaBlanks.dimension(MaxAlphas, true);
     178           3 :         lNumericBlanks.dimension(MaxNumbers, true);
     179             : 
     180           3 :         state.dataSolarCollectors->NumOfCollectors = NumFlatPlateUnits + NumOfICSUnits;
     181           3 :         state.dataSolarCollectors->NumOfParameters = NumOfFlatPlateParam + NumOfICSParam;
     182             : 
     183           3 :         if (state.dataSolarCollectors->NumOfParameters > 0) {
     184           3 :             state.dataSolarCollectors->Parameters.allocate(state.dataSolarCollectors->NumOfParameters);
     185             : 
     186           3 :             CurrentModuleParamObject = "SolarCollectorPerformance:FlatPlate";
     187             : 
     188           5 :             for (int FlatPlateParamNum = 1; FlatPlateParamNum <= NumOfFlatPlateParam; ++FlatPlateParamNum) {
     189             : 
     190           2 :                 int ParametersNum = FlatPlateParamNum;
     191           6 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     192             :                                                                          CurrentModuleParamObject,
     193             :                                                                          ParametersNum,
     194           2 :                                                                          state.dataIPShortCut->cAlphaArgs,
     195             :                                                                          NumAlphas,
     196           2 :                                                                          state.dataIPShortCut->rNumericArgs,
     197             :                                                                          NumNumbers,
     198             :                                                                          IOStatus,
     199           2 :                                                                          state.dataIPShortCut->lNumericFieldBlanks,
     200             :                                                                          _,
     201           2 :                                                                          state.dataIPShortCut->cAlphaFieldNames,
     202           2 :                                                                          state.dataIPShortCut->cNumericFieldNames);
     203             : 
     204             :                 // Collector module parameters name
     205           2 :                 GlobalNames::VerifyUniqueInterObjectName(state,
     206           2 :                                                          state.dataSolarCollectors->UniqueParametersNames,
     207           2 :                                                          state.dataIPShortCut->cAlphaArgs(1),
     208             :                                                          CurrentModuleObject,
     209           2 :                                                          state.dataIPShortCut->cAlphaFieldNames(1),
     210             :                                                          ErrorsFound);
     211           2 :                 state.dataSolarCollectors->Parameters(ParametersNum).Name = state.dataIPShortCut->cAlphaArgs(1);
     212             : 
     213             :                 // NOTE:  This values serves mainly as a reference.  The area of the associated surface object is used in all calculations.
     214           2 :                 state.dataSolarCollectors->Parameters(ParametersNum).Area = state.dataIPShortCut->rNumericArgs(1);
     215             : 
     216             :                 //                The TestFluid member variable was never accessed, so this input field seems to not do anything as of right now
     217             :                 //                if (state.dataIPShortCut->cAlphaArgs(2) == "WATER") {
     218             :                 //                    state.dataSolarCollectors->Parameters(ParametersNum).TestFluid = FluidEnum::WATER;
     219             :                 //                    // CASE('AIR')
     220             :                 //                    //  Parameters(ParametersNum)%TestFluid = AIR
     221             :                 //                } else {
     222             :                 //                    ShowSevereError(state, format("{}{} = {}:  {}{} is an unsupported Test Fluid for {}{}", //,
     223             :                 //                    CurrentModuleParamObject, state.dataIPShortCut->cAlphaArgs(1), //, state.dataIPShortCut->cAlphaArgs(2), //,
     224             :                 //                    state.dataIPShortCut->cAlphaFieldNames(2))); ErrorsFound = true;
     225             :                 //                }
     226             : 
     227           2 :                 if (state.dataIPShortCut->rNumericArgs(2) > 0.0) {
     228           2 :                     state.dataSolarCollectors->Parameters(ParametersNum).TestMassFlowRate =
     229           2 :                         state.dataIPShortCut->rNumericArgs(2) * Psychrometrics::RhoH2O(Constant::InitConvTemp);
     230             :                 } else {
     231           0 :                     ShowSevereError(state,
     232           0 :                                     format("{} = {}:  flow rate must be greater than zero for {}",
     233             :                                            CurrentModuleParamObject,
     234           0 :                                            state.dataIPShortCut->cAlphaArgs(1),
     235           0 :                                            state.dataIPShortCut->cNumericFieldNames(2)));
     236           0 :                     ErrorsFound = true;
     237             :                 }
     238             : 
     239           2 :                 std::string_view const key = state.dataIPShortCut->cAlphaArgs(3);
     240           2 :                 state.dataSolarCollectors->Parameters(ParametersNum).TestType = static_cast<TestTypeEnum>(getEnumValue(testTypesUC, key));
     241           2 :                 if (state.dataSolarCollectors->Parameters(ParametersNum).TestType == TestTypeEnum::INVALID) {
     242           0 :                     ShowSevereError(state,
     243           0 :                                     format("{} = {}: {} is not supported for {}",
     244             :                                            CurrentModuleParamObject,
     245           0 :                                            state.dataIPShortCut->cAlphaArgs(1),
     246             :                                            key,
     247           0 :                                            state.dataIPShortCut->cAlphaFieldNames(3)));
     248           0 :                     ErrorsFound = true;
     249             :                 }
     250             : 
     251             :                 // Efficiency equation coefficients
     252           2 :                 state.dataSolarCollectors->Parameters(ParametersNum).eff0 = state.dataIPShortCut->rNumericArgs(3);
     253           2 :                 state.dataSolarCollectors->Parameters(ParametersNum).eff1 = state.dataIPShortCut->rNumericArgs(4);
     254             : 
     255           2 :                 if (NumNumbers > 4) {
     256           2 :                     state.dataSolarCollectors->Parameters(ParametersNum).eff2 = state.dataIPShortCut->rNumericArgs(5);
     257             :                 } else {
     258           0 :                     state.dataSolarCollectors->Parameters(ParametersNum).eff2 = 0.0;
     259             :                 }
     260             : 
     261             :                 // Incident angle modifier coefficients
     262           2 :                 if (NumNumbers > 5) {
     263           2 :                     state.dataSolarCollectors->Parameters(ParametersNum).iam1 = state.dataIPShortCut->rNumericArgs(6);
     264             :                 } else {
     265           0 :                     state.dataSolarCollectors->Parameters(ParametersNum).iam1 = 0.0;
     266             :                 }
     267             : 
     268           2 :                 if (NumNumbers > 6) {
     269           2 :                     state.dataSolarCollectors->Parameters(FlatPlateParamNum).iam2 = state.dataIPShortCut->rNumericArgs(7);
     270             :                 } else {
     271           0 :                     state.dataSolarCollectors->Parameters(ParametersNum).iam2 = 0.0;
     272             :                 }
     273             :             } // ParametersNum
     274             : 
     275           3 :             if (ErrorsFound) ShowFatalError(state, format("Errors in {} input.", CurrentModuleParamObject));
     276             :         }
     277             : 
     278           3 :         if (state.dataSolarCollectors->NumOfCollectors > 0) {
     279           3 :             state.dataSolarCollectors->Collector.allocate(state.dataSolarCollectors->NumOfCollectors);
     280             : 
     281           3 :             CurrentModuleObject = "SolarCollector:FlatPlate:Water";
     282             : 
     283          11 :             for (int FlatPlateUnitsNum = 1; FlatPlateUnitsNum <= NumFlatPlateUnits; ++FlatPlateUnitsNum) {
     284             : 
     285           8 :                 int CollectorNum = FlatPlateUnitsNum;
     286             : 
     287          16 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     288             :                                                                          CurrentModuleObject,
     289             :                                                                          CollectorNum,
     290           8 :                                                                          state.dataIPShortCut->cAlphaArgs,
     291             :                                                                          NumAlphas,
     292           8 :                                                                          state.dataIPShortCut->rNumericArgs,
     293             :                                                                          NumNumbers,
     294             :                                                                          IOStatus);
     295             : 
     296             :                 // Collector name
     297           8 :                 GlobalNames::VerifyUniqueInterObjectName(
     298           8 :                     state, state.dataSolarCollectors->UniqueCollectorNames, state.dataIPShortCut->cAlphaArgs(1), CurrentModuleObject, ErrorsFound);
     299           8 :                 state.dataSolarCollectors->Collector(CollectorNum).Name = state.dataIPShortCut->cAlphaArgs(1);
     300           8 :                 state.dataSolarCollectors->Collector(CollectorNum).Type =
     301             :                     DataPlant::PlantEquipmentType::SolarCollectorFlatPlate; // parameter assigned in DataPlant
     302             : 
     303             :                 // Get parameters object
     304           8 :                 int ParametersNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataSolarCollectors->Parameters);
     305             : 
     306           8 :                 if (ParametersNum == 0) {
     307           0 :                     ShowSevereError(state,
     308           0 :                                     format("{} = {}: {} object called {} not found.",
     309             :                                            CurrentModuleObject,
     310           0 :                                            state.dataIPShortCut->cAlphaArgs(1),
     311             :                                            CurrentModuleParamObject,
     312           0 :                                            state.dataIPShortCut->cAlphaArgs(2)));
     313           0 :                     ErrorsFound = true;
     314             :                 } else {
     315           8 :                     state.dataSolarCollectors->Collector(CollectorNum).Parameters = ParametersNum;
     316             :                 }
     317             : 
     318             :                 // Get surface object
     319           8 :                 int SurfNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(3), state.dataSurface->Surface);
     320             : 
     321           8 :                 if (SurfNum == 0) {
     322           0 :                     ShowSevereError(state,
     323           0 :                                     format("{} = {}:  Surface {} not found.",
     324             :                                            CurrentModuleObject,
     325           0 :                                            state.dataIPShortCut->cAlphaArgs(1),
     326           0 :                                            state.dataIPShortCut->cAlphaArgs(3)));
     327           0 :                     ErrorsFound = true;
     328           0 :                     continue; // avoid hard crash
     329             :                 } else {
     330             : 
     331           8 :                     if (!state.dataSurface->Surface(SurfNum).ExtSolar) {
     332           0 :                         ShowWarningError(state,
     333           0 :                                          format("{} = {}:  Surface {} is not exposed to exterior radiation.",
     334             :                                                 CurrentModuleObject,
     335           0 :                                                 state.dataIPShortCut->cAlphaArgs(1),
     336           0 :                                                 state.dataIPShortCut->cAlphaArgs(3)));
     337             :                     }
     338             : 
     339             :                     // check surface orientation, warn if upside down
     340           8 :                     if ((state.dataSurface->Surface(SurfNum).Tilt < -95.0) || (state.dataSurface->Surface(SurfNum).Tilt > 95.0)) {
     341           0 :                         ShowWarningError(state,
     342           0 :                                          format("Suspected input problem with {} = {}",
     343           0 :                                                 state.dataIPShortCut->cAlphaFieldNames(3),
     344           0 :                                                 state.dataIPShortCut->cAlphaArgs(3)));
     345           0 :                         ShowContinueError(
     346           0 :                             state, format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     347           0 :                         ShowContinueError(state, "Surface used for solar collector faces down");
     348           0 :                         ShowContinueError(
     349             :                             state,
     350           0 :                             format("Surface tilt angle (degrees from ground outward normal) = {:.2R}", state.dataSurface->Surface(SurfNum).Tilt));
     351             :                     }
     352             : 
     353             :                     // Check to make sure other solar collectors are not using the same surface
     354             :                     // NOTE:  Must search over all solar collector types
     355          48 :                     for (int CollectorNum2 = 1; CollectorNum2 <= NumFlatPlateUnits; ++CollectorNum2) {
     356          40 :                         if (state.dataSolarCollectors->Collector(CollectorNum2).Surface == SurfNum) {
     357           0 :                             ShowSevereError(state,
     358           0 :                                             format("{} = {}:  Surface {} is referenced by more than one {}",
     359             :                                                    CurrentModuleObject,
     360           0 :                                                    state.dataIPShortCut->cAlphaArgs(1),
     361           0 :                                                    state.dataIPShortCut->cAlphaArgs(3),
     362             :                                                    CurrentModuleObject));
     363           0 :                             ErrorsFound = true;
     364           0 :                             break;
     365             :                         }
     366             :                     } // CollectorNum2
     367             : 
     368           8 :                     state.dataSolarCollectors->Collector(CollectorNum).Surface = SurfNum;
     369             :                 }
     370             : 
     371             :                 // Give warning if surface area and gross area do not match within tolerance
     372          16 :                 if (SurfNum > 0 && ParametersNum > 0 && state.dataSolarCollectors->Parameters(ParametersNum).Area > 0.0 &&
     373           8 :                     std::abs(state.dataSolarCollectors->Parameters(ParametersNum).Area - state.dataSurface->Surface(SurfNum).Area) /
     374           8 :                             state.dataSurface->Surface(SurfNum).Area >
     375             :                         0.01) {
     376             : 
     377           0 :                     ShowWarningError(state,
     378           0 :                                      format("{} = {}:  Gross Area of solar collector parameters and surface object differ by more than 1%.",
     379             :                                             CurrentModuleObject,
     380           0 :                                             state.dataIPShortCut->cAlphaArgs(1)));
     381           0 :                     ShowContinueError(state, "Area of surface object will be used in all calculations.");
     382             :                 }
     383             : 
     384           8 :                 state.dataSolarCollectors->Collector(CollectorNum).InletNode =
     385          16 :                     NodeInputManager::GetOnlySingleNode(state,
     386           8 :                                                         state.dataIPShortCut->cAlphaArgs(4),
     387             :                                                         ErrorsFound,
     388             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorFlatPlateWater,
     389           8 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     390             :                                                         DataLoopNode::NodeFluidType::Water,
     391             :                                                         DataLoopNode::ConnectionType::Inlet,
     392             :                                                         NodeInputManager::CompFluidStream::Primary,
     393             :                                                         DataLoopNode::ObjectIsNotParent);
     394           8 :                 state.dataSolarCollectors->Collector(CollectorNum).OutletNode =
     395          16 :                     NodeInputManager::GetOnlySingleNode(state,
     396           8 :                                                         state.dataIPShortCut->cAlphaArgs(5),
     397             :                                                         ErrorsFound,
     398             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorFlatPlateWater,
     399           8 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     400             :                                                         DataLoopNode::NodeFluidType::Water,
     401             :                                                         DataLoopNode::ConnectionType::Outlet,
     402             :                                                         NodeInputManager::CompFluidStream::Primary,
     403             :                                                         DataLoopNode::ObjectIsNotParent);
     404             : 
     405           8 :                 if (NumNumbers > 0) {
     406           8 :                     state.dataSolarCollectors->Collector(CollectorNum).VolFlowRateMax =
     407           8 :                         state.dataIPShortCut->rNumericArgs(1); // Max volumetric flow rate used for plant sizing calculation
     408             :                 } else {
     409           0 :                     state.dataSolarCollectors->Collector(CollectorNum).VolFlowRateMax =
     410             :                         0.0; // Max vol flow rate is not specified; no flow for plant sizing calculation
     411           0 :                     state.dataSolarCollectors->Collector(CollectorNum).MassFlowRateMax =
     412             :                         999999.9; // But...set a very high value so that it demands as much as possible
     413             :                 }
     414             : 
     415          16 :                 BranchNodeConnections::TestCompSet(state,
     416             :                                                    CurrentModuleObject,
     417           8 :                                                    state.dataIPShortCut->cAlphaArgs(1),
     418           8 :                                                    state.dataIPShortCut->cAlphaArgs(4),
     419           8 :                                                    state.dataIPShortCut->cAlphaArgs(5),
     420             :                                                    "Water Nodes");
     421             : 
     422             :             } // FlatPlateUnitsNum
     423             : 
     424             :             // Get data for ICS collector
     425           3 :             CurrentModuleParamObject = "SolarCollectorPerformance:IntegralCollectorStorage";
     426             : 
     427           4 :             for (int ICSParamNum = 1; ICSParamNum <= NumOfICSParam; ++ICSParamNum) {
     428             : 
     429           1 :                 int ParametersNum = ICSParamNum + NumOfFlatPlateParam;
     430             : 
     431           3 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     432             :                                                                          CurrentModuleParamObject,
     433             :                                                                          ICSParamNum,
     434           1 :                                                                          state.dataIPShortCut->cAlphaArgs,
     435             :                                                                          NumAlphas,
     436           1 :                                                                          state.dataIPShortCut->rNumericArgs,
     437             :                                                                          NumNumbers,
     438             :                                                                          IOStatus,
     439           1 :                                                                          state.dataIPShortCut->lNumericFieldBlanks,
     440             :                                                                          _,
     441           1 :                                                                          state.dataIPShortCut->cAlphaFieldNames,
     442           1 :                                                                          state.dataIPShortCut->cNumericFieldNames);
     443             : 
     444             :                 // Collector module parameters name
     445           1 :                 GlobalNames::VerifyUniqueInterObjectName(state,
     446           1 :                                                          state.dataSolarCollectors->UniqueParametersNames,
     447           1 :                                                          state.dataIPShortCut->cAlphaArgs(1),
     448             :                                                          CurrentModuleObject,
     449           1 :                                                          state.dataIPShortCut->cAlphaFieldNames(1),
     450             :                                                          ErrorsFound);
     451           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).Name = state.dataIPShortCut->cAlphaArgs(1);
     452             :                 // NOTE:  currently the only available choice is RectangularTank.  In the future progressive tube type will be
     453             :                 //        added
     454             :                 //                if (Util::SameString(state.dataIPShortCut->cAlphaArgs(2), "RectangularTank")) {
     455             :                 //                    state.dataSolarCollectors->Parameters(ParametersNum).ICSType_Num = TankTypeEnum::ICSRectangularTank;
     456             :                 //                } else {
     457             :                 //                    ShowSevereError(state, format("{}{} not found={}{} in {}{} ={}{}", //,
     458             :                 //                    state.dataIPShortCut->cAlphaFieldNames(2), state.dataIPShortCut->cAlphaArgs(2), //, //,
     459             :                 //                    CurrentModuleParamObject, //, state.dataSolarCollectors->Parameters(ParametersNum).Name)); ErrorsFound = true;
     460             :                 //                }
     461             :                 // NOTE:  This collector gross area is used in all the calculations.
     462           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).Area = state.dataIPShortCut->rNumericArgs(1);
     463           1 :                 if (state.dataIPShortCut->rNumericArgs(1) <= 0.0) {
     464           0 :                     ShowSevereError(state, format("{} = {}", CurrentModuleParamObject, state.dataIPShortCut->cAlphaArgs(1)));
     465           0 :                     ShowContinueError(
     466           0 :                         state, format("Illegal {} = {:.2R}", state.dataIPShortCut->cNumericFieldNames(1), state.dataIPShortCut->rNumericArgs(1)));
     467           0 :                     ShowContinueError(state, " Collector gross area must be always gretaer than zero.");
     468           0 :                     ErrorsFound = true;
     469             :                 }
     470           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).Volume = state.dataIPShortCut->rNumericArgs(2);
     471           1 :                 if (state.dataIPShortCut->rNumericArgs(2) <= 0.0) {
     472           0 :                     ShowSevereError(state, format("{} = {}", CurrentModuleParamObject, state.dataIPShortCut->cAlphaArgs(1)));
     473           0 :                     ShowContinueError(
     474           0 :                         state, format("Illegal {} = {:.2R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
     475           0 :                     ShowContinueError(state, " Collector water volume must be always gretaer than zero.");
     476           0 :                     ErrorsFound = true;
     477             :                 }
     478             :                 // Note: this value is used to calculate the heat loss through the bottom and side of the collector
     479           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).ULossBottom = state.dataIPShortCut->rNumericArgs(3);
     480           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).ULossSide = state.dataIPShortCut->rNumericArgs(4);
     481           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).AspectRatio = state.dataIPShortCut->rNumericArgs(5);
     482           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).SideHeight = state.dataIPShortCut->rNumericArgs(6);
     483           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).ThermalMass = state.dataIPShortCut->rNumericArgs(7);
     484           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).NumOfCovers = state.dataIPShortCut->rNumericArgs(8);
     485           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).CoverSpacing = state.dataIPShortCut->rNumericArgs(9);
     486             : 
     487           1 :                 if (state.dataSolarCollectors->Parameters(ParametersNum).NumOfCovers == 2) {
     488             :                     // Outer cover refractive index
     489           0 :                     state.dataSolarCollectors->Parameters(ParametersNum).RefractiveIndex[0] = state.dataIPShortCut->rNumericArgs(10);
     490             :                     // Outer cover extinction coefficient times thickness of the cover
     491           0 :                     state.dataSolarCollectors->Parameters(ParametersNum).ExtCoefTimesThickness[0] = state.dataIPShortCut->rNumericArgs(11);
     492             :                     // Outer cover Emissivity
     493           0 :                     state.dataSolarCollectors->Parameters(ParametersNum).EmissOfCover[0] = state.dataIPShortCut->rNumericArgs(12);
     494             : 
     495           0 :                     if (!state.dataIPShortCut->lNumericFieldBlanks(13) || !state.dataIPShortCut->lNumericFieldBlanks(14) ||
     496           0 :                         !state.dataIPShortCut->lNumericFieldBlanks(15)) {
     497           0 :                         state.dataSolarCollectors->Parameters(ParametersNum).RefractiveIndex[1] = state.dataIPShortCut->rNumericArgs(13);
     498           0 :                         state.dataSolarCollectors->Parameters(ParametersNum).ExtCoefTimesThickness[1] = state.dataIPShortCut->rNumericArgs(14);
     499           0 :                         state.dataSolarCollectors->Parameters(ParametersNum).EmissOfCover[1] = state.dataIPShortCut->rNumericArgs(15);
     500             :                     } else {
     501           0 :                         ShowSevereError(state, format("{} = {}", CurrentModuleParamObject, state.dataIPShortCut->cAlphaArgs(1)));
     502           0 :                         ShowContinueError(state, "Illegal input for one of the three inputs of the inner cover optical properties");
     503           0 :                         ErrorsFound = true;
     504             :                     }
     505           1 :                 } else if (state.dataSolarCollectors->Parameters(ParametersNum).NumOfCovers == 1) {
     506             :                     // Outer cover refractive index
     507           1 :                     state.dataSolarCollectors->Parameters(ParametersNum).RefractiveIndex[0] = state.dataIPShortCut->rNumericArgs(10);
     508             :                     // Outer cover extinction coefficient times thickness of the cover
     509           1 :                     state.dataSolarCollectors->Parameters(ParametersNum).ExtCoefTimesThickness[0] = state.dataIPShortCut->rNumericArgs(11);
     510             :                     // Outer cover emissivity
     511           1 :                     state.dataSolarCollectors->Parameters(ParametersNum).EmissOfCover[0] = state.dataIPShortCut->rNumericArgs(12);
     512             :                 } else {
     513           0 :                     ShowSevereError(state, format("{} = {}", CurrentModuleParamObject, state.dataIPShortCut->cAlphaArgs(1)));
     514           0 :                     ShowContinueError(
     515           0 :                         state, format("Illegal {} = {:.2R}", state.dataIPShortCut->cNumericFieldNames(8), state.dataIPShortCut->rNumericArgs(8)));
     516           0 :                     ErrorsFound = true;
     517             :                 }
     518             :                 // Solar absorptance of the absorber plate
     519           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).AbsorOfAbsPlate = state.dataIPShortCut->rNumericArgs(16);
     520             :                 // thermal emmissivity of the absorber plate
     521           1 :                 state.dataSolarCollectors->Parameters(ParametersNum).EmissOfAbsPlate = state.dataIPShortCut->rNumericArgs(17);
     522             : 
     523             :             } // end of ParametersNum
     524             : 
     525           3 :             if (ErrorsFound) ShowFatalError(state, format("Errors in {} input.", CurrentModuleParamObject));
     526             : 
     527           3 :             CurrentModuleObject = "SolarCollector:IntegralCollectorStorage";
     528             : 
     529           5 :             for (int ICSUnitsNum = 1; ICSUnitsNum <= NumOfICSUnits; ++ICSUnitsNum) {
     530             : 
     531           2 :                 int CollectorNum = ICSUnitsNum + NumFlatPlateUnits;
     532             : 
     533           6 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     534             :                                                                          CurrentModuleObject,
     535             :                                                                          ICSUnitsNum,
     536           2 :                                                                          state.dataIPShortCut->cAlphaArgs,
     537             :                                                                          NumAlphas,
     538           2 :                                                                          state.dataIPShortCut->rNumericArgs,
     539             :                                                                          NumNumbers,
     540             :                                                                          IOStatus,
     541           2 :                                                                          state.dataIPShortCut->lNumericFieldBlanks,
     542             :                                                                          lAlphaBlanks,
     543           2 :                                                                          state.dataIPShortCut->cAlphaFieldNames,
     544           2 :                                                                          state.dataIPShortCut->cNumericFieldNames);
     545             : 
     546             :                 // Collector name
     547           2 :                 GlobalNames::VerifyUniqueInterObjectName(state,
     548           2 :                                                          state.dataSolarCollectors->UniqueCollectorNames,
     549           2 :                                                          state.dataIPShortCut->cAlphaArgs(1),
     550             :                                                          CurrentModuleObject,
     551           2 :                                                          state.dataIPShortCut->cAlphaFieldNames(1),
     552             :                                                          ErrorsFound);
     553           2 :                 state.dataSolarCollectors->Collector(CollectorNum).Name = state.dataIPShortCut->cAlphaArgs(1);
     554           2 :                 state.dataSolarCollectors->Collector(CollectorNum).Type =
     555             :                     DataPlant::PlantEquipmentType::SolarCollectorICS; // parameter assigned in DataPlant
     556             : 
     557           2 :                 state.dataSolarCollectors->Collector(CollectorNum).InitICS = true;
     558             : 
     559             :                 // Get parameters object
     560           2 :                 int ParametersNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataSolarCollectors->Parameters);
     561             : 
     562           2 :                 if (ParametersNum == 0) {
     563           0 :                     ShowSevereError(state,
     564           0 :                                     format("{} = {}: {} object called {} not found.",
     565             :                                            CurrentModuleObject,
     566           0 :                                            state.dataIPShortCut->cAlphaArgs(1),
     567             :                                            CurrentModuleParamObject,
     568           0 :                                            state.dataIPShortCut->cAlphaArgs(2)));
     569           0 :                     ErrorsFound = true;
     570             :                 } else {
     571           2 :                     state.dataSolarCollectors->Collector(CollectorNum).Parameters = ParametersNum;
     572             :                 }
     573             : 
     574           2 :                 if (ParametersNum > 0) {
     575             :                     // Calculate constant collector parameters only once
     576           2 :                     Real64 Perimeter = 2.0 * std::sqrt(state.dataSolarCollectors->Parameters(ParametersNum).Area) *
     577           2 :                                        (std::sqrt(state.dataSolarCollectors->Parameters(ParametersNum).AspectRatio) +
     578           2 :                                         1.0 / std::sqrt(state.dataSolarCollectors->Parameters(ParametersNum).AspectRatio));
     579           2 :                     state.dataSolarCollectors->Collector(CollectorNum).Length = std::sqrt(
     580           2 :                         state.dataSolarCollectors->Parameters(ParametersNum).Area / state.dataSolarCollectors->Parameters(ParametersNum).AspectRatio);
     581             : 
     582             :                     // calculate the collector side heat transfer area and loss coefficient
     583           2 :                     state.dataSolarCollectors->Collector(CollectorNum).Area = state.dataSolarCollectors->Parameters(ParametersNum).Area;
     584           2 :                     state.dataSolarCollectors->Collector(CollectorNum).Volume = state.dataSolarCollectors->Parameters(ParametersNum).Volume;
     585           2 :                     state.dataSolarCollectors->Collector(CollectorNum).SideArea =
     586           2 :                         Perimeter * state.dataSolarCollectors->Parameters(ParametersNum).SideHeight;
     587           2 :                     state.dataSolarCollectors->Collector(CollectorNum).AreaRatio =
     588           2 :                         state.dataSolarCollectors->Collector(CollectorNum).SideArea / state.dataSolarCollectors->Collector(CollectorNum).Area;
     589             :                 }
     590             :                 // Get surface object
     591           2 :                 int SurfNum = Util::FindItemInList(state.dataIPShortCut->cAlphaArgs(3), state.dataSurface->Surface);
     592             : 
     593           2 :                 if (SurfNum == 0) {
     594           0 :                     ShowSevereError(state,
     595           0 :                                     format("{} = {}:  Surface {} not found.",
     596             :                                            CurrentModuleObject,
     597           0 :                                            state.dataIPShortCut->cAlphaArgs(1),
     598           0 :                                            state.dataIPShortCut->cAlphaArgs(3)));
     599           0 :                     ErrorsFound = true;
     600           0 :                     continue; // avoid hard crash
     601             :                 } else {
     602             : 
     603           2 :                     if (!state.dataSurface->Surface(SurfNum).ExtSolar) {
     604           0 :                         ShowWarningError(state,
     605           0 :                                          format("{} = {}:  Surface {} is not exposed to exterior radiation.",
     606             :                                                 CurrentModuleObject,
     607           0 :                                                 state.dataIPShortCut->cAlphaArgs(1),
     608           0 :                                                 state.dataIPShortCut->cAlphaArgs(3)));
     609             :                     }
     610             : 
     611             :                     // check surface orientation, warn if upside down
     612           2 :                     if ((state.dataSurface->Surface(SurfNum).Tilt < -95.0) || (state.dataSurface->Surface(SurfNum).Tilt > 95.0)) {
     613           0 :                         ShowWarningError(state,
     614           0 :                                          format("Suspected input problem with {} = {}",
     615           0 :                                                 state.dataIPShortCut->cAlphaFieldNames(3),
     616           0 :                                                 state.dataIPShortCut->cAlphaArgs(3)));
     617           0 :                         ShowContinueError(
     618           0 :                             state, format("Entered in {} = {}", state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     619           0 :                         ShowContinueError(state, "Surface used for solar collector faces down");
     620           0 :                         ShowContinueError(
     621             :                             state,
     622           0 :                             format("Surface tilt angle (degrees from ground outward normal) = {:.2R}", state.dataSurface->Surface(SurfNum).Tilt));
     623             :                     }
     624             : 
     625             :                     // Check to make sure other solar collectors are not using the same surface
     626             :                     // NOTE:  Must search over all solar collector types
     627           6 :                     for (int CollectorNum2 = 1; CollectorNum2 <= state.dataSolarCollectors->NumOfCollectors; ++CollectorNum2) {
     628           4 :                         if (state.dataSolarCollectors->Collector(CollectorNum2).Surface == SurfNum) {
     629           0 :                             ShowSevereError(state,
     630           0 :                                             format("{} = {}:  Surface {} is referenced by more than one {}",
     631             :                                                    CurrentModuleObject,
     632           0 :                                                    state.dataIPShortCut->cAlphaArgs(1),
     633           0 :                                                    state.dataIPShortCut->cAlphaArgs(3),
     634             :                                                    CurrentModuleObject));
     635           0 :                             ErrorsFound = true;
     636           0 :                             break;
     637             :                         }
     638             :                     } // ICSNum2
     639             : 
     640           2 :                     state.dataSolarCollectors->Collector(CollectorNum).Surface = SurfNum;
     641             :                 }
     642             : 
     643             :                 // Give warning if surface area and gross area do not match within tolerance
     644           4 :                 if (SurfNum > 0 && ParametersNum > 0 && state.dataSolarCollectors->Parameters(ParametersNum).Area > 0.0 &&
     645           2 :                     std::abs(state.dataSolarCollectors->Parameters(ParametersNum).Area - state.dataSurface->Surface(SurfNum).Area) /
     646           2 :                             state.dataSurface->Surface(SurfNum).Area >
     647             :                         0.01) {
     648             : 
     649           0 :                     ShowWarningError(state, format("{} = {}: ", CurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
     650           0 :                     ShowContinueError(state, "Gross area of solar collector parameters and surface object differ by more than 1%.");
     651           0 :                     ShowContinueError(state, "Gross collector area is always used in the calculation.  Modify the surface ");
     652           0 :                     ShowContinueError(state, "coordinates to match its area with collector gross area. Otherwise, the underlying ");
     653           0 :                     ShowContinueError(state, "surface is assumed to be fully shaded when it is not.");
     654             :                 }
     655             : 
     656           2 :                 state.dataSolarCollectors->Collector(CollectorNum).BCType = state.dataIPShortCut->cAlphaArgs(4);
     657           2 :                 if (Util::SameString(state.dataIPShortCut->cAlphaArgs(4), "AmbientAir")) {
     658           0 :                     state.dataSolarCollectors->Collector(CollectorNum).OSCMName = "";
     659           2 :                 } else if (Util::SameString(state.dataIPShortCut->cAlphaArgs(4), "OtherSideConditionsModel")) {
     660           2 :                     state.dataSolarCollectors->Collector(CollectorNum).OSCMName = state.dataIPShortCut->cAlphaArgs(5);
     661           2 :                     state.dataSolarCollectors->Collector(CollectorNum).OSCM_ON = true;
     662           2 :                     int Found = Util::FindItemInList(state.dataSolarCollectors->Collector(CollectorNum).OSCMName, state.dataSurface->OSCM);
     663           2 :                     if (Found == 0) {
     664           0 :                         ShowSevereError(state,
     665           0 :                                         format("{} not found={} in {} ={}",
     666           0 :                                                state.dataIPShortCut->cAlphaFieldNames(5),
     667           0 :                                                state.dataSolarCollectors->Collector(CollectorNum).OSCMName,
     668             :                                                CurrentModuleObject,
     669           0 :                                                state.dataSolarCollectors->Collector(CollectorNum).Name));
     670           0 :                         ErrorsFound = true;
     671             :                     }
     672             :                 } else {
     673           0 :                     ShowSevereError(state,
     674           0 :                                     format("{} not found={} in {} ={}",
     675           0 :                                            state.dataIPShortCut->cAlphaFieldNames(5),
     676           0 :                                            state.dataSolarCollectors->Collector(CollectorNum).BCType,
     677             :                                            CurrentModuleObject,
     678           0 :                                            state.dataSolarCollectors->Collector(CollectorNum).Name));
     679           0 :                     ErrorsFound = true;
     680             :                 }
     681             : 
     682           2 :                 if (state.dataSolarCollectors->Collector(CollectorNum).OSCM_ON) {
     683             :                     // get index of ventilated cavity object
     684           2 :                     int VentCavIndex = 0;
     685           2 :                     SolarCollectors::CollectorData::GetExtVentedCavityIndex(state, SurfNum, VentCavIndex);
     686           2 :                     state.dataSolarCollectors->Collector(CollectorNum).VentCavIndex = VentCavIndex;
     687             :                 }
     688             : 
     689           2 :                 state.dataSolarCollectors->Collector(CollectorNum).InletNode =
     690           4 :                     NodeInputManager::GetOnlySingleNode(state,
     691           2 :                                                         state.dataIPShortCut->cAlphaArgs(6),
     692             :                                                         ErrorsFound,
     693             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorIntegralCollectorStorage,
     694           2 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     695             :                                                         DataLoopNode::NodeFluidType::Water,
     696             :                                                         DataLoopNode::ConnectionType::Inlet,
     697             :                                                         NodeInputManager::CompFluidStream::Primary,
     698             :                                                         DataLoopNode::ObjectIsNotParent);
     699           2 :                 state.dataSolarCollectors->Collector(CollectorNum).OutletNode =
     700           4 :                     NodeInputManager::GetOnlySingleNode(state,
     701           2 :                                                         state.dataIPShortCut->cAlphaArgs(7),
     702             :                                                         ErrorsFound,
     703             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorIntegralCollectorStorage,
     704           2 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     705             :                                                         DataLoopNode::NodeFluidType::Water,
     706             :                                                         DataLoopNode::ConnectionType::Outlet,
     707             :                                                         NodeInputManager::CompFluidStream::Primary,
     708             :                                                         DataLoopNode::ObjectIsNotParent);
     709             : 
     710           2 :                 if (NumNumbers > 0) {
     711           2 :                     state.dataSolarCollectors->Collector(CollectorNum).VolFlowRateMax =
     712           2 :                         state.dataIPShortCut->rNumericArgs(1); // Max volumetric flow rate used for plant sizing calculation
     713             :                 } else {
     714           0 :                     state.dataSolarCollectors->Collector(CollectorNum).VolFlowRateMax =
     715             :                         0.0; // Max vol flow rate is not specified; no flow for plant sizing calculation
     716           0 :                     state.dataSolarCollectors->Collector(CollectorNum).MassFlowRateMax =
     717             :                         999999.9; // But...set a very high value so that it demands as much as possible
     718             :                 }
     719             : 
     720           4 :                 BranchNodeConnections::TestCompSet(state,
     721             :                                                    CurrentModuleObject,
     722           2 :                                                    state.dataIPShortCut->cAlphaArgs(1),
     723           2 :                                                    state.dataIPShortCut->cAlphaArgs(6),
     724           2 :                                                    state.dataIPShortCut->cAlphaArgs(7),
     725             :                                                    "Water Nodes");
     726             : 
     727             :             } // ICSNum
     728             : 
     729           3 :             if (ErrorsFound) ShowFatalError(state, format("Errors in {} input.", CurrentModuleObject));
     730             :         }
     731           3 :     }
     732             : 
     733          10 :     void CollectorData::setupOutputVars(EnergyPlusData &state)
     734             :     {
     735          10 :         if (this->Type == DataPlant::PlantEquipmentType::SolarCollectorFlatPlate) {
     736             :             // Setup report variables
     737          16 :             SetupOutputVariable(state,
     738             :                                 "Solar Collector Incident Angle Modifier",
     739             :                                 Constant::Units::None,
     740           8 :                                 this->IncidentAngleModifier,
     741             :                                 OutputProcessor::TimeStepType::System,
     742             :                                 OutputProcessor::StoreType::Average,
     743           8 :                                 this->Name);
     744             : 
     745          16 :             SetupOutputVariable(state,
     746             :                                 "Solar Collector Efficiency",
     747             :                                 Constant::Units::None,
     748           8 :                                 this->Efficiency,
     749             :                                 OutputProcessor::TimeStepType::System,
     750             :                                 OutputProcessor::StoreType::Average,
     751           8 :                                 this->Name);
     752             : 
     753          16 :             SetupOutputVariable(state,
     754             :                                 "Solar Collector Heat Transfer Rate",
     755             :                                 Constant::Units::W,
     756           8 :                                 this->Power,
     757             :                                 OutputProcessor::TimeStepType::System,
     758             :                                 OutputProcessor::StoreType::Average,
     759           8 :                                 this->Name);
     760             : 
     761          16 :             SetupOutputVariable(state,
     762             :                                 "Solar Collector Heat Gain Rate",
     763             :                                 Constant::Units::W,
     764           8 :                                 this->HeatGain,
     765             :                                 OutputProcessor::TimeStepType::System,
     766             :                                 OutputProcessor::StoreType::Average,
     767           8 :                                 this->Name);
     768             : 
     769          16 :             SetupOutputVariable(state,
     770             :                                 "Solar Collector Heat Loss Rate",
     771             :                                 Constant::Units::W,
     772           8 :                                 this->HeatLoss,
     773             :                                 OutputProcessor::TimeStepType::System,
     774             :                                 OutputProcessor::StoreType::Average,
     775           8 :                                 this->Name);
     776             : 
     777          16 :             SetupOutputVariable(state,
     778             :                                 "Solar Collector Heat Transfer Energy",
     779             :                                 Constant::Units::J,
     780           8 :                                 this->Energy,
     781             :                                 OutputProcessor::TimeStepType::System,
     782             :                                 OutputProcessor::StoreType::Sum,
     783           8 :                                 this->Name,
     784             :                                 Constant::eResource::SolarWater,
     785             :                                 OutputProcessor::Group::Plant,
     786             :                                 OutputProcessor::EndUseCat::HeatProduced);
     787           2 :         } else if (this->Type == DataPlant::PlantEquipmentType::SolarCollectorICS) {
     788             : 
     789           4 :             SetupOutputVariable(state,
     790             :                                 "Solar Collector Transmittance Absorptance Product",
     791             :                                 Constant::Units::None,
     792           2 :                                 this->TauAlpha,
     793             :                                 OutputProcessor::TimeStepType::System,
     794             :                                 OutputProcessor::StoreType::Average,
     795           2 :                                 this->Name);
     796             : 
     797           4 :             SetupOutputVariable(state,
     798             :                                 "Solar Collector Overall Top Heat Loss Coefficient",
     799             :                                 Constant::Units::W_m2C,
     800           2 :                                 this->UTopLoss,
     801             :                                 OutputProcessor::TimeStepType::System,
     802             :                                 OutputProcessor::StoreType::Average,
     803           2 :                                 this->Name);
     804             : 
     805           4 :             SetupOutputVariable(state,
     806             :                                 "Solar Collector Absorber Plate Temperature",
     807             :                                 Constant::Units::C,
     808           2 :                                 this->TempOfAbsPlate,
     809             :                                 OutputProcessor::TimeStepType::System,
     810             :                                 OutputProcessor::StoreType::Average,
     811           2 :                                 this->Name);
     812             : 
     813           4 :             SetupOutputVariable(state,
     814             :                                 "Solar Collector Storage Water Temperature",
     815             :                                 Constant::Units::C,
     816           2 :                                 this->TempOfWater,
     817             :                                 OutputProcessor::TimeStepType::System,
     818             :                                 OutputProcessor::StoreType::Average,
     819           2 :                                 this->Name);
     820             : 
     821           4 :             SetupOutputVariable(state,
     822             :                                 "Solar Collector Thermal Efficiency",
     823             :                                 Constant::Units::None,
     824           2 :                                 this->Efficiency,
     825             :                                 OutputProcessor::TimeStepType::System,
     826             :                                 OutputProcessor::StoreType::Average,
     827           2 :                                 this->Name);
     828             : 
     829           4 :             SetupOutputVariable(state,
     830             :                                 "Solar Collector Storage Heat Transfer Rate",
     831             :                                 Constant::Units::W,
     832           2 :                                 this->StoredHeatRate,
     833             :                                 OutputProcessor::TimeStepType::System,
     834             :                                 OutputProcessor::StoreType::Average,
     835           2 :                                 this->Name);
     836             : 
     837           4 :             SetupOutputVariable(state,
     838             :                                 "Solar Collector Storage Heat Transfer Energy",
     839             :                                 Constant::Units::J,
     840           2 :                                 this->StoredHeatEnergy,
     841             :                                 OutputProcessor::TimeStepType::System,
     842             :                                 OutputProcessor::StoreType::Sum,
     843           2 :                                 this->Name,
     844             :                                 Constant::eResource::SolarWater,
     845             :                                 OutputProcessor::Group::Plant,
     846             :                                 OutputProcessor::EndUseCat::HeatProduced);
     847             : 
     848           4 :             SetupOutputVariable(state,
     849             :                                 "Solar Collector Skin Heat Transfer Rate",
     850             :                                 Constant::Units::W,
     851           2 :                                 this->SkinHeatLossRate,
     852             :                                 OutputProcessor::TimeStepType::System,
     853             :                                 OutputProcessor::StoreType::Average,
     854           2 :                                 this->Name);
     855             : 
     856           4 :             SetupOutputVariable(state,
     857             :                                 "Solar Collector Skin Heat Transfer Energy",
     858             :                                 Constant::Units::J,
     859           2 :                                 this->CollHeatLossEnergy,
     860             :                                 OutputProcessor::TimeStepType::System,
     861             :                                 OutputProcessor::StoreType::Sum,
     862           2 :                                 this->Name,
     863             :                                 Constant::eResource::SolarWater,
     864             :                                 OutputProcessor::Group::Plant,
     865             :                                 OutputProcessor::EndUseCat::HeatProduced);
     866             : 
     867           4 :             SetupOutputVariable(state,
     868             :                                 "Solar Collector Heat Transfer Rate",
     869             :                                 Constant::Units::W,
     870           2 :                                 this->HeatRate,
     871             :                                 OutputProcessor::TimeStepType::System,
     872             :                                 OutputProcessor::StoreType::Average,
     873           2 :                                 this->Name);
     874             : 
     875           4 :             SetupOutputVariable(state,
     876             :                                 "Solar Collector Heat Transfer Energy",
     877             :                                 Constant::Units::J,
     878           2 :                                 this->HeatEnergy,
     879             :                                 OutputProcessor::TimeStepType::System,
     880             :                                 OutputProcessor::StoreType::Sum,
     881           2 :                                 this->Name,
     882             :                                 Constant::eResource::SolarWater,
     883             :                                 OutputProcessor::Group::Plant,
     884             :                                 OutputProcessor::EndUseCat::HeatProduced);
     885             :         }
     886          10 :     }
     887             : 
     888      121166 :     void CollectorData::simulate(EnergyPlusData &state,
     889             :                                  [[maybe_unused]] const PlantLocation &calledFromLocation,
     890             :                                  [[maybe_unused]] bool const FirstHVACIteration,
     891             :                                  [[maybe_unused]] Real64 &CurLoad,
     892             :                                  [[maybe_unused]] bool const RunFlag)
     893             :     {
     894      121166 :         this->initialize(state);
     895             : 
     896      121166 :         switch (this->Type) {
     897             :             // Select and CALL models based on collector type
     898       68788 :         case DataPlant::PlantEquipmentType::SolarCollectorFlatPlate: {
     899       68788 :             this->CalcSolarCollector(state);
     900       68788 :         } break;
     901       52378 :         case DataPlant::PlantEquipmentType::SolarCollectorICS: {
     902       52378 :             this->CalcICSSolarCollector(state);
     903       52378 :         } break;
     904           0 :         default: {
     905             :             assert(false); // LCOV_EXCL_LINE
     906             :         } break;
     907             :         }
     908             : 
     909      121166 :         this->update(state);
     910             : 
     911      121166 :         this->report(state);
     912      121166 :     }
     913             : 
     914      121166 :     void CollectorData::initialize(EnergyPlusData &state)
     915             :     {
     916             : 
     917             :         // SUBROUTINE INFORMATION:
     918             :         //       AUTHOR         Peter Graham Ellis
     919             :         //       DATE WRITTEN   January 2004
     920             :         //       MODIFIED       na
     921             :         //       RE-ENGINEERED  na
     922             : 
     923             :         // PURPOSE OF THIS SUBROUTINE:
     924             :         // Initializes the solar collector object during the plant simulation.
     925             : 
     926             :         // METHODOLOGY EMPLOYED:
     927             :         // Inlet and outlet nodes are initialized.  The maximum collector flow rate is requested.
     928             : 
     929             :         static constexpr std::string_view RoutineName("InitSolarCollector");
     930      121166 :         Real64 constexpr BigNumber(9999.9); // Component desired mass flow rate
     931             : 
     932      121166 :         if (!state.dataGlobal->SysSizingCalc && this->InitSizing) {
     933          10 :             PlantUtilities::RegisterPlantCompDesignFlow(state, this->InletNode, this->VolFlowRateMax);
     934          10 :             this->InitSizing = false;
     935             :         }
     936             : 
     937      121166 :         if (state.dataGlobal->BeginEnvrnFlag && this->Init) {
     938             :             // Clear node initial conditions
     939          62 :             if (this->VolFlowRateMax > 0) {
     940          62 :                 Real64 rho = FluidProperties::GetDensityGlycol(state,
     941          62 :                                                                state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
     942             :                                                                Constant::InitConvTemp,
     943          62 :                                                                state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
     944             :                                                                RoutineName);
     945             : 
     946          62 :                 this->MassFlowRateMax = this->VolFlowRateMax * rho;
     947             :             } else {
     948           0 :                 this->MassFlowRateMax = BigNumber;
     949             :             }
     950             : 
     951          62 :             PlantUtilities::InitComponentNodes(state, 0.0, this->MassFlowRateMax, this->InletNode, this->OutletNode);
     952             : 
     953          62 :             this->Init = false;
     954             : 
     955          62 :             if (this->InitICS) {
     956          12 :                 this->TempOfWater = 20.0;
     957          12 :                 this->SavedTempOfWater = this->TempOfWater;
     958          12 :                 this->SavedTempOfAbsPlate = this->TempOfWater;
     959          12 :                 this->TempOfAbsPlate = this->TempOfWater;
     960          12 :                 this->TempOfInnerCover = this->TempOfWater;
     961          12 :                 this->TempOfOuterCover = this->TempOfWater;
     962          12 :                 this->SavedTempOfInnerCover = this->TempOfWater;
     963          12 :                 this->SavedTempOfOuterCover = this->TempOfWater;
     964          12 :                 this->SavedTempCollectorOSCM = this->TempOfWater;
     965             :             }
     966             :         }
     967             : 
     968      121166 :         if (!state.dataGlobal->BeginEnvrnFlag) this->Init = true;
     969             : 
     970      121166 :         if (this->SetDiffRadFlag && this->InitICS) {
     971             :             // calculates the sky and ground reflective diffuse radiation optical properties (only one time)
     972           2 :             int SurfNum = this->Surface;
     973           2 :             int ParamNum = this->Parameters;
     974             : 
     975           2 :             this->Tilt = state.dataSurface->Surface(SurfNum).Tilt;
     976           2 :             this->TiltR2V = std::abs(90.0 - Tilt);
     977           2 :             this->CosTilt = std::cos(Tilt * Constant::DegToRadians);
     978           2 :             this->SinTilt = std::sin(1.8 * Tilt * Constant::DegToRadians);
     979             : 
     980             :             // Diffuse reflectance of the cover for solar radiation diffusely reflected back from the absober
     981             :             // plate to the cover.  The diffuse solar radiation reflected back from the absober plate to the
     982             :             // cover is represented by the 60 degree equivalent incident angle.  This diffuse reflectance is
     983             :             // used to calculate the transmittance - absorptance product (Duffie and Beckman, 1991)
     984           2 :             Real64 Theta = 60.0 * Constant::DegToRadians;
     985           2 :             Real64 TransSys = 0.0;
     986           2 :             Real64 RefSys = 0.0;
     987           2 :             Real64 AbsCover1 = 0.0;
     988           2 :             Real64 AbsCover2 = 0.0;
     989           2 :             Real64 RefSysDiffuse = 0.0;
     990           2 :             this->CalcTransRefAbsOfCover(state, Theta, TransSys, RefSys, AbsCover1, AbsCover2, true, RefSysDiffuse);
     991           2 :             this->RefDiffInnerCover = RefSysDiffuse;
     992             : 
     993             :             // transmittance-absorptance product normal incident:
     994           2 :             Theta = 0.0;
     995           2 :             this->CalcTransRefAbsOfCover(state, Theta, TransSys, RefSys, AbsCover1, AbsCover2);
     996             : 
     997             :             // transmittance-absorptance product for sky diffuse radiation.  Uses equivalent incident angle
     998             :             // of sky radiation (radians), and is calculated according to Brandemuehl and Beckman (1980):
     999           2 :             Theta = (59.68 - 0.1388 * Tilt + 0.001497 * pow_2(Tilt)) * Constant::DegToRadians;
    1000           2 :             this->CalcTransRefAbsOfCover(state, Theta, TransSys, RefSys, AbsCover1, AbsCover2);
    1001           2 :             this->TauAlphaSkyDiffuse = TransSys * state.dataSolarCollectors->Parameters(ParamNum).AbsorOfAbsPlate /
    1002           2 :                                        (1.0 - (1.0 - state.dataSolarCollectors->Parameters(ParamNum).AbsorOfAbsPlate) * this->RefDiffInnerCover);
    1003           2 :             this->CoversAbsSkyDiffuse[0] = AbsCover1;
    1004           2 :             this->CoversAbsSkyDiffuse[1] = AbsCover2;
    1005             : 
    1006             :             // transmittance-absorptance product for ground diffuse radiation.  Uses equivalent incident angle
    1007             :             // of ground radiation (radians), and is calculated according to Brandemuehl and Beckman (1980):
    1008           2 :             Theta = (90.0 - 0.5788 * Tilt + 0.002693 * pow_2(Tilt)) * Constant::DegToRadians;
    1009           2 :             this->CalcTransRefAbsOfCover(state, Theta, TransSys, RefSys, AbsCover1, AbsCover2);
    1010           2 :             this->TauAlphaGndDiffuse = TransSys * state.dataSolarCollectors->Parameters(ParamNum).AbsorOfAbsPlate /
    1011           2 :                                        (1.0 - (1.0 - state.dataSolarCollectors->Parameters(ParamNum).AbsorOfAbsPlate) * this->RefDiffInnerCover);
    1012           2 :             this->CoversAbsGndDiffuse[0] = AbsCover1;
    1013           2 :             this->CoversAbsGndDiffuse[1] = AbsCover2;
    1014             : 
    1015           2 :             this->SetDiffRadFlag = false;
    1016             :         }
    1017             : 
    1018      121166 :         this->InletTemp = state.dataLoopNodes->Node(this->InletNode).Temp;
    1019             : 
    1020      121166 :         this->MassFlowRate = this->MassFlowRateMax;
    1021             : 
    1022             :         // Request the mass flow rate from the plant component flow utility routine
    1023      121166 :         PlantUtilities::SetComponentFlowRate(state, this->MassFlowRate, this->InletNode, this->OutletNode, this->plantLoc);
    1024             : 
    1025      121166 :         if (this->InitICS) {
    1026             : 
    1027             :             Real64 timeElapsed =
    1028       52378 :                 state.dataGlobal->HourOfDay + state.dataGlobal->TimeStep * state.dataGlobal->TimeStepZone + state.dataHVACGlobal->SysTimeElapsed;
    1029             : 
    1030       52378 :             if (this->TimeElapsed != timeElapsed) {
    1031             :                 // The simulation has advanced to the next system timestep.  Save conditions from the end of the previous
    1032             :                 // system timestep for use as initial condition of each iteration that does not advance system timestep.
    1033        5716 :                 this->SavedTempOfWater = this->TempOfWater;
    1034        5716 :                 this->SavedTempOfAbsPlate = this->TempOfAbsPlate;
    1035        5716 :                 this->SavedTempOfInnerCover = this->TempOfInnerCover;
    1036        5716 :                 this->SavedTempOfOuterCover = this->TempOfOuterCover;
    1037        5716 :                 if (this->OSCM_ON) {
    1038        5716 :                     this->SavedTempCollectorOSCM = state.dataHeatBal->ExtVentedCavity(this->VentCavIndex).Tbaffle;
    1039             :                 }
    1040        5716 :                 this->TimeElapsed = timeElapsed;
    1041             :             }
    1042             :         }
    1043      121166 :     }
    1044             : 
    1045       68788 :     void CollectorData::CalcSolarCollector(EnergyPlusData &state)
    1046             :     {
    1047             : 
    1048             :         // SUBROUTINE INFORMATION:
    1049             :         //       AUTHOR         Peter Graham Ellis
    1050             :         //       DATE WRITTEN   January 2004
    1051             :         //       MODIFIED       na
    1052             :         //       RE-ENGINEERED  na
    1053             : 
    1054             :         // PURPOSE OF THIS SUBROUTINE:
    1055             :         // Calculates the heat gain (or loss), outlet temperature, and solar energy conversion efficiency for a flat-plate
    1056             :         // solar collector when there is a fluid flow.  For the no flow condition, the fluid stagnation temperature is
    1057             :         // calculated as the outlet temperature.  Glazed and unglazed collectors are both handled.
    1058             : 
    1059             :         // METHODOLOGY EMPLOYED:
    1060             :         // Calculation is performed using the methodology described in the ASHRAE standards and references below.  Measured
    1061             :         // collector performance coefficients (available from the Solar Rating & Certification Corporation, for example)
    1062             :         // are modified from the test conditions to match the actual optical (incident angle modifier) and thermal (flow rate
    1063             :         // modifier) conditions.  Water is assumed to be the heat transfer fluid.
    1064             : 
    1065             :         // REFERENCES:
    1066             :         // ASHRAE Standard 93-1986 (RA 91), "Methods of Testing to Determine the Thermal Performance of Solar Collectors".
    1067             :         // ASHRAE Standard 96-1980 (RA 89), "Methods of Testing to Determine the Thermal Performance of Unglazed Flat-Plate
    1068             :         //   Liquid-Type Solar Collectors".
    1069             :         // Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, Second Edition.  Wiley-Interscience:
    1070             :         //   New York (1991).
    1071             : 
    1072             :         // NOTES:
    1073             :         // This subroutine has been validated against the TRNSYS Type 1 flat-plate solar collector module.  Results are
    1074             :         // identical except for slight differences at extreme incident angles (>80 degrees) and extreme surface tilts (<20
    1075             :         // degrees).  The differences are due to the fact that Type 1 does not prevent the *component* incident angle
    1076             :         // modifiers from being less than zero.  There is an effect on the net incident angle modifier if one or more
    1077             :         // components are less than zero but the net adds up to greater than zero.  The EnergyPlus subroutine, on the other
    1078             :         // hand, requires each component incident angle modifier always to be greater than zero.
    1079             : 
    1080             :         static constexpr std::string_view RoutineName("CalcSolarCollector");
    1081       68788 :         Real64 efficiency = 0.0; // Thermal efficiency of solar energy conversion
    1082             : 
    1083       68788 :         int SurfNum = this->Surface;
    1084       68788 :         int ParamNum = this->Parameters;
    1085             :         Real64 incidentAngleModifier; // Net incident angle modifier combining beam, sky, and ground radiation
    1086             : 
    1087             :         // Calculate incident angle modifier
    1088       68788 :         if (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) > 0.0) {
    1089             :             // Equivalent incident angle of sky radiation (radians)
    1090       21376 :             Real64 ThetaBeam = std::acos(state.dataHeatBal->SurfCosIncidenceAngle(SurfNum));
    1091             : 
    1092             :             // Calculate equivalent incident angles for sky and ground radiation according to Brandemuehl and Beckman (1980)
    1093             :             // Surface tilt angle (degrees)
    1094       21376 :             Real64 tilt = state.dataSurface->Surface(SurfNum).Tilt;
    1095             : 
    1096             :             // Equivalent incident angle of sky radiation (radians)
    1097       21376 :             Real64 ThetaSky = (59.68 - 0.1388 * tilt + 0.001497 * pow_2(tilt)) * Constant::DegToRadians;
    1098             : 
    1099             :             // Equivalent incident angle of ground radiation (radians)
    1100       21376 :             Real64 ThetaGnd = (90.0 - 0.5788 * tilt + 0.002693 * pow_2(tilt)) * Constant::DegToRadians;
    1101             : 
    1102       21376 :             incidentAngleModifier =
    1103       21376 :                 (state.dataHeatBal->SurfQRadSWOutIncidentBeam(SurfNum) * state.dataSolarCollectors->Parameters(ParamNum).IAM(state, ThetaBeam) +
    1104       21376 :                  state.dataHeatBal->SurfQRadSWOutIncidentSkyDiffuse(SurfNum) * state.dataSolarCollectors->Parameters(ParamNum).IAM(state, ThetaSky) +
    1105       21376 :                  state.dataHeatBal->SurfQRadSWOutIncidentGndDiffuse(SurfNum) * state.dataSolarCollectors->Parameters(ParamNum).IAM(state, ThetaGnd)) /
    1106       21376 :                 state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
    1107             :         } else {
    1108       47412 :             incidentAngleModifier = 0.0;
    1109             :         }
    1110             : 
    1111             :         // Inlet temperature from plant (C)
    1112       68788 :         Real64 inletTemp = this->InletTemp;
    1113             : 
    1114             :         // Mass flow rate through collector (kg/s)
    1115       68788 :         Real64 massFlowRate = this->MassFlowRate;
    1116             : 
    1117             :         // Specific heat of collector fluid (J/kg-K)
    1118       68788 :         Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state,
    1119       68788 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    1120             :                                                            inletTemp,
    1121       68788 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    1122             :                                                            RoutineName);
    1123             : 
    1124             :         // Gross area of collector (m2)
    1125       68788 :         Real64 area = state.dataSurface->Surface(SurfNum).Area;
    1126             : 
    1127             :         // = MassFlowRate * Cp / Area
    1128       68788 :         Real64 mCpA = massFlowRate * Cp / area;
    1129             : 
    1130             :         // = MassFlowRateTest * Cp / Area (tested area)
    1131             :         Real64 mCpATest =
    1132       68788 :             state.dataSolarCollectors->Parameters(ParamNum).TestMassFlowRate * Cp / state.dataSolarCollectors->Parameters(this->Parameters).Area;
    1133             : 
    1134       68788 :         int Iteration = 1;
    1135             : 
    1136             :         // Outlet temperature or stagnation temperature in the collector (C)
    1137       68788 :         Real64 outletTemp = 0.0;
    1138             : 
    1139             :         // Outlet temperature saved from previous iteration for convergence check (C)
    1140       68788 :         Real64 OutletTempPrev = 999.9; // Set to a ridiculous number so that DO loop runs at least once
    1141             : 
    1142             :         // Heat gain or loss to collector fluid (W)
    1143       68788 :         Real64 Q = 0.0;
    1144             : 
    1145       68788 :         while (std::abs(outletTemp - OutletTempPrev) > state.dataHeatBal->TempConvergTol) { // Check for temperature convergence
    1146             : 
    1147       68788 :             OutletTempPrev = outletTemp; // Save previous outlet temperature
    1148             : 
    1149             :             // Modifier for test correlation type:  INLET, AVERAGE, or OUTLET
    1150       68788 :             Real64 TestTypeMod = 0.0;
    1151             : 
    1152             :             // FR * ULoss "prime" for test conditions = (eff1 + eff2 * deltaT)
    1153       68788 :             Real64 FRULpTest = 0.0;
    1154             : 
    1155             :             // Modify coefficients depending on test correlation type
    1156       68788 :             switch (state.dataSolarCollectors->Parameters(ParamNum).TestType) {
    1157       68788 :             case TestTypeEnum::INLET: {
    1158       68788 :                 FRULpTest = state.dataSolarCollectors->Parameters(ParamNum).eff1 +
    1159       68788 :                             state.dataSolarCollectors->Parameters(ParamNum).eff2 * (inletTemp - state.dataSurface->SurfOutDryBulbTemp(SurfNum));
    1160       68788 :                 TestTypeMod = 1.0;
    1161       68788 :             } break;
    1162           0 :             case TestTypeEnum::AVERAGE: {
    1163           0 :                 FRULpTest = state.dataSolarCollectors->Parameters(ParamNum).eff1 +
    1164           0 :                             state.dataSolarCollectors->Parameters(ParamNum).eff2 *
    1165           0 :                                 ((inletTemp + outletTemp) * 0.5 - state.dataSurface->SurfOutDryBulbTemp(SurfNum));
    1166           0 :                 TestTypeMod = 1.0 / (1.0 - FRULpTest / (2.0 * mCpATest));
    1167           0 :             } break;
    1168           0 :             case TestTypeEnum::OUTLET: {
    1169           0 :                 FRULpTest = state.dataSolarCollectors->Parameters(ParamNum).eff1 +
    1170           0 :                             state.dataSolarCollectors->Parameters(ParamNum).eff2 * (outletTemp - state.dataSurface->SurfOutDryBulbTemp(SurfNum));
    1171           0 :                 TestTypeMod = 1.0 / (1.0 - FRULpTest / mCpATest);
    1172           0 :             } break;
    1173           0 :             default:
    1174           0 :                 break;
    1175             :             }
    1176             : 
    1177             :             // FR * tau * alpha at normal incidence = Y-intercept of collector efficiency
    1178       68788 :             Real64 FRTAN = state.dataSolarCollectors->Parameters(ParamNum).eff0 * TestTypeMod;
    1179             : 
    1180             :             // FR * ULoss = 1st order coefficient of collector efficiency
    1181       68788 :             Real64 FRUL = state.dataSolarCollectors->Parameters(ParamNum).eff1 * TestTypeMod;
    1182             : 
    1183             :             // FR * ULoss / T = 2nd order coefficient of collector efficiency
    1184       68788 :             Real64 FRULT = state.dataSolarCollectors->Parameters(ParamNum).eff2 * TestTypeMod;
    1185       68788 :             FRULpTest *= TestTypeMod;
    1186             : 
    1187       68788 :             if (massFlowRate > 0.0) { // Calculate efficiency and heat transfer with flow
    1188             : 
    1189             :                 // Modifier for flow rate different from test flow rate
    1190       42558 :                 Real64 FlowMod = 0.0;
    1191             : 
    1192             :                 // F prime * ULoss for test conditions = collector efficiency factor * overall loss coefficient
    1193             :                 Real64 FpULTest;
    1194             : 
    1195       42558 :                 if ((1.0 + FRULpTest / mCpATest) > 0.0) {
    1196       42558 :                     FpULTest = -mCpATest * std::log(1.0 + FRULpTest / mCpATest);
    1197             :                 } else {
    1198           0 :                     FpULTest = FRULpTest; // Avoid LOG( <0 )
    1199             :                 }
    1200             : 
    1201       42558 :                 if ((-FpULTest / mCpA) < 700.0) {
    1202       42558 :                     FlowMod = mCpA * (1.0 - std::exp(-FpULTest / mCpA));
    1203             :                 } else { // avoid EXP(too large #)
    1204             :                          // FlowMod = FlowMod; // Self-assignment commented out
    1205             :                 }
    1206       42558 :                 if ((-FpULTest / mCpATest) < 700.0) {
    1207       42558 :                     FlowMod /= (mCpATest * (1.0 - std::exp(-FpULTest / mCpATest)));
    1208             :                 } else {
    1209             :                     // FlowMod = FlowMod; // Self-assignment commented out
    1210             :                 }
    1211             : 
    1212             :                 // Calculate fluid heat gain (or loss)
    1213             :                 // Heat loss is possible if there is no incident radiation and fluid is still flowing.
    1214       42558 :                 Q = (FRTAN * incidentAngleModifier * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) +
    1215       42558 :                      FRULpTest * (inletTemp - state.dataSurface->SurfOutDryBulbTemp(SurfNum))) *
    1216             :                     area * FlowMod;
    1217             : 
    1218       42558 :                 outletTemp = inletTemp + Q / (massFlowRate * Cp);
    1219             : 
    1220             :                 // CR 7877 bound unreasonable result
    1221       42558 :                 if (outletTemp < -100) {
    1222           0 :                     outletTemp = -100.0;
    1223           0 :                     Q = massFlowRate * Cp * (outletTemp - inletTemp);
    1224             :                 }
    1225       42558 :                 if (outletTemp > 200) {
    1226           0 :                     outletTemp = 200.0;
    1227           0 :                     Q = massFlowRate * Cp * (outletTemp - inletTemp);
    1228             :                 }
    1229             : 
    1230       42558 :                 if (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) > 0.0) { // Calculate thermal efficiency
    1231             :                     // NOTE: Efficiency can be > 1 if Q > SurfQRadSWOutIncident because of favorable delta T, i.e. warm outdoor temperature
    1232       14540 :                     efficiency =
    1233       14540 :                         Q / (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) * area); // Q has units of W; SurfQRadSWOutIncident has units of W/m2
    1234             :                 } else {
    1235       28018 :                     efficiency = 0.0;
    1236             :                 }
    1237             : 
    1238             :             } else { // Calculate stagnation temperature of fluid in collector (no flow)
    1239       26230 :                 Q = 0.0;
    1240       26230 :                 efficiency = 0.0;
    1241             : 
    1242             :                 // Calculate temperature of stagnant fluid in collector
    1243       26230 :                 Real64 A = -FRULT;
    1244       26230 :                 Real64 B = -FRUL + 2.0 * FRULT * state.dataSurface->SurfOutDryBulbTemp(SurfNum);
    1245       26230 :                 Real64 C = -FRULT * pow_2(state.dataSurface->SurfOutDryBulbTemp(SurfNum)) + FRUL * state.dataSurface->SurfOutDryBulbTemp(SurfNum) -
    1246       26230 :                            FRTAN * incidentAngleModifier * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
    1247       26230 :                 Real64 qEquation = (pow_2(B) - 4.0 * A * C);
    1248       26230 :                 if (qEquation < 0.0) {
    1249           0 :                     if (this->ErrIndex == 0) {
    1250           0 :                         ShowSevereMessage(state,
    1251           0 :                                           format("CalcSolarCollector: {}=\"{}\", possible bad input coefficients.",
    1252           0 :                                                  DataPlant::PlantEquipTypeNames[static_cast<int>(this->Type)],
    1253           0 :                                                  this->Name));
    1254           0 :                         ShowContinueError(state,
    1255             :                                           "...coefficients cause negative quadratic equation part in calculating temperature of stagnant fluid.");
    1256           0 :                         ShowContinueError(state, "...examine input coefficients for accuracy. Calculation will be treated as linear.");
    1257             :                     }
    1258           0 :                     ShowRecurringSevereErrorAtEnd(state,
    1259           0 :                                                   format("CalcSolarCollector: {}=\"{}\", coefficient error continues.",
    1260           0 :                                                          DataPlant::PlantEquipTypeNames[static_cast<int>(this->Type)],
    1261           0 :                                                          this->Name),
    1262           0 :                                                   this->ErrIndex,
    1263             :                                                   qEquation,
    1264             :                                                   qEquation);
    1265             :                 }
    1266       26230 :                 if (FRULT == 0.0 || qEquation < 0.0) { // Linear, 1st order solution
    1267           0 :                     outletTemp = state.dataSurface->SurfOutDryBulbTemp(SurfNum) -
    1268           0 :                                  FRTAN * incidentAngleModifier * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) / FRUL;
    1269             :                 } else { // Quadratic, 2nd order solution
    1270       26230 :                     outletTemp = (-B + std::sqrt(qEquation)) / (2.0 * A);
    1271             :                 }
    1272             :             }
    1273             : 
    1274       68788 :             if (state.dataSolarCollectors->Parameters(ParamNum).TestType == TestTypeEnum::INLET)
    1275       68788 :                 break; // Inlet temperature test correlations do not need to iterate
    1276             : 
    1277           0 :             if (Iteration > 100) {
    1278           0 :                 if (this->IterErrIndex == 0) {
    1279           0 :                     ShowWarningMessage(state,
    1280           0 :                                        format("CalcSolarCollector: {}=\"{}\":  Solution did not converge.",
    1281           0 :                                               DataPlant::PlantEquipTypeNames[static_cast<int>(this->Type)],
    1282           0 :                                               this->Name));
    1283             :                 }
    1284           0 :                 ShowRecurringWarningErrorAtEnd(state,
    1285           0 :                                                format("CalcSolarCollector: {}=\"{}\", solution not converge error continues.",
    1286           0 :                                                       DataPlant::PlantEquipTypeNames[static_cast<int>(this->Type)],
    1287           0 :                                                       this->Name),
    1288           0 :                                                this->IterErrIndex);
    1289           0 :                 break;
    1290             :             } else {
    1291           0 :                 ++Iteration;
    1292             :             }
    1293             : 
    1294             :         } // Check for temperature convergence
    1295             : 
    1296       68788 :         this->IncidentAngleModifier = incidentAngleModifier;
    1297       68788 :         this->Power = Q;
    1298       68788 :         this->HeatGain = max(Q, 0.0);
    1299       68788 :         this->HeatLoss = min(Q, 0.0);
    1300       68788 :         this->OutletTemp = outletTemp;
    1301       68788 :         this->Efficiency = efficiency;
    1302       68788 :     }
    1303             : 
    1304       64128 :     Real64 ParametersData::IAM(EnergyPlusData &state, Real64 const IncidentAngle)
    1305             :     {
    1306             : 
    1307             :         // SUBROUTINE INFORMATION:
    1308             :         //       AUTHOR         Peter Graham Ellis
    1309             :         //       DATE WRITTEN   December 2003
    1310             :         //       MODIFIED       Sept 2008, BG cut off IAM beyond 60 degrees.
    1311             :         //       RE-ENGINEERED  na
    1312             : 
    1313             :         // PURPOSE OF THIS SUBROUTINE:
    1314             :         // Calculates the incident angle modifier based on the solar collector parameters.  Both first and second order
    1315             :         // correlations are allowed.
    1316             : 
    1317             :         // METHODOLOGY EMPLOYED:
    1318             :         // A simple function.
    1319             : 
    1320             :         // REFERENCES:
    1321             :         // ASHRAE Standard 93-1986 (RA 91), "Methods of Testing to Determine the Thermal Performance of Solar Collectors".
    1322             :         // ASHRAE Standard 96-1980 (RA 89), "Methods of Testing to Determine the Thermal Performance of Unglazed Flat-Plate
    1323             :         //   Liquid-Type Solar Collectors".
    1324             :         // Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, Second Edition.  Wiley-Interscience:
    1325             :         //   New York (1991).
    1326             : 
    1327             :         Real64 IAM;
    1328             : 
    1329             :         // cut off IAM for angles greater than 60 degrees. (CR 7534)
    1330       64128 :         Real64 CutoffAngle = 60.0 * Constant::DegToRadians;
    1331       64128 :         if (std::abs(IncidentAngle) > CutoffAngle) { // cut off, model curves not robust beyond cutoff
    1332             :             // curves from FSEC/SRCC testing are only certified to 60 degrees, larger angles can cause numerical problems in curves
    1333       32656 :             IAM = 0.0;
    1334             :         } else {
    1335             : 
    1336       31472 :             Real64 s = (1.0 / std::cos(IncidentAngle)) - 1.0;
    1337             : 
    1338       31472 :             IAM = 1.0 + this->iam1 * s + this->iam2 * pow_2(s);
    1339       31472 :             IAM = max(IAM, 0.0); // Never allow to be less than zero, but greater than one is a possibility
    1340             : 
    1341       31472 :             if (IAM > 10.0) { // Greater than 10 is probably not a possibility
    1342           0 :                 ShowSevereError(
    1343             :                     state,
    1344           0 :                     format(
    1345             :                         "IAM Function: SolarCollectorPerformance:FlatPlate = {}:  Incident Angle Modifier is out of bounds due to bad coefficients.",
    1346           0 :                         this->Name));
    1347           0 :                 ShowContinueError(state, format("Coefficient 2 of Incident Angle Modifier = {}", this->iam1));
    1348           0 :                 ShowContinueError(state, format("Coefficient 3 of Incident Angle Modifier = {}", this->iam2));
    1349           0 :                 ShowContinueError(state, format("Calculated Incident Angle Modifier = {}", IAM));
    1350           0 :                 ShowContinueError(state, "Expected Incident Angle Modifier should be approximately 1.5 or less.");
    1351           0 :                 ShowFatalError(state, "Errors in SolarCollectorPerformance:FlatPlate input.");
    1352             :             }
    1353             : 
    1354             :         } // not greater than cut off angle
    1355             : 
    1356       64128 :         return IAM;
    1357             :     }
    1358             : 
    1359       52378 :     void CollectorData::CalcICSSolarCollector(EnergyPlusData &state)
    1360             :     {
    1361             : 
    1362             :         // SUBROUTINE INFORMATION:
    1363             :         //       AUTHOR         Bereket Nigusse, FSEC/UCF
    1364             :         //       DATE WRITTEN   February 2012
    1365             :         //       MODIFIED       na
    1366             :         //       RE-ENGINEERED  na
    1367             : 
    1368             :         // PURPOSE OF THIS SUBROUTINE:
    1369             :         // Calculates the heat transferred (gain or loss), energy stored, skin heat loss, outlet temperature, solar energy
    1370             :         // conversion efficiency, and transmittance-absorptance product of an ICS solar collector.
    1371             : 
    1372             :         // METHODOLOGY EMPLOYED:
    1373             :         // The governing equations for the absorber and collector water heat balance equations are solved simultaneously.
    1374             :         // The two coupled first ODE are solved analytically.
    1375             :         // The transmittance-absorptance product of the collector cover-absorber system is calculated using ray tracing
    1376             :         // method according to Duffie and Beckman(1991).
    1377             :         // REFERENCES:
    1378             :         // Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, 2nd. Edition.  Wiley-Interscience:
    1379             :         // New York (1991).
    1380             :         // NOTES:
    1381             : 
    1382             :         static constexpr std::string_view RoutineName("CalcICSSolarCollector");
    1383             : 
    1384       52378 :         int SurfNum = this->Surface;
    1385       52378 :         int ParamNum = this->Parameters;
    1386       52378 :         Real64 SecInTimeStep = state.dataHVACGlobal->TimeStepSysSec;
    1387       52378 :         Real64 TempWater = this->SavedTempOfWater;
    1388       52378 :         Real64 TempAbsPlate = this->SavedTempOfAbsPlate;
    1389       52378 :         Real64 TempOutdoorAir = state.dataSurface->SurfOutDryBulbTemp(SurfNum);
    1390             : 
    1391             :         Real64 TempOSCM; // Otherside condition model temperature [C]
    1392       52378 :         if (this->OSCM_ON) {
    1393       52378 :             TempOSCM = this->SavedTempCollectorOSCM;
    1394             :         } else {
    1395           0 :             TempOSCM = TempOutdoorAir;
    1396             :         }
    1397             : 
    1398             :         // Calculate transmittance-absorptance product of the system
    1399             :         // Incident angle of beam radiation (radians)
    1400       52378 :         Real64 ThetaBeam = std::acos(state.dataHeatBal->SurfCosIncidenceAngle(SurfNum));
    1401       52378 :         this->CalcTransAbsorProduct(state, ThetaBeam);
    1402             : 
    1403       52378 :         Real64 inletTemp = this->InletTemp;
    1404             : 
    1405       52378 :         Real64 massFlowRate = this->MassFlowRate;
    1406             : 
    1407             :         // Specific heat of collector fluid (J/kg-K)
    1408       52378 :         Real64 Cpw = FluidProperties::GetSpecificHeatGlycol(state,
    1409       52378 :                                                             state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    1410             :                                                             inletTemp,
    1411       52378 :                                                             state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    1412             :                                                             RoutineName);
    1413             : 
    1414             :         // density of collector fluid (kg/m3)
    1415       52378 :         Real64 Rhow = FluidProperties::GetDensityGlycol(state,
    1416       52378 :                                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    1417             :                                                         inletTemp,
    1418       52378 :                                                         state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    1419             :                                                         RoutineName);
    1420             : 
    1421             :         // calculate heat transfer coefficients and covers temperature:
    1422       52378 :         this->CalcHeatTransCoeffAndCoverTemp(state);
    1423             : 
    1424             :         // Calc convection heat transfer coefficient between the absorber plate and water:
    1425             : 
    1426             :         // convection coeff between absorber plate and water [W/m2K]
    1427             :         Real64 hConvCoefA2W =
    1428       52378 :             EnergyPlus::SolarCollectors::CollectorData::CalcConvCoeffAbsPlateAndWater(state, TempAbsPlate, TempWater, this->Length, this->TiltR2V);
    1429       52378 :         Real64 TempWaterOld = TempWater;
    1430       52378 :         Real64 TempAbsPlateOld = TempAbsPlate;
    1431             : 
    1432             :         // flag if the absorber has thermal mass or not
    1433             :         bool AbsPlateMassFlag;
    1434             : 
    1435             :         Real64 a1; // coefficient of ODE for absorber temperature Tp
    1436             :         Real64 a2; // coefficient of ODE for absorber temperature Tw
    1437             :         Real64 a3; // constant term of ODE for absorber temperature
    1438             : 
    1439             :         // Gross area of collector (m2)
    1440       52378 :         Real64 area = state.dataSolarCollectors->Parameters(ParamNum).Area;
    1441             : 
    1442       52378 :         if (state.dataSolarCollectors->Parameters(ParamNum).ThermalMass > 0.0) {
    1443           0 :             AbsPlateMassFlag = true;
    1444             : 
    1445             :             // thermal mass of the absorber plate [J/K]
    1446           0 :             Real64 ap = state.dataSolarCollectors->Parameters(ParamNum).ThermalMass * area;
    1447           0 :             a1 = -area * (hConvCoefA2W + this->UTopLoss) / ap;
    1448           0 :             a2 = area * hConvCoefA2W / ap;
    1449           0 :             a3 = area * (this->TauAlpha * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) + this->UTopLoss * TempOutdoorAir) / ap;
    1450             :         } else {
    1451       52378 :             AbsPlateMassFlag = false;
    1452       52378 :             a1 = -area * (hConvCoefA2W + this->UTopLoss);
    1453       52378 :             a2 = area * hConvCoefA2W;
    1454       52378 :             a3 = area * (this->TauAlpha * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) + this->UTopLoss * TempOutdoorAir);
    1455             :         }
    1456             : 
    1457             :         // thermal mass of the collector water [J/K]
    1458       52378 :         Real64 aw = state.dataSolarCollectors->Parameters(ParamNum).Volume * Rhow * Cpw;
    1459             : 
    1460             :         // coefficient of ODE for water temperature Tp
    1461       52378 :         Real64 b1 = area * hConvCoefA2W / aw;
    1462             : 
    1463             :         // coefficient of ODE for water temperature Tw
    1464       52378 :         Real64 b2 = -(area * (hConvCoefA2W + this->UbLoss + this->UsLoss) + massFlowRate * Cpw) / aw;
    1465             : 
    1466             :         // constant term of ODE for water temperature
    1467       52378 :         Real64 b3 = (area * (this->UbLoss * TempOSCM + this->UsLoss * TempOutdoorAir) + massFlowRate * Cpw * inletTemp) / aw;
    1468             : 
    1469       52378 :         EnergyPlus::SolarCollectors::CollectorData::ICSCollectorAnalyticalSolution(
    1470             :             state, SecInTimeStep, a1, a2, a3, b1, b2, b3, TempAbsPlateOld, TempWaterOld, TempAbsPlate, TempWater, AbsPlateMassFlag);
    1471             : 
    1472       52378 :         this->SkinHeatLossRate = area * (this->UTopLoss * (TempOutdoorAir - TempAbsPlate) + this->UsLoss * (TempOutdoorAir - TempWater) +
    1473       52378 :                                          this->UbLoss * (TempOSCM - TempWater));
    1474       52378 :         this->StoredHeatRate = aw * (TempWater - TempWaterOld) / SecInTimeStep;
    1475             : 
    1476             :         // heat gain rate (W)
    1477       52378 :         Real64 QHeatRate = massFlowRate * Cpw * (TempWater - inletTemp);
    1478       52378 :         this->HeatRate = QHeatRate;
    1479       52378 :         this->HeatGainRate = max(0.0, QHeatRate);
    1480             : 
    1481       52378 :         Real64 outletTemp = TempWater;
    1482       52378 :         this->OutletTemp = outletTemp;
    1483       52378 :         this->TempOfWater = TempWater;
    1484       52378 :         this->TempOfAbsPlate = TempAbsPlate;
    1485             : 
    1486       52378 :         Real64 efficiency = 0.0; // Thermal efficiency of solar energy conversion
    1487       52378 :         if (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) > 0.0) {
    1488       25200 :             efficiency = (this->HeatGainRate + this->StoredHeatRate) / (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) * area);
    1489       25200 :             if (efficiency < 0.0) efficiency = 0.0;
    1490             :         }
    1491       52378 :         this->Efficiency = efficiency;
    1492       52378 :     }
    1493             : 
    1494       52378 :     void CollectorData::ICSCollectorAnalyticalSolution(EnergyPlusData &state,
    1495             :                                                        Real64 const SecInTimeStep,     // seconds in a time step
    1496             :                                                        Real64 const a1,                // coefficient of ODE for Tp
    1497             :                                                        Real64 const a2,                // coefficient of ODE for Tp
    1498             :                                                        Real64 const a3,                // coefficient of ODE for Tp
    1499             :                                                        Real64 const b1,                // coefficient of ODE for TW
    1500             :                                                        Real64 const b2,                // coefficient of ODE for TW
    1501             :                                                        Real64 const b3,                // coefficient of ODE for TW
    1502             :                                                        Real64 const TempAbsPlateOld,   // absorber plate temperature at previous time step [C]
    1503             :                                                        Real64 const TempWaterOld,      // collector water temperature at previous time step [C]
    1504             :                                                        Real64 &TempAbsPlate,           // absorber plate temperature at current time step [C]
    1505             :                                                        Real64 &TempWater,              // collector water temperature at current time step [C]
    1506             :                                                        bool const AbsorberPlateHasMass // flag for absorber thermal mass
    1507             :     )
    1508             :     {
    1509             : 
    1510             :         // SUBROUTINE INFORMATION:
    1511             :         //       AUTHOR         Bereket Nigusse, FSEC/UCF
    1512             :         //       DATE WRITTEN   February 2012
    1513             :         //       MODIFIED       na
    1514             :         //       RE-ENGINEERED  na
    1515             : 
    1516             :         // PURPOSE OF THIS SUBROUTINE:
    1517             :         // Calculates the absorber plate and collector water temperatures.
    1518             :         // METHODOLOGY EMPLOYED:
    1519             :         // Analytical method: Solves the coupled absorber plate and collector water energy balance
    1520             :         // equations.  The two non-homogeneous ordinary differential equations of the form.
    1521             :         //          Tp' = a1*Tp + a2*Tw + a3.
    1522             :         //          Tw' = b1*Tp + b2*Tw + b3.
    1523             :         // The general solution of these coupled equation with real routes has the following form:
    1524             :         //          Tp = ConstantC1*exp(lamda1*t) + ConstantC2*exp(lamda2*t) + ConstOfTpSln
    1525             :         //          Tw = r1*ConstantC2*exp(lamda1*t) + r2*ConstantC2*exp(lamda2*t) + ConstOfTwSln
    1526             : 
    1527       52378 :         if (AbsorberPlateHasMass) {
    1528             : 
    1529             :             // coefficients of quadratic equation a*m2+b*m+c=0
    1530           0 :             Real64 a = 1.0;
    1531           0 :             Real64 b = -(a1 + b2);
    1532           0 :             Real64 c = a1 * b2 - a2 * b1;
    1533           0 :             Real64 BSquareM4TimesATimesC = pow_2(b) - 4.0 * a * c;
    1534             : 
    1535           0 :             if (BSquareM4TimesATimesC > 0.0) {
    1536             : 
    1537             :                 // the real roots of the quadratic equation
    1538           0 :                 Real64 lamda1 = (-b + std::sqrt(BSquareM4TimesATimesC)) / (2.0 * a);
    1539           0 :                 Real64 lamda2 = (-b - std::sqrt(BSquareM4TimesATimesC)) / (2.0 * a);
    1540             : 
    1541             :                 // the particular solution for the ODE
    1542           0 :                 Real64 ConstOfTpSln = (-a3 * b2 + b3 * a2) / c;
    1543           0 :                 Real64 ConstOfTwSln = (-a1 * b3 + b1 * a3) / c;
    1544             : 
    1545             :                 // ratio of the ODE solution constant coefficients
    1546           0 :                 Real64 r1 = (lamda1 - a1) / a2;
    1547           0 :                 Real64 r2 = (lamda2 - a1) / a2;
    1548             : 
    1549             :                 // coefficients of the ODE solution
    1550           0 :                 Real64 ConstantC2 = (TempWaterOld + r1 * ConstOfTpSln - r1 * TempAbsPlateOld - ConstOfTwSln) / (r2 - r1);
    1551           0 :                 Real64 ConstantC1 = (TempAbsPlateOld - ConstOfTpSln - ConstantC2);
    1552             : 
    1553           0 :                 TempAbsPlate = ConstantC1 * std::exp(lamda1 * SecInTimeStep) + ConstantC2 * std::exp(lamda2 * SecInTimeStep) + ConstOfTpSln;
    1554           0 :                 TempWater = r1 * ConstantC1 * std::exp(lamda1 * SecInTimeStep) + r2 * ConstantC2 * std::exp(lamda2 * SecInTimeStep) + ConstOfTwSln;
    1555             : 
    1556             :             } else { // this should never occur
    1557           0 :                 ShowSevereError(
    1558             :                     state, "ICSCollectorAnalyticalSoluton: Unanticipated differential equation coefficient - report to EnergyPlus Development Team");
    1559           0 :                 ShowFatalError(state, "Program terminates due to above conditions.");
    1560             :             }
    1561             :         } else {
    1562             :             // In the absence of absorber plate thermal mass, only the collector water heat balance has a
    1563             :             // differential equation of the form: Tw' = b1*Tp + b2*Tw + b3. The absorber plate energy balance
    1564             :             // equation in the absence of thermal mass is a steady state form:  b1*Tp + b2*Tw + b3 = 0
    1565       52378 :             Real64 b = b2 - b1 * (a2 / a1);
    1566       52378 :             Real64 c = b3 - b1 * (a3 / a1);
    1567       52378 :             TempWater = (TempWaterOld + c / b) * std::exp(b * SecInTimeStep) - c / b;
    1568       52378 :             TempAbsPlate = -(a2 * TempWater + a3) / a1;
    1569             :         }
    1570       52378 :     }
    1571             : 
    1572       52378 :     void CollectorData::CalcTransAbsorProduct(EnergyPlusData &state, Real64 const IncidAngle)
    1573             :     {
    1574             : 
    1575             :         // SUBROUTINE INFORMATION:
    1576             :         //       AUTHOR         Bereket A Nigusse
    1577             :         //       DATE WRITTEN   February 2012
    1578             :         //       MODIFIED       na
    1579             :         //       RE-ENGINEERED  na
    1580             : 
    1581             :         // PURPOSE OF THIS SUBROUTINE:
    1582             :         // Calculates transmittance-absorptance product and the fraction of total solar radiation
    1583             :         // absorbed by each cover of a multicover ICS solar collector.
    1584             : 
    1585             :         // METHODOLOGY EMPLOYED:
    1586             :         // Uses a ray tracing method.
    1587             : 
    1588             :         // REFERENCES:
    1589             :         // Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, Second Edition.
    1590             :         // Wiley-Interscience: New York (1991).
    1591             : 
    1592       52378 :         Real64 TransSys = 1.0;  // cover system solar transmittance
    1593       52378 :         Real64 ReflSys = 0.0;   // cover system solar reflectance
    1594       52378 :         Real64 AbsCover1 = 0.0; // Inner cover solar absorbtance
    1595       52378 :         Real64 AbsCover2 = 0.0; // Outer cover solar absorbtance
    1596             :         Real64 TuaAlpha;        // weighted trans-abs product of system
    1597             :         Real64 TuaAlphaBeam;    // trans-abs product of beam radiation
    1598       52378 :         this->CoverAbs[0] = 0.0;
    1599       52378 :         this->CoverAbs[1] = 0.0;
    1600             : 
    1601       52378 :         int SurfNum = this->Surface;
    1602       52378 :         int ParamNum = this->Parameters;
    1603             : 
    1604       52378 :         if (state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) > 0.0) {
    1605             : 
    1606             :             // cover system transmittance and reflectance from outer to inner cover
    1607       25200 :             this->CalcTransRefAbsOfCover(state, IncidAngle, TransSys, ReflSys, AbsCover1, AbsCover2);
    1608             : 
    1609       25200 :             TuaAlphaBeam = TransSys * state.dataSolarCollectors->Parameters(ParamNum).AbsorOfAbsPlate /
    1610       25200 :                            (1.0 - (1.0 - state.dataSolarCollectors->Parameters(ParamNum).AbsorOfAbsPlate) * this->RefDiffInnerCover);
    1611             : 
    1612       25200 :             this->TauAlphaBeam = max(0.0, TuaAlphaBeam);
    1613             : 
    1614       25200 :             Array1D<Real64> CoversAbsBeam(2); // Inner and Outer Cover absorptance
    1615       25200 :             CoversAbsBeam(1) = AbsCover1;
    1616       25200 :             CoversAbsBeam(2) = AbsCover2;
    1617             : 
    1618             :             // calc total solar radiation weighted transmittance-absorptance product
    1619       25200 :             TuaAlpha = (state.dataHeatBal->SurfQRadSWOutIncidentBeam(SurfNum) * this->TauAlphaBeam +
    1620       25200 :                         state.dataHeatBal->SurfQRadSWOutIncidentSkyDiffuse(SurfNum) * this->TauAlphaSkyDiffuse +
    1621       25200 :                         state.dataHeatBal->SurfQRadSWOutIncidentGndDiffuse(SurfNum) * this->TauAlphaGndDiffuse) /
    1622       25200 :                        state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
    1623             : 
    1624       25200 :             if (state.dataSolarCollectors->Parameters(ParamNum).NumOfCovers == 1) {
    1625             :                 // calc total solar radiation weighted cover absorptance
    1626       50400 :                 this->CoverAbs[0] = (state.dataHeatBal->SurfQRadSWOutIncidentBeam(SurfNum) * CoversAbsBeam(1) +
    1627       25200 :                                      state.dataHeatBal->SurfQRadSWOutIncidentSkyDiffuse(SurfNum) * this->CoversAbsSkyDiffuse[0] +
    1628       25200 :                                      state.dataHeatBal->SurfQRadSWOutIncidentGndDiffuse(SurfNum) * this->CoversAbsGndDiffuse[0]) /
    1629       25200 :                                     state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
    1630             : 
    1631           0 :             } else if (state.dataSolarCollectors->Parameters(ParamNum).NumOfCovers == 2) {
    1632             :                 // Num = 1 represents outer cover and Num = 2 represents inner cover
    1633           0 :                 for (int Num = 0; Num < state.dataSolarCollectors->Parameters(ParamNum).NumOfCovers; ++Num) {
    1634           0 :                     this->CoverAbs[Num] = (state.dataHeatBal->SurfQRadSWOutIncidentBeam(SurfNum) * CoversAbsBeam(Num + 1) +
    1635           0 :                                            state.dataHeatBal->SurfQRadSWOutIncidentSkyDiffuse(SurfNum) * this->CoversAbsSkyDiffuse[Num] +
    1636           0 :                                            state.dataHeatBal->SurfQRadSWOutIncidentGndDiffuse(SurfNum) * this->CoversAbsGndDiffuse[Num]) /
    1637           0 :                                           state.dataHeatBal->SurfQRadSWOutIncident(SurfNum);
    1638             :                 }
    1639             :             }
    1640             : 
    1641       25200 :         } else {
    1642       27178 :             TuaAlpha = 0.0;
    1643             :         }
    1644       52378 :         this->TauAlpha = TuaAlpha;
    1645       52378 :     }
    1646             : 
    1647       25208 :     void CollectorData::CalcTransRefAbsOfCover(EnergyPlusData &state,
    1648             :                                                Real64 const IncidentAngle,               // Angle of incidence (radians)
    1649             :                                                Real64 &TransSys,                         // cover system solar transmittance
    1650             :                                                Real64 &ReflSys,                          // cover system solar reflectance
    1651             :                                                Real64 &AbsCover1,                        // Inner cover solar absorbtance
    1652             :                                                Real64 &AbsCover2,                        // Outer cover solar absorbtance
    1653             :                                                ObjexxFCL::Optional_bool_const InOUTFlag, // flag for calc. diffuse solar refl of cover from inside out
    1654             :                                                ObjexxFCL::Optional<Real64> RefSysDiffuse // cover system solar reflectance from inner to outer cover
    1655             :     ) const
    1656             :     {
    1657             : 
    1658             :         // SUBROUTINE INFORMATION:
    1659             :         //       AUTHOR         Bereket A Nigusse
    1660             :         //       DATE WRITTEN   February 2012
    1661             : 
    1662             :         // PURPOSE OF THIS SUBROUTINE:
    1663             :         // Calculates the transmitance, reflectance, and absorptance of the collector covers based on
    1664             :         // solar collector optical parameters specified.
    1665             : 
    1666             :         // METHODOLOGY EMPLOYED:
    1667             :         // Uses a ray tracing method.
    1668             : 
    1669             :         // REFERENCES:
    1670             :         // Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, Second Edition.
    1671             :         // Wiley-Interscience: New York (1991).
    1672             : 
    1673       25208 :         Real64 constexpr AirRefIndex(1.0003); // refractive index of air
    1674             : 
    1675       25208 :         Array1D<Real64> TransPara(2);    // cover transmittance parallel component
    1676       25208 :         Array1D<Real64> TransPerp(2);    // cover transmittance perpendicular component
    1677       25208 :         Array1D<Real64> ReflPara(2);     // cover reflectance parallel component
    1678       25208 :         Array1D<Real64> ReflPerp(2);     // cover reflectance Perpendicular component
    1679       25208 :         Array1D<Real64> AbsorPara(2);    // cover absorbtance parallel component
    1680       25208 :         Array1D<Real64> AbsorPerp(2);    // cover absorbtance Perpendicular component
    1681       25208 :         Array1D<Real64> TransAbsOnly(2); // cover transmittance with absorptance only considered
    1682             : 
    1683       25208 :         TransPerp = 1.0;
    1684       25208 :         TransPara = 1.0;
    1685       25208 :         ReflPerp = 0.0;
    1686       25208 :         ReflPara = 0.0;
    1687       25208 :         AbsorPerp = 0.0;
    1688       25208 :         AbsorPara = 0.0;
    1689       25208 :         TransAbsOnly = 1.0;
    1690       25208 :         TransSys = 0.0;
    1691       25208 :         ReflSys = 0.0;
    1692       25208 :         AbsCover1 = 0.0;
    1693       25208 :         AbsCover2 = 0.0;
    1694             : 
    1695             :         bool DiffRefFlag; // flag for calc. diffuse refl of cover from inside to outsidd
    1696       25208 :         if (present(InOUTFlag)) {
    1697           2 :             DiffRefFlag = InOUTFlag;
    1698             :         } else {
    1699       25206 :             DiffRefFlag = false;
    1700             :         }
    1701             : 
    1702             :         // get the incidence and refraction angles
    1703       25208 :         int ParamNum = this->Parameters;
    1704       25208 :         Real64 const sin_IncAngle(std::sin(IncidentAngle));
    1705             : 
    1706       50416 :         for (int nCover = 1; nCover <= state.dataSolarCollectors->Parameters(ParamNum).NumOfCovers; ++nCover) {
    1707             : 
    1708             :             // refractive index of collector cover
    1709       25208 :             Real64 CoverRefrIndex = state.dataSolarCollectors->Parameters(ParamNum).RefractiveIndex[nCover - 1];
    1710             : 
    1711             :             // angle of refraction
    1712       25208 :             Real64 RefrAngle = std::asin(sin_IncAngle * AirRefIndex / CoverRefrIndex);
    1713             : 
    1714             :             // transmitted component with absorption only considered:
    1715       25208 :             TransAbsOnly(nCover) = std::exp(-state.dataSolarCollectors->Parameters(ParamNum).ExtCoefTimesThickness[nCover - 1] / std::cos(RefrAngle));
    1716             : 
    1717             :             // parallel reflected component of unpolarized solar radiation
    1718             :             Real64 ParaRad;
    1719             : 
    1720             :             // Perpendicular reflected component of unpolarized solar radiation
    1721             :             Real64 PerpRad;
    1722             : 
    1723             :             // parallel and perpendicular reflection components:
    1724       25208 :             if (IncidentAngle == 0.0) {
    1725           2 :                 ParaRad = pow_2((CoverRefrIndex - AirRefIndex) / (CoverRefrIndex + AirRefIndex));
    1726           2 :                 PerpRad = pow_2((CoverRefrIndex - AirRefIndex) / (CoverRefrIndex + AirRefIndex));
    1727             :             } else {
    1728       25206 :                 ParaRad = pow_2(std::tan(RefrAngle - IncidentAngle)) / pow_2(std::tan(RefrAngle + IncidentAngle));
    1729       25206 :                 PerpRad = pow_2(std::sin(RefrAngle - IncidentAngle)) / pow_2(std::sin(RefrAngle + IncidentAngle));
    1730             :             }
    1731             : 
    1732             :             // parallel and perpendicular transmitted components:
    1733       50416 :             TransPerp(nCover) =
    1734       25208 :                 TransAbsOnly(nCover) * ((1.0 - PerpRad) / (1.0 + PerpRad)) * ((1.0 - pow_2(PerpRad)) / (1.0 - pow_2(PerpRad * TransAbsOnly(nCover))));
    1735       50416 :             TransPara(nCover) =
    1736       25208 :                 TransAbsOnly(nCover) * ((1.0 - ParaRad) / (1.0 + ParaRad)) * ((1.0 - pow_2(ParaRad)) / (1.0 - pow_2(ParaRad * TransAbsOnly(nCover))));
    1737             : 
    1738       50416 :             ReflPerp(nCover) =
    1739       25208 :                 (PerpRad + (pow_2(1.0 - PerpRad) * pow_2(TransAbsOnly(nCover)) * PerpRad) / (1.0 - pow_2(PerpRad * TransAbsOnly(nCover))));
    1740       50416 :             ReflPara(nCover) =
    1741       25208 :                 (ParaRad + (pow_2(1.0 - ParaRad) * pow_2(TransAbsOnly(nCover)) * ParaRad) / (1.0 - pow_2(ParaRad * TransAbsOnly(nCover))));
    1742             : 
    1743       25208 :             AbsorPerp(nCover) = 1.0 - TransPerp(nCover) - ReflPerp(nCover);
    1744       25208 :             AbsorPara(nCover) = 1.0 - TransPara(nCover) - ReflPara(nCover);
    1745             :         }
    1746             : 
    1747             :         // solar absorptance of the individual cover
    1748       25208 :         AbsCover1 = 0.5 * (AbsorPerp(1) + AbsorPara(1));
    1749       25208 :         if (state.dataSolarCollectors->Parameters(ParamNum).NumOfCovers == 2) AbsCover2 = 0.5 * (AbsorPerp(2) + AbsorPara(2));
    1750             : 
    1751             :         // calculate from outer to inner cover:
    1752       25208 :         TransSys =
    1753       25208 :             0.5 * (TransPerp(1) * TransPerp(2) / (1.0 - ReflPerp(1) * ReflPerp(2)) + TransPara(1) * TransPara(2) / (1.0 - ReflPara(1) * ReflPara(2)));
    1754       25208 :         ReflSys = 0.5 * (ReflPerp(1) + TransSys * ReflPerp(2) * TransPerp(1) / TransPerp(2) + ReflPara(1) +
    1755       25208 :                          TransSys * ReflPara(2) * TransPara(1) / TransPara(2));
    1756       25208 :         if (DiffRefFlag) {
    1757             :             // calculate from inner to outer cover:
    1758             : 
    1759             :             // cover system solar transmittance from inner to outer cover
    1760           2 :             Real64 TransSysDiff = 0.5 * (TransPerp(2) * TransPerp(1) / (1.0 - ReflPerp(2) * ReflPerp(1)) +
    1761           2 :                                          TransPara(2) * TransPara(1) / (1.0 - ReflPara(2) * ReflPara(1)));
    1762           4 :             RefSysDiffuse = 0.5 * (ReflPerp(2) + TransSysDiff * ReflPerp(1) * TransPerp(2) / TransPerp(1) + ReflPara(2) +
    1763           2 :                                    TransSysDiff * ReflPara(1) * TransPara(2) / TransPara(1));
    1764             :         }
    1765       25208 :     }
    1766             : 
    1767       52378 :     void CollectorData::CalcHeatTransCoeffAndCoverTemp(EnergyPlusData &state) // Collector object number
    1768             :     {
    1769             :         // SUBROUTINE INFORMATION:
    1770             :         //       AUTHOR         Bereket A Nigusse, FSEC/UCF
    1771             :         //       DATE WRITTEN   February 2012
    1772             : 
    1773             :         // PURPOSE OF THIS SUBROUTINE:
    1774             :         // Calculates the various heat transfer coefficients, and collector cover temperatures.
    1775             : 
    1776             :         // METHODOLOGY EMPLOYED:
    1777             : 
    1778             :         // REFERENCES:
    1779             :         // Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, Second Edition.
    1780             :         // Wiley-Interscience: New York (1991).
    1781             : 
    1782             :         Real64 tempnom;             // intermediate variable
    1783             :         Real64 tempdenom;           // intermediate variable
    1784             :         Real64 hRadCoefC2Sky;       // radiation coeff from collector to the sky [W/m2C]
    1785       52378 :         Real64 hRadCoefC2Gnd = 0.0; // radiation coeff from collector to the ground [W/m2C]
    1786       52378 :         Real64 hConvCoefA2C = 0.0;  // convection coeff. between abs plate and cover [W/m2C]
    1787       52378 :         Real64 hConvCoefC2C = 0.0;  // convection coeff. between covers [W/m2C]
    1788       52378 :         Real64 hConvCoefC2O = 0.0;  // convection coeff. between outer cover and the ambient [W/m2C]
    1789       52378 :         Real64 hRadCoefA2C = 0.0;   // radiation coeff. between abs plate and cover [W/m2C]
    1790       52378 :         Real64 hRadCoefC2C = 0.0;   // radiation coeff. between covers [W/m2C]
    1791       52378 :         Real64 hRadCoefC2O = 0.0;   // radiation coeff. between outer covers and the ambient [W/m2C]
    1792             : 
    1793       52378 :         int ParamNum = this->Parameters;
    1794       52378 :         int NumCovers = state.dataSolarCollectors->Parameters(ParamNum).NumOfCovers;
    1795       52378 :         int SurfNum = this->Surface;
    1796             : 
    1797       52378 :         Real64 TempAbsPlate = this->SavedTempOfAbsPlate;                        // absorber plate average temperature [C]
    1798       52378 :         Real64 TempInnerCover = this->SavedTempOfInnerCover;                    // inner cover average temperature [C]
    1799       52378 :         Real64 TempOuterCover = this->SavedTempOfOuterCover;                    // outer cover average temperature [C]
    1800       52378 :         Real64 TempOutdoorAir = state.dataSurface->SurfOutDryBulbTemp(SurfNum); // outdoor air temperature [C]
    1801             : 
    1802       52378 :         Real64 EmissOfAbsPlate = state.dataSolarCollectors->Parameters(ParamNum).EmissOfAbsPlate;   // emissivity of absorber plate
    1803       52378 :         Real64 EmissOfOuterCover = state.dataSolarCollectors->Parameters(ParamNum).EmissOfCover[0]; // emissivity of outer cover
    1804       52378 :         Real64 EmissOfInnerCover = state.dataSolarCollectors->Parameters(ParamNum).EmissOfCover[1]; // emissivity of inner cover
    1805       52378 :         Real64 AirGapDepth = state.dataSolarCollectors->Parameters(ParamNum).CoverSpacing;          // characteristic length [m]
    1806             : 
    1807       52378 :         if (NumCovers == 1) {
    1808             :             // calc linearized radiation coefficient
    1809      104756 :             tempnom = Constant::StefanBoltzmann * ((TempAbsPlate + Constant::Kelvin) + (TempOuterCover + Constant::Kelvin)) *
    1810       52378 :                       (pow_2(TempAbsPlate + Constant::Kelvin) + pow_2(TempOuterCover + Constant::Kelvin));
    1811       52378 :             tempdenom = 1.0 / EmissOfAbsPlate + 1.0 / EmissOfOuterCover - 1.0;
    1812       52378 :             hRadCoefA2C = tempnom / tempdenom;
    1813       52378 :             hRadCoefC2C = 0.0;
    1814       52378 :             hConvCoefC2C = 0.0;
    1815             :             // Calc convection heat transfer coefficient:
    1816       52378 :             hConvCoefA2C = EnergyPlus::SolarCollectors::CollectorData::CalcConvCoeffBetweenPlates(
    1817             :                 TempAbsPlate, TempOuterCover, AirGapDepth, this->CosTilt, this->SinTilt);
    1818           0 :         } else if (NumCovers == 2) {
    1819           0 :             for (int CoverNum = 1; CoverNum <= NumCovers; ++CoverNum) {
    1820           0 :                 if (CoverNum == 1) {
    1821             :                     // calc linearized radiation coefficient
    1822           0 :                     tempnom = Constant::StefanBoltzmann * ((TempAbsPlate + Constant::Kelvin) + (TempInnerCover + Constant::Kelvin)) *
    1823           0 :                               (pow_2(TempAbsPlate + Constant::Kelvin) + pow_2(TempInnerCover + Constant::Kelvin));
    1824           0 :                     tempdenom = 1.0 / EmissOfAbsPlate + 1.0 / EmissOfInnerCover - 1.0;
    1825           0 :                     hRadCoefA2C = tempnom / tempdenom;
    1826             :                     // Calc convection heat transfer coefficient:
    1827           0 :                     hConvCoefA2C = EnergyPlus::SolarCollectors::CollectorData::CalcConvCoeffBetweenPlates(
    1828             :                         TempAbsPlate, TempOuterCover, AirGapDepth, this->CosTilt, this->SinTilt);
    1829             :                 } else {
    1830             :                     // calculate the linearized radiation coeff.
    1831           0 :                     tempnom = Constant::StefanBoltzmann * ((TempInnerCover + Constant::Kelvin) + (TempOuterCover + Constant::Kelvin)) *
    1832           0 :                               (pow_2(TempInnerCover + Constant::Kelvin) + pow_2(TempOuterCover + Constant::Kelvin));
    1833           0 :                     tempdenom = 1.0 / EmissOfInnerCover + 1.0 / EmissOfOuterCover - 1.0;
    1834           0 :                     hRadCoefC2C = tempnom / tempdenom;
    1835             :                     // Calc convection heat transfer coefficient:
    1836           0 :                     hConvCoefC2C = EnergyPlus::SolarCollectors::CollectorData::CalcConvCoeffBetweenPlates(
    1837             :                         TempInnerCover, TempOuterCover, AirGapDepth, this->CosTilt, this->SinTilt);
    1838             :                 }
    1839             :             }
    1840             :         }
    1841             : 
    1842             :         // Calc collector outside surface convection heat transfer coefficient:
    1843       52378 :         hConvCoefC2O = 2.8 + 3.0 * state.dataSurface->SurfOutWindSpeed(SurfNum);
    1844             : 
    1845             :         // Calc linearized radiation coefficient between outer cover and the surrounding:
    1846       52378 :         tempnom = state.dataSurface->Surface(SurfNum).ViewFactorSky * EmissOfOuterCover * Constant::StefanBoltzmann *
    1847       52378 :                   ((TempOuterCover + Constant::Kelvin) + state.dataEnvrn->SkyTempKelvin) *
    1848       52378 :                   (pow_2(TempOuterCover + Constant::Kelvin) + pow_2(state.dataEnvrn->SkyTempKelvin));
    1849       52378 :         tempdenom = (TempOuterCover - TempOutdoorAir) / (TempOuterCover - state.dataEnvrn->SkyTemp);
    1850       52378 :         if (tempdenom < 0.0) {
    1851             :             // use approximate linearized radiation coefficient
    1852        3392 :             hRadCoefC2Sky = tempnom;
    1853       48986 :         } else if (tempdenom == 0.0) {
    1854             :             // if temperature difference is zero, no radiation exchange
    1855        1024 :             hRadCoefC2Sky = 0.0;
    1856             :         } else {
    1857       47962 :             hRadCoefC2Sky = tempnom / tempdenom;
    1858             :         }
    1859             : 
    1860       52378 :         tempnom = state.dataSurface->Surface(SurfNum).ViewFactorGround * EmissOfOuterCover * Constant::StefanBoltzmann *
    1861       52378 :                   ((TempOuterCover + Constant::Kelvin) + state.dataEnvrn->GroundTempKelvin) *
    1862       52378 :                   (pow_2(TempOuterCover + Constant::Kelvin) + pow_2(state.dataEnvrn->GroundTempKelvin));
    1863       52378 :         tempdenom =
    1864       52378 :             (TempOuterCover - TempOutdoorAir) / (TempOuterCover - state.dataEnvrn->GroundTemp[(int)DataEnvironment::GroundTempType::BuildingSurface]);
    1865       52378 :         if (tempdenom < 0.0) {
    1866             :             // use approximate linearized radiation coefficient
    1867       20746 :             hRadCoefC2Gnd = tempnom;
    1868       31632 :         } else if (tempdenom == 0.0) {
    1869             :             // if temperature difference is zero, no radiation exchange
    1870        1024 :             hRadCoefC2Gnd = 0.0;
    1871             :         } else {
    1872       30608 :             hRadCoefC2Gnd = tempnom / tempdenom;
    1873             :         }
    1874             : 
    1875             :         // combine the radiation coefficients
    1876       52378 :         hRadCoefC2O = hRadCoefC2Sky + hRadCoefC2Gnd;
    1877             : 
    1878             :         // calculate the overall top heat loss coefficient:
    1879             : 
    1880       52378 :         if (NumCovers == 1) {
    1881       52378 :             this->UTopLoss = 1.0 / (1.0 / (hRadCoefA2C + hConvCoefA2C) + 1.0 / (hRadCoefC2O + hConvCoefC2O));
    1882             :         } else {
    1883           0 :             this->UTopLoss = 1.0 / (1.0 / (hRadCoefA2C + hConvCoefA2C) + 1.0 / (hRadCoefC2C + hConvCoefC2C) + 1.0 / (hRadCoefC2O + hConvCoefC2O));
    1884             :         }
    1885             : 
    1886             :         // calculate the side loss coefficient.  Adds the insulation resistance and the combined
    1887             :         // convection-radiation coefficients in series.
    1888       52378 :         Real64 hRadConvOut = 5.7 + 3.8 * state.dataSurface->SurfOutWindSpeed(SurfNum);
    1889       52378 :         this->UsLoss =
    1890       52378 :             1.0 / (1.0 / (state.dataSolarCollectors->Parameters(ParamNum).ULossSide * this->AreaRatio) + 1.0 / (hRadConvOut * this->AreaRatio));
    1891             : 
    1892             :         // the bottom loss coefficient calculation depends on the boundary condition
    1893       52378 :         if (this->OSCM_ON) { // OtherSideConditionsModel
    1894       52378 :             this->UbLoss = state.dataSolarCollectors->Parameters(ParamNum).ULossBottom;
    1895             :         } else { // AmbientAir
    1896           0 :             this->UbLoss = 1.0 / (1.0 / state.dataSolarCollectors->Parameters(ParamNum).ULossBottom + 1.0 / hRadConvOut);
    1897             :         }
    1898             : 
    1899             :         // Calculate current timestep covers temperature
    1900       52378 :         if (NumCovers == 1) {
    1901       52378 :             tempnom = this->CoverAbs[0] * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) + TempOutdoorAir * (hConvCoefC2O + hRadCoefC2O) +
    1902       52378 :                       TempAbsPlate * (hConvCoefA2C + hRadCoefA2C);
    1903       52378 :             tempdenom = (hConvCoefC2O + hRadCoefC2O) + (hConvCoefA2C + hRadCoefA2C);
    1904       52378 :             TempOuterCover = tempnom / tempdenom;
    1905           0 :         } else if (NumCovers == 2) {
    1906           0 :             for (int Num = 0; Num < NumCovers; ++Num) {
    1907           0 :                 if (Num == 0) {
    1908           0 :                     tempnom = this->CoverAbs[Num] * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) +
    1909           0 :                               TempOutdoorAir * (hConvCoefC2O + hRadCoefC2O) + TempInnerCover * (hConvCoefC2C + hRadCoefC2C);
    1910           0 :                     tempdenom = (hConvCoefC2O + hRadCoefC2O) + (hConvCoefC2C + hRadCoefC2C);
    1911           0 :                     TempOuterCover = tempnom / tempdenom;
    1912           0 :                 } else if (Num == 1) {
    1913           0 :                     tempnom = this->CoverAbs[Num] * state.dataHeatBal->SurfQRadSWOutIncident(SurfNum) + TempAbsPlate * (hConvCoefA2C + hRadCoefA2C) +
    1914           0 :                               TempOuterCover * (hConvCoefC2C + hRadCoefC2C);
    1915           0 :                     tempdenom = (hConvCoefC2C + hRadCoefC2C + hConvCoefA2C + hRadCoefA2C);
    1916           0 :                     TempInnerCover = tempnom / tempdenom;
    1917             :                 }
    1918             :             }
    1919             :         }
    1920       52378 :         this->TempOfInnerCover = TempInnerCover;
    1921       52378 :         this->TempOfOuterCover = TempOuterCover;
    1922       52378 :     }
    1923             : 
    1924       52378 :     Real64 CollectorData::CalcConvCoeffBetweenPlates(Real64 const TempSurf1, // temperature of surface 1
    1925             :                                                      Real64 const TempSurf2, // temperature of surface 1
    1926             :                                                      Real64 const AirGap,    // characteristic length [m]
    1927             :                                                      Real64 const CosTilt,   // cosine of surface tilt angle relative to the horizontal
    1928             :                                                      Real64 const SinTilt    // sine of surface tilt angle relative to the horizontal
    1929             :     )
    1930             :     {
    1931             : 
    1932             :         // FUNCTION INFORMATION:
    1933             :         //       AUTHOR         Bereket Nigusse, FSEC/UCF
    1934             :         //       DATE WRITTEN   February 2012
    1935             : 
    1936             :         // PURPOSE OF THIS FUNCTION:
    1937             :         //   Calculates the convection coefficient for an enclosure between two parallel surfaces
    1938             :         //   at different temperatures.
    1939             :         // METHODOLOGY EMPLOYED:
    1940             :         //   Uses empirical correlation by Holands et al (1976) to determine free convection between
    1941             :         //   inclined parallel plates at different temperature.
    1942             :         // REFERENCES:
    1943             :         //   Duffie, J. A., and Beckman, W. A.  Solar Engineering of Thermal Processes, 2nd. Edition.
    1944             :         //   Wiley-Interscience: New York (1991).
    1945             :         //   Property data for air at atmospheric pressure were taken from Table A-11, Yunus A Cengel
    1946             :         //   Heat Transfer: A Practical Approach, McGraw-Hill, Boston, MA, 1998.
    1947             : 
    1948       52378 :         Real64 constexpr gravity(9.806); // gravitational constant [m/s^2]
    1949             : 
    1950       52378 :         int constexpr NumOfPropDivisions(11);
    1951             :         static constexpr std::array<Real64, NumOfPropDivisions> Temps = {
    1952             :             -23.15, 6.85, 16.85, 24.85, 26.85, 36.85, 46.85, 56.85, 66.85, 76.85, 126.85}; // Temperature, in C
    1953             :         static constexpr std::array<Real64, NumOfPropDivisions> Mu = {
    1954             :             0.0000161, 0.0000175, 0.000018, 0.0000184, 0.0000185, 0.000019, 0.0000194, 0.0000199, 0.0000203, 0.0000208, 0.0000229}; // Viscosity, in
    1955             :                                                                                                                                     // kg/(m.s)
    1956             :         static constexpr std::array<Real64, NumOfPropDivisions> Conductivity = {
    1957             :             0.0223, 0.0246, 0.0253, 0.0259, 0.0261, 0.0268, 0.0275, 0.0283, 0.0290, 0.0297, 0.0331}; // Conductivity, in W/mK
    1958             :         static constexpr std::array<Real64, NumOfPropDivisions> Pr = {
    1959             :             0.724, 0.717, 0.714, 0.712, 0.712, 0.711, 0.71, 0.708, 0.707, 0.706, 0.703}; // Prandtl number (dimensionless)
    1960             :         static constexpr std::array<Real64, NumOfPropDivisions> Density = {
    1961             :             1.413, 1.271, 1.224, 1.186, 1.177, 1.143, 1.110, 1.076, 1.043, 1.009, 0.883}; // Density, in kg/m3
    1962             : 
    1963             :         Real64 CondOfAir; // thermal conductivity of air [W/mK]
    1964             :         Real64 VisDOfAir; // dynamic viscosity of air [kg/m.s]
    1965             :         Real64 DensOfAir; // density of air [W/mK]
    1966             :         Real64 PrOfAir;   // Prandtl number of air [W/mK]
    1967             :         Real64 VolExpAir; // volumetric expansion of air [1/K]
    1968             : 
    1969       52378 :         Real64 DeltaT = std::abs(TempSurf1 - TempSurf2);
    1970       52378 :         Real64 Tref = 0.5 * (TempSurf1 + TempSurf2);
    1971       52378 :         int Index = 0;
    1972      279840 :         while (Index < NumOfPropDivisions) {
    1973      279840 :             if (Tref < Temps[Index]) break; // DO loop
    1974      227462 :             ++Index;
    1975             :         }
    1976             : 
    1977             :         // Initialize thermal properties of air
    1978       52378 :         if (Index == 0) {
    1979           0 :             VisDOfAir = Mu[Index];
    1980           0 :             CondOfAir = Conductivity[Index];
    1981           0 :             PrOfAir = Pr[Index];
    1982           0 :             DensOfAir = Density[Index];
    1983       52378 :         } else if (Index >= NumOfPropDivisions) { // 0-index, hence MaxIndex = NumOfPropDivisions - 1
    1984           0 :             Index = NumOfPropDivisions - 1;
    1985           0 :             VisDOfAir = Mu[Index];
    1986           0 :             CondOfAir = Conductivity[Index];
    1987           0 :             PrOfAir = Pr[Index];
    1988           0 :             DensOfAir = Density[Index];
    1989             :         } else {
    1990       52378 :             Real64 InterpFrac = (Tref - Temps[Index - 1]) / (Temps[Index] - Temps[Index - 1]);
    1991       52378 :             VisDOfAir = Mu[Index - 1] + InterpFrac * (Mu[Index] - Mu[Index - 1]);
    1992       52378 :             CondOfAir = Conductivity[Index - 1] + InterpFrac * (Conductivity[Index] - Conductivity[Index - 1]);
    1993       52378 :             PrOfAir = Pr[Index - 1] + InterpFrac * (Pr[Index] - Pr[Index - 1]);
    1994       52378 :             DensOfAir = Density[Index - 1] + InterpFrac * (Density[Index] - Density[Index - 1]);
    1995             :         }
    1996             : 
    1997       52378 :         VolExpAir = 1.0 / (Tref + Constant::Kelvin);
    1998             : 
    1999             :         // Rayleigh number
    2000       52378 :         Real64 RaNum = gravity * pow_2(DensOfAir) * VolExpAir * PrOfAir * DeltaT * pow_3(AirGap) / pow_2(VisDOfAir);
    2001             : 
    2002             :         // Rayleigh number of air times cosine of collector tilt []
    2003       52378 :         Real64 RaNumCosTilt = RaNum * CosTilt;
    2004             : 
    2005             :         Real64 NuL; // Nusselt number
    2006       52378 :         if (RaNum == 0.0) {
    2007         202 :             NuL = 0.0;
    2008             :         } else {
    2009       52176 :             if (RaNumCosTilt > 1708.0) {
    2010       47584 :                 NuL = 1.44 * (1.0 - 1708.0 * std::pow(SinTilt, 1.6) / (RaNum * CosTilt)) * (1.0 - 1708.0 / RaNumCosTilt);
    2011             :             } else {
    2012        4592 :                 NuL = 0.0;
    2013             :             }
    2014             :         }
    2015       52378 :         if (RaNumCosTilt > 5830.0) {
    2016       41448 :             NuL += std::pow(RaNumCosTilt / 5830.0 - 1.0, 1.0 / 3.0);
    2017             :         }
    2018       52378 :         ++NuL;
    2019       52378 :         Real64 hConvCoef = NuL * CondOfAir / AirGap;
    2020             : 
    2021       52378 :         return hConvCoef;
    2022             :     }
    2023             : 
    2024       52378 :     Real64 CollectorData::CalcConvCoeffAbsPlateAndWater(EnergyPlusData &state,
    2025             :                                                         Real64 const TAbsorber, // temperature of absorber plate [C]
    2026             :                                                         Real64 const TWater,    // temperature of water [C]
    2027             :                                                         Real64 const Lc,        // characteristic length [m]
    2028             :                                                         Real64 const TiltR2V    // collector tilt angle relative to the vertical [degree]
    2029             :     )
    2030             :     {
    2031             : 
    2032             :         // FUNCTION INFORMATION:
    2033             :         //       AUTHOR         Bereket Nigusse, FSEC/UCF
    2034             :         //       DATE WRITTEN   February 2012
    2035             : 
    2036             :         // PURPOSE OF THIS FUNCTION:
    2037             :         //  Calculates the free convection coefficient between the absorber plate and water.
    2038             :         // METHODOLOGY EMPLOYED:
    2039             :         //  The convection coefficient calculation were based on the Fujii and Imura emperical correlations
    2040             :         // REFERENCES:
    2041             :         //  T.Fujii, and H.Imura,Natural convection heat transfer from aplate with arbitrary inclination.
    2042             :         //  International Journal of Heat and Mass Transfer: 15(4), (1972), 755-764.
    2043             : 
    2044             :         Real64 hConvA2W; // convection coefficient, [W/m2K]
    2045             : 
    2046       52378 :         Real64 constexpr gravity(9.806); // gravitational constant [m/s^2]
    2047             :         static constexpr std::string_view CalledFrom("SolarCollectors:CalcConvCoeffAbsPlateAndWater");
    2048             : 
    2049       52378 :         Real64 DeltaT = std::abs(TAbsorber - TWater);
    2050       52378 :         Real64 TReference = TAbsorber - 0.25 * (TAbsorber - TWater);
    2051             :         // record fluid prop index for water
    2052       52378 :         int WaterIndex = FluidProperties::GetGlycolNum(state, fluidNameWater);
    2053             :         // find properties of water - always assume water
    2054       52378 :         Real64 WaterSpecHeat = FluidProperties::GetSpecificHeatGlycol(state, fluidNameWater, max(TReference, 0.0), WaterIndex, CalledFrom);
    2055       52378 :         Real64 CondOfWater = FluidProperties::GetConductivityGlycol(state, fluidNameWater, max(TReference, 0.0), WaterIndex, CalledFrom);
    2056       52378 :         Real64 VisOfWater = FluidProperties::GetViscosityGlycol(state, fluidNameWater, max(TReference, 0.0), WaterIndex, CalledFrom);
    2057       52378 :         Real64 DensOfWater = FluidProperties::GetDensityGlycol(state, fluidNameWater, max(TReference, 0.0), WaterIndex, CalledFrom);
    2058       52378 :         Real64 PrOfWater = VisOfWater * WaterSpecHeat / CondOfWater;
    2059             :         // Requires a different reference temperature for volumetric expansion coefficient
    2060       52378 :         TReference = TWater - 0.25 * (TWater - TAbsorber);
    2061       52378 :         Real64 VolExpWater = -(FluidProperties::GetDensityGlycol(state, fluidNameWater, max(TReference, 10.0) + 5.0, WaterIndex, CalledFrom) -
    2062       52378 :                                FluidProperties::GetDensityGlycol(state, fluidNameWater, max(TReference, 10.0) - 5.0, WaterIndex, CalledFrom)) /
    2063       52378 :                              (10.0 * DensOfWater);
    2064             : 
    2065             :         // Grashof number
    2066       52378 :         Real64 GrNum = gravity * VolExpWater * DensOfWater * DensOfWater * PrOfWater * DeltaT * pow_3(Lc) / pow_2(VisOfWater);
    2067       52378 :         Real64 CosTilt = std::cos(TiltR2V * Constant::DegToRadians);
    2068             : 
    2069             :         Real64 RaNum; // Raleigh number
    2070             :         Real64 NuL;   // Nusselt number
    2071       52378 :         if (TAbsorber > TWater) {
    2072             :             // hot absorber plate facing down
    2073       20192 :             if (std::abs(TiltR2V - 90.0) < 1.0) {
    2074             :                 // It is a horizontal surface
    2075           0 :                 RaNum = GrNum * PrOfWater;
    2076           0 :                 if (RaNum <= 1708.0) {
    2077           0 :                     NuL = 1.0;
    2078             :                 } else {
    2079           0 :                     NuL = 0.58 * std::pow(RaNum, 0.20);
    2080             :                 }
    2081             :             } else {
    2082       20192 :                 RaNum = GrNum * PrOfWater * CosTilt;
    2083       20192 :                 if (RaNum <= 1708.0) {
    2084           0 :                     NuL = 1.0;
    2085             :                 } else {
    2086       20192 :                     NuL = 0.56 * root_4(RaNum);
    2087             :                 }
    2088             :             }
    2089             :         } else {
    2090             :             // cold plate facing down or hot plate facing up
    2091       32186 :             RaNum = GrNum * PrOfWater;
    2092       32186 :             if (RaNum > 5.0e8) {
    2093       31984 :                 NuL = 0.13 * std::pow(RaNum, 1.0 / 3.0);
    2094             :             } else {
    2095         202 :                 NuL = 0.16 * std::pow(RaNum, 1.0 / 3.0);
    2096         202 :                 if (RaNum <= 1708.0) NuL = 1.0;
    2097             :             }
    2098             :         }
    2099       52378 :         hConvA2W = NuL * CondOfWater / Lc;
    2100             : 
    2101       52378 :         return hConvA2W;
    2102             :     }
    2103             : 
    2104      121166 :     void CollectorData::update(EnergyPlusData &state) // NOLINT(readability-make-member-function-const)
    2105             :     {
    2106             : 
    2107             :         // SUBROUTINE INFORMATION:
    2108             :         //       AUTHOR         Peter Graham Ellis
    2109             :         //       DATE WRITTEN   January 2004
    2110             : 
    2111             :         // PURPOSE OF THIS SUBROUTINE:
    2112             :         // Updates the node variables with local variables.
    2113             : 
    2114             :         static constexpr std::string_view RoutineName("UpdateSolarCollector");
    2115             : 
    2116      121166 :         PlantUtilities::SafeCopyPlantNode(state, this->InletNode, this->OutletNode);
    2117             :         // Set outlet node variables that are possibly changed
    2118      121166 :         state.dataLoopNodes->Node(this->OutletNode).Temp = this->OutletTemp;
    2119      121166 :         Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state,
    2120      121166 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidName,
    2121             :                                                            this->OutletTemp,
    2122      121166 :                                                            state.dataPlnt->PlantLoop(this->plantLoc.loopNum).FluidIndex,
    2123             :                                                            RoutineName);
    2124      121166 :         state.dataLoopNodes->Node(this->OutletNode).Enthalpy = Cp * state.dataLoopNodes->Node(this->OutletNode).Temp;
    2125      121166 :     }
    2126             : 
    2127      121166 :     void CollectorData::report(EnergyPlusData &state)
    2128             :     {
    2129             : 
    2130             :         // SUBROUTINE INFORMATION:
    2131             :         //       AUTHOR         Peter Graham Ellis
    2132             :         //       DATE WRITTEN   January 2004
    2133             : 
    2134             :         // PURPOSE OF THIS SUBROUTINE:
    2135             :         // Calculates report variables.
    2136             : 
    2137      121166 :         Real64 TimeStepInSecond = state.dataHVACGlobal->TimeStepSysSec;
    2138             : 
    2139      121166 :         this->Energy = this->Power * TimeStepInSecond;
    2140      121166 :         this->HeatEnergy = this->HeatRate * TimeStepInSecond;
    2141      121166 :         this->CollHeatLossEnergy = this->SkinHeatLossRate * TimeStepInSecond;
    2142      121166 :         this->StoredHeatEnergy = this->StoredHeatRate * TimeStepInSecond;
    2143      121166 :     }
    2144             : 
    2145           2 :     void CollectorData::GetExtVentedCavityIndex(EnergyPlusData &state, int const SurfacePtr, int &VentCavIndex)
    2146             :     {
    2147             : 
    2148             :         // SUBROUTINE INFORMATION:
    2149             :         //       AUTHOR         B. Nigusse, FSEC. Adopted from Photovoltaics module
    2150             :         //       DATE WRITTEN   February 2012
    2151             : 
    2152             :         // PURPOSE OF THIS SUBROUTINE:
    2153             :         // object oriented "Get" routine for establishing correct integer index from outside this module
    2154             : 
    2155             :         // METHODOLOGY EMPLOYED:
    2156             :         // mine Surface derived type for correct index/number of surface
    2157             :         // mine  ExtVentedCavity derived type that has the surface.
    2158             :         // Adapted from Photovoltaics module, originally developed by Brent G. (2004)
    2159             : 
    2160             :         bool Found;
    2161             : 
    2162           2 :         if (SurfacePtr == 0) {
    2163             :             // should be trapped already
    2164           0 :             ShowFatalError(state, "Invalid surface passed to GetExtVentedCavityIndex");
    2165             :         }
    2166             : 
    2167           2 :         int CavNum = 0;
    2168           2 :         Found = false;
    2169           6 :         for (int thisCav = 1; thisCav <= state.dataSurface->TotExtVentCav; ++thisCav) {
    2170           8 :             for (int ThisSurf = 1; ThisSurf <= state.dataHeatBal->ExtVentedCavity(thisCav).NumSurfs; ++ThisSurf) {
    2171           4 :                 if (SurfacePtr == state.dataHeatBal->ExtVentedCavity(thisCav).SurfPtrs(ThisSurf)) {
    2172           2 :                     Found = true;
    2173           2 :                     CavNum = thisCav;
    2174             :                 }
    2175             :             }
    2176             :         }
    2177             : 
    2178           2 :         if (!Found) {
    2179           0 :             ShowFatalError(state,
    2180           0 :                            format("Did not find surface in Exterior Vented Cavity description in GetExtVentedCavityIndex, Surface name = {}",
    2181           0 :                                   state.dataSurface->Surface(SurfacePtr).Name));
    2182             :         } else {
    2183             : 
    2184           2 :             VentCavIndex = CavNum;
    2185             :         }
    2186           2 :     }
    2187          10 :     void CollectorData::oneTimeInit_new(EnergyPlusData &state)
    2188             :     {
    2189             : 
    2190          10 :         this->setupOutputVars(state);
    2191             : 
    2192          10 :         if (this->SetLoopIndexFlag) {
    2193          10 :             if (allocated(state.dataPlnt->PlantLoop)) {
    2194          10 :                 bool errFlag = false;
    2195          10 :                 PlantUtilities::ScanPlantLoopsForObject(state, this->Name, this->Type, this->plantLoc, errFlag, _, _, _, _, _);
    2196          10 :                 if (errFlag) {
    2197           0 :                     ShowFatalError(state, "InitSolarCollector: Program terminated due to previous condition(s).");
    2198             :                 }
    2199          10 :                 this->SetLoopIndexFlag = false;
    2200             :             }
    2201             :         }
    2202          10 :     }
    2203           0 :     void CollectorData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
    2204             :     {
    2205           0 :     }
    2206             : 
    2207             : } // namespace SolarCollectors
    2208             : 
    2209             : } // namespace EnergyPlus

Generated by: LCOV version 1.14