LCOV - code coverage report
Current view: top level - EnergyPlus - IndoorGreen.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 66.3 % 288 191
Test Date: 2025-06-02 07:23:51 Functions: 100.0 % 6 6

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : // C++ Headers
      49              : 
      50              : #include <EnergyPlus/Construction.hh>
      51              : #include <EnergyPlus/Data/EnergyPlusData.hh>
      52              : #include <EnergyPlus/DataDaylighting.hh>
      53              : #include <EnergyPlus/DataEnvironment.hh>
      54              : #include <EnergyPlus/DataHVACGlobals.hh>
      55              : #include <EnergyPlus/DataHeatBalSurface.hh>
      56              : #include <EnergyPlus/DataHeatBalance.hh>
      57              : #include <EnergyPlus/DataIPShortCuts.hh>
      58              : #include <EnergyPlus/DataSurfaces.hh>
      59              : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      60              : #include <EnergyPlus/DataZoneEquipment.hh>
      61              : #include <EnergyPlus/DaylightingManager.hh>
      62              : #include <EnergyPlus/EMSManager.hh>
      63              : #include <EnergyPlus/General.hh>
      64              : #include <EnergyPlus/GeneralRoutines.hh>
      65              : #include <EnergyPlus/HeatBalanceInternalHeatGains.hh>
      66              : #include <EnergyPlus/IndoorGreen.hh>
      67              : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      68              : #include <EnergyPlus/OutputProcessor.hh>
      69              : #include <EnergyPlus/Psychrometrics.hh>
      70              : #include <EnergyPlus/ScheduleManager.hh>
      71              : #include <EnergyPlus/UtilityRoutines.hh>
      72              : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      73              : #include <EnergyPlus/api/datatransfer.h>
      74              : 
      75              : namespace EnergyPlus {
      76              : 
      77              : namespace IndoorGreen {
      78              :     // Module containing the routines dealing with the Indoor Living Walls
      79              :     static constexpr std::array<std::string_view, static_cast<int>(ETCalculationMethod::Num)> etCalculationMethodsUC = {"PENMAN-MONTEITH",
      80              :                                                                                                                         "STANGHELLINI"};
      81              :     static constexpr std::array<std::string_view, static_cast<int>(LightingMethod::Num)> lightingMethodsUC = {"LED", "DAYLIGHT", "LED-DAYLIGHT"};
      82              : 
      83      2828212 :     void SimIndoorGreen(EnergyPlusData &state)
      84              :     {
      85              :         // PURPOSE OF THIS SUBROUTINE:
      86              :         // This subroutine simulates the thermal performance of indoor living walls including the grow lights.
      87              :         // This subroutine interacts with inside surface heat balance, zone air heat balance and zone air moisture balance in EnergyPlus.
      88      2828212 :         auto const &lw = state.dataIndoorGreen;
      89      2828212 :         if (lw->getInputFlag) {
      90          801 :             bool ErrorsFound(false);
      91          801 :             const char *RoutineName("IndoorLivingWall: "); // include trailing blank space
      92          801 :             GetIndoorGreenInput(state, ErrorsFound);
      93          801 :             if (ErrorsFound) {
      94            0 :                 ShowFatalError(state, format("{}Errors found in input.  Program terminates.", RoutineName));
      95              :             }
      96          801 :             SetIndoorGreenOutput(state);
      97          801 :             lw->getInputFlag = false;
      98              :         }
      99      2828212 :         if (lw->NumIndoorGreen > 0) {
     100         1353 :             InitIndoorGreen(state);
     101              :             // Simulate evapotranspiration from indoor living walls
     102         1353 :             ETModel(state);
     103              :         }
     104      2828212 :     }
     105              : 
     106          801 :     void GetIndoorGreenInput(EnergyPlusData &state, bool &ErrorsFound)
     107              :     {
     108              :         // PURPOSE OF THIS SUBROUTINE:
     109              :         // Get the input for the indoor living wall objects and store the input data in the indoorGreens array.
     110              : 
     111          801 :         auto &s_lw = state.dataIndoorGreen;
     112          801 :         auto &s_ip = state.dataInputProcessing->inputProcessor;
     113          801 :         auto &s_ipsc = state.dataIPShortCut;
     114              : 
     115              :         static constexpr std::string_view RoutineName("GetIndoorLivingWallInput: ");
     116          801 :         std::string_view cCurrentModuleObject = "IndoorLivingWall"; // match the idd
     117              :         int NumNums;                                                // Number of real numbers returned by GetObjectItem
     118              :         int NumAlphas;                                              // Number of alphanumerics returned by GetObjectItem
     119              :         int IOStat;                                                 // Status flag from GetObjectItem
     120              : 
     121          801 :         s_lw->NumIndoorGreen = s_ip->getNumObjectsFound(state, cCurrentModuleObject);
     122          801 :         if (s_lw->NumIndoorGreen > 0) {
     123            1 :             s_lw->indoorGreens.allocate(s_lw->NumIndoorGreen); // Allocate the IndoorGreen input data array
     124              :         }
     125          802 :         for (int IndoorGreenNum = 1; IndoorGreenNum <= s_lw->NumIndoorGreen; ++IndoorGreenNum) {
     126            1 :             auto &ig = s_lw->indoorGreens(IndoorGreenNum);
     127            2 :             s_ip->getObjectItem(state,
     128              :                                 cCurrentModuleObject,
     129              :                                 IndoorGreenNum,
     130            1 :                                 s_ipsc->cAlphaArgs,
     131              :                                 NumAlphas,
     132            1 :                                 s_ipsc->rNumericArgs,
     133              :                                 NumNums,
     134              :                                 IOStat,
     135            1 :                                 s_ipsc->lNumericFieldBlanks,
     136            1 :                                 s_ipsc->lAlphaFieldBlanks,
     137            1 :                                 s_ipsc->cAlphaFieldNames,
     138            1 :                                 s_ipsc->cNumericFieldNames);
     139            1 :             ErrorObjectHeader eoh{RoutineName, cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     140            1 :             Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), cCurrentModuleObject, ErrorsFound);
     141            1 :             ig.Name = s_ipsc->cAlphaArgs(1);
     142            1 :             ig.SurfName = s_ipsc->cAlphaArgs(2);
     143            1 :             ig.SurfPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataSurface->Surface);
     144            1 :             if (ig.SurfPtr <= 0) {
     145            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     146            0 :                 ErrorsFound = true;
     147              :             } else {
     148            1 :                 if (state.dataSurface->Surface(ig.SurfPtr).insideHeatSourceTermSched != nullptr) {
     149            0 :                     ShowSevereError(state,
     150            0 :                                     format("The indoor green surface {} has an Inside Face Heat Source Term Schedule defined. This surface cannot "
     151              :                                            "also be used for indoor green.",
     152            0 :                                            s_ipsc->cAlphaArgs(2)));
     153            0 :                     ErrorsFound = true;
     154              :                 }
     155            1 :                 ig.ZonePtr = state.dataSurface->Surface(ig.SurfPtr).Zone;
     156            1 :                 ig.SpacePtr = state.dataSurface->Surface(ig.SurfPtr).spaceNum;
     157              : 
     158            1 :                 if (ig.ZonePtr <= 0 || ig.SpacePtr <= 0) {
     159            0 :                     ShowSevereError(state,
     160            0 :                                     format("{}=\"{}\", invalid {} entered={}, {} is not assoicated with a thermal zone or space",
     161              :                                            RoutineName,
     162            0 :                                            s_ipsc->cAlphaArgs(1),
     163            0 :                                            s_ipsc->cAlphaFieldNames(2),
     164            0 :                                            s_ipsc->cAlphaArgs(2),
     165            0 :                                            s_ipsc->cAlphaArgs(2)));
     166            0 :                     ErrorsFound = true;
     167            2 :                 } else if (state.dataSurface->Surface(ig.SurfPtr).ExtBoundCond < 0 ||
     168            1 :                            state.dataSurface->Surface(ig.SurfPtr).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CTF) {
     169            0 :                     ShowSevereError(state,
     170            0 :                                     format("{}=\"{}\", invalid {} entered={}, not a valid surface for indoor green module",
     171              :                                            RoutineName,
     172            0 :                                            s_ipsc->cAlphaArgs(1),
     173            0 :                                            s_ipsc->cAlphaFieldNames(2),
     174            0 :                                            s_ipsc->cAlphaArgs(2)));
     175            0 :                     ErrorsFound = true;
     176              :                 }
     177              :             }
     178              : 
     179            1 :             if ((ig.sched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
     180            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     181            0 :                 ErrorsFound = true;
     182            1 :             } else if (!ig.sched->checkMinVal(state, Clusive::In, 0.0)) {
     183            0 :                 Sched::ShowSevereBadMin(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), Clusive::In, 0.0);
     184            0 :                 ErrorsFound = true;
     185              :             }
     186              : 
     187            1 :             ig.etCalculationMethod = ETCalculationMethod::PenmanMonteith; // default
     188            1 :             ig.etCalculationMethod = static_cast<ETCalculationMethod>(getEnumValue(etCalculationMethodsUC, s_ipsc->cAlphaArgs(4)));
     189            1 :             ig.lightingMethod = LightingMethod::LED; // default
     190            1 :             ig.lightingMethod = static_cast<LightingMethod>(getEnumValue(lightingMethodsUC, s_ipsc->cAlphaArgs(5)));
     191              : 
     192            1 :             switch (ig.lightingMethod) {
     193            0 :             case LightingMethod::LED: {
     194            0 :                 if ((ig.ledSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(6))) == nullptr) {
     195            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     196            0 :                     ErrorsFound = true;
     197            0 :                 } else if (!ig.ledSched->checkMinVal(state, Clusive::In, 0.0)) {
     198            0 :                     Sched::ShowSevereBadMin(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6), Clusive::In, 0.0);
     199            0 :                     ErrorsFound = true;
     200              :                 }
     201            0 :             } break;
     202            0 :             case LightingMethod::Daylighting: {
     203            0 :                 ig.LightRefPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     204            0 :                                                       state.dataDayltg->DaylRefPt,
     205              :                                                       &EnergyPlus::Dayltg::RefPointData::Name); // Field: Daylighting Reference Point Name
     206            0 :                 ig.LightControlPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     207            0 :                                                           state.dataDayltg->daylightControl,
     208              :                                                           &EnergyPlus::Dayltg::DaylightingControl::Name); // Field: Daylighting Control Name
     209            0 :                 if (ig.LightControlPtr == 0) {
     210            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
     211            0 :                     ErrorsFound = true;
     212            0 :                     continue;
     213              :                 }
     214            0 :             } break;
     215            1 :             case LightingMethod::LEDDaylighting: {
     216            1 :                 ig.LightRefPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     217            1 :                                                       state.dataDayltg->DaylRefPt,
     218              :                                                       &EnergyPlus::Dayltg::RefPointData::Name); // Field: Daylighting Reference Point Name
     219            1 :                 ig.LightControlPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     220            1 :                                                           state.dataDayltg->daylightControl,
     221              :                                                           &EnergyPlus::Dayltg::DaylightingControl::Name); // Field: Daylighting Control Name
     222            1 :                 if (ig.LightControlPtr == 0) {
     223            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
     224            0 :                     ErrorsFound = true;
     225            0 :                     continue;
     226              :                 }
     227              : 
     228            1 :                 if ((ig.ledDaylightTargetSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(8))) == nullptr) {
     229            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8));
     230            0 :                     ErrorsFound = true;
     231            1 :                 } else if (!ig.ledDaylightTargetSched->checkMinVal(state, Clusive::In, 0.0)) {
     232            0 :                     Sched::ShowSevereBadMin(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8), Clusive::In, 0.0);
     233            0 :                     ErrorsFound = true;
     234              :                 }
     235            1 :             } break;
     236              : 
     237            0 :             default:
     238            0 :                 break;
     239              :             }
     240              : 
     241            1 :             ig.LeafArea = s_ipsc->rNumericArgs(1);
     242            1 :             if (ig.LeafArea < 0) {
     243            0 :                 ShowSevereError(state,
     244            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     245              :                                        RoutineName,
     246            0 :                                        s_ipsc->cAlphaArgs(1),
     247            0 :                                        s_ipsc->cNumericFieldNames(1),
     248            0 :                                        s_ipsc->rNumericArgs(1)));
     249            0 :                 ErrorsFound = true;
     250              :             }
     251            1 :             ig.LEDNominalPPFD = s_ipsc->rNumericArgs(2);
     252            1 :             if (ig.LEDNominalPPFD < 0) {
     253            0 :                 ShowSevereError(state,
     254            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     255              :                                        RoutineName,
     256            0 :                                        s_ipsc->cAlphaArgs(1),
     257            0 :                                        s_ipsc->cNumericFieldNames(2),
     258            0 :                                        s_ipsc->rNumericArgs(2)));
     259            0 :                 ErrorsFound = true;
     260              :             }
     261            1 :             ig.LEDNominalEleP = s_ipsc->rNumericArgs(3);
     262            1 :             if (ig.LEDNominalEleP < 0) {
     263            0 :                 ShowSevereError(state,
     264            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     265              :                                        RoutineName,
     266            0 :                                        s_ipsc->cAlphaArgs(1),
     267            0 :                                        s_ipsc->cNumericFieldNames(3),
     268            0 :                                        s_ipsc->rNumericArgs(3)));
     269            0 :                 ErrorsFound = true;
     270              :             }
     271            1 :             ig.LEDRadFraction = s_ipsc->rNumericArgs(4);
     272            1 :             if (ig.LEDRadFraction < 0 || ig.LEDRadFraction > 1.0) {
     273            0 :                 ShowSevereError(state,
     274            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     275              :                                        RoutineName,
     276            0 :                                        s_ipsc->cAlphaArgs(1),
     277            0 :                                        s_ipsc->cNumericFieldNames(4),
     278            0 :                                        s_ipsc->rNumericArgs(4)));
     279            0 :                 ErrorsFound = true;
     280              :             }
     281            1 :             if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
     282            0 :                 SetupEMSActuator(state, "IndoorLivingWall", ig.Name, "Evapotranspiration Rate", "[kg_m2s]", ig.EMSETCalOverrideOn, ig.EMSET);
     283              :             } // EMS and API
     284              :         }
     285          801 :     }
     286              : 
     287          801 :     void SetIndoorGreenOutput(EnergyPlusData &state)
     288              :     {
     289              :         // Set up output variables
     290          801 :         auto &lw = state.dataIndoorGreen;
     291          802 :         for (int IndoorGreenNum = 1; IndoorGreenNum <= lw->NumIndoorGreen; ++IndoorGreenNum) {
     292            1 :             auto &ig = lw->indoorGreens(IndoorGreenNum);
     293            1 :             SetupZoneInternalGain(state,
     294              :                                   ig.ZonePtr,
     295              :                                   ig.Name,
     296              :                                   DataHeatBalance::IntGainType::IndoorGreen,
     297              :                                   &ig.SensibleRate,
     298              :                                   nullptr,
     299              :                                   nullptr,
     300              :                                   &ig.LatentRate,
     301              :                                   nullptr,
     302              :                                   nullptr,
     303              :                                   nullptr);
     304              : 
     305            2 :             SetupOutputVariable(state,
     306              :                                 "Indoor Living Wall Plant Surface Temperature",
     307              :                                 Constant::Units::C,
     308            1 :                                 state.dataHeatBalSurf->SurfTempIn(ig.SurfPtr),
     309              :                                 OutputProcessor::TimeStepType::Zone,
     310              :                                 OutputProcessor::StoreType::Average,
     311            1 :                                 ig.Name);
     312            2 :             SetupOutputVariable(state,
     313              :                                 "Indoor Living Wall Sensible Heat Gain Rate",
     314              :                                 Constant::Units::W,
     315            1 :                                 ig.SensibleRate,
     316              :                                 OutputProcessor::TimeStepType::Zone,
     317              :                                 OutputProcessor::StoreType::Average,
     318            1 :                                 ig.Name);
     319            2 :             SetupOutputVariable(state,
     320              :                                 "Indoor Living Wall Latent Heat Gain Rate",
     321              :                                 Constant::Units::W,
     322            1 :                                 ig.LatentRate,
     323              :                                 OutputProcessor::TimeStepType::Zone,
     324              :                                 OutputProcessor::StoreType::Average,
     325            1 :                                 ig.Name);
     326            2 :             SetupOutputVariable(state,
     327              :                                 "Indoor Living Wall Evapotranspiration Rate",
     328              :                                 Constant::Units::kg_m2s,
     329            1 :                                 ig.ETRate,
     330              :                                 OutputProcessor::TimeStepType::Zone,
     331              :                                 OutputProcessor::StoreType::Average,
     332            1 :                                 ig.Name);
     333            2 :             SetupOutputVariable(state,
     334              :                                 "Indoor Living Wall Energy Rate Required For Evapotranspiration Per Unit Area",
     335              :                                 Constant::Units::W_m2,
     336            1 :                                 ig.LambdaET,
     337              :                                 OutputProcessor::TimeStepType::Zone,
     338              :                                 OutputProcessor::StoreType::Average,
     339            1 :                                 ig.Name);
     340            2 :             SetupOutputVariable(state,
     341              :                                 "Indoor Living Wall LED Operational PPFD",
     342              :                                 Constant::Units::umol_m2s,
     343            1 :                                 ig.LEDActualPPFD,
     344              :                                 OutputProcessor::TimeStepType::Zone,
     345              :                                 OutputProcessor::StoreType::Average,
     346            1 :                                 ig.Name);
     347            2 :             SetupOutputVariable(state,
     348              :                                 "Indoor Living Wall PPFD",
     349              :                                 Constant::Units::umol_m2s,
     350            1 :                                 ig.ZPPFD,
     351              :                                 OutputProcessor::TimeStepType::Zone,
     352              :                                 OutputProcessor::StoreType::Average,
     353            1 :                                 ig.Name);
     354            2 :             SetupOutputVariable(state,
     355              :                                 "Indoor Living Wall Vapor Pressure Deficit",
     356              :                                 Constant::Units::Pa,
     357            1 :                                 ig.ZVPD,
     358              :                                 OutputProcessor::TimeStepType::Zone,
     359              :                                 OutputProcessor::StoreType::Average,
     360            1 :                                 ig.Name);
     361            2 :             SetupOutputVariable(state,
     362              :                                 "Indoor Living Wall LED Sensible Heat Gain Rate",
     363              :                                 Constant::Units::W,
     364            1 :                                 ig.SensibleRateLED,
     365              :                                 OutputProcessor::TimeStepType::Zone,
     366              :                                 OutputProcessor::StoreType::Average,
     367            1 :                                 ig.Name);
     368            2 :             SetupOutputVariable(state,
     369              :                                 "Indoor Living Wall LED Operational Power",
     370              :                                 Constant::Units::W,
     371            1 :                                 ig.LEDActualEleP,
     372              :                                 OutputProcessor::TimeStepType::Zone,
     373              :                                 OutputProcessor::StoreType::Average,
     374            1 :                                 ig.Name);
     375            2 :             SetupOutputVariable(state,
     376              :                                 "Indoor Living Wall LED Electricity Energy",
     377              :                                 Constant::Units::J,
     378            1 :                                 ig.LEDActualEleCon,
     379              :                                 OutputProcessor::TimeStepType::Zone,
     380              :                                 OutputProcessor::StoreType::Sum,
     381            1 :                                 ig.Name,
     382              :                                 Constant::eResource::Electricity,
     383              :                                 OutputProcessor::Group::Building,
     384              :                                 OutputProcessor::EndUseCat::InteriorLights,
     385              :                                 "IndoorLivingWall", // End Use subcategory
     386            1 :                                 state.dataHeatBal->Zone(ig.ZonePtr).Name,
     387            1 :                                 state.dataHeatBal->Zone(ig.ZonePtr).Multiplier,
     388            1 :                                 state.dataHeatBal->Zone(ig.ZonePtr).ListMultiplier,
     389            1 :                                 state.dataHeatBal->space(ig.SpacePtr).spaceType);
     390              :         }
     391          801 :     }
     392              : 
     393         1353 :     void InitIndoorGreen(EnergyPlusData const &state)
     394              :     {
     395              :         // Set the reporting variables to zero at each timestep.
     396         2706 :         for (auto &ig : state.dataIndoorGreen->indoorGreens) {
     397         1353 :             ig.SensibleRate = 0.0;
     398         1353 :             ig.SensibleRateLED = 0.0;
     399         1353 :             ig.LatentRate = 0.0;
     400         1353 :             ig.ZCO2 = 400;
     401         1353 :             ig.ZPPFD = 0;
     402              :         }
     403         1353 :     }
     404              : 
     405         1353 :     void ETModel(EnergyPlusData &state)
     406              :     {
     407              :         // PURPOSE OF THIS SUBROUTINE:
     408              :         // This subroutine is for the calculation of evapotranspiration effects from the Indoor Greenery System objects.
     409              :         // SUBROUTINE PARAMETER DEFINITIONS:
     410              :         static constexpr std::string_view RoutineName("ETModel: ");
     411         1353 :         auto &lw = state.dataIndoorGreen;
     412              :         Real64 ZonePreTemp; // Indoor air temprature (C)
     413              :         Real64 ZonePreHum;  // Indoor humidity ratio (kg moisture / kg dry air)
     414              :         Real64 ZoneNewTemp; // Indoor air temprature (C) after ET
     415              :         Real64 ZoneNewHum;  // Indoor humidity ratio (kg moisture / kg dry air) after ET
     416              :         Real64 ZoneSatHum;  // Saturated humidity ratio
     417              :         Real64 ZoneCO2;     // Indoor zone co2 concentration (ppm)
     418              :         Real64 ZonePPFD;    // Indoor net radiation (PPFD)
     419              :         Real64 ZoneVPD;     // vapor pressure deficit (kpa); local variable
     420              :         Real64 Timestep;    // s
     421              :         Real64 ETTotal;     // kg
     422              :         Real64 rhoair;      // kg/m3
     423              :         Real64 Tdp;         // dew point temperature
     424              :         Real64 Twb;         // wet bulb temperature
     425              :         Real64 HCons;       // enthalpy (J/kg)
     426              :         Real64 HMid;        // enthalpy 3rd point (J/kg)
     427              :         Real64 ZoneAirVol;  // zone air volume (m3)
     428              :         Real64 LAI;         // leaf area index, the ratio of one-side leaf area per unit plant growing area, maximum LAI =2 if LAI_cal>2.0
     429              :         Real64 LAI_Cal;     // calculated leaf area index based on users's input on total leaf area
     430              :         Real64 OutPb;       // outdoor pressure (kPa)
     431              :         Real64 vp;          // actual vapor pressure of the air (kpa)
     432              :         Real64 vpSat;       // saturated vapor pressure at air temperature (kpa)
     433         1353 :         Timestep = state.dataHVACGlobal->TimeStepSysSec; // unit s
     434         2706 :         for (int IndoorGreenNum = 1; IndoorGreenNum <= lw->NumIndoorGreen; ++IndoorGreenNum) {
     435         1353 :             auto &ig = lw->indoorGreens(IndoorGreenNum);
     436         1353 :             ZonePreTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ig.ZonePtr).ZT;
     437         1353 :             ZonePreHum = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ig.ZonePtr).airHumRat;
     438         1353 :             ZoneCO2 = 400;
     439         1353 :             OutPb = state.dataEnvrn->OutBaroPress / 1000;
     440         1353 :             Tdp = Psychrometrics::PsyTdpFnWPb(state, ZonePreHum, OutPb * 1000);
     441         1353 :             vp = Psychrometrics::PsyPsatFnTemp(state, Tdp, RoutineName) / 1000;
     442         1353 :             vpSat = Psychrometrics::PsyPsatFnTemp(state, ZonePreTemp, RoutineName) / 1000;
     443         1353 :             ig.ZVPD = (vpSat - vp) * 1000; // Pa
     444         1353 :             LAI_Cal = ig.LeafArea / state.dataSurface->Surface(ig.SurfPtr).Area;
     445         1353 :             LAI = LAI_Cal;
     446         1353 :             if (LAI_Cal > 2.0) {
     447            0 :                 LAI = 2.0; // maximum LAI=2.0 in the surface heat balance
     448            0 :                 ShowSevereError(state, format("Maximum indoor living wall leaf area index (LAI) =2.0 is used,calculated LAI is {}", LAI_Cal));
     449              :             }
     450         1353 :             switch (ig.lightingMethod) {
     451            0 :             case LightingMethod::LED: {
     452            0 :                 ig.ZPPFD = ig.ledSched->getCurrentVal() * ig.LEDNominalPPFD; // PPFD
     453            0 :                 ig.LEDActualPPFD = ig.ZPPFD;
     454            0 :                 ig.LEDActualEleP = ig.ledSched->getCurrentVal() * ig.LEDNominalEleP;
     455            0 :                 ig.LEDActualEleCon = ig.LEDActualEleP * Timestep;
     456            0 :             } break;
     457            0 :             case LightingMethod::Daylighting: {
     458            0 :                 ig.ZPPFD = 0;
     459            0 :                 ig.LEDActualPPFD = 0;
     460            0 :                 ig.LEDActualEleP = 0;
     461            0 :                 ig.LEDActualEleCon = 0;
     462            0 :                 if (!state.dataDayltg->CalcDayltghCoefficients_firstTime && state.dataEnvrn->SunIsUp) {
     463            0 :                     ig.ZPPFD = state.dataDayltg->daylightControl(ig.LightControlPtr).refPts(1).lums[DataSurfaces::iLum_Illum] /
     464              :                                77; // To be updated currently only take one reference point; 77 conversion factor from Lux to PPFD
     465              :                 }
     466            0 :             } break;
     467              : 
     468         1353 :             case LightingMethod::LEDDaylighting: {
     469         1353 :                 Real64 a = ig.ledDaylightTargetSched->getCurrentVal();
     470         1353 :                 Real64 b = 0;
     471         1353 :                 if (!state.dataDayltg->CalcDayltghCoefficients_firstTime && state.dataEnvrn->SunIsUp) {
     472          665 :                     b = state.dataDayltg->daylightControl(ig.LightControlPtr).refPts(1).lums[DataSurfaces::iLum_Illum] /
     473              :                         77; // To be updated currently only take one reference point; 77 conversion factor from Lux to PPFD
     474              :                 }
     475         1353 :                 ig.LEDActualPPFD = max((a - b), 0.0);
     476         1353 :                 if (ig.LEDActualPPFD >= ig.LEDNominalPPFD) {
     477          336 :                     ig.ZPPFD = ig.LEDNominalPPFD + b; // LED Nominal + Daylight
     478          336 :                     ig.LEDActualEleP = ig.LEDNominalEleP;
     479          336 :                     ig.LEDActualEleCon = ig.LEDNominalEleP * Timestep;
     480              :                 } else {
     481         1017 :                     ig.ZPPFD = a; // Targeted PPFD
     482         1017 :                     ig.LEDActualEleP = ig.LEDNominalEleP * ig.LEDActualPPFD / ig.LEDNominalPPFD;
     483         1017 :                     ig.LEDActualEleCon = ig.LEDActualEleP * Timestep;
     484              :                 }
     485         1353 :             } break;
     486            0 :             default:
     487            0 :                 break;
     488              :             }
     489         1353 :             ZonePPFD = ig.ZPPFD;
     490         1353 :             ZoneVPD = ig.ZVPD / 1000; // kPa
     491              :             // ET Calculation
     492         1353 :             if (ig.EMSETCalOverrideOn) {
     493            0 :                 ig.ETRate = ig.EMSET;
     494              :             } else {
     495         1353 :                 Real64 SwitchF = ig.etCalculationMethod == ETCalculationMethod::PenmanMonteith ? 1.0 : 2 * LAI;
     496         1353 :                 ig.ETRate = ETBaseFunction(state, ZonePreTemp, ZonePreHum, ZonePPFD, ZoneVPD, LAI, SwitchF);
     497              :             }
     498         1353 :             Real64 effectivearea = std::min(ig.LeafArea, LAI * state.dataSurface->Surface(ig.SurfPtr).Area);
     499         2706 :             ETTotal = ig.ETRate * Timestep * effectivearea *
     500         1353 :                       ig.sched->getCurrentVal(); // kg; this unit area should be surface area instead of total leaf area
     501         1353 :             Real64 hfg = Psychrometrics::PsyHfgAirFnWTdb(ZonePreHum, ZonePreTemp) / std::pow(10, 6); // Latent heat of vaporization (MJ/kg)
     502         1353 :             ig.LambdaET = ETTotal * hfg * std::pow(10, 6) / state.dataSurface->Surface(ig.SurfPtr).Area / Timestep; // (W/m2))
     503         1353 :             rhoair = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, ZonePreTemp, ZonePreHum);
     504         1353 :             ZoneAirVol = state.dataHeatBal->Zone(ig.ZonePtr).Volume;
     505         1353 :             ZoneNewHum = ZonePreHum + ETTotal / (rhoair * ZoneAirVol);
     506         1353 :             Twb = Psychrometrics::PsyTwbFnTdbWPb(state, ZonePreTemp, ZonePreHum, state.dataEnvrn->OutBaroPress);
     507         1353 :             ZoneSatHum = Psychrometrics::PsyWFnTdbRhPb(state, Twb, 1.0, state.dataEnvrn->OutBaroPress); // saturated humidity ratio
     508         1353 :             HCons = Psychrometrics::PsyHFnTdbW(ZonePreTemp, ZonePreHum);
     509         1353 :             if (ZoneNewHum <= ZoneSatHum) {
     510         1353 :                 ZoneNewTemp = Psychrometrics::PsyTdbFnHW(HCons, ZoneNewHum);
     511              :             } else {
     512            0 :                 ZoneNewTemp = Twb;
     513            0 :                 ZoneNewHum = ZoneSatHum;
     514              :             }
     515         1353 :             HMid = Psychrometrics::PsyHFnTdbW(ZoneNewTemp, ZonePreHum);
     516         1353 :             ig.LatentRate = ZoneAirVol * rhoair * (HCons - HMid) / Timestep; // unit W
     517         1353 :             ig.SensibleRateLED = (1 - ig.LEDRadFraction) * ig.LEDActualEleP; // convective heat gain from LED lights when LED is on;
     518         1353 :             ig.SensibleRate = -1.0 * ig.LatentRate + ig.SensibleRateLED;
     519         1353 :             state.dataHeatBalSurf->SurfQAdditionalHeatSourceInside(ig.SurfPtr) =
     520         2706 :                 ig.LEDRadFraction * 0.9 * ig.LEDActualEleP /
     521         1353 :                 state.dataSurface->Surface(ig.SurfPtr).Area; // assume the energy from radiation for photosynthesis is only 10%.
     522              :         }
     523         1353 :     }
     524              : 
     525         1353 :     Real64 ETBaseFunction(EnergyPlusData &state, Real64 ZonePreTemp, Real64 ZonePreHum, Real64 ZonePPFD, Real64 ZoneVPD, Real64 LAI, Real64 SwitchF)
     526              :     {
     527              :         // This subroutine provides calculation for Penman-Monteith model and Stanghellini models to predict evapotranspiration rates of plants.
     528              :         // Reference: Monteith, J.L. Evaporation and environment. in Symposia of the society for experimental biology. 1965. Cambridge University
     529              :         // Press (CUP) Cambridge
     530              :         // Reference: Stanghellini, C., Transpiration of greenhouse crops: an aid to climate management, 1987, Institute of Agricultural Engineering,
     531              :         // Wageningen, The Netherlands
     532              : 
     533         1353 :         Real64 hfg = Psychrometrics::PsyHfgAirFnWTdb(ZonePreHum, ZonePreTemp) / std::pow(10, 6); // Latent heat of vaporization (MJ/kg)
     534              :         // Slope of the saturation vapor pressure-temperature curve (kPa/°C)
     535         1353 :         Real64 slopepat = 0.200 * std::pow((0.00738 * ZonePreTemp + 0.8072), 7) - 0.000116;
     536         1353 :         Real64 CpAir = Psychrometrics::PsyCpAirFnW(ZonePreHum) / std::pow(10, 6); // specific heat of air at constant pressure (MJ kg−1 °C−1)
     537         1353 :         Real64 OutPb = state.dataEnvrn->OutBaroPress / 1000;                      // outdoor pressure (kPa)
     538         1353 :         Real64 constexpr mw(0.622);                                               // ratio molecular weight of water vapor / dry air = 0.622.
     539         1353 :         Real64 psyconst = CpAir * OutPb / (hfg * mw);                             // Psychrometric constant (kPa/°C)
     540         1353 :         Real64 In = ZonePPFD * 0.327 / std::pow(10, 6);                           // net radiation MW/m2
     541         1353 :         Real64 G = 0.0;                                                           // soil heat flux (MJ/(m2s))
     542         1353 :         Real64 rhoair = Psychrometrics::PsyRhoAirFnPbTdbW(state, OutPb * 1000, ZonePreTemp, ZonePreHum); // kg/m3
     543              :         Real64 ETRate;                                                                                   // mm/s; kg/(m2s)
     544         1353 :         Real64 rs = 60 * (1500 + ZonePPFD) / (200 + ZonePPFD);                                           // stomatal resistance s/m
     545         1353 :         Real64 ra = 350 * std::pow((0.1 / 0.1), 0.5) * (1 / (LAI + 1e-10));                              // aerodynamic resistance s/m
     546         1353 :         ETRate = (1 / hfg) * (slopepat * (In - G) + (SwitchF * rhoair * CpAir * ZoneVPD) / ra) /
     547         1353 :                  (slopepat + psyconst * (1 + rs / ra)); // Penman-Monteith ET model
     548         1353 :         return ETRate;                                  // mm/s; kg/(m2s)
     549              :     }
     550              : 
     551              : } // namespace IndoorGreen
     552              : 
     553              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1