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