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

Generated by: LCOV version 2.0-1