LCOV - code coverage report
Current view: top level - EnergyPlus - RoomAirModelUserTempPattern.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 314 365 86.0 %
Date: 2023-01-17 19:17:23 Functions: 13 13 100.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // ObjexxFCL Headers
      49             : #include <ObjexxFCL/Array.functions.hh>
      50             : #include <ObjexxFCL/Array1D.hh>
      51             : #include <ObjexxFCL/ArrayS.functions.hh>
      52             : #include <ObjexxFCL/Fmath.hh>
      53             : #include <ObjexxFCL/member.functions.hh>
      54             : 
      55             : // EnergyPlus Headers
      56             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      57             : #include <EnergyPlus/DataEnvironment.hh>
      58             : #include <EnergyPlus/DataErrorTracking.hh>
      59             : #include <EnergyPlus/DataHVACGlobals.hh>
      60             : #include <EnergyPlus/DataHeatBalFanSys.hh>
      61             : #include <EnergyPlus/DataHeatBalance.hh>
      62             : #include <EnergyPlus/DataLoopNode.hh>
      63             : #include <EnergyPlus/DataRoomAirModel.hh>
      64             : #include <EnergyPlus/DataSurfaces.hh>
      65             : #include <EnergyPlus/DataZoneEnergyDemands.hh>
      66             : #include <EnergyPlus/DataZoneEquipment.hh>
      67             : #include <EnergyPlus/FluidProperties.hh>
      68             : #include <EnergyPlus/General.hh>
      69             : #include <EnergyPlus/InternalHeatGains.hh>
      70             : #include <EnergyPlus/OutputProcessor.hh>
      71             : #include <EnergyPlus/Psychrometrics.hh>
      72             : #include <EnergyPlus/RoomAirModelUserTempPattern.hh>
      73             : #include <EnergyPlus/ScheduleManager.hh>
      74             : #include <EnergyPlus/UtilityRoutines.hh>
      75             : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      76             : 
      77             : namespace EnergyPlus::RoomAirModelUserTempPattern {
      78             : 
      79             : // MODULE INFORMATION:
      80             : //       AUTHOR         Brent Griffith
      81             : //       DATE WRITTEN   August 2005 (started in January 2004)
      82             : //       RE-ENGINEERED
      83             : 
      84             : // PURPOSE OF THIS MODULE:
      85             : // This module is the main module for running the
      86             : // user-defined temperature pattern model.
      87             : // This "air model" doesn't predict anything about the room air
      88             : // but provides a method for users to model the
      89             : // impact of non-uniform air temps.  the distribution of air temperatures
      90             : // is defined by the user and referred to as a "pattern"
      91             : 
      92             : // METHODOLOGY EMPLOYED:
      93             : // This module contains all subroutines required by the
      94             : // user defined temperature pattern roomair modeling.
      95             : // See DataRoomAir.cc for variable declarations
      96             : 
      97             : // Using/Aliasing
      98             : using namespace DataRoomAirModel;
      99             : 
     100             : // Functions
     101             : 
     102       37512 : void ManageUserDefinedPatterns(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
     103             : {
     104             : 
     105             :     // SUBROUTINE INFORMATION:
     106             :     //       AUTHOR         Brent Griffith
     107             :     //       DATE WRITTEN   January 2004/Aug 2005
     108             :     //       MODIFIED       na
     109             :     //       RE-ENGINEERED  na
     110             : 
     111             :     // PURPOSE OF THIS SUBROUTINE:
     112             :     //  manage the user-defined air temp. distribution model
     113             : 
     114             :     // METHODOLOGY EMPLOYED:
     115             :     // calls subroutines
     116             : 
     117             :     // transfer data from surface domain to air domain for the specified zone
     118       37512 :     InitTempDistModel(state, ZoneNum);
     119             : 
     120       37512 :     GetSurfHBDataForTempDistModel(state, ZoneNum);
     121             : 
     122             :     // perform TempDist model calculations
     123       37512 :     CalcTempDistModel(state, ZoneNum);
     124             : 
     125             :     // transfer data from air domain back to surface domain for the specified zone
     126       37512 :     SetSurfHBDataForTempDistModel(state, ZoneNum);
     127       37512 : }
     128             : 
     129             : //****************************************************
     130             : 
     131       37512 : void InitTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
     132             : {
     133             : 
     134             :     // SUBROUTINE INFORMATION:
     135             :     //       AUTHOR         <author>
     136             :     //       DATE WRITTEN   <date_written>
     137             :     //       MODIFIED       na
     138             :     //       RE-ENGINEERED  na
     139             : 
     140             :     int SurfNum; // do loop counter
     141             : 
     142       37512 :     if (state.dataRoomAirModelTempPattern->MyOneTimeFlag) {
     143           1 :         state.dataRoomAirModelTempPattern->MyEnvrnFlag.dimension(state.dataGlobal->NumOfZones, true);
     144           1 :         state.dataRoomAirModelTempPattern->MyOneTimeFlag = false;
     145             :     }
     146             : 
     147       37512 :     if (state.dataGlobal->BeginEnvrnFlag && state.dataRoomAirModelTempPattern->MyEnvrnFlag(ZoneNum)) {
     148          81 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean = 23.0;
     149          81 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = 23.0;
     150          81 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = 23.0;
     151          81 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = 23.0;
     152          81 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient = 0.0;
     153        3780 :         for (SurfNum = 1; SurfNum <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++SurfNum) {
     154        3699 :             state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(SurfNum).TadjacentAir = 23.0;
     155             :         }
     156          81 :         state.dataRoomAirModelTempPattern->MyEnvrnFlag(ZoneNum) = false;
     157             :     }
     158             : 
     159       37512 :     if (!state.dataGlobal->BeginEnvrnFlag) state.dataRoomAirModelTempPattern->MyEnvrnFlag(ZoneNum) = true;
     160             : 
     161             :     // init report variable
     162       37512 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient = 0.0;
     163       37512 : }
     164             : 
     165       37512 : void GetSurfHBDataForTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
     166             : {
     167             : 
     168             :     // SUBROUTINE INFORMATION:
     169             :     //       AUTHOR         B. Griffith
     170             :     //       DATE WRITTEN   August 2005
     171             :     //       MODIFIED
     172             :     //       RE-ENGINEERED  na
     173             : 
     174             :     // PURPOSE OF THIS SUBROUTINE:
     175             :     //  map data from Heat Balance domain to Room Air Modeling Domain
     176             :     //  for the current zone, (only need mean air temp)
     177             :     //  also acts as an init routine
     178             : 
     179             :     // METHODOLOGY EMPLOYED:
     180             :     // use ZT from DataHeatBalFanSys
     181             : 
     182             :     // Using/Aliasing
     183             : 
     184             :     // intialize in preperation for calculations
     185       37512 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
     186       37512 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
     187       37512 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
     188     1750560 :     for (auto &e : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf)
     189     1713048 :         e.TadjacentAir = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT;
     190             : 
     191             :     // the only input this method needs is the zone MAT or ZT or ZTAV  ?  (original was ZT)
     192       37512 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean =
     193       37512 :         state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum).MAT; // this is lagged from previous corrector result
     194       37512 : }
     195             : 
     196             : //*****************************************************************************************
     197             : 
     198       37512 : void CalcTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
     199             : {
     200             : 
     201             :     // SUBROUTINE INFORMATION:
     202             :     //       AUTHOR         Brent Griffith
     203             :     //       DATE WRITTEN   August 2005
     204             :     //       MODIFIED
     205             :     //       RE-ENGINEERED
     206             : 
     207             :     // PURPOSE OF THIS SUBROUTINE:
     208             :     // figure out which pattern is scheduled and call
     209             :     // appropriate subroutine
     210             : 
     211             :     // Using/Aliasing
     212             :     using General::FindNumberInList;
     213             :     using ScheduleManager::GetCurrentScheduleValue;
     214             : 
     215             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     216             :     // unused    INTEGER    :: thisZoneInfo
     217             :     Real64 AvailTest;
     218             :     int CurntPatternKey;
     219             :     int CurPatrnID;
     220             : 
     221             :     // first determine availability
     222       37512 :     AvailTest = GetCurrentScheduleValue(state, state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).AvailSchedID);
     223             : 
     224       37512 :     if ((AvailTest != 1.0) || (!state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).IsUsed)) {
     225             :         // model not to be used. Use complete mixing method
     226             : 
     227        3498 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     228        3498 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     229        3498 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     230      166155 :         for (auto &e : state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf)
     231      162657 :             e.TadjacentAir = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     232             : 
     233        6996 :         return;
     234             : 
     235             :     } else { // choose pattern and call subroutine
     236             : 
     237       34014 :         CurntPatternKey = GetCurrentScheduleValue(state, state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).PatternSchedID);
     238             : 
     239       34014 :         CurPatrnID = FindNumberInList(CurntPatternKey, state.dataRoomAirMod->RoomAirPattern, &TemperaturePatternStruct::PatrnID);
     240             : 
     241       34014 :         if (CurPatrnID == 0) {
     242             :             // throw error here ? way to test schedules before getting to this point?
     243           0 :             ShowFatalError(state, format("User defined room air pattern index not found: {}", CurntPatternKey));
     244           0 :             return;
     245             :         }
     246             : 
     247       34014 :         switch (state.dataRoomAirMod->RoomAirPattern(CurPatrnID).PatternMode) {
     248       10492 :         case DataRoomAirModel::UserDefinedPatternType::ConstGradTemp: {
     249       10492 :             FigureConstGradPattern(state, CurPatrnID, ZoneNum);
     250       10492 :         } break;
     251       17860 :         case DataRoomAirModel::UserDefinedPatternType::TwoGradInterp: {
     252       17860 :             FigureTwoGradInterpPattern(state, CurPatrnID, ZoneNum);
     253       17860 :         } break;
     254        4868 :         case DataRoomAirModel::UserDefinedPatternType::NonDimenHeight: {
     255        4868 :             FigureHeightPattern(state, CurPatrnID, ZoneNum);
     256        4868 :         } break;
     257         794 :         case DataRoomAirModel::UserDefinedPatternType::SurfMapTemp:
     258         794 :             FigureSurfMapPattern(state, CurPatrnID, ZoneNum);
     259         794 :             break;
     260           0 :         default: {
     261             :             // should not come here
     262           0 :             break;
     263             :         }
     264             :         }
     265             :     } // availability control construct
     266             : }
     267             : 
     268         794 : void FigureSurfMapPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
     269             : {
     270             : 
     271             :     // SUBROUTINE INFORMATION:
     272             :     //       AUTHOR         B Griffith
     273             :     //       DATE WRITTEN   August 2005
     274             :     //       MODIFIED       na
     275             :     //       RE-ENGINEERED  na
     276             : 
     277             :     // PURPOSE OF THIS SUBROUTINE:
     278             :     // main calculation routine for surface pattern
     279             : 
     280             :     // METHODOLOGY EMPLOYED:
     281             :     // simple polling and applying prescribed
     282             :     // delta Tai's to current mean air temp
     283             :     // on a surface by surface basis
     284             : 
     285             :     // Using/Aliasing
     286             :     using General::FindNumberInList;
     287             : 
     288             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     289             :     Real64 Tmean;
     290             :     int found;
     291             :     int i;
     292             : 
     293         794 :     Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     294             : 
     295       39700 :     for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
     296             :         // cycle through zone surfaces and look for match
     297       77812 :         found = FindNumberInList(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).SurfID,
     298       38906 :                                  state.dataRoomAirMod->RoomAirPattern(PattrnID).MapPatrn.SurfID,
     299       38906 :                                  state.dataRoomAirMod->RoomAirPattern(PattrnID).MapPatrn.NumSurfs);
     300       38906 :         if (found != 0) { // if surf is in map then assign, else give it MAT
     301        7146 :             state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir =
     302        7146 :                 state.dataRoomAirMod->RoomAirPattern(PattrnID).MapPatrn.DeltaTai(found) + Tmean;
     303             :         } else {
     304       31760 :             state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = Tmean;
     305             :         }
     306             :     }
     307             : 
     308         794 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTstat + Tmean;
     309         794 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTleaving + Tmean;
     310         794 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTexhaust + Tmean;
     311         794 : }
     312             : 
     313        4868 : void FigureHeightPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
     314             : {
     315             : 
     316             :     // SUBROUTINE INFORMATION:
     317             :     //       AUTHOR         B Griffith
     318             :     //       DATE WRITTEN   August 2005
     319             :     //       MODIFIED       na
     320             :     //       RE-ENGINEERED  na
     321             : 
     322             :     // PURPOSE OF THIS SUBROUTINE:
     323             :     // calculate the pattern for non-dimensional vertical profile
     324             : 
     325             :     // METHODOLOGY EMPLOYED:
     326             :     // treat profile as lookup table and interpolate
     327             : 
     328             :     // Using/Aliasing
     329             :     using FluidProperties::FindArrayIndex;
     330             : 
     331             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     332             :     Real64 Tmean;
     333             :     int lowSideID;
     334             :     int highSideID;
     335             :     Real64 thisZeta;
     336             :     int i;
     337             :     Real64 lowSideZeta;
     338             :     Real64 hiSideZeta;
     339             :     Real64 fractBtwn;
     340             :     Real64 tmpDeltaTai;
     341             : 
     342        4868 :     tmpDeltaTai = 0.0;
     343        4868 :     Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     344             : 
     345      198220 :     for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
     346             : 
     347      193352 :         thisZeta = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).Zeta;
     348      193352 :         lowSideID = FindArrayIndex(thisZeta, state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn);
     349      193352 :         highSideID = lowSideID + 1;
     350      193352 :         if (lowSideID == 0) lowSideID = 1; // protect against array bounds
     351             : 
     352      193352 :         lowSideZeta = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn(lowSideID);
     353      193352 :         if (highSideID <= isize(state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn)) {
     354      188484 :             hiSideZeta = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.ZetaPatrn(highSideID);
     355             :         } else { // trap array bounds
     356        4868 :             hiSideZeta = lowSideZeta;
     357             :         }
     358      193352 :         if ((hiSideZeta - lowSideZeta) != 0.0) {
     359      183616 :             fractBtwn = (thisZeta - lowSideZeta) / (hiSideZeta - lowSideZeta);
     360      367232 :             tmpDeltaTai = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(lowSideID) +
     361      367232 :                           fractBtwn * (state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(highSideID) -
     362      183616 :                                        state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(lowSideID));
     363             : 
     364             :         } else { // would divide by zero, using low side value
     365             : 
     366        9736 :             tmpDeltaTai = state.dataRoomAirMod->RoomAirPattern(PattrnID).VertPatrn.DeltaTaiPatrn(lowSideID);
     367             :         }
     368             : 
     369      193352 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = tmpDeltaTai + Tmean;
     370             : 
     371             :     } // surfaces in this zone
     372             : 
     373        4868 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTstat + Tmean;
     374        4868 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTleaving + Tmean;
     375        4868 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTexhaust + Tmean;
     376        4868 : }
     377             : 
     378       17860 : void FigureTwoGradInterpPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
     379             : {
     380             : 
     381             :     // SUBROUTINE INFORMATION:
     382             :     //       AUTHOR         B Griffith
     383             :     //       DATE WRITTEN   Aug 2005
     384             :     //       MODIFIED       na
     385             :     //       RE-ENGINEERED  na
     386             : 
     387             :     // PURPOSE OF THIS SUBROUTINE:
     388             :     // calculate two gradient interpolation pattern
     389             : 
     390             :     // METHODOLOGY EMPLOYED:
     391             :     // Case statement controls how interpolations are done
     392             :     // based on user selected mode.
     393             :     // calculations vary by mode
     394             : 
     395             :     // Using/Aliasing
     396             : 
     397             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     398             :     Real64 Tmean;        // MAT deg C
     399             :     Real64 Grad;         // vertical temperature gradient C/m
     400             :     Real64 DeltaT;       // temperature difference
     401             :     Real64 CoolLoad;     // sensible cooling load
     402             :     Real64 HeatLoad;     // sensible heating load
     403             :     Real64 ZetaTmean;    // non-dimensional height for mean air temp
     404             :     int i;               // do loop index
     405             :     Real64 thisZeta;     // non-dimensional height
     406             :     Real64 DeltaHeight;  // height difference in m
     407             :     Real64 tempDeltaTai; // temporary temperature difference
     408             : 
     409       17860 :     if (state.dataRoomAirModelTempPattern->MyOneTimeFlag2) {
     410           1 :         state.dataRoomAirModelTempPattern->SetupOutputFlag.dimension(state.dataGlobal->NumOfZones, true); // init
     411           1 :         state.dataRoomAirModelTempPattern->MyOneTimeFlag2 = false;
     412             :     }
     413             : 
     414       17860 :     if (state.dataRoomAirModelTempPattern->SetupOutputFlag(ZoneNum)) {
     415          24 :         SetupOutputVariable(state,
     416             :                             "Room Air Zone Vertical Temperature Gradient",
     417             :                             OutputProcessor::Unit::K_m,
     418           6 :                             state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient,
     419             :                             OutputProcessor::SOVTimeStepType::HVAC,
     420             :                             OutputProcessor::SOVStoreType::State,
     421          12 :                             state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneName);
     422             : 
     423           6 :         state.dataRoomAirModelTempPattern->SetupOutputFlag(ZoneNum) = false;
     424             :     }
     425             : 
     426       17860 :     Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     427             : 
     428             :     // determine gradient depending on mode
     429       17860 :     switch (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.InterpolationMode) {
     430        2696 :     case DataRoomAirModel::UserDefinedPatternMode::OutdoorDryBulb: {
     431       10784 :         Grad = OutdoorDryBulbGrad(state.dataHeatBal->Zone(ZoneNum).OutDryBulbTemp,
     432        2696 :                                   state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale,
     433        2696 :                                   state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient,
     434        2696 :                                   state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale,
     435        2696 :                                   state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
     436        2696 :     } break;
     437        4409 :     case DataRoomAirModel::UserDefinedPatternMode::ZoneAirTemp: {
     438        4409 :         if (Tmean >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale) {
     439        1241 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
     440             : 
     441        3168 :         } else if (Tmean <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) {
     442             : 
     443        2163 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     444             :         } else { // interpolate
     445        3015 :             if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
     446        2010 :                  state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) == 0.0) {
     447             :                 // bad user input, trapped during get input
     448           0 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     449             :             } else {
     450             : 
     451        2010 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
     452        2010 :                        ((Tmean - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) /
     453        2010 :                         (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
     454        2010 :                          state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale)) *
     455        2010 :                            (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
     456        1005 :                             state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
     457             :             }
     458             :         }
     459        4409 :     } break;
     460        2419 :     case DataRoomAirModel::UserDefinedPatternMode::DeltaOutdoorZone: {
     461        2419 :         DeltaT = state.dataHeatBal->Zone(ZoneNum).OutDryBulbTemp - Tmean;
     462        2419 :         if (DeltaT >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale) {
     463         569 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
     464             : 
     465        1850 :         } else if (DeltaT <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) {
     466             : 
     467        1157 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     468             :         } else { // interpolate
     469        2079 :             if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
     470        1386 :                  state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) == 0.0) {
     471             :                 // bad user input, trapped during get input
     472           0 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     473             :             } else {
     474             : 
     475        1386 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
     476        1386 :                        ((DeltaT - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale) /
     477        1386 :                         (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale -
     478        1386 :                          state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale)) *
     479        1386 :                            (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
     480         693 :                             state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
     481             :             }
     482             :         }
     483        2419 :     } break;
     484        4168 :     case DataRoomAirModel::UserDefinedPatternMode::SensibleCooling: {
     485        4168 :         CoolLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).ZoneSNLoadCoolRate;
     486        4168 :         if (CoolLoad >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale) {
     487         271 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
     488             : 
     489        3897 :         } else if (CoolLoad <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) {
     490             : 
     491        2523 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     492             :         } else { // interpolate
     493        4122 :             if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
     494        2748 :                  state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) == 0.0) {
     495           0 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     496             :             } else {
     497             : 
     498        2748 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
     499        2748 :                        ((CoolLoad - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) /
     500        2748 :                         (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
     501        2748 :                          state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale)) *
     502        2748 :                            (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
     503        1374 :                             state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
     504             :             }
     505             :         }
     506        4168 :     } break;
     507        4168 :     case DataRoomAirModel::UserDefinedPatternMode::SensibleHeating: {
     508        4168 :         HeatLoad = state.dataZoneEnergyDemand->ZoneSysEnergyDemand(ZoneNum).ZoneSNLoadHeatRate;
     509        4168 :         if (HeatLoad >= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale) {
     510        2296 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient;
     511             : 
     512        1872 :         } else if (HeatLoad <= state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) {
     513             : 
     514        1871 :             Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     515             :         } else { // interpolate
     516           3 :             if ((state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
     517           2 :                  state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) == 0.0) {
     518           0 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient;
     519             :             } else {
     520             : 
     521           2 :                 Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient +
     522           2 :                        ((HeatLoad - state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale) /
     523           2 :                         (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundHeatRateScale -
     524           2 :                          state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundHeatRateScale)) *
     525           2 :                            (state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient -
     526           1 :                             state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient);
     527             :             }
     528             :         }
     529        4168 :     } break;
     530           0 :     default:
     531           0 :         break;
     532             :     }
     533             : 
     534       17860 :     ZetaTmean = 0.5; // by definition,
     535             : 
     536      857475 :     for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
     537      839615 :         thisZeta = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).Zeta;
     538             : 
     539      839615 :         DeltaHeight = -1.0 * (ZetaTmean - thisZeta) * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight;
     540             : 
     541      839615 :         tempDeltaTai = DeltaHeight * Grad;
     542             : 
     543      839615 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = tempDeltaTai + Tmean;
     544             :     }
     545             : 
     546       35720 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = -1.0 *
     547       35720 :                                                                   (0.5 * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight -
     548       35720 :                                                                    state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.TstatHeight) *
     549       17860 :                                                                   Grad +
     550             :                                                               Tmean;
     551       35720 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = -1.0 *
     552       35720 :                                                                      (0.5 * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight -
     553       35720 :                                                                       state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.TleavingHeight) *
     554       17860 :                                                                      Grad +
     555             :                                                                  Tmean;
     556       35720 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = -1.0 *
     557       35720 :                                                                      (0.5 * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight -
     558       35720 :                                                                       state.dataRoomAirMod->RoomAirPattern(PattrnID).TwoGradPatrn.TexhaustHeight) *
     559       17860 :                                                                      Grad +
     560             :                                                                  Tmean;
     561             : 
     562       17860 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Gradient = Grad;
     563       17860 : }
     564        2696 : Real64 OutdoorDryBulbGrad(Real64 DryBulbTemp, // Zone(ZoneNum).OutDryBulbTemp
     565             :                           Real64 UpperBound,  // RoomAirPattern(PattrnID).TwoGradPatrn.UpperBoundTempScale
     566             :                           Real64 HiGradient,  // RoomAirPattern(PattrnID).TwoGradPatrn.HiGradient
     567             :                           Real64 LowerBound,  // RoomAirPattern(PattrnID).TwoGradPatrn.LowerBoundTempScale
     568             :                           Real64 LowGradient  // RoomAirPattern(PattrnID).TwoGradPatrn.LowGradient
     569             : )
     570             : {
     571             :     Real64 Grad;
     572        2696 :     if (DryBulbTemp >= UpperBound) {
     573        1050 :         Grad = HiGradient;
     574             : 
     575        1646 :     } else if (DryBulbTemp <= LowerBound) {
     576             : 
     577        1434 :         Grad = LowGradient;
     578             :     } else { // interpolate
     579             : 
     580         212 :         if ((UpperBound - LowerBound) == 0.0) {
     581             :             // bad user input. should be trapped during get input in RoomAirManager.cc
     582           0 :             Grad = LowGradient;
     583             :         } else {
     584             : 
     585         212 :             Grad = LowGradient + ((DryBulbTemp - LowerBound) / (UpperBound - LowerBound)) * (HiGradient - LowGradient);
     586             :         }
     587             :     }
     588        2696 :     return Grad;
     589             : }
     590             : 
     591       10492 : void FigureConstGradPattern(EnergyPlusData &state, int const PattrnID, int const ZoneNum)
     592             : {
     593             : 
     594             :     // SUBROUTINE INFORMATION:
     595             :     //       AUTHOR         B. Griffith
     596             :     //       DATE WRITTEN   August 2005
     597             :     //       MODIFIED       na
     598             :     //       RE-ENGINEERED  na
     599             : 
     600             :     Real64 Tmean;        // MAT
     601             :     int i;               // loop counter
     602             :     Real64 Grad;         // vertical temperature gradient
     603             :     Real64 ZetaTmean;    // non-dimens. height for MAT, 0.5
     604             :     Real64 thisZeta;     // temporary non-dimens height
     605             :     Real64 DeltaHeight;  // temporary height difference
     606             :     Real64 tempDeltaTai; // temporary Delta Tai
     607             : 
     608       10492 :     Tmean = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).TairMean;
     609       10492 :     Grad = state.dataRoomAirMod->RoomAirPattern(PattrnID).GradPatrn.Gradient;
     610             : 
     611       10492 :     ZetaTmean = 0.5; // by definition,
     612             : 
     613      489010 :     for (i = 1; i <= state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).totNumSurfs; ++i) {
     614      478518 :         thisZeta = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).Zeta;
     615      478518 :         DeltaHeight = -1.0 * (ZetaTmean - thisZeta) * state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneHeight;
     616      478518 :         tempDeltaTai = DeltaHeight * Grad;
     617      478518 :         state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(i).TadjacentAir = tempDeltaTai + Tmean;
     618             :     }
     619             : 
     620       10492 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTstat + Tmean;
     621       10492 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTleaving + Tmean;
     622       10492 :     state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Texhaust = state.dataRoomAirMod->RoomAirPattern(PattrnID).DeltaTexhaust + Tmean;
     623       10492 : }
     624             : 
     625             : //*****************************************************************************************
     626             : 
     627         402 : Real64 FigureNDheightInZone(EnergyPlusData &state, int const thisHBsurf) // index in main Surface array
     628             : {
     629             :     // FUNCTION INFORMATION:
     630             :     //       AUTHOR         B.Griffith
     631             :     //       DATE WRITTEN   aug 2005, Jan2004
     632             :     //       MODIFIED       na
     633             :     //       RE-ENGINEERED  na
     634             : 
     635             :     // PURPOSE OF THIS FUNCTION:
     636             :     // return a non-dimensional height zeta
     637             : 
     638             :     // METHODOLOGY EMPLOYED:
     639             :     // figure average floor height (follows code in surfacegeometry.cc
     640             :     // use ceiling height from Zone structure
     641             :     // non dimensionalize surface's centroid's Z value
     642             : 
     643             :     // Using/Aliasing
     644             :     using DataVectorTypes::Vector;
     645             : 
     646             :     // Return value
     647             :     Real64 FigureNDheightInZone;
     648             : 
     649             :     // FUNCTION PARAMETER DEFINITIONS:
     650         402 :     Real64 constexpr TolValue(0.0001);
     651             : 
     652             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
     653             :     int thisZone;
     654             :     Real64 ZoneZorig;
     655             :     Real64 ZoneCeilHeight;
     656             :     Real64 Zcm;
     657             :     Real64 SurfMinZ;
     658             :     Real64 SurfMaxZ;
     659             :     Real64 Zeta;
     660             :     Real64 FloorCount;
     661             :     Real64 ZFlrAvg;
     662             :     Real64 ZMax;
     663             :     Real64 ZMin;
     664             :     int Count;
     665             :     Real64 Z1;
     666             :     Real64 Z2;
     667             : 
     668             :     // Get the centroid height for the surface
     669         402 :     Zcm = state.dataSurface->Surface(thisHBsurf).Centroid.z;
     670         402 :     thisZone = state.dataSurface->Surface(thisHBsurf).Zone;
     671             : 
     672             :     // this next Do block is copied from SurfaceGeometry.cc with modification for just floor Z
     673             :     // used find floor z.
     674         402 :     FloorCount = 0.0;
     675         402 :     ZFlrAvg = 0.0;
     676         402 :     ZMax = 0.0;
     677         402 :     ZMin = 0.0;
     678         402 :     Count = 0;
     679         804 :     for (int spaceNum : state.dataHeatBal->Zone(thisZone).spaceIndexes) {
     680         402 :         auto &thisSpace = state.dataHeatBal->space(spaceNum);
     681       18860 :         for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
     682       18458 :             if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Floor) {
     683             :                 // Use Average Z for surface, more important for roofs than floors...
     684         402 :                 ++FloorCount;
     685         402 :                 Z1 = minval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z);
     686         402 :                 Z2 = maxval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z);
     687         402 :                 ZFlrAvg += (Z1 + Z2) / 2.0;
     688             :             }
     689       18458 :             if (state.dataSurface->Surface(SurfNum).Class == DataSurfaces::SurfaceClass::Wall) {
     690             :                 // Use Wall calculation in case no floor in zone
     691       14472 :                 ++Count;
     692       14472 :                 if (Count == 1) {
     693         402 :                     ZMax = state.dataSurface->Surface(SurfNum).Vertex(1).z;
     694         402 :                     ZMin = ZMax;
     695             :                 }
     696       14472 :                 ZMax = max(ZMax, maxval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z));
     697       14472 :                 ZMin = min(ZMin, minval(state.dataSurface->Surface(SurfNum).Vertex({1, state.dataSurface->Surface(SurfNum).Sides}), &Vector::z));
     698             :             }
     699             :         }
     700             :     }
     701         402 :     if (FloorCount > 0.0) {
     702         402 :         ZFlrAvg /= FloorCount;
     703             :     } else {
     704           0 :         ZFlrAvg = ZMin;
     705             :     }
     706         402 :     ZoneZorig = ZFlrAvg; // Z floor  [M]
     707         402 :     ZoneCeilHeight = state.dataHeatBal->Zone(thisZone).CeilingHeight;
     708             : 
     709             :     // first check if some basic things are reasonable
     710             : 
     711         402 :     SurfMinZ = minval(state.dataSurface->Surface(thisHBsurf).Vertex, &Vector::z);
     712         402 :     SurfMaxZ = maxval(state.dataSurface->Surface(thisHBsurf).Vertex, &Vector::z);
     713             : 
     714         402 :     if (SurfMinZ < (ZoneZorig - TolValue)) {
     715           0 :         if (state.dataGlobal->DisplayExtraWarnings) {
     716           0 :             ShowWarningError(state, "RoomAirModelUserTempPattern: Problem in non-dimensional height calculation");
     717           0 :             ShowContinueError(
     718           0 :                 state, "too low surface: " + state.dataSurface->Surface(thisHBsurf).Name + " in zone: " + state.dataHeatBal->Zone(thisZone).Name);
     719           0 :             ShowContinueError(state, format("**** Average floor height of zone is: {:.3R}", ZoneZorig));
     720           0 :             ShowContinueError(state, format("**** Surface minimum height is: {:.3R}", SurfMinZ));
     721             :         } else {
     722           0 :             ++state.dataErrTracking->TotalRoomAirPatternTooLow;
     723             :         }
     724             :     }
     725             : 
     726         402 :     if (SurfMaxZ > (ZoneZorig + ZoneCeilHeight + TolValue)) {
     727           0 :         if (state.dataGlobal->DisplayExtraWarnings) {
     728           0 :             ShowWarningError(state, "RoomAirModelUserTempPattern: Problem in non-dimensional height calculation");
     729           0 :             ShowContinueError(
     730           0 :                 state, " too high surface: " + state.dataSurface->Surface(thisHBsurf).Name + " in zone: " + state.dataHeatBal->Zone(thisZone).Name);
     731           0 :             ShowContinueError(state, format("**** Average Ceiling height of zone is: {:.3R}", (ZoneZorig + ZoneCeilHeight)));
     732           0 :             ShowContinueError(state, format("**** Surface Maximum height is: {:.3R}", SurfMaxZ));
     733             :         } else {
     734           0 :             ++state.dataErrTracking->TotalRoomAirPatternTooHigh;
     735             :         }
     736             :     }
     737             : 
     738             :     // non dimensionalize.
     739         402 :     Zeta = (Zcm - ZoneZorig) / ZoneCeilHeight;
     740             :     // bound so that floors and ceiling are just in from endpoints.
     741             : 
     742         402 :     if (Zeta > 0.99) Zeta = 0.99;
     743             : 
     744         402 :     if (Zeta < 0.01) Zeta = 0.01;
     745             : 
     746         402 :     FigureNDheightInZone = Zeta;
     747             : 
     748         402 :     return FigureNDheightInZone;
     749             : }
     750             : 
     751             : //***************************************************
     752             : 
     753       37512 : void SetSurfHBDataForTempDistModel(EnergyPlusData &state, int const ZoneNum) // index number for the specified zone
     754             : {
     755             : 
     756             :     // SUBROUTINE INFORMATION:
     757             :     //       AUTHOR         Brent Griffith
     758             :     //       DATE WRITTEN   August 2005,Feb. 2004
     759             :     //       MODIFIED       na
     760             :     //       RE-ENGINEERED  na
     761             : 
     762             :     // PURPOSE OF THIS SUBROUTINE:
     763             :     //  map data from air domain back to surface domain for each zone
     764             :     //  collects code couples to remote data structures
     765             : 
     766             :     // METHODOLOGY EMPLOYED:
     767             :     // sets values in Heat balance variables
     768             : 
     769             :     // Using/Aliasing
     770             :     using DataHVACGlobals::RetTempMax;
     771             :     using DataHVACGlobals::RetTempMin;
     772             :     using InternalHeatGains::SumAllReturnAirLatentGains;
     773             :     using Psychrometrics::PsyCpAirFnW;
     774             :     using Psychrometrics::PsyHFnTdbW;
     775             :     using Psychrometrics::PsyHgAirFnWTdb;
     776             :     using Psychrometrics::PsyRhoAirFnPbTdbW;
     777             : 
     778             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     779             :     Real64 QRetAir;         // Heat to return air from lights
     780             :     Real64 CpAir;           // Air heat capacity [J/kg-K]
     781             :     Real64 TempRetAir;      // Return air temperature [C]
     782             :     Real64 TempZoneAir;     // Zone air temperature [C]
     783             :     int ZoneNode;           // Node number of controlled zone
     784             :     Real64 MassFlowRA;      // Return air mass flow [kg/s]
     785             :     Real64 FlowThisTS;      // Window gap air mass flow [kg/s]
     786             :     Real64 WinGapFlowToRA;  // Mass flow to return air from all airflow windows in zone [kg/s]
     787             :     Real64 WinGapFlowTtoRA; // Sum of mass flow times outlet temp for all airflow windows in zone [(kg/s)-C]
     788             :     Real64 WinGapTtoRA;     // Temp of outlet flow mixture to return air from all airflow windows in zone [C]
     789             :     Real64 H2OHtOfVap;      // Heat of vaporization of water (W/kg)
     790             :     Real64 RhoAir;          // Density of air (Kg/m3)
     791             :     Real64 ZoneMult;
     792             :     Real64 SumRetAirLatentGainRate;
     793             : 
     794             :     // set air system leaving node conditions
     795             :     // this is not so easy.  THis task is normally done in CalcZoneLeavingConditions
     796             :     //  but efforts to do this update there were not successful.
     797             :     //  Need to revisit how to best implement this. Ended up taking code from CalcZoneLeavingConditions
     798             :     //  ZoneNum is already equal to ActualZoneNum , changed block of source
     799             : 
     800       37512 :     if (state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneNodeID != 0) {
     801             :         // the zone system node should get the conditions leaving the zone (but before return air heat gains are added).
     802       37512 :         state.dataLoopNodes->Node(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneNodeID).Temp =
     803       37512 :             state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving;
     804             :     }
     805             : 
     806       37512 :     auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum);
     807       75024 :     for (int nodeCount = 1; nodeCount <= state.dataZoneEquip->ZoneEquipConfig(ZoneNum).NumReturnNodes; ++nodeCount) {
     808             :         // BEGIN BLOCK of code from CalcZoneLeavingConditions*********************************
     809       37512 :         int ReturnNode = state.dataZoneEquip->ZoneEquipConfig(ZoneNum).ReturnNode(nodeCount);
     810       37512 :         ZoneNode = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ZoneNodeID;
     811       37512 :         ZoneMult = state.dataHeatBal->Zone(ZoneNum).Multiplier * state.dataHeatBal->Zone(ZoneNum).ListMultiplier;
     812             :         // RETURN AIR HEAT GAIN from the Lights statement; this heat gain is stored in
     813             :         // Add sensible heat gain from refrigerated cases with under case returns
     814       37512 :         QRetAir = InternalHeatGains::zoneSumAllReturnAirConvectionGains(state, ZoneNum, ReturnNode);
     815             : 
     816       37512 :         CpAir = PsyCpAirFnW(state.dataLoopNodes->Node(ZoneNode).HumRat);
     817             : 
     818             :         // Need to add the energy to the return air from lights and from airflow windows. Where the heat
     819             :         // is added depends on if there is system flow or not.  If there is system flow the heat is added
     820             :         // to the Zone Return Node.  If there is no system flow then the heat is added back to the zone in the
     821             :         // Correct step through the SysDepZoneLoads variable.
     822             : 
     823       37512 :         MassFlowRA = state.dataLoopNodes->Node(ReturnNode).MassFlowRate / ZoneMult;
     824       37512 :         TempZoneAir = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tleaving; // key difference from
     825       37512 :         TempRetAir = TempZoneAir;
     826       37512 :         WinGapFlowToRA = 0.0;
     827       37512 :         WinGapTtoRA = 0.0;
     828       37512 :         WinGapFlowTtoRA = 0.0;
     829             : 
     830       37512 :         if (state.dataHeatBal->Zone(ZoneNum).HasAirFlowWindowReturn) {
     831           0 :             for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
     832           0 :                 auto &thisSpace = state.dataHeatBal->space(spaceNum);
     833           0 :                 for (int SurfNum = thisSpace.HTSurfaceFirst; SurfNum <= thisSpace.HTSurfaceLast; ++SurfNum) {
     834           0 :                     if (state.dataSurface->SurfWinAirflowThisTS(SurfNum) > 0.0 &&
     835           0 :                         state.dataSurface->SurfWinAirflowDestination(SurfNum) == DataSurfaces::WindowAirFlowDestination::Return) {
     836           0 :                         FlowThisTS = PsyRhoAirFnPbTdbW(state,
     837           0 :                                                        state.dataEnvrn->OutBaroPress,
     838           0 :                                                        state.dataSurface->SurfWinTAirflowGapOutlet(SurfNum),
     839           0 :                                                        state.dataLoopNodes->Node(ZoneNode).HumRat) *
     840           0 :                                      state.dataSurface->SurfWinAirflowThisTS(SurfNum) * state.dataSurface->Surface(SurfNum).Width;
     841           0 :                         WinGapFlowToRA += FlowThisTS;
     842           0 :                         WinGapFlowTtoRA += FlowThisTS * state.dataSurface->SurfWinTAirflowGapOutlet(SurfNum);
     843             :                     }
     844             :                 }
     845             :             }
     846             :         }
     847       37512 :         if (WinGapFlowToRA > 0.0) WinGapTtoRA = WinGapFlowTtoRA / WinGapFlowToRA;
     848             : 
     849       37512 :         if (!state.dataHeatBal->Zone(ZoneNum).NoHeatToReturnAir) {
     850       37512 :             if (MassFlowRA > 0.0) {
     851       37161 :                 if (WinGapFlowToRA > 0.0) {
     852             :                     // Add heat-to-return from window gap airflow
     853           0 :                     if (MassFlowRA >= WinGapFlowToRA) {
     854           0 :                         TempRetAir = (WinGapFlowTtoRA + (MassFlowRA - WinGapFlowToRA) * TempZoneAir) / MassFlowRA;
     855             :                     } else {
     856             :                         // All of return air comes from flow through airflow windows
     857           0 :                         TempRetAir = WinGapTtoRA;
     858             :                         // Put heat from window airflow that exceeds return air flow into zone air
     859           0 :                         thisZoneHB.SysDepZoneLoads += (WinGapFlowToRA - MassFlowRA) * CpAir * (WinGapTtoRA - TempZoneAir);
     860             :                     }
     861             :                 }
     862             :                 // Add heat-to-return from lights
     863       37161 :                 TempRetAir += QRetAir / (MassFlowRA * CpAir);
     864       37161 :                 if (TempRetAir > RetTempMax) {
     865           1 :                     state.dataLoopNodes->Node(ReturnNode).Temp = RetTempMax;
     866           1 :                     if (!state.dataGlobal->ZoneSizingCalc) {
     867           0 :                         thisZoneHB.SysDepZoneLoads += CpAir * MassFlowRA * (TempRetAir - RetTempMax);
     868             :                     }
     869       37160 :                 } else if (TempRetAir < RetTempMin) {
     870           0 :                     state.dataLoopNodes->Node(ReturnNode).Temp = RetTempMin;
     871           0 :                     if (!state.dataGlobal->ZoneSizingCalc) {
     872           0 :                         thisZoneHB.SysDepZoneLoads += CpAir * MassFlowRA * (TempRetAir - RetTempMin);
     873             :                     }
     874             :                 } else {
     875       37160 :                     state.dataLoopNodes->Node(ReturnNode).Temp = TempRetAir;
     876             :                 }
     877             :             } else { // No return air flow
     878             :                 // Assign all heat-to-return from window gap airflow to zone air
     879         351 :                 if (WinGapFlowToRA > 0.0) thisZoneHB.SysDepZoneLoads += WinGapFlowToRA * CpAir * (WinGapTtoRA - TempZoneAir);
     880             :                 // Assign all heat-to-return from lights to zone air
     881         351 :                 if (QRetAir > 0.0) thisZoneHB.SysDepZoneLoads += QRetAir;
     882         351 :                 state.dataLoopNodes->Node(ReturnNode).Temp = state.dataLoopNodes->Node(ZoneNode).Temp;
     883             :             }
     884             :         } else {
     885           0 :             state.dataLoopNodes->Node(ReturnNode).Temp = state.dataLoopNodes->Node(ZoneNode).Temp;
     886             :         }
     887             : 
     888             :         // Update the rest of the Return Air Node conditions, if the return air system exists!
     889       37512 :         state.dataLoopNodes->Node(ReturnNode).Press = state.dataLoopNodes->Node(ZoneNode).Press;
     890             : 
     891       37512 :         H2OHtOfVap = PsyHgAirFnWTdb(state.dataLoopNodes->Node(ZoneNode).HumRat, state.dataLoopNodes->Node(ReturnNode).Temp);
     892      112536 :         RhoAir = PsyRhoAirFnPbTdbW(
     893      112536 :             state, state.dataEnvrn->OutBaroPress, state.dataLoopNodes->Node(ReturnNode).Temp, state.dataLoopNodes->Node(ZoneNode).HumRat);
     894             : 
     895             :         // Include impact of under case returns for refrigerated display cases when updateing return node
     896             :         // humidity ratio
     897       37512 :         if (!state.dataHeatBal->Zone(ZoneNum).NoHeatToReturnAir) {
     898       37512 :             if (MassFlowRA > 0) {
     899       37161 :                 SumRetAirLatentGainRate = SumAllReturnAirLatentGains(state, ZoneNum, ReturnNode);
     900       37161 :                 state.dataLoopNodes->Node(ReturnNode).HumRat =
     901       37161 :                     state.dataLoopNodes->Node(ZoneNode).HumRat + (SumRetAirLatentGainRate / (H2OHtOfVap * MassFlowRA));
     902             :             } else {
     903             :                 // If no mass flow rate exists, include the latent HVAC case credit with the latent Zone case credit
     904         351 :                 state.dataLoopNodes->Node(ReturnNode).HumRat = state.dataLoopNodes->Node(ZoneNode).HumRat;
     905         351 :                 state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToZone += state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToHVAC;
     906             :                 // shouldn't the HVAC term be zeroed out then?
     907         351 :                 SumRetAirLatentGainRate = SumAllReturnAirLatentGains(state, ZoneNum, 0);
     908         351 :                 thisZoneHB.ZoneLatentGain += SumRetAirLatentGainRate;
     909             :             }
     910             :         } else {
     911           0 :             state.dataLoopNodes->Node(ReturnNode).HumRat = state.dataLoopNodes->Node(ZoneNode).HumRat;
     912           0 :             state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToZone += state.dataHeatBal->RefrigCaseCredit(ZoneNum).LatCaseCreditToHVAC;
     913             :             // shouldn't the HVAC term be zeroed out then?
     914           0 :             SumRetAirLatentGainRate = SumAllReturnAirLatentGains(state, ZoneNum, ReturnNode);
     915           0 :             thisZoneHB.ZoneLatentGain += SumRetAirLatentGainRate;
     916             :         }
     917             : 
     918       37512 :         state.dataLoopNodes->Node(ReturnNode).Enthalpy =
     919       37512 :             PsyHFnTdbW(state.dataLoopNodes->Node(ReturnNode).Temp, state.dataLoopNodes->Node(ReturnNode).HumRat);
     920             : 
     921             :         // END BLOCK of code from CalcZoneLeavingConditions*********************************
     922             :     }
     923             : 
     924             :     // set exhaust node leaving temp if present
     925       37512 :     if (allocated(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).ExhaustAirNodeID)) {
     926       37512 :         auto const &APZoneInfo(state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum));
     927       37512 :         auto const &EANodeID(APZoneInfo.ExhaustAirNodeID);
     928       37512 :         Real64 const Texhaust(APZoneInfo.Texhaust);
     929       45848 :         for (int i = 1, ie = EANodeID.u(); i <= ie; ++i) {
     930        8336 :             state.dataLoopNodes->Node(EANodeID(i)).Temp = Texhaust;
     931             :         }
     932             :     }
     933             : 
     934             :     // set thermostat reading for air system .
     935       37512 :     state.dataHeatBalFanSys->TempTstatAir(ZoneNum) = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Tstat;
     936             : 
     937             :     // set results for all surface
     938       75024 :     for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
     939       37512 :         auto &thisSpace = state.dataHeatBal->space(spaceNum);
     940       37512 :         int j = 0;
     941     1750560 :         for (int i = thisSpace.HTSurfaceFirst; i <= thisSpace.HTSurfaceLast; ++i) {
     942     1713048 :             ++j;
     943     1713048 :             state.dataHeatBal->SurfTempEffBulkAir(i) = state.dataRoomAirMod->AirPatternZoneInfo(ZoneNum).Surf(j).TadjacentAir;
     944             :         }
     945             :     }
     946             : 
     947             :     // set flag for reference air temperature mode
     948       75024 :     for (int spaceNum : state.dataHeatBal->Zone(ZoneNum).spaceIndexes) {
     949       37512 :         auto &thisSpace = state.dataHeatBal->space(spaceNum);
     950     1750560 :         for (int i = thisSpace.HTSurfaceFirst; i <= thisSpace.HTSurfaceLast; ++i) {
     951     1713048 :             state.dataSurface->SurfTAirRef(i) = DataSurfaces::RefAirTemp::AdjacentAirTemp;
     952     1713048 :             state.dataSurface->SurfTAirRefRpt(i) = DataSurfaces::SurfTAirRefReportVals[state.dataSurface->SurfTAirRef(i)];
     953             :         }
     954             :     }
     955       37512 : }
     956             : 
     957             : //*****************************************************************************************
     958             : 
     959        2313 : } // namespace EnergyPlus::RoomAirModelUserTempPattern

Generated by: LCOV version 1.13