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