LCOV - code coverage report
Current view: top level - EnergyPlus - PhotovoltaicThermalCollectors.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 422 624 67.6 %
Date: 2023-01-17 19:17:23 Functions: 16 18 88.9 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <cmath>
      50             : 
      51             : // ObjexxFCL Headers
      52             : #include <ObjexxFCL/Array.functions.hh>
      53             : #include <ObjexxFCL/Fmath.hh>
      54             : 
      55             : // EnergyPlus Headers
      56             : #include <EnergyPlus/Autosizing/Base.hh>
      57             : #include <EnergyPlus/BranchNodeConnections.hh>
      58             : #include <EnergyPlus/ConvectionCoefficients.hh>
      59             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      60             : #include <EnergyPlus/DataEnvironment.hh>
      61             : #include <EnergyPlus/DataHVACGlobals.hh>
      62             : #include <EnergyPlus/DataHeatBalance.hh>
      63             : #include <EnergyPlus/DataIPShortCuts.hh>
      64             : #include <EnergyPlus/DataLoopNode.hh>
      65             : #include <EnergyPlus/DataPhotovoltaics.hh>
      66             : #include <EnergyPlus/DataSizing.hh>
      67             : #include <EnergyPlus/DataSurfaces.hh>
      68             : #include <EnergyPlus/EMSManager.hh>
      69             : #include <EnergyPlus/FluidProperties.hh>
      70             : #include <EnergyPlus/General.hh>
      71             : #include <EnergyPlus/GeneralRoutines.hh>
      72             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      73             : #include <EnergyPlus/NodeInputManager.hh>
      74             : #include <EnergyPlus/OutputProcessor.hh>
      75             : #include <EnergyPlus/PhotovoltaicThermalCollectors.hh>
      76             : #include <EnergyPlus/Plant/DataPlant.hh>
      77             : #include <EnergyPlus/Plant/PlantLocation.hh>
      78             : #include <EnergyPlus/PlantUtilities.hh>
      79             : #include <EnergyPlus/Psychrometrics.hh>
      80             : #include <EnergyPlus/ScheduleManager.hh>
      81             : #include <EnergyPlus/UtilityRoutines.hh>
      82             : 
      83             : namespace EnergyPlus {
      84             : 
      85             : namespace PhotovoltaicThermalCollectors {
      86             : 
      87             :     // Module containing the routines dealing with the photovoltaic thermal collectors
      88             : 
      89             :     // MODULE INFORMATION:
      90             :     //       AUTHOR         Brent. Griffith
      91             :     //       DATE WRITTEN   June-August 2008
      92             :     //       MODIFIED       na
      93             :     //       RE-ENGINEERED  na
      94             : 
      95             :     // PURPOSE OF THIS MODULE:
      96             :     // collect models related to PVT or hybrid, photovoltaic - thermal solar collectors
      97             : 
      98             :     // METHODOLOGY EMPLOYED:
      99             :     // The approach is to have one PVT structure that works with different models.
     100             :     //  the PVT model reuses photovoltaic modeling in Photovoltaics.cc for electricity generation.
     101             :     //  the electric load center and "generator" is all accessed thru PV objects and models.
     102             :     //  this module is for the thermal portion of PVT.
     103             :     //  the first model is a "simple" or "ideal" model useful for sizing, early design, or policy analyses
     104             :     //  Simple PV/T model just converts incoming solar to electricity and temperature rise of a working fluid.
     105             : 
     106             :     int constexpr SimplePVTmodel(1001);
     107             : 
     108             :     Real64 constexpr SimplePVTWaterSizeFactor(1.905e-5); // [ m3/s/m2 ] average of collectors in SolarCollectors.idf
     109             : 
     110           5 :     PlantComponent *PVTCollectorStruct::factory(EnergyPlusData &state, std::string_view objectName)
     111             :     {
     112           5 :         if (state.dataPhotovoltaicThermalCollector->GetInputFlag) {
     113           1 :             GetPVTcollectorsInput(state);
     114           1 :             state.dataPhotovoltaicThermalCollector->GetInputFlag = false;
     115             :         }
     116             : 
     117          40 :         for (auto &thisComp : state.dataPhotovoltaicThermalCollector->PVT) {
     118          40 :             if (thisComp.Name == objectName) {
     119           5 :                 return &thisComp;
     120             :             }
     121             :         }
     122             : 
     123             :         // If we didn't find it, fatal
     124           0 :         ShowFatalError(state, "Solar Thermal Collector Factory: Error getting inputs for object named: " + std::string{objectName});
     125             :         // Shut up the compiler
     126           0 :         return nullptr;
     127             :     }
     128             : 
     129          25 :     void PVTCollectorStruct::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
     130             :     {
     131          25 :         this->initialize(state, true);
     132          25 :         this->size(state);
     133          25 :     }
     134             : 
     135      197268 :     void PVTCollectorStruct::simulate(EnergyPlusData &state,
     136             :                                       [[maybe_unused]] const PlantLocation &calledFromLocation,
     137             :                                       bool const FirstHVACIteration,
     138             :                                       [[maybe_unused]] Real64 &CurLoad,
     139             :                                       [[maybe_unused]] bool const RunFlag)
     140             :     {
     141             : 
     142      197268 :         this->initialize(state, FirstHVACIteration);
     143      197268 :         this->control(state);
     144      197268 :         this->calculate(state);
     145      197268 :         this->update(state);
     146      197268 :     }
     147             : 
     148           1 :     void GetPVTcollectorsInput(EnergyPlusData &state)
     149             :     {
     150             : 
     151             :         // SUBROUTINE INFORMATION:
     152             :         //       AUTHOR         B. Griffith
     153             :         //       DATE WRITTEN   June 2008
     154             :         //       MODIFIED       na
     155             :         //       RE-ENGINEERED  na
     156             : 
     157             :         // PURPOSE OF THIS SUBROUTINE:
     158             :         // Get input for PVT objects
     159             : 
     160             :         int Item;                // Item to be "gotten"
     161             :         int NumAlphas;           // Number of Alphas for each GetObjectItem call
     162             :         int NumNumbers;          // Number of Numbers for each GetObjectItem call
     163             :         int IOStatus;            // Used in GetObjectItem
     164           1 :         bool ErrorsFound(false); // Set to true if errors in input, fatal at end of routine
     165             : 
     166             :         // Object Data
     167           2 :         Array1D<SimplePVTModelStruct> tmpSimplePVTperf;
     168             : 
     169             :         // first load the performance object info into temporary structure
     170           1 :         state.dataIPShortCut->cCurrentModuleObject = "SolarCollectorPerformance:PhotovoltaicThermal:Simple";
     171           1 :         int NumSimplePVTPerform = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
     172           1 :         if (NumSimplePVTPerform > 0) {
     173           1 :             tmpSimplePVTperf.allocate(NumSimplePVTPerform);
     174           3 :             for (Item = 1; Item <= NumSimplePVTPerform; ++Item) {
     175          14 :                 state.dataInputProcessing->inputProcessor->getObjectItem(state,
     176           2 :                                                                          state.dataIPShortCut->cCurrentModuleObject,
     177             :                                                                          Item,
     178           2 :                                                                          state.dataIPShortCut->cAlphaArgs,
     179             :                                                                          NumAlphas,
     180           2 :                                                                          state.dataIPShortCut->rNumericArgs,
     181             :                                                                          NumNumbers,
     182             :                                                                          IOStatus,
     183             :                                                                          _,
     184           2 :                                                                          state.dataIPShortCut->lAlphaFieldBlanks,
     185           2 :                                                                          state.dataIPShortCut->cAlphaFieldNames,
     186           2 :                                                                          state.dataIPShortCut->cNumericFieldNames);
     187           2 :                 if (UtilityRoutines::IsNameEmpty(state, state.dataIPShortCut->cAlphaArgs(1), state.dataIPShortCut->cCurrentModuleObject, ErrorsFound))
     188           0 :                     continue;
     189             : 
     190           2 :                 tmpSimplePVTperf(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
     191           2 :                 if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "Fixed")) {
     192           2 :                     tmpSimplePVTperf(Item).ThermEfficMode = ThermEfficEnum::FIXED;
     193           0 :                 } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(2), "Scheduled")) {
     194           0 :                     tmpSimplePVTperf(Item).ThermEfficMode = ThermEfficEnum::SCHEDULED;
     195             :                 } else {
     196           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + " = " + state.dataIPShortCut->cAlphaArgs(2));
     197           0 :                     ShowContinueError(state,
     198           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     199           0 :                     ErrorsFound = true;
     200             :                 }
     201           2 :                 tmpSimplePVTperf(Item).ThermalActiveFract = state.dataIPShortCut->rNumericArgs(1);
     202           2 :                 tmpSimplePVTperf(Item).ThermEffic = state.dataIPShortCut->rNumericArgs(2);
     203             : 
     204           2 :                 tmpSimplePVTperf(Item).ThermEffSchedNum = ScheduleManager::GetScheduleIndex(state, state.dataIPShortCut->cAlphaArgs(3));
     205           2 :                 if ((tmpSimplePVTperf(Item).ThermEffSchedNum == 0) && (tmpSimplePVTperf(Item).ThermEfficMode == ThermEfficEnum::SCHEDULED)) {
     206           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + " = " + state.dataIPShortCut->cAlphaArgs(3));
     207           0 :                     ShowContinueError(state,
     208           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     209           0 :                     ErrorsFound = true;
     210             :                 }
     211           2 :                 tmpSimplePVTperf(Item).SurfEmissivity = state.dataIPShortCut->rNumericArgs(3);
     212             :             }
     213             :         } // NumSimplePVTPerform > 0
     214             : 
     215             :         // now get main PVT objects
     216           1 :         state.dataIPShortCut->cCurrentModuleObject = "SolarCollector:FlatPlate:PhotovoltaicThermal";
     217           1 :         state.dataPhotovoltaicThermalCollector->NumPVT =
     218           1 :             state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
     219           1 :         state.dataPhotovoltaicThermalCollector->PVT.allocate(state.dataPhotovoltaicThermalCollector->NumPVT);
     220             : 
     221          11 :         for (Item = 1; Item <= state.dataPhotovoltaicThermalCollector->NumPVT; ++Item) {
     222          70 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     223          10 :                                                                      state.dataIPShortCut->cCurrentModuleObject,
     224             :                                                                      Item,
     225          10 :                                                                      state.dataIPShortCut->cAlphaArgs,
     226             :                                                                      NumAlphas,
     227          10 :                                                                      state.dataIPShortCut->rNumericArgs,
     228             :                                                                      NumNumbers,
     229             :                                                                      IOStatus,
     230             :                                                                      _,
     231          10 :                                                                      state.dataIPShortCut->lAlphaFieldBlanks,
     232          10 :                                                                      state.dataIPShortCut->cAlphaFieldNames,
     233          10 :                                                                      state.dataIPShortCut->cNumericFieldNames);
     234          10 :             if (UtilityRoutines::IsNameEmpty(state, state.dataIPShortCut->cAlphaArgs(1), state.dataIPShortCut->cCurrentModuleObject, ErrorsFound))
     235           0 :                 continue;
     236             : 
     237          10 :             state.dataPhotovoltaicThermalCollector->PVT(Item).Name = state.dataIPShortCut->cAlphaArgs(1);
     238          10 :             state.dataPhotovoltaicThermalCollector->PVT(Item).Type = DataPlant::PlantEquipmentType::PVTSolarCollectorFlatPlate;
     239             : 
     240          10 :             state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum =
     241          10 :                 UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(2), state.dataSurface->Surface);
     242             :             // check surface
     243          10 :             if (state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum == 0) {
     244           0 :                 if (state.dataIPShortCut->lAlphaFieldBlanks(2)) {
     245           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + " = " + state.dataIPShortCut->cAlphaArgs(2));
     246           0 :                     ShowContinueError(state,
     247           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     248             : 
     249           0 :                     ShowContinueError(state, "Surface name cannot be blank.");
     250             :                 } else {
     251           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + " = " + state.dataIPShortCut->cAlphaArgs(2));
     252           0 :                     ShowContinueError(state,
     253           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     254           0 :                     ShowContinueError(state, "Surface was not found.");
     255             :                 }
     256           0 :                 ErrorsFound = true;
     257             :             } else {
     258             : 
     259          10 :                 if (!state.dataSurface->Surface(state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum).ExtSolar) {
     260           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + " = " + state.dataIPShortCut->cAlphaArgs(2));
     261           0 :                     ShowContinueError(state,
     262           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     263           0 :                     ShowContinueError(state, "Surface must be exposed to solar.");
     264           0 :                     ErrorsFound = true;
     265             :                 }
     266             :                 // check surface orientation, warn if upside down
     267          20 :                 if ((state.dataSurface->Surface(state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum).Tilt < -95.0) ||
     268          10 :                     (state.dataSurface->Surface(state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum).Tilt > 95.0)) {
     269           0 :                     ShowWarningError(state,
     270           0 :                                      "Suspected input problem with " + state.dataIPShortCut->cAlphaFieldNames(2) + " = " +
     271           0 :                                          state.dataIPShortCut->cAlphaArgs(2));
     272           0 :                     ShowContinueError(state,
     273           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     274           0 :                     ShowContinueError(state, "Surface used for solar collector faces down");
     275           0 :                     ShowContinueError(state,
     276           0 :                                       format("Surface tilt angle (degrees from ground outward normal) = {:.2R}",
     277           0 :                                              state.dataSurface->Surface(state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum).Tilt));
     278             :                 }
     279             : 
     280             :             } // check surface
     281             : 
     282          10 :             if (state.dataIPShortCut->lAlphaFieldBlanks(3)) {
     283           0 :                 ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + " = " + state.dataIPShortCut->cAlphaArgs(3));
     284           0 :                 ShowContinueError(state, "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     285           0 :                 ShowContinueError(state, state.dataIPShortCut->cAlphaFieldNames(3) + ", name cannot be blank.");
     286           0 :                 ErrorsFound = true;
     287             :             } else {
     288          10 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).PVTModelName = state.dataIPShortCut->cAlphaArgs(3);
     289          10 :                 int ThisParamObj = UtilityRoutines::FindItemInList(state.dataPhotovoltaicThermalCollector->PVT(Item).PVTModelName, tmpSimplePVTperf);
     290          10 :                 if (ThisParamObj > 0) {
     291          10 :                     state.dataPhotovoltaicThermalCollector->PVT(Item).Simple = tmpSimplePVTperf(ThisParamObj); // entire structure assigned
     292             :                     // do one-time setups on input data
     293          10 :                     state.dataPhotovoltaicThermalCollector->PVT(Item).AreaCol =
     294          20 :                         state.dataSurface->Surface(state.dataPhotovoltaicThermalCollector->PVT(Item).SurfNum).Area *
     295          10 :                         state.dataPhotovoltaicThermalCollector->PVT(Item).Simple.ThermalActiveFract;
     296          10 :                     state.dataPhotovoltaicThermalCollector->PVT(Item).PVTModelType = SimplePVTmodel;
     297             :                 } else {
     298           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(3) + " = " + state.dataIPShortCut->cAlphaArgs(3));
     299           0 :                     ShowContinueError(state,
     300           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     301           0 :                     ShowContinueError(state, state.dataIPShortCut->cAlphaFieldNames(3) + ", was not found.");
     302           0 :                     ErrorsFound = true;
     303             :                 }
     304             :             }
     305          10 :             if (allocated(state.dataPhotovoltaic->PVarray)) { // then PV input gotten... but don't expect this to be true.
     306           0 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).PVnum =
     307           0 :                     UtilityRoutines::FindItemInList(state.dataIPShortCut->cAlphaArgs(4), state.dataPhotovoltaic->PVarray);
     308             :                 // check PV
     309           0 :                 if (state.dataPhotovoltaicThermalCollector->PVT(Item).PVnum == 0) {
     310           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(4) + " = " + state.dataIPShortCut->cAlphaArgs(4));
     311           0 :                     ShowContinueError(state,
     312           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     313           0 :                     ErrorsFound = true;
     314             :                 } else {
     315           0 :                     state.dataPhotovoltaicThermalCollector->PVT(Item).PVname = state.dataIPShortCut->cAlphaArgs(4);
     316           0 :                     state.dataPhotovoltaicThermalCollector->PVT(Item).PVfound = true;
     317             :                 }
     318             :             } else { // no PV or not yet gotten.
     319          10 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).PVname = state.dataIPShortCut->cAlphaArgs(4);
     320          10 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).PVfound = false;
     321             :             }
     322             : 
     323          10 :             if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(5), "Water")) {
     324           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).WorkingFluidType = WorkingFluidEnum::LIQUID;
     325           5 :             } else if (UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(5), "Air")) {
     326           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).WorkingFluidType = WorkingFluidEnum::AIR;
     327             :             } else {
     328           0 :                 if (state.dataIPShortCut->lAlphaFieldBlanks(5)) {
     329           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(5) + " = " + state.dataIPShortCut->cAlphaArgs(5));
     330           0 :                     ShowContinueError(state,
     331           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     332           0 :                     ShowContinueError(state, state.dataIPShortCut->cAlphaFieldNames(5) + " field cannot be blank.");
     333             :                 } else {
     334           0 :                     ShowSevereError(state, "Invalid " + state.dataIPShortCut->cAlphaFieldNames(5) + " = " + state.dataIPShortCut->cAlphaArgs(5));
     335           0 :                     ShowContinueError(state,
     336           0 :                                       "Entered in " + state.dataIPShortCut->cCurrentModuleObject + " = " + state.dataIPShortCut->cAlphaArgs(1));
     337             :                 }
     338           0 :                 ErrorsFound = true;
     339             :             }
     340             : 
     341          10 :             if (state.dataPhotovoltaicThermalCollector->PVT(Item).WorkingFluidType == WorkingFluidEnum::LIQUID) {
     342           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).PlantInletNodeNum =
     343          10 :                     NodeInputManager::GetOnlySingleNode(state,
     344           5 :                                                         state.dataIPShortCut->cAlphaArgs(6),
     345             :                                                         ErrorsFound,
     346             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorFlatPlatePhotovoltaicThermal,
     347           5 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     348             :                                                         DataLoopNode::NodeFluidType::Water,
     349             :                                                         DataLoopNode::ConnectionType::Inlet,
     350             :                                                         NodeInputManager::CompFluidStream::Primary,
     351           5 :                                                         DataLoopNode::ObjectIsNotParent);
     352           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).PlantOutletNodeNum =
     353          10 :                     NodeInputManager::GetOnlySingleNode(state,
     354           5 :                                                         state.dataIPShortCut->cAlphaArgs(7),
     355             :                                                         ErrorsFound,
     356             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorFlatPlatePhotovoltaicThermal,
     357           5 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     358             :                                                         DataLoopNode::NodeFluidType::Water,
     359             :                                                         DataLoopNode::ConnectionType::Outlet,
     360             :                                                         NodeInputManager::CompFluidStream::Primary,
     361           5 :                                                         DataLoopNode::ObjectIsNotParent);
     362             : 
     363          15 :                 BranchNodeConnections::TestCompSet(state,
     364           5 :                                                    state.dataIPShortCut->cCurrentModuleObject,
     365           5 :                                                    state.dataIPShortCut->cAlphaArgs(1),
     366           5 :                                                    state.dataIPShortCut->cAlphaArgs(6),
     367           5 :                                                    state.dataIPShortCut->cAlphaArgs(7),
     368             :                                                    "Water Nodes");
     369             : 
     370           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).WPlantLoc.loopSideNum = DataPlant::LoopSideLocation::Invalid;
     371             :             }
     372             : 
     373          10 :             if (state.dataPhotovoltaicThermalCollector->PVT(Item).WorkingFluidType == WorkingFluidEnum::AIR) {
     374           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).HVACInletNodeNum =
     375          10 :                     NodeInputManager::GetOnlySingleNode(state,
     376           5 :                                                         state.dataIPShortCut->cAlphaArgs(8),
     377             :                                                         ErrorsFound,
     378             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorFlatPlatePhotovoltaicThermal,
     379           5 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     380             :                                                         DataLoopNode::NodeFluidType::Air,
     381             :                                                         DataLoopNode::ConnectionType::Inlet,
     382             :                                                         NodeInputManager::CompFluidStream::Primary,
     383           5 :                                                         DataLoopNode::ObjectIsNotParent);
     384           5 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).HVACOutletNodeNum =
     385          10 :                     NodeInputManager::GetOnlySingleNode(state,
     386           5 :                                                         state.dataIPShortCut->cAlphaArgs(9),
     387             :                                                         ErrorsFound,
     388             :                                                         DataLoopNode::ConnectionObjectType::SolarCollectorFlatPlatePhotovoltaicThermal,
     389           5 :                                                         state.dataIPShortCut->cAlphaArgs(1),
     390             :                                                         DataLoopNode::NodeFluidType::Air,
     391             :                                                         DataLoopNode::ConnectionType::Outlet,
     392             :                                                         NodeInputManager::CompFluidStream::Primary,
     393           5 :                                                         DataLoopNode::ObjectIsNotParent);
     394             : 
     395          15 :                 BranchNodeConnections::TestCompSet(state,
     396           5 :                                                    state.dataIPShortCut->cCurrentModuleObject,
     397           5 :                                                    state.dataIPShortCut->cAlphaArgs(1),
     398           5 :                                                    state.dataIPShortCut->cAlphaArgs(8),
     399           5 :                                                    state.dataIPShortCut->cAlphaArgs(9),
     400             :                                                    "Air Nodes");
     401             :             }
     402             : 
     403          10 :             state.dataPhotovoltaicThermalCollector->PVT(Item).DesignVolFlowRate = state.dataIPShortCut->rNumericArgs(1);
     404          10 :             state.dataPhotovoltaicThermalCollector->PVT(Item).SizingInit = true;
     405          10 :             if (state.dataPhotovoltaicThermalCollector->PVT(Item).DesignVolFlowRate == DataSizing::AutoSize) {
     406          10 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).DesignVolFlowRateWasAutoSized = true;
     407             :             }
     408          10 :             if (state.dataPhotovoltaicThermalCollector->PVT(Item).DesignVolFlowRate != DataSizing::AutoSize) {
     409             : 
     410           0 :                 if (state.dataPhotovoltaicThermalCollector->PVT(Item).WorkingFluidType == WorkingFluidEnum::LIQUID) {
     411           0 :                     PlantUtilities::RegisterPlantCompDesignFlow(state,
     412           0 :                                                                 state.dataPhotovoltaicThermalCollector->PVT(Item).PlantInletNodeNum,
     413           0 :                                                                 state.dataPhotovoltaicThermalCollector->PVT(Item).DesignVolFlowRate);
     414           0 :                 } else if (state.dataPhotovoltaicThermalCollector->PVT(Item).WorkingFluidType == WorkingFluidEnum::AIR) {
     415           0 :                     state.dataPhotovoltaicThermalCollector->PVT(Item).MaxMassFlowRate =
     416           0 :                         state.dataPhotovoltaicThermalCollector->PVT(Item).DesignVolFlowRate * state.dataEnvrn->StdRhoAir;
     417             :                 }
     418           0 :                 state.dataPhotovoltaicThermalCollector->PVT(Item).SizingInit = false;
     419             :             }
     420             :         }
     421             : 
     422           1 :         if (ErrorsFound) {
     423           0 :             ShowFatalError(state, "Errors found in processing input for photovoltaic thermal collectors");
     424             :         }
     425             : 
     426           1 :         if (allocated(tmpSimplePVTperf)) tmpSimplePVTperf.deallocate();
     427           1 :     }
     428             : 
     429          10 :     void PVTCollectorStruct::setupReportVars(EnergyPlusData &state)
     430             :     {
     431          20 :         SetupOutputVariable(state,
     432             :                             "Generator Produced Thermal Rate",
     433             :                             OutputProcessor::Unit::W,
     434             :                             this->Report.ThermPower,
     435             :                             OutputProcessor::SOVTimeStepType::System,
     436             :                             OutputProcessor::SOVStoreType::Average,
     437          10 :                             this->Name);
     438             : 
     439          10 :         if (this->WorkingFluidType == WorkingFluidEnum::LIQUID) {
     440          10 :             SetupOutputVariable(state,
     441             :                                 "Generator Produced Thermal Energy",
     442             :                                 OutputProcessor::Unit::J,
     443             :                                 this->Report.ThermEnergy,
     444             :                                 OutputProcessor::SOVTimeStepType::System,
     445             :                                 OutputProcessor::SOVStoreType::Summed,
     446             :                                 this->Name,
     447             :                                 _,
     448             :                                 "SolarWater",
     449             :                                 "HeatProduced",
     450             :                                 _,
     451           5 :                                 "Plant");
     452             : 
     453           5 :         } else if (this->WorkingFluidType == WorkingFluidEnum::AIR) {
     454          10 :             SetupOutputVariable(state,
     455             :                                 "Generator Produced Thermal Energy",
     456             :                                 OutputProcessor::Unit::J,
     457             :                                 this->Report.ThermEnergy,
     458             :                                 OutputProcessor::SOVTimeStepType::System,
     459             :                                 OutputProcessor::SOVStoreType::Summed,
     460             :                                 this->Name,
     461             :                                 _,
     462             :                                 "SolarAir",
     463             :                                 "HeatProduced",
     464             :                                 _,
     465           5 :                                 "System");
     466             : 
     467          10 :             SetupOutputVariable(state,
     468             :                                 "Generator PVT Fluid Bypass Status",
     469             :                                 OutputProcessor::Unit::None,
     470             :                                 this->Report.BypassStatus,
     471             :                                 OutputProcessor::SOVTimeStepType::System,
     472             :                                 OutputProcessor::SOVStoreType::Average,
     473           5 :                                 this->Name);
     474             :         }
     475             : 
     476          20 :         SetupOutputVariable(state,
     477             :                             "Generator PVT Fluid Inlet Temperature",
     478             :                             OutputProcessor::Unit::C,
     479             :                             this->Report.TinletWorkFluid,
     480             :                             OutputProcessor::SOVTimeStepType::System,
     481             :                             OutputProcessor::SOVStoreType::Average,
     482          10 :                             this->Name);
     483             : 
     484          20 :         SetupOutputVariable(state,
     485             :                             "Generator PVT Fluid Outlet Temperature",
     486             :                             OutputProcessor::Unit::C,
     487             :                             this->Report.ToutletWorkFluid,
     488             :                             OutputProcessor::SOVTimeStepType::System,
     489             :                             OutputProcessor::SOVStoreType::Average,
     490          10 :                             this->Name);
     491             : 
     492          20 :         SetupOutputVariable(state,
     493             :                             "Generator PVT Fluid Mass Flow Rate",
     494             :                             OutputProcessor::Unit::kg_s,
     495             :                             this->Report.MdotWorkFluid,
     496             :                             OutputProcessor::SOVTimeStepType::System,
     497             :                             OutputProcessor::SOVStoreType::Average,
     498          10 :                             this->Name);
     499          10 :     }
     500             : 
     501      197293 :     void PVTCollectorStruct::initialize(EnergyPlusData &state, bool const FirstHVACIteration)
     502             :     {
     503             : 
     504             :         // SUBROUTINE INFORMATION:
     505             :         //       AUTHOR         B. Griffith
     506             :         //       DATE WRITTEN   June 2008
     507             :         //       MODIFIED       B. Griffith, May 2009, EMS setpoint check
     508             :         //       RE-ENGINEERED  na
     509             : 
     510             :         // PURPOSE OF THIS SUBROUTINE:
     511             :         // init for PVT
     512             : 
     513             :         static constexpr std::string_view RoutineName("InitPVTcollectors");
     514             : 
     515             :         // Do the one time initializations
     516      197293 :         this->oneTimeInit(state);
     517             : 
     518             :         // finish set up of PV, because PV get-input follows PVT's get input.
     519      197293 :         if (!this->PVfound) {
     520          10 :             if (allocated(state.dataPhotovoltaic->PVarray)) {
     521          10 :                 this->PVnum = UtilityRoutines::FindItemInList(this->PVname, state.dataPhotovoltaic->PVarray);
     522          10 :                 if (this->PVnum == 0) {
     523           0 :                     ShowSevereError(state, "Invalid name for photovoltaic generator = " + this->PVname);
     524           0 :                     ShowContinueError(state, "Entered in flat plate photovoltaic-thermal collector = " + this->Name);
     525             :                 } else {
     526          10 :                     this->PVfound = true;
     527             :                 }
     528             :             } else {
     529           0 :                 if ((!state.dataGlobal->BeginEnvrnFlag) && (!FirstHVACIteration)) {
     530           0 :                     ShowSevereError(state, "Photovoltaic generators are missing for Photovoltaic Thermal modeling");
     531           0 :                     ShowContinueError(state, "Needed for flat plate photovoltaic-thermal collector = " + this->Name);
     532             :                 }
     533             :             }
     534             :         }
     535             : 
     536      197293 :         if (!state.dataGlobal->SysSizingCalc && this->MySetPointCheckFlag && state.dataHVACGlobal->DoSetPointTest) {
     537         110 :             for (int PVTindex = 1; PVTindex <= state.dataPhotovoltaicThermalCollector->NumPVT; ++PVTindex) {
     538         100 :                 if (state.dataPhotovoltaicThermalCollector->PVT(PVTindex).WorkingFluidType == WorkingFluidEnum::AIR) {
     539          50 :                     if (state.dataLoopNodes->Node(state.dataPhotovoltaicThermalCollector->PVT(PVTindex).HVACOutletNodeNum).TempSetPoint ==
     540             :                         DataLoopNode::SensedNodeFlagValue) {
     541           0 :                         if (!state.dataGlobal->AnyEnergyManagementSystemInModel) {
     542           0 :                             ShowSevereError(state, "Missing temperature setpoint for PVT outlet node  ");
     543           0 :                             ShowContinueError(state,
     544           0 :                                               "Add a setpoint manager to outlet node of PVT named " +
     545           0 :                                                   state.dataPhotovoltaicThermalCollector->PVT(PVTindex).Name);
     546           0 :                             state.dataHVACGlobal->SetPointErrorFlag = true;
     547             :                         } else {
     548             :                             // need call to EMS to check node
     549           0 :                             EMSManager::CheckIfNodeSetPointManagedByEMS(state,
     550           0 :                                                                         state.dataPhotovoltaicThermalCollector->PVT(PVTindex).HVACOutletNodeNum,
     551             :                                                                         EMSManager::SPControlType::TemperatureSetPoint,
     552           0 :                                                                         state.dataHVACGlobal->SetPointErrorFlag);
     553           0 :                             if (state.dataHVACGlobal->SetPointErrorFlag) {
     554           0 :                                 ShowSevereError(state, "Missing temperature setpoint for PVT outlet node  ");
     555           0 :                                 ShowContinueError(state,
     556           0 :                                                   "Add a setpoint manager to outlet node of PVT named " +
     557           0 :                                                       state.dataPhotovoltaicThermalCollector->PVT(PVTindex).Name);
     558           0 :                                 ShowContinueError(state, "  or use an EMS actuator to establish a setpoint at the outlet node of PVT");
     559             :                             }
     560             :                         }
     561             :                     }
     562             :                 }
     563             :             }
     564          10 :             this->MySetPointCheckFlag = false;
     565             :         }
     566             : 
     567      197293 :         if (!state.dataGlobal->SysSizingCalc && this->SizingInit && (this->WorkingFluidType == WorkingFluidEnum::AIR)) {
     568           5 :             this->size(state);
     569             :         }
     570             : 
     571      197293 :         int InletNode = 0;
     572      197293 :         int OutletNode = 0;
     573             : 
     574      197293 :         switch (this->WorkingFluidType) {
     575      151650 :         case WorkingFluidEnum::LIQUID: {
     576      151650 :             InletNode = this->PlantInletNodeNum;
     577      151650 :             OutletNode = this->PlantOutletNodeNum;
     578      151650 :         } break;
     579       45643 :         case WorkingFluidEnum::AIR: {
     580       45643 :             InletNode = this->HVACInletNodeNum;
     581       45643 :             OutletNode = this->HVACOutletNodeNum;
     582       45643 :         } break;
     583           0 :         default: {
     584           0 :             assert(false);
     585             :         } break;
     586             :         }
     587             : 
     588      197293 :         if (state.dataGlobal->BeginEnvrnFlag && this->EnvrnInit) {
     589             : 
     590          60 :             this->MassFlowRate = 0.0;
     591          60 :             this->BypassDamperOff = true;
     592          60 :             this->CoolingUseful = false;
     593          60 :             this->HeatingUseful = false;
     594          60 :             this->Simple.LastCollectorTemp = 0.0;
     595          60 :             this->Simple.CollectorTemp = 0.0;
     596          60 :             this->Report.ThermEfficiency = 0.0;
     597          60 :             this->Report.ThermPower = 0.0;
     598          60 :             this->Report.ThermHeatGain = 0.0;
     599          60 :             this->Report.ThermHeatLoss = 0.0;
     600          60 :             this->Report.ThermEnergy = 0.0;
     601          60 :             this->Report.MdotWorkFluid = 0.0;
     602          60 :             this->Report.TinletWorkFluid = 0.0;
     603          60 :             this->Report.ToutletWorkFluid = 0.0;
     604          60 :             this->Report.BypassStatus = 0.0;
     605             : 
     606          60 :             switch (this->WorkingFluidType) {
     607          30 :             case WorkingFluidEnum::LIQUID: {
     608             : 
     609          60 :                 Real64 rho = FluidProperties::GetDensityGlycol(state,
     610          30 :                                                                state.dataPlnt->PlantLoop(this->WPlantLoc.loopNum).FluidName,
     611             :                                                                DataGlobalConstants::HWInitConvTemp,
     612          30 :                                                                state.dataPlnt->PlantLoop(this->WPlantLoc.loopNum).FluidIndex,
     613          30 :                                                                RoutineName);
     614             : 
     615          30 :                 this->MaxMassFlowRate = this->DesignVolFlowRate * rho;
     616             : 
     617          30 :                 PlantUtilities::InitComponentNodes(state, 0.0, this->MaxMassFlowRate, InletNode, OutletNode);
     618             : 
     619          30 :                 this->Simple.LastCollectorTemp = 23.0;
     620             : 
     621          30 :             } break;
     622          30 :             case WorkingFluidEnum::AIR: {
     623          30 :                 this->Simple.LastCollectorTemp = 23.0;
     624          30 :             } break;
     625           0 :             default:
     626           0 :                 break;
     627             :             }
     628             : 
     629          60 :             this->EnvrnInit = false;
     630             :         }
     631      197293 :         if (!state.dataGlobal->BeginEnvrnFlag) this->EnvrnInit = true;
     632             : 
     633      197293 :         switch (this->WorkingFluidType) {
     634      151650 :         case WorkingFluidEnum::LIQUID: {
     635             :             // heating only right now, so control flow requests based on incident solar;
     636      151650 :             if (state.dataHeatBal->SurfQRadSWOutIncident(this->SurfNum) > DataPhotovoltaics::MinIrradiance) {
     637       31280 :                 this->MassFlowRate = this->MaxMassFlowRate;
     638             :             } else {
     639      120370 :                 this->MassFlowRate = 0.0;
     640             :             }
     641             : 
     642      151650 :             PlantUtilities::SetComponentFlowRate(state, this->MassFlowRate, InletNode, OutletNode, this->WPlantLoc);
     643      151650 :         } break;
     644       45643 :         case WorkingFluidEnum::AIR: {
     645       45643 :             this->MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
     646       45643 :         } break;
     647           0 :         default:
     648           0 :             break;
     649             :         }
     650      197293 :     }
     651             : 
     652          30 :     void PVTCollectorStruct::size(EnergyPlusData &state)
     653             :     {
     654             : 
     655             :         // SUBROUTINE INFORMATION:
     656             :         //       AUTHOR         Brent Griffith
     657             :         //       DATE WRITTEN   August 2008
     658             :         //       MODIFIED       November 2013 Daeho Kang, add component sizing table entries
     659             :         //       RE-ENGINEERED  na
     660             : 
     661             :         // PURPOSE OF THIS SUBROUTINE:
     662             :         // This subroutine is for sizing PVT flow rates that
     663             :         // have not been specified in the input.
     664             : 
     665             :         // METHODOLOGY EMPLOYED:
     666             :         // Obtains hot water flow rate from the plant sizing array.
     667             : 
     668             :         bool SizingDesRunThisAirSys; // true if a particular air system had a Sizing:System object and system sizing done
     669             : 
     670             :         // Indicator to hardsize and no sizing run
     671          30 :         bool HardSizeNoDesRun = !(state.dataSize->SysSizingRunDone || state.dataSize->ZoneSizingRunDone);
     672             : 
     673          30 :         if (state.dataSize->CurSysNum > 0) {
     674           5 :             CheckThisAirSystemForSizing(state, state.dataSize->CurSysNum, SizingDesRunThisAirSys);
     675             :         } else {
     676          25 :             SizingDesRunThisAirSys = false;
     677             :         }
     678             : 
     679          30 :         Real64 DesignVolFlowRateDes = 0.0; // Autosize design volume flow for reporting
     680          30 :         int PltSizNum = 0;                 // Plant Sizing index corresponding to CurLoopNum
     681          30 :         bool ErrorsFound = false;
     682             : 
     683          30 :         if (this->WorkingFluidType == WorkingFluidEnum::LIQUID) {
     684             : 
     685          25 :             if (!allocated(state.dataSize->PlantSizData)) return;
     686          25 :             if (!allocated(state.dataPlnt->PlantLoop)) return;
     687             : 
     688          25 :             if (this->WPlantLoc.loopNum > 0) {
     689          25 :                 PltSizNum = state.dataPlnt->PlantLoop(this->WPlantLoc.loopNum).PlantSizNum;
     690             :             }
     691          25 :             if (this->WPlantLoc.loopSideNum == DataPlant::LoopSideLocation::Supply) {
     692           0 :                 if (PltSizNum > 0) {
     693           0 :                     if (state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate >= DataHVACGlobals::SmallWaterVolFlow) {
     694           0 :                         DesignVolFlowRateDes = state.dataSize->PlantSizData(PltSizNum).DesVolFlowRate;
     695             :                     } else {
     696           0 :                         DesignVolFlowRateDes = 0.0;
     697             :                     }
     698             :                 } else {
     699           0 :                     if (this->DesignVolFlowRateWasAutoSized) {
     700           0 :                         if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
     701           0 :                             ShowSevereError(state, "Autosizing of PVT solar collector design flow rate requires a Sizing:Plant object");
     702           0 :                             ShowContinueError(state, "Occurs in PVT object=" + this->Name);
     703           0 :                             ErrorsFound = true;
     704             :                         }
     705             :                     } else { // Hardsized
     706           0 :                         if (state.dataPlnt->PlantFinalSizesOkayToReport && this->DesignVolFlowRate > 0.0) {
     707           0 :                             BaseSizer::reportSizerOutput(state,
     708             :                                                          "SolarCollector:FlatPlate:PhotovoltaicThermal",
     709             :                                                          this->Name,
     710             :                                                          "User-Specified Design Flow Rate [m3/s]",
     711           0 :                                                          this->DesignVolFlowRate);
     712             :                         }
     713             :                     }
     714             :                 }
     715          25 :             } else if (this->WPlantLoc.loopSideNum == DataPlant::LoopSideLocation::Demand) {
     716          25 :                 DesignVolFlowRateDes = this->AreaCol * SimplePVTWaterSizeFactor;
     717             :             }
     718          25 :             if (this->DesignVolFlowRateWasAutoSized) {
     719          25 :                 this->DesignVolFlowRate = DesignVolFlowRateDes;
     720          25 :                 if (state.dataPlnt->PlantFinalSizesOkayToReport) {
     721          15 :                     BaseSizer::reportSizerOutput(state,
     722             :                                                  "SolarCollector:FlatPlate:PhotovoltaicThermal",
     723             :                                                  this->Name,
     724             :                                                  "Design Size Design Flow Rate [m3/s]",
     725          10 :                                                  DesignVolFlowRateDes);
     726             :                 }
     727          25 :                 if (state.dataPlnt->PlantFirstSizesOkayToReport) {
     728           0 :                     BaseSizer::reportSizerOutput(state,
     729             :                                                  "SolarCollector:FlatPlate:PhotovoltaicThermal",
     730             :                                                  this->Name,
     731             :                                                  "Initial Design Size Design Flow Rate [m3/s]",
     732           0 :                                                  DesignVolFlowRateDes);
     733             :                 }
     734          25 :                 PlantUtilities::RegisterPlantCompDesignFlow(state, this->PlantInletNodeNum, this->DesignVolFlowRate);
     735             : 
     736             :             } else { // Hardsized with sizing data
     737           0 :                 if (this->DesignVolFlowRate > 0.0 && DesignVolFlowRateDes > 0.0 && state.dataPlnt->PlantFinalSizesOkayToReport) {
     738           0 :                     Real64 DesignVolFlowRateUser = this->DesignVolFlowRate;
     739           0 :                     BaseSizer::reportSizerOutput(state,
     740             :                                                  "SolarCollector:FlatPlate:PhotovoltaicThermal",
     741             :                                                  this->Name,
     742             :                                                  "Design Size Design Flow Rate [m3/s]",
     743             :                                                  DesignVolFlowRateDes,
     744             :                                                  "User-Specified Design Flow Rate [m3/s]",
     745           0 :                                                  DesignVolFlowRateUser);
     746           0 :                     if (state.dataGlobal->DisplayExtraWarnings) {
     747           0 :                         if ((std::abs(DesignVolFlowRateDes - DesignVolFlowRateUser) / DesignVolFlowRateUser) >
     748           0 :                             state.dataSize->AutoVsHardSizingThreshold) {
     749           0 :                             ShowMessage(state, "SizeSolarCollector: Potential issue with equipment sizing for " + this->Name);
     750           0 :                             ShowContinueError(state, format("User-Specified Design Flow Rate of {:.5R} [W]", DesignVolFlowRateUser));
     751           0 :                             ShowContinueError(state, format("differs from Design Size Design Flow Rate of {:.5R} [W]", DesignVolFlowRateDes));
     752           0 :                             ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     753           0 :                             ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     754             :                         }
     755             :                     }
     756             :                 }
     757             :             }
     758             :         } // plant component
     759             : 
     760          30 :         if (this->WorkingFluidType == WorkingFluidEnum::AIR) {
     761             : 
     762           5 :             if (state.dataSize->CurSysNum > 0) {
     763           5 :                 if (!this->DesignVolFlowRateWasAutoSized && !SizingDesRunThisAirSys) { // Simulation continue
     764           0 :                     HardSizeNoDesRun = true;
     765           0 :                     if (this->DesignVolFlowRate > 0.0) {
     766           0 :                         BaseSizer::reportSizerOutput(state,
     767             :                                                      "SolarCollector:FlatPlate:PhotovoltaicThermal",
     768             :                                                      this->Name,
     769             :                                                      "User-Specified Design Flow Rate [m3/s]",
     770           0 :                                                      this->DesignVolFlowRate);
     771             :                     }
     772             :                 } else {
     773           5 :                     CheckSysSizing(state, "SolarCollector:FlatPlate:PhotovoltaicThermal", this->Name);
     774           5 :                     auto &thisFinalSysSizing(state.dataSize->FinalSysSizing(state.dataSize->CurSysNum));
     775           5 :                     if (state.dataSize->CurOASysNum > 0) {
     776           5 :                         DesignVolFlowRateDes = thisFinalSysSizing.DesOutAirVolFlow;
     777             :                     } else {
     778           0 :                         switch (state.dataSize->CurDuctType) {
     779           0 :                         case DataHVACGlobals::AirDuctType::Main: {
     780           0 :                             DesignVolFlowRateDes = thisFinalSysSizing.SysAirMinFlowRat * thisFinalSysSizing.DesMainVolFlow;
     781           0 :                         } break;
     782           0 :                         case DataHVACGlobals::AirDuctType::Cooling: {
     783           0 :                             DesignVolFlowRateDes = thisFinalSysSizing.SysAirMinFlowRat * thisFinalSysSizing.DesCoolVolFlow;
     784           0 :                         } break;
     785           0 :                         case DataHVACGlobals::AirDuctType::Heating: {
     786           0 :                             DesignVolFlowRateDes = thisFinalSysSizing.DesHeatVolFlow;
     787           0 :                         } break;
     788           0 :                         default: {
     789           0 :                             DesignVolFlowRateDes = thisFinalSysSizing.DesMainVolFlow;
     790           0 :                         } break;
     791             :                         }
     792             :                     }
     793           5 :                     Real64 DesMassFlow = state.dataEnvrn->StdRhoAir * DesignVolFlowRateDes;
     794           5 :                     this->MaxMassFlowRate = DesMassFlow;
     795             :                 }
     796           5 :                 if (!HardSizeNoDesRun) {
     797           5 :                     if (this->DesignVolFlowRateWasAutoSized) {
     798           5 :                         this->DesignVolFlowRate = DesignVolFlowRateDes;
     799          15 :                         BaseSizer::reportSizerOutput(state,
     800             :                                                      "SolarCollector:FlatPlate:PhotovoltaicThermal",
     801             :                                                      this->Name,
     802             :                                                      "Design Size Design Flow Rate [m3/s]",
     803          10 :                                                      DesignVolFlowRateDes);
     804           5 :                         this->SizingInit = false;
     805             :                     } else {
     806           0 :                         if (this->DesignVolFlowRate > 0.0 && DesignVolFlowRateDes > 0.0) {
     807           0 :                             Real64 DesignVolFlowRateUser = this->DesignVolFlowRate;
     808           0 :                             BaseSizer::reportSizerOutput(state,
     809             :                                                          "SolarCollector:FlatPlate:PhotovoltaicThermal",
     810             :                                                          this->Name,
     811             :                                                          "Design Size Design Flow Rate [m3/s]",
     812             :                                                          DesignVolFlowRateDes,
     813             :                                                          "User-Specified Design Flow Rate [m3/s]",
     814           0 :                                                          DesignVolFlowRateUser);
     815           0 :                             if (state.dataGlobal->DisplayExtraWarnings) {
     816           0 :                                 if ((std::abs(DesignVolFlowRateDes - DesignVolFlowRateUser) / DesignVolFlowRateUser) >
     817           0 :                                     state.dataSize->AutoVsHardSizingThreshold) {
     818           0 :                                     ShowMessage(state, "SizeSolarCollector: Potential issue with equipment sizing for " + this->Name);
     819           0 :                                     ShowContinueError(state, format("User-Specified Design Flow Rate of {:.5R} [W]", DesignVolFlowRateUser));
     820           0 :                                     ShowContinueError(state, format("differs from Design Size Design Flow Rate of {:.5R} [W]", DesignVolFlowRateDes));
     821           0 :                                     ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
     822           0 :                                     ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
     823             :                                 }
     824             :                             }
     825             :                         }
     826             :                     }
     827             :                 }
     828           0 :             } else if (state.dataSize->CurZoneEqNum > 0) {
     829             :                 // PVT is not currently for zone equipment, should not come here.
     830             :             }
     831             :         }
     832             : 
     833          30 :         if (ErrorsFound) {
     834           0 :             ShowFatalError(state, "Preceding sizing errors cause program termination");
     835             :         }
     836             :     }
     837             : 
     838      197268 :     void PVTCollectorStruct::control(EnergyPlusData &state)
     839             :     {
     840             : 
     841             :         // SUBROUTINE INFORMATION:
     842             :         //       AUTHOR         Brent Griffith
     843             :         //       DATE WRITTEN   August 2008
     844             :         //       MODIFIED       na
     845             :         //       RE-ENGINEERED  na
     846             : 
     847             :         // PURPOSE OF THIS SUBROUTINE:
     848             :         // make control decisions for PVT collector
     849             : 
     850             :         // METHODOLOGY EMPLOYED:
     851             :         // decide if PVT should be in cooling or heat mode and if it should be bypassed or not
     852             : 
     853      197268 :         if (this->WorkingFluidType == WorkingFluidEnum::AIR) {
     854             : 
     855       45643 :             if (this->PVTModelType == SimplePVTmodel) {
     856       45643 :                 if (state.dataHeatBal->SurfQRadSWOutIncident(this->SurfNum) > DataPhotovoltaics::MinIrradiance) {
     857             :                     // is heating wanted?
     858             :                     //  Outlet node is required to have a setpoint.
     859        9298 :                     if (state.dataLoopNodes->Node(this->HVACOutletNodeNum).TempSetPoint > state.dataLoopNodes->Node(this->HVACInletNodeNum).Temp) {
     860        1306 :                         this->HeatingUseful = true;
     861        1306 :                         this->CoolingUseful = false;
     862        1306 :                         this->BypassDamperOff = true;
     863             :                     } else {
     864        7992 :                         this->HeatingUseful = false;
     865        7992 :                         this->CoolingUseful = true;
     866        7992 :                         this->BypassDamperOff = false;
     867             :                     }
     868             :                 } else {
     869             :                     // is cooling wanted?
     870       36345 :                     if (state.dataLoopNodes->Node(this->HVACOutletNodeNum).TempSetPoint < state.dataLoopNodes->Node(this->HVACInletNodeNum).Temp) {
     871        3806 :                         this->CoolingUseful = true;
     872        3806 :                         this->HeatingUseful = false;
     873        3806 :                         this->BypassDamperOff = true;
     874             :                     } else {
     875       32539 :                         this->CoolingUseful = false;
     876       32539 :                         this->HeatingUseful = true;
     877       32539 :                         this->BypassDamperOff = false;
     878             :                     }
     879             :                 }
     880             :             }
     881             : 
     882      151625 :         } else if (this->WorkingFluidType == WorkingFluidEnum::LIQUID) {
     883      151625 :             if (this->PVTModelType == SimplePVTmodel) {
     884      151625 :                 if (state.dataHeatBal->SurfQRadSWOutIncident(this->SurfNum) > DataPhotovoltaics::MinIrradiance) {
     885             :                     // is heating wanted?
     886       31280 :                     this->HeatingUseful = true;
     887       31280 :                     this->BypassDamperOff = true;
     888             :                 } else {
     889             :                     // is cooling wanted?
     890      120345 :                     this->CoolingUseful = false;
     891      120345 :                     this->BypassDamperOff = false;
     892             :                 }
     893             :             }
     894             :         }
     895      197268 :     }
     896             : 
     897      197268 :     void PVTCollectorStruct::calculate(EnergyPlusData &state)
     898             :     {
     899             : 
     900             :         // SUBROUTINE INFORMATION:
     901             :         //       AUTHOR         Brent Griffith
     902             :         //       DATE WRITTEN   August 2008
     903             :         //       MODIFIED       na
     904             :         //       RE-ENGINEERED  na
     905             : 
     906             :         // PURPOSE OF THIS SUBROUTINE:
     907             :         // Calculate PVT collector thermal
     908             : 
     909             :         // METHODOLOGY EMPLOYED:
     910             :         // Current model is "simple" fixed efficiency and simple night sky balance for cooling
     911             : 
     912             :         static constexpr std::string_view RoutineName("CalcPVTcollectors");
     913             : 
     914      197268 :         int InletNode(0);
     915             : 
     916      197268 :         switch (this->WorkingFluidType) {
     917      151625 :         case WorkingFluidEnum::LIQUID: {
     918      151625 :             InletNode = this->PlantInletNodeNum;
     919      151625 :         } break;
     920       45643 :         case WorkingFluidEnum::AIR: {
     921       45643 :             InletNode = this->HVACInletNodeNum;
     922       45643 :         } break;
     923           0 :         default:
     924           0 :             break;
     925             :         }
     926             : 
     927      197268 :         Real64 mdot = this->MassFlowRate;
     928      197268 :         Real64 Tinlet = state.dataLoopNodes->Node(InletNode).Temp;
     929             : 
     930      197268 :         if (this->PVTModelType == SimplePVTmodel) {
     931             : 
     932      197268 :             Real64 BypassFraction(0.0);
     933      197268 :             Real64 PotentialOutletTemp(0.0);
     934             : 
     935      197268 :             if (this->HeatingUseful && this->BypassDamperOff && (mdot > 0.0)) {
     936             : 
     937       27860 :                 Real64 Eff(0.0);
     938             : 
     939       27860 :                 switch (this->Simple.ThermEfficMode) {
     940       27860 :                 case ThermEfficEnum::FIXED: {
     941       27860 :                     Eff = this->Simple.ThermEffic;
     942       27860 :                 } break;
     943           0 :                 case ThermEfficEnum::SCHEDULED: {
     944           0 :                     Eff = ScheduleManager::GetCurrentScheduleValue(state, this->Simple.ThermEffSchedNum);
     945           0 :                     this->Simple.ThermEffic = Eff;
     946           0 :                 } break;
     947           0 :                 default:
     948           0 :                     break;
     949             :                 }
     950             : 
     951       27860 :                 Real64 PotentialHeatGain = state.dataHeatBal->SurfQRadSWOutIncident(this->SurfNum) * Eff * this->AreaCol;
     952             : 
     953       27860 :                 if (this->WorkingFluidType == WorkingFluidEnum::AIR) {
     954         490 :                     Real64 Winlet = state.dataLoopNodes->Node(InletNode).HumRat;
     955         490 :                     Real64 CpInlet = Psychrometrics::PsyCpAirFnW(Winlet);
     956         490 :                     if (mdot * CpInlet > 0.0) {
     957         490 :                         PotentialOutletTemp = Tinlet + PotentialHeatGain / (mdot * CpInlet);
     958             :                     } else {
     959           0 :                         PotentialOutletTemp = Tinlet;
     960             :                     }
     961             :                     // now compare heating potential to setpoint and figure bypass fraction
     962         490 :                     if (PotentialOutletTemp > state.dataLoopNodes->Node(this->HVACOutletNodeNum).TempSetPoint) { // need to modulate
     963         455 :                         if (Tinlet != PotentialOutletTemp) {
     964         910 :                             BypassFraction = (state.dataLoopNodes->Node(this->HVACOutletNodeNum).TempSetPoint - PotentialOutletTemp) /
     965         455 :                                              (Tinlet - PotentialOutletTemp);
     966             :                         } else {
     967           0 :                             BypassFraction = 0.0;
     968             :                         }
     969         455 :                         BypassFraction = max(0.0, BypassFraction);
     970         455 :                         PotentialOutletTemp = state.dataLoopNodes->Node(this->HVACOutletNodeNum).TempSetPoint;
     971         455 :                         PotentialHeatGain = mdot * Psychrometrics::PsyCpAirFnW(Winlet) * (PotentialOutletTemp - Tinlet);
     972             : 
     973             :                     } else {
     974          35 :                         BypassFraction = 0.0;
     975             :                     }
     976       27370 :                 } else if (this->WorkingFluidType == WorkingFluidEnum::LIQUID) {
     977       27370 :                     Real64 CpInlet = Psychrometrics::CPHW(Tinlet);
     978       27370 :                     if (mdot * CpInlet != 0.0) { // protect divide by zero
     979       27370 :                         PotentialOutletTemp = Tinlet + PotentialHeatGain / (mdot * CpInlet);
     980             :                     } else {
     981           0 :                         PotentialOutletTemp = Tinlet;
     982             :                     }
     983       27370 :                     BypassFraction = 0.0;
     984             :                 }
     985             : 
     986       27860 :                 this->Report.ThermEfficiency = Eff;
     987       27860 :                 this->Report.ThermHeatGain = PotentialHeatGain;
     988       27860 :                 this->Report.ThermPower = this->Report.ThermHeatGain;
     989       27860 :                 this->Report.ThermEnergy = this->Report.ThermPower * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
     990       27860 :                 this->Report.ThermHeatLoss = 0.0;
     991       27860 :                 this->Report.TinletWorkFluid = Tinlet;
     992       27860 :                 this->Report.MdotWorkFluid = mdot;
     993       27860 :                 this->Report.ToutletWorkFluid = PotentialOutletTemp;
     994       27860 :                 this->Report.BypassStatus = BypassFraction;
     995             : 
     996      169408 :             } else if (this->CoolingUseful && this->BypassDamperOff && (mdot > 0.0)) {
     997             :                 // calculate cooling using energy balance
     998        2898 :                 Real64 HrGround(0.0);
     999        2898 :                 Real64 HrAir(0.0);
    1000        2898 :                 Real64 HcExt(0.0);
    1001        2898 :                 Real64 HrSky(0.0);
    1002             : 
    1003        2898 :                 ConvectionCoefficients::InitExteriorConvectionCoeff(state,
    1004             :                                                                     this->SurfNum,
    1005             :                                                                     0.0,
    1006             :                                                                     DataSurfaces::SurfaceRoughness::VerySmooth,
    1007             :                                                                     this->Simple.SurfEmissivity,
    1008             :                                                                     this->Simple.LastCollectorTemp,
    1009             :                                                                     HcExt,
    1010             :                                                                     HrSky,
    1011             :                                                                     HrGround,
    1012             :                                                                     HrAir);
    1013             : 
    1014        2898 :                 Real64 WetBulbInlet(0.0);
    1015        2898 :                 Real64 DewPointInlet(0.0);
    1016        2898 :                 Real64 CpInlet(0.0);
    1017             : 
    1018        2898 :                 if (this->WorkingFluidType == WorkingFluidEnum::AIR) {
    1019        2898 :                     Real64 Winlet = state.dataLoopNodes->Node(InletNode).HumRat;
    1020        2898 :                     CpInlet = Psychrometrics::PsyCpAirFnW(Winlet);
    1021        2898 :                     WetBulbInlet = Psychrometrics::PsyTwbFnTdbWPb(state, Tinlet, Winlet, state.dataEnvrn->OutBaroPress, RoutineName);
    1022        2898 :                     DewPointInlet = Psychrometrics::PsyTdpFnTdbTwbPb(state, Tinlet, WetBulbInlet, state.dataEnvrn->OutBaroPress, RoutineName);
    1023           0 :                 } else if (this->WorkingFluidType == WorkingFluidEnum::LIQUID) {
    1024           0 :                     CpInlet = Psychrometrics::CPHW(Tinlet);
    1025             :                 }
    1026             : 
    1027             :                 Real64 Tcollector =
    1028        8694 :                     (2.0 * mdot * CpInlet * Tinlet + this->AreaCol * (HrGround * state.dataEnvrn->OutDryBulbTemp + HrSky * state.dataEnvrn->SkyTemp +
    1029        5796 :                                                                       HrAir * state.dataSurface->SurfOutDryBulbTemp(this->SurfNum) +
    1030        2898 :                                                                       HcExt * state.dataSurface->SurfOutDryBulbTemp(this->SurfNum))) /
    1031        2898 :                     (2.0 * mdot * CpInlet + this->AreaCol * (HrGround + HrSky + HrAir + HcExt));
    1032             : 
    1033        2898 :                 PotentialOutletTemp = 2.0 * Tcollector - Tinlet;
    1034        2898 :                 this->Report.ToutletWorkFluid = PotentialOutletTemp;
    1035             :                 // trap for air not being cooled below its wetbulb.
    1036        2898 :                 if (this->WorkingFluidType == WorkingFluidEnum::AIR) {
    1037        2898 :                     if (PotentialOutletTemp < DewPointInlet) {
    1038             :                         //  water removal would be needed.. not going to allow that for now.  limit cooling to dew point and model bypass
    1039        2883 :                         if (Tinlet != PotentialOutletTemp) {
    1040        2883 :                             BypassFraction = (DewPointInlet - PotentialOutletTemp) / (Tinlet - PotentialOutletTemp);
    1041             :                         } else {
    1042           0 :                             BypassFraction = 0.0;
    1043             :                         }
    1044        2883 :                         BypassFraction = max(0.0, BypassFraction);
    1045        2883 :                         PotentialOutletTemp = DewPointInlet;
    1046             :                     }
    1047             :                 }
    1048             : 
    1049        2898 :                 this->Report.MdotWorkFluid = mdot;
    1050        2898 :                 this->Report.TinletWorkFluid = Tinlet;
    1051        2898 :                 this->Report.ToutletWorkFluid = PotentialOutletTemp;
    1052        2898 :                 this->Report.ThermHeatLoss = mdot * CpInlet * (Tinlet - this->Report.ToutletWorkFluid);
    1053        2898 :                 this->Report.ThermHeatGain = 0.0;
    1054        2898 :                 this->Report.ThermPower = -1.0 * this->Report.ThermHeatLoss;
    1055        2898 :                 this->Report.ThermEnergy = this->Report.ThermPower * state.dataHVACGlobal->TimeStepSys * DataGlobalConstants::SecInHour;
    1056        2898 :                 this->Report.ThermEfficiency = 0.0;
    1057        2898 :                 this->Simple.LastCollectorTemp = Tcollector;
    1058        2898 :                 this->Report.BypassStatus = BypassFraction;
    1059             : 
    1060             :             } else {
    1061      166510 :                 this->Report.TinletWorkFluid = Tinlet;
    1062      166510 :                 this->Report.ToutletWorkFluid = Tinlet;
    1063      166510 :                 this->Report.ThermHeatLoss = 0.0;
    1064      166510 :                 this->Report.ThermHeatGain = 0.0;
    1065      166510 :                 this->Report.ThermPower = 0.0;
    1066      166510 :                 this->Report.ThermEfficiency = 0.0;
    1067      166510 :                 this->Report.ThermEnergy = 0.0;
    1068      166510 :                 this->Report.BypassStatus = 1.0;
    1069      166510 :                 this->Report.MdotWorkFluid = mdot;
    1070             :             }
    1071             :         }
    1072      197268 :     }
    1073             : 
    1074      197268 :     void PVTCollectorStruct::update(EnergyPlusData &state)
    1075             :     {
    1076             : 
    1077             :         // SUBROUTINE INFORMATION:
    1078             :         //       AUTHOR         Brent Griffith
    1079             :         //       DATE WRITTEN   August 2008
    1080             :         //       MODIFIED       na
    1081             :         //       RE-ENGINEERED  na
    1082             : 
    1083             :         int InletNode;
    1084             :         int OutletNode;
    1085             : 
    1086      197268 :         switch (this->WorkingFluidType) {
    1087      151625 :         case WorkingFluidEnum::LIQUID: {
    1088      151625 :             InletNode = this->PlantInletNodeNum;
    1089      151625 :             OutletNode = this->PlantOutletNodeNum;
    1090             : 
    1091      151625 :             PlantUtilities::SafeCopyPlantNode(state, InletNode, OutletNode);
    1092      151625 :             state.dataLoopNodes->Node(OutletNode).Temp = this->Report.ToutletWorkFluid;
    1093      151625 :         } break;
    1094       45643 :         case WorkingFluidEnum::AIR: {
    1095       45643 :             InletNode = this->HVACInletNodeNum;
    1096       45643 :             OutletNode = this->HVACOutletNodeNum;
    1097             : 
    1098             :             // Set the outlet nodes for properties that just pass through & not used
    1099       45643 :             state.dataLoopNodes->Node(OutletNode).Quality = state.dataLoopNodes->Node(InletNode).Quality;
    1100       45643 :             state.dataLoopNodes->Node(OutletNode).Press = state.dataLoopNodes->Node(InletNode).Press;
    1101       45643 :             state.dataLoopNodes->Node(OutletNode).MassFlowRate = state.dataLoopNodes->Node(InletNode).MassFlowRate;
    1102       45643 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMin = state.dataLoopNodes->Node(InletNode).MassFlowRateMin;
    1103       45643 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMax = state.dataLoopNodes->Node(InletNode).MassFlowRateMax;
    1104       45643 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMinAvail = state.dataLoopNodes->Node(InletNode).MassFlowRateMinAvail;
    1105       45643 :             state.dataLoopNodes->Node(OutletNode).MassFlowRateMaxAvail = state.dataLoopNodes->Node(InletNode).MassFlowRateMaxAvail;
    1106             : 
    1107             :             // Set outlet node variables that are possibly changed
    1108       45643 :             state.dataLoopNodes->Node(OutletNode).Temp = this->Report.ToutletWorkFluid;
    1109       45643 :             state.dataLoopNodes->Node(OutletNode).HumRat = state.dataLoopNodes->Node(InletNode).HumRat; // assumes dewpoint bound on cooling ....
    1110       45643 :             state.dataLoopNodes->Node(OutletNode).Enthalpy =
    1111       45643 :                 Psychrometrics::PsyHFnTdbW(this->Report.ToutletWorkFluid, state.dataLoopNodes->Node(OutletNode).HumRat);
    1112       45643 :         } break;
    1113           0 :         default:
    1114           0 :             break;
    1115             :         }
    1116      197268 :     }
    1117      197293 :     void PVTCollectorStruct::oneTimeInit(EnergyPlusData &state)
    1118             :     {
    1119             : 
    1120      197293 :         if (this->MyOneTimeFlag) {
    1121          10 :             this->setupReportVars(state);
    1122          10 :             this->MyOneTimeFlag = false;
    1123             :         }
    1124             : 
    1125      197293 :         if (this->SetLoopIndexFlag) {
    1126       45648 :             if (allocated(state.dataPlnt->PlantLoop) && (this->PlantInletNodeNum > 0)) {
    1127           5 :                 bool errFlag = false;
    1128           5 :                 PlantUtilities::ScanPlantLoopsForObject(state, this->Name, this->Type, this->WPlantLoc, errFlag, _, _, _, _, _);
    1129           5 :                 if (errFlag) {
    1130           0 :                     ShowFatalError(state, "InitPVTcollectors: Program terminated for previous conditions.");
    1131             :                 }
    1132           5 :                 this->SetLoopIndexFlag = false;
    1133             :             }
    1134             :         }
    1135      197293 :     }
    1136             : 
    1137       80592 :     void GetPVTThermalPowerProduction(EnergyPlusData &state, int const PVindex, Real64 &ThermalPower, Real64 &ThermalEnergy)
    1138             :     {
    1139             : 
    1140             :         // SUBROUTINE INFORMATION:
    1141             :         //       AUTHOR         <author>
    1142             :         //       DATE WRITTEN   <date_written>
    1143             :         //       MODIFIED       na
    1144             :         //       RE-ENGINEERED  na
    1145             : 
    1146       80592 :         int PVTnum(0);
    1147             : 
    1148             :         // first find PVT index that is associated with this PV generator
    1149      886512 :         for (int loop = 1; loop <= state.dataPhotovoltaicThermalCollector->NumPVT; ++loop) {
    1150      805920 :             if (!state.dataPhotovoltaicThermalCollector->PVT(loop).PVfound) continue;
    1151      682170 :             if (state.dataPhotovoltaicThermalCollector->PVT(loop).PVnum == PVindex) { // we found it
    1152       68215 :                 PVTnum = loop;
    1153             :             }
    1154             :         }
    1155             : 
    1156       80592 :         if (PVTnum > 0) {
    1157       68215 :             ThermalPower = state.dataPhotovoltaicThermalCollector->PVT(PVTnum).Report.ThermPower;
    1158       68215 :             ThermalEnergy = state.dataPhotovoltaicThermalCollector->PVT(PVTnum).Report.ThermEnergy;
    1159             :         } else {
    1160       12377 :             ThermalPower = 0.0;
    1161       12377 :             ThermalEnergy = 0.0;
    1162             :         }
    1163       80592 :     }
    1164             : 
    1165           0 :     int GetAirInletNodeNum(EnergyPlusData &state, std::string_view PVTName, bool &ErrorsFound)
    1166             :     {
    1167             :         // FUNCTION INFORMATION:
    1168             :         //       AUTHOR         Lixing Gu
    1169             :         //       DATE WRITTEN   May 2019
    1170             :         //       MODIFIED       na
    1171             :         //       RE-ENGINEERED  na
    1172             : 
    1173             :         // PURPOSE OF THIS FUNCTION:
    1174             :         // This function looks up the given PVT and returns the air inlet node number.
    1175             :         // If incorrect PVT name is given, ErrorsFound is returned as true and node number as zero.
    1176             : 
    1177             :         int NodeNum; // node number returned
    1178             :         int WhichPVT;
    1179             : 
    1180           0 :         if (state.dataPhotovoltaicThermalCollector->GetInputFlag) {
    1181           0 :             GetPVTcollectorsInput(state);
    1182           0 :             state.dataPhotovoltaicThermalCollector->GetInputFlag = false;
    1183             :         }
    1184             : 
    1185           0 :         WhichPVT = UtilityRoutines::FindItemInList(PVTName, state.dataPhotovoltaicThermalCollector->PVT);
    1186           0 :         if (WhichPVT != 0) {
    1187           0 :             NodeNum = state.dataPhotovoltaicThermalCollector->PVT(WhichPVT).HVACInletNodeNum;
    1188             :         } else {
    1189           0 :             ShowSevereError(state,
    1190           0 :                             "GetAirInletNodeNum: Could not find SolarCollector FlatPlate PhotovoltaicThermal = \"" + std::string{PVTName} + "\"");
    1191           0 :             ErrorsFound = true;
    1192           0 :             NodeNum = 0;
    1193             :         }
    1194             : 
    1195           0 :         return NodeNum;
    1196             :     }
    1197           0 :     int GetAirOutletNodeNum(EnergyPlusData &state, std::string_view PVTName, bool &ErrorsFound)
    1198             :     {
    1199             :         // FUNCTION INFORMATION:
    1200             :         //       AUTHOR         Lixing Gu
    1201             :         //       DATE WRITTEN   May 2019
    1202             :         //       MODIFIED       na
    1203             :         //       RE-ENGINEERED  na
    1204             : 
    1205             :         // PURPOSE OF THIS FUNCTION:
    1206             :         // This function looks up the given PVT and returns the air outlet node number.
    1207             :         // If incorrect PVT name is given, ErrorsFound is returned as true and node number as zero.
    1208             : 
    1209             :         int NodeNum; // node number returned
    1210             :         int WhichPVT;
    1211             : 
    1212           0 :         if (state.dataPhotovoltaicThermalCollector->GetInputFlag) {
    1213           0 :             GetPVTcollectorsInput(state);
    1214           0 :             state.dataPhotovoltaicThermalCollector->GetInputFlag = false;
    1215             :         }
    1216             : 
    1217           0 :         WhichPVT = UtilityRoutines::FindItemInList(PVTName, state.dataPhotovoltaicThermalCollector->PVT);
    1218           0 :         if (WhichPVT != 0) {
    1219           0 :             NodeNum = state.dataPhotovoltaicThermalCollector->PVT(WhichPVT).HVACOutletNodeNum;
    1220             :         } else {
    1221           0 :             ShowSevereError(state,
    1222           0 :                             "GetAirInletNodeNum: Could not find SolarCollector FlatPlate PhotovoltaicThermal = \"" + std::string{PVTName} + "\"");
    1223           0 :             ErrorsFound = true;
    1224           0 :             NodeNum = 0;
    1225             :         }
    1226             : 
    1227           0 :         return NodeNum;
    1228             :     }
    1229             : 
    1230           5 :     int getPVTindexFromName(EnergyPlusData &state, std::string_view objectName)
    1231             :     {
    1232           5 :         if (state.dataPhotovoltaicThermalCollector->GetInputFlag) {
    1233           0 :             GetPVTcollectorsInput(state);
    1234           0 :             state.dataPhotovoltaicThermalCollector->GetInputFlag = false;
    1235             :         }
    1236             : 
    1237          15 :         for (auto it = state.dataPhotovoltaicThermalCollector->PVT.begin(); it != state.dataPhotovoltaicThermalCollector->PVT.end(); ++it) {
    1238          15 :             if (it->Name == objectName) {
    1239           5 :                 return static_cast<int>(std::distance(state.dataPhotovoltaicThermalCollector->PVT.begin(), it) + 1);
    1240             :             }
    1241             :         }
    1242             : 
    1243             :         // If we didn't find it, fatal
    1244           0 :         ShowFatalError(state, "Solar Thermal Collector GetIndexFromName: Error getting inputs for object named: " + std::string{objectName});
    1245           0 :         assert(false);
    1246             :         return 0; // Shutup compiler
    1247             :     }
    1248             : 
    1249       45643 :     void simPVTfromOASys(EnergyPlusData &state, int const index, bool const FirstHVACIteration)
    1250             :     {
    1251       45643 :         PlantLocation dummyLoc(0, DataPlant::LoopSideLocation::Invalid, 0, 0);
    1252       45643 :         Real64 dummyCurLoad(0.0);
    1253       45643 :         bool dummyRunFlag(true);
    1254             : 
    1255       45643 :         state.dataPhotovoltaicThermalCollector->PVT(index).simulate(state, dummyLoc, FirstHVACIteration, dummyCurLoad, dummyRunFlag);
    1256       45643 :     }
    1257             : 
    1258             : } // namespace PhotovoltaicThermalCollectors
    1259             : 
    1260        2313 : } // namespace EnergyPlus

Generated by: LCOV version 1.13