LCOV - code coverage report
Current view: top level - EnergyPlus - OutputProcessor.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 77.7 % 2319 1803
Test Date: 2025-06-02 12:03:30 Functions: 93.3 % 60 56

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

Generated by: LCOV version 2.0-1