LCOV - code coverage report
Current view: top level - EnergyPlus - OutputProcessor.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 1897 2246 84.5 %
Date: 2024-08-24 18:31:18 Functions: 54 60 90.0 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <algorithm>
      50             : #include <cassert>
      51             : #include <memory>
      52             : #include <string>
      53             : #include <unordered_set>
      54             : 
      55             : // ObjexxFCL Headers
      56             : #include <ObjexxFCL/Array.functions.hh>
      57             : #include <ObjexxFCL/Fmath.hh>
      58             : #include <ObjexxFCL/string.functions.hh>
      59             : 
      60             : // EnergyPlus Headers
      61             : #include "re2/re2.h"
      62             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      63             : #include <EnergyPlus/DataEnvironment.hh>
      64             : #include <EnergyPlus/DataGlobalConstants.hh>
      65             : #include <EnergyPlus/DataHVACGlobals.hh>
      66             : #include <EnergyPlus/DataIPShortCuts.hh>
      67             : #include <EnergyPlus/DataOutputs.hh>
      68             : #include <EnergyPlus/DataStringGlobals.hh>
      69             : #include <EnergyPlus/DataSystemVariables.hh>
      70             : #include <EnergyPlus/General.hh>
      71             : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
      72             : #include <EnergyPlus/OutputProcessor.hh>
      73             : #include <EnergyPlus/OutputReportPredefined.hh>
      74             : #include <EnergyPlus/ResultsFramework.hh>
      75             : #include <EnergyPlus/SQLiteProcedures.hh>
      76             : #include <EnergyPlus/ScheduleManager.hh>
      77             : #include <EnergyPlus/UtilityRoutines.hh>
      78             : 
      79             : #include <fmt/ostream.h>
      80             : #include <milo/dtoa.h>
      81             : 
      82             : namespace EnergyPlus {
      83             : 
      84             : namespace OutputProcessor {
      85             : 
      86             :     // MODULE INFORMATION:
      87             :     //       AUTHOR         Linda Lawrie
      88             :     //       DATE WRITTEN   December 1998
      89             :     //       MODIFIED       na
      90             :     //       RE-ENGINEERED  na
      91             : 
      92             :     // PURPOSE OF THIS MODULE:
      93             :     // This module contains the major Output Processor routines.
      94             :     // In addition, in this file are several routines which can be called
      95             :     // without using the OutputProcessor Module
      96             : 
      97             :     // METHODOLOGY EMPLOYED:
      98             :     // Lots of pointers and other fancy data stuff.  (I didn't see a single pointer here)
      99             : 
     100             :     // Routines tagged on the end of this module:
     101             :     //  AddDDOutVar
     102             :     //  GenOutputVariablesAuditReport
     103             :     //  GetCurrentMeterValue
     104             :     //  GetInstantMeterValue
     105             :     //  GetInternalVariableValue
     106             :     //  GetInternalVariableValueExternalInterface
     107             :     //  GetMeteredVariables
     108             :     //  GetMeterIndex
     109             :     //  GetMeterResourceType
     110             :     //  GetNumMeteredVariables
     111             :     //  GetVariableKeyCountandType
     112             :     //  GetVariableKeys
     113             :     //  InitPollutionMeterReporting
     114             :     //  ProduceRDDMDD
     115             :     //  ReportingThisVariable
     116             :     //  SetInitialMeterReportingAndOutputNames
     117             :     //  SetupOutputVariable
     118             :     //  UpdateDataandReport
     119             :     //  UpdateMeterReporting
     120             : 
     121             :     // Functions
     122             : 
     123        4074 :     int DetermineMinuteForReporting(EnergyPlusData &state)
     124             :     {
     125             : 
     126             :         // FUNCTION INFORMATION:
     127             :         //       AUTHOR         Linda Lawrie
     128             :         //       DATE WRITTEN   January 2012
     129             :         //       MODIFIED       na
     130             :         //       RE-ENGINEERED  na
     131             : 
     132             :         // PURPOSE OF THIS FUNCTION:
     133             :         // When reporting peaks, minutes are used but not necessarily easily calculated.
     134             : 
     135        4074 :         Real64 constexpr FracToMin(60.0);
     136        4074 :         return ((state.dataGlobal->CurrentTime + state.dataHVACGlobal->SysTimeElapsed) - int(state.dataGlobal->CurrentTime)) * FracToMin;
     137             :     }
     138             : 
     139         796 :     void InitializeOutput(EnergyPlusData &state)
     140             :     {
     141             :         // SUBROUTINE INFORMATION:
     142             :         //       AUTHOR         Linda K. Lawrie
     143             :         //       DATE WRITTEN   December 1998
     144             : 
     145             :         // PURPOSE OF THIS SUBROUTINE:
     146             :         // This subroutine initializes the OutputProcessor data structures.
     147             : 
     148         796 :         auto &op = state.dataOutputProcessor;
     149             : 
     150             :         // Initialize end use category names - the indices must match up with endUseNames in OutputReportTabular
     151         796 :         op->EndUseCategory.allocate((int)Constant::EndUse::Num);
     152         796 :         op->EndUseCategory((int)Constant::EndUse::Heating + 1).Name = "Heating";
     153         796 :         op->EndUseCategory((int)Constant::EndUse::Cooling + 1).Name = "Cooling";
     154         796 :         op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).Name = "InteriorLights";
     155         796 :         op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).Name = "ExteriorLights";
     156         796 :         op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).Name = "InteriorEquipment";
     157         796 :         op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).Name = "ExteriorEquipment";
     158         796 :         op->EndUseCategory((int)Constant::EndUse::Fans + 1).Name = "Fans";
     159         796 :         op->EndUseCategory((int)Constant::EndUse::Pumps + 1).Name = "Pumps";
     160         796 :         op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).Name = "HeatRejection";
     161         796 :         op->EndUseCategory((int)Constant::EndUse::Humidification + 1).Name = "Humidifier";
     162         796 :         op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).Name = "HeatRecovery";
     163         796 :         op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).Name = "WaterSystems";
     164         796 :         op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).Name = "Refrigeration";
     165         796 :         op->EndUseCategory((int)Constant::EndUse::Cogeneration + 1).Name = "Cogeneration";
     166             : 
     167             :         // Initialize display names for output table - this could go away if end use key names are changed to match
     168         796 :         op->EndUseCategory((int)Constant::EndUse::Heating + 1).DisplayName = "Heating";
     169         796 :         op->EndUseCategory((int)Constant::EndUse::Cooling + 1).DisplayName = "Cooling";
     170         796 :         op->EndUseCategory((int)Constant::EndUse::InteriorLights + 1).DisplayName = "Interior Lighting";
     171         796 :         op->EndUseCategory((int)Constant::EndUse::ExteriorLights + 1).DisplayName = "Exterior Lighting";
     172         796 :         op->EndUseCategory((int)Constant::EndUse::InteriorEquipment + 1).DisplayName = "Interior Equipment";
     173         796 :         op->EndUseCategory((int)Constant::EndUse::ExteriorEquipment + 1).DisplayName = "Exterior Equipment";
     174         796 :         op->EndUseCategory((int)Constant::EndUse::Fans + 1).DisplayName = "Fans";
     175         796 :         op->EndUseCategory((int)Constant::EndUse::Pumps + 1).DisplayName = "Pumps";
     176         796 :         op->EndUseCategory((int)Constant::EndUse::HeatRejection + 1).DisplayName = "Heat Rejection";
     177         796 :         op->EndUseCategory((int)Constant::EndUse::Humidification + 1).DisplayName = "Humidification";
     178         796 :         op->EndUseCategory((int)Constant::EndUse::HeatRecovery + 1).DisplayName = "Heat Recovery";
     179         796 :         op->EndUseCategory((int)Constant::EndUse::WaterSystem + 1).DisplayName = "Water Systems";
     180         796 :         op->EndUseCategory((int)Constant::EndUse::Refrigeration + 1).DisplayName = "Refrigeration";
     181         796 :         op->EndUseCategory((int)Constant::EndUse::Cogeneration + 1).DisplayName = "Generators";
     182             : 
     183         796 :         op->OutputInitialized = true;
     184             : 
     185         796 :         op->TimeStepZoneSec = double(state.dataGlobal->MinutesPerTimeStep) * 60.0;
     186             : 
     187         796 :         state.files.mtd.ensure_open(state, "InitializeMeters", state.files.outputControl.mtd);
     188         796 :     } // InitializeOutput()
     189             : 
     190       41052 :     void addEndUseSubcategory(EnergyPlusData &state, EndUseCat endUseCat, std::string_view const endUseSubName)
     191             :     {
     192             :         // SUBROUTINE INFORMATION:
     193             :         //       AUTHOR         Peter Graham Ellis
     194             :         //       DATE WRITTEN   February 2006
     195             : 
     196             :         // PURPOSE OF THIS SUBROUTINE:
     197             :         // This subroutine manages the list of subcategories for each end-use category.
     198             : 
     199             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     200       41052 :         auto &op = state.dataOutputProcessor;
     201             : 
     202       41052 :         Constant::EndUse endUse = endUseCat2endUse[(int)endUseCat];
     203       41052 :         if (endUse == Constant::EndUse::Invalid) {
     204           0 :             ShowSevereError(state, format("Nonexistent end use passed to addEndUseSpaceType={}", endUseCatNames[(int)endUseCat]));
     205           0 :             return;
     206             :         }
     207             : 
     208       41052 :         auto &endUseCategory = op->EndUseCategory((int)endUse + 1);
     209             : 
     210       46833 :         for (int EndUseSubNum = 1; EndUseSubNum <= endUseCategory.NumSubcategories; ++EndUseSubNum) {
     211       40385 :             if (Util::SameString(endUseCategory.SubcategoryName(EndUseSubNum), endUseSubName)) {
     212       34604 :                 return; // Subcategory already exists, no further action required
     213             :             }
     214             :         }
     215             : 
     216             :         // Add the subcategory by reallocating the array
     217        6448 :         endUseCategory.SubcategoryName.redimension(++endUseCategory.NumSubcategories);
     218        6448 :         endUseCategory.SubcategoryName(endUseCategory.NumSubcategories) = endUseSubName;
     219             : 
     220        6448 :         if (endUseCategory.NumSubcategories > op->MaxNumSubcategories) {
     221         686 :             op->MaxNumSubcategories = endUseCategory.NumSubcategories;
     222             :         }
     223             :     } // addEndUseSubcategory()
     224             : 
     225        8853 :     void addEndUseSpaceType(EnergyPlusData &state, OutputProcessor::EndUseCat sovEndUseCat, std::string_view const EndUseSpaceTypeName)
     226             :     {
     227        8853 :         auto &op = state.dataOutputProcessor;
     228             : 
     229        8853 :         Constant::EndUse endUse = endUseCat2endUse[(int)sovEndUseCat];
     230             : 
     231        8853 :         if (endUse == Constant::EndUse::Invalid) {
     232           0 :             ShowSevereError(state, format("Nonexistent end use passed to addEndUseSpaceType={}", endUseCatNames[(int)sovEndUseCat]));
     233           0 :             return;
     234             :         }
     235             : 
     236        8853 :         auto &endUseCat = op->EndUseCategory((int)endUse + 1);
     237             : 
     238        8901 :         for (int endUseSpTypeNum = 1; endUseSpTypeNum <= endUseCat.numSpaceTypes; ++endUseSpTypeNum) {
     239        7564 :             if (Util::SameString(endUseCat.spaceTypeName(endUseSpTypeNum), EndUseSpaceTypeName)) {
     240        7516 :                 return; // SpaceType already exists, no further action required
     241             :             }
     242             :         }
     243             : 
     244             :         // Add the space type by reallocating the array
     245        1337 :         endUseCat.spaceTypeName.redimension(++endUseCat.numSpaceTypes);
     246        1337 :         endUseCat.spaceTypeName(endUseCat.numSpaceTypes) = EndUseSpaceTypeName;
     247             : 
     248        1337 :         if (endUseCat.numSpaceTypes > op->maxNumEndUseSpaceTypes) {
     249          12 :             op->maxNumEndUseSpaceTypes = endUseCat.numSpaceTypes;
     250             :         }
     251             :     } // addEndUseSpaceType()
     252             : 
     253        1592 :     void SetupTimePointers(EnergyPlusData &state,
     254             :                            TimeStepType const timeStep, // Which timestep is being set up, 'Zone'=1, 'HVAC'=2
     255             :                            Real64 &TimeStep             // The timestep variable.  Used to get the address
     256             :     )
     257             :     {
     258             : 
     259             :         // SUBROUTINE INFORMATION:
     260             :         //       AUTHOR         Linda K. Lawrie
     261             :         //       DATE WRITTEN   December 1998
     262             :         //       MODIFIED       na
     263             :         //       RE-ENGINEERED  na
     264             : 
     265             :         // PURPOSE OF THIS SUBROUTINE:
     266             :         // This subroutine sets up the derived type for the output processor that
     267             :         // contains pointers to the TimeStep values used in the simulation.
     268             : 
     269             :         // METHODOLOGY EMPLOYED:
     270             :         // Indicate that the TimeStep passed in is a target for the pointer
     271             :         // attributes in the derived types.
     272             : 
     273             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     274             :         // ValidateTimeStepType will throw a Fatal if not valid
     275        1592 :         if (state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep != nullptr) {
     276           0 :             ShowFatalError(state, format("SetupTimePointers was already called for {}", timeStepTypeNames[(int)timeStep]));
     277             :         }
     278        1592 :         state.dataOutputProcessor->TimeValue[(int)timeStep].TimeStep = &TimeStep;
     279        1592 :     }
     280             : 
     281     6401529 :     void CheckReportVariable(EnergyPlusData &state, std::string_view const Name, std::string const &Key, std::vector<int> &reqVarList)
     282             :     {
     283             : 
     284             :         // SUBROUTINE INFORMATION:
     285             :         //       AUTHOR         Linda K. Lawrie
     286             :         //       DATE WRITTEN   December 1998
     287             : 
     288             :         // PURPOSE OF THIS SUBROUTINE:
     289             :         // This subroutine will get the report variable information from input and
     290             :         // determine if this variable (KeyedValue and VariableName) should be reported
     291             :         // and, if so, what frequency to report.
     292             : 
     293             :         // This routine is called when SetupOutputVariable is called with no "optional"
     294             :         // Reporting Frequency.  It is expected that SetupOutputVariable would only be
     295             :         // called once for each keyed variable to be triggered for output (from the input
     296             :         // requests).  The optional report frequency would only be used for debugging
     297             :         // purposes.  Therefore, this routine will collect all occasions where this
     298             :         // passed variablename would be reported from the requested input.  It builds
     299             :         // a list of these requests (ReportList) so that the calling routine can propagate
     300             :         // the requests into the correct data structure.
     301             : 
     302             :         // METHODOLOGY EMPLOYED:
     303             :         // This instance being requested will always have a key associated with it.  Matching
     304             :         // instances (from input) may or may not have keys, but only one instance of a reporting
     305             :         // frequency per variable is allowed.  ReportList will be populated with ReqRepVars indices
     306             :         // of those extra things from input that satisfy this condition.
     307             : 
     308             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     309             : 
     310             :         // Make sure that input has been read
     311     6401529 :         GetReportVariableInput(state);
     312             : 
     313     6401529 :         auto &op = state.dataOutputProcessor;
     314             : 
     315   196390931 :         for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
     316   189989402 :             auto *reqVar = op->reqVars[iReqVar];
     317             : 
     318   189989402 :             if (!Util::SameString(reqVar->name, Name)) {
     319   189195750 :                 continue;
     320             :             }
     321             : 
     322     1524952 :             if (!reqVar->key.empty() && !(reqVar->is_simple_string && Util::SameString(reqVar->key, Key)) &&
     323     1524952 :                 !(!reqVar->is_simple_string && RE2::FullMatch(std::string{Key}, *(reqVar->case_insensitive_pattern)))) {
     324      731300 :                 continue;
     325             :             }
     326             : 
     327             :             // A match. Make sure doesn't duplicate
     328       62352 :             reqVar->Used = true;
     329       62352 :             bool Dup = false;
     330             :             // op->ReportList is allocated to a large value, so we can't use a std::find_if on it (why not?)
     331       64599 :             for (int iReqVar2 : reqVarList) {
     332        3557 :                 if (op->reqVars[iReqVar2]->freq == reqVar->freq && op->reqVars[iReqVar2]->SchedPtr == reqVar->SchedPtr) {
     333        1310 :                     Dup = true;
     334        1310 :                     break;
     335             :                 }
     336       62352 :             }
     337             : 
     338       62352 :             if (!Dup) {
     339       61042 :                 reqVarList.push_back(iReqVar);
     340             :             }
     341             :         }
     342     6401529 :     }
     343             : 
     344             :     constexpr std::array<std::string_view, (int)ReportFreq::Num> reportingFrequencyNoticeStrings = {
     345             :         " !Each Call",                                                             // EachCall
     346             :         " !TimeStep",                                                              // TimeStep
     347             :         " !Hourly",                                                                // Hourly
     348             :         " !Daily [Value,Min,Hour,Minute,Max,Hour,Minute]",                         // Daily
     349             :         " !Monthly [Value,Min,Day,Hour,Minute,Max,Day,Hour,Minute]",               // Monthly
     350             :         " !RunPeriod [Value,Min,Month,Day,Hour,Minute,Max,Month,Day,Hour,Minute]", // Simulation
     351             :         " !Annual [Value,Min,Month,Day,Hour,Minute,Max,Month,Day,Hour,Minute]"     // Yearly
     352             :     };
     353             : 
     354       28334 :     ReportFreq determineFrequency(EnergyPlusData &state, const std::string_view FreqString)
     355             :     {
     356             : 
     357             :         // SUBROUTINE INFORMATION:
     358             :         //       AUTHOR         Linda K. Lawrie
     359             :         //       DATE WRITTEN   December 1998
     360             :         //       MODIFIED       December 2017; Jason DeGraw
     361             : 
     362             :         // REFERENCES:
     363             :         //       \field Reporting Frequency
     364             :         //       \type choice
     365             :         //       \key Detailed
     366             :         //       \note Detailed lists every instance (i.e. HVAC variable timesteps)
     367             :         //       \key Timestep
     368             :         //       \note Timestep refers to the zone Timestep/Number of Timesteps in hour value
     369             :         //       \note RunPeriod, Environment, and Annual are the same
     370             :         //       \key Hourly
     371             :         //       \key Daily
     372             :         //       \key Monthly
     373             :         //       \key RunPeriod
     374             :         //       \key Environment
     375             :         //       \key Annual
     376             :         //       \default Hourly
     377             :         //       \note RunPeriod and Environment are synonymous
     378             : 
     379             :         // SUBROUTINE PARAMETER DEFINITIONS:
     380             :         static constexpr std::array<std::string_view, (int)ReportFreq::Num + 1> PossibleFreqs = {
     381             :             "DETA", "TIME", "HOUR", "DAIL", "MONT", "RUNP", "ENVI", "ANNU"};
     382             :         //=(/'detail','Timestep','Hourly','Daily','Monthly','RunPeriod','Environment','Annual'/)
     383             :         static constexpr std::array<std::string_view, (int)ReportFreq::Num + 1> ExactFreqStrings = {
     384             :             "Detailed", "Timestep", "Hourly", "Daily", "Monthly", "RunPeriod", "Environment", "Annual"};
     385             :         static constexpr std::array<std::string_view, (int)ReportFreq::Num + 1> ExactFreqStringsUC = {
     386             :             "DETAILED", "TIMESTEP", "HOURLY", "DAILY", "MONTHLY", "RUNPERIOD", "ENVIRONMENT", "ANNUAL"};
     387             :         // Vector of the result, was { -1, 0, 1, 2, 3, 4, 4, 4 } before the addition of Yearly;
     388             :         static constexpr std::array<ReportFreq, (int)ReportFreq::Num + 1> FreqValues = {ReportFreq::EachCall,
     389             :                                                                                         ReportFreq::TimeStep,
     390             :                                                                                         ReportFreq::Hour,
     391             :                                                                                         ReportFreq::Day,
     392             :                                                                                         ReportFreq::Month,
     393             :                                                                                         ReportFreq::Simulation,
     394             :                                                                                         ReportFreq::Simulation,
     395             :                                                                                         ReportFreq::Year};
     396             : 
     397       28334 :         ReportFreq freq = ReportFreq::Hour; // Default
     398             :         // TODO: I think it's supposed to be upper case already, but tests aren't doing that at least...
     399       28334 :         const std::string FreqStringUpper = Util::makeUPPER(FreqString);
     400       28334 :         std::string::size_type const LenString = min(len(FreqString), static_cast<std::string::size_type>(4u));
     401             : 
     402       28334 :         if (LenString < 4u) {
     403           0 :             return freq;
     404             :         }
     405             : 
     406       28334 :         std::string const FreqStringTrim(FreqStringUpper.substr(0, LenString));
     407       92712 :         for (unsigned Loop = 0; Loop < FreqValues.size(); ++Loop) {
     408       92712 :             if (FreqStringTrim == PossibleFreqs[Loop]) {
     409       28334 :                 if (FreqStringUpper != ExactFreqStringsUC[Loop]) {
     410           0 :                     ShowWarningError(state, format("DetermineFrequency: Entered frequency=\"{}\" is not an exact match to key strings.", FreqString));
     411           0 :                     ShowContinueError(state, format("Frequency={} will be used.", ExactFreqStrings[Loop]));
     412             :                 }
     413       28334 :                 freq = std::max(FreqValues[Loop], state.dataOutputProcessor->minimumReportFreq);
     414       28334 :                 break;
     415             :             }
     416             :         }
     417       28334 :         return freq;
     418       28334 :     }
     419             : 
     420     6401529 :     void GetReportVariableInput(EnergyPlusData &state)
     421             :     {
     422             : 
     423             :         // SUBROUTINE INFORMATION:
     424             :         //       AUTHOR         Linda K. Lawrie
     425             :         //       DATE WRITTEN   December 1998
     426             :         //       MODIFIED       December 2017; Jason DeGraw
     427             : 
     428             :         // PURPOSE OF THIS SUBROUTINE:
     429             :         // This subroutine gets the requested report variables from
     430             :         // the input file.
     431             :         // Report Variable,
     432             :         //        \memo each Report Variable command picks variables to be put onto the standard output file (.eso)
     433             :         //        \memo some variables may not be reported for every simulation
     434             :         //   A1 , \field Key_Value
     435             :         //        \note use '*' (without quotes) to apply this variable to all keys
     436             :         //   A2 , \field Variable_Name
     437             :         //   A3 , \field Reporting_Frequency
     438             :         //        \type choice
     439             :         //        \key detailed
     440             :         //        \key timestep
     441             :         //        \key hourly
     442             :         //        \key daily
     443             :         //        \key monthly
     444             :         //        \key runperiod
     445             :         //   A4 ; \field Schedule_Name
     446             :         //        \type object-list
     447             :         //        \object-list ScheduleNames
     448             : 
     449     6401529 :         constexpr std::string_view routineName = "GetReportVariableInput";
     450             :         // Using/Aliasing
     451             :         using ScheduleManager::GetScheduleIndex;
     452             : 
     453             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     454             :         int NumAlpha;
     455             :         int NumNumbers;
     456             :         int IOStat;
     457     6401529 :         bool ErrorsFound(false); // If errors detected in input
     458     6401529 :         std::string cCurrentModuleObject;
     459     6401529 :         Array1D_string cAlphaArgs(4);
     460     6401529 :         Array1D_string cAlphaFieldNames(4);
     461     6401529 :         Array1D_bool lAlphaBlanks(4);
     462     6401529 :         Array1D<Real64> rNumericArgs(1);
     463     6401529 :         Array1D_string cNumericFieldNames(1);
     464     6401529 :         Array1D_bool lNumericBlanks(1);
     465     6401529 :         auto &op = state.dataOutputProcessor;
     466             : 
     467             :         // Bail out if the input has already been read in
     468     6401529 :         if (!op->GetOutputInputFlag) {
     469     6400733 :             return;
     470             :         }
     471         796 :         op->GetOutputInputFlag = false;
     472             : 
     473             :         // First check environment variable to see of possible override for minimum reporting frequency
     474         796 :         if (!state.dataSysVars->MinReportFrequency.empty()) {
     475             :             // Formats
     476             :             static constexpr std::string_view Format_800("! <Minimum Reporting Frequency (overriding input value)>, Value, Input Value\n");
     477             :             static constexpr std::string_view Format_801(" Minimum Reporting Frequency, {},{}\n");
     478           0 :             op->minimumReportFreq = determineFrequency(state, state.dataSysVars->MinReportFrequency);
     479           0 :             print(state.files.eio, Format_800);
     480           0 :             print(state.files.eio, Format_801, reportingFrequencyNoticeStrings[(int)op->minimumReportFreq], state.dataSysVars->MinReportFrequency);
     481             :         }
     482             : 
     483         796 :         cCurrentModuleObject = "Output:Variable";
     484         796 :         int numReqVariables = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, cCurrentModuleObject);
     485             : 
     486       22338 :         for (int Loop = 1; Loop <= numReqVariables; ++Loop) {
     487             : 
     488       21542 :             state.dataInputProcessing->inputProcessor->getObjectItem(state,
     489             :                                                                      cCurrentModuleObject,
     490             :                                                                      Loop,
     491             :                                                                      cAlphaArgs,
     492             :                                                                      NumAlpha,
     493             :                                                                      rNumericArgs,
     494             :                                                                      NumNumbers,
     495             :                                                                      IOStat,
     496             :                                                                      lNumericBlanks,
     497             :                                                                      lAlphaBlanks,
     498             :                                                                      cAlphaFieldNames,
     499             :                                                                      cNumericFieldNames);
     500             : 
     501       21542 :             ErrorObjectHeader eoh{routineName, cCurrentModuleObject, cAlphaArgs(1)};
     502             : 
     503             :             // Check for duplicates?
     504       21542 :             ReqVar *reqVar = new ReqVar();
     505       21542 :             op->reqVars.push_back(reqVar);
     506             : 
     507       21542 :             reqVar->key = cAlphaArgs(1);
     508       21542 :             if (reqVar->key == "*") {
     509       15519 :                 reqVar->key = std::string();
     510             :             }
     511             : 
     512       21542 :             bool is_simple_string = !DataOutputs::isKeyRegexLike(reqVar->key);
     513       21542 :             reqVar->is_simple_string = is_simple_string;
     514       21542 :             if (!is_simple_string) {
     515           0 :                 reqVar->case_insensitive_pattern = std::make_shared<RE2>("(?i)" + reqVar->key);
     516             :             }
     517             : 
     518       21542 :             std::string::size_type const lbpos = index(cAlphaArgs(2), '['); // Remove Units designation if user put it in
     519       21542 :             if (lbpos != std::string::npos) {
     520           0 :                 cAlphaArgs(2).erase(lbpos);
     521             :                 // right trim
     522           0 :                 cAlphaArgs(2) = cAlphaArgs(2).substr(0, std::min(cAlphaArgs(2).find_last_not_of(" \f\n\r\t\v") + 1, cAlphaArgs(2).size()));
     523             :             }
     524       21542 :             reqVar->name = cAlphaArgs(2);
     525             : 
     526       21542 :             reqVar->freq = determineFrequency(state, Util::makeUPPER(cAlphaArgs(3)));
     527       21542 :             if (reqVar->freq == ReportFreq::Invalid) {
     528           0 :                 ShowSevereInvalidKey(state, eoh, cAlphaFieldNames(3), cAlphaArgs(3));
     529           0 :                 ErrorsFound = true;
     530             :             }
     531             : 
     532             :             // Schedule information
     533       21542 :             if (lAlphaBlanks(4)) {
     534       20907 :                 reqVar->SchedPtr = 0;
     535         635 :             } else if ((reqVar->SchedPtr = GetScheduleIndex(state, Util::makeUPPER(cAlphaArgs(4)))) == 0) {
     536           0 :                 ShowSevereItemNotFound(state, eoh, cAlphaFieldNames(4), cAlphaArgs(4));
     537           0 :                 ErrorsFound = true;
     538             :             }
     539             : 
     540       21542 :             reqVar->Used = false;
     541             :         }
     542             : 
     543         796 :         if (ErrorsFound) {
     544           0 :             ShowFatalError(state, format("GetReportVariableInput:{}: errors in input.", cCurrentModuleObject));
     545             :         }
     546    44805927 :     }
     547             : 
     548      105684 :     std::string produceDateString(int const date,       // Date of min/max
     549             :                                   ReportFreq const freq // Reporting Frequency
     550             :     )
     551             :     {
     552             : 
     553             :         // SUBROUTINE INFORMATION:
     554             :         //       AUTHOR         Linda K. Lawrie
     555             :         //       DATE WRITTEN   December 1998
     556             : 
     557             :         // PURPOSE OF THIS SUBROUTINE:
     558             :         // This subroutine produces the appropriate min/max string depending
     559             :         // on the reporting frequency.
     560             : 
     561             :         // METHODOLOGY EMPLOYED:
     562             :         // Prior to calling this routine, the basic value string will be
     563             :         // produced, but DecodeMonDayHrMin will not have been called.
     564             : 
     565             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     566             :         int Mon;
     567             :         int Day;
     568             :         int Hour;
     569             :         int Minute;
     570             : 
     571      105684 :         General::DecodeMonDayHrMin(date, Mon, Day, Hour, Minute);
     572             : 
     573      105684 :         switch (freq) {
     574       20386 :         case ReportFreq::Day:
     575       20386 :             return format("{:2},{:2}", Hour, Minute);
     576       71842 :         case ReportFreq::Month:
     577       71842 :             return format("{:2},{:2},{:2}", Day, Hour, Minute);
     578       13456 :         case ReportFreq::Year:
     579             :         case ReportFreq::Simulation:
     580       13456 :             return format("{:2},{:2},{:2},{:2}", Mon, Day, Hour, Minute);
     581           0 :         default:
     582           0 :             return std::string();
     583             :         }
     584             :     }
     585             : 
     586             :     // *****************************************************************************
     587             :     // The following routines implement Energy Meters in EnergyPlus.
     588             :     // *****************************************************************************
     589             : 
     590         795 :     void GetCustomMeterInput(EnergyPlusData &state, bool &ErrorsFound)
     591             :     {
     592             : 
     593             :         // SUBROUTINE INFORMATION:
     594             :         //       AUTHOR         Linda Lawrie
     595             :         //       DATE WRITTEN   January 2006
     596             : 
     597             :         // PURPOSE OF THIS SUBROUTINE:
     598             :         // This routine will help implement "custom"/user defined meters.  However, it must be called after all
     599             :         // the other meters are set up and all report variables are established.
     600             : 
     601             :         // REFERENCES:
     602             :         // Processes the objects:
     603             :         // Meter:Custom,
     604             :         //    \extensible:2 - repeat last two fields, remembering to remove ; from "inner" fields.
     605             :         //    \memo Used to allow users to combine specific variables and/or meters into
     606             :         //    \memo "custom" meter configurations.
     607             :         //    A1,  \field Name
     608             :         //         \required-field
     609             :         //         \reference CustomMeterNames
     610             :         //    A2,  \field Fuel Type
     611             :         //         \type choice
     612             :         //         \key Electricity
     613             :         //         \key NaturalGas
     614             :         //         \key PropaneGas
     615             :         //         \key FuelOilNo1
     616             :         //         \key FuelOilNo2
     617             :         //         \key Coal
     618             :         //         \key Diesel
     619             :         //         \key Gasoline
     620             :         //         \key Water
     621             :         //         \key Generic
     622             :         //         \key OtherFuel1
     623             :         //         \key OtherFuel2
     624             :         //    A3,  \field Key Name 1
     625             :         //         \required-field
     626             :         //         \begin-extensible
     627             :         //    A4,  \field Report Variable or Meter Name 1
     628             :         //         \required-field
     629             :         // <etc>
     630             :         // AND
     631             :         // Meter:CustomDecrement,
     632             :         //    \extensible:2 - repeat last two fields, remembering to remove ; from "inner" fields.
     633             :         //    \memo Used to allow users to combine specific variables and/or meters into
     634             :         //    \memo "custom" meter configurations.
     635             :         //    A1,  \field Name
     636             :         //         \required-field
     637             :         //         \reference CustomMeterNames
     638             :         //    A2,  \field Fuel Type
     639             :         //         \type choice
     640             :         //         \key Electricity
     641             :         //         \key NaturalGas
     642             :         //         \key PropaneGas
     643             :         //         \key FuelOilNo1
     644             :         //         \key FuelOilNo2
     645             :         //         \key Coal
     646             :         //         \key Diesel
     647             :         //         \key Gasoline
     648             :         //         \key Water
     649             :         //         \key Generic
     650             :         //         \key OtherFuel1
     651             :         //         \key OtherFuel2
     652             :         //    A3,  \field Source Meter Name
     653             :         //         \required-field
     654             :         //    A4,  \field Key Name 1
     655             :         //         \required-field
     656             :         //         \begin-extensible
     657             :         //    A5,  \field Report Variable or Meter Name 1
     658             :         //         \required-field
     659             :         // <etc>
     660             : 
     661         795 :         constexpr std::string_view routineName = "GetCustomMeterInput";
     662             : 
     663             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     664         795 :         auto &op = state.dataOutputProcessor;
     665         795 :         auto &ip = state.dataInputProcessing->inputProcessor;
     666         795 :         auto &ipsc = state.dataIPShortCut;
     667             : 
     668             :         int NumAlpha;
     669             :         int NumNumbers;
     670             :         int IOStat;
     671         795 :         Array1D_string NamesOfKeys;   // Specific key name
     672         795 :         Array1D_int IndexesForKeyVar; // Array index
     673             : 
     674         795 :         std::vector<int> onCustomMeterVarNums;
     675         795 :         Array1D_int VarsOnSourceMeter;
     676             :         bool BigErrorsFound;
     677             : 
     678         795 :         BigErrorsFound = false;
     679             : 
     680         795 :         int numCustomMeters = 0, numCustomDecMeters = 0;
     681         795 :         std::vector<std::string> customMeterNames;
     682         795 :         std::vector<std::string> customDecMeterNames;
     683         795 :         if (auto const found = ip->epJSON.find("Meter:Custom"); found != ip->epJSON.end()) {
     684         143 :             for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomMeters)
     685         143 :                 customMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
     686         795 :         }
     687             : 
     688         795 :         if (auto const found = ip->epJSON.find("Meter:CustomDecrement"); found != ip->epJSON.end()) {
     689          82 :             for (auto meterInstance = found.value().begin(); meterInstance != found.value().end(); ++meterInstance, ++numCustomDecMeters)
     690          82 :                 customDecMeterNames.push_back(Util::makeUPPER(meterInstance.key()));
     691         795 :         }
     692             : 
     693         795 :         ipsc->cCurrentModuleObject = "Meter:Custom";
     694         892 :         for (int Loop = 1; Loop <= numCustomMeters; ++Loop) {
     695         194 :             ip->getObjectItem(state,
     696          97 :                               ipsc->cCurrentModuleObject,
     697             :                               Loop,
     698          97 :                               ipsc->cAlphaArgs,
     699             :                               NumAlpha,
     700          97 :                               ipsc->rNumericArgs,
     701             :                               NumNumbers,
     702             :                               IOStat,
     703          97 :                               ipsc->lNumericFieldBlanks,
     704          97 :                               ipsc->lAlphaFieldBlanks,
     705          97 :                               ipsc->cAlphaFieldNames,
     706          97 :                               ipsc->cNumericFieldNames);
     707             : 
     708          97 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
     709             : 
     710          97 :             std::string meterName = ipsc->cAlphaArgs(1);
     711          97 :             std::string::size_type lbrackPos = index(meterName, '[');
     712          97 :             if (lbrackPos != std::string::npos) meterName.erase(lbrackPos);
     713             : 
     714          97 :             std::string meterNameUC = Util::makeUPPER(meterName);
     715             : 
     716             :             // Check for duplicate name
     717          97 :             if (op->meterMap.find(meterNameUC) != op->meterMap.end()) {
     718           0 :                 ShowSevereDuplicateName(state, eoh);
     719           0 :                 ErrorsFound = true;
     720           0 :                 continue;
     721             :             }
     722             : 
     723             :             // Check for invalid resource
     724             :             Constant::eResource resource =
     725          97 :                 static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
     726          97 :             if (resource == Constant::eResource::Invalid) {
     727           0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
     728           0 :                 ErrorsFound = true;
     729           0 :                 continue;
     730             :             }
     731             : 
     732          97 :             Constant::Units units = Constant::Units::Invalid;
     733             : 
     734             :             // We essentially have to do this loop twice, once to
     735             :             // check for errors and once to construct the meter.  The
     736             :             // reason is that meters are cross-linked with source
     737             :             // meters and variables and those back-links will be
     738             :             // tricky to undo later.
     739          97 :             bool foundBadSrc = false;
     740          97 :             bool itemsAssigned = false;
     741             : 
     742         476 :             for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
     743         380 :                 if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
     744           0 :                     ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
     745           0 :                     foundBadSrc = true;
     746           1 :                     break;
     747             :                 }
     748             : 
     749         380 :                 std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
     750         380 :                 std::string::size_type lbrackPos = index(meterOrVarNameUC, '[');
     751         380 :                 if (lbrackPos != std::string::npos) meterOrVarNameUC.erase(lbrackPos);
     752             : 
     753             :                 // A custom meter cannot reference another custom meter
     754         380 :                 if (std::find(customMeterNames.begin(), customMeterNames.end(), meterOrVarNameUC) != customMeterNames.end()) {
     755           0 :                     ShowWarningError(state,
     756           0 :                                      format(R"(Meter:Custom="{}", contains a reference to another Meter:Custom in field: {}="{}".)",
     757           0 :                                             ipsc->cAlphaArgs(1),
     758           0 :                                             ipsc->cAlphaFieldNames(fldIndex + 1),
     759           0 :                                             ipsc->cAlphaArgs(fldIndex + 1)));
     760           0 :                     foundBadSrc = true;
     761           0 :                     break;
     762             :                 }
     763             : 
     764             :                 // A custom meter cannot reference another customDec meter
     765         380 :                 if (std::find(customDecMeterNames.begin(), customDecMeterNames.end(), meterOrVarNameUC) != customDecMeterNames.end()) {
     766           0 :                     ShowWarningError(state,
     767           0 :                                      format(R"(Meter:Custom="{}", contains a reference to another Meter:CustomDecrement in field: {}="{}".)",
     768           0 :                                             ipsc->cAlphaArgs(1),
     769           0 :                                             ipsc->cAlphaFieldNames(fldIndex + 1),
     770           0 :                                             ipsc->cAlphaArgs(fldIndex + 1)));
     771           0 :                     foundBadSrc = true;
     772           0 :                     break;
     773             :                 }
     774             : 
     775         380 :                 if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
     776          47 :                     int srcMeterNum = foundSrcMeter->second;
     777          47 :                     auto *srcMeter = op->meters[srcMeterNum];
     778          47 :                     assert(srcMeter->type == MeterType::Normal);
     779             : 
     780             :                     // If it's the first meter, it gets to set the units
     781          47 :                     if (units == Constant::Units::Invalid) {
     782          46 :                         units = srcMeter->units;
     783          46 :                         itemsAssigned = true;
     784           1 :                     } else if (units != srcMeter->units) {
     785           0 :                         ShowWarningCustomMessage(state,
     786             :                                                  eoh,
     787           0 :                                                  format(R"(Meter:Custom="{}", differing units in {}="{}".)",
     788           0 :                                                         ipsc->cAlphaArgs(1),
     789           0 :                                                         ipsc->cAlphaFieldNames(fldIndex + 1),
     790             :                                                         meterOrVarNameUC));
     791           0 :                         ShowContinueError(state,
     792           0 :                                           format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
     793           0 :                                                  Constant::unitNames[(int)units],
     794           0 :                                                  Constant::unitNames[(int)srcMeter->units]));
     795           0 :                         foundBadSrc = true;
     796           0 :                         break;
     797             :                     }
     798             : 
     799             :                     // It's a variable
     800         333 :                 } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
     801         332 :                     int srcDDVarNum = foundSrcDDVar->second;
     802         332 :                     auto *srcDDVar = op->ddOutVars[srcDDVarNum];
     803             : 
     804             :                     // Has to be a summed variable
     805         332 :                     if (srcDDVar->storeType != StoreType::Sum) {
     806           0 :                         ShowWarningCustomMessage(state,
     807             :                                                  eoh,
     808           0 :                                                  format(R"(Meter:Custom="{}", variable not summed variable {}="{}".)",
     809           0 :                                                         ipsc->cAlphaArgs(1),
     810           0 :                                                         ipsc->cAlphaFieldNames(fldIndex + 1),
     811             :                                                         meterOrVarNameUC));
     812           0 :                         ShowContinueError(state,
     813           0 :                                           format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
     814           0 :                                                  Constant::unitNames[(int)units],
     815           0 :                                                  Constant::unitNames[(int)srcDDVar->units]));
     816           0 :                         foundBadSrc = true;
     817           0 :                         break;
     818             :                     }
     819             : 
     820             :                     // If it's the first variable, it gets to set the units
     821         332 :                     if (units == Constant::Units::Invalid) {
     822          50 :                         units = srcDDVar->units;
     823             :                         // Otherwise it has to match the existing units
     824         282 :                     } else if (units != srcDDVar->units) {
     825           0 :                         ShowWarningCustomMessage(
     826           0 :                             state, eoh, format("differing units in {}=\"{}\".", ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC));
     827           0 :                         ShowContinueError(state,
     828           0 :                                           format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
     829           0 :                                                  Constant::unitNames[(int)units],
     830           0 :                                                  Constant::unitNames[(int)srcDDVar->units]));
     831           0 :                         foundBadSrc = true;
     832           0 :                         break;
     833             :                     }
     834             : 
     835         332 :                     bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
     836             :                     // Have already checked for mismatching units between meter and source variable and assigned units
     837         332 :                     if (KeyIsStar) {
     838          13 :                         if (srcDDVar->keyOutVarNums.empty()) {
     839           0 :                             ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
     840           0 :                             foundBadSrc = true;
     841           0 :                             break;
     842             :                         }
     843             : 
     844          13 :                         itemsAssigned = true;
     845             :                     } else { // Key is not "*"
     846         319 :                         bool foundKey = false;
     847        3367 :                         for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
     848        3367 :                             if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
     849         319 :                                 foundKey = true;
     850         319 :                                 itemsAssigned = true;
     851         319 :                                 break;
     852             :                             }
     853         319 :                         }
     854         319 :                         if (!foundKey) {
     855           0 :                             ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
     856           0 :                             foundBadSrc = true;
     857           0 :                             break;
     858             :                         }
     859             :                     } // if (keyIsStar)
     860             : 
     861             :                     // Not a meter or a variable
     862             :                 } else {
     863             :                     // Cannot use ShowWarningItemNotFound because this string appears in a unit test
     864           2 :                     ShowWarningError(state,
     865           4 :                                      format(R"(Meter:Custom="{}", invalid {}="{}".)",
     866           1 :                                             ipsc->cAlphaArgs(1),
     867           1 :                                             ipsc->cAlphaFieldNames(fldIndex + 1),
     868           1 :                                             ipsc->cAlphaArgs(fldIndex + 1)));
     869           1 :                     ShowContinueError(state, "...will not be shown with the Meter results.");
     870           1 :                     foundBadSrc = true;
     871           1 :                     break;
     872         713 :                 }
     873             : 
     874         380 :             } // for (fldIndex)
     875             : 
     876             :             // Somehow, this meter is not linked to any variables either directly or via another meter
     877          97 :             if (!itemsAssigned) {
     878           1 :                 ShowWarningError(state, format("Meter:Custom=\"{}\", no items assigned ", ipsc->cAlphaArgs(1)));
     879           1 :                 ShowContinueError(
     880             :                     state, "...will not be shown with the Meter results. This may be caused by a Meter:Custom be assigned to another Meter:Custom.");
     881           1 :                 continue;
     882             :             }
     883             : 
     884             :             // One of the sources is bad
     885          96 :             if (foundBadSrc) {
     886           0 :                 continue;
     887             :             }
     888             : 
     889          96 :             auto *meter = new Meter(meterName);
     890          96 :             meter->type = MeterType::Custom;
     891          96 :             meter->resource = resource;
     892          96 :             meter->units = units;
     893          96 :             bool errFlag = false;
     894          96 :             meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
     895          96 :             if (errFlag) {
     896           1 :                 ShowContinueError(state, format("..on {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
     897           1 :                 ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
     898             :             }
     899             : 
     900             :             // This meter is good
     901          96 :             int meterNum = op->meters.size();
     902          96 :             op->meters.push_back(meter);
     903          96 :             op->meterMap.insert_or_assign(meterNameUC, meterNum);
     904             : 
     905          96 :             for (ReportFreq reportFreq :
     906         768 :                  {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
     907         576 :                 meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
     908             :             }
     909             : 
     910          96 :             for (ReportFreq reportFreq :
     911         768 :                  {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
     912         576 :                 meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
     913             :             }
     914             : 
     915             :             // Do the loop again, this time without error checking
     916         475 :             for (int fldIndex = 3; fldIndex <= NumAlpha; fldIndex += 2) {
     917             :                 // No need to check for empty fields
     918         379 :                 std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
     919         379 :                 std::string::size_type lbrackPos = index(meterOrVarNameUC, '[');
     920         379 :                 if (lbrackPos != std::string::npos) meterOrVarNameUC.erase(lbrackPos);
     921             : 
     922             :                 // No need to check for custom source meters
     923         379 :                 if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
     924          47 :                     int srcMeterNum = foundSrcMeter->second;
     925          47 :                     auto *srcMeter = op->meters[srcMeterNum];
     926          47 :                     assert(srcMeter->type == MeterType::Normal);
     927             : 
     928             :                     // No need to check for units
     929             :                     // No need to check for duplicates
     930             : 
     931             :                     // Check for duplicates
     932          47 :                     if (std::find(meter->srcMeterNums.begin(), meter->srcMeterNums.end(), srcMeterNum) != meter->srcMeterNums.end()) {
     933           0 :                         ShowWarningCustomMessage(state,
     934             :                                                  eoh,
     935           0 :                                                  format("{}=\"{}\" referenced multiple times, only first instance will be used",
     936           0 :                                                         ipsc->cAlphaFieldNames(fldIndex + 1),
     937             :                                                         meterOrVarNameUC));
     938           0 :                         continue;
     939             :                     }
     940             : 
     941             :                     // Link meter to src meter and var and vice versa
     942          47 :                     meter->srcMeterNums.push_back(srcMeterNum);
     943          47 :                     srcMeter->dstMeterNums.push_back(meterNum);
     944             : 
     945         408 :                     for (int srcVarNum : srcMeter->srcVarNums) {
     946         361 :                         if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
     947         361 :                             meter->srcVarNums.push_back(srcVarNum);
     948         361 :                             op->outVars[srcVarNum]->meterNums.push_back(meterNum);
     949             :                         }
     950          47 :                     }
     951             : 
     952             :                     // It's a variable
     953         332 :                 } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
     954         332 :                     int srcDDVarNum = foundSrcDDVar->second;
     955         332 :                     auto *srcDDVar = op->ddOutVars[srcDDVarNum];
     956             : 
     957             :                     // No need to check for a summed variable
     958             :                     // No need to check for units match or to assign units
     959             : 
     960         332 :                     bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
     961             :                     // Have already checked for mismatching units between meter and source variable and assigned units
     962         332 :                     if (KeyIsStar) {
     963             :                         // No need to check for empty keys
     964          26 :                         for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
     965          13 :                             if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
     966           0 :                                 ShowWarningCustomMessage(state,
     967             :                                                          eoh,
     968           0 :                                                          format("Output variable \"{}\" referenced multiple times (directly or via meter)",
     969           0 :                                                                 op->outVars[keyOutVarNum]->keyColonNameUC));
     970             : 
     971             :                             } else {
     972          13 :                                 meter->srcVarNums.push_back(keyOutVarNum);
     973          13 :                                 op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
     974             :                             }
     975          13 :                         }
     976             :                     } else { // Key is not "*"
     977        3367 :                         for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
     978        3367 :                             if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
     979         319 :                                 if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
     980           0 :                                     ShowWarningCustomMessage(state,
     981             :                                                              eoh,
     982           0 :                                                              format("Output variable \"{}\" referenced multiple times (directly or via meter)",
     983           0 :                                                                     op->outVars[keyOutVarNum]->keyColonNameUC));
     984             :                                 } else {
     985         319 :                                     meter->srcVarNums.push_back(keyOutVarNum);
     986         319 :                                     op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
     987             :                                 }
     988         319 :                                 break;
     989             :                             }
     990         319 :                         }
     991             :                     } // if (keyIsStar)
     992         711 :                 }     // if (meter or variable)
     993             : 
     994         379 :             } // for (fldIndex)
     995          98 :         }     // for (Loop)
     996             : 
     997         795 :         ipsc->cCurrentModuleObject = "Meter:CustomDecrement";
     998         836 :         for (int Loop = 1; Loop <= numCustomDecMeters; ++Loop) {
     999          82 :             ip->getObjectItem(state,
    1000          41 :                               ipsc->cCurrentModuleObject,
    1001             :                               Loop,
    1002          41 :                               ipsc->cAlphaArgs,
    1003             :                               NumAlpha,
    1004          41 :                               ipsc->rNumericArgs,
    1005             :                               NumNumbers,
    1006             :                               IOStat,
    1007          41 :                               ipsc->lNumericFieldBlanks,
    1008          41 :                               ipsc->lAlphaFieldBlanks,
    1009          41 :                               ipsc->cAlphaFieldNames,
    1010          41 :                               ipsc->cNumericFieldNames);
    1011             : 
    1012          41 :             ErrorObjectHeader eoh{routineName, ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)};
    1013             : 
    1014          41 :             std::string meterName = ipsc->cAlphaArgs(1);
    1015          41 :             std::string::size_type lbrackPos = index(meterName, '[');
    1016          41 :             if (lbrackPos != std::string::npos) meterName.erase(lbrackPos);
    1017          41 :             std::string meterNameUC = Util::makeUPPER(meterName);
    1018             : 
    1019             :             // Search for duplicate name
    1020          41 :             if (op->meterMap.find(meterNameUC) != op->meterMap.end()) {
    1021           0 :                 ShowSevereDuplicateName(state, eoh);
    1022           0 :                 ErrorsFound = true;
    1023           0 :                 continue;
    1024             :             }
    1025             : 
    1026             :             // Can't use resource type in AddMeter cause it will confuse it with other meters.  So, now:
    1027             :             Constant::eResource resource =
    1028          41 :                 static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, Util::makeUPPER(ipsc->cAlphaArgs(2))));
    1029          41 :             if (resource == Constant::eResource::Invalid) {
    1030           0 :                 ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(2), ipsc->cAlphaArgs(2));
    1031           0 :                 ErrorsFound = true;
    1032           0 :                 continue;
    1033             :             }
    1034             : 
    1035          41 :             bool itemsAssigned = false;
    1036             : 
    1037          41 :             std::string decMeterName = ipsc->cAlphaArgs(3);
    1038          41 :             lbrackPos = index(decMeterName, '[');
    1039          41 :             if (lbrackPos != std::string::npos) decMeterName.erase(lbrackPos);
    1040          41 :             std::string decMeterNameUC = Util::makeUPPER(decMeterName);
    1041             : 
    1042             :             // DecMeter cannot be a Meter:Custom
    1043          41 :             if (std::find(customDecMeterNames.begin(), customDecMeterNames.end(), decMeterNameUC) != customDecMeterNames.end()) {
    1044           0 :                 ShowWarningError(state,
    1045           0 :                                  format(R"(Meter:CustomDec="{}", contains a reference to another Meter:CustomDecrement in field: {}="{}".)",
    1046           0 :                                         ipsc->cAlphaArgs(1),
    1047           0 :                                         ipsc->cAlphaFieldNames(3),
    1048           0 :                                         ipsc->cAlphaArgs(3)));
    1049           0 :                 ErrorsFound = true;
    1050           0 :                 continue;
    1051             :             }
    1052             : 
    1053          41 :             auto foundDecMeter = op->meterMap.find(decMeterName);
    1054          41 :             if (foundDecMeter == op->meterMap.end()) {
    1055           0 :                 ShowSevereItemNotFound(state, eoh, ipsc->cAlphaFieldNames(3), decMeterName);
    1056           0 :                 ErrorsFound = true;
    1057           0 :                 continue;
    1058             :             }
    1059             : 
    1060          41 :             int decMeterNum = foundDecMeter->second;
    1061          41 :             auto *decMeter = op->meters[decMeterNum];
    1062          41 :             assert(decMeter->type == MeterType::Normal);
    1063             : 
    1064          41 :             Constant::Units units = decMeter->units;
    1065             : 
    1066          41 :             itemsAssigned = true;
    1067             : 
    1068             :             // We essentially have to do this loop twice, once to
    1069             :             // check for errors and once to construct the meter.  The
    1070             :             // reason is that meters are cross-linked with source
    1071             :             // meters and variables and those back-links will be
    1072             :             // tricky to undo later.
    1073          41 :             bool foundBadSrc = false;
    1074             : 
    1075          82 :             for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
    1076          41 :                 if (ipsc->lAlphaFieldBlanks(fldIndex + 1)) {
    1077           0 :                     ShowSevereEmptyField(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1));
    1078           0 :                     foundBadSrc = true;
    1079           0 :                     break;
    1080             :                 }
    1081             : 
    1082          41 :                 std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
    1083          41 :                 std::string::size_type lbrackPos = index(meterOrVarNameUC, '[');
    1084          41 :                 if (lbrackPos != std::string::npos) meterOrVarNameUC.erase(lbrackPos);
    1085             : 
    1086             :                 // A custom meter cannot reference another custom meter
    1087          41 :                 if (std::find(customDecMeterNames.begin(), customDecMeterNames.end(), meterOrVarNameUC) != customDecMeterNames.end()) {
    1088           0 :                     ShowWarningError(state,
    1089           0 :                                      format(R"(Meter:Custom="{}", contains a reference to another Meter:CustomDecrement in field: {}="{}".)",
    1090           0 :                                             ipsc->cAlphaArgs(1),
    1091           0 :                                             ipsc->cAlphaFieldNames(fldIndex + 1),
    1092           0 :                                             ipsc->cAlphaArgs(fldIndex + 1)));
    1093           0 :                     foundBadSrc = true;
    1094           0 :                     break;
    1095             :                 }
    1096             : 
    1097          41 :                 if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
    1098          37 :                     int srcMeterNum = foundSrcMeter->second;
    1099          37 :                     auto *srcMeter = op->meters[srcMeterNum];
    1100          37 :                     assert(srcMeter->type == MeterType::Normal || srcMeter->type == MeterType::Custom);
    1101             : 
    1102             :                     // If it's the first meter, it gets to set the units
    1103          37 :                     if (units == Constant::Units::Invalid) {
    1104           0 :                         units = srcMeter->units;
    1105           0 :                         itemsAssigned = true;
    1106          37 :                     } else if (units != srcMeter->units) {
    1107           0 :                         ShowWarningCustomMessage(state,
    1108             :                                                  eoh,
    1109           0 :                                                  format(R"(Meter:Custom="{}", differing units in {}="{}".)",
    1110           0 :                                                         ipsc->cAlphaArgs(1),
    1111           0 :                                                         ipsc->cAlphaFieldNames(fldIndex + 1),
    1112             :                                                         meterOrVarNameUC));
    1113           0 :                         ShowContinueError(state,
    1114           0 :                                           format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
    1115           0 :                                                  Constant::unitNames[(int)units],
    1116           0 :                                                  Constant::unitNames[(int)srcMeter->units]));
    1117           0 :                         foundBadSrc = true;
    1118           0 :                         break;
    1119             :                     }
    1120             : 
    1121             :                     // It's a variable
    1122           4 :                 } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
    1123           4 :                     int srcDDVarNum = foundSrcDDVar->second;
    1124           4 :                     auto *srcDDVar = op->ddOutVars[srcDDVarNum];
    1125             : 
    1126             :                     // Has to be a summed variable
    1127           4 :                     if (srcDDVar->storeType != StoreType::Sum) {
    1128           0 :                         ShowWarningCustomMessage(state,
    1129             :                                                  eoh,
    1130           0 :                                                  format(R"(Meter:Custom="{}", variable not summed variable {}="{}".)",
    1131           0 :                                                         ipsc->cAlphaArgs(1),
    1132           0 :                                                         ipsc->cAlphaFieldNames(fldIndex + 1),
    1133             :                                                         meterOrVarNameUC));
    1134           0 :                         ShowContinueError(state,
    1135           0 :                                           format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
    1136           0 :                                                  Constant::unitNames[(int)units],
    1137           0 :                                                  Constant::unitNames[(int)srcDDVar->units]));
    1138           0 :                         foundBadSrc = true;
    1139           0 :                         break;
    1140             :                     }
    1141             : 
    1142             :                     // If it's the first variable, it gets to set the units
    1143           4 :                     if (units == Constant::Units::Invalid) {
    1144           0 :                         units = srcDDVar->units;
    1145             :                         // Otherwise it has to match the existing units
    1146           4 :                     } else if (units != srcDDVar->units) {
    1147           0 :                         ShowWarningCustomMessage(
    1148           0 :                             state, eoh, format("differing units in {}=\"{}\".", ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC));
    1149           0 :                         ShowContinueError(state,
    1150           0 :                                           format("...will not be shown with the Meter results; units for meter={}, units for this variable={}.",
    1151           0 :                                                  Constant::unitNames[(int)units],
    1152           0 :                                                  Constant::unitNames[(int)srcDDVar->units]));
    1153           0 :                         foundBadSrc = true;
    1154           0 :                         break;
    1155             :                     }
    1156             : 
    1157           4 :                     bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
    1158             :                     // Have already checked for mismatching units between meter and source variable and assigned units
    1159           4 :                     if (KeyIsStar) {
    1160           0 :                         if (srcDDVar->keyOutVarNums.empty()) {
    1161           0 :                             ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
    1162           0 :                             foundBadSrc = true;
    1163           0 :                             break;
    1164             :                         }
    1165             : 
    1166           0 :                         itemsAssigned = true;
    1167             :                     } else { // Key is not "*"
    1168           4 :                         bool foundKey = false;
    1169         195 :                         for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
    1170         195 :                             if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
    1171           4 :                                 foundKey = true;
    1172           4 :                                 itemsAssigned = true;
    1173           4 :                                 break;
    1174             :                             }
    1175           4 :                         }
    1176           4 :                         if (!foundKey) {
    1177           0 :                             ShowSevereInvalidKey(state, eoh, ipsc->cAlphaFieldNames(fldIndex + 1), meterOrVarNameUC);
    1178           0 :                             foundBadSrc = true;
    1179           0 :                             break;
    1180             :                         }
    1181             :                     } // if (keyIsStar)
    1182             : 
    1183             :                     // Not a meter or a variable
    1184             :                 } else {
    1185             :                     // Cannot use ShowWarningItemNotFound because this string appears in a unit test
    1186           0 :                     ShowWarningError(state,
    1187           0 :                                      format(R"(Meter:Custom="{}", invalid {}="{}".)",
    1188           0 :                                             ipsc->cAlphaArgs(1),
    1189           0 :                                             ipsc->cAlphaFieldNames(fldIndex + 1),
    1190           0 :                                             ipsc->cAlphaArgs(fldIndex + 1)));
    1191           0 :                     ShowContinueError(state, "...will not be shown with the Meter results.");
    1192           0 :                     foundBadSrc = true;
    1193           0 :                     break;
    1194          45 :                 }
    1195             : 
    1196          41 :             } // for (fldIndex)
    1197             : 
    1198             :             // Somehow, this meter is not linked to any variables either directly or via another meter
    1199          41 :             if (!itemsAssigned) {
    1200           0 :                 ShowWarningError(state, format("Meter:Custom=\"{}\", no items assigned ", ipsc->cAlphaArgs(1)));
    1201           0 :                 ShowContinueError(
    1202             :                     state, "...will not be shown with the Meter results. This may be caused by a Meter:Custom be assigned to another Meter:Custom.");
    1203           0 :                 continue;
    1204             :             }
    1205             : 
    1206             :             // One of the sources is bad
    1207          41 :             if (foundBadSrc) {
    1208           0 :                 continue;
    1209             :             }
    1210             : 
    1211          41 :             auto *meter = new Meter(meterName);
    1212          41 :             meter->type = MeterType::CustomDec;
    1213          41 :             meter->resource = resource;
    1214          41 :             meter->units = units;
    1215          41 :             bool errFlag = false;
    1216          41 :             meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, meter->units, errFlag);
    1217          41 :             if (errFlag) {
    1218           0 :                 ShowContinueError(state, format("..on {}=\"{}\".", ipsc->cCurrentModuleObject, ipsc->cAlphaArgs(1)));
    1219           0 :                 ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
    1220             :             }
    1221             : 
    1222          41 :             meter->decMeterNum = decMeterNum;
    1223             : 
    1224             :             // This meter is good
    1225          41 :             int meterNum = op->meters.size();
    1226          41 :             op->meters.push_back(meter);
    1227          41 :             op->meterMap.insert_or_assign(meterNameUC, meterNum);
    1228             : 
    1229          41 :             for (ReportFreq reportFreq :
    1230         328 :                  {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
    1231         246 :                 meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
    1232             :             }
    1233             : 
    1234          41 :             for (ReportFreq reportFreq :
    1235         328 :                  {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
    1236         246 :                 meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
    1237             :             }
    1238             : 
    1239             :             //  Links meter to dec meter and its output variable and vice versa
    1240          41 :             meter->srcMeterNums.push_back(meter->decMeterNum);
    1241          41 :             decMeter->dstMeterNums.push_back(meterNum);
    1242             : 
    1243             :             // Not linking decMeter vars to this meter and vice versa
    1244             :             // for (int srcVarNum : decMeter->srcVarNums) {
    1245             :             //    if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) != meter->srcVarNums.end()) continue; // Already linked
    1246             :             //    meter->srcVarNums.push_back(srcVarNum);
    1247             :             //    op->outVars[srcVarNum]->meterNums.push_back(meterNum);
    1248             :             // }
    1249             : 
    1250             :             // Do the loop again, this time without error checking
    1251          82 :             for (int fldIndex = 4; fldIndex <= NumAlpha; fldIndex += 2) {
    1252             :                 // No need to check for empty fields
    1253          41 :                 std::string meterOrVarNameUC = Util::makeUPPER(ipsc->cAlphaArgs(fldIndex + 1));
    1254          41 :                 std::string::size_type lbrackPos = index(meterOrVarNameUC, '[');
    1255          41 :                 if (lbrackPos != std::string::npos) meterOrVarNameUC.erase(lbrackPos);
    1256             : 
    1257             :                 // No need to check for custom source meters
    1258          41 :                 if (auto foundSrcMeter = op->meterMap.find(meterOrVarNameUC); foundSrcMeter != op->meterMap.end()) {
    1259          37 :                     int srcMeterNum = foundSrcMeter->second;
    1260          37 :                     auto *srcMeter = op->meters[srcMeterNum];
    1261          37 :                     assert(srcMeter->type == MeterType::Normal || srcMeter->type == MeterType::Custom);
    1262             : 
    1263             :                     // No need to check for units
    1264             :                     // No need to check for duplicates
    1265             : 
    1266             :                     // Check for duplicates
    1267          37 :                     if (std::find(meter->srcMeterNums.begin(), meter->srcMeterNums.end(), srcMeterNum) != meter->srcMeterNums.end()) {
    1268           0 :                         ShowWarningCustomMessage(state,
    1269             :                                                  eoh,
    1270           0 :                                                  format("{}=\"{}\" referenced multiple times, only first instance will be used",
    1271           0 :                                                         ipsc->cAlphaFieldNames(fldIndex + 1),
    1272             :                                                         meterOrVarNameUC));
    1273           0 :                         continue;
    1274             :                     }
    1275             : 
    1276             :                     // Link meter to src meter and var and vice versa
    1277          37 :                     meter->srcMeterNums.push_back(srcMeterNum);
    1278          37 :                     srcMeter->dstMeterNums.push_back(meterNum);
    1279             : 
    1280         224 :                     for (int srcVarNum : srcMeter->srcVarNums) {
    1281         187 :                         if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), srcVarNum) == meter->srcVarNums.end()) {
    1282         187 :                             meter->srcVarNums.push_back(srcVarNum);
    1283         187 :                             op->outVars[srcVarNum]->meterNums.push_back(meterNum);
    1284             :                         }
    1285          37 :                     }
    1286             : 
    1287             :                     // It's a variable
    1288           4 :                 } else if (auto foundSrcDDVar = op->ddOutVarMap.find(meterOrVarNameUC); foundSrcDDVar != op->ddOutVarMap.end()) {
    1289           4 :                     int srcDDVarNum = foundSrcDDVar->second;
    1290           4 :                     auto *srcDDVar = op->ddOutVars[srcDDVarNum];
    1291             : 
    1292             :                     // No need to check for a summed variable
    1293             :                     // No need to check for units match or to assign units
    1294             : 
    1295           4 :                     bool KeyIsStar = (ipsc->cAlphaArgs(fldIndex) == "*" || ipsc->lAlphaFieldBlanks(fldIndex));
    1296             :                     // Have already checked for mismatching units between meter and source variable and assigned units
    1297           4 :                     if (KeyIsStar) {
    1298             :                         // No need to check for empty keys
    1299           0 :                         for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
    1300           0 :                             if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
    1301           0 :                                 ShowWarningCustomMessage(state,
    1302             :                                                          eoh,
    1303           0 :                                                          format("Output variable \"{}\" referenced multiple times (directly or via meter)",
    1304           0 :                                                                 op->outVars[keyOutVarNum]->keyColonNameUC));
    1305             : 
    1306             :                             } else {
    1307           0 :                                 meter->srcVarNums.push_back(keyOutVarNum);
    1308           0 :                                 op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
    1309             :                             }
    1310           0 :                         }
    1311             :                     } else { // Key is not "*"
    1312         195 :                         for (int keyOutVarNum : srcDDVar->keyOutVarNums) {
    1313         195 :                             if (op->outVars[keyOutVarNum]->keyUC == ipsc->cAlphaArgs(fldIndex)) {
    1314           4 :                                 if (std::find(meter->srcVarNums.begin(), meter->srcVarNums.end(), keyOutVarNum) != meter->srcVarNums.end()) {
    1315           0 :                                     ShowWarningCustomMessage(state,
    1316             :                                                              eoh,
    1317           0 :                                                              format("Output variable \"{}\" referenced multiple times (directly or via meter)",
    1318           0 :                                                                     op->outVars[keyOutVarNum]->keyColonNameUC));
    1319             :                                 } else {
    1320           4 :                                     meter->srcVarNums.push_back(keyOutVarNum);
    1321           4 :                                     op->outVars[keyOutVarNum]->meterNums.push_back(meterNum);
    1322             :                                 }
    1323           4 :                                 break;
    1324             :                             }
    1325           4 :                         }
    1326             :                     } // if (keyIsStar)
    1327          45 :                 }     // if (meter or variable)
    1328             : 
    1329          41 :             } // for (fldIndex)
    1330          41 :         }
    1331             : 
    1332         795 :         if (BigErrorsFound) ErrorsFound = true;
    1333         795 :     }
    1334             : 
    1335      297245 :     int AddMeter(EnergyPlusData &state,
    1336             :                  std::string const &Name,          // Name for the meter
    1337             :                  Constant::Units const units,      // Units for the meter
    1338             :                  Constant::eResource resource,     // ResourceType for the meter
    1339             :                  EndUseCat endUseCat,              // EndUse for the meter
    1340             :                  std::string_view const EndUseSub, // EndUse subcategory for the meter
    1341             :                  Group group,
    1342             :                  int outVarNum) // Variable index
    1343             :     {
    1344             : 
    1345             :         // SUBROUTINE INFORMATION:
    1346             :         //       AUTHOR         Linda Lawrie
    1347             :         //       DATE WRITTEN   January 2001
    1348             : 
    1349             :         // PURPOSE OF THIS SUBROUTINE:
    1350             :         // This subroutine adds a meter to the current definition set of meters.  If the maximum has
    1351             :         // already been reached, a reallocation procedure begins.  This action needs to be done at the
    1352             :         // start of the simulation, primarily before any output is stored.
    1353             : 
    1354             :         // Make sure this isn't already in the list of meter names
    1355      297245 :         auto &op = state.dataOutputProcessor;
    1356             : 
    1357      297245 :         int meterNum = -1;
    1358      297245 :         Meter *meter = nullptr;
    1359             : 
    1360      297245 :         std::string nameUC = Util::makeUPPER(Name);
    1361             : 
    1362      297245 :         if (auto found = op->meterMap.find(nameUC); found != op->meterMap.end()) {
    1363      200064 :             meterNum = found->second;
    1364      200064 :             meter = op->meters[meterNum];
    1365             :         } else {
    1366             : 
    1367       97181 :             meterNum = op->meters.size();
    1368       97181 :             meter = new Meter(Name);
    1369       97181 :             op->meters.push_back(meter);
    1370       97181 :             op->meterMap.insert_or_assign(nameUC, meterNum);
    1371             : 
    1372       97181 :             meter->type = MeterType::Normal;
    1373       97181 :             meter->resource = resource;
    1374       97181 :             meter->endUseCat = endUseCat;
    1375       97181 :             meter->EndUseSub = EndUseSub;
    1376       97181 :             meter->group = group;
    1377       97181 :             meter->units = units;
    1378       97181 :             meter->CurTSValue = 0.0;
    1379             : 
    1380       97181 :             for (ReportFreq reportFreq :
    1381      777448 :                  {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
    1382      583086 :                 meter->periods[(int)reportFreq].RptNum = ++op->ReportNumberCounter;
    1383             :             }
    1384             : 
    1385       97181 :             for (ReportFreq reportFreq :
    1386      777448 :                  {ReportFreq::TimeStep, ReportFreq::Hour, ReportFreq::Day, ReportFreq::Month, ReportFreq::Year, ReportFreq::Simulation}) {
    1387      583086 :                 meter->periods[(int)reportFreq].accRptNum = ++op->ReportNumberCounter;
    1388             :             }
    1389             : 
    1390       97181 :             if (meter->resource != Constant::eResource::Invalid) {
    1391       97181 :                 bool errFlag = false;
    1392       97181 :                 meter->RT_forIPUnits = GetResourceIPUnits(state, meter->resource, units, errFlag);
    1393       97181 :                 if (errFlag) {
    1394           0 :                     ShowContinueError(state, format("..on Meter=\"{}\".", Name));
    1395           0 :                     ShowContinueError(state, "..requests for IP units from this meter will be ignored.");
    1396             :                 }
    1397             :             }
    1398      297245 :         }
    1399             : 
    1400             :         // outVarNum == -1 is only true in unit tests
    1401      297245 :         if (outVarNum != -1) {
    1402      297245 :             OutVar *var = op->outVars[outVarNum];
    1403      297245 :             var->meterNums.push_back(meterNum);
    1404      297245 :             meter->srcVarNums.push_back(outVarNum);
    1405             :         }
    1406             : 
    1407      297245 :         return meterNum;
    1408      297245 :     }
    1409             : 
    1410       58870 :     void AttachMeters(EnergyPlusData &state,
    1411             :                       Constant::Units const units,      // Units for this meter
    1412             :                       Constant::eResource resource,     // Electricity, Gas, etc.
    1413             :                       EndUseCat endUseCat,              // End-use category (Lights, Heating, etc.)
    1414             :                       std::string_view const EndUseSub, // End-use subcategory (user-defined, e.g., General Lights, Task Lights, etc.)
    1415             :                       Group group,                      // Group key (Facility, Zone, Building, etc.)
    1416             :                       std::string const &ZoneName,      // Zone key only applicable for Building group
    1417             :                       std::string const &SpaceType,     // Space Type key only applicable for Building group
    1418             :                       int const outVarNum               // Number of this report variable
    1419             :     )
    1420             :     {
    1421             : 
    1422             :         // SUBROUTINE INFORMATION:
    1423             :         //       AUTHOR         Linda Lawrie
    1424             :         //       DATE WRITTEN   January 2001
    1425             : 
    1426             :         // PURPOSE OF THIS SUBROUTINE:
    1427             :         // This subroutine determines which meters this variable will be on (if any),
    1428             :         // creates those meters and links the variable to them (and vice versa).
    1429             : 
    1430       58870 :         std::string_view resourceName = Constant::eResourceNames[(int)resource];
    1431             : 
    1432       58870 :         std::string endUseSub = standardizeEndUseSub(endUseCat, EndUseSub);
    1433             : 
    1434       58870 :         if (!endUseSub.empty()) addEndUseSubcategory(state, endUseCat, endUseSub);
    1435             : 
    1436       58870 :         if (!SpaceType.empty()) addEndUseSpaceType(state, endUseCat, SpaceType);
    1437             : 
    1438       58870 :         std::string meterName = format("{}:Facility", resourceName);
    1439       58870 :         AddMeter(state, meterName, units, resource, EndUseCat::Invalid, "", Group::Invalid, outVarNum);
    1440             : 
    1441       58870 :         if (group != Group::Invalid) {
    1442       52437 :             std::string groupMeterName = format("{}:{}", resourceName, groupNames[(int)group]);
    1443       52437 :             AddMeter(state, groupMeterName, units, resource, EndUseCat::Invalid, "", group, outVarNum);
    1444             : 
    1445       52437 :             if (group == Group::Building) {
    1446       20168 :                 if (!ZoneName.empty()) {
    1447       19830 :                     std::string zoneMeterName = format("{}:Zone:{}", resourceName, ZoneName);
    1448       19830 :                     AddMeter(state, zoneMeterName, units, resource, EndUseCat::Invalid, "", Group::Zone, outVarNum);
    1449       19830 :                 }
    1450       20168 :                 if (!SpaceType.empty()) {
    1451        8853 :                     std::string spaceMeterName = format("{}:SpaceType:{}", resourceName, SpaceType);
    1452        8853 :                     AddMeter(state, spaceMeterName, units, resource, EndUseCat::Invalid, "", Group::SpaceType, outVarNum);
    1453        8853 :                 }
    1454             :             } // if (Group == "Building")
    1455       52437 :         }
    1456             : 
    1457             :         //!! Following if we do EndUse by ResourceType
    1458       58870 :         if (endUseCat != EndUseCat::Invalid) {
    1459       58837 :             std::string_view endUseCatName = endUseCatNames[(int)endUseCat];
    1460       58837 :             std::string enduseMeterName = format("{}:{}", endUseCatName, resourceName);
    1461       58837 :             AddMeter(state, enduseMeterName, units, resource, endUseCat, "", Group::Invalid, outVarNum);
    1462             : 
    1463       58837 :             if (group == Group::Building) { // Match to Zone and Space
    1464       20162 :                 if (!ZoneName.empty()) {
    1465       19830 :                     std::string enduseZoneMeterName = format("{}:{}:Zone:{}", endUseCatName, resourceName, ZoneName);
    1466       19830 :                     AddMeter(state, enduseZoneMeterName, units, resource, endUseCat, "", Group::Zone, outVarNum);
    1467       19830 :                 }
    1468       20162 :                 if (!SpaceType.empty()) {
    1469        8853 :                     std::string enduseSpaceMeterName = format("{}:{}:SpaceType:{}", endUseCatName, resourceName, SpaceType);
    1470        8853 :                     AddMeter(state, enduseSpaceMeterName, units, resource, endUseCat, "", Group::SpaceType, outVarNum);
    1471        8853 :                 }
    1472             :             }
    1473             : 
    1474             :             // End-Use Subcategories
    1475       58837 :             if (!endUseSub.empty()) {
    1476       41052 :                 std::string subEnduseMeterName = format("{}:{}:{}", endUseSub, endUseCatNames[(int)endUseCat], resourceName);
    1477       41052 :                 AddMeter(state, subEnduseMeterName, units, resource, endUseCat, endUseSub, Group::Invalid, outVarNum);
    1478             : 
    1479       41052 :                 if (group == Group::Building) { // Match to Zone and Space
    1480       20162 :                     if (!ZoneName.empty()) {
    1481       19830 :                         std::string subEnduseZoneMeterName = format("{}:{}:{}:Zone:{}", endUseSub, endUseCatName, resourceName, ZoneName);
    1482       19830 :                         AddMeter(state, subEnduseZoneMeterName, units, resource, endUseCat, endUseSub, Group::Zone, outVarNum);
    1483       19830 :                     }
    1484       20162 :                     if (!SpaceType.empty()) {
    1485        8853 :                         std::string subEnduseSpaceMeterName = format("{}:{}:{}:SpaceType:{}", endUseSub, endUseCatName, resourceName, SpaceType);
    1486        8853 :                         AddMeter(state, subEnduseSpaceMeterName, units, resource, endUseCat, endUseSub, Group::SpaceType, outVarNum);
    1487        8853 :                     }
    1488             :                 } // if (sovGroup == Building)
    1489       41052 :             }     // if (!endUseSub.empty())
    1490       58837 :         }         // if (sovEndUseCat != Invalid)
    1491       58870 :     }             // AttachMeters()
    1492             : 
    1493       58870 :     std::string standardizeEndUseSub(EndUseCat endUseCat, std::string_view endUseSubName)
    1494             :     {
    1495       58870 :         if (!endUseSubName.empty()) {
    1496       19203 :             return std::string(endUseSubName);
    1497       39667 :         } else if (endUseCat == EndUseCat::Invalid) {
    1498          33 :             return "";
    1499       39634 :         } else if (endUseCat2endUse[(int)endUseCat] != Constant::EndUse::Invalid) {
    1500       21849 :             return "General";
    1501             :         } else {
    1502       17785 :             return "";
    1503             :         }
    1504             :     }
    1505             : 
    1506       97318 :     OutputProcessor::RT_IPUnits GetResourceIPUnits(EnergyPlusData &state,
    1507             :                                                    Constant::eResource resource, // Resource Type
    1508             :                                                    Constant::Units const units,  // Meter units
    1509             :                                                    bool &ErrorsFound             // true if errors found during subroutine
    1510             :     )
    1511             :     {
    1512             : 
    1513             :         // SUBROUTINE INFORMATION:
    1514             :         //       AUTHOR         Linda Lawrie
    1515             :         //       DATE WRITTEN   January 2012
    1516             :         //       MODIFIED       September 2012; made into subroutine
    1517             :         //       RE-ENGINEERED  na
    1518             : 
    1519             :         // PURPOSE OF THIS SUBROUTINE:
    1520             :         // In order to set up tabular reports for IP units, need to search on same strings
    1521             :         // that tabular reports does for IP conversion.
    1522             : 
    1523             :         // REFERENCES:
    1524             :         // OutputReportTabular looks for:
    1525             :         // CONSUMP - not used in meters
    1526             :         // ELEC - Electricity (kWH)
    1527             :         // GAS - Gas (therm)
    1528             :         // COOL - Cooling (ton)
    1529             :         // and we need to add WATER (for m3/gal, etc)
    1530             : 
    1531             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1532             :         RT_IPUnits IPUnits;
    1533             : 
    1534             :         // Commented this out to avoid accidentally clearing an error condition by calling this function
    1535             :         // ErrorsFound = false;
    1536             : 
    1537       97318 :         switch (resource) {
    1538       45188 :         case Constant::eResource::Electricity:
    1539             :         case Constant::eResource::ElectricityProduced:
    1540             :         case Constant::eResource::ElectricityPurchased:
    1541             :         case Constant::eResource::ElectricitySurplusSold:
    1542             :         case Constant::eResource::ElectricityNet: {
    1543       45188 :             IPUnits = RT_IPUnits::Electricity;
    1544       45188 :         } break;
    1545        2770 :         case Constant::eResource::NaturalGas: {
    1546        2770 :             IPUnits = RT_IPUnits::Gas;
    1547        2770 :         } break;
    1548        3522 :         case Constant::eResource::Water:
    1549             :         case Constant::eResource::MainsWater:
    1550             :         case Constant::eResource::RainWater:
    1551             :         case Constant::eResource::WellWater:
    1552             :         case Constant::eResource::OnSiteWater: {
    1553        3522 :             IPUnits = RT_IPUnits::Water;
    1554        3522 :         } break;
    1555        2015 :         case Constant::eResource::DistrictCooling:
    1556             :         case Constant::eResource::PlantLoopCoolingDemand: {
    1557        2015 :             IPUnits = RT_IPUnits::Cooling;
    1558        2015 :         } break;
    1559       43823 :         default: {
    1560       43823 :             if (units == Constant::Units::m3) {
    1561         301 :                 IPUnits = RT_IPUnits::OtherM3;
    1562       43522 :             } else if (units == Constant::Units::kg) {
    1563        5804 :                 IPUnits = RT_IPUnits::OtherKG;
    1564       37718 :             } else if (units == Constant::Units::L) {
    1565         301 :                 IPUnits = RT_IPUnits::OtherL;
    1566             :             } else {
    1567       37417 :                 IPUnits = RT_IPUnits::OtherJ;
    1568             :             }
    1569       43823 :         } break;
    1570             :         } // switch
    1571             : 
    1572             :         //  write(outputfiledebug,*) 'resourcetype=',TRIM(resourcetype)
    1573             :         //  write(outputfiledebug,*) 'ipunits type=',CodeForIPUnits
    1574       97318 :         if (units != Constant::Units::kg && units != Constant::Units::J && units != Constant::Units::m3 && units != Constant::Units::L) {
    1575           2 :             ShowWarningMessage(
    1576           2 :                 state, format("DetermineMeterIPUnits: Meter units not recognized for IP Units conversion=[{}].", Constant::unitNames[(int)units]));
    1577           1 :             ErrorsFound = true;
    1578             :         }
    1579       97318 :         return IPUnits;
    1580             :     }
    1581             : 
    1582      485664 :     void UpdateMeters(EnergyPlusData &state, int const TimeStamp) // Current TimeStamp (for max/min)
    1583             :     {
    1584             : 
    1585             :         // SUBROUTINE INFORMATION:
    1586             :         //       AUTHOR         Linda Lawrie
    1587             :         //       DATE WRITTEN   April 2001
    1588             : 
    1589             :         // PURPOSE OF THIS SUBROUTINE:
    1590             :         // This subroutine updates the meters with the current time step value
    1591             :         // for each meter.  Also, sets min/max values for hourly...run period reporting.
    1592             : 
    1593      485664 :         if (state.dataGlobal->WarmupFlag) {
    1594        3360 :             return;
    1595             :         }
    1596             : 
    1597      482304 :         auto &op = state.dataOutputProcessor;
    1598             : 
    1599      482304 :         if (op->meters.size() == 0 || op->meterValues.size() == 0) {
    1600           0 :             return;
    1601             :         }
    1602             : 
    1603    49419408 :         for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
    1604    48937104 :             auto *meter = op->meters[iMeter];
    1605    48937104 :             if (meter->type != MeterType::CustomDec && meter->type != MeterType::CustomDiff) {
    1606    48859584 :                 meter->periods[(int)ReportFreq::TimeStep].Value += op->meterValues[iMeter];
    1607             :                 // Is this correct? What is going on here?
    1608             :             } else {
    1609       77520 :                 meter->periods[(int)ReportFreq::TimeStep].Value += op->meterValues[iMeter];
    1610             :                 //                meter->periods[(int)ReportFreq::TimeStep].Value =
    1611             :                 //        op->meters[meter->decMeterNum]->periods[(int)ReportFreq::TimeStep].Value - op->meterValues[iMeter];
    1612             :             }
    1613             : 
    1614    48937104 :             Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
    1615    48937104 :             meter->periods[(int)ReportFreq::Hour].Value += TSValue;
    1616    48937104 :             meter->periods[(int)ReportFreq::Day].Value += TSValue;
    1617    48937104 :             meter->periods[(int)ReportFreq::Month].Value += TSValue;
    1618    48937104 :             meter->periods[(int)ReportFreq::Year].Value += TSValue;
    1619    48937104 :             meter->periods[(int)ReportFreq::Simulation].Value += TSValue;
    1620    48937104 :             meter->periodFinYrSM.Value += TSValue;
    1621             :         } // for (iMeter)
    1622             : 
    1623             :         // Set Max
    1624    49419408 :         for (auto *meter : op->meters) {
    1625    48937104 :             Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
    1626    48937104 :             Real64 TSValueComp = TSValue; //  - 0.00001;
    1627             : 
    1628             :             // Todo - HRMinVal, HRMaxVal not used
    1629    48937104 :             auto &periodDY = meter->periods[(int)ReportFreq::Day];
    1630    48937104 :             if (TSValueComp <= periodDY.MaxVal) continue;
    1631     1780767 :             periodDY.MaxVal = TSValue;
    1632     1780767 :             periodDY.MaxValDate = TimeStamp;
    1633             : 
    1634     1780767 :             auto &periodMN = meter->periods[(int)ReportFreq::Month];
    1635     1780767 :             if (TSValueComp <= periodMN.MaxVal) continue;
    1636     1780631 :             periodMN.MaxVal = TSValue;
    1637     1780631 :             periodMN.MaxValDate = TimeStamp;
    1638             : 
    1639     1780631 :             auto &periodYR = meter->periods[(int)ReportFreq::Year];
    1640     1780631 :             if (TSValueComp > periodYR.MaxVal) {
    1641     1779896 :                 periodYR.MaxVal = TSValue;
    1642     1779896 :                 periodYR.MaxValDate = TimeStamp;
    1643             :             }
    1644             : 
    1645     1780631 :             auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
    1646     1780631 :             if (TSValueComp > periodSM.MaxVal) {
    1647     1780484 :                 periodSM.MaxVal = TSValue;
    1648     1780484 :                 periodSM.MaxValDate = TimeStamp;
    1649             :             }
    1650             : 
    1651     1780631 :             if (TSValueComp > meter->periodFinYrSM.MaxVal) {
    1652     1779896 :                 meter->periodFinYrSM.MaxVal = TSValue;
    1653     1779896 :                 meter->periodFinYrSM.MaxValDate = TimeStamp;
    1654             :             }
    1655      482304 :         } // for (meter)
    1656             : 
    1657             :         // Set Min
    1658    49419408 :         for (auto *meter : op->meters) {
    1659    48937104 :             Real64 TSValue = meter->periods[(int)ReportFreq::TimeStep].Value;
    1660    48937104 :             Real64 TSValueComp = TSValue; // + 0.00001;
    1661             : 
    1662    48937104 :             auto &periodDY = meter->periods[(int)ReportFreq::Day];
    1663    48937104 :             if (TSValueComp >= periodDY.MinVal) continue;
    1664             : 
    1665      671141 :             periodDY.MinVal = TSValue;
    1666      671141 :             periodDY.MinValDate = TimeStamp;
    1667             : 
    1668      671141 :             auto &periodMN = meter->periods[(int)ReportFreq::Month];
    1669      671141 :             if (TSValueComp >= periodMN.MinVal) continue;
    1670             : 
    1671      671105 :             periodMN.MinVal = TSValue;
    1672      671105 :             periodMN.MinValDate = TimeStamp;
    1673             : 
    1674      671105 :             auto &periodYR = meter->periods[(int)ReportFreq::Year];
    1675      671105 :             if (TSValueComp < periodYR.MinVal) {
    1676      670986 :                 periodYR.MinVal = TSValue;
    1677      670986 :                 periodYR.MinValDate = TimeStamp;
    1678             :             }
    1679             : 
    1680      671105 :             auto &periodSM = meter->periods[(int)ReportFreq::Simulation];
    1681      671105 :             if (TSValueComp < periodSM.MinVal) {
    1682      671088 :                 periodSM.MinVal = TSValue;
    1683      671088 :                 periodSM.MinValDate = TimeStamp;
    1684             :             }
    1685             : 
    1686      671105 :             if (TSValueComp < meter->periodFinYrSM.MinVal) {
    1687      670986 :                 meter->periodFinYrSM.MinVal = TSValue;
    1688      670986 :                 meter->periodFinYrSM.MinValDate = TimeStamp;
    1689             :             }
    1690      482304 :         } // for (meter)
    1691             : 
    1692    49419408 :         for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
    1693    48937104 :             op->meterValues[iMeter] = 0.0; // Ready for next update
    1694             :         }
    1695             :     } // UpdateMeters()
    1696             : 
    1697           0 :     void ResetAccumulationWhenWarmupComplete(EnergyPlusData &state)
    1698             :     {
    1699             :         // SUBROUTINE INFORMATION:
    1700             :         //       AUTHOR         Jason Glazer
    1701             :         //       DATE WRITTEN   June 2015
    1702             : 
    1703             :         // PURPOSE OF THIS SUBROUTINE:
    1704             :         // Resets the accumulating meter values. Needed after warmup period is over to
    1705             :         // reset the totals on meters so that they are not accumulated over the warmup period
    1706             : 
    1707           0 :         auto &op = state.dataOutputProcessor;
    1708             : 
    1709           0 :         for (auto *meter : op->meters) {
    1710           0 :             for (int iPeriod = (int)ReportFreq::Hour; iPeriod < (int)ReportFreq::Num; ++iPeriod) {
    1711           0 :                 meter->periods[iPeriod].resetVals();
    1712             :             }
    1713           0 :             meter->periodFinYrSM.resetVals();
    1714           0 :         }
    1715             : 
    1716           0 :         for (auto *var : op->outVars) {
    1717           0 :             if (var->freq == ReportFreq::Month || var->freq == ReportFreq::Year || var->freq == ReportFreq::Simulation) {
    1718           0 :                 var->StoreValue = 0.0;
    1719           0 :                 var->NumStored = 0;
    1720             :             }
    1721           0 :         }
    1722           0 :     } // ResetAccumulationWhenWarmupComplete()
    1723             : 
    1724      485664 :     void ReportTSMeters(EnergyPlusData &state,
    1725             :                         Real64 const StartMinute, // Start Minute for TimeStep
    1726             :                         Real64 const EndMinute,   // End Minute for TimeStep
    1727             :                         bool &PrintESOTimeStamp,  // True if the ESO Time Stamp also needs to be printed
    1728             :                         bool PrintTimeStampToSQL  // Print Time Stamp to SQL file
    1729             :     )
    1730             :     {
    1731             : 
    1732             :         // SUBROUTINE INFORMATION:
    1733             :         //       AUTHOR         Linda Lawrie
    1734             :         //       DATE WRITTEN   January 2001
    1735             :         //       MODIFIED       na
    1736             :         //       RE-ENGINEERED  na
    1737             : 
    1738             :         // PURPOSE OF THIS SUBROUTINE:
    1739             :         // This subroutine reports on the meters that have been requested for
    1740             :         // reporting on each time step.
    1741             : 
    1742             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1743             :         bool PrintTimeStamp;
    1744             :         int CurDayType;
    1745      485664 :         auto &op = state.dataOutputProcessor;
    1746      485664 :         auto &rf = state.dataResultsFramework->resultsFramework;
    1747      485664 :         auto &rfMetersTS = rf->Meters[(int)ReportFreq::TimeStep];
    1748             : 
    1749      485664 :         if (!rfMetersTS.dataFrameEnabled()) {
    1750      480108 :             rf->initializeMeters(op->meters, ReportFreq::TimeStep);
    1751             :         }
    1752             : 
    1753      485664 :         PrintTimeStamp = true;
    1754    49605456 :         for (int Loop = 0; Loop < (int)op->meters.size(); ++Loop) {
    1755    49119792 :             auto *meter = op->meters[Loop];
    1756    49119792 :             auto &periodTS = meter->periods[(int)ReportFreq::TimeStep];
    1757    49119792 :             meter->CurTSValue = periodTS.Value;
    1758    49119792 :             if (!periodTS.Rpt && !periodTS.accRpt) continue;
    1759       25824 :             if (PrintTimeStamp) {
    1760        5568 :                 CurDayType = state.dataEnvrn->DayOfWeek;
    1761        5568 :                 if (state.dataEnvrn->HolidayIndex > 0) {
    1762        5568 :                     CurDayType = state.dataEnvrn->HolidayIndex;
    1763             :                 }
    1764       33408 :                 WriteTimeStampFormatData(state,
    1765        5568 :                                          state.files.mtr,
    1766             :                                          ReportFreq::EachCall,
    1767        5568 :                                          op->freqStampReportNums[(int)ReportFreq::TimeStep],
    1768        5568 :                                          state.dataGlobal->DayOfSimChr,
    1769        5568 :                                          PrintTimeStamp && PrintTimeStampToSQL,
    1770        5568 :                                          state.dataEnvrn->Month,
    1771        5568 :                                          state.dataEnvrn->DayOfMonth,
    1772        5568 :                                          state.dataGlobal->HourOfDay,
    1773             :                                          EndMinute,
    1774             :                                          StartMinute,
    1775        5568 :                                          state.dataEnvrn->DSTIndicator,
    1776        5568 :                                          ScheduleManager::dayTypeNames[CurDayType]);
    1777        5568 :                 if (rfMetersTS.dataFrameEnabled()) {
    1778        5568 :                     rfMetersTS.newRow(
    1779        5568 :                         state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, EndMinute, state.dataGlobal->CalendarYear);
    1780             :                 }
    1781        5568 :                 PrintTimeStamp = false;
    1782        5568 :                 PrintTimeStampToSQL = false;
    1783             :             }
    1784             : 
    1785       25824 :             if (PrintESOTimeStamp && !periodTS.RptFO && !periodTS.accRptFO) {
    1786           0 :                 CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
    1787           0 :                 WriteTimeStampFormatData(state,
    1788           0 :                                          state.files.eso,
    1789             :                                          ReportFreq::EachCall,
    1790           0 :                                          op->freqStampReportNums[(int)ReportFreq::TimeStep],
    1791           0 :                                          state.dataGlobal->DayOfSimChr,
    1792           0 :                                          PrintTimeStamp && PrintESOTimeStamp && PrintTimeStampToSQL,
    1793           0 :                                          state.dataEnvrn->Month,
    1794           0 :                                          state.dataEnvrn->DayOfMonth,
    1795           0 :                                          state.dataGlobal->HourOfDay,
    1796             :                                          EndMinute,
    1797             :                                          StartMinute,
    1798           0 :                                          state.dataEnvrn->DSTIndicator,
    1799           0 :                                          ScheduleManager::dayTypeNames[CurDayType]);
    1800           0 :                 PrintESOTimeStamp = false;
    1801             :             }
    1802             : 
    1803       25824 :             if (periodTS.Rpt) {
    1804       25824 :                 periodTS.WriteReportData(state, ReportFreq::TimeStep);
    1805       25824 :                 rfMetersTS.pushVariableValue(periodTS.RptNum, periodTS.Value);
    1806             :             }
    1807             : 
    1808       25824 :             if (periodTS.accRpt) {
    1809           0 :                 WriteCumulativeReportMeterData(state, periodTS.accRptNum, periodTS.Value, periodTS.accRptFO);
    1810           0 :                 rfMetersTS.pushVariableValue(periodTS.accRptNum, periodTS.Value);
    1811             :             }
    1812             :         }
    1813             : 
    1814    49605456 :         for (auto *meter : op->meters) {
    1815    49119792 :             meter->periods[(int)ReportFreq::TimeStep].Value = 0.0;
    1816      485664 :         }
    1817      485664 :     } // ReportTSMeters()
    1818             : 
    1819      102041 :     void ReportMeters(EnergyPlusData &state,
    1820             :                       ReportFreq freq,
    1821             :                       bool PrintTimeStampToSQL // Print Time Stamp to SQL file
    1822             :     )
    1823             :     {
    1824             : 
    1825             :         // SUBROUTINE INFORMATION:
    1826             :         //       AUTHOR         Linda Lawrie
    1827             :         //       DATE WRITTEN   January 2001
    1828             : 
    1829             :         // PURPOSE OF THIS SUBROUTINE:
    1830             :         // This subroutine reports on the meters that have been requested for
    1831             :         // reporting on each hour.
    1832             : 
    1833             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1834             :         bool PrintTimeStamp;
    1835             :         int CurDayType;
    1836      102041 :         auto &op = state.dataOutputProcessor;
    1837      102041 :         auto &rf = state.dataResultsFramework->resultsFramework;
    1838      102041 :         auto &rfMeters = rf->Meters[(int)freq];
    1839             : 
    1840      102041 :         assert(freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
    1841             :                freq == ReportFreq::Simulation);
    1842             : 
    1843      102041 :         if (!rfMeters.dataFrameEnabled()) {
    1844       92650 :             rf->initializeMeters(op->meters, freq);
    1845             :         }
    1846             : 
    1847      102041 :         PrintTimeStamp = true;
    1848    10635510 :         for (auto *meter : op->meters) {
    1849    10533469 :             auto &period = meter->periods[(int)freq];
    1850             : 
    1851    10533469 :             if (freq == ReportFreq::Simulation) {
    1852      213032 :                 meter->periodLastSM.Value = period.Value;
    1853      213032 :                 meter->periodLastSM.MinVal = period.MinVal;
    1854      213032 :                 meter->periodLastSM.MinValDate = period.MinValDate;
    1855      213032 :                 meter->periodLastSM.MaxVal = period.MaxVal;
    1856      213032 :                 meter->periodLastSM.MaxValDate = period.MaxValDate;
    1857             :             }
    1858             : 
    1859    10533469 :             if (!period.Rpt && !period.accRpt) continue;
    1860       58047 :             if (PrintTimeStamp) {
    1861       10640 :                 CurDayType = (state.dataEnvrn->HolidayIndex > 0) ? state.dataEnvrn->HolidayIndex : state.dataEnvrn->DayOfWeek;
    1862             : 
    1863       10640 :                 switch (freq) {
    1864             : 
    1865        8232 :                 case ReportFreq::Hour: {
    1866       49392 :                     WriteTimeStampFormatData(state,
    1867        8232 :                                              state.files.mtr,
    1868             :                                              freq,
    1869        8232 :                                              op->freqStampReportNums[(int)freq],
    1870        8232 :                                              state.dataGlobal->DayOfSimChr,
    1871        8232 :                                              PrintTimeStamp && PrintTimeStampToSQL,
    1872        8232 :                                              state.dataEnvrn->Month,
    1873        8232 :                                              state.dataEnvrn->DayOfMonth,
    1874        8232 :                                              state.dataGlobal->HourOfDay,
    1875             :                                              -1, // EndMinute
    1876             :                                              -1, // StartMinute
    1877        8232 :                                              state.dataEnvrn->DSTIndicator,
    1878        8232 :                                              ScheduleManager::dayTypeNames[CurDayType]);
    1879        8232 :                 } break;
    1880             : 
    1881          48 :                 case ReportFreq::Day: {
    1882         240 :                     WriteTimeStampFormatData(state,
    1883          48 :                                              state.files.mtr,
    1884             :                                              freq,
    1885          48 :                                              op->freqStampReportNums[(int)freq],
    1886          48 :                                              state.dataGlobal->DayOfSimChr,
    1887          48 :                                              PrintTimeStamp && PrintTimeStampToSQL,
    1888          48 :                                              state.dataEnvrn->Month,
    1889          48 :                                              state.dataEnvrn->DayOfMonth,
    1890             :                                              -1, // Hour
    1891             :                                              -1, // EndMinute
    1892             :                                              -1, // StartMinute
    1893          48 :                                              state.dataEnvrn->DSTIndicator,
    1894          48 :                                              ScheduleManager::dayTypeNames[CurDayType]);
    1895          48 :                 } break;
    1896             : 
    1897        1275 :                 case ReportFreq::Month: {
    1898        1275 :                     WriteTimeStampFormatData(state,
    1899        1275 :                                              state.files.mtr,
    1900             :                                              freq,
    1901        1275 :                                              op->freqStampReportNums[(int)freq],
    1902        1275 :                                              state.dataGlobal->DayOfSimChr,
    1903        1275 :                                              PrintTimeStamp && PrintTimeStampToSQL,
    1904        1275 :                                              state.dataEnvrn->Month);
    1905        1275 :                 } break;
    1906             : 
    1907           0 :                 case ReportFreq::Year: {
    1908           0 :                     WriteYearlyTimeStamp(state,
    1909           0 :                                          state.files.mtr,
    1910           0 :                                          op->freqStampReportNums[(int)freq],
    1911           0 :                                          state.dataGlobal->CalendarYearChr,
    1912           0 :                                          PrintTimeStamp && PrintTimeStampToSQL);
    1913           0 :                 } break;
    1914             : 
    1915        1085 :                 case ReportFreq::Simulation: {
    1916        1085 :                     WriteTimeStampFormatData(state,
    1917        1085 :                                              state.files.mtr,
    1918             :                                              freq,
    1919        1085 :                                              op->freqStampReportNums[(int)freq],
    1920        1085 :                                              state.dataGlobal->DayOfSimChr,
    1921        1085 :                                              PrintTimeStamp && PrintTimeStampToSQL);
    1922        1085 :                 } break;
    1923             : 
    1924           0 :                 default: {
    1925           0 :                 } break;
    1926             :                 } // switch (freq)
    1927             : 
    1928       10640 :                 if (rfMeters.dataFrameEnabled()) {
    1929       10640 :                     rfMeters.newRow(
    1930       10640 :                         state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
    1931             :                 }
    1932       10640 :                 PrintTimeStamp = false;
    1933       10640 :                 PrintTimeStampToSQL = false;
    1934             :             }
    1935             : 
    1936       58047 :             if (period.Rpt) {
    1937       58047 :                 period.WriteReportData(state, freq);
    1938       58047 :                 rfMeters.pushVariableValue(period.RptNum, period.Value);
    1939       58047 :                 period.Value = 0.0;
    1940             : 
    1941       58047 :                 if (freq != ReportFreq::Hour) {
    1942       18087 :                     period.MinVal = MinSetValue;
    1943       18087 :                     period.MaxVal = MaxSetValue;
    1944             :                 }
    1945             :             }
    1946             : 
    1947       58047 :             if (period.accRpt) {
    1948          10 :                 WriteCumulativeReportMeterData(state, period.accRptNum, meter->periods[(int)ReportFreq::Simulation].Value, period.accRptFO);
    1949          10 :                 rfMeters.pushVariableValue(period.accRptNum, meter->periods[(int)ReportFreq::Simulation].Value);
    1950             :             }
    1951      102041 :         } // for (meter)
    1952      102041 :     }     // ReportMeters()
    1953             : 
    1954         794 :     void ReportForTabularReports(EnergyPlusData &state)
    1955             :     {
    1956             : 
    1957             :         // SUBROUTINE INFORMATION:
    1958             :         //       AUTHOR         Linda Lawrie
    1959             :         //       DATE WRITTEN   August 2013
    1960             :         //       MODIFIED       na
    1961             :         //       RE-ENGINEERED  na
    1962             : 
    1963             :         // PURPOSE OF THIS SUBROUTINE:
    1964             :         // This subroutine is called after all the simulation is done and before
    1965             :         // tabular reports in order to reduce the number of calls to the predefined routine
    1966             :         // for SM (Simulation period) meters, the value of the last calculation is stored
    1967             :         // in the data structure.
    1968             : 
    1969             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    1970         794 :         auto &op = state.dataOutputProcessor;
    1971             : 
    1972       97811 :         for (auto *meter : op->meters) {
    1973       97017 :             auto &period = meter->periodFinYrSM;
    1974             : 
    1975       97017 :             switch (meter->RT_forIPUnits) {
    1976       45080 :             case RT_IPUnits::Electricity: {
    1977       90160 :                 OutputReportPredefined::PreDefTableEntry(
    1978       45080 :                     state, state.dataOutRptPredefined->pdchEMelecannual, meter->Name, period.Value * Constant::convertJtoGJ);
    1979       90160 :                 OutputReportPredefined::PreDefTableEntry(
    1980       45080 :                     state, state.dataOutRptPredefined->pdchEMelecminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
    1981       90160 :                 OutputReportPredefined::PreDefTableEntry(
    1982      135240 :                     state, state.dataOutRptPredefined->pdchEMelecminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    1983       90160 :                 OutputReportPredefined::PreDefTableEntry(
    1984       45080 :                     state, state.dataOutRptPredefined->pdchEMelecmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
    1985       90160 :                 OutputReportPredefined::PreDefTableEntry(
    1986      135240 :                     state, state.dataOutRptPredefined->pdchEMelecmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    1987       45080 :             } break;
    1988             : 
    1989        2763 :             case RT_IPUnits::Gas: {
    1990        5526 :                 OutputReportPredefined::PreDefTableEntry(
    1991        2763 :                     state, state.dataOutRptPredefined->pdchEMgasannual, meter->Name, period.Value * Constant::convertJtoGJ);
    1992        5526 :                 OutputReportPredefined::PreDefTableEntry(
    1993        2763 :                     state, state.dataOutRptPredefined->pdchEMgasminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
    1994        5526 :                 OutputReportPredefined::PreDefTableEntry(
    1995        8289 :                     state, state.dataOutRptPredefined->pdchEMgasminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    1996        5526 :                 OutputReportPredefined::PreDefTableEntry(
    1997        2763 :                     state, state.dataOutRptPredefined->pdchEMgasmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
    1998        5526 :                 OutputReportPredefined::PreDefTableEntry(
    1999        8289 :                     state, state.dataOutRptPredefined->pdchEMgasmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2000        2763 :             } break;
    2001             : 
    2002        2015 :             case RT_IPUnits::Cooling: {
    2003        4030 :                 OutputReportPredefined::PreDefTableEntry(
    2004        2015 :                     state, state.dataOutRptPredefined->pdchEMcoolannual, meter->Name, period.Value * Constant::convertJtoGJ);
    2005        4030 :                 OutputReportPredefined::PreDefTableEntry(
    2006        2015 :                     state, state.dataOutRptPredefined->pdchEMcoolminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
    2007        4030 :                 OutputReportPredefined::PreDefTableEntry(
    2008        6045 :                     state, state.dataOutRptPredefined->pdchEMcoolminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    2009        4030 :                 OutputReportPredefined::PreDefTableEntry(
    2010        2015 :                     state, state.dataOutRptPredefined->pdchEMcoolmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
    2011        4030 :                 OutputReportPredefined::PreDefTableEntry(
    2012        6045 :                     state, state.dataOutRptPredefined->pdchEMcoolmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2013        2015 :             } break;
    2014             : 
    2015        3512 :             case RT_IPUnits::Water: {
    2016        3512 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMwaterannual, meter->Name, period.Value);
    2017        7024 :                 OutputReportPredefined::PreDefTableEntry(
    2018        3512 :                     state, state.dataOutRptPredefined->pdchEMwaterminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
    2019        7024 :                 OutputReportPredefined::PreDefTableEntry(
    2020       10536 :                     state, state.dataOutRptPredefined->pdchEMwaterminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    2021        7024 :                 OutputReportPredefined::PreDefTableEntry(
    2022        3512 :                     state, state.dataOutRptPredefined->pdchEMwatermaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
    2023        7024 :                 OutputReportPredefined::PreDefTableEntry(
    2024       10536 :                     state, state.dataOutRptPredefined->pdchEMwatermaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2025        3512 :             } break;
    2026             : 
    2027        5732 :             case RT_IPUnits::OtherKG: {
    2028        5732 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherKGannual, meter->Name, period.Value);
    2029        5732 :                 OutputReportPredefined::PreDefTableEntry(
    2030        5732 :                     state, state.dataOutRptPredefined->pdchEMotherKGminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
    2031       11464 :                 OutputReportPredefined::PreDefTableEntry(
    2032       17196 :                     state, state.dataOutRptPredefined->pdchEMotherKGminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    2033        5732 :                 OutputReportPredefined::PreDefTableEntry(
    2034        5732 :                     state, state.dataOutRptPredefined->pdchEMotherKGmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
    2035       11464 :                 OutputReportPredefined::PreDefTableEntry(
    2036       17196 :                     state, state.dataOutRptPredefined->pdchEMotherKGmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2037        5732 :             } break;
    2038             : 
    2039         296 :             case RT_IPUnits::OtherM3: {
    2040         296 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherM3annual, meter->Name, period.Value, 3);
    2041         296 :                 OutputReportPredefined::PreDefTableEntry(
    2042         296 :                     state, state.dataOutRptPredefined->pdchEMotherM3minvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
    2043         592 :                 OutputReportPredefined::PreDefTableEntry(
    2044         888 :                     state, state.dataOutRptPredefined->pdchEMotherM3minvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    2045         296 :                 OutputReportPredefined::PreDefTableEntry(
    2046         296 :                     state, state.dataOutRptPredefined->pdchEMotherM3maxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
    2047         592 :                 OutputReportPredefined::PreDefTableEntry(
    2048         888 :                     state, state.dataOutRptPredefined->pdchEMotherM3maxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2049         296 :             } break;
    2050             : 
    2051         296 :             case RT_IPUnits::OtherL: {
    2052         296 :                 OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchEMotherLannual, meter->Name, period.Value, 3);
    2053         296 :                 OutputReportPredefined::PreDefTableEntry(
    2054         296 :                     state, state.dataOutRptPredefined->pdchEMotherLminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec, 3);
    2055         592 :                 OutputReportPredefined::PreDefTableEntry(
    2056         888 :                     state, state.dataOutRptPredefined->pdchEMotherLminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    2057         296 :                 OutputReportPredefined::PreDefTableEntry(
    2058         296 :                     state, state.dataOutRptPredefined->pdchEMotherLmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec, 3);
    2059         592 :                 OutputReportPredefined::PreDefTableEntry(
    2060         888 :                     state, state.dataOutRptPredefined->pdchEMotherLmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2061         296 :             } break;
    2062             : 
    2063       37323 :             default: {
    2064       74646 :                 OutputReportPredefined::PreDefTableEntry(
    2065       37323 :                     state, state.dataOutRptPredefined->pdchEMotherJannual, meter->Name, period.Value * Constant::convertJtoGJ);
    2066       74646 :                 OutputReportPredefined::PreDefTableEntry(
    2067       37323 :                     state, state.dataOutRptPredefined->pdchEMotherJminvalue, meter->Name, period.MinVal / state.dataGlobal->TimeStepZoneSec);
    2068       74646 :                 OutputReportPredefined::PreDefTableEntry(
    2069      111969 :                     state, state.dataOutRptPredefined->pdchEMotherJminvaluetime, meter->Name, DateToStringWithMonth(period.MinValDate));
    2070       74646 :                 OutputReportPredefined::PreDefTableEntry(
    2071       37323 :                     state, state.dataOutRptPredefined->pdchEMotherJmaxvalue, meter->Name, period.MaxVal / state.dataGlobal->TimeStepZoneSec);
    2072       74646 :                 OutputReportPredefined::PreDefTableEntry(
    2073      111969 :                     state, state.dataOutRptPredefined->pdchEMotherJmaxvaluetime, meter->Name, DateToStringWithMonth(period.MaxValDate));
    2074       37323 :             } break;
    2075             :             } // switch
    2076         794 :         }     // for (meter)
    2077         794 :     }         // ReportForTabularReports()
    2078             : 
    2079      194034 :     std::string DateToStringWithMonth(int const codedDate) // word containing encoded month, day, hour, minute
    2080             :     {
    2081             :         // SUBROUTINE INFORMATION:
    2082             :         //       AUTHOR         Jason Glazer
    2083             :         //       DATE WRITTEN   August 2003
    2084             :         //       MODIFIED       na
    2085             :         //       RE-ENGINEERED  na
    2086             : 
    2087             :         // PURPOSE OF THIS SUBROUTINE:
    2088             :         //   Convert the coded date format into a usable
    2089             :         //   string
    2090             : 
    2091      194034 :         if (codedDate == 0) return "-";
    2092             : 
    2093             :         static constexpr std::string_view DateFmt("{:02}-{:3}-{:02}:{:02}");
    2094             : 
    2095             :         // ((month*100 + day)*100 + hour)*100 + minute
    2096             :         int Month;  // month in integer format (1-12)
    2097             :         int Day;    // day in integer format (1-31)
    2098             :         int Hour;   // hour in integer format (1-24)
    2099             :         int Minute; // minute in integer format (0:59)
    2100             : 
    2101      194034 :         General::DecodeMonDayHrMin(codedDate, Month, Day, Hour, Minute);
    2102             : 
    2103      194034 :         if (Month < 1 || Month > 12) return "-";
    2104      194034 :         if (Day < 1 || Day > 31) return "-";
    2105      194034 :         if (Hour < 1 || Hour > 24) return "-";
    2106      194034 :         if (Minute < 0 || Minute > 60) return "-";
    2107             : 
    2108      194034 :         --Hour;
    2109      194034 :         if (Minute == 60) {
    2110       24399 :             ++Hour;
    2111       24399 :             Minute = 0;
    2112             :         }
    2113             : 
    2114      194034 :         std::string monthName;
    2115      194034 :         switch (Month) {
    2116       95579 :         case 1:
    2117       95579 :             monthName = "JAN";
    2118       95579 :             break;
    2119          14 :         case 2:
    2120          14 :             monthName = "FEB";
    2121          14 :             break;
    2122          22 :         case 3:
    2123          22 :             monthName = "MAR";
    2124          22 :             break;
    2125          32 :         case 4:
    2126          32 :             monthName = "APR";
    2127          32 :             break;
    2128           0 :         case 5:
    2129           0 :             monthName = "MAY";
    2130           0 :             break;
    2131          21 :         case 6:
    2132          21 :             monthName = "JUN";
    2133          21 :             break;
    2134       80142 :         case 7:
    2135       80142 :             monthName = "JUL";
    2136       80142 :             break;
    2137         456 :         case 8:
    2138         456 :             monthName = "AUG";
    2139         456 :             break;
    2140         235 :         case 9:
    2141         235 :             monthName = "SEP";
    2142         235 :             break;
    2143           5 :         case 10:
    2144           5 :             monthName = "OCT";
    2145           5 :             break;
    2146           0 :         case 11:
    2147           0 :             monthName = "NOV";
    2148           0 :             break;
    2149       17528 :         case 12:
    2150       17528 :             monthName = "DEC";
    2151       17528 :             break;
    2152           0 :         default:
    2153           0 :             assert(false);
    2154             :         }
    2155             : 
    2156      194034 :         return format(DateFmt, Day, monthName, Hour, Minute);
    2157      194034 :     }
    2158             : 
    2159      356934 :     std::string OutVar::multiplierString() const
    2160             :     {
    2161      345904 :         return (ZoneMult == 1 && ZoneListMult == 1)
    2162      356934 :                    ? ""
    2163     1048225 :                    : format(" * {}  (Zone Multiplier = {}, Zone List Multiplier = {})", ZoneMult * ZoneListMult, ZoneMult, ZoneListMult);
    2164             :     }
    2165             : 
    2166         795 :     void ReportMeterDetails(EnergyPlusData &state)
    2167             :     {
    2168             : 
    2169             :         // SUBROUTINE INFORMATION:
    2170             :         //       AUTHOR         Linda Lawrie
    2171             :         //       DATE WRITTEN   January 2006
    2172             : 
    2173             :         // PURPOSE OF THIS SUBROUTINE:
    2174             :         // Writes the meter details report.  This shows which variables are on
    2175             :         // meters as well as the meter contents.
    2176             : 
    2177             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2178         795 :         auto &op = state.dataOutputProcessor;
    2179             : 
    2180      180927 :         for (auto const *var : op->outVars) {
    2181             : 
    2182      180132 :             if (var->meterNums.empty()) continue;
    2183             : 
    2184       59005 :             print(state.files.mtd,
    2185             :                   "\n Meters for {},{} [{}]{}\n",
    2186       59005 :                   var->ReportID,
    2187       59005 :                   var->keyColonName,
    2188       59005 :                   Constant::unitNames[(int)var->units],
    2189      118010 :                   var->multiplierString());
    2190             : 
    2191      356934 :             for (int const meterNum : var->meterNums) {
    2192      297929 :                 auto const *meter = op->meters[meterNum];
    2193             : 
    2194      297929 :                 print(state.files.mtd,
    2195             :                       "  On{}Meter={} [{}]\n",
    2196      595858 :                       (meter->type == MeterType::Normal) ? "" : "Custom",
    2197      297929 :                       meter->Name,
    2198      297929 :                       Constant::unitNames[(int)meter->units]);
    2199       59005 :             }
    2200         795 :         } // for (var)
    2201             : 
    2202       98025 :         for (auto const *meter : op->meters) {
    2203             : 
    2204       97230 :             print(state.files.mtd, "\n For Meter={} [{}]", meter->Name, Constant::unitNames[(int)meter->units]);
    2205       97230 :             if (meter->resource != Constant::eResource::Invalid) {
    2206       97230 :                 print(state.files.mtd, ", ResourceType={}", Constant::eResourceNames[(int)meter->resource]);
    2207             :             }
    2208       97230 :             if (meter->endUseCat != EndUseCat::Invalid) {
    2209       69880 :                 print(state.files.mtd, ", EndUse={}", endUseCatNames[(int)meter->endUseCat]);
    2210             :             }
    2211       97230 :             if (meter->group != Group::Invalid) {
    2212       59427 :                 print(state.files.mtd, ", Group={}", groupNames[(int)meter->group]);
    2213             :             }
    2214       97230 :             print(state.files.mtd, ", contents are:\n");
    2215             : 
    2216       97230 :             if (meter->type == MeterType::Normal) {
    2217      394138 :                 for (int srcVarNum : meter->srcVarNums) {
    2218      297045 :                     auto const *var = op->outVars[srcVarNum];
    2219      297045 :                     print(state.files.mtd, "  {}{}\n", var->keyColonName, var->multiplierString());
    2220       97093 :                 }
    2221             : 
    2222         137 :             } else if (meter->type == MeterType::Custom) {
    2223         789 :                 for (int srcVarNum : meter->srcVarNums) {
    2224         693 :                     auto const *var = op->outVars[srcVarNum];
    2225         693 :                     print(state.files.mtd, "  {}{}\n", var->keyColonName, var->multiplierString());
    2226          96 :                 }
    2227             : 
    2228          41 :             } else if (meter->type == MeterType::CustomDec) {
    2229          41 :                 print(state.files.mtd,
    2230             :                       " Values for this meter will be Source Meter={}; but will be decremented by:\n",
    2231          41 :                       op->meters[meter->decMeterNum]->Name);
    2232         232 :                 for (int srcVarNum : meter->srcVarNums) {
    2233         191 :                     auto const *var = op->outVars[srcVarNum];
    2234         191 :                     print(state.files.mtd, "  {}{}\n", var->keyColonName, var->multiplierString());
    2235          41 :                 }
    2236             :             }
    2237         795 :         } // for (meter)
    2238         795 :     }     // ReportMeterDetails()
    2239             : 
    2240             :     // *****************************************************************************
    2241             :     // End of routines for Energy Meters implementation in EnergyPlus.
    2242             :     // *****************************************************************************
    2243             : 
    2244      361317 :     void WriteTimeStampFormatData(
    2245             :         EnergyPlusData &state,
    2246             :         InputOutputFile &outputFile,
    2247             :         ReportFreq const reportingInterval, // See Module Parameter Definitions for ReportEach, ReportTimeStep, ReportHourly, etc.
    2248             :         int const reportID,                 // The ID of the time stamp
    2249             :         std::string const &DayOfSimChr,     // the number of days simulated so far
    2250             :         bool writeToSQL,
    2251             :         int const Month,               // the month of the reporting interval
    2252             :         int const DayOfMonth,          // The day of the reporting interval
    2253             :         int const Hour,                // The hour of the reporting interval
    2254             :         Real64 const EndMinute,        // The last minute in the reporting interval
    2255             :         Real64 const StartMinute,      // The starting minute of the reporting interval
    2256             :         int const DST,                 // A flag indicating whether daylight savings time is observed
    2257             :         std::string_view const DayType // The day tied for the data (e.g., Monday)
    2258             :     )
    2259             :     {
    2260             : 
    2261             :         // FUNCTION INFORMATION:
    2262             :         //       AUTHOR         Greg Stark
    2263             :         //       DATE WRITTEN   July 2008
    2264             :         //       MODIFIED       na
    2265             :         //       RE-ENGINEERED  na
    2266             : 
    2267             :         // PURPOSE OF THIS FUNCTION:
    2268             :         // This function reports the timestamp data for the output processor
    2269             :         // Much of the code in this function was embedded in earlier versions of EnergyPlus
    2270             :         // and was moved to this location to simplify maintenance and to allow for data output
    2271             :         // to the SQL database
    2272             : 
    2273      361317 :         std::string reportStr = (reportID == -1) ? "" : std::to_string(reportID);
    2274             : 
    2275      361317 :         assert(reportStr.length() + DayOfSimChr.length() + (DayType.length()) + 26 < N_WriteTimeStampFormatData); // Check will fit in stamp size
    2276             : 
    2277      361317 :         if (!outputFile.good()) return;
    2278             : 
    2279      361317 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2280             : 
    2281      361317 :         switch (reportingInterval) {
    2282      309343 :         case ReportFreq::EachCall:
    2283             :         case ReportFreq::TimeStep: {
    2284      309343 :             assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && StartMinute != -1 && EndMinute != -1 && DST != -1 && !DayType.empty());
    2285      309343 :             print<FormatSyntax::FMT>(outputFile,
    2286             :                                      "{},{},{:2d},{:2d},{:2d},{:2d},{:5.2f},{:5.2f},{}\n",
    2287             :                                      reportStr,
    2288             :                                      DayOfSimChr,
    2289             :                                      Month,
    2290             :                                      DayOfMonth,
    2291             :                                      DST,
    2292             :                                      Hour,
    2293             :                                      StartMinute,
    2294             :                                      EndMinute,
    2295             :                                      DayType);
    2296             : 
    2297      309343 :             if (writeToSQL && sql) {
    2298       35724 :                 sql->createSQLiteTimeIndexRecord(reportingInterval,
    2299             :                                                  reportID,
    2300       17862 :                                                  state.dataGlobal->DayOfSim,
    2301       17862 :                                                  state.dataEnvrn->CurEnvirNum,
    2302       17862 :                                                  state.dataGlobal->CalendarYear,
    2303       17862 :                                                  state.dataEnvrn->CurrentYearIsLeapYear,
    2304             :                                                  Month,
    2305             :                                                  DayOfMonth,
    2306             :                                                  Hour,
    2307             :                                                  EndMinute,
    2308             :                                                  StartMinute,
    2309             :                                                  DST,
    2310             :                                                  DayType,
    2311       17862 :                                                  state.dataGlobal->WarmupFlag);
    2312             :             }
    2313      309343 :         } break;
    2314             : 
    2315       49152 :         case ReportFreq::Hour: {
    2316       49152 :             assert(Month != -1 && DayOfMonth != -1 && Hour != -1 && DST != -1 && !DayType.empty());
    2317       49152 :             print<FormatSyntax::FMT>(outputFile,
    2318             :                                      "{},{},{:2d},{:2d},{:2d},{:2d},{:5.2f},{:5.2f},{}\n",
    2319             :                                      reportStr,
    2320             :                                      DayOfSimChr,
    2321             :                                      Month,
    2322             :                                      DayOfMonth,
    2323             :                                      DST,
    2324             :                                      Hour,
    2325           0 :                                      0.0,
    2326       49152 :                                      60.0,
    2327             :                                      DayType);
    2328       49152 :             if (writeToSQL && sql) {
    2329       10320 :                 sql->createSQLiteTimeIndexRecord(reportingInterval,
    2330             :                                                  reportID,
    2331        5160 :                                                  state.dataGlobal->DayOfSim,
    2332        5160 :                                                  state.dataEnvrn->CurEnvirNum,
    2333        5160 :                                                  state.dataGlobal->CalendarYear,
    2334        5160 :                                                  state.dataEnvrn->CurrentYearIsLeapYear,
    2335             :                                                  Month,
    2336             :                                                  DayOfMonth,
    2337             :                                                  Hour,
    2338             :                                                  -1, // EndMinute
    2339             :                                                  -1, // StartMinute
    2340             :                                                  DST,
    2341             :                                                  DayType,
    2342        5160 :                                                  state.dataGlobal->WarmupFlag);
    2343             :             }
    2344       49152 :         } break;
    2345         144 :         case ReportFreq::Day: {
    2346         144 :             assert(Month != -1 && DayOfMonth != -1 && DST != -1 && !DayType.empty());
    2347         144 :             print<FormatSyntax::FMT>(outputFile, "{},{},{:2d},{:2d},{:2d},{}\n", reportStr, DayOfSimChr, Month, DayOfMonth, DST, DayType);
    2348         144 :             if (writeToSQL && sql) {
    2349          34 :                 sql->createSQLiteTimeIndexRecord(reportingInterval,
    2350             :                                                  reportID,
    2351          17 :                                                  state.dataGlobal->DayOfSim,
    2352          17 :                                                  state.dataEnvrn->CurEnvirNum,
    2353          17 :                                                  state.dataGlobal->CalendarYear,
    2354          17 :                                                  state.dataEnvrn->CurrentYearIsLeapYear,
    2355             :                                                  Month,
    2356             :                                                  DayOfMonth,
    2357             :                                                  -1, // Hour
    2358             :                                                  -1, // EndMinute
    2359             :                                                  -1, // StartMinute
    2360             :                                                  DST,
    2361             :                                                  DayType,
    2362          17 :                                                  state.dataGlobal->WarmupFlag);
    2363             :             }
    2364         144 :         } break;
    2365             : 
    2366        1506 :         case ReportFreq::Month: {
    2367        1506 :             assert(Month != -1);
    2368        1506 :             print<FormatSyntax::FMT>(outputFile, "{},{},{:2d}\n", reportStr, DayOfSimChr, Month);
    2369        1506 :             if (writeToSQL && sql) {
    2370         230 :                 sql->createSQLiteTimeIndexRecord(reportingInterval,
    2371             :                                                  reportID,
    2372         115 :                                                  state.dataGlobal->DayOfSim,
    2373         115 :                                                  state.dataEnvrn->CurEnvirNum,
    2374         115 :                                                  state.dataGlobal->CalendarYear,
    2375         115 :                                                  state.dataEnvrn->CurrentYearIsLeapYear,
    2376             :                                                  Month);
    2377             :             }
    2378        1506 :         } break;
    2379             : 
    2380        1172 :         case ReportFreq::Simulation: {
    2381        1172 :             print<FormatSyntax::FMT>(outputFile, "{},{}\n", reportStr, DayOfSimChr);
    2382        1172 :             if (writeToSQL && sql) {
    2383          16 :                 sql->createSQLiteTimeIndexRecord(reportingInterval,
    2384             :                                                  reportID,
    2385           8 :                                                  state.dataGlobal->DayOfSim,
    2386           8 :                                                  state.dataEnvrn->CurEnvirNum,
    2387           8 :                                                  state.dataGlobal->CalendarYear,
    2388           8 :                                                  state.dataEnvrn->CurrentYearIsLeapYear);
    2389             :             }
    2390        1172 :         } break;
    2391           0 :         default: {
    2392           0 :             if (sql) {
    2393           0 :                 sql->sqliteWriteMessage(
    2394           0 :                     format<FormatSyntax::FMT>("Illegal reportingInterval passed to WriteTimeStampFormatData: {}", (int)reportingInterval));
    2395             :             }
    2396           0 :         } break;
    2397             :         } // switch (reportFreq)
    2398      361317 :     }     // WriteTimeStampFormatData()
    2399             : 
    2400           0 :     void WriteYearlyTimeStamp(EnergyPlusData &state,
    2401             :                               InputOutputFile &outputFile,
    2402             :                               int reportID,                    // The ID of the time stamp
    2403             :                               std::string const &yearOfSimChr, // the year of the simulation
    2404             :                               bool writeToSQL)
    2405             :     {
    2406           0 :         print(outputFile, "{},{}\n", reportID, yearOfSimChr);
    2407           0 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2408           0 :         if (writeToSQL && sql) {
    2409           0 :             sql->createYearlyTimeIndexRecord(state.dataGlobal->CalendarYear, state.dataEnvrn->CurEnvirNum);
    2410             :         }
    2411           0 :     } // WriteYearlyTimeStamp()
    2412             : 
    2413       61042 :     void OutVar::writeReportDictionaryItem(EnergyPlusData &state)
    2414             :     {
    2415             : 
    2416             :         // SUBROUTINE INFORMATION:
    2417             :         //       AUTHOR         Greg Stark
    2418             :         //       DATE WRITTEN   August 2008
    2419             :         //       MODIFIED       April 2011; Linda Lawrie
    2420             :         //       RE-ENGINEERED  na
    2421             : 
    2422             :         // PURPOSE OF THIS SUBROUTINE:
    2423             :         // This subroutine writes the ESO data dictionary information to the output files
    2424             :         // and the SQL database
    2425             : 
    2426             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2427       61042 :         auto &op = state.dataOutputProcessor;
    2428       61042 :         auto &rf = state.dataResultsFramework->resultsFramework;
    2429       61042 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2430             : 
    2431          10 :         std::string_view unitsString = (units == Constant::Units::customEMS && !unitNameCustomEMS.empty())
    2432       61042 :                                            ? unitNameCustomEMS
    2433       61042 :                                            : ((units == Constant::Units::Invalid) ? "" : Constant::unitNames[(int)units]);
    2434             : 
    2435       61042 :         std::string schedString = (SchedPtr != 0) ? state.dataScheduleMgr->Schedule(SchedPtr).Name : "";
    2436             : 
    2437       61042 :         if (state.files.eso.good()) {
    2438       61042 :             print(state.files.eso,
    2439             :                   "{},{},{},{} [{}]{}{}{}\n",
    2440       61042 :                   ReportID,
    2441       61042 :                   reportFreqArbitraryInts[(int)freq],
    2442       61042 :                   key,
    2443       61042 :                   name,
    2444             :                   unitsString,
    2445       61042 :                   reportingFrequencyNoticeStrings[(int)freq],
    2446      122084 :                   !schedString.empty() ? "," : "",
    2447             :                   schedString);
    2448             :         }
    2449             : 
    2450       61042 :         if (freq == ReportFreq::Hour || freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year ||
    2451       23777 :             freq == ReportFreq::Simulation)
    2452       37541 :             op->freqTrackingVariables[(int)freq] = true;
    2453             : 
    2454       61042 :         if (sql) {
    2455       13942 :             sql->createSQLiteReportDictionaryRecord(ReportID, storeType, indexGroup, key, name, timeStepType, unitsString, freq, false, schedString);
    2456             :         }
    2457             : 
    2458             :         // add to ResultsFramework for output variable list, need to check RVI/MVI later
    2459       61042 :         rf->addReportVariable(key, name, unitsString, freq);
    2460             : 
    2461       61042 :     } // OutVar::WriteReportDictionaryItem()
    2462             : 
    2463        8478 :     void WriteMeterDictionaryItem(EnergyPlusData &state,
    2464             :                                   ReportFreq const freq, // The reporting interval (e.g., hourly, daily)
    2465             :                                   StoreType const storeType,
    2466             :                                   int const reportID,             // The reporting ID in for the variable
    2467             :                                   std::string const &indexGroup,  // The reporting group for the variable
    2468             :                                   std::string const &meterName,   // The variable's meter name
    2469             :                                   Constant::Units const units,    // The variables units
    2470             :                                   bool const cumulativeMeterFlag, // A flag indicating cumulative data
    2471             :                                   bool const meterFileOnlyFlag    // A flag indicating whether the data is to be written to standard output
    2472             :     )
    2473             :     {
    2474             : 
    2475             :         // SUBROUTINE INFORMATION:
    2476             :         //       AUTHOR         Greg Stark
    2477             :         //       DATE WRITTEN   August 2008
    2478             :         //       MODIFIED       April 2011; Linda Lawrie
    2479             :         //       RE-ENGINEERED  na
    2480             : 
    2481             :         // PURPOSE OF THIS SUBROUTINE:
    2482             :         // The subroutine writes meter data dictionary information to the output files
    2483             :         // and the SQL database. Much of the code here was embedded in other subroutines
    2484             :         // and was moved here for the purposes of ease of maintenance and to allow easy
    2485             :         // data reporting to the SQL database
    2486             : 
    2487             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2488        8478 :         auto &rf = state.dataResultsFramework->resultsFramework;
    2489        8478 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2490             : 
    2491        8478 :         std::string FreqString = std::string(reportingFrequencyNoticeStrings[(int)freq]);
    2492        8478 :         std::string FreqString2 = FreqString.substr(0, index(FreqString, '['));
    2493             : 
    2494        8478 :         const auto print_meter = [&](EnergyPlusData &state, const int frequency) {
    2495       11050 :             const auto out = [&](InputOutputFile &of) {
    2496       11050 :                 if (of.good()) {
    2497       11050 :                     if (cumulativeMeterFlag) {
    2498             :                         static constexpr std::string_view fmt = "{},{},Cumulative {} [{}]{}\n";
    2499           6 :                         print(of, fmt, reportID, 1, meterName, Constant::unitNames[(int)units], FreqString2);
    2500             :                     } else {
    2501             :                         static constexpr std::string_view fmt = "{},{},{} [{}]{}\n";
    2502       11044 :                         print(of, fmt, reportID, frequency, meterName, Constant::unitNames[(int)units], FreqString);
    2503             :                     }
    2504             :                 }
    2505       11050 :             };
    2506             : 
    2507        8478 :             out(state.files.mtr);
    2508        8478 :             if (!meterFileOnlyFlag) {
    2509        2572 :                 out(state.files.eso);
    2510             :             }
    2511        8478 :         };
    2512             : 
    2513        8478 :         print_meter(state, reportFreqArbitraryInts[(int)freq]);
    2514             : 
    2515             :         static constexpr std::string_view keyedValueStringCum("Cumulative ");
    2516             :         static constexpr std::string_view keyedValueStringNon;
    2517        8478 :         std::string_view const keyedValueString(cumulativeMeterFlag ? keyedValueStringCum : keyedValueStringNon);
    2518             : 
    2519        8478 :         if (sql) {
    2520        3920 :             sql->createSQLiteReportDictionaryRecord(
    2521        1960 :                 reportID, storeType, indexGroup, keyedValueString, meterName, TimeStepType::Zone, Constant::unitNames[(int)units], freq, true);
    2522             :         }
    2523             : 
    2524             :         // add to ResultsFramework for output variable list, need to check RVI/MVI later
    2525        8478 :         rf->addReportMeter(meterName, Constant::unitNames[(int)units], freq);
    2526             : 
    2527        8478 :     } // WriteMeterDictionaryItem()
    2528             : 
    2529     1467752 :     void OutVar::writeOutput(EnergyPlusData &state,
    2530             :                              ReportFreq const reportFreq // The report type or interval (e.g., hourly)
    2531             :     )
    2532             :     {
    2533             : 
    2534             :         // SUBROUTINE INFORMATION:
    2535             :         //       AUTHOR         Greg Stark
    2536             :         //       DATE WRITTEN   August 2008
    2537             :         //       MODIFIED       April 2011; Linda Lawrie, December 2017; Jason DeGraw
    2538             :         //       RE-ENGINEERED  na
    2539             : 
    2540             :         // PURPOSE OF THIS SUBROUTINE:
    2541             :         // This subroutine writes real report variable data to the output file and
    2542             :         // SQL database. Much of the code here was an included in earlier versions
    2543             :         // of the UpdateDataandReport subroutine. The code was moved to facilitate
    2544             :         // easier maintenance and writing of data to the SQL database.
    2545             : 
    2546     1467752 :         if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) return;
    2547             : 
    2548     1466790 :         if (!Report || freq != reportFreq || !Stored) return;
    2549             : 
    2550       34761 :         if (NumStored > 0.0) {
    2551       34755 :             writeReportData(state);
    2552       34755 :             ++state.dataGlobal->StdOutputRecordCount;
    2553             :         }
    2554             : 
    2555       34761 :         StoreValue = 0.0;
    2556       34761 :         NumStored = 0.0;
    2557       34761 :         MinValue = MinSetValue;
    2558       34761 :         MaxValue = MaxSetValue;
    2559       34761 :         Stored = false;
    2560             :     } // OutVar::WriteOutput()
    2561             : 
    2562          10 :     void WriteCumulativeReportMeterData(EnergyPlusData &state,
    2563             :                                         int const reportID,      // The variable's report ID
    2564             :                                         Real64 const repValue,   // The variable's value
    2565             :                                         bool const meterOnlyFlag // A flag that indicates if the data should be written to standard output
    2566             :     )
    2567             :     {
    2568             : 
    2569             :         // SUBROUTINE INFORMATION:
    2570             :         //       AUTHOR         Greg Stark
    2571             :         //       DATE WRITTEN   July 2008
    2572             :         //       MODIFIED       na
    2573             :         //       RE-ENGINEERED  na
    2574             : 
    2575             :         // PURPOSE OF THIS SUBROUTINE:
    2576             :         // This subroutine writes the cumulative meter data to the output files and
    2577             :         // SQL database.
    2578             : 
    2579          10 :         std::string NumberOut; // Character for producing "number out"
    2580          10 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2581             : 
    2582          10 :         if (repValue == 0.0) {
    2583           0 :             NumberOut = "0.0";
    2584             :         } else {
    2585             :             char meterData[129];
    2586          10 :             dtoa(repValue, meterData);
    2587          10 :             NumberOut = std::string(meterData);
    2588             :         }
    2589             : 
    2590          10 :         if (sql) {
    2591           4 :             sql->createSQLiteReportDataRecord(reportID, repValue);
    2592             :         }
    2593             : 
    2594          10 :         if (state.files.mtr.good()) print(state.files.mtr, "{},{}\n", reportID, NumberOut);
    2595          10 :         ++state.dataGlobal->StdMeterRecordCount;
    2596             : 
    2597          10 :         if (!meterOnlyFlag) {
    2598           2 :             if (state.files.eso.good()) print(state.files.eso, "{},{}\n", reportID, NumberOut);
    2599           2 :             ++state.dataGlobal->StdOutputRecordCount;
    2600             :         }
    2601          10 :     } // WriteCumulativeReportMeterData()
    2602             : 
    2603       83871 :     void MeterPeriod::WriteReportData(EnergyPlusData &state, ReportFreq const freq)
    2604             :     {
    2605             : 
    2606             :         // SUBROUTINE INFORMATION:
    2607             :         //       AUTHOR         Greg Stark
    2608             :         //       DATE WRITTEN   July 2008
    2609             : 
    2610             :         // PURPOSE OF THIS SUBROUTINE:
    2611             :         // This subroutine writes for the non-cumulative meter data to the output files and
    2612             :         // SQL database.
    2613             : 
    2614       83871 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2615             : 
    2616       83871 :         std::string NumberOut;
    2617             : 
    2618       83871 :         if (Value == 0.0) {
    2619       28356 :             NumberOut = "0.0";
    2620             :         } else {
    2621             :             char tmp[128];
    2622       55515 :             dtoa(Value, tmp);
    2623       55515 :             NumberOut = std::string(tmp);
    2624             :         }
    2625             : 
    2626       83871 :         if (sql) {
    2627       43012 :             sql->createSQLiteReportDataRecord(RptNum, Value, freq, MinVal, MinValDate, MaxVal, MaxValDate, state.dataGlobal->MinutesPerTimeStep);
    2628             :         }
    2629             : 
    2630       83871 :         if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
    2631       65784 :             if (state.files.mtr.good()) {
    2632       65784 :                 print(state.files.mtr, "{},{}\n", RptNum, NumberOut);
    2633             :             }
    2634       65784 :             ++state.dataGlobal->StdMeterRecordCount;
    2635       65784 :             if (state.files.eso.good() && !RptFO) {
    2636       55512 :                 print(state.files.eso, "{},{}\n", RptNum, NumberOut);
    2637       55512 :                 ++state.dataGlobal->StdOutputRecordCount;
    2638             :             }
    2639             :         } else { // if ( ( reportingInterval == ReportDaily ) || ( reportingInterval == ReportMonthly ) || ( reportingInterval == ReportSim ) ) {
    2640             :                  // // 2, 3, 4
    2641             :             // Append the min and max strings with date information
    2642             :             char minValString[128], maxValString[128];
    2643       18087 :             dtoa(MinVal, minValString);
    2644       18087 :             dtoa(MaxVal, maxValString);
    2645             : 
    2646       18087 :             std::string minDateString = produceDateString(MinValDate, freq);
    2647       18087 :             std::string maxDateString = produceDateString(MaxValDate, freq);
    2648             : 
    2649       18087 :             if (state.files.mtr.good()) {
    2650       18087 :                 print(state.files.mtr, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
    2651             :             }
    2652             : 
    2653       18087 :             ++state.dataGlobal->StdMeterRecordCount;
    2654       18087 :             if (state.files.eso.good() && !RptFO) {
    2655        4672 :                 print(state.files.eso, "{},{},{},{},{},{}\n", RptNum, NumberOut, minValString, minDateString, maxValString, maxDateString);
    2656        4672 :                 ++state.dataGlobal->StdOutputRecordCount;
    2657             :             }
    2658       18087 :         }
    2659       83871 :     } // MeterPeriod::WriteReportData()
    2660             : 
    2661    16583882 :     void WriteNumericData(EnergyPlusData &state,
    2662             :                           int const reportID,   // The variable's reporting ID
    2663             :                           Real64 const repValue // The variable's value
    2664             :     )
    2665             :     {
    2666             :         // SUBROUTINE INFORMATION:
    2667             :         //       AUTHOR         Mark Adams
    2668             :         //       DATE WRITTEN   May 2016
    2669             :         //       MODIFIED       na
    2670             :         //       RE-ENGINEERED  na
    2671             : 
    2672             :         // PURPOSE:
    2673             :         // This subroutine writes real data to the output files and
    2674             :         // SQL database.
    2675             :         // This is a refactor of WriteRealData.
    2676             :         //
    2677             :         // Much of the code here was an included in earlier versions
    2678             :         // of the UpdateDataandReport subroutine. The code was moved to facilitate
    2679             :         // easier maintenance and writing of data to the SQL database.
    2680    16583882 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2681             : 
    2682    16583882 :         if (state.dataSysVars->UpdateDataDuringWarmupExternalInterface && !state.dataSysVars->ReportDuringWarmup) return;
    2683             : 
    2684    16513706 :         if (sql) {
    2685     2103504 :             sql->createSQLiteReportDataRecord(reportID, repValue);
    2686             :         }
    2687             : 
    2688    16513706 :         if (state.files.eso.good()) {
    2689             :             char numericData[129];
    2690    16513706 :             dtoa(repValue, numericData);
    2691    16513706 :             print<FormatSyntax::FMT>(state.files.eso, "{},{}\n", reportID, numericData);
    2692             :         }
    2693             :     } // WriteNumericData()
    2694             : 
    2695           0 :     void WriteNumericData(EnergyPlusData &state,
    2696             :                           int const reportID,    // The variable's reporting ID
    2697             :                           int32_t const repValue // The variable's value
    2698             :     )
    2699             :     {
    2700             :         // SUBROUTINE INFORMATION:
    2701             :         //       AUTHOR         Mark Adams
    2702             :         //       DATE WRITTEN   May 2016
    2703             :         //       MODIFIED       na
    2704             :         //       RE-ENGINEERED  na
    2705             : 
    2706             :         // PURPOSE:
    2707             :         // This subroutine writes real data to the output files and
    2708             :         // SQL database.
    2709             :         // This is a refactor of WriteIntegerData.
    2710             :         //
    2711             :         // Much of the code here was an included in earlier versions
    2712             :         // of the UpdateDataandReport subroutine. The code was moved to facilitate
    2713             :         // easier maintenance and writing of data to the SQL database.
    2714             : 
    2715             :         //        i32toa(repValue, state.dataOutputProcessor->s_WriteNumericData);
    2716           0 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2717             : 
    2718           0 :         if (sql) {
    2719           0 :             sql->createSQLiteReportDataRecord(reportID, repValue);
    2720             :         }
    2721             : 
    2722           0 :         if (state.files.eso.good()) {
    2723           0 :             print<FormatSyntax::FMT>(state.files.eso, "{},{}\n", reportID, fmt::format_int(repValue).c_str());
    2724             :         }
    2725           0 :     } // WriteNumericData()
    2726             : 
    2727       34755 :     void OutVar::writeReportData(EnergyPlusData &state)
    2728             :     {
    2729             : 
    2730             :         // SUBROUTINE INFORMATION:
    2731             : 
    2732             :         // PURPOSE OF THIS SUBROUTINE:
    2733             :         // This subroutine writes averaged integer data to the output files and
    2734             :         // SQL database. It supports the WriteIntegerVariableOutput subroutine.
    2735             :         // Much of the code here was an included in earlier versions
    2736             :         // of the UpdateDataandReport subroutine. The code was moved to facilitate
    2737             :         // easier maintenance and writing of data to the SQL database.
    2738             : 
    2739             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2740       34755 :         std::string MaxOut; // Character for Max out string
    2741       34755 :         std::string MinOut; // Character for Min out string
    2742       34755 :         auto &rf = state.dataResultsFramework->resultsFramework;
    2743       34755 :         auto &sql = state.dataSQLiteProcedures->sqlite;
    2744             : 
    2745       34755 :         Real64 repVal = (storeType == StoreType::Average) ? (StoreValue / NumStored) : StoreValue;
    2746             : 
    2747             :         // Append the min and max strings with date information
    2748       34755 :         if (rf->timeSeriesEnabled() &&
    2749           0 :             (freq == ReportFreq::Day || freq == ReportFreq::Month || freq == ReportFreq::Year || freq == ReportFreq::Simulation)) {
    2750             :             // add to daily TS data store
    2751           0 :             rf->freqTSData[(int)freq].pushVariableValue(ReportID, repVal);
    2752             :         }
    2753             : 
    2754       34755 :         if (sql) {
    2755        1704 :             sql->createSQLiteReportDataRecord(ReportID, repVal, freq, MinValue, minValueDate, MaxValue, maxValueDate);
    2756             :         }
    2757             : 
    2758       34755 :         if (state.files.eso.good()) {
    2759       34755 :             std::string NumberOut;
    2760       34755 :             if (varType == VariableType::Real) {
    2761             :                 char tmp[128];
    2762       34347 :                 dtoa(repVal, tmp);
    2763       34347 :                 NumberOut = std::string(tmp);
    2764             :             } else {
    2765             :                 // Can someone explain why we are printing integers as
    2766             :                 // floats and why we are doing it differently than
    2767             :                 // floats?
    2768         408 :                 NumberOut = (repVal == 0.0) ? "0.0" : format("{:f}", repVal);
    2769             :             }
    2770             : 
    2771       34755 :             if ((freq == ReportFreq::EachCall) || (freq == ReportFreq::TimeStep) || (freq == ReportFreq::Hour)) { // -1, 0, 1
    2772           0 :                 print(state.files.eso, "{},{}\n", ReportID, NumberOut);
    2773             :             } else {
    2774             :                 char minValString[128], maxValString[128];
    2775       34755 :                 dtoa(MinValue, minValString);
    2776       34755 :                 dtoa(MaxValue, maxValString);
    2777             : 
    2778       34755 :                 std::string minDateString = produceDateString(minValueDate, freq);
    2779       34755 :                 std::string maxDateString = produceDateString(maxValueDate, freq);
    2780             : 
    2781       34755 :                 print(state.files.eso, "{},{},{},{},{},{}\n", ReportID, NumberOut, minValString, minDateString, maxValString, maxDateString);
    2782       34755 :             }
    2783       34755 :         }
    2784       34755 :     } // OutVar::WriteReportData()
    2785             : 
    2786           0 :     int DetermineIndexGroupKeyFromMeterName([[maybe_unused]] EnergyPlusData &state, std::string const &meterName) // the meter name
    2787             :     {
    2788             : 
    2789             :         // FUNCTION INFORMATION:
    2790             :         //       AUTHOR         Greg Stark
    2791             :         //       DATE WRITTEN   May 2009
    2792             : 
    2793             :         // PURPOSE OF THIS FUNCTION:
    2794             :         // This function attemps to guess determine how a meter variable should be
    2795             :         // grouped.  It does this by parsing the meter name and then assigns a
    2796             :         // indexGroupKey based on the name
    2797             : 
    2798             :         // Facility indices are in the 100s
    2799           0 :         if (has(meterName, "Electricity:Facility")) {
    2800           0 :             return 100;
    2801           0 :         } else if (has(meterName, "NaturalGas:Facility")) {
    2802           0 :             return 101;
    2803           0 :         } else if (has(meterName, "DistricHeatingWater:Facility")) {
    2804           0 :             return 102;
    2805           0 :         } else if (has(meterName, "DistricCooling:Facility")) {
    2806           0 :             return 103;
    2807           0 :         } else if (has(meterName, "ElectricityNet:Facility")) {
    2808           0 :             return 104;
    2809             : 
    2810             :             // Building indices are in the 200s
    2811           0 :         } else if (has(meterName, "Electricity:Building")) {
    2812           0 :             return 201;
    2813           0 :         } else if (has(meterName, "NaturalGas:Building")) {
    2814           0 :             return 202;
    2815             : 
    2816             :             // HVAC indices are in the 300s
    2817           0 :         } else if (has(meterName, "Electricity:HVAC")) {
    2818           0 :             return 301;
    2819             : 
    2820             :             // InteriorLights:Electricity:Zone indices are in the 500s
    2821           0 :         } else if (has(meterName, "InteriorLights:Electricity:Zone")) {
    2822           0 :             return 501;
    2823             : 
    2824             :             // InteriorLights:Electricity indices are in the 400s
    2825           0 :         } else if (has(meterName, "InteriorLights:Electricity")) {
    2826           0 :             return 401;
    2827             : 
    2828             :             // Unknown items have negative indices
    2829             :         } else {
    2830           0 :             return -11;
    2831             :         }
    2832             : 
    2833             :         return -1;
    2834             :     } // DetermineIndexGroupKeyFromMeterName()
    2835             : 
    2836        8542 :     std::string DetermineIndexGroupFromMeterGroup(Meter const *meter) // the meter
    2837             :     {
    2838             : 
    2839             :         // FUNCTION INFORMATION:
    2840             :         //       AUTHOR         Greg Stark
    2841             :         //       DATE WRITTEN   May 2009
    2842             :         //       MODIFIED       na
    2843             :         //       RE-ENGINEERED  na
    2844             : 
    2845             :         // PURPOSE OF THIS FUNCTION:
    2846             :         // This function attemps to determine how a meter variable should be
    2847             :         // grouped.  It does this by parsing the meter group
    2848             : 
    2849             :         // Return value
    2850        8542 :         std::string indexGroup;
    2851             : 
    2852        8542 :         if (meter->group != Group::Invalid) {
    2853        3146 :             indexGroup = groupNames[(int)meter->group];
    2854             :         } else {
    2855        5396 :             indexGroup = "Facility";
    2856             :         }
    2857             : 
    2858        8542 :         if (meter->resource != Constant::eResource::Invalid) {
    2859        8542 :             indexGroup += format(":{}", Constant::eResourceNames[(int)meter->resource]);
    2860             :         }
    2861             : 
    2862        8542 :         if (meter->endUseCat != EndUseCat::Invalid) {
    2863        1649 :             indexGroup += format(":{}", endUseCatNames[(int)meter->endUseCat]);
    2864             :         }
    2865             : 
    2866        8542 :         if (len(meter->EndUseSub) > 0) {
    2867          51 :             indexGroup += ":" + meter->EndUseSub;
    2868             :         }
    2869             : 
    2870        8542 :         return indexGroup;
    2871           0 :     } // DetermineIndexGroupFromMeterGroup()
    2872             : 
    2873        6081 :     void SetInternalVariableValue(EnergyPlusData &state,
    2874             :                                   OutputProcessor::VariableType const varType, // 1=integer, 2=real, 3=meter
    2875             :                                   int const keyVarIndex,                       // Array index
    2876             :                                   Real64 const SetRealVal,                     // real value to set, if type is real or meter
    2877             :                                   int const SetIntVal                          // integer value to set if type is integer
    2878             :     )
    2879             :     {
    2880             : 
    2881             :         // SUBROUTINE INFORMATION:
    2882             :         //       AUTHOR         B. Griffith
    2883             :         //       DATE WRITTEN   August 2012
    2884             : 
    2885             :         // PURPOSE OF THIS SUBROUTINE:
    2886             :         // This is a simple set routine for output pointers
    2887             :         // It is intended for special use to reinitializations those pointers used for EMS sensors
    2888             : 
    2889             :         // METHODOLOGY EMPLOYED:
    2890             :         // given a variable type and variable index,
    2891             :         // assign the pointers the values passed in.
    2892             : 
    2893             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    2894        6081 :         auto &op = state.dataOutputProcessor;
    2895             : 
    2896        6081 :         if (varType == VariableType::Integer) {
    2897          15 :             OutVarInt *varInt = dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]);
    2898          15 :             assert(varInt != nullptr);
    2899          15 :             *varInt->Which = SetIntVal;
    2900        6066 :         } else if (varType == VariableType::Real) {
    2901        6021 :             OutVarReal *varReal = dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]);
    2902        6021 :             assert(varReal != nullptr);
    2903        6021 :             *varReal->Which = SetRealVal;
    2904          45 :         } else if (varType == VariableType::Meter) {
    2905          45 :             op->meters[keyVarIndex]->CurTSValue = SetRealVal;
    2906             :         }
    2907        6081 :     } // SetInternalVariableValue()
    2908             : 
    2909             :     // returns the unit string for a DDVariableTypes item and custom string when customEMS is used
    2910           0 :     std::string unitStringFromDDitem(EnergyPlusData &state, int const ddNum // index provided for DDVariableTypes
    2911             :     )
    2912             :     {
    2913             :         // This function is here just for unit test purposes
    2914           0 :         DDOutVar *ddVar = state.dataOutputProcessor->ddOutVars[ddNum];
    2915           0 :         Constant::Units units = ddVar->units;
    2916           0 :         return format(" [{}]", units == Constant::Units::customEMS ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)units]);
    2917             :     } // unitStringFromDDitem()
    2918             : 
    2919             : } // namespace OutputProcessor
    2920             : 
    2921             : // TODO: Probably move these to a different location
    2922             : 
    2923     6135374 : void SetupOutputVariable(EnergyPlusData &state,
    2924             :                          std::string_view const name,            // String Name of variable (with units)
    2925             :                          Constant::Units const units,            // Actual units corresponding to the actual variable
    2926             :                          Real64 &ActualVariable,                 // Actual Variable, used to set up pointer
    2927             :                          OutputProcessor::TimeStepType timeStep, // Zone, HeatBalance=1, HVAC, System, Plant=2
    2928             :                          OutputProcessor::StoreType store,       // State, Average=1, NonState, Sum=2
    2929             :                          std::string const &key,                 // Associated Key for this variable
    2930             :                          Constant::eResource resource,           // Meter Resource Type (Electricity, Gas, etc)
    2931             :                          OutputProcessor::Group group,           // Meter Super Group Key (Building, System, Plant)
    2932             :                          OutputProcessor::EndUseCat endUseCat,   // Meter End Use Key (Lights, Heating, Cooling, etc)
    2933             :                          std::string_view const EndUseSub,       // Meter End Use Sub Key (General Lights, Task Lights, etc)
    2934             :                          std::string const &zone,                // Meter Zone Key (zone name)
    2935             :                          int const ZoneMult,                     // Zone Multiplier, defaults to 1
    2936             :                          int const ZoneListMult,                 // Zone List Multiplier, defaults to 1
    2937             :                          std::string const &spaceType,           // Space type (applicable for Building group only)
    2938             :                          int const indexGroupKey,                // Group identifier for SQL output
    2939             :                          std::string_view const customUnitName,  // the custom name for the units from EMS definition of units
    2940             :                          OutputProcessor::ReportFreq freq        // Internal use -- causes reporting at this frequency
    2941             : )
    2942             : {
    2943             : 
    2944             :     // SUBROUTINE INFORMATION:
    2945             :     //       AUTHOR         Linda K. Lawrie
    2946             :     //       DATE WRITTEN   December 1998
    2947             :     //       MODIFIED       January 2001; Implement Meters
    2948             :     //                      August 2008; Implement SQL output
    2949             :     //       RE-ENGINEERED  na
    2950             : 
    2951             :     // PURPOSE OF THIS SUBROUTINE:
    2952             :     // This subroutine sets up the variable data structure that will be used
    2953             :     // to track values of the output variables of EnergyPlus.
    2954             : 
    2955             :     // METHODOLOGY EMPLOYED:
    2956             :     // Pointers (as pointers), pointers (as indices), and lots of other KEWL data stuff.
    2957             : 
    2958             :     using namespace OutputProcessor;
    2959             : 
    2960     6135374 :     auto &op = state.dataOutputProcessor;
    2961             : 
    2962     6135374 :     if (!op->OutputInitialized) InitializeOutput(state);
    2963             : 
    2964     6135374 :     std::vector<int> reqVarNums;
    2965             : 
    2966             :     // Determine whether to Report or not
    2967     6135374 :     CheckReportVariable(state, name, key, reqVarNums);
    2968     6135374 :     if (reqVarNums.empty()) {
    2969     6077635 :         reqVarNums.push_back(-1);
    2970             :     }
    2971             : 
    2972             :     // Is this redundant with CheckReportVariable?
    2973     6135374 :     bool const ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
    2974             : 
    2975     6076504 :     bool OnMeter = (resource != Constant::eResource::Invalid) || (endUseCat != EndUseCat::Invalid) || (!EndUseSub.empty()) ||
    2976    12211878 :                    (group != Group::Invalid) || (!zone.empty()) || (!spaceType.empty());
    2977             : 
    2978     6135374 :     if (OnMeter && store == StoreType::Average) {
    2979           0 :         ShowSevereError(state, "Meters can only be \"Summed\" variables");
    2980           0 :         ShowContinueError(state, fmt::format("..reference variable={}:{}", key, name));
    2981           0 :         OnMeter = false;
    2982             :     }
    2983             : 
    2984     6135374 :     int ddOutVarNum = AddDDOutVar(state, name, timeStep, store, VariableType::Real, units, customUnitName);
    2985     6135374 :     auto *ddOutVar = op->ddOutVars[ddOutVarNum];
    2986             : 
    2987     6135374 :     ++op->NumOfRVariable_Setup;
    2988             : 
    2989             :     // If we add any output variables here at all, the first one will be at this index
    2990     6135374 :     int firstAddedOutVarNum = (int)op->outVars.size();
    2991             : 
    2992     6135374 :     op->NumTotalRVariable += reqVarNums.size();
    2993             : 
    2994     6135374 :     if (!OnMeter && !ThisOneOnTheList) return;
    2995             : 
    2996      174761 :     if (store == StoreType::Sum) ++op->NumOfRVariable_Sum;
    2997      174761 :     if (OnMeter) ++op->NumOfRVariable_Meter;
    2998             : 
    2999      351178 :     for (int reqVarNum : reqVarNums) {
    3000             : 
    3001      176417 :         ++op->NumOfRVariable;
    3002             : 
    3003      176417 :         OutVarReal *var = new OutVarReal;
    3004      176417 :         op->outVars.push_back(var);
    3005             : 
    3006             :         // Link this keyed variable to the dictionary entry
    3007      176417 :         ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
    3008      176417 :         var->ddVarNum = ddOutVarNum;
    3009             : 
    3010      176417 :         var->varType = VariableType::Real;
    3011      176417 :         var->timeStepType = timeStep;
    3012      176417 :         var->storeType = store;
    3013      176417 :         var->name = name;
    3014      176417 :         var->nameUC = Util::makeUPPER(var->name);
    3015      176417 :         var->key = key;
    3016      176417 :         var->keyUC = Util::makeUPPER(key);
    3017      176417 :         var->keyColonName = fmt::format("{}:{}", key, name);
    3018      176417 :         var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
    3019      176417 :         var->units = units;
    3020      176417 :         if (units == Constant::Units::customEMS) var->unitNameCustomEMS = customUnitName;
    3021      176417 :         var->freq = freq;
    3022      176417 :         var->SchedPtr = 0;
    3023      176417 :         var->ReportID = ++op->ReportNumberCounter;
    3024      176417 :         var->Which = &ActualVariable;
    3025      176417 :         var->ZoneMult = ZoneMult;
    3026      176417 :         var->ZoneListMult = ZoneListMult;
    3027      176417 :         var->indexGroupKey = indexGroupKey;
    3028      176417 :         var->indexGroup = timeStepTypeNames[(int)var->timeStepType];
    3029             : 
    3030             :         // This is only done for the first variable in the list.  It
    3031             :         // could be moved out of this loop entirely but then some
    3032             :         // numberings in unit tests would not line up
    3033      176417 :         if (OnMeter) {
    3034       58870 :             AttachMeters(state, units, resource, endUseCat, EndUseSub, group, zone, spaceType, firstAddedOutVarNum);
    3035       58870 :             OnMeter = false;
    3036             :         }
    3037             : 
    3038             :         // This is a dummy variable that is not being reported, it is only being used to feed a particular meter.
    3039      176417 :         if (reqVarNum == -1) continue;
    3040             : 
    3041       59395 :         var->Report = true;
    3042             : 
    3043             :         // freq != ReportFreq::Hour
    3044       59395 :         if (freq == ReportFreq::Hour) {
    3045       59394 :             var->freq = op->reqVars[reqVarNum]->freq;
    3046       59394 :             var->SchedPtr = op->reqVars[reqVarNum]->SchedPtr;
    3047             :         }
    3048             : 
    3049       59395 :         var->writeReportDictionaryItem(state);
    3050      174761 :     }
    3051             : 
    3052     6135374 : } // SetupOutputVariable()
    3053             : 
    3054      266155 : void SetupOutputVariable(EnergyPlusData &state,
    3055             :                          std::string_view const name,                // String Name of variable
    3056             :                          Constant::Units const units,                // Actual units corresponding to the actual variable
    3057             :                          int &ActualVariable,                        // Actual Variable, used to set up pointer
    3058             :                          OutputProcessor::TimeStepType timeStepType, // Zone, HeatBalance=1, HVAC, System, Plant=2
    3059             :                          OutputProcessor::StoreType storeType,       // State, Average=1, NonState, Sum=2
    3060             :                          std::string const &key,                     // Associated Key for this variable
    3061             :                          int const indexGroupKey,                    // Group identifier for SQL output
    3062             :                          OutputProcessor::ReportFreq freq            // Internal use -- causes reporting at this freqency
    3063             : )
    3064             : {
    3065             : 
    3066             :     // SUBROUTINE INFORMATION:
    3067             :     //       AUTHOR         Linda K. Lawrie
    3068             :     //       DATE WRITTEN   December 1998
    3069             :     //       MODIFIED       August 2008; Added SQL output capability
    3070             :     //       RE-ENGINEERED  na
    3071             : 
    3072             :     // PURPOSE OF THIS SUBROUTINE:
    3073             :     // This subroutine sets up the variable data structure that will be used
    3074             :     // to track values of the output variables of EnergyPlus.
    3075             : 
    3076             :     // METHODOLOGY EMPLOYED:
    3077             :     // Pointers (as pointers), pointers (as indices), and lots of other KEWL data stuff <-- LOL
    3078             : 
    3079             :     // Using/Aliasing
    3080             :     using namespace OutputProcessor;
    3081             : 
    3082             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3083      266155 :     auto &op = state.dataOutputProcessor;
    3084             : 
    3085      266155 :     if (!op->OutputInitialized) InitializeOutput(state);
    3086             : 
    3087             :     // Determine whether to Report or not
    3088      266155 :     std::vector<int> reqVarNums;
    3089      266155 :     CheckReportVariable(state, name, key, reqVarNums);
    3090      266155 :     if (reqVarNums.empty()) {
    3091      264528 :         reqVarNums.push_back(-1);
    3092             :     }
    3093             : 
    3094             :     // DataOutputs::OutputVariablesForSimulation is case-insentitive
    3095      266155 :     int ddOutVarNum = AddDDOutVar(state, name, timeStepType, storeType, VariableType::Integer, units);
    3096      266155 :     auto *ddOutVar = op->ddOutVars[ddOutVarNum];
    3097             : 
    3098      266155 :     ++op->NumOfIVariable_Setup;
    3099             : 
    3100      266155 :     op->NumTotalIVariable += (!reqVarNums.empty()) ? reqVarNums.size() : 1;
    3101      266155 :     bool ThisOneOnTheList = DataOutputs::FindItemInVariableList(state, key, name);
    3102      266155 :     if (!ThisOneOnTheList) return;
    3103             : 
    3104        3866 :     if (storeType == StoreType::Sum) {
    3105         145 :         ++op->NumOfIVariable_Sum;
    3106             :     }
    3107             : 
    3108        7752 :     for (int reqVarNum : reqVarNums) {
    3109             : 
    3110        3886 :         ++op->NumOfIVariable;
    3111             : 
    3112        3886 :         OutVarInt *var = new OutVarInt;
    3113        3886 :         op->outVars.push_back(var);
    3114             :         // Add to ddVar key list
    3115        3886 :         ddOutVar->keyOutVarNums.push_back(op->outVars.size() - 1);
    3116             : 
    3117        3886 :         var->varType = VariableType::Integer;
    3118        3886 :         var->timeStepType = timeStepType;
    3119        3886 :         var->storeType = storeType;
    3120        3886 :         var->name = name;
    3121        3886 :         var->nameUC = Util::makeUPPER(var->name);
    3122        3886 :         var->key = key;
    3123        3886 :         var->keyUC = Util::makeUPPER(key);
    3124        3886 :         var->keyColonName = fmt::format("{}:{}", key, name);
    3125        3886 :         var->keyColonNameUC = Util::makeUPPER(var->keyColonName);
    3126        3886 :         var->units = units;
    3127        3886 :         var->ReportID = ++op->ReportNumberCounter;
    3128        3886 :         var->Which = &ActualVariable;
    3129        3886 :         var->indexGroupKey = -1;
    3130             : 
    3131        3886 :         if (reqVarNum == -1) continue;
    3132             : 
    3133        1647 :         var->Report = true;
    3134             : 
    3135        1647 :         if (freq != ReportFreq::Hour) {
    3136           0 :             var->freq = freq;
    3137           0 :             var->SchedPtr = 0;
    3138             :         } else {
    3139        1647 :             var->freq = op->reqVars[reqVarNum]->freq;
    3140        1647 :             var->SchedPtr = op->reqVars[reqVarNum]->SchedPtr;
    3141             :         }
    3142             : 
    3143        1647 :         var->writeReportDictionaryItem(state);
    3144        3866 :     }
    3145      266155 : } // SetOutputVariable()
    3146             : 
    3147     1104688 : void UpdateDataandReport(EnergyPlusData &state, OutputProcessor::TimeStepType const t_TimeStepTypeKey) // What kind of data to update (Zone, HVAC)
    3148             : {
    3149             : 
    3150             :     // SUBROUTINE INFORMATION:
    3151             :     //       AUTHOR         Linda K. Lawrie
    3152             :     //       DATE WRITTEN   December 1998
    3153             :     //       MODIFIED       January 2001; Resolution integrated at the Zone TimeStep intervals
    3154             :     //       MODIFIED       August 2008; Added SQL output capability
    3155             :     //       RE-ENGINEERED  na
    3156             : 
    3157             :     // PURPOSE OF THIS SUBROUTINE:
    3158             :     // This subroutine writes the actual report variable (for user requested
    3159             :     // Report Variables) strings to the standard output file.
    3160             : 
    3161             :     // Using/Aliasing
    3162             :     using namespace OutputProcessor;
    3163             :     using General::EncodeMonDayHrMin;
    3164             :     using ScheduleManager::GetCurrentScheduleValue;
    3165             : 
    3166             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3167     1104688 :     bool TimePrint(true);        // True if the time needs to be printed
    3168     1104688 :     bool EndTimeStepFlag(false); // True when it's the end of the Zone Time Step
    3169     1104688 :     auto &op = state.dataOutputProcessor;
    3170     1104688 :     auto &rf = state.dataResultsFramework->resultsFramework;
    3171             : 
    3172     1104688 :     if (t_TimeStepTypeKey != TimeStepType::Zone && t_TimeStepTypeKey != TimeStepType::System) {
    3173           0 :         ShowFatalError(state, "Invalid reporting requested -- UpdateDataAndReport");
    3174             :     }
    3175             : 
    3176             :     // Basic record keeping and report out if "detailed"
    3177     1104688 :     Real64 StartMinute = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // StartMinute for UpdateData call
    3178     1104688 :     op->TimeValue[(int)t_TimeStepTypeKey].CurMinute += (*op->TimeValue[(int)t_TimeStepTypeKey].TimeStep) * 60.0;
    3179     1723712 :     if (t_TimeStepTypeKey == TimeStepType::System &&
    3180      619024 :         (op->TimeValue[(int)TimeStepType::System].CurMinute == op->TimeValue[(int)TimeStepType::Zone].CurMinute)) {
    3181           0 :         EndTimeStepFlag = true;
    3182     1104688 :     } else if (t_TimeStepTypeKey == TimeStepType::Zone) {
    3183      485664 :         EndTimeStepFlag = true;
    3184             :     } else {
    3185      619024 :         EndTimeStepFlag = false;
    3186             :     }
    3187     1104688 :     Real64 MinuteNow = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute; // What minute it is now
    3188             : 
    3189             :     int MDHM; // Month,Day,Hour,Minute
    3190     1104688 :     EncodeMonDayHrMin(MDHM, state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, int(MinuteNow));
    3191     1104688 :     TimePrint = true;
    3192             : 
    3193     1104688 :     Real64 rxTime = (MinuteNow - StartMinute) /
    3194     1104688 :                     double(state.dataGlobal->MinutesPerTimeStep); // (MinuteNow-StartMinute)/REAL(MinutesPerTimeStep,r64) - for execution time
    3195             : 
    3196     1104688 :     if (rf->timeSeriesEnabled()) {
    3197             :         // R and I data frames for TimeStepType::TimeStepZone
    3198        6037 :         if (!rf->detailedTSData[(int)t_TimeStepTypeKey].variablesScanned()) {
    3199          20 :             rf->initializeTSDataFrame(ReportFreq::EachCall, op->outVars, t_TimeStepTypeKey);
    3200             :         }
    3201             :     }
    3202             : 
    3203     1104688 :     if (rf->timeSeriesEnabled()) {
    3204       12074 :         rf->detailedTSData[(int)t_TimeStepTypeKey].newRow(state.dataEnvrn->Month,
    3205        6037 :                                                           state.dataEnvrn->DayOfMonth,
    3206        6037 :                                                           state.dataGlobal->HourOfDay,
    3207        6037 :                                                           op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
    3208        6037 :                                                           state.dataGlobal->CalendarYear);
    3209             :     }
    3210             : 
    3211             :     // Main "Record Keeping" Loops for R and I variables
    3212   176784155 :     for (auto *var : op->outVars) {
    3213   175679467 :         if (var->timeStepType != t_TimeStepTypeKey) continue;
    3214             : 
    3215    92030794 :         Real64 value = (var->varType == VariableType::Real) ? *(dynamic_cast<OutVarReal *>(var))->Which : *(dynamic_cast<OutVarInt *>(var))->Which;
    3216             : 
    3217    92030794 :         var->Stored = true;
    3218             : 
    3219    92030794 :         if (var->storeType == StoreType::Average) {
    3220    45817836 :             Real64 CurVal = value * rxTime;
    3221             :             // TODO: Is this correct? Integer logic is different
    3222    45817836 :             if (var->varType == VariableType::Real) {
    3223    44126828 :                 if (value > var->MaxValue) {
    3224     2530214 :                     var->MaxValue = value;
    3225     2530214 :                     var->maxValueDate = MDHM;
    3226             :                 }
    3227    44126828 :                 if (value < var->MinValue) {
    3228     1667040 :                     var->MinValue = value;
    3229     1667040 :                     var->minValueDate = MDHM;
    3230             :                 }
    3231             :             } else { // var->varType == VariableType::Integer
    3232     1691008 :                 if (CurVal > var->MaxValue) {
    3233        5390 :                     var->MaxValue = CurVal;
    3234        5390 :                     var->maxValueDate = MDHM;
    3235             :                 }
    3236     1691008 :                 if (CurVal < var->MinValue) {
    3237        4911 :                     var->MinValue = CurVal;
    3238        4911 :                     var->minValueDate = MDHM;
    3239             :                 }
    3240             :             }
    3241    45817836 :             var->TSValue += CurVal;
    3242    45817836 :             var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
    3243             :         } else {
    3244    46212958 :             if (value > var->MaxValue) {
    3245     1390078 :                 var->MaxValue = value;
    3246     1390078 :                 var->maxValueDate = MDHM;
    3247             :             }
    3248    46212958 :             if (value < var->MinValue) {
    3249      179099 :                 var->MinValue = value;
    3250      179099 :                 var->minValueDate = MDHM;
    3251             :             }
    3252    46212958 :             var->TSValue += value;
    3253    46212958 :             var->EITSValue = var->TSValue; // CR - 8481 fix - 09/06/2011
    3254             :         }
    3255             : 
    3256             :         // End of "record keeping"  Report if applicable
    3257    92030794 :         if (!var->Report) continue;
    3258             : 
    3259    44821332 :         if (var->SchedPtr > 0 && GetCurrentScheduleValue(state, var->SchedPtr) == 0.0) continue;
    3260             : 
    3261    44676016 :         var->tsStored = true;
    3262    44676016 :         if (!var->thisTSStored) {
    3263    34678808 :             ++var->thisTSCount;
    3264    34678808 :             var->thisTSStored = true;
    3265             :         }
    3266             : 
    3267    44676016 :         if (var->freq != ReportFreq::EachCall) continue;
    3268             : 
    3269      624174 :         if (TimePrint) {
    3270       25045 :             if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
    3271        2853 :                 std::abs(op->LEndMin - op->TimeValue[(int)t_TimeStepTypeKey].CurMinute) > 0.001) {
    3272       19339 :                 int CurDayType = state.dataEnvrn->DayOfWeek;
    3273       19339 :                 if (state.dataEnvrn->HolidayIndex > 0) {
    3274       19339 :                     CurDayType = state.dataEnvrn->HolidayIndex;
    3275             :                 }
    3276      116034 :                 WriteTimeStampFormatData(state,
    3277       19339 :                                          state.files.eso,
    3278             :                                          ReportFreq::EachCall,
    3279       19339 :                                          op->freqStampReportNums[(int)ReportFreq::TimeStep],
    3280       19339 :                                          state.dataGlobal->DayOfSimChr,
    3281             :                                          true,
    3282       19339 :                                          state.dataEnvrn->Month,
    3283       19339 :                                          state.dataEnvrn->DayOfMonth,
    3284       19339 :                                          state.dataGlobal->HourOfDay,
    3285       19339 :                                          op->TimeValue[(int)t_TimeStepTypeKey].CurMinute,
    3286             :                                          StartMinute,
    3287       19339 :                                          state.dataEnvrn->DSTIndicator,
    3288       19339 :                                          ScheduleManager::dayTypeNames[CurDayType]);
    3289       19339 :                 op->LHourP = state.dataGlobal->HourOfDay;
    3290       19339 :                 op->LStartMin = StartMinute;
    3291       19339 :                 op->LEndMin = op->TimeValue[(int)t_TimeStepTypeKey].CurMinute;
    3292             :             }
    3293       22192 :             TimePrint = false;
    3294             :         }
    3295             : 
    3296      624174 :         WriteNumericData(state, var->ReportID, value);
    3297      624174 :         ++state.dataGlobal->StdOutputRecordCount;
    3298             : 
    3299      624174 :         if (rf->timeSeriesEnabled()) {
    3300       12412 :             rf->detailedTSData[(int)t_TimeStepTypeKey].pushVariableValue(var->ReportID, value);
    3301             :         }
    3302     1104688 :     } // for (var)
    3303             : 
    3304     1586409 :     if (t_TimeStepTypeKey == TimeStepType::System) return; // All other stuff happens at the "zone" time step call to this routine.
    3305             : 
    3306             :     // TimeStep Block (Report on Zone TimeStep)
    3307             : 
    3308      485664 :     if (EndTimeStepFlag) {
    3309      485664 :         if (rf->timeSeriesEnabled()) {
    3310        2304 :             if (!rf->freqTSData[(int)ReportFreq::TimeStep].variablesScanned()) {
    3311        2304 :                 rf->initializeTSDataFrame(ReportFreq::TimeStep, op->outVars);
    3312             :             }
    3313        4608 :             rf->freqTSData[(int)ReportFreq::TimeStep].newRow(state.dataEnvrn->Month,
    3314        2304 :                                                              state.dataEnvrn->DayOfMonth,
    3315        2304 :                                                              state.dataGlobal->HourOfDay,
    3316        2304 :                                                              op->TimeValue[(int)TimeStepType::Zone].CurMinute,
    3317        2304 :                                                              state.dataGlobal->CalendarYear);
    3318             :         }
    3319             : 
    3320             :         // Update meters on the TimeStep  (Zone)
    3321      485664 :         if (op->meterValues.capacity() > 0) {
    3322    49605456 :             for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
    3323    49119792 :                 auto *meter = op->meters[iMeter];
    3324    49119792 :                 if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
    3325   186225792 :                     for (int srcVarNum : meter->srcVarNums) {
    3326   137183520 :                         auto *var = op->outVars[srcVarNum];
    3327             :                         // Separate the Zone variables from the HVAC variables using TimeStepType
    3328   137183520 :                         if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3329             :                         // Add to the total all of the appropriate variables, make sure to use var->TSValue and not *var->Which
    3330   137183520 :                         op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
    3331    49042272 :                     }
    3332    49119792 :                 } else if (meter->type == MeterType::CustomDec) {
    3333       77520 :                     auto *decMeter = op->meters[meter->decMeterNum];
    3334      888048 :                     for (int srcVarNum : decMeter->srcVarNums) {
    3335      810528 :                         auto *var = op->outVars[srcVarNum];
    3336      810528 :                         if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3337      810528 :                         op->meterValues[iMeter] += var->TSValue * var->ZoneMult * var->ZoneListMult;
    3338       77520 :                     }
    3339      463008 :                     for (int srcVarNum : meter->srcVarNums) {
    3340      385488 :                         auto *var = op->outVars[srcVarNum];
    3341      385488 :                         if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3342      385488 :                         op->meterValues[iMeter] -= var->TSValue * var->ZoneMult * var->ZoneListMult;
    3343       77520 :                     }
    3344             :                 } else {
    3345           0 :                     assert(false);
    3346             :                 }
    3347             :             } // for (iMeter)
    3348             :         }     // if (op->meterValues.capacity() > 0)
    3349             : 
    3350    73406784 :         for (auto *var : op->outVars) {
    3351    72921120 :             if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3352             : 
    3353    72921120 :             bool ReportNow = true;
    3354    72921120 :             if (var->SchedPtr > 0) ReportNow = (GetCurrentScheduleValue(state, var->SchedPtr) != 0.0); // SetReportNow(RVar%SchedPtr)
    3355    72921120 :             if (!ReportNow || !var->Report) {
    3356    38242312 :                 var->TSValue = 0.0;
    3357             :             }
    3358             :             //        IF (RVar%StoreType == AveragedVar) THEN
    3359             :             //          RVar%Value=RVar%Value+RVar%TSValue/NumOfTimeStepInHour
    3360             :             //        ELSE
    3361    72921120 :             var->Value += var->TSValue;
    3362             :             //        ENDIF
    3363             : 
    3364    72921120 :             if (!ReportNow || !var->Report) continue;
    3365             : 
    3366    34678808 :             if (var->freq == ReportFreq::TimeStep) {
    3367    13153584 :                 if (TimePrint) {
    3368      284556 :                     if (op->LHourP != state.dataGlobal->HourOfDay || std::abs(op->LStartMin - StartMinute) > 0.001 ||
    3369          60 :                         std::abs(op->LEndMin - op->TimeValue[(int)var->timeStepType].CurMinute) > 0.001) {
    3370      284436 :                         int CurDayType = state.dataEnvrn->DayOfWeek;
    3371      284436 :                         if (state.dataEnvrn->HolidayIndex > 0) {
    3372      101940 :                             CurDayType = state.dataEnvrn->HolidayIndex;
    3373             :                         }
    3374     1706616 :                         WriteTimeStampFormatData(state,
    3375      284436 :                                                  state.files.eso,
    3376             :                                                  ReportFreq::EachCall,
    3377      284436 :                                                  op->freqStampReportNums[(int)ReportFreq::TimeStep],
    3378      284436 :                                                  state.dataGlobal->DayOfSimChr,
    3379             :                                                  true,
    3380      284436 :                                                  state.dataEnvrn->Month,
    3381      284436 :                                                  state.dataEnvrn->DayOfMonth,
    3382      284436 :                                                  state.dataGlobal->HourOfDay,
    3383      284436 :                                                  op->TimeValue[(int)var->timeStepType].CurMinute,
    3384             :                                                  StartMinute,
    3385      284436 :                                                  state.dataEnvrn->DSTIndicator,
    3386      284436 :                                                  ScheduleManager::dayTypeNames[CurDayType]);
    3387      284436 :                         op->LHourP = state.dataGlobal->HourOfDay;
    3388      284436 :                         op->LStartMin = StartMinute;
    3389      284436 :                         op->LEndMin = op->TimeValue[(int)var->timeStepType].CurMinute;
    3390             :                     }
    3391      284496 :                     TimePrint = false;
    3392             :                 } // if (TimePrint)
    3393             : 
    3394    13153584 :                 WriteNumericData(state, var->ReportID, var->TSValue);
    3395    13153584 :                 ++state.dataGlobal->StdOutputRecordCount;
    3396             : 
    3397    13153584 :                 if (rf->timeSeriesEnabled()) {
    3398        7488 :                     rf->freqTSData[(int)ReportFreq::TimeStep].pushVariableValue(var->ReportID, var->TSValue);
    3399             :                 }
    3400             :             }
    3401    34678808 :             var->TSValue = 0.0;
    3402    34678808 :             var->thisTSStored = false;
    3403      485664 :         } // for (var)
    3404             : 
    3405      485664 :         UpdateMeters(state, MDHM);
    3406             : 
    3407      485664 :         ReportTSMeters(state, StartMinute, op->TimeValue[(int)TimeStepType::Zone].CurMinute, TimePrint, TimePrint);
    3408             : 
    3409             :     } // TimeStep Block
    3410             : 
    3411             :     // Hour Block
    3412      485664 :     if (state.dataGlobal->EndHourFlag) {
    3413       94632 :         if (op->freqTrackingVariables[(int)ReportFreq::Hour]) {
    3414       40920 :             int CurDayType = state.dataEnvrn->DayOfWeek;
    3415       40920 :             if (state.dataEnvrn->HolidayIndex > 0) {
    3416       23232 :                 CurDayType = state.dataEnvrn->HolidayIndex;
    3417             :             }
    3418      204600 :             WriteTimeStampFormatData(state,
    3419       40920 :                                      state.files.eso,
    3420             :                                      ReportFreq::Hour,
    3421       40920 :                                      op->freqStampReportNums[(int)ReportFreq::TimeStep],
    3422       40920 :                                      state.dataGlobal->DayOfSimChr,
    3423             :                                      true,
    3424       40920 :                                      state.dataEnvrn->Month,
    3425       40920 :                                      state.dataEnvrn->DayOfMonth,
    3426       40920 :                                      state.dataGlobal->HourOfDay,
    3427             :                                      -1, // EndMinute
    3428             :                                      -1, // startMinute
    3429       40920 :                                      state.dataEnvrn->DSTIndicator,
    3430       40920 :                                      ScheduleManager::dayTypeNames[CurDayType]);
    3431       40920 :             TimePrint = false;
    3432             :         }
    3433             : 
    3434       94632 :         if (rf->timeSeriesEnabled()) {
    3435         480 :             if (!rf->freqTSData[(int)ReportFreq::Hour].variablesScanned()) {
    3436         480 :                 rf->initializeTSDataFrame(ReportFreq::Hour, op->outVars);
    3437             :             }
    3438         960 :             rf->freqTSData[(int)ReportFreq::Hour].newRow(
    3439         480 :                 state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
    3440             :         }
    3441             : 
    3442       94632 :         op->TimeValue[(int)TimeStepType::Zone].CurMinute = 0.0;
    3443       94632 :         op->TimeValue[(int)TimeStepType::System].CurMinute = 0.0;
    3444             : 
    3445    14955048 :         for (auto *var : op->outVars) {
    3446             : 
    3447    14860416 :             if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3448             : 
    3449             :             //        ReportNow=.TRUE.
    3450             :             //        IF (RVar%SchedPtr > 0) &
    3451             :             //          ReportNow=(GetCurrentScheduleValue(state, RVar%SchedPtr) /= 0.0)  !SetReportNow(RVar%SchedPtr)
    3452             : 
    3453             :             //        IF (ReportNow) THEN
    3454    14860416 :             if (var->tsStored) {
    3455     6640454 :                 if (var->storeType == StoreType::Average) {
    3456     6024038 :                     var->Value /= double(var->thisTSCount);
    3457             :                 }
    3458     6640454 :                 if (var->Report && var->freq == ReportFreq::Hour && var->Stored) {
    3459     2806124 :                     WriteNumericData(state, var->ReportID, var->Value);
    3460     2806124 :                     ++state.dataGlobal->StdOutputRecordCount;
    3461     2806124 :                     var->Stored = false;
    3462             :                     // add time series value for hourly to data store
    3463     2806124 :                     if (rf->timeSeriesEnabled()) {
    3464       19308 :                         rf->freqTSData[(int)ReportFreq::Hour].pushVariableValue(var->ReportID, var->Value);
    3465             :                     }
    3466             :                 }
    3467     6640454 :                 var->StoreValue += var->Value;
    3468     6640454 :                 ++var->NumStored;
    3469             :             }
    3470    14860416 :             var->tsStored = false;
    3471    14860416 :             var->thisTSStored = false;
    3472    14860416 :             var->thisTSCount = 0;
    3473    14860416 :             var->Value = 0.0;
    3474       94632 :         } // for (var)
    3475             : 
    3476       94632 :         ReportMeters(state, ReportFreq::Hour, TimePrint);
    3477             :     } // Hour Block
    3478             : 
    3479      485664 :     if (!state.dataGlobal->EndHourFlag) return;
    3480             : 
    3481             :     // Day Block
    3482       94632 :     if (state.dataGlobal->EndDayFlag) {
    3483        3943 :         if (op->freqTrackingVariables[(int)ReportFreq::Day]) {
    3484          96 :             int CurDayType = state.dataEnvrn->DayOfWeek;
    3485          96 :             if (state.dataEnvrn->HolidayIndex > 0) {
    3486          92 :                 CurDayType = state.dataEnvrn->HolidayIndex;
    3487             :             }
    3488         384 :             WriteTimeStampFormatData(state,
    3489          96 :                                      state.files.eso,
    3490             :                                      ReportFreq::Day,
    3491          96 :                                      op->freqStampReportNums[(int)ReportFreq::Day],
    3492          96 :                                      state.dataGlobal->DayOfSimChr,
    3493             :                                      true,
    3494          96 :                                      state.dataEnvrn->Month,
    3495          96 :                                      state.dataEnvrn->DayOfMonth,
    3496             :                                      -1, // Hour
    3497             :                                      -1, // EndMinute
    3498             :                                      -1, // StartMinute
    3499          96 :                                      state.dataEnvrn->DSTIndicator,
    3500          96 :                                      ScheduleManager::dayTypeNames[CurDayType]);
    3501          96 :             TimePrint = false;
    3502             :         }
    3503        3943 :         if (rf->timeSeriesEnabled()) {
    3504          20 :             if (!rf->freqTSData[(int)ReportFreq::Day].variablesScanned()) {
    3505          20 :                 rf->initializeTSDataFrame(ReportFreq::Day, op->outVars);
    3506             :             }
    3507          40 :             rf->freqTSData[(int)ReportFreq::Day].newRow(
    3508          20 :                 state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
    3509             :         }
    3510             : 
    3511        3943 :         op->NumHoursInMonth += 24;
    3512      623127 :         for (auto *var : op->outVars) {
    3513      619184 :             if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3514      619184 :             var->writeOutput(state, ReportFreq::Day);
    3515        3943 :         }
    3516             : 
    3517        3943 :         ReportMeters(state, ReportFreq::Day, TimePrint);
    3518             : 
    3519             :     } // Day Block
    3520             : 
    3521             :     // Only continue if EndDayFlag is set
    3522       94632 :     if (!state.dataGlobal->EndDayFlag) return;
    3523             : 
    3524             :     // Month Block
    3525        3943 :     if (state.dataEnvrn->EndMonthFlag || state.dataGlobal->EndEnvrnFlag) {
    3526        1764 :         if (op->freqTrackingVariables[(int)ReportFreq::Month]) {
    3527         231 :             WriteTimeStampFormatData(state,
    3528         231 :                                      state.files.eso,
    3529             :                                      ReportFreq::Month,
    3530         231 :                                      op->freqStampReportNums[(int)ReportFreq::Month],
    3531         231 :                                      state.dataGlobal->DayOfSimChr,
    3532             :                                      true,
    3533         231 :                                      state.dataEnvrn->Month);
    3534         231 :             TimePrint = false;
    3535             :         }
    3536             : 
    3537        1764 :         if (rf->timeSeriesEnabled()) {
    3538          20 :             if (!rf->freqTSData[(int)ReportFreq::Month].variablesScanned()) {
    3539          20 :                 rf->initializeTSDataFrame(ReportFreq::Month, op->outVars);
    3540             :             }
    3541          40 :             rf->freqTSData[(int)ReportFreq::Month].newRow(
    3542          20 :                 state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
    3543             :         }
    3544             : 
    3545        1764 :         op->NumHoursInSim += op->NumHoursInMonth;
    3546        1764 :         state.dataEnvrn->EndMonthFlag = false;
    3547      428783 :         for (auto *var : op->outVars) {
    3548      427019 :             if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3549      427019 :             var->writeOutput(state, ReportFreq::Month);
    3550        1764 :         }
    3551             : 
    3552        1764 :         ReportMeters(state, ReportFreq::Month, TimePrint);
    3553             : 
    3554        1764 :         op->NumHoursInMonth = 0;
    3555             :     } // Month Block
    3556             : 
    3557             :     // Sim/Environment Block
    3558        3943 :     if (state.dataGlobal->EndEnvrnFlag) {
    3559        1696 :         if (op->freqTrackingVariables[(int)ReportFreq::Simulation]) {
    3560          87 :             WriteTimeStampFormatData(state,
    3561          87 :                                      state.files.eso,
    3562             :                                      ReportFreq::Simulation,
    3563          87 :                                      op->freqStampReportNums[(int)ReportFreq::Simulation],
    3564          87 :                                      state.dataGlobal->DayOfSimChr,
    3565             :                                      true);
    3566          87 :             TimePrint = false;
    3567             :         }
    3568             : 
    3569        1696 :         if (rf->timeSeriesEnabled()) {
    3570          20 :             if (!rf->freqTSData[(int)ReportFreq::Simulation].variablesScanned()) {
    3571          20 :                 rf->initializeTSDataFrame(ReportFreq::Simulation, op->outVars);
    3572             :             }
    3573          40 :             rf->freqTSData[(int)ReportFreq::Simulation].newRow(
    3574          20 :                 state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
    3575             :         }
    3576             : 
    3577      422711 :         for (auto *var : op->outVars) {
    3578      421015 :             if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3579      421015 :             var->writeOutput(state, ReportFreq::Simulation);
    3580        1696 :         }
    3581             : 
    3582        1696 :         ReportMeters(state, ReportFreq::Simulation, TimePrint);
    3583             : 
    3584        1696 :         op->NumHoursInSim = 0;
    3585             :     }
    3586             : 
    3587             :     // Yearly Block
    3588        3943 :     if (state.dataEnvrn->EndYearFlag) {
    3589           6 :         if (op->freqTrackingVariables[(int)ReportFreq::Year]) {
    3590           0 :             WriteYearlyTimeStamp(state, state.files.eso, op->freqStampReportNums[(int)ReportFreq::Year], state.dataGlobal->CalendarYearChr, true);
    3591           0 :             TimePrint = false;
    3592             :         }
    3593           6 :         if (rf->timeSeriesEnabled()) {
    3594           0 :             if (!rf->freqTSData[(int)ReportFreq::Year].variablesScanned()) {
    3595           0 :                 rf->initializeTSDataFrame(ReportFreq::Year, op->outVars);
    3596             :             }
    3597           0 :             rf->freqTSData[(int)ReportFreq::Year].newRow(
    3598           0 :                 state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay, 0, state.dataGlobal->CalendarYear);
    3599             :         }
    3600             : 
    3601         540 :         for (auto *var : op->outVars) {
    3602         534 :             if (var->timeStepType != TimeStepType::Zone && var->timeStepType != TimeStepType::System) continue;
    3603         534 :             var->writeOutput(state, ReportFreq::Year);
    3604           6 :         }
    3605             : 
    3606           6 :         ReportMeters(state, ReportFreq::Year, TimePrint);
    3607             : 
    3608           6 :         state.dataGlobal->CalendarYear += 1;
    3609           6 :         state.dataGlobal->CalendarYearChr = fmt::to_string(state.dataGlobal->CalendarYear);
    3610             :     }
    3611             : } // UpdateDataandReport()
    3612             : 
    3613         794 : void GenOutputVariablesAuditReport(EnergyPlusData &state)
    3614             : {
    3615             : 
    3616             :     // SUBROUTINE INFORMATION:
    3617             :     //       AUTHOR         Linda Lawrie
    3618             :     //       DATE WRITTEN   February 2000
    3619             : 
    3620             :     // PURPOSE OF THIS SUBROUTINE:
    3621             :     // This subroutine reports (to the .err file) any report variables
    3622             :     // which were requested but not "setup" during the run.  These will
    3623             :     // either be items that were not used in the IDF file or misspellings
    3624             :     // of report variable names.
    3625             : 
    3626             :     // METHODOLOGY EMPLOYED:
    3627             :     // Use flagged data structure in OutputProcessor.
    3628             : 
    3629             :     // Using/Aliasing
    3630             :     using namespace OutputProcessor;
    3631             : 
    3632             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3633         794 :     auto &op = state.dataOutputProcessor;
    3634             : 
    3635             :     static constexpr std::array<std::string_view, (int)ReportFreq::Num> localReportFreqNames = {
    3636             :         "Detailed",  // EachCall  // For some reason, this is "Detailed" here and "Each Call" in other places
    3637             :         "TimeStep",  // TimeStep
    3638             :         "Hourly",    // Hourly
    3639             :         "Daily",     // Daily
    3640             :         "Monthly",   // Monthly
    3641             :         "RunPeriod", // Simulation
    3642             :         "Annual"     // Yearly
    3643             :     };
    3644             : 
    3645       22329 :     for (auto *reqVar : op->reqVars) {
    3646       21535 :         if (reqVar->Used) continue;
    3647          79 :         if (reqVar->key.empty()) reqVar->key = "*";
    3648          79 :         if (has(reqVar->name, "OPAQUE SURFACE INSIDE FACE CONDUCTION") && !state.dataGlobal->DisplayAdvancedReportVariables &&
    3649           0 :             !state.dataOutputProcessor->OpaqSurfWarned) {
    3650           0 :             ShowWarningMessage(state, R"(Variables containing "Opaque Surface Inside Face Conduction" are now "advanced" variables.)");
    3651           0 :             ShowContinueError(state, "You must enter the \"Output:Diagnostics,DisplayAdvancedReportVariables;\" statement to view.");
    3652           0 :             ShowContinueError(state, "First, though, read cautionary statements in the \"InputOutputReference\" document.");
    3653           0 :             state.dataOutputProcessor->OpaqSurfWarned = true;
    3654             :         }
    3655          79 :         if (!state.dataOutputProcessor->Rept) {
    3656          19 :             ShowWarningMessage(state, "The following Report Variables were requested but not generated -- check.rdd file");
    3657          19 :             ShowContinueError(state, "Either the IDF did not contain these elements, the variable name is misspelled,");
    3658          19 :             ShowContinueError(state,
    3659             :                               "or the requested variable is an advanced output which requires Output : Diagnostics, DisplayAdvancedReportVariables;");
    3660          19 :             state.dataOutputProcessor->Rept = true;
    3661             :         }
    3662          79 :         ShowMessage(state, format("Key={}, VarName={}, Frequency={}", reqVar->key, reqVar->name, localReportFreqNames[(int)reqVar->freq]));
    3663         794 :     }
    3664         794 : } // GenOutputVariablesAuditReport()
    3665             : 
    3666         795 : void UpdateMeterReporting(EnergyPlusData &state)
    3667             : {
    3668             : 
    3669             :     // SUBROUTINE INFORMATION:
    3670             :     //       AUTHOR         Linda Lawrie
    3671             :     //       DATE WRITTEN   January 2001
    3672             :     //       MODIFIED       February 2007 -- add cumulative meter reporting
    3673             :     //                      January 2012 -- add predefined tabular meter reporting
    3674             :     //       RE-ENGINEERED  na
    3675             : 
    3676             :     // PURPOSE OF THIS SUBROUTINE:
    3677             :     // This subroutine is called at the end of the first HVAC iteration and
    3678             :     // sets up the reporting for the Energy Meters.  It also may show a fatal error
    3679             :     // if errors occurred during initial SetupOutputVariable processing.  It "gets"
    3680             :     // the Report Meter input:
    3681             :     // Report Meter,
    3682             :     //        \memo Meters requested here show up on eplusout.eso and eplusout.mtr
    3683             :     //   A1 , \field Meter_Name
    3684             :     //        \required-field
    3685             :     //        \note Form is EnergyUseType:..., e.g. Electricity:* for all Electricity meters
    3686             :     //        \note or EndUse:..., e.g. InteriorLights:* for all interior lights
    3687             :     //        \note Report MeterFileOnly puts results on the eplusout.mtr file only
    3688             :     //   A2 ; \field Reporting_Frequency
    3689             :     //        \type choice
    3690             :     //        \key timestep
    3691             :     //        \note timestep refers to the zone timestep/timestep in hour value
    3692             :     //        \note runperiod, environment, and annual are the same
    3693             :     //        \key hourly
    3694             :     //        \key daily
    3695             :     //        \key monthly
    3696             :     //        \key runperiod
    3697             :     //        \key environment
    3698             :     //        \key annual
    3699             :     //        \note runperiod, environment, and annual are synonymous
    3700             :     // Report MeterFileOnly,
    3701             :     //        \memo same reporting as Report Meter -- goes to eplusout.mtr only
    3702             :     //   A1 , \field Meter_Name
    3703             :     //        \required-field
    3704             :     //        \note Form is EnergyUseType:..., e.g. Electricity:* for all Electricity meters
    3705             :     //        \note or EndUse:..., e.g. InteriorLights:* for all interior lights
    3706             :     //        \note Report MeterFileOnly puts results on the eplusout.mtr file only
    3707             :     //   A2 ; \field Reporting_Frequency
    3708             :     //        \type choice
    3709             :     //        \key timestep
    3710             :     //        \note timestep refers to the zone timestep/timestep in hour value
    3711             :     //        \note runperiod, environment, and annual are the same
    3712             :     //        \key hourly
    3713             :     //        \key daily
    3714             :     //        \key monthly
    3715             :     //        \key runperiod
    3716             :     //        \key environment
    3717             :     //        \key annual
    3718             :     //        \note runperiod, environment, and annual are synonymous
    3719             : 
    3720             :     // Using/Aliasing
    3721             :     using namespace OutputProcessor;
    3722             : 
    3723             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3724             :     int Loop;
    3725         795 :     Array1D_string Alphas(2);
    3726         795 :     Array1D<Real64> Numbers(1);
    3727             :     int NumAlpha;
    3728             :     int NumNumbers;
    3729             :     int IOStat;
    3730             :     int NumReqMeters;
    3731             :     int NumReqMeterFOs;
    3732             : 
    3733         795 :     bool ErrorsFound(false); // If errors detected in input
    3734         795 :     auto &op = state.dataOutputProcessor;
    3735         795 :     auto &ipsc = state.dataIPShortCut;
    3736             : 
    3737         795 :     GetCustomMeterInput(state, ErrorsFound);
    3738         795 :     if (ErrorsFound) {
    3739           0 :         op->ErrorsLogged = true;
    3740             :     }
    3741             : 
    3742             :     // Helper lambda to locate a meter index from its name. Returns a negative value if not found
    3743             :     auto setupMeterFromMeterName = // (AUTO_OK_LAMBDA)
    3744       20473 :         [&state](std::string &name, ReportFreq freq, bool MeterFileOnlyIndicator, bool CumulativeIndicator) -> bool {
    3745        6792 :         bool result = false;
    3746             : 
    3747        6792 :         size_t varnameLen = index(name, '[');
    3748        6792 :         if (varnameLen != std::string::npos) {
    3749           0 :             name.erase(varnameLen);
    3750             :         }
    3751             : 
    3752        6792 :         auto &op = state.dataOutputProcessor;
    3753             : 
    3754        6792 :         std::string::size_type wildCardPosition = index(name, '*');
    3755             : 
    3756        6792 :         if (wildCardPosition == std::string::npos) {
    3757        6768 :             if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
    3758        6705 :                 SetInitialMeterReportingAndOutputNames(state, found->second, MeterFileOnlyIndicator, freq, CumulativeIndicator);
    3759        6705 :                 result = true;
    3760        6768 :             }
    3761             :         } else { // Wildcard input
    3762          24 :             std::string nameSubstr = name.substr(0, wildCardPosition);
    3763        2395 :             for (int iMeter = 0; iMeter < (int)op->meters.size(); ++iMeter) {
    3764        2371 :                 if (Util::SameString(op->meters[iMeter]->Name.substr(0, wildCardPosition), nameSubstr)) {
    3765         184 :                     SetInitialMeterReportingAndOutputNames(state, iMeter, MeterFileOnlyIndicator, freq, CumulativeIndicator);
    3766         184 :                     result = true;
    3767             :                 }
    3768             :             }
    3769          24 :         }
    3770             : 
    3771        6792 :         return result;
    3772         795 :     };
    3773             : 
    3774         795 :     ipsc->cCurrentModuleObject = "Output:Meter";
    3775         795 :     NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    3776             : 
    3777        1630 :     for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
    3778             : 
    3779        2505 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3780         835 :                                                                  ipsc->cCurrentModuleObject,
    3781             :                                                                  Loop,
    3782             :                                                                  Alphas,
    3783             :                                                                  NumAlpha,
    3784             :                                                                  Numbers,
    3785             :                                                                  NumNumbers,
    3786             :                                                                  IOStat,
    3787         835 :                                                                  ipsc->lNumericFieldBlanks,
    3788         835 :                                                                  ipsc->lAlphaFieldBlanks,
    3789         835 :                                                                  ipsc->cAlphaFieldNames,
    3790         835 :                                                                  ipsc->cNumericFieldNames);
    3791             : 
    3792         835 :         bool meterFileOnlyIndicator = false;
    3793         835 :         bool cumulativeIndicator = false;
    3794         835 :         ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
    3795             : 
    3796         835 :         if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
    3797          16 :             ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
    3798             :         }
    3799             :     }
    3800             : 
    3801         795 :     ipsc->cCurrentModuleObject = "Output:Meter:MeterFileOnly";
    3802         795 :     NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    3803        6747 :     for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
    3804             : 
    3805       17856 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3806        5952 :                                                                  ipsc->cCurrentModuleObject,
    3807             :                                                                  Loop,
    3808             :                                                                  Alphas,
    3809             :                                                                  NumAlpha,
    3810             :                                                                  Numbers,
    3811             :                                                                  NumNumbers,
    3812             :                                                                  IOStat,
    3813        5952 :                                                                  ipsc->lNumericFieldBlanks,
    3814        5952 :                                                                  ipsc->lAlphaFieldBlanks,
    3815        5952 :                                                                  ipsc->cAlphaFieldNames,
    3816        5952 :                                                                  ipsc->cNumericFieldNames);
    3817             : 
    3818        5952 :         bool meterFileOnlyIndicator = true;
    3819        5952 :         bool cumulativeIndicator = false;
    3820        5952 :         ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
    3821        5952 :         if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
    3822          47 :             ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
    3823             :         }
    3824             :     }
    3825             : 
    3826         795 :     ipsc->cCurrentModuleObject = "Output:Meter:Cumulative";
    3827         795 :     NumReqMeters = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    3828             : 
    3829         796 :     for (Loop = 1; Loop <= NumReqMeters; ++Loop) {
    3830             : 
    3831           3 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3832           1 :                                                                  ipsc->cCurrentModuleObject,
    3833             :                                                                  Loop,
    3834             :                                                                  Alphas,
    3835             :                                                                  NumAlpha,
    3836             :                                                                  Numbers,
    3837             :                                                                  NumNumbers,
    3838             :                                                                  IOStat,
    3839           1 :                                                                  ipsc->lNumericFieldBlanks,
    3840           1 :                                                                  ipsc->lAlphaFieldBlanks,
    3841           1 :                                                                  ipsc->cAlphaFieldNames,
    3842           1 :                                                                  ipsc->cNumericFieldNames);
    3843             : 
    3844           1 :         bool meterFileOnlyIndicator = false;
    3845           1 :         bool cumulativeIndicator = true;
    3846           1 :         ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
    3847           1 :         if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
    3848           0 :             ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
    3849             :         }
    3850             :     }
    3851             : 
    3852         795 :     ipsc->cCurrentModuleObject = "Output:Meter:Cumulative:MeterFileOnly";
    3853         795 :     NumReqMeterFOs = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, ipsc->cCurrentModuleObject);
    3854         799 :     for (Loop = 1; Loop <= NumReqMeterFOs; ++Loop) {
    3855             : 
    3856          12 :         state.dataInputProcessing->inputProcessor->getObjectItem(state,
    3857           4 :                                                                  ipsc->cCurrentModuleObject,
    3858             :                                                                  Loop,
    3859             :                                                                  Alphas,
    3860             :                                                                  NumAlpha,
    3861             :                                                                  Numbers,
    3862             :                                                                  NumNumbers,
    3863             :                                                                  IOStat,
    3864           4 :                                                                  ipsc->lNumericFieldBlanks,
    3865           4 :                                                                  ipsc->lAlphaFieldBlanks,
    3866           4 :                                                                  ipsc->cAlphaFieldNames,
    3867           4 :                                                                  ipsc->cNumericFieldNames);
    3868             : 
    3869           4 :         bool meterFileOnlyIndicator = true;
    3870           4 :         bool cumulativeIndicator = true;
    3871           4 :         ReportFreq freq = determineFrequency(state, Util::makeUPPER(Alphas(2)));
    3872           4 :         if (!setupMeterFromMeterName(Alphas(1), freq, meterFileOnlyIndicator, cumulativeIndicator)) {
    3873           0 :             ShowWarningError(state, format("{}: invalid {}=\"{}\" - not found.", ipsc->cCurrentModuleObject, ipsc->cAlphaFieldNames(1), Alphas(1)));
    3874             :         }
    3875             :     }
    3876             : 
    3877         795 :     ReportMeterDetails(state);
    3878             : 
    3879         795 :     if (op->ErrorsLogged) {
    3880           0 :         ShowFatalError(state, "UpdateMeterReporting: Previous Meter Specification errors cause program termination.");
    3881             :     }
    3882             : 
    3883         795 :     op->meterValues.resize(op->meters.size(), 0.0);
    3884         795 :     std::fill(op->meterValues.begin(), op->meterValues.end(), 0.0);
    3885         795 : } // UpdateMeterReporting()
    3886             : 
    3887        6889 : void SetInitialMeterReportingAndOutputNames(EnergyPlusData &state,
    3888             :                                             int const WhichMeter,              // Which meter number
    3889             :                                             bool const MeterFileOnlyIndicator, // true if this is a meter file only reporting
    3890             :                                             OutputProcessor::ReportFreq freq,  // at what frequency is the meter reported
    3891             :                                             bool const CumulativeIndicator     // true if this is a Cumulative meter reporting
    3892             : )
    3893             : {
    3894             : 
    3895             :     // SUBROUTINE INFORMATION:
    3896             :     //       AUTHOR         Linda Lawrie
    3897             :     //       DATE WRITTEN   February 2007
    3898             : 
    3899             :     // PURPOSE OF THIS SUBROUTINE:
    3900             :     // Set values and output initial names to output files.
    3901             : 
    3902             :     // Using/Aliasing
    3903             :     using namespace OutputProcessor;
    3904             : 
    3905             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    3906        6889 :     auto &op = state.dataOutputProcessor;
    3907             : 
    3908        6889 :     auto *meter = op->meters[WhichMeter];
    3909        6889 :     auto &period = meter->periods[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq];
    3910        6889 :     if (!CumulativeIndicator) {
    3911        6884 :         if (MeterFileOnlyIndicator && period.Rpt) {
    3912           6 :             ShowWarningError(state,
    3913           6 :                              format(R"(Output:Meter:MeterFileOnly requested for "{}" ({}), already on "Output:Meter". Will report to both {} and {})",
    3914           3 :                                     meter->Name,
    3915           3 :                                     reportFreqNames[(freq == ReportFreq::EachCall) ? (int)ReportFreq::TimeStep : (int)freq],
    3916           6 :                                     state.files.eso.filePath.filename(),
    3917           6 :                                     state.files.mtr.filePath.filename()));
    3918             :         }
    3919        6884 :         if (!period.Rpt) {
    3920        6881 :             period.Rpt = true;
    3921        6881 :             if (MeterFileOnlyIndicator)
    3922        5902 :                 period.RptFO = true;
    3923             :             else
    3924         979 :                 op->freqTrackingVariables[(int)freq] = true;
    3925             :             // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
    3926        6881 :             meter->indexGroup = DetermineIndexGroupFromMeterGroup(meter);
    3927        6881 :             WriteMeterDictionaryItem(
    3928        6881 :                 state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, MeterFileOnlyIndicator);
    3929             :         }
    3930             :     } else { // !CumulativeIndicator
    3931           5 :         if (MeterFileOnlyIndicator && period.accRpt) {
    3932           0 :             ShowWarningError(state,
    3933           0 :                              format("Output:Meter:MeterFileOnly requested for \"Cumulative {}\" (TimeStep), already on \"Output:Meter\". "
    3934             :                                     "Will report to both {} and {}",
    3935           0 :                                     meter->Name,
    3936           0 :                                     state.files.eso.filePath.filename(),
    3937           0 :                                     state.files.mtr.filePath.filename()));
    3938             :         }
    3939             : 
    3940           5 :         if (!period.accRpt) {
    3941           5 :             period.accRpt = true;
    3942           5 :             if (MeterFileOnlyIndicator) period.accRptFO = true;
    3943             :             // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
    3944           5 :             meter->indexGroup = DetermineIndexGroupFromMeterGroup(op->meters[WhichMeter]);
    3945           5 :             WriteMeterDictionaryItem(
    3946           5 :                 state, freq, StoreType::Sum, period.accRptNum, meter->indexGroup, meter->Name, meter->units, true, MeterFileOnlyIndicator);
    3947             :         }
    3948             :     } // if (CumulativeIndicator)
    3949        6889 : } // SetInitialMeterReportingAndOutputNames()
    3950             : 
    3951      365814 : int GetMeterIndex(EnergyPlusData &state, std::string const &name)
    3952             : {
    3953             : 
    3954             :     // FUNCTION INFORMATION:
    3955             :     //       AUTHOR         Linda K. Lawrie
    3956             :     //       DATE WRITTEN   August 2002
    3957             : 
    3958             :     // PURPOSE OF THIS FUNCTION:
    3959             :     // This function returns a index to the meter "number" (aka assigned report number)
    3960             :     // for the meter name.  If none active for this run, a zero is returned.  This is used later to
    3961             :     // obtain a meter "value".
    3962             : 
    3963      365814 :     auto &op = state.dataOutputProcessor;
    3964             : 
    3965      365814 :     auto found = op->meterMap.find(name);
    3966      731628 :     return (found != op->meterMap.end()) ? found->second : -1;
    3967      365814 : } // GetMeterIndex()
    3968             : 
    3969           0 : Constant::eResource GetMeterResourceType(EnergyPlusData &state, int const MeterNumber) // Which Meter Number (from GetMeterIndex)
    3970             : {
    3971             : 
    3972             :     // FUNCTION INFORMATION:
    3973             :     //       AUTHOR         Linda K. Lawrie
    3974             :     //       DATE WRITTEN   August 2002
    3975             : 
    3976             :     // Using/Aliasing
    3977             :     using namespace OutputProcessor;
    3978             : 
    3979           0 :     return (MeterNumber != -1) ? state.dataOutputProcessor->meters[MeterNumber]->resource : Constant::eResource::Invalid;
    3980             : } // GetMeterResourceType()
    3981             : 
    3982    12602102 : Real64 GetCurrentMeterValue(EnergyPlusData &state, int const MeterNumber) // Which Meter Number (from GetMeterIndex)
    3983             : {
    3984             : 
    3985             :     // FUNCTION INFORMATION:
    3986             :     //       AUTHOR         Linda K. Lawrie
    3987             :     //       DATE WRITTEN   August 2002
    3988             : 
    3989             :     // PURPOSE OF THIS FUNCTION:
    3990             :     // This function returns the current meter value (timestep) for the meter number indicated.
    3991             : 
    3992    12602102 :     return (MeterNumber != -1) ? state.dataOutputProcessor->meters[MeterNumber]->CurTSValue : 0.0;
    3993             : } // GetCurrentMeterValue()
    3994             : 
    3995   276425812 : Real64 GetInstantMeterValue(EnergyPlusData &state,
    3996             :                             int const meterNum,                              // Which Meter Number (from GetMeterIndex)
    3997             :                             OutputProcessor::TimeStepType const timeStepType // Whether this is zone of HVAC
    3998             : )
    3999             : {
    4000             : 
    4001             :     // FUNCTION INFORMATION:
    4002             :     //       AUTHOR         Richard Liesen
    4003             :     //       DATE WRITTEN   February 2003
    4004             : 
    4005             :     // PURPOSE OF THIS FUNCTION:
    4006             :     // This function returns the Instantaneous meter value (timestep) for the meter number indicated
    4007             :     //  using TimeStepType to differentiate between Zone and HVAC values.
    4008             : 
    4009             :     // Using/Aliasing
    4010             :     using namespace OutputProcessor;
    4011             : 
    4012   276425812 :     Real64 InstantMeterValue = 0.0;
    4013             : 
    4014   276425812 :     if (meterNum == -1) return InstantMeterValue;
    4015             : 
    4016    66378326 :     auto &op = state.dataOutputProcessor;
    4017    66378326 :     auto *meter = op->meters[meterNum];
    4018             : 
    4019    66378326 :     if (meter->type == MeterType::Normal || meter->type == MeterType::Custom) {
    4020  1819969605 :         for (int srcVarNum : meter->srcVarNums) {
    4021  1753728495 :             auto *var = op->outVars[srcVarNum];
    4022             :             // Separate the Zone variables from the HVAC variables using TimeStepType
    4023  1753728495 :             if (var->timeStepType != timeStepType) continue;
    4024             : 
    4025   878688769 :             auto *rVar = dynamic_cast<OutVarReal *>(var);
    4026   878688769 :             assert(rVar != nullptr);
    4027             :             // Add to the total all of the appropriate variables
    4028   878688769 :             InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
    4029    66241110 :         }
    4030             : 
    4031    66378326 :     } else if (meter->type == MeterType::CustomDec) {
    4032      137216 :         auto *decMeter = op->meters[meter->decMeterNum];
    4033     4494286 :         for (int srcVarNum : decMeter->srcVarNums) {
    4034     4357070 :             auto *var = op->outVars[srcVarNum];
    4035     4357070 :             if (var->timeStepType != timeStepType) continue;
    4036     2178535 :             auto *rVar = dynamic_cast<OutVarReal *>(var);
    4037     2178535 :             assert(rVar != nullptr);
    4038     2178535 :             InstantMeterValue += (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
    4039      137216 :         }
    4040      530656 :         for (int srcVarNum : meter->srcVarNums) {
    4041      393440 :             auto *var = op->outVars[srcVarNum];
    4042      393440 :             if (var->timeStepType != timeStepType) continue;
    4043      196720 :             auto *rVar = dynamic_cast<OutVarReal *>(var);
    4044      196720 :             assert(rVar != nullptr);
    4045      196720 :             InstantMeterValue -= (*rVar->Which) * rVar->ZoneMult * rVar->ZoneListMult;
    4046      137216 :         }
    4047             :     } else {
    4048           0 :         assert(false);
    4049             :     }
    4050             : 
    4051    66378326 :     return InstantMeterValue;
    4052             : } // GetInstantMeterValue()
    4053             : 
    4054   109913922 : Real64 GetInternalVariableValue(EnergyPlusData &state,
    4055             :                                 OutputProcessor::VariableType const varType, // 1=integer, 2=real, 3=meter
    4056             :                                 int const keyVarIndex                        // Array index
    4057             : )
    4058             : {
    4059             :     // FUNCTION INFORMATION:
    4060             :     //       AUTHOR         Linda K. Lawrie
    4061             :     //       DATE WRITTEN   December 2000
    4062             :     //       MODIFIED       August 2003, M. J. Witte
    4063             : 
    4064             :     // PURPOSE OF THIS FUNCTION:
    4065             :     // This function returns the current value of the Internal Variable assigned to
    4066             :     // the varType and keyVarIndex.  Values may be accessed for REAL(r64) and integer
    4067             :     // report variables and meter variables.  The variable type (varType) may be
    4068             :     // determined by calling subroutine and GetVariableKeyCountandType.  The
    4069             :     // index (keyVarIndex) may be determined by calling subroutine GetVariableKeys.
    4070             : 
    4071             :     // METHODOLOGY EMPLOYED:
    4072             :     // Uses Internal OutputProcessor data structure to return value.
    4073             : 
    4074             :     // Using/Aliasing
    4075             :     using namespace OutputProcessor;
    4076             :     using ScheduleManager::GetCurrentScheduleValue;
    4077             : 
    4078             :     // Return value
    4079             :     Real64 resultVal; // value returned
    4080             : 
    4081   109913922 :     auto &op = state.dataOutputProcessor;
    4082             : 
    4083             :     // Select based on variable type:  integer, real, or meter
    4084   109913922 :     if (varType == VariableType::Invalid) { // Variable not a found variable
    4085    12295428 :         resultVal = 0.0;
    4086    97618494 :     } else if (varType == VariableType::Integer || varType == VariableType::Real) {
    4087    97225894 :         if (keyVarIndex < 0 || keyVarIndex >= (int)op->outVars.size()) {
    4088           0 :             ShowFatalError(state, "GetInternalVariableValue: passed variable index beyond range of array.");
    4089           0 :             ShowContinueError(state, format("Index = {} Number of variables = {}", keyVarIndex, op->outVars.size()));
    4090             :         }
    4091             : 
    4092             :         // must use %Which, %Value is always zero if variable is not a requested report variable
    4093   194427653 :         resultVal = (varType == VariableType::Integer) ? (double)*(dynamic_cast<OutVarInt *>(op->outVars[keyVarIndex]))->Which
    4094    97201759 :                                                        : (double)*(dynamic_cast<OutVarReal *>(op->outVars[keyVarIndex]))->Which;
    4095      392600 :     } else if (varType == VariableType::Meter) {
    4096      392600 :         resultVal = GetCurrentMeterValue(state, keyVarIndex);
    4097           0 :     } else if (varType == VariableType::Schedule) {
    4098           0 :         resultVal = GetCurrentScheduleValue(state, keyVarIndex);
    4099             :     } else {
    4100           0 :         resultVal = 0.0;
    4101             :     }
    4102             : 
    4103   109913922 :     return resultVal;
    4104             : } // GetInternalVariableValue()
    4105             : 
    4106      360376 : Real64 GetInternalVariableValueExternalInterface(EnergyPlusData &state,
    4107             :                                                  OutputProcessor::VariableType const varType, // 1=integer, 2=REAL(r64), 3=meter
    4108             :                                                  int const keyVarIndex                        // Array index
    4109             : )
    4110             : {
    4111             :     // FUNCTION INFORMATION:
    4112             :     //       AUTHOR         Thierry S. Nouidui
    4113             :     //       DATE WRITTEN   August 2011
    4114             : 
    4115             :     // PURPOSE OF THIS FUNCTION:
    4116             :     // This function returns the last zone-timestep value of the Internal Variable assigned to
    4117             :     // the varType and keyVarIndex.  Values may be accessed for REAL(r64) and integer
    4118             :     // report variables and meter variables.  The variable type (varType) may be
    4119             :     // determined by calling subroutine and GetVariableKeyCountandType.  The
    4120             :     // index (keyVarIndex) may be determined by calling subroutine GetVariableKeys.
    4121             : 
    4122             :     // METHODOLOGY EMPLOYED:
    4123             :     // Uses Internal OutputProcessor data structure to return value.
    4124             : 
    4125             :     // Using/Aliasing
    4126             :     using namespace OutputProcessor;
    4127             :     using ScheduleManager::GetCurrentScheduleValue;
    4128             : 
    4129             :     // Return value
    4130             :     Real64 resultVal; // value returned
    4131             : 
    4132      360376 :     auto &op = state.dataOutputProcessor;
    4133             : 
    4134             :     // Select based on variable type:  integer, REAL(r64), or meter
    4135      360376 :     if (varType == VariableType::Invalid) { // Variable not a found variable
    4136           0 :         resultVal = 0.0;
    4137      360376 :     } else if (varType == VariableType::Integer || varType == VariableType::Real) {
    4138      360376 :         if (keyVarIndex < 0 || keyVarIndex >= (int)op->outVars.size()) {
    4139           0 :             ShowFatalError(state, "GetInternalVariableValueExternalInterface: passed index beyond range of array.");
    4140             :         }
    4141             : 
    4142      360376 :         resultVal = (double)op->outVars[keyVarIndex]->EITSValue;
    4143           0 :     } else if (varType == VariableType::Meter) {
    4144           0 :         resultVal = GetCurrentMeterValue(state, keyVarIndex);
    4145           0 :     } else if (varType == VariableType::Schedule) {
    4146           0 :         resultVal = GetCurrentScheduleValue(state, keyVarIndex);
    4147             :     } else {
    4148           0 :         resultVal = 0.0;
    4149             :     }
    4150             : 
    4151      360376 :     return resultVal;
    4152             : } // GetInternalVariableValueExternalInterface()
    4153             : 
    4154       33978 : int GetNumMeteredVariables(EnergyPlusData &state,
    4155             :                            [[maybe_unused]] std::string const &ComponentType, // Given Component Type
    4156             :                            std::string const &ComponentName                   // Given Component Name (user defined)
    4157             : )
    4158             : {
    4159             : 
    4160             :     // FUNCTION INFORMATION:
    4161             :     //       AUTHOR         Linda Lawrie
    4162             :     //       DATE WRITTEN   May 2005
    4163             : 
    4164             :     // PURPOSE OF THIS FUNCTION:
    4165             :     // This function counts the number of metered variables associated with the
    4166             :     // given ComponentType/Name.   This resultant number would then be used to
    4167             :     // allocate arrays for a call the GetMeteredVariables routine.
    4168             : 
    4169             :     // FUNCTION LOCAL VARIABLE DECLARATIONS:
    4170       33978 :     int NumVariables = 0;
    4171       33978 :     auto &op = state.dataOutputProcessor;
    4172             : 
    4173    37961819 :     for (auto *var : op->outVars) {
    4174             :         //    Pos=INDEX(RVariableTypes(Loop)%VarName,':')
    4175             :         //    IF (ComponentName /= RVariableTypes(Loop)%VarNameUC(1:Pos-1)) CYCLE
    4176    37927841 :         if (var->varType != OutputProcessor::VariableType::Real) continue;
    4177    37037229 :         if (ComponentName != var->keyUC) continue;
    4178       64516 :         if (!var->meterNums.empty()) {
    4179       38972 :             ++NumVariables;
    4180             :         }
    4181       33978 :     }
    4182       33978 :     return NumVariables;
    4183             : } // GetNumMeteredVariables()
    4184             : 
    4185       17455 : int GetMeteredVariables(EnergyPlusData &state,
    4186             :                         std::string const &ComponentName,                 // Given Component Name (user defined)
    4187             :                         Array1D<OutputProcessor::MeteredVar> &meteredVars // Variable Types (1=integer, 2=real, 3=meter)
    4188             : )
    4189             : {
    4190             : 
    4191             :     // SUBROUTINE INFORMATION:
    4192             :     //       AUTHOR         Linda Lawrie
    4193             :     //       DATE WRITTEN   May 2005
    4194             :     //       MODIFIED       Jason DeGraw 2/12/2020, de-optionalized
    4195             :     //       RE-ENGINEERED  na
    4196             : 
    4197             :     // PURPOSE OF THIS SUBROUTINE:
    4198             :     // This routine gets the variable names and other associated information
    4199             :     // for metered variables associated with the given ComponentType/Name.
    4200             : 
    4201             :     // Using/Aliasing
    4202             :     using namespace OutputProcessor;
    4203             : 
    4204             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4205             :     int NumVariables;
    4206       17455 :     auto &op = state.dataOutputProcessor;
    4207             : 
    4208       17455 :     NumVariables = 0;
    4209             : 
    4210    21785735 :     for (int iVar = 0; iVar < (int)op->outVars.size(); ++iVar) {
    4211             :         //    Pos=INDEX(RVariableTypes(Loop)%VarName,':')
    4212             :         //    IF (ComponentName /= RVariableTypes(Loop)%VarNameUC(1:Pos-1)) CYCLE
    4213             : 
    4214    21768280 :         auto *var = op->outVars[iVar];
    4215    21768280 :         if (var->varType != VariableType::Real) continue;
    4216    21264537 :         if (ComponentName != var->keyUC) continue;
    4217       62322 :         if (var->meterNums.empty()) continue;
    4218             : 
    4219       38972 :         auto &meteredVar = meteredVars(++NumVariables);
    4220             : 
    4221       38972 :         meteredVar.num = iVar;
    4222       38972 :         meteredVar.varType = VariableType::Real;
    4223       38972 :         meteredVar.timeStepType = var->timeStepType;
    4224       38972 :         meteredVar.units = var->units;
    4225             : 
    4226       38972 :         meteredVar.resource = op->meters[var->meterNums[0]]->resource;
    4227       38972 :         meteredVar.name = var->keyColonNameUC;
    4228             : 
    4229       38972 :         bool foundEndUse = false;
    4230       38972 :         bool foundGroup = false;
    4231      116916 :         for (int meterNum : var->meterNums) {
    4232      116884 :             auto *meter = op->meters[meterNum];
    4233      116884 :             if (!foundEndUse && meter->endUseCat != EndUseCat::Invalid) {
    4234       38940 :                 meteredVar.endUseCat = meter->endUseCat;
    4235       38940 :                 foundEndUse = true;
    4236             :             }
    4237             : 
    4238      116884 :             if (!foundGroup && meter->group != Group::Invalid) {
    4239       38972 :                 meteredVar.group = meter->group;
    4240       38972 :                 foundGroup = true;
    4241             :             }
    4242             : 
    4243      116884 :             if (foundEndUse && foundGroup) break;
    4244       38972 :         }
    4245             : 
    4246       38972 :         meteredVar.rptNum = var->ReportID;
    4247             :     }
    4248       17455 :     return NumVariables;
    4249             : } // GetMeteredVariables()
    4250             : 
    4251       58011 : void GetVariableKeyCountandType(EnergyPlusData &state,
    4252             :                                 std::string const &name, // Standard variable name
    4253             :                                 int &numKeys,            // Number of keys found
    4254             :                                 OutputProcessor::VariableType &varType,
    4255             :                                 OutputProcessor::StoreType &storeType,       // Variable  is Averaged=1 or Summed=2
    4256             :                                 OutputProcessor::TimeStepType &timeStepType, // Variable time step is Zone=1 or HVAC=2
    4257             :                                 Constant::Units &units                       // Units enumeration
    4258             : )
    4259             : {
    4260             : 
    4261             :     // SUBROUTINE INFORMATION:
    4262             :     //       AUTHOR         Michael J. Witte
    4263             :     //       DATE WRITTEN   August 2003
    4264             : 
    4265             :     // PURPOSE OF THIS SUBROUTINE:
    4266             :     // This subroutine returns the variable TYPE (Real, integer, meter, schedule, etc.)
    4267             :     // (varType) whether it is an averaged or summed variable (varAvgSum),
    4268             :     // whether it is a zone or HVAC time step (varStepType),
    4269             :     // and the number of keynames for a given report variable or report meter name
    4270             :     // (varName).  The variable type (varType) and number of keys (numKeys) are
    4271             :     // used when calling subroutine GetVariableKeys to obtain a list of the
    4272             :     // keynames for a particular variable and a corresponding list of indexes.
    4273             : 
    4274             :     // METHODOLOGY EMPLOYED:
    4275             :     // Uses Internal OutputProcessor data structure to search for varName
    4276             :     // in each of the three output data arrays:
    4277             :     //       RVariableTypes - real report variables
    4278             :     //       IVariableTypes - integer report variables
    4279             :     //       EnergyMeters   - report meters (via GetMeterIndex function)
    4280             :     //       Schedules      - specific schedule values
    4281             :     // When the variable is found, the variable type (varType) is set and the
    4282             :     // number of associated keys is counted.
    4283             : 
    4284             :     // Using/Aliasing
    4285             :     using namespace OutputProcessor;
    4286             :     using ScheduleManager::GetScheduleIndex;
    4287             :     using ScheduleManager::GetScheduleType;
    4288             : 
    4289             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4290       58011 :     auto &op = state.dataOutputProcessor;
    4291             : 
    4292       58011 :     varType = VariableType::Invalid;
    4293       58011 :     numKeys = 0;
    4294       58011 :     storeType = StoreType::Average;
    4295       58011 :     timeStepType = TimeStepType::Zone;
    4296       58011 :     units = Constant::Units::None; // Why is this None and not Invalid?
    4297             : 
    4298       58011 :     std::string nameUC = Util::makeUPPER(name);
    4299             : 
    4300             :     // Search Variable List First
    4301       58011 :     if (auto found = op->ddOutVarMap.find(nameUC); found != op->ddOutVarMap.end()) {
    4302        1797 :         auto const *ddOutVar = op->ddOutVars[found->second];
    4303        1797 :         varType = ddOutVar->variableType;
    4304        1797 :         storeType = ddOutVar->storeType;
    4305        1797 :         timeStepType = ddOutVar->timeStepType;
    4306        1797 :         units = ddOutVar->units;
    4307        1797 :         numKeys = ddOutVar->keyOutVarNums.size();
    4308             : 
    4309       56214 :     } else if (auto found = op->meterMap.find(nameUC); found != op->meterMap.end()) {
    4310             :         // Search Meters if not found in integers or reals
    4311             :         // Use the GetMeterIndex function
    4312             :         // Meters do not have keys, so only one will be found
    4313         294 :         int meterNum = found->second;
    4314         294 :         numKeys = 1;
    4315         294 :         varType = VariableType::Meter;
    4316         294 :         units = op->meters[meterNum]->units;
    4317         294 :         storeType = StoreType::Sum;
    4318         294 :         timeStepType = TimeStepType::Zone;
    4319             : 
    4320             :     } else {
    4321             :         // Search schedules if not found in integers, reals, or meters
    4322             :         // Use the GetScheduleIndex function
    4323             :         // Schedules do not have keys, so only one will be found
    4324       55920 :         int schedNum = GetScheduleIndex(state, nameUC);
    4325       55920 :         if (schedNum > 0) {
    4326           0 :             numKeys = 1;
    4327           0 :             varType = VariableType::Schedule;
    4328           0 :             units = static_cast<Constant::Units>(getEnumValue(Constant::unitNamesUC, Util::makeUPPER(GetScheduleType(state, schedNum))));
    4329           0 :             storeType = StoreType::Average;
    4330           0 :             timeStepType = TimeStepType::Zone;
    4331             :         }
    4332      114225 :     }
    4333       58011 : } // GetVariableKeyCountandType()
    4334             : 
    4335        2091 : void GetVariableKeys(EnergyPlusData &state,
    4336             :                      std::string const &varName,                  // Standard variable name
    4337             :                      OutputProcessor::VariableType const varType, // 1=integer, 2=real, 3=meter
    4338             :                      Array1D_string &keyNames,
    4339             :                      Array1D_int &keyOutVarNums // Array index for
    4340             : 
    4341             : )
    4342             : {
    4343             : 
    4344             :     // SUBROUTINE INFORMATION:
    4345             :     //       AUTHOR         Michael J. Witte
    4346             :     //       DATE WRITTEN   August 2003
    4347             : 
    4348             :     // PURPOSE OF THIS SUBROUTINE:
    4349             :     // This subroutine returns a list of keynames and indexes associated
    4350             :     // with a particular report variable or report meter name (varName).
    4351             :     // This routine assumes that the variable TYPE (Real, integer, meter, etc.)
    4352             :     // may be determined by calling GetVariableKeyCountandType.  The variable type
    4353             :     // and index can then be used with function GetInternalVariableValue to
    4354             :     // to retrieve the current value of a particular variable/keyname combination.
    4355             : 
    4356             :     // METHODOLOGY EMPLOYED:
    4357             :     // Uses Internal OutputProcessor data structure to search for varName
    4358             :     // and build list of keynames and indexes.  The indexes are the array index
    4359             :     // in the data array for the
    4360             : 
    4361             :     // Using/Aliasing
    4362             :     using namespace OutputProcessor;
    4363             :     using ScheduleManager::GetScheduleIndex;
    4364             : 
    4365             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4366        2091 :     std::string nameUC = Util::makeUPPER(varName);
    4367        2091 :     auto &op = state.dataOutputProcessor;
    4368             : 
    4369             :     // Select based on variable type:  integer, real, or meter
    4370        2091 :     if (varType == VariableType::Integer || varType == VariableType::Real) {
    4371        1797 :         auto found = op->ddOutVarMap.find(nameUC);
    4372        1797 :         if (found == op->ddOutVarMap.end()) return;
    4373             : 
    4374        1797 :         auto const *ddOutVar = op->ddOutVars[found->second];
    4375             : 
    4376        1797 :         if (ddOutVar->keyOutVarNums.size() > size(keyOutVarNums)) {
    4377           0 :             ShowFatalError(state, "Invalid array size in GetVariableKeys");
    4378             :         }
    4379             : 
    4380        1797 :         int iKey = 0;
    4381      147380 :         for (int keyOutVarNum : ddOutVar->keyOutVarNums) {
    4382      145583 :             ++iKey;
    4383      145583 :             keyOutVarNums(iKey) = keyOutVarNum;
    4384      145583 :             keyNames(iKey) = op->outVars[keyOutVarNum]->keyUC;
    4385        1797 :         }
    4386             : 
    4387        3888 :     } else if (varType == VariableType::Meter) { // Meter
    4388             : 
    4389         294 :         if (size(keyOutVarNums) == 0) {
    4390           0 :             ShowFatalError(state, "Invalid array size in GetVariableKeys");
    4391             :         }
    4392         294 :         keyOutVarNums(1) = GetMeterIndex(state, varName);
    4393         294 :         keyNames(1) = "Meter";
    4394             : 
    4395           0 :     } else if (varType == VariableType::Schedule) { // Schedule
    4396             : 
    4397           0 :         if (size(keyOutVarNums) == 0) {
    4398           0 :             ShowFatalError(state, "Invalid array size in GetVariableKeys");
    4399             :         }
    4400           0 :         keyOutVarNums(1) = GetScheduleIndex(state, varName);
    4401           0 :         keyNames(1) = "Environment";
    4402             :     } else {
    4403             :         // do nothing
    4404             :     }
    4405        2091 : } // GetVariableKeys()
    4406             : 
    4407        8373 : bool ReportingThisVariable(EnergyPlusData &state, std::string const &RepVarName)
    4408             : {
    4409             : 
    4410             :     // FUNCTION INFORMATION:
    4411             :     //       AUTHOR         Linda Lawrie
    4412             :     //       DATE WRITTEN   October 2008
    4413             : 
    4414             :     // PURPOSE OF THIS FUNCTION:
    4415             :     // This function scans the report variables and reports back
    4416             :     // if user has requested this variable be reported.
    4417             :     using namespace OutputProcessor;
    4418             : 
    4419        8373 :     auto &op = state.dataOutputProcessor;
    4420             : 
    4421        8373 :     std::string name = Util::makeUPPER(RepVarName);
    4422             : 
    4423      235000 :     for (int iReqVar = 0; iReqVar < (int)op->reqVars.size(); ++iReqVar) {
    4424      226627 :         if (op->reqVars[iReqVar]->name == name) return true;
    4425             :     }
    4426             : 
    4427        8373 :     if (auto found = op->meterMap.find(name); found != op->meterMap.end()) {
    4428        1437 :         auto const *meter = op->meters[found->second];
    4429       10044 :         for (int iFreq = (int)ReportFreq::TimeStep; iFreq < (int)ReportFreq::Num; ++iFreq) {
    4430        8610 :             if (iFreq == (int)ReportFreq::Year) continue;
    4431        7176 :             auto const &period = meter->periods[iFreq];
    4432        7176 :             if (period.Rpt || period.RptFO || period.accRpt || period.accRptFO) return true;
    4433             :         }
    4434        8373 :     }
    4435             : 
    4436        8370 :     return false;
    4437        8373 : } // ReportingThisVariable()
    4438             : 
    4439          61 : void InitPollutionMeterReporting(EnergyPlusData &state, OutputProcessor::ReportFreq freq)
    4440             : {
    4441             : 
    4442             :     // SUBROUTINE INFORMATION:Richard Liesen
    4443             :     //       DATE WRITTEN   July 2002
    4444             : 
    4445             :     // PURPOSE OF THIS SUBROUTINE:
    4446             :     // This subroutine is called at the end of the first HVAC iteration and
    4447             :     // sets up the reporting for the Pollution Meters.
    4448             :     // ReportPollutionOutput,
    4449             :     //   A1 ; \field Reporting_Frequency
    4450             :     //        \type choice
    4451             :     //        \key timestep
    4452             :     //        \key hourly
    4453             :     //        \key daily
    4454             :     //        \key monthly
    4455             :     //        \key runperiod
    4456             :     // METHODOLOGY EMPLOYED:
    4457             :     // The program tries to setup all of the following meters if the Pollution Report is initiated.
    4458             :     //       Electricity:Facility [J]
    4459             :     //       Diesel:Facility [J]
    4460             :     //       DistrictCooling:Facility [J]
    4461             :     //       DistrictHeating:Facility [J]
    4462             :     //       Gas:Facility [J]
    4463             :     //       GASOLINE:Facility [J]
    4464             :     //       COAL:Facility [J]
    4465             :     //       FuelOilNo1:Facility [J]
    4466             :     //       FuelOilNo2:Facility [J]
    4467             :     //       Propane:Facility [J]
    4468             :     //       ElectricityProduced:Facility [J]
    4469             :     //       Pollutant:CO2
    4470             :     //       Pollutant:CO
    4471             :     //       Pollutant:CH4
    4472             :     //       Pollutant:NOx
    4473             :     //       Pollutant:N2O
    4474             :     //       Pollutant:SO2
    4475             :     //       Pollutant:PM
    4476             :     //       Pollutant:PM10
    4477             :     //       Pollutant:PM2.5
    4478             :     //       Pollutant:NH3
    4479             :     //       Pollutant:NMVOC
    4480             :     //       Pollutant:Hg
    4481             :     //       Pollutant:Pb
    4482             :     //       Pollutant:WaterEnvironmentalFactors
    4483             :     //       Pollutant:Nuclear High
    4484             :     //       Pollutant:Nuclear Low
    4485             :     //       Pollutant:Carbon Equivalent
    4486             : 
    4487             :     // Using/Aliasing
    4488             :     using namespace OutputProcessor;
    4489             : 
    4490             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4491             : 
    4492          61 :     auto &op = state.dataOutputProcessor;
    4493             : 
    4494        2989 :     for (int iResource = 0; iResource < (int)Constant::eResource::Num; ++iResource) {
    4495        2928 :         std::string meterName = format("{}:Facility", Constant::eResourceNames[iResource]);
    4496        2928 :         std::string meterNameUC = Util::makeUPPER(meterName);
    4497             : 
    4498        2928 :         auto found = op->meterMap.find(meterNameUC);
    4499        2928 :         if (found == op->meterMap.end()) continue;
    4500             : 
    4501        1656 :         auto *meter = op->meters[found->second];
    4502        1656 :         auto &period = meter->periods[(int)freq];
    4503             : 
    4504             :         // int indexGroupKey = DetermineIndexGroupKeyFromMeterName(state, meter->Name);
    4505        1656 :         meter->indexGroup = DetermineIndexGroupFromMeterGroup(meter);
    4506             : 
    4507             :         // All of the specified meters are checked and the headers printed to the meter file if this
    4508             :         //  has not been done previously
    4509        1656 :         if (!period.Rpt) {
    4510        1592 :             period.Rpt = true;
    4511        1592 :             WriteMeterDictionaryItem(state, freq, StoreType::Sum, period.RptNum, meter->indexGroup, meter->Name, meter->units, false, false);
    4512        1592 :             op->freqTrackingVariables[(int)freq] = true;
    4513             :         }
    4514        5472 :     }
    4515          61 : } // InitPollutionMeterReporting()
    4516             : 
    4517         795 : void ProduceRDDMDD(EnergyPlusData &state)
    4518             : {
    4519             : 
    4520             :     // SUBROUTINE INFORMATION:
    4521             :     //       AUTHOR         Linda Lawrie
    4522             :     //       DATE WRITTEN   March 2009
    4523             : 
    4524             :     // PURPOSE OF THIS SUBROUTINE:
    4525             :     // provide a single call for writing out the Report Data Dictionary and Meter Data Dictionary.
    4526             : 
    4527             :     // Using/Aliasing
    4528             :     using namespace OutputProcessor;
    4529             :     using General::ScanForReports;
    4530             : 
    4531             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4532         795 :     std::string VarOption1;
    4533         795 :     std::string VarOption2;
    4534             :     bool DoReport;
    4535             :     bool SortByName;
    4536             : 
    4537         795 :     auto &op = state.dataOutputProcessor;
    4538         795 :     auto &rf = state.dataResultsFramework->resultsFramework;
    4539             : 
    4540             :     //  See if Report Variables should be turned on
    4541         795 :     SortByName = false;
    4542         795 :     ScanForReports(state, "VariableDictionary", DoReport, _, VarOption1, VarOption2);
    4543             :     //  IF (.not. DoReport) RETURN
    4544             : 
    4545         795 :     if (DoReport) {
    4546         781 :         op->ProduceReportVDD = ReportVDD::Yes;
    4547         781 :         if (VarOption1 == std::string("IDF")) {
    4548         263 :             op->ProduceReportVDD = ReportVDD::IDF;
    4549             :         }
    4550         781 :         if (!VarOption2.empty()) {
    4551          71 :             if (Util::SameString(VarOption2, "Name") || Util::SameString(VarOption2, "AscendingName")) {
    4552           3 :                 SortByName = true;
    4553             :             }
    4554             :         }
    4555             :     }
    4556             : 
    4557         795 :     state.files.rdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.rdd);
    4558         795 :     state.files.mdd.ensure_open(state, "ProduceRDDMDD", state.files.outputControl.mdd);
    4559         795 :     if (op->ProduceReportVDD == ReportVDD::Yes) {
    4560         518 :         print(state.files.rdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
    4561         518 :         print(state.files.rdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
    4562             : 
    4563         518 :         print(state.files.mdd, "Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
    4564         518 :         print(state.files.mdd, "Var Type (reported time step),Var Report Type,Variable Name [Units]{}", '\n');
    4565         277 :     } else if (op->ProduceReportVDD == ReportVDD::IDF) {
    4566         263 :         print(state.files.rdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
    4567         263 :         print(state.files.rdd, "! Output:Variable Objects (applicable to this run){}", '\n');
    4568             : 
    4569         263 :         print(state.files.mdd, "! Program Version,{},{}{}", state.dataStrGlobals->VerStringVar, state.dataStrGlobals->IDDVerString, '\n');
    4570         263 :         print(state.files.mdd, "! Output:Meter Objects (applicable to this run){}", '\n');
    4571             :     }
    4572             : 
    4573         795 :     if (op->ProduceReportVDD == ReportVDD::Yes || op->ProduceReportVDD == ReportVDD::IDF) {
    4574             : 
    4575         781 :         auto miVar = op->ddOutVarMap.begin();
    4576         781 :         int aiVar = 0;
    4577             :         for (;;) {
    4578      456108 :             int iVar = -1;
    4579             :             // Too complicated to do this logic in the for loop header
    4580      456108 :             if (SortByName) {
    4581        1216 :                 if (miVar == op->ddOutVarMap.end()) break;
    4582        1213 :                 iVar = miVar->second;
    4583        1213 :                 ++miVar;
    4584             :             } else {
    4585      454892 :                 if (aiVar == (int)op->ddOutVars.size()) break;
    4586      454114 :                 iVar = aiVar;
    4587      454114 :                 ++aiVar;
    4588             :             }
    4589             : 
    4590      455327 :             auto *ddVar = op->ddOutVars[iVar];
    4591             : 
    4592      455327 :             if (ddVar->ReportedOnDDFile) continue;
    4593             : 
    4594             :             static constexpr std::array<std::string_view, (int)TimeStepType::Num> timeStepNamesLocal = {"Zone", "HVAC"};
    4595      455327 :             std::string_view timeStepName = timeStepNamesLocal[(int)ddVar->timeStepType];
    4596      455327 :             std::string_view storeTypeName = storeTypeNames[(int)ddVar->storeType];
    4597      455327 :             std::string_view varName = ddVar->name;
    4598             :             std::string_view unitName =
    4599      455327 :                 (ddVar->units == Constant::Units::customEMS) ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)ddVar->units];
    4600      455327 :             if (op->ProduceReportVDD == ReportVDD::Yes) {
    4601      296950 :                 print(state.files.rdd, "{},{},{} [{}]\n", timeStepName, storeTypeName, varName, unitName);
    4602      296950 :                 rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
    4603             :             } else {
    4604      158377 :                 print(state.files.rdd, "Output:Variable,*,{},hourly; !- {} {} [{}]\n", varName, timeStepName, storeTypeName, unitName);
    4605      158377 :                 rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
    4606             :             }
    4607             : 
    4608      455327 :             ddVar->ReportedOnDDFile = true;
    4609      455327 :             if (SortByName) {
    4610        1213 :                 while (ddVar->Next != -1) {
    4611           0 :                     ddVar = op->ddOutVars[ddVar->Next];
    4612             : 
    4613           0 :                     std::string_view timeStepName = timeStepTypeNames[(int)ddVar->timeStepType];
    4614           0 :                     std::string_view storeTypeName = storeTypeNames[(int)ddVar->storeType];
    4615           0 :                     std::string_view varName = ddVar->name;
    4616             :                     std::string_view unitName =
    4617           0 :                         (ddVar->units == Constant::Units::customEMS) ? ddVar->unitNameCustomEMS : Constant::unitNames[(int)ddVar->units];
    4618             : 
    4619           0 :                     if (op->ProduceReportVDD == ReportVDD::Yes) {
    4620           0 :                         print(state.files.rdd, "{},{},{} [{}]\n", timeStepName, storeTypeName, varName, unitName);
    4621           0 :                         rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
    4622             :                     } else {
    4623           0 :                         print(state.files.rdd, "Output:Variable,*,{},hourly; !- {} {} [{}]\n", varName, timeStepName, storeTypeName, unitName);
    4624           0 :                         rf->RDD.push_back(format("{},{},{} [{}]", timeStepName, storeTypeName, varName, unitName));
    4625             :                     }
    4626           0 :                     ddVar->ReportedOnDDFile = true;
    4627             :                 } // while (ddVar->Next != 0)
    4628             :             }     // if (SortByName)
    4629      455327 :         }         // for (aiVar, miVar)
    4630         781 :     }             // if (produceReportVDD)
    4631         795 :     state.files.rdd.close();
    4632             : 
    4633         795 :     auto miMeter = op->meterMap.begin();
    4634       98025 :     for (int aiMeter = 0; aiMeter < (int)op->meters.size() && miMeter != op->meterMap.end(); ++aiMeter, ++miMeter) {
    4635       97230 :         int iMeter = (SortByName) ? miMeter->second : aiMeter;
    4636       97230 :         auto *meter = op->meters[iMeter];
    4637       97230 :         std::string_view unitName = Constant::unitNames[(int)meter->units];
    4638       97230 :         if (op->ProduceReportVDD == ReportVDD::Yes) {
    4639       47519 :             print(state.files.mdd, "Zone,Meter,{} [{}]\n", meter->Name, unitName);
    4640       47519 :             rf->MDD.push_back(format("Zone,Meter,{} [{}]", meter->Name, unitName));
    4641       49711 :         } else if (op->ProduceReportVDD == ReportVDD::IDF) {
    4642       48164 :             print(state.files.mdd, "Output:Meter,{},hourly; !- [{}]\n", meter->Name, unitName);
    4643       48164 :             rf->MDD.push_back(format("Output:Meter,{} [{}]", meter->Name, unitName));
    4644       48164 :             print(state.files.mdd, "Output:Meter:Cumulative,{},hourly; !- [{}]\n", meter->Name, unitName);
    4645       48164 :             rf->MDD.push_back(format("Output:Meter:Cumulative,{} [{}]", meter->Name, unitName));
    4646             :         }
    4647             :     }
    4648         795 :     state.files.mdd.close();
    4649         795 : } // ProduceRDDMDD()
    4650             : 
    4651     6401529 : int AddDDOutVar(EnergyPlusData &state,
    4652             :                 std::string_view const name, // Variable Name
    4653             :                 OutputProcessor::TimeStepType const timeStepType,
    4654             :                 OutputProcessor::StoreType const storeType,
    4655             :                 OutputProcessor::VariableType const variableType,
    4656             :                 Constant::Units const units,
    4657             :                 std::string_view const customUnitName // the custom name for the units from EMS definition of units
    4658             : )
    4659             : {
    4660             : 
    4661             :     // SUBROUTINE INFORMATION:
    4662             :     //       AUTHOR         Linda Lawrie
    4663             :     //       DATE WRITTEN   August 2010
    4664             : 
    4665             :     // PURPOSE OF THIS SUBROUTINE:
    4666             :     // This routine maintains a unique list of Output Variables for the
    4667             :     // Variable Dictionary output.
    4668             : 
    4669             :     // Using/Aliasing
    4670             :     using namespace OutputProcessor;
    4671             : 
    4672             :     // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
    4673     6401529 :     auto &op = state.dataOutputProcessor;
    4674             : 
    4675     6401529 :     std::string nameUC = Util::makeUPPER(name);
    4676             : 
    4677     6401529 :     auto found = op->ddOutVarMap.find(nameUC);
    4678     6401529 :     if (found == op->ddOutVarMap.end()) {
    4679      464039 :         auto *ddVar = new DDOutVar();
    4680      464039 :         op->ddOutVars.push_back(ddVar);
    4681             :         // Add to map
    4682      464039 :         op->ddOutVarMap.insert_or_assign(nameUC, op->ddOutVars.size() - 1);
    4683             : 
    4684      464039 :         ddVar->timeStepType = timeStepType;
    4685      464039 :         ddVar->storeType = storeType;
    4686      464039 :         ddVar->variableType = variableType;
    4687      464039 :         ddVar->name = name;
    4688      464039 :         ddVar->units = units;
    4689      464039 :         if (!customUnitName.empty() && units == Constant::Units::customEMS) {
    4690          10 :             ddVar->unitNameCustomEMS = customUnitName;
    4691             :         }
    4692      464039 :         return op->ddOutVars.size() - 1;
    4693             : 
    4694     5937490 :     } else if (units == op->ddOutVars[found->second]->units) {
    4695     5937484 :         return found->second;
    4696             : 
    4697             :     } else {           // not the same as first units
    4698           6 :         int dup2 = -1; // for duplicate variable name
    4699           6 :         auto *ddVarDup = op->ddOutVars[found->second];
    4700           7 :         while (ddVarDup->Next != -1) {
    4701           2 :             if (units != op->ddOutVars[ddVarDup->Next]->units) {
    4702           1 :                 ddVarDup = op->ddOutVars[ddVarDup->Next];
    4703           1 :                 continue;
    4704             :             }
    4705           1 :             dup2 = ddVarDup->Next;
    4706           1 :             break;
    4707             :         }
    4708           6 :         if (dup2 == -1) {
    4709           5 :             DDOutVar *ddVar2 = new DDOutVar();
    4710           5 :             op->ddOutVars.push_back(ddVar2);
    4711             :             // Don't add this one to the map.  Leave the map pointing to the first one
    4712           5 :             ddVar2->timeStepType = timeStepType;
    4713           5 :             ddVar2->storeType = storeType;
    4714           5 :             ddVar2->variableType = variableType;
    4715           5 :             ddVar2->name = name;
    4716           5 :             ddVar2->units = units;
    4717           5 :             if (!customUnitName.empty() && units == Constant::Units::customEMS) {
    4718           0 :                 ddVar2->unitNameCustomEMS = customUnitName;
    4719             :             }
    4720           5 :             ddVarDup->Next = op->ddOutVars.size() - 1;
    4721             : 
    4722           5 :             return op->ddOutVars.size() - 1;
    4723             :         } else {
    4724           1 :             return dup2;
    4725             :         } // if (dup2 == 0)
    4726             :     }     // if (unitsForVar)
    4727     6401529 : } // AddDDOutVar()
    4728             : 
    4729         796 : int initErrorFile(EnergyPlusData &state)
    4730             : {
    4731         796 :     state.files.err_stream = std::make_unique<std::ofstream>(state.files.outputErrFilePath);
    4732         796 :     if (state.files.err_stream->bad()) {
    4733           0 :         DisplayString(state, fmt::format("ERROR: Could not open file {} for output (write).", state.files.outputErrFilePath));
    4734           0 :         return EXIT_FAILURE;
    4735             :     }
    4736         796 :     return EXIT_SUCCESS;
    4737             : } // initErrorFile()
    4738             : 
    4739             : } // namespace EnergyPlus

Generated by: LCOV version 1.14