LCOV - code coverage report
Current view: top level - EnergyPlus - OutputProcessor.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 84.2 % 2319 1953
Test Date: 2025-06-02 07:23:51 Functions: 90.0 % 60 54

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

Generated by: LCOV version 2.0-1