LCOV - code coverage report
Current view: top level - EnergyPlus - SolarCollectors.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 733 974 75.3 %
Date: 2023-01-17 19:17:23 Functions: 20 21 95.2 %

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

Generated by: LCOV version 1.13