LCOV - code coverage report
Current view: top level - EnergyPlus - IndoorGreen.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 27.5 % 287 79
Test Date: 2025-05-22 16:09:37 Functions: 66.7 % 6 4

            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       248593 :     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       248593 :         auto const &lw = state.dataIndoorGreen;
      89       248593 :         if (lw->getInputFlag) {
      90          102 :             bool ErrorsFound(false);
      91          102 :             const char *RoutineName("IndoorLivingWall: "); // include trailing blank space
      92          102 :             GetIndoorGreenInput(state, ErrorsFound);
      93          102 :             if (ErrorsFound) {
      94            0 :                 ShowFatalError(state, format("{}Errors found in input.  Program terminates.", RoutineName));
      95              :             }
      96          102 :             SetIndoorGreenOutput(state);
      97          102 :             lw->getInputFlag = false;
      98              :         }
      99       248593 :         if (lw->NumIndoorGreen > 0) {
     100            0 :             InitIndoorGreen(state);
     101              :             // Simulate evapotranspiration from indoor living walls
     102            0 :             ETModel(state);
     103              :         }
     104       248593 :     }
     105              : 
     106          103 :     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          103 :         auto &s_lw = state.dataIndoorGreen;
     112          103 :         auto &s_ip = state.dataInputProcessing->inputProcessor;
     113          103 :         auto &s_ipsc = state.dataIPShortCut;
     114              : 
     115              :         static constexpr std::string_view RoutineName("GetIndoorLivingWallInput: ");
     116          103 :         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          103 :         s_lw->NumIndoorGreen = s_ip->getNumObjectsFound(state, cCurrentModuleObject);
     122          103 :         if (s_lw->NumIndoorGreen > 0) s_lw->indoorGreens.allocate(s_lw->NumIndoorGreen); // Allocate the IndoorGreen input data array
     123          104 :         for (int IndoorGreenNum = 1; IndoorGreenNum <= s_lw->NumIndoorGreen; ++IndoorGreenNum) {
     124            1 :             auto &ig = s_lw->indoorGreens(IndoorGreenNum);
     125            2 :             s_ip->getObjectItem(state,
     126              :                                 cCurrentModuleObject,
     127              :                                 IndoorGreenNum,
     128            1 :                                 s_ipsc->cAlphaArgs,
     129              :                                 NumAlphas,
     130            1 :                                 s_ipsc->rNumericArgs,
     131              :                                 NumNums,
     132              :                                 IOStat,
     133            1 :                                 s_ipsc->lNumericFieldBlanks,
     134            1 :                                 s_ipsc->lAlphaFieldBlanks,
     135            1 :                                 s_ipsc->cAlphaFieldNames,
     136            1 :                                 s_ipsc->cNumericFieldNames);
     137            1 :             ErrorObjectHeader eoh{RoutineName, cCurrentModuleObject, s_ipsc->cAlphaArgs(1)};
     138            1 :             Util::IsNameEmpty(state, s_ipsc->cAlphaArgs(1), cCurrentModuleObject, ErrorsFound);
     139            1 :             ig.Name = s_ipsc->cAlphaArgs(1);
     140            1 :             ig.SurfName = s_ipsc->cAlphaArgs(2);
     141            1 :             ig.SurfPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(2), state.dataSurface->Surface);
     142            1 :             if (ig.SurfPtr <= 0) {
     143            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(2), s_ipsc->cAlphaArgs(2));
     144            0 :                 ErrorsFound = true;
     145              :             } else {
     146            1 :                 if (state.dataSurface->Surface(ig.SurfPtr).insideHeatSourceTermSched != nullptr) {
     147            0 :                     ShowSevereError(state,
     148            0 :                                     format("The indoor green surface {} has an Inside Face Heat Source Term Schedule defined. This surface cannot "
     149              :                                            "also be used for indoor green.",
     150            0 :                                            s_ipsc->cAlphaArgs(2)));
     151            0 :                     ErrorsFound = true;
     152              :                 }
     153            1 :                 ig.ZonePtr = state.dataSurface->Surface(ig.SurfPtr).Zone;
     154            1 :                 ig.SpacePtr = state.dataSurface->Surface(ig.SurfPtr).spaceNum;
     155              : 
     156            1 :                 if (ig.ZonePtr <= 0 || ig.SpacePtr <= 0) {
     157            0 :                     ShowSevereError(state,
     158            0 :                                     format("{}=\"{}\", invalid {} entered={}, {} is not assoicated with a thermal zone or space",
     159              :                                            RoutineName,
     160            0 :                                            s_ipsc->cAlphaArgs(1),
     161            0 :                                            s_ipsc->cAlphaFieldNames(2),
     162            0 :                                            s_ipsc->cAlphaArgs(2),
     163            0 :                                            s_ipsc->cAlphaArgs(2)));
     164            0 :                     ErrorsFound = true;
     165            2 :                 } else if (state.dataSurface->Surface(ig.SurfPtr).ExtBoundCond < 0 ||
     166            1 :                            state.dataSurface->Surface(ig.SurfPtr).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CTF) {
     167            0 :                     ShowSevereError(state,
     168            0 :                                     format("{}=\"{}\", invalid {} entered={}, not a valid surface for indoor green module",
     169              :                                            RoutineName,
     170            0 :                                            s_ipsc->cAlphaArgs(1),
     171            0 :                                            s_ipsc->cAlphaFieldNames(2),
     172            0 :                                            s_ipsc->cAlphaArgs(2)));
     173            0 :                     ErrorsFound = true;
     174              :                 }
     175              :             }
     176              : 
     177            1 :             if ((ig.sched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(3))) == nullptr) {
     178            0 :                 ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3));
     179            0 :                 ErrorsFound = true;
     180            1 :             } else if (!ig.sched->checkMinVal(state, Clusive::In, 0.0)) {
     181            0 :                 Sched::ShowSevereBadMin(state, eoh, s_ipsc->cAlphaFieldNames(3), s_ipsc->cAlphaArgs(3), Clusive::In, 0.0);
     182            0 :                 ErrorsFound = true;
     183              :             }
     184              : 
     185            1 :             ig.etCalculationMethod = ETCalculationMethod::PenmanMonteith; // default
     186            1 :             ig.etCalculationMethod = static_cast<ETCalculationMethod>(getEnumValue(etCalculationMethodsUC, s_ipsc->cAlphaArgs(4)));
     187            1 :             ig.lightingMethod = LightingMethod::LED; // default
     188            1 :             ig.lightingMethod = static_cast<LightingMethod>(getEnumValue(lightingMethodsUC, s_ipsc->cAlphaArgs(5)));
     189              : 
     190            1 :             switch (ig.lightingMethod) {
     191            1 :             case LightingMethod::LED: {
     192            1 :                 if ((ig.ledSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(6))) == nullptr) {
     193            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6));
     194            0 :                     ErrorsFound = true;
     195            1 :                 } else if (!ig.ledSched->checkMinVal(state, Clusive::In, 0.0)) {
     196            0 :                     Sched::ShowSevereBadMin(state, eoh, s_ipsc->cAlphaFieldNames(6), s_ipsc->cAlphaArgs(6), Clusive::In, 0.0);
     197            0 :                     ErrorsFound = true;
     198              :                 }
     199            1 :             } break;
     200            0 :             case LightingMethod::Daylighting: {
     201            0 :                 ig.LightRefPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     202            0 :                                                       state.dataDayltg->DaylRefPt,
     203              :                                                       &EnergyPlus::Dayltg::RefPointData::Name); // Field: Daylighting Reference Point Name
     204            0 :                 ig.LightControlPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     205            0 :                                                           state.dataDayltg->daylightControl,
     206              :                                                           &EnergyPlus::Dayltg::DaylightingControl::Name); // Field: Daylighting Control Name
     207            0 :                 if (ig.LightControlPtr == 0) {
     208            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
     209            0 :                     ErrorsFound = true;
     210            0 :                     continue;
     211              :                 }
     212            0 :             } break;
     213            0 :             case LightingMethod::LEDDaylighting: {
     214            0 :                 ig.LightRefPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     215            0 :                                                       state.dataDayltg->DaylRefPt,
     216              :                                                       &EnergyPlus::Dayltg::RefPointData::Name); // Field: Daylighting Reference Point Name
     217            0 :                 ig.LightControlPtr = Util::FindItemInList(s_ipsc->cAlphaArgs(7),
     218            0 :                                                           state.dataDayltg->daylightControl,
     219              :                                                           &EnergyPlus::Dayltg::DaylightingControl::Name); // Field: Daylighting Control Name
     220            0 :                 if (ig.LightControlPtr == 0) {
     221            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(7), s_ipsc->cAlphaArgs(7));
     222            0 :                     ErrorsFound = true;
     223            0 :                     continue;
     224              :                 }
     225              : 
     226            0 :                 if ((ig.ledDaylightTargetSched = Sched::GetSchedule(state, s_ipsc->cAlphaArgs(8))) == nullptr) {
     227            0 :                     ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8));
     228            0 :                     ErrorsFound = true;
     229            0 :                 } else if (!ig.ledDaylightTargetSched->checkMinVal(state, Clusive::In, 0.0)) {
     230            0 :                     Sched::ShowSevereBadMin(state, eoh, s_ipsc->cAlphaFieldNames(8), s_ipsc->cAlphaArgs(8), Clusive::In, 0.0);
     231            0 :                     ErrorsFound = true;
     232              :                 }
     233            0 :             } break;
     234              : 
     235            0 :             default:
     236            0 :                 break;
     237              :             }
     238              : 
     239            1 :             ig.LeafArea = s_ipsc->rNumericArgs(1);
     240            1 :             if (ig.LeafArea < 0) {
     241            0 :                 ShowSevereError(state,
     242            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     243              :                                        RoutineName,
     244            0 :                                        s_ipsc->cAlphaArgs(1),
     245            0 :                                        s_ipsc->cNumericFieldNames(1),
     246            0 :                                        s_ipsc->rNumericArgs(1)));
     247            0 :                 ErrorsFound = true;
     248              :             }
     249            1 :             ig.LEDNominalPPFD = s_ipsc->rNumericArgs(2);
     250            1 :             if (ig.LEDNominalPPFD < 0) {
     251            0 :                 ShowSevereError(state,
     252            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     253              :                                        RoutineName,
     254            0 :                                        s_ipsc->cAlphaArgs(1),
     255            0 :                                        s_ipsc->cNumericFieldNames(2),
     256            0 :                                        s_ipsc->rNumericArgs(2)));
     257            0 :                 ErrorsFound = true;
     258              :             }
     259            1 :             ig.LEDNominalEleP = s_ipsc->rNumericArgs(3);
     260            1 :             if (ig.LEDNominalEleP < 0) {
     261            0 :                 ShowSevereError(state,
     262            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     263              :                                        RoutineName,
     264            0 :                                        s_ipsc->cAlphaArgs(1),
     265            0 :                                        s_ipsc->cNumericFieldNames(3),
     266            0 :                                        s_ipsc->rNumericArgs(3)));
     267            0 :                 ErrorsFound = true;
     268              :             }
     269            1 :             ig.LEDRadFraction = s_ipsc->rNumericArgs(4);
     270            1 :             if (ig.LEDRadFraction < 0 || ig.LEDRadFraction > 1.0) {
     271            0 :                 ShowSevereError(state,
     272            0 :                                 format("{}=\"{}\", invalid {} entered={}",
     273              :                                        RoutineName,
     274            0 :                                        s_ipsc->cAlphaArgs(1),
     275            0 :                                        s_ipsc->cNumericFieldNames(4),
     276            0 :                                        s_ipsc->rNumericArgs(4)));
     277            0 :                 ErrorsFound = true;
     278              :             }
     279            1 :             if (state.dataGlobal->AnyEnergyManagementSystemInModel) {
     280            0 :                 SetupEMSActuator(state, "IndoorLivingWall", ig.Name, "Evapotranspiration Rate", "[kg_m2s]", ig.EMSETCalOverrideOn, ig.EMSET);
     281              :             } // EMS and API
     282              :         }
     283          103 :     }
     284              : 
     285          102 :     void SetIndoorGreenOutput(EnergyPlusData &state)
     286              :     {
     287              :         // Set up output variables
     288          102 :         auto &lw = state.dataIndoorGreen;
     289          102 :         for (int IndoorGreenNum = 1; IndoorGreenNum <= lw->NumIndoorGreen; ++IndoorGreenNum) {
     290            0 :             auto &ig = lw->indoorGreens(IndoorGreenNum);
     291            0 :             SetupZoneInternalGain(state,
     292              :                                   ig.ZonePtr,
     293              :                                   ig.Name,
     294              :                                   DataHeatBalance::IntGainType::IndoorGreen,
     295              :                                   &ig.SensibleRate,
     296              :                                   nullptr,
     297              :                                   nullptr,
     298              :                                   &ig.LatentRate,
     299              :                                   nullptr,
     300              :                                   nullptr,
     301              :                                   nullptr);
     302              : 
     303            0 :             SetupOutputVariable(state,
     304              :                                 "Indoor Living Wall Plant Surface Temperature",
     305              :                                 Constant::Units::C,
     306            0 :                                 state.dataHeatBalSurf->SurfTempIn(ig.SurfPtr),
     307              :                                 OutputProcessor::TimeStepType::Zone,
     308              :                                 OutputProcessor::StoreType::Average,
     309            0 :                                 ig.Name);
     310            0 :             SetupOutputVariable(state,
     311              :                                 "Indoor Living Wall Sensible Heat Gain Rate",
     312              :                                 Constant::Units::W,
     313            0 :                                 ig.SensibleRate,
     314              :                                 OutputProcessor::TimeStepType::Zone,
     315              :                                 OutputProcessor::StoreType::Average,
     316            0 :                                 ig.Name);
     317            0 :             SetupOutputVariable(state,
     318              :                                 "Indoor Living Wall Latent Heat Gain Rate",
     319              :                                 Constant::Units::W,
     320            0 :                                 ig.LatentRate,
     321              :                                 OutputProcessor::TimeStepType::Zone,
     322              :                                 OutputProcessor::StoreType::Average,
     323            0 :                                 ig.Name);
     324            0 :             SetupOutputVariable(state,
     325              :                                 "Indoor Living Wall Evapotranspiration Rate",
     326              :                                 Constant::Units::kg_m2s,
     327            0 :                                 ig.ETRate,
     328              :                                 OutputProcessor::TimeStepType::Zone,
     329              :                                 OutputProcessor::StoreType::Average,
     330            0 :                                 ig.Name);
     331            0 :             SetupOutputVariable(state,
     332              :                                 "Indoor Living Wall Energy Rate Required For Evapotranspiration Per Unit Area",
     333              :                                 Constant::Units::W_m2,
     334            0 :                                 ig.LambdaET,
     335              :                                 OutputProcessor::TimeStepType::Zone,
     336              :                                 OutputProcessor::StoreType::Average,
     337            0 :                                 ig.Name);
     338            0 :             SetupOutputVariable(state,
     339              :                                 "Indoor Living Wall LED Operational PPFD",
     340              :                                 Constant::Units::umol_m2s,
     341            0 :                                 ig.LEDActualPPFD,
     342              :                                 OutputProcessor::TimeStepType::Zone,
     343              :                                 OutputProcessor::StoreType::Average,
     344            0 :                                 ig.Name);
     345            0 :             SetupOutputVariable(state,
     346              :                                 "Indoor Living Wall PPFD",
     347              :                                 Constant::Units::umol_m2s,
     348            0 :                                 ig.ZPPFD,
     349              :                                 OutputProcessor::TimeStepType::Zone,
     350              :                                 OutputProcessor::StoreType::Average,
     351            0 :                                 ig.Name);
     352            0 :             SetupOutputVariable(state,
     353              :                                 "Indoor Living Wall Vapor Pressure Deficit",
     354              :                                 Constant::Units::Pa,
     355            0 :                                 ig.ZVPD,
     356              :                                 OutputProcessor::TimeStepType::Zone,
     357              :                                 OutputProcessor::StoreType::Average,
     358            0 :                                 ig.Name);
     359            0 :             SetupOutputVariable(state,
     360              :                                 "Indoor Living Wall LED Sensible Heat Gain Rate",
     361              :                                 Constant::Units::W,
     362            0 :                                 ig.SensibleRateLED,
     363              :                                 OutputProcessor::TimeStepType::Zone,
     364              :                                 OutputProcessor::StoreType::Average,
     365            0 :                                 ig.Name);
     366            0 :             SetupOutputVariable(state,
     367              :                                 "Indoor Living Wall LED Operational Power",
     368              :                                 Constant::Units::W,
     369            0 :                                 ig.LEDActualEleP,
     370              :                                 OutputProcessor::TimeStepType::Zone,
     371              :                                 OutputProcessor::StoreType::Average,
     372            0 :                                 ig.Name);
     373            0 :             SetupOutputVariable(state,
     374              :                                 "Indoor Living Wall LED Electricity Energy",
     375              :                                 Constant::Units::J,
     376            0 :                                 ig.LEDActualEleCon,
     377              :                                 OutputProcessor::TimeStepType::Zone,
     378              :                                 OutputProcessor::StoreType::Sum,
     379            0 :                                 ig.Name,
     380              :                                 Constant::eResource::Electricity,
     381              :                                 OutputProcessor::Group::Building,
     382              :                                 OutputProcessor::EndUseCat::InteriorLights,
     383              :                                 "IndoorLivingWall", // End Use subcategory
     384            0 :                                 state.dataHeatBal->Zone(ig.ZonePtr).Name,
     385            0 :                                 state.dataHeatBal->Zone(ig.ZonePtr).Multiplier,
     386            0 :                                 state.dataHeatBal->Zone(ig.ZonePtr).ListMultiplier,
     387            0 :                                 state.dataHeatBal->space(ig.SpacePtr).spaceType);
     388              :         }
     389          102 :     }
     390              : 
     391            0 :     void InitIndoorGreen(EnergyPlusData const &state)
     392              :     {
     393              :         // Set the reporting variables to zero at each timestep.
     394            0 :         for (auto &ig : state.dataIndoorGreen->indoorGreens) {
     395            0 :             ig.SensibleRate = 0.0;
     396            0 :             ig.SensibleRateLED = 0.0;
     397            0 :             ig.LatentRate = 0.0;
     398            0 :             ig.ZCO2 = 400;
     399            0 :             ig.ZPPFD = 0;
     400              :         }
     401            0 :     }
     402              : 
     403            0 :     void ETModel(EnergyPlusData &state)
     404              :     {
     405              :         // PURPOSE OF THIS SUBROUTINE:
     406              :         // This subroutine is for the calculation of evapotranspiration effects from the Indoor Greenery System objects.
     407              :         // SUBROUTINE PARAMETER DEFINITIONS:
     408              :         static constexpr std::string_view RoutineName("ETModel: ");
     409            0 :         auto &lw = state.dataIndoorGreen;
     410              :         Real64 ZonePreTemp; // Indoor air temprature (C)
     411              :         Real64 ZonePreHum;  // Indoor humidity ratio (kg moisture / kg dry air)
     412              :         Real64 ZoneNewTemp; // Indoor air temprature (C) after ET
     413              :         Real64 ZoneNewHum;  // Indoor humidity ratio (kg moisture / kg dry air) after ET
     414              :         Real64 ZoneSatHum;  // Saturated humidity ratio
     415              :         Real64 ZoneCO2;     // Indoor zone co2 concentration (ppm)
     416              :         Real64 ZonePPFD;    // Indoor net radiation (PPFD)
     417              :         Real64 ZoneVPD;     // vapor pressure deficit (kpa); local variable
     418              :         Real64 Timestep;    // s
     419              :         Real64 ETTotal;     // kg
     420              :         Real64 rhoair;      // kg/m3
     421              :         Real64 Tdp;         // dew point temperature
     422              :         Real64 Twb;         // wet bulb temperature
     423              :         Real64 HCons;       // enthalpy (J/kg)
     424              :         Real64 HMid;        // enthalpy 3rd point (J/kg)
     425              :         Real64 ZoneAirVol;  // zone air volume (m3)
     426              :         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
     427              :         Real64 LAI_Cal;     // calculated leaf area index based on users's input on total leaf area
     428              :         Real64 OutPb;       // outdoor pressure (kPa)
     429              :         Real64 vp;          // actual vapor pressure of the air (kpa)
     430              :         Real64 vpSat;       // saturated vapor pressure at air temperature (kpa)
     431            0 :         Timestep = state.dataHVACGlobal->TimeStepSysSec; // unit s
     432            0 :         for (int IndoorGreenNum = 1; IndoorGreenNum <= lw->NumIndoorGreen; ++IndoorGreenNum) {
     433            0 :             auto &ig = lw->indoorGreens(IndoorGreenNum);
     434            0 :             ZonePreTemp = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ig.ZonePtr).ZT;
     435            0 :             ZonePreHum = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ig.ZonePtr).airHumRat;
     436            0 :             ZoneCO2 = 400;
     437            0 :             OutPb = state.dataEnvrn->OutBaroPress / 1000;
     438            0 :             Tdp = Psychrometrics::PsyTdpFnWPb(state, ZonePreHum, OutPb * 1000);
     439            0 :             vp = Psychrometrics::PsyPsatFnTemp(state, Tdp, RoutineName) / 1000;
     440            0 :             vpSat = Psychrometrics::PsyPsatFnTemp(state, ZonePreTemp, RoutineName) / 1000;
     441            0 :             ig.ZVPD = (vpSat - vp) * 1000; // Pa
     442            0 :             LAI_Cal = ig.LeafArea / state.dataSurface->Surface(ig.SurfPtr).Area;
     443            0 :             LAI = LAI_Cal;
     444            0 :             if (LAI_Cal > 2.0) {
     445            0 :                 LAI = 2.0; // maximum LAI=2.0 in the surface heat balance
     446            0 :                 ShowSevereError(state, format("Maximum indoor living wall leaf area index (LAI) =2.0 is used,calculated LAI is {}", LAI_Cal));
     447              :             }
     448            0 :             switch (ig.lightingMethod) {
     449            0 :             case LightingMethod::LED: {
     450            0 :                 ig.ZPPFD = ig.ledSched->getCurrentVal() * ig.LEDNominalPPFD; // PPFD
     451            0 :                 ig.LEDActualPPFD = ig.ZPPFD;
     452            0 :                 ig.LEDActualEleP = ig.ledSched->getCurrentVal() * ig.LEDNominalEleP;
     453            0 :                 ig.LEDActualEleCon = ig.LEDActualEleP * Timestep;
     454            0 :             } break;
     455            0 :             case LightingMethod::Daylighting: {
     456            0 :                 ig.ZPPFD = 0;
     457            0 :                 ig.LEDActualPPFD = 0;
     458            0 :                 ig.LEDActualEleP = 0;
     459            0 :                 ig.LEDActualEleCon = 0;
     460            0 :                 if (!state.dataDayltg->CalcDayltghCoefficients_firstTime && state.dataEnvrn->SunIsUp) {
     461            0 :                     ig.ZPPFD = state.dataDayltg->daylightControl(ig.LightControlPtr).refPts(1).lums[DataSurfaces::iLum_Illum] /
     462              :                                77; // To be updated currently only take one reference point; 77 conversion factor from Lux to PPFD
     463              :                 }
     464            0 :             } break;
     465              : 
     466            0 :             case LightingMethod::LEDDaylighting: {
     467            0 :                 Real64 a = ig.ledDaylightTargetSched->getCurrentVal();
     468            0 :                 Real64 b = 0;
     469            0 :                 if (!state.dataDayltg->CalcDayltghCoefficients_firstTime && state.dataEnvrn->SunIsUp) {
     470            0 :                     b = state.dataDayltg->daylightControl(ig.LightControlPtr).refPts(1).lums[DataSurfaces::iLum_Illum] /
     471              :                         77; // To be updated currently only take one reference point; 77 conversion factor from Lux to PPFD
     472              :                 }
     473            0 :                 ig.LEDActualPPFD = max((a - b), 0.0);
     474            0 :                 if (ig.LEDActualPPFD >= ig.LEDNominalPPFD) {
     475            0 :                     ig.ZPPFD = ig.LEDNominalPPFD + b; // LED Nominal + Daylight
     476            0 :                     ig.LEDActualEleP = ig.LEDNominalEleP;
     477            0 :                     ig.LEDActualEleCon = ig.LEDNominalEleP * Timestep;
     478              :                 } else {
     479            0 :                     ig.ZPPFD = a; // Targeted PPFD
     480            0 :                     ig.LEDActualEleP = ig.LEDNominalEleP * ig.LEDActualPPFD / ig.LEDNominalPPFD;
     481            0 :                     ig.LEDActualEleCon = ig.LEDActualEleP * Timestep;
     482              :                 }
     483            0 :             } break;
     484            0 :             default:
     485            0 :                 break;
     486              :             }
     487            0 :             ZonePPFD = ig.ZPPFD;
     488            0 :             ZoneVPD = ig.ZVPD / 1000; // kPa
     489              :             // ET Calculation
     490            0 :             if (ig.EMSETCalOverrideOn) {
     491            0 :                 ig.ETRate = ig.EMSET;
     492              :             } else {
     493            0 :                 Real64 SwitchF = ig.etCalculationMethod == ETCalculationMethod::PenmanMonteith ? 1.0 : 2 * LAI;
     494            0 :                 ig.ETRate = ETBaseFunction(state, ZonePreTemp, ZonePreHum, ZonePPFD, ZoneVPD, LAI, SwitchF);
     495              :             }
     496            0 :             Real64 effectivearea = std::min(ig.LeafArea, LAI * state.dataSurface->Surface(ig.SurfPtr).Area);
     497            0 :             ETTotal = ig.ETRate * Timestep * effectivearea *
     498            0 :                       ig.sched->getCurrentVal(); // kg; this unit area should be surface area instead of total leaf area
     499            0 :             Real64 hfg = Psychrometrics::PsyHfgAirFnWTdb(ZonePreHum, ZonePreTemp) / std::pow(10, 6); // Latent heat of vaporization (MJ/kg)
     500            0 :             ig.LambdaET = ETTotal * hfg * std::pow(10, 6) / state.dataSurface->Surface(ig.SurfPtr).Area / Timestep; // (W/m2))
     501            0 :             rhoair = Psychrometrics::PsyRhoAirFnPbTdbW(state, state.dataEnvrn->OutBaroPress, ZonePreTemp, ZonePreHum);
     502            0 :             ZoneAirVol = state.dataHeatBal->Zone(ig.ZonePtr).Volume;
     503            0 :             ZoneNewHum = ZonePreHum + ETTotal / (rhoair * ZoneAirVol);
     504            0 :             Twb = Psychrometrics::PsyTwbFnTdbWPb(state, ZonePreTemp, ZonePreHum, state.dataEnvrn->OutBaroPress);
     505            0 :             ZoneSatHum = Psychrometrics::PsyWFnTdbRhPb(state, Twb, 1.0, state.dataEnvrn->OutBaroPress); // saturated humidity ratio
     506            0 :             HCons = Psychrometrics::PsyHFnTdbW(ZonePreTemp, ZonePreHum);
     507            0 :             if (ZoneNewHum <= ZoneSatHum) {
     508            0 :                 ZoneNewTemp = Psychrometrics::PsyTdbFnHW(HCons, ZoneNewHum);
     509              :             } else {
     510            0 :                 ZoneNewTemp = Twb;
     511            0 :                 ZoneNewHum = ZoneSatHum;
     512              :             }
     513            0 :             HMid = Psychrometrics::PsyHFnTdbW(ZoneNewTemp, ZonePreHum);
     514            0 :             ig.LatentRate = ZoneAirVol * rhoair * (HCons - HMid) / Timestep; // unit W
     515            0 :             ig.SensibleRateLED = (1 - ig.LEDRadFraction) * ig.LEDActualEleP; // convective heat gain from LED lights when LED is on;
     516            0 :             ig.SensibleRate = -1.0 * ig.LatentRate + ig.SensibleRateLED;
     517            0 :             state.dataHeatBalSurf->SurfQAdditionalHeatSourceInside(ig.SurfPtr) =
     518            0 :                 ig.LEDRadFraction * 0.9 * ig.LEDActualEleP /
     519            0 :                 state.dataSurface->Surface(ig.SurfPtr).Area; // assume the energy from radiation for photosynthesis is only 10%.
     520              :         }
     521            0 :     }
     522              : 
     523            1 :     Real64 ETBaseFunction(EnergyPlusData &state, Real64 ZonePreTemp, Real64 ZonePreHum, Real64 ZonePPFD, Real64 ZoneVPD, Real64 LAI, Real64 SwitchF)
     524              :     {
     525              :         // This subroutine provides calculation for Penman-Monteith model and Stanghellini models to predict evapotranspiration rates of plants.
     526              :         // Reference: Monteith, J.L. Evaporation and environment. in Symposia of the society for experimental biology. 1965. Cambridge University
     527              :         // Press (CUP) Cambridge
     528              :         // Reference: Stanghellini, C., Transpiration of greenhouse crops: an aid to climate management, 1987, Institute of Agricultural Engineering,
     529              :         // Wageningen, The Netherlands
     530              : 
     531            1 :         Real64 hfg = Psychrometrics::PsyHfgAirFnWTdb(ZonePreHum, ZonePreTemp) / std::pow(10, 6); // Latent heat of vaporization (MJ/kg)
     532              :         Real64 slopepat =
     533            1 :             0.200 * std::pow((0.00738 * ZonePreTemp + 0.8072), 7) - 0.000116; // Slope of the saturation vapor pressure-temperature curve (kPa/°C)
     534            1 :         Real64 CpAir = Psychrometrics::PsyCpAirFnW(ZonePreHum) / std::pow(10, 6); // specific heat of air at constant pressure (MJ kg−1 °C−1)
     535            1 :         Real64 OutPb = state.dataEnvrn->OutBaroPress / 1000;                      // outdoor pressure (kPa)
     536            1 :         Real64 constexpr mw(0.622);                                               // ratio molecular weight of water vapor / dry air = 0.622.
     537            1 :         Real64 psyconst = CpAir * OutPb / (hfg * mw);                             // Psychrometric constant (kPa/°C)
     538            1 :         Real64 In = ZonePPFD * 0.327 / std::pow(10, 6);                           // net radiation MW/m2
     539            1 :         Real64 G = 0.0;                                                           // soil heat flux (MJ/(m2s))
     540            1 :         Real64 rhoair = Psychrometrics::PsyRhoAirFnPbTdbW(state, OutPb * 1000, ZonePreTemp, ZonePreHum); // kg/m3
     541              :         Real64 ETRate;                                                                                   // mm/s; kg/(m2s)
     542            1 :         Real64 rs = 60 * (1500 + ZonePPFD) / (200 + ZonePPFD);                                           // stomatal resistance s/m
     543            1 :         Real64 ra = 350 * std::pow((0.1 / 0.1), 0.5) * (1 / (LAI + 1e-10));                              // aerodynamic resistance s/m
     544            1 :         ETRate = (1 / hfg) * (slopepat * (In - G) + (SwitchF * rhoair * CpAir * ZoneVPD) / ra) /
     545            1 :                  (slopepat + psyconst * (1 + rs / ra)); // Penman-Monteith ET model
     546            1 :         return ETRate;                                  // mm/s; kg/(m2s)
     547              :     }
     548              : 
     549              : } // namespace IndoorGreen
     550              : 
     551              : } // namespace EnergyPlus
        

Generated by: LCOV version 2.0-1