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 <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Fmath.hh>
53 : #include <ObjexxFCL/string.functions.hh>
54 :
55 : // EnergyPlus Headers
56 : #include <EnergyPlus/CostEstimateManager.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataGlobalConstants.hh>
59 : #include <EnergyPlus/DataIPShortCuts.hh>
60 : #include <EnergyPlus/DisplayRoutines.hh>
61 : #include <EnergyPlus/EconomicLifeCycleCost.hh>
62 : #include <EnergyPlus/EconomicTariff.hh>
63 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
64 : #include <EnergyPlus/OutputReportTabular.hh>
65 : #include <EnergyPlus/ResultsFramework.hh>
66 : #include <EnergyPlus/SQLiteProcedures.hh>
67 : #include <EnergyPlus/UtilityRoutines.hh>
68 :
69 : namespace EnergyPlus::EconomicLifeCycleCost {
70 :
71 : // Module containing the routines dealing with the EconomicLifeCycleCost
72 :
73 : // MODULE INFORMATION:
74 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
75 : // DATE WRITTEN May 2010
76 : // MODIFIED na
77 : // RE-ENGINEERED na
78 :
79 : // PURPOSE OF THIS MODULE:
80 : // To compute life-cycle cost measures such as present value based
81 : // on input provided by the user as well as calculated energy costs.
82 :
83 : // METHODOLOGY EMPLOYED:
84 : // Uses NIST Handbook 135 "Life-Cycle Costing Manual for the Federal
85 : // Energy Management Program" for most computations.
86 :
87 : // REFERENCES:
88 : // To compute the net present value for all costs entered in the
89 : // LifeCycleCosts objects, the algorithms from NIST Handbook 135
90 : // "Life-Cycle Costing Manual for the Federal Energy Management
91 : // Program" will be used as the primary source. Supplemental sources
92 : // of algorithms will be derived from ASTM E833-09a "Standard
93 : // Terminology of Building Economics", ASTM E917-05 "Standard
94 : // Practice for Measuring Life-Cycle Cost of Buildings and Building
95 : // Systems", and "Engineering Economic Analysis, Ninth Edition", by
96 : // Donald Newnan, Ted Eschenback, and Jerome Lavelle.
97 :
98 795 : void GetInputForLifeCycleCost(EnergyPlusData &state)
99 : {
100 : // SUBROUTINE INFORMATION:
101 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
102 : // DATE WRITTEN May 2010
103 : // MODIFIED na
104 : // RE-ENGINEERED na
105 :
106 : // PURPOSE OF THIS SUBROUTINE:
107 : // Read the input file for "LifeCycleCost:Parameters" object.
108 :
109 795 : if (state.dataEconLifeCycleCost->GetInput_GetLifeCycleCostInput) {
110 795 : GetInputLifeCycleCostParameters(state);
111 795 : GetInputLifeCycleCostRecurringCosts(state);
112 795 : GetInputLifeCycleCostNonrecurringCost(state);
113 795 : GetInputLifeCycleCostUsePriceEscalation(state);
114 795 : GetInputLifeCycleCostUseAdjustment(state);
115 795 : state.dataEconLifeCycleCost->GetInput_GetLifeCycleCostInput = false;
116 : }
117 795 : }
118 :
119 794 : void ComputeLifeCycleCostAndReport(EnergyPlusData &state)
120 : {
121 : // SUBROUTINE INFORMATION:
122 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
123 : // DATE WRITTEN May 2010
124 : // MODIFIED na
125 : // RE-ENGINEERED na
126 :
127 : // PURPOSE OF THIS SUBROUTINE:
128 : // Perform the life cycle cost computations and write report.
129 :
130 794 : if (state.dataEconLifeCycleCost->LCCparamPresent) {
131 2 : DisplayString(state, "Computing Life Cycle Costs and Reporting");
132 2 : ExpressAsCashFlows(state);
133 2 : ComputePresentValue(state);
134 2 : ComputeEscalatedEnergyCosts(state);
135 2 : ComputeTaxAndDepreciation(state);
136 2 : WriteTabularLifeCycleCostReport(state);
137 : }
138 794 : }
139 :
140 : //======================================================================================================================
141 : //======================================================================================================================
142 :
143 : // GET INPUT ROUTINES
144 :
145 : //======================================================================================================================
146 : //======================================================================================================================
147 :
148 795 : void GetInputLifeCycleCostParameters(EnergyPlusData &state)
149 : {
150 : // SUBROUTINE INFORMATION:
151 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
152 : // DATE WRITTEN May 2010
153 :
154 : // PURPOSE OF THIS SUBROUTINE:
155 : // Read the input file for "LifeCycleCost:Parameters" object.
156 :
157 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
158 :
159 : int jFld; // loop counter
160 : int NumFields; // Total number of elements
161 : int NumAlphas; // Number of elements in the alpha array
162 : int NumNums; // Number of elements in the numeric array
163 795 : Array1D_string AlphaArray; // character string data
164 795 : Array1D<Real64> NumArray; // numeric data
165 : int IOStat; // IO Status when calling get input subroutine
166 795 : std::string CurrentModuleObject; // for ease in renaming.
167 : int NumObj; // count of objects
168 :
169 795 : CurrentModuleObject = "LifeCycleCost:Parameters";
170 795 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
171 795 : NumArray.allocate(NumNums);
172 795 : AlphaArray.allocate(NumAlphas);
173 795 : NumObj = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
174 :
175 795 : auto &elcc(state.dataEconLifeCycleCost);
176 :
177 795 : if (NumObj == 0) {
178 793 : elcc->LCCparamPresent = false;
179 2 : } else if (NumObj == 1) {
180 2 : elcc->LCCparamPresent = true;
181 4 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
182 : CurrentModuleObject,
183 : 1,
184 : AlphaArray,
185 : NumAlphas,
186 : NumArray,
187 : NumNums,
188 : IOStat,
189 2 : state.dataIPShortCut->lNumericFieldBlanks,
190 2 : state.dataIPShortCut->lAlphaFieldBlanks,
191 2 : state.dataIPShortCut->cAlphaFieldNames,
192 2 : state.dataIPShortCut->cNumericFieldNames);
193 : // check to make sure none of the values are another life cycle cost object
194 14 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
195 12 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
196 0 : ShowWarningError(state,
197 0 : format("In {} named {} a field was found containing LifeCycleCost: which may indicate a missing comma.",
198 : CurrentModuleObject,
199 : AlphaArray(1)));
200 : }
201 : }
202 : // start to extract values from input array into appropriate fields
203 : // A1, \field Name
204 : // \required-field
205 : // \type alpha
206 2 : elcc->LCCname = AlphaArray(1);
207 : // A2, \field Discounting Convention
208 : // \type choice
209 : // \key EndOfYear
210 : // \key MidYear
211 : // \key BeginningOfYear
212 : // \default EndOfYear
213 2 : elcc->discountConvention = static_cast<DiscConv>(getEnumValue(DiscConvNamesUC, Util::makeUPPER(AlphaArray(2))));
214 2 : if (elcc->discountConvention == DiscConv::Invalid) {
215 0 : elcc->discountConvention = DiscConv::EndOfYear;
216 0 : ShowWarningError(
217 : state,
218 0 : format(
219 0 : "{}: Invalid {}=\"{}\". EndOfYear will be used.", CurrentModuleObject, state.dataIPShortCut->cAlphaFieldNames(2), AlphaArray(2)));
220 : }
221 : // A3, \field Inflation Approach
222 : // \type choice
223 : // \key ConstantDollar
224 : // \key CurrentDollar
225 : // \default ConstantDollar
226 2 : elcc->inflationApproach = static_cast<InflAppr>(getEnumValue(InflApprNamesUC, Util::makeUPPER(AlphaArray(3))));
227 2 : if (elcc->inflationApproach == InflAppr::Invalid) {
228 0 : elcc->inflationApproach = InflAppr::ConstantDollar;
229 0 : ShowWarningError(state,
230 0 : format("{}: Invalid {}=\"{}\". ConstantDollar will be used.",
231 : CurrentModuleObject,
232 0 : state.dataIPShortCut->cAlphaFieldNames(3),
233 : AlphaArray(3)));
234 : }
235 : // N1, \field Real Discount Rate
236 : // \type real
237 2 : elcc->realDiscountRate = NumArray(1);
238 2 : if ((elcc->inflationApproach == InflAppr::ConstantDollar) && state.dataIPShortCut->lNumericFieldBlanks(1)) {
239 0 : ShowWarningError(state,
240 0 : format("{}: Invalid for field {} to be blank when ConstantDollar analysis is be used.",
241 : CurrentModuleObject,
242 0 : state.dataIPShortCut->cNumericFieldNames(1)));
243 : }
244 2 : if ((elcc->realDiscountRate > 0.30) || (elcc->realDiscountRate < -0.30)) {
245 0 : ShowWarningError(
246 : state,
247 0 : format("{}: Invalid value in field {}. This value is the decimal value not a percentage so most values are between 0.02 and 0.15. ",
248 : CurrentModuleObject,
249 0 : state.dataIPShortCut->cNumericFieldNames(1)));
250 : }
251 : // N2, \field Nominal Discount Rate
252 : // \type real
253 2 : elcc->nominalDiscountRate = NumArray(2);
254 2 : if ((elcc->inflationApproach == InflAppr::CurrentDollar) && state.dataIPShortCut->lNumericFieldBlanks(2)) {
255 0 : ShowWarningError(state,
256 0 : format("{}: Invalid for field {} to be blank when CurrentDollar analysis is be used.",
257 : CurrentModuleObject,
258 0 : state.dataIPShortCut->cNumericFieldNames(2)));
259 : }
260 2 : if ((elcc->nominalDiscountRate > 0.30) || (elcc->nominalDiscountRate < -0.30)) {
261 0 : ShowWarningError(
262 : state,
263 0 : format("{}: Invalid value in field {}. This value is the decimal value not a percentage so most values are between 0.02 and 0.15. ",
264 : CurrentModuleObject,
265 0 : state.dataIPShortCut->cNumericFieldNames(2)));
266 : }
267 : // N3, \field Inflation
268 : // \type real
269 2 : elcc->inflation = NumArray(3);
270 2 : if ((elcc->inflationApproach == InflAppr::ConstantDollar) && (!state.dataIPShortCut->lNumericFieldBlanks(3))) {
271 0 : ShowWarningError(state,
272 0 : format("{}: Invalid for field {} contain a value when ConstantDollar analysis is be used.",
273 : CurrentModuleObject,
274 0 : state.dataIPShortCut->cNumericFieldNames(3)));
275 : }
276 2 : if ((elcc->inflation > 0.30) || (elcc->inflation < -0.30)) {
277 0 : ShowWarningError(
278 : state,
279 0 : format("{}: Invalid value in field {}. This value is the decimal value not a percentage so most values are between 0.02 and 0.15. ",
280 : CurrentModuleObject,
281 0 : state.dataIPShortCut->cNumericFieldNames(3)));
282 : }
283 : // A4, \field Base Date Month
284 : // \type choice
285 : // \key January
286 : // \key February
287 : // \key March
288 : // \key April
289 : // \key May
290 : // \key June
291 : // \key July
292 : // \key August
293 : // \key September
294 : // \key October
295 : // \key November
296 : // \key December
297 : // \default January
298 2 : elcc->baseDateMonth = getEnumValue(Util::MonthNamesUC, Util::makeUPPER(AlphaArray(4)));
299 2 : if (elcc->baseDateMonth == -1) {
300 0 : elcc->baseDateMonth = 0;
301 0 : ShowWarningError(state,
302 0 : format("{}: Invalid month entered in field {}. Using January instead of \"{}\"",
303 : CurrentModuleObject,
304 0 : state.dataIPShortCut->cAlphaFieldNames(4),
305 : AlphaArray(4)));
306 : }
307 : // N4, \field Base Date Year
308 : // \type integer
309 : // \minimum 1900
310 : // \maximum 2100
311 2 : elcc->baseDateYear = int(NumArray(4));
312 2 : if (elcc->baseDateYear > 2100) {
313 0 : ShowWarningError(state,
314 0 : format("{}: Invalid value in field {}. Value greater than 2100 yet it is representing a year. ",
315 : CurrentModuleObject,
316 0 : state.dataIPShortCut->cNumericFieldNames(4)));
317 : }
318 2 : if (elcc->baseDateYear < 1900) {
319 0 : ShowWarningError(state,
320 0 : format("{}: Invalid value in field {}. Value less than 1900 yet it is representing a year. ",
321 : CurrentModuleObject,
322 0 : state.dataIPShortCut->cNumericFieldNames(4)));
323 : }
324 : // A5, \field Service Date Month
325 : // \type choice
326 : // \key January
327 : // \key February
328 : // \key March
329 : // \key April
330 : // \key May
331 : // \key June
332 : // \key July
333 : // \key August
334 : // \key September
335 : // \key October
336 : // \key November
337 : // \key December
338 : // \default January
339 2 : elcc->serviceDateMonth = getEnumValue(Util::MonthNamesUC, Util::makeUPPER(AlphaArray(5)));
340 2 : if (elcc->serviceDateMonth == -1) {
341 0 : elcc->serviceDateMonth = 0;
342 0 : ShowWarningError(state,
343 0 : format("{}: Invalid month entered in field {}. Using January instead of \"{}\"",
344 : CurrentModuleObject,
345 0 : state.dataIPShortCut->cAlphaFieldNames(5),
346 : AlphaArray(5)));
347 : }
348 : // N5, \field Service Date Year
349 : // \type integer
350 : // \minimum 1900
351 : // \maximum 2100
352 2 : elcc->serviceDateYear = int(NumArray(5));
353 2 : if (elcc->serviceDateYear > 2100) {
354 0 : ShowWarningError(state,
355 0 : format("{}: Invalid value in field {}. Value greater than 2100 yet it is representing a year. ",
356 : CurrentModuleObject,
357 0 : state.dataIPShortCut->cNumericFieldNames(5)));
358 : }
359 2 : if (elcc->serviceDateYear < 1900) {
360 0 : ShowWarningError(state,
361 0 : format("{}: Invalid value in field {}. Value less than 1900 yet it is representing a year. ",
362 : CurrentModuleObject,
363 0 : state.dataIPShortCut->cNumericFieldNames(5)));
364 : }
365 : // N6, \field Length of Study Period in Years
366 : // \type integer
367 : // \minimum 1
368 : // \maximum 100
369 2 : elcc->lengthStudyYears = int(NumArray(6));
370 2 : if (elcc->lengthStudyYears > 100) {
371 0 : ShowWarningError(state,
372 0 : format("{}: Invalid value in field {}. A value greater than 100 is not reasonable for an economic evaluation. ",
373 : CurrentModuleObject,
374 0 : state.dataIPShortCut->cNumericFieldNames(6)));
375 : }
376 2 : if (elcc->lengthStudyYears < 1) {
377 0 : ShowWarningError(state,
378 0 : format("{}: Invalid value in field {}. A value less than 1 is not reasonable for an economic evaluation. ",
379 : CurrentModuleObject,
380 0 : state.dataIPShortCut->cNumericFieldNames(6)));
381 : }
382 2 : elcc->lengthStudyTotalMonths = elcc->lengthStudyYears * 12;
383 : // N7, \field Tax rate
384 : // \type real
385 : // \minimum 0.0
386 2 : elcc->taxRate = NumArray(7);
387 2 : if (elcc->taxRate < 0.0 && (!state.dataIPShortCut->lNumericFieldBlanks(7))) {
388 0 : ShowWarningError(state,
389 0 : format("{}: Invalid value in field {}. A value less than 0 is not reasonable for a tax rate. ",
390 : CurrentModuleObject,
391 0 : state.dataIPShortCut->cNumericFieldNames(10)));
392 : }
393 : // A6; \field Depreciation Method
394 : // \type choice
395 : // \key ModifiedAcceleratedCostRecoverySystem-3year
396 : // \key ModifiedAcceleratedCostRecoverySystem-5year
397 : // \key ModifiedAcceleratedCostRecoverySystem-7year
398 : // \key ModifiedAcceleratedCostRecoverySystem-10year
399 : // \key ModifiedAcceleratedCostRecoverySystem-15year
400 : // \key ModifiedAcceleratedCostRecoverySystem-20year
401 : // \key StraightLine-27year
402 : // \key StraightLine-31year
403 : // \key StraightLine-39year
404 : // \key StraightLine-40year
405 : // \key None
406 : // \default None
407 2 : elcc->depreciationMethod = static_cast<DeprMethod>(getEnumValue(DeprMethodNamesUC, Util::makeUPPER(AlphaArray(6))));
408 2 : if (elcc->depreciationMethod == DeprMethod::Invalid) {
409 0 : elcc->depreciationMethod = DeprMethod::None;
410 0 : if (state.dataIPShortCut->lAlphaFieldBlanks(6)) {
411 0 : ShowWarningError(
412 : state,
413 0 : format("{}: The input field {}is blank. \"None\" will be used.", CurrentModuleObject, state.dataIPShortCut->cAlphaFieldNames(6)));
414 : } else {
415 0 : ShowWarningError(state,
416 0 : format("{}: Invalid {}=\"{}{}",
417 : CurrentModuleObject,
418 0 : state.dataIPShortCut->cAlphaFieldNames(6),
419 : AlphaArray(6),
420 : R"(". "None" will be used.)"));
421 : }
422 : }
423 : // compute derived variables
424 2 : elcc->lastDateYear = elcc->baseDateYear + elcc->lengthStudyYears - 1;
425 : } else {
426 0 : ShowWarningError(
427 0 : state, format("{}: Only one instance of this object is allowed. No life-cycle cost reports will be generated.", CurrentModuleObject));
428 0 : elcc->LCCparamPresent = false;
429 : }
430 795 : }
431 :
432 795 : void GetInputLifeCycleCostRecurringCosts(EnergyPlusData &state)
433 : {
434 : // SUBROUTINE INFORMATION:
435 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
436 : // DATE WRITTEN May 2010
437 :
438 : // PURPOSE OF THIS SUBROUTINE:
439 : // Read the input file for "LifeCycleCost:RecurringCosts" object.
440 :
441 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
442 :
443 : int iInObj; // loop index variable for reading in objects
444 : int jFld; // loop counter
445 : int NumFields; // Total number of elements
446 : int NumAlphas; // Number of elements in the alpha array
447 : int NumNums; // Number of elements in the numeric array
448 795 : Array1D_string AlphaArray; // character string data
449 795 : Array1D<Real64> NumArray; // numeric data
450 : int IOStat; // IO Status when calling get input subroutine
451 795 : std::string CurrentModuleObject; // for ease in renaming.
452 :
453 795 : auto &elcc(state.dataEconLifeCycleCost);
454 :
455 795 : if (!elcc->LCCparamPresent) return;
456 2 : CurrentModuleObject = "LifeCycleCost:RecurringCosts";
457 2 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
458 2 : NumArray.allocate(NumNums);
459 2 : AlphaArray.allocate(NumAlphas);
460 2 : elcc->numRecurringCosts = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
461 2 : elcc->RecurringCosts.resize(elcc->numRecurringCosts);
462 3 : for (iInObj = 0; iInObj < elcc->numRecurringCosts; ++iInObj) {
463 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
464 : CurrentModuleObject,
465 : iInObj + 1, // since this index needs to start from 1
466 : AlphaArray,
467 : NumAlphas,
468 : NumArray,
469 : NumNums,
470 : IOStat,
471 1 : state.dataIPShortCut->lNumericFieldBlanks,
472 1 : state.dataIPShortCut->lAlphaFieldBlanks,
473 1 : state.dataIPShortCut->cAlphaFieldNames,
474 1 : state.dataIPShortCut->cNumericFieldNames);
475 : // check to make sure none of the values are another life cycle cost object
476 4 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
477 3 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
478 0 : ShowWarningError(state,
479 0 : format("In {} named {} a field was found containing LifeCycleCost: which may indicate a missing comma.",
480 : CurrentModuleObject,
481 : AlphaArray(1)));
482 : }
483 : }
484 : // start to extract values from input array into appropriate fields
485 : // A1, \field Name
486 : // \required-field
487 : // \type alpha
488 1 : elcc->RecurringCosts[iInObj].name = AlphaArray(1);
489 : // A2, \field Category
490 : // \type choice
491 : // \key Maintenance
492 : // \key Repair
493 : // \key Operation
494 : // \key Replacement
495 : // \key MinorOverhaul
496 : // \key MajorOverhaul
497 : // \key OtherOperational
498 : // \default Maintenance
499 1 : elcc->RecurringCosts[iInObj].category = static_cast<CostCategory>(getEnumValue(CostCategoryNamesUCNoSpace, Util::makeUPPER(AlphaArray(2))));
500 : bool isNotRecurringCost =
501 1 : (elcc->RecurringCosts[iInObj].category != CostCategory::Maintenance && elcc->RecurringCosts[iInObj].category != CostCategory::Repair &&
502 0 : elcc->RecurringCosts[iInObj].category != CostCategory::Operation && elcc->RecurringCosts[iInObj].category != CostCategory::Replacement &&
503 0 : elcc->RecurringCosts[iInObj].category != CostCategory::MinorOverhaul &&
504 1 : elcc->RecurringCosts[iInObj].category != CostCategory::MajorOverhaul &&
505 0 : elcc->RecurringCosts[iInObj].category != CostCategory::OtherOperational);
506 1 : if (isNotRecurringCost) {
507 0 : elcc->RecurringCosts[iInObj].category = CostCategory::Maintenance;
508 0 : ShowWarningError(state,
509 0 : format("{}: Invalid {}=\"{}\". The category of Maintenance will be used.",
510 : CurrentModuleObject,
511 0 : state.dataIPShortCut->cAlphaFieldNames(2),
512 : AlphaArray(2)));
513 : }
514 : // N1, \field Cost
515 : // \type real
516 1 : elcc->RecurringCosts[iInObj].cost = NumArray(1);
517 : // A3, \field Start of Costs
518 : // \type choice
519 : // \key ServicePeriod
520 : // \key BasePeriod
521 : // \default ServicePeriod
522 1 : elcc->RecurringCosts[iInObj].startOfCosts = static_cast<StartCosts>(getEnumValue(StartCostNamesUC, Util::makeUPPER(AlphaArray(3))));
523 1 : if (elcc->RecurringCosts[iInObj].startOfCosts == StartCosts::Invalid) {
524 0 : elcc->RecurringCosts[iInObj].startOfCosts = StartCosts::ServicePeriod;
525 0 : ShowWarningError(state,
526 0 : format("{}: Invalid {}=\"{}\". The start of the service period will be used.",
527 : CurrentModuleObject,
528 0 : state.dataIPShortCut->cAlphaFieldNames(3),
529 : AlphaArray(3)));
530 : }
531 : // N2, \field Years from Start
532 : // \type integer
533 : // \minimum 0
534 : // \maximum 100
535 1 : elcc->RecurringCosts[iInObj].yearsFromStart = int(NumArray(2));
536 1 : if (elcc->RecurringCosts[iInObj].yearsFromStart > 100) {
537 0 : ShowWarningError(state,
538 0 : format("{}: Invalid value in field {}. This value is the number of years from the start so a value greater than 100 is "
539 : "not reasonable for an economic evaluation. ",
540 : CurrentModuleObject,
541 0 : state.dataIPShortCut->cNumericFieldNames(2)));
542 : }
543 1 : if (elcc->RecurringCosts[iInObj].yearsFromStart < 0) {
544 0 : ShowWarningError(state,
545 0 : format("{}: Invalid value in field {}. This value is the number of years from the start so a value less than 0 is not "
546 : "reasonable for an economic evaluation. ",
547 : CurrentModuleObject,
548 0 : state.dataIPShortCut->cNumericFieldNames(2)));
549 : }
550 : // N3, \field Months from Start
551 : // \type integer
552 : // \minimum 0
553 : // \maximum 1200
554 1 : elcc->RecurringCosts[iInObj].monthsFromStart = int(NumArray(3));
555 1 : if (elcc->RecurringCosts[iInObj].monthsFromStart > 1200) {
556 0 : ShowWarningError(state,
557 0 : format("{}: Invalid value in field {}. This value is the number of months from the start so a value greater than 1200 "
558 : "is not reasonable for an economic evaluation. ",
559 : CurrentModuleObject,
560 0 : state.dataIPShortCut->cNumericFieldNames(3)));
561 : }
562 1 : if (elcc->RecurringCosts[iInObj].monthsFromStart < 0) {
563 0 : ShowWarningError(state,
564 0 : format("{}: Invalid value in field {}. This value is the number of months from the start so a value less than 0 is not "
565 : "reasonable for an economic evaluation. ",
566 : CurrentModuleObject,
567 0 : state.dataIPShortCut->cNumericFieldNames(3)));
568 : }
569 : // N4, \field Repeat Period Years
570 : // \type integer
571 : // \minimum 1
572 : // \maximum 100
573 1 : elcc->RecurringCosts[iInObj].repeatPeriodYears = int(NumArray(4));
574 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodYears > 100) {
575 0 : ShowWarningError(state,
576 0 : format("{}: Invalid value in field {}. This value is the number of years between occurrences of the cost so a value "
577 : "greater than 100 is not reasonable for an economic evaluation. ",
578 : CurrentModuleObject,
579 0 : state.dataIPShortCut->cNumericFieldNames(4)));
580 : }
581 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodYears < 1) {
582 0 : ShowWarningError(state,
583 0 : format("{}: Invalid value in field {}. This value is the number of years between occurrences of the cost so a value "
584 : "less than 1 is not reasonable for an economic evaluation. ",
585 : CurrentModuleObject,
586 0 : state.dataIPShortCut->cNumericFieldNames(4)));
587 : }
588 : // N5, \field Repeat Period Months
589 : // \type integer
590 : // \minimum 0
591 : // \maximum 1200
592 1 : elcc->RecurringCosts[iInObj].repeatPeriodMonths = int(NumArray(5));
593 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodMonths > 1200) {
594 0 : ShowWarningError(state,
595 0 : format("{}: Invalid value in field {}. This value is the number of months between occurrences of the cost so a value "
596 : "greater than 1200 is not reasonable for an economic evaluation. ",
597 : CurrentModuleObject,
598 0 : state.dataIPShortCut->cNumericFieldNames(5)));
599 : }
600 1 : if (elcc->RecurringCosts[iInObj].repeatPeriodMonths < 0) {
601 0 : ShowWarningError(state,
602 0 : format("{}: Invalid value in field {}. This value is the number of months between occurrences of the cost so a value "
603 : "less than 0 is not reasonable for an economic evaluation. ",
604 : CurrentModuleObject,
605 0 : state.dataIPShortCut->cNumericFieldNames(5)));
606 : }
607 1 : if ((elcc->RecurringCosts[iInObj].repeatPeriodMonths == 0) && (elcc->RecurringCosts[iInObj].repeatPeriodYears == 0)) {
608 0 : ShowWarningError(state,
609 0 : format("{}: Invalid value in fields {} and {}. The repeat period must not be zero months and zero years. ",
610 : CurrentModuleObject,
611 0 : state.dataIPShortCut->cNumericFieldNames(5),
612 0 : state.dataIPShortCut->cNumericFieldNames(4)));
613 : }
614 : // N6; \field Annual escalation rate
615 : // \type real
616 1 : elcc->RecurringCosts[iInObj].annualEscalationRate = int(NumArray(6));
617 1 : if (elcc->RecurringCosts[iInObj].annualEscalationRate > 0.30) {
618 0 : ShowWarningError(state,
619 0 : format("{}: Invalid value in field {}. This value is the decimal value for the annual escalation so most values are "
620 : "between 0.02 and 0.15. ",
621 : CurrentModuleObject,
622 0 : state.dataIPShortCut->cNumericFieldNames(6)));
623 : }
624 1 : if (elcc->RecurringCosts[iInObj].annualEscalationRate < -0.30) {
625 0 : ShowWarningError(state,
626 0 : format("{}: Invalid value in field {}. This value is the decimal value for the annual escalation so most values are "
627 : "between 0.02 and 0.15. ",
628 : CurrentModuleObject,
629 0 : state.dataIPShortCut->cNumericFieldNames(6)));
630 : }
631 : // express the years and months fields in total months
632 1 : elcc->RecurringCosts[iInObj].totalMonthsFromStart =
633 1 : elcc->RecurringCosts[iInObj].yearsFromStart * 12 + elcc->RecurringCosts[iInObj].monthsFromStart;
634 1 : elcc->RecurringCosts[iInObj].totalRepeatPeriodMonths =
635 1 : elcc->RecurringCosts[iInObj].repeatPeriodYears * 12 + elcc->RecurringCosts[iInObj].repeatPeriodMonths;
636 : }
637 2381 : }
638 :
639 795 : void GetInputLifeCycleCostNonrecurringCost(EnergyPlusData &state)
640 : {
641 : // SUBROUTINE INFORMATION:
642 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
643 : // DATE WRITTEN May 2010
644 :
645 : // PURPOSE OF THIS SUBROUTINE:
646 : // Read the input file for "LifeCycleCost:NonrecurringCost" object.
647 :
648 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
649 :
650 : int iInObj; // loop index variable for reading in objects
651 : int jFld; // loop counter
652 : int NumFields; // Total number of elements
653 : int NumAlphas; // Number of elements in the alpha array
654 : int NumNums; // Number of elements in the numeric array
655 795 : Array1D_string AlphaArray; // character string data
656 795 : Array1D<Real64> NumArray; // numeric data
657 : int IOStat; // IO Status when calling get input subroutine
658 795 : std::string CurrentModuleObject; // for ease in renaming.
659 : int numComponentCostLineItems; // number of ComponentCost:LineItem objects
660 :
661 795 : auto &elcc(state.dataEconLifeCycleCost);
662 :
663 795 : if (!elcc->LCCparamPresent) return;
664 2 : CurrentModuleObject = "LifeCycleCost:NonrecurringCost";
665 2 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
666 2 : NumArray.allocate(NumNums);
667 2 : AlphaArray.allocate(NumAlphas);
668 2 : elcc->numNonrecurringCost = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
669 2 : numComponentCostLineItems = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "ComponentCost:LineItem");
670 2 : if (numComponentCostLineItems > 0) { // leave room for component cost total
671 0 : elcc->NonrecurringCost.resize(elcc->numNonrecurringCost + 1); // add a place for CostEstimate total
672 : } else {
673 2 : elcc->NonrecurringCost.resize(elcc->numNonrecurringCost);
674 : }
675 8 : for (iInObj = 0; iInObj < elcc->numNonrecurringCost; ++iInObj) {
676 12 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
677 : CurrentModuleObject,
678 : iInObj + 1, // since this index needs to start from 1
679 : AlphaArray,
680 : NumAlphas,
681 : NumArray,
682 : NumNums,
683 : IOStat,
684 6 : state.dataIPShortCut->lNumericFieldBlanks,
685 6 : state.dataIPShortCut->lAlphaFieldBlanks,
686 6 : state.dataIPShortCut->cAlphaFieldNames,
687 6 : state.dataIPShortCut->cNumericFieldNames);
688 : // check to make sure none of the values are another life cycle cost object
689 24 : for (jFld = 1; jFld <= NumAlphas; ++jFld) {
690 18 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
691 0 : ShowWarningError(state,
692 0 : format("In {} named {} a field was found containing LifeCycleCost: which may indicate a missing comma.",
693 : CurrentModuleObject,
694 : AlphaArray(1)));
695 : }
696 : }
697 : // start to extract values from input array into appropriate fields
698 : // A1, \field Name
699 : // \required-field
700 : // \type alpha
701 6 : elcc->NonrecurringCost[iInObj].name = AlphaArray(1);
702 : // A2, \field Category
703 : // \type choice
704 : // \key Construction
705 : // \key Salvage
706 : // \key OtherCapital
707 : // \default Construction
708 6 : elcc->NonrecurringCost[iInObj].category = static_cast<CostCategory>(getEnumValue(CostCategoryNamesUCNoSpace, Util::makeUPPER(AlphaArray(2))));
709 6 : bool isNotNonRecurringCost = (elcc->NonrecurringCost[iInObj].category != CostCategory::Construction &&
710 8 : elcc->NonrecurringCost[iInObj].category != CostCategory::Salvage &&
711 2 : elcc->NonrecurringCost[iInObj].category != CostCategory::OtherCapital);
712 6 : if (isNotNonRecurringCost) {
713 0 : elcc->NonrecurringCost[iInObj].category = CostCategory::Construction;
714 0 : ShowWarningError(state,
715 0 : format("{}: Invalid {}=\"{}\". The category of Construction will be used.",
716 : CurrentModuleObject,
717 0 : state.dataIPShortCut->cAlphaFieldNames(2),
718 : AlphaArray(2)));
719 : }
720 : // N1, \field Cost
721 : // \type real
722 6 : elcc->NonrecurringCost[iInObj].cost = NumArray(1);
723 : // A3, \field Start of Costs
724 : // \type choice
725 : // \key ServicePeriod
726 : // \key BasePeriod
727 : // \default ServicePeriod
728 6 : elcc->NonrecurringCost[iInObj].startOfCosts = static_cast<StartCosts>(getEnumValue(StartCostNamesUC, Util::makeUPPER(AlphaArray(3))));
729 6 : if (elcc->NonrecurringCost[iInObj].startOfCosts == StartCosts::Invalid) {
730 0 : elcc->NonrecurringCost[iInObj].startOfCosts = StartCosts::ServicePeriod;
731 0 : ShowWarningError(state,
732 0 : format("{}: Invalid {}=\"{}\". The start of the service period will be used.",
733 : CurrentModuleObject,
734 0 : state.dataIPShortCut->cAlphaFieldNames(3),
735 : AlphaArray(3)));
736 : }
737 : // N2, \field Years from Start
738 : // \type integer
739 : // \minimum 0
740 : // \maximum 100
741 6 : elcc->NonrecurringCost[iInObj].yearsFromStart = int(NumArray(2));
742 6 : if (elcc->NonrecurringCost[iInObj].yearsFromStart > 100) {
743 0 : ShowWarningError(state,
744 0 : format("{}: Invalid value in field {}. This value is the number of years from the start so a value greater than 100 is "
745 : "not reasonable for an economic evaluation. ",
746 : CurrentModuleObject,
747 0 : state.dataIPShortCut->cNumericFieldNames(2)));
748 : }
749 6 : if (elcc->NonrecurringCost[iInObj].yearsFromStart < 0) {
750 0 : ShowWarningError(state,
751 0 : format("{}: Invalid value in field {}. This value is the number of years from the start so a value less than 0 is not "
752 : "reasonable for an economic evaluation. ",
753 : CurrentModuleObject,
754 0 : state.dataIPShortCut->cNumericFieldNames(2)));
755 : }
756 : // N3; \field Months from Start
757 : // \type integer
758 : // \minimum 0
759 : // \maximum 11
760 6 : elcc->NonrecurringCost[iInObj].monthsFromStart = int(NumArray(3));
761 6 : if (elcc->NonrecurringCost[iInObj].monthsFromStart > 1200) {
762 0 : ShowWarningError(state,
763 0 : format("{}: Invalid value in field {}. This value is the number of months from the start so a value greater than 1200 "
764 : "is not reasonable for an economic evaluation. ",
765 : CurrentModuleObject,
766 0 : state.dataIPShortCut->cNumericFieldNames(3)));
767 : }
768 6 : if (elcc->NonrecurringCost[iInObj].monthsFromStart < 0) {
769 0 : ShowWarningError(state,
770 0 : format("{}: Invalid value in field {}. This value is the number of months from the start so a value less than 0 is not "
771 : "reasonable for an economic evaluation. ",
772 : CurrentModuleObject,
773 0 : state.dataIPShortCut->cNumericFieldNames(3)));
774 : }
775 : // express the years and months fields in total months
776 6 : elcc->NonrecurringCost[iInObj].totalMonthsFromStart =
777 6 : elcc->NonrecurringCost[iInObj].yearsFromStart * 12 + elcc->NonrecurringCost[iInObj].monthsFromStart;
778 : }
779 2381 : }
780 :
781 795 : void GetInputLifeCycleCostUsePriceEscalation(EnergyPlusData &state)
782 : {
783 : // SUBROUTINE INFORMATION:
784 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
785 : // DATE WRITTEN May 2010
786 :
787 : // PURPOSE OF THIS SUBROUTINE:
788 : // Read the input file for "LifeCycleCost:UsePriceEscalation" object.
789 :
790 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
791 :
792 : int iInObj; // loop index variable for reading in objects
793 : int NumFields; // Total number of elements
794 : int NumAlphas; // Number of elements in the alpha array
795 : int NumNums; // Number of elements in the numeric array
796 795 : Array1D_string AlphaArray; // character string data
797 795 : Array1D<Real64> NumArray; // numeric data
798 795 : std::string CurrentModuleObject; // for ease in renaming.
799 :
800 795 : auto &elcc(state.dataEconLifeCycleCost);
801 :
802 795 : if (!elcc->LCCparamPresent) return;
803 2 : CurrentModuleObject = "LifeCycleCost:UsePriceEscalation";
804 2 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
805 2 : NumArray.allocate(NumNums);
806 2 : AlphaArray.allocate(NumAlphas);
807 2 : elcc->numUsePriceEscalation = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
808 2 : elcc->UsePriceEscalation.allocate(elcc->numUsePriceEscalation);
809 10 : for (iInObj = 1; iInObj <= elcc->numUsePriceEscalation; ++iInObj) {
810 8 : elcc->UsePriceEscalation(iInObj).Escalation.allocate(elcc->lengthStudyYears);
811 : }
812 2 : if (elcc->numUsePriceEscalation > 0) {
813 : int IOStat; // IO Status when calling get input subroutine
814 10 : for (iInObj = 1; iInObj <= elcc->numUsePriceEscalation; ++iInObj) {
815 16 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
816 : CurrentModuleObject,
817 : iInObj,
818 : AlphaArray,
819 : NumAlphas,
820 : NumArray,
821 : NumNums,
822 : IOStat,
823 8 : state.dataIPShortCut->lNumericFieldBlanks,
824 8 : state.dataIPShortCut->lAlphaFieldBlanks,
825 8 : state.dataIPShortCut->cAlphaFieldNames,
826 8 : state.dataIPShortCut->cNumericFieldNames);
827 : // check to make sure none of the values are another life cycle cost object
828 32 : for (int jFld = 1; jFld <= NumAlphas; ++jFld) {
829 24 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
830 0 : ShowWarningError(state,
831 0 : format("In {} named {} a field was found containing LifeCycleCost: which may indicate a missing comma.",
832 : CurrentModuleObject,
833 : AlphaArray(1)));
834 : }
835 : }
836 : // start to extract values from input array into appropriate fields
837 : // A1, \field Name
838 : // \required-field
839 : // \type alpha
840 8 : elcc->UsePriceEscalation(iInObj).name = AlphaArray(1);
841 : // A2, \field Resource
842 : // \required-field
843 : // \type choice
844 : // \key Electricity
845 : // \key NaturalGas
846 : // \key Steam
847 : // \key Gasoline
848 : // \key Diesel
849 : // \key Coal
850 : // \key FuelOilNo1
851 : // \key FuelOilNo2
852 : // \key Propane
853 : // \key Water
854 : // \key OtherFuel1
855 : // \key OtherFuel2
856 8 : elcc->UsePriceEscalation(iInObj).resource = static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, AlphaArray(2)));
857 8 : if (NumAlphas > 3) {
858 0 : ShowWarningError(state, format("In {} contains more alpha fields than expected.", CurrentModuleObject));
859 : }
860 : // N1, \field Escalation Start Year
861 : // \type integer
862 : // \minimum 1900
863 : // \maximum 2100
864 8 : elcc->UsePriceEscalation(iInObj).escalationStartYear = int(NumArray(1));
865 8 : if (elcc->UsePriceEscalation(iInObj).escalationStartYear > 2100) {
866 0 : ShowWarningError(state,
867 0 : format("{}: Invalid value in field {}. Value greater than 2100 yet it is representing a year. ",
868 : CurrentModuleObject,
869 0 : state.dataIPShortCut->cNumericFieldNames(1)));
870 : }
871 8 : if (elcc->UsePriceEscalation(iInObj).escalationStartYear < 1900) {
872 0 : ShowWarningError(state,
873 0 : format("{}: Invalid value in field {}. Value less than 1900 yet it is representing a year. ",
874 : CurrentModuleObject,
875 0 : state.dataIPShortCut->cNumericFieldNames(1)));
876 : }
877 : // A3, \field Escalation Start Month
878 : // \type choice
879 : // \key January
880 : // \key February
881 : // \key March
882 : // \key April
883 : // \key May
884 : // \key June
885 : // \key July
886 : // \key August
887 : // \key September
888 : // \key October
889 : // \key November
890 : // \key December
891 : // \default January
892 8 : elcc->UsePriceEscalation(iInObj).escalationStartMonth = getEnumValue(Util::MonthNamesUC, Util::makeUPPER(AlphaArray(3)));
893 8 : if (elcc->UsePriceEscalation(iInObj).escalationStartMonth == -1) {
894 0 : elcc->UsePriceEscalation(iInObj).escalationStartMonth = 0;
895 0 : ShowWarningError(state,
896 0 : format("{}: Invalid month entered in field {}. Using January instead of \"{}\"",
897 : CurrentModuleObject,
898 0 : state.dataIPShortCut->cAlphaFieldNames(3),
899 : AlphaArray(3)));
900 : }
901 : // N2, \field Year 1 Escalation
902 : // \type real
903 : // \begin-extensible
904 : // The array is from the baseDateYear until baseDateYear + lengthStudyYears
905 : // Set the array to default to 1.0
906 199 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
907 191 : elcc->UsePriceEscalation(iInObj).Escalation(jYear) = 1.0;
908 : }
909 : // Since the years in the UsePriceEscalation may not match up with the baseDateYear and
910 : // the lenghtStudyYears, need to make adjustments when reading in the values to align
911 : // with the baseDateYear (the first item in all yearly arrays)
912 8 : elcc->UsePriceEscalation_escStartYear = elcc->UsePriceEscalation(iInObj).escalationStartYear;
913 8 : elcc->UsePriceEscalation_escNumYears = NumNums - 1;
914 8 : elcc->UsePriceEscalation_escEndYear = elcc->UsePriceEscalation_escStartYear + elcc->UsePriceEscalation_escNumYears - 1;
915 8 : elcc->UsePriceEscalation_earlierEndYear = min(elcc->UsePriceEscalation_escEndYear, elcc->lastDateYear); // pick the earlier ending date
916 8 : elcc->UsePriceEscalation_laterStartYear = max(elcc->UsePriceEscalation_escStartYear, elcc->baseDateYear); // pick the later starting date
917 199 : for (int jYear = elcc->UsePriceEscalation_laterStartYear; jYear <= elcc->UsePriceEscalation_earlierEndYear; ++jYear) {
918 191 : elcc->UsePriceEscalation_curFld = 2 + jYear - elcc->UsePriceEscalation_escStartYear;
919 191 : elcc->UsePriceEscalation_curEsc = 1 + jYear - elcc->baseDateYear;
920 191 : if ((elcc->UsePriceEscalation_curFld <= NumNums) && (elcc->UsePriceEscalation_curFld >= 1)) {
921 191 : if ((elcc->UsePriceEscalation_curEsc <= elcc->lengthStudyYears) && (elcc->UsePriceEscalation_curEsc >= 1)) {
922 191 : elcc->UsePriceEscalation(iInObj).Escalation(elcc->UsePriceEscalation_curEsc) = NumArray(elcc->UsePriceEscalation_curFld);
923 : }
924 : }
925 : }
926 : }
927 : }
928 2381 : }
929 :
930 795 : void GetInputLifeCycleCostUseAdjustment(EnergyPlusData &state)
931 : {
932 : // SUBROUTINE INFORMATION:
933 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
934 : // DATE WRITTEN May 2010
935 :
936 : // PURPOSE OF THIS SUBROUTINE:
937 : // Read the input file for "LifeCycleCost:UseAdjustment" object.
938 :
939 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
940 :
941 : int iInObj; // loop index variable for reading in objects
942 : int NumFields; // Total number of elements
943 : int NumAlphas; // Number of elements in the alpha array
944 : int NumNums; // Number of elements in the numeric array
945 795 : Array1D_string AlphaArray; // character string data
946 795 : Array1D<Real64> NumArray; // numeric data
947 795 : std::string CurrentModuleObject; // for ease in renaming.
948 :
949 795 : auto &elcc(state.dataEconLifeCycleCost);
950 :
951 795 : if (!elcc->LCCparamPresent) return;
952 2 : CurrentModuleObject = "LifeCycleCost:UseAdjustment";
953 2 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumFields, NumAlphas, NumNums);
954 2 : NumArray.allocate(NumNums);
955 2 : AlphaArray.allocate(NumAlphas);
956 2 : elcc->numUseAdjustment = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
957 2 : elcc->UseAdjustment.allocate(elcc->numUseAdjustment);
958 3 : for (iInObj = 1; iInObj <= elcc->numUseAdjustment; ++iInObj) {
959 1 : elcc->UseAdjustment(iInObj).Adjustment.allocate(elcc->lengthStudyYears);
960 : }
961 2 : if (elcc->numUseAdjustment > 0) {
962 : int IOStat; // IO Status when calling get input subroutine
963 2 : for (iInObj = 1; iInObj <= elcc->numUseAdjustment; ++iInObj) {
964 2 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
965 : CurrentModuleObject,
966 : iInObj,
967 : AlphaArray,
968 : NumAlphas,
969 : NumArray,
970 : NumNums,
971 : IOStat,
972 1 : state.dataIPShortCut->lNumericFieldBlanks,
973 1 : state.dataIPShortCut->lAlphaFieldBlanks,
974 1 : state.dataIPShortCut->cAlphaFieldNames,
975 1 : state.dataIPShortCut->cNumericFieldNames);
976 : // check to make sure none of the values are another life cycle cost object
977 3 : for (int jFld = 1; jFld <= NumAlphas; ++jFld) {
978 2 : if (hasi(AlphaArray(jFld), "LifeCycleCost:")) {
979 0 : ShowWarningError(state,
980 0 : format("In {} named {} a field was found containing LifeCycleCost: which may indicate a missing comma.",
981 : CurrentModuleObject,
982 : AlphaArray(1)));
983 : }
984 : }
985 : // start to extract values from input array into appropriate fields
986 : // A1, \field Name
987 : // \required-field
988 : // \type alpha
989 1 : elcc->UseAdjustment(iInObj).name = AlphaArray(1);
990 : // A2, \field Resource
991 : // \required-field
992 : // \type choice
993 : // \key Electricity
994 : // \key NaturalGas
995 : // \key Steam
996 : // \key Gasoline
997 : // \key Diesel
998 : // \key Coal
999 : // \key FuelOilNo1
1000 : // \key FuelOilNo2
1001 : // \key Propane
1002 : // \key Water
1003 : // \key OtherFuel1
1004 : // \key OtherFuel2
1005 1 : elcc->UseAdjustment(iInObj).resource = static_cast<Constant::eResource>(getEnumValue(Constant::eResourceNamesUC, AlphaArray(2)));
1006 1 : if (NumAlphas > 2) {
1007 0 : ShowWarningError(state, format("In {} contains more alpha fields than expected.", CurrentModuleObject));
1008 : }
1009 : // N1, \field Year 1 Multiplier
1010 : // \type real
1011 : // \begin-extensible
1012 : // Set the array to default to 1.0
1013 23 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1014 22 : elcc->UseAdjustment(iInObj).Adjustment(jYear) = 1.0;
1015 : }
1016 1 : int numFldsToUse = min(NumNums, elcc->lengthStudyYears);
1017 2 : for (int jYear = 1; jYear <= numFldsToUse; ++jYear) {
1018 1 : elcc->UseAdjustment(iInObj).Adjustment(jYear) = NumArray(jYear);
1019 : }
1020 : }
1021 : }
1022 2381 : }
1023 :
1024 : //======================================================================================================================
1025 : //======================================================================================================================
1026 :
1027 : // COMPUTATION ROUTINES
1028 :
1029 : //======================================================================================================================
1030 : //======================================================================================================================
1031 :
1032 2 : void ExpressAsCashFlows(EnergyPlusData &state)
1033 : {
1034 : // SUBROUTINE INFORMATION:
1035 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1036 : // DATE WRITTEN July 2010
1037 :
1038 : // PURPOSE OF THIS SUBROUTINE:
1039 : // Convert all recurring and nonrecurring costs into cash flows
1040 : // used in calculations and reporting.
1041 :
1042 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1043 : int iCashFlow;
1044 : int jCost;
1045 : int jAdj;
1046 : int kYear;
1047 : int offset;
1048 : int month; // number of months since base date
1049 : int firstMonth;
1050 : int monthsBaseToService;
1051 :
1052 2 : std::map<int, std::array<Real64, static_cast<int>(Constant::eResource::Num)>> resourceCosts;
1053 26 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1054 24 : resourceCosts[jMonth] = std::array<Real64, static_cast<int>(Constant::eResource::Num)>();
1055 24 : std::fill(resourceCosts[jMonth].begin(), resourceCosts[jMonth].end(), 0.0);
1056 : }
1057 :
1058 2 : Array1D<Real64> curResourceCosts(12);
1059 :
1060 2 : std::array<bool, static_cast<int>(Constant::eResource::Num)> resourceCostNotZero{};
1061 2 : std::fill(resourceCostNotZero.begin(), resourceCostNotZero.end(), false);
1062 :
1063 2 : std::array<Real64, static_cast<int>(Constant::eResource::Num)> resourceCostAnnual{};
1064 2 : std::fill(resourceCostAnnual.begin(), resourceCostAnnual.end(), 0.0);
1065 :
1066 : Real64 annualCost;
1067 : int found;
1068 : CostCategory curCategory;
1069 2 : Array1D<Real64> monthlyInflationFactor;
1070 : Real64 inflationPerMonth;
1071 : int iLoop;
1072 :
1073 2 : auto &elcc(state.dataEconLifeCycleCost);
1074 :
1075 : // compute months from 1900 for base and service period
1076 4 : elcc->ExpressAsCashFlows_baseMonths1900 =
1077 2 : (elcc->baseDateYear - 1900) * 12 + (elcc->baseDateMonth + 1); // elcc->baseDateMonth + 1 to account for baseDateMonth starting at 0
1078 4 : elcc->ExpressAsCashFlows_serviceMonths1900 =
1079 2 : (elcc->serviceDateYear - 1900) * 12 + elcc->serviceDateMonth + 1; // elcc->serviceDateMonth + 1 to account for serviceDateMonth starting at 0
1080 2 : monthsBaseToService = elcc->ExpressAsCashFlows_serviceMonths1900 - elcc->ExpressAsCashFlows_baseMonths1900;
1081 : // if ComponentCost:LineItem exist, the grand total of all costs are another non-recurring cost
1082 2 : if (state.dataCostEstimateManager->CurntBldg.GrandTotal >
1083 : 0.0) { // from DataCostEstimate and computed in WriteCompCostTable within OutputReportTabular
1084 0 : ++elcc->numNonrecurringCost;
1085 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].name = "Total of ComponentCost:*";
1086 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].lineItem = "";
1087 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].category = CostCategory::Construction;
1088 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].cost = state.dataCostEstimateManager->CurntBldg.GrandTotal;
1089 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].startOfCosts = StartCosts::BasePeriod;
1090 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].yearsFromStart = 0;
1091 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].monthsFromStart = 0;
1092 0 : elcc->NonrecurringCost[elcc->numNonrecurringCost].totalMonthsFromStart = 0;
1093 : }
1094 :
1095 : // gather costs from EconomicTariff for each end use
1096 2 : elcc->numResourcesUsed = 0;
1097 98 : for (int iResource = 0; iResource < static_cast<int>(Constant::eResource::Num); ++iResource) {
1098 96 : EconomicTariff::GetMonthlyCostForResource(state, static_cast<Constant::eResource>(iResource), curResourceCosts);
1099 96 : annualCost = 0.0;
1100 1248 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1101 1152 : resourceCosts[jMonth][iResource] = curResourceCosts(jMonth);
1102 1152 : annualCost += resourceCosts[jMonth][iResource];
1103 : }
1104 96 : if (annualCost != 0.0) {
1105 1 : ++elcc->numResourcesUsed;
1106 1 : resourceCostNotZero[iResource] = true;
1107 : } else {
1108 95 : resourceCostNotZero[iResource] = false;
1109 : }
1110 96 : resourceCostAnnual[iResource] = annualCost;
1111 : }
1112 : // allocate the escalated energy cost arrays
1113 49 : for (int year = 1; year <= elcc->lengthStudyYears; ++year) {
1114 47 : elcc->EscalatedEnergy[year] = std::array<Real64, static_cast<int>(Constant::eResource::Num)>();
1115 47 : std::fill(elcc->EscalatedEnergy[year].begin(), elcc->EscalatedEnergy[year].end(), 0.0);
1116 : }
1117 :
1118 2 : elcc->EscalatedTotEnergy.allocate(elcc->lengthStudyYears);
1119 2 : elcc->EscalatedTotEnergy = 0.0;
1120 :
1121 : // pre-compute the inflation factors for each year
1122 2 : monthlyInflationFactor.allocate(elcc->lengthStudyTotalMonths);
1123 2 : if (elcc->inflationApproach == InflAppr::ConstantDollar) {
1124 2 : monthlyInflationFactor = 1.0; // not really used but just in case
1125 0 : } else if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1126 : // to allocate an interest rate (in this case inflation) cannot just use 1/12
1127 : // for the monthly value since it will be slightly wrong. Instead, use inverse of
1128 : // formula from Newnan (4-32) which is r = m x (ia + 1)^(1/m) - 1)
1129 0 : inflationPerMonth = std::pow(elcc->inflation + 1.0, 1.0 / 12.0) - 1;
1130 0 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1131 0 : monthlyInflationFactor(jMonth) = std::pow(1.0 + inflationPerMonth, jMonth - 1);
1132 : }
1133 : }
1134 :
1135 2 : elcc->numCashFlow = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed;
1136 : // Cashflow array order:
1137 : // 1 cost categories
1138 : // 2 recurring costs
1139 : // 3 nonrecurring costs
1140 : // 4 resource costs
1141 2 : elcc->CashFlow.resize(elcc->numCashFlow);
1142 42 : for (iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1143 40 : elcc->CashFlow[iCashFlow].mnAmount.allocate(elcc->lengthStudyTotalMonths);
1144 40 : elcc->CashFlow[iCashFlow].yrAmount.allocate(elcc->lengthStudyYears);
1145 40 : elcc->CashFlow[iCashFlow].yrPresVal.allocate(elcc->lengthStudyYears);
1146 40 : elcc->CashFlow[iCashFlow].mnAmount = 0.0; // zero all cash flow values
1147 40 : elcc->CashFlow[iCashFlow].yrAmount = 0.0; // zero all cash flow values
1148 40 : elcc->CashFlow[iCashFlow].yrPresVal = 0.0; // zero all present values
1149 : }
1150 : // Put nonrecurring costs into cashflows
1151 2 : offset = CostCategory::Num + elcc->numRecurringCosts;
1152 8 : for (jCost = 0; jCost < elcc->numNonrecurringCost; ++jCost) {
1153 6 : elcc->CashFlow[offset + jCost].name = elcc->NonrecurringCost[jCost].name;
1154 6 : elcc->CashFlow[offset + jCost].SourceKind = SourceKindType::Nonrecurring;
1155 6 : elcc->CashFlow[offset + jCost].Category = elcc->NonrecurringCost[jCost].category;
1156 6 : elcc->CashFlow[offset + jCost].orginalCost = elcc->NonrecurringCost[jCost].cost;
1157 6 : elcc->CashFlow[offset + jCost].mnAmount = 0.0;
1158 6 : if (elcc->NonrecurringCost[jCost].startOfCosts == StartCosts::ServicePeriod) {
1159 1 : month = elcc->NonrecurringCost[jCost].totalMonthsFromStart + monthsBaseToService + 1;
1160 5 : } else if (elcc->NonrecurringCost[jCost].startOfCosts == StartCosts::BasePeriod) {
1161 5 : month = elcc->NonrecurringCost[jCost].totalMonthsFromStart + 1;
1162 : }
1163 6 : if ((month >= 1) && (month <= elcc->lengthStudyTotalMonths)) {
1164 6 : elcc->CashFlow[offset + jCost].mnAmount(month) = elcc->NonrecurringCost[jCost].cost * monthlyInflationFactor(month);
1165 : } else {
1166 0 : ShowWarningError(state,
1167 0 : format("For life cycle costing a nonrecurring cost named {} contains a cost which is not within the study period.",
1168 0 : elcc->NonrecurringCost[jCost].name));
1169 : }
1170 : }
1171 : // Put recurring costs into cashflows
1172 2 : offset = CostCategory::Num;
1173 3 : for (jCost = 0; jCost < elcc->numRecurringCosts; ++jCost) {
1174 1 : elcc->CashFlow[offset + jCost].name = elcc->RecurringCosts[jCost].name;
1175 1 : elcc->CashFlow[offset + jCost].SourceKind = SourceKindType::Recurring;
1176 1 : elcc->CashFlow[offset + jCost].Category = elcc->RecurringCosts[jCost].category;
1177 1 : elcc->CashFlow[offset + jCost].orginalCost = elcc->RecurringCosts[jCost].cost;
1178 1 : if (elcc->RecurringCosts[jCost].startOfCosts == StartCosts::ServicePeriod) {
1179 1 : firstMonth = elcc->RecurringCosts[jCost].totalMonthsFromStart + monthsBaseToService + 1;
1180 0 : } else if (elcc->RecurringCosts[jCost].startOfCosts == StartCosts::BasePeriod) {
1181 0 : firstMonth = elcc->RecurringCosts[jCost].totalMonthsFromStart + 1;
1182 : }
1183 1 : if ((firstMonth >= 1) && (firstMonth <= elcc->lengthStudyTotalMonths)) {
1184 1 : month = firstMonth;
1185 1 : if (elcc->RecurringCosts[jCost].totalRepeatPeriodMonths >= 1) {
1186 20 : for (iLoop = 0; iLoop < 10000; ++iLoop) { // add a limit to the loop to prevent runaway condition
1187 20 : elcc->CashFlow[offset + jCost].mnAmount(month) = elcc->RecurringCosts[jCost].cost * monthlyInflationFactor(month);
1188 20 : month += elcc->RecurringCosts[jCost].totalRepeatPeriodMonths;
1189 20 : if (month > elcc->lengthStudyTotalMonths) break;
1190 : }
1191 : }
1192 : } else {
1193 0 : ShowWarningError(
1194 : state,
1195 0 : format("For life cycle costing the recurring cost named {} has the first year of the costs that is not within the study period.",
1196 0 : elcc->RecurringCosts[jCost].name));
1197 : }
1198 : }
1199 : // Put resource costs into cashflows
1200 : // the first cash flow for resources should be after the categories, recurring and nonrecurring costs
1201 2 : int cashFlowCounter = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost - 1; // Since CashFlow starts at 0
1202 98 : for (int iResource = 0; iResource < static_cast<int>(Constant::eResource::Num); ++iResource) {
1203 96 : if (resourceCostNotZero[iResource]) {
1204 1 : ++cashFlowCounter;
1205 :
1206 1 : switch (static_cast<Constant::eResource>(iResource)) {
1207 0 : case Constant::eResource::Water:
1208 : case Constant::eResource::OnSiteWater:
1209 : case Constant::eResource::MainsWater:
1210 : case Constant::eResource::RainWater:
1211 : case Constant::eResource::WellWater:
1212 : case Constant::eResource::Condensate:
1213 0 : elcc->CashFlow[cashFlowCounter].Category = CostCategory::Water;
1214 0 : break;
1215 1 : case Constant::eResource::Electricity:
1216 : case Constant::eResource::NaturalGas:
1217 : case Constant::eResource::Gasoline:
1218 : case Constant::eResource::Diesel:
1219 : case Constant::eResource::Coal:
1220 : case Constant::eResource::FuelOilNo1:
1221 : case Constant::eResource::FuelOilNo2:
1222 : case Constant::eResource::Propane:
1223 : case Constant::eResource::EnergyTransfer:
1224 : case Constant::eResource::DistrictCooling:
1225 : case Constant::eResource::DistrictHeatingWater:
1226 : case Constant::eResource::DistrictHeatingSteam:
1227 : case Constant::eResource::ElectricityProduced:
1228 : case Constant::eResource::ElectricityPurchased:
1229 : case Constant::eResource::ElectricityNet:
1230 : case Constant::eResource::SolarWater:
1231 : case Constant::eResource::SolarAir:
1232 1 : elcc->CashFlow[cashFlowCounter].Category = CostCategory::Energy;
1233 1 : break;
1234 0 : default:
1235 0 : elcc->CashFlow[cashFlowCounter].Category = CostCategory::Operation;
1236 : }
1237 :
1238 1 : elcc->CashFlow[cashFlowCounter].Resource = static_cast<Constant::eResource>(iResource);
1239 1 : elcc->CashFlow[cashFlowCounter].SourceKind = SourceKindType::Resource;
1240 1 : elcc->CashFlow[cashFlowCounter].name = Constant::eResourceNames[static_cast<int>(iResource)];
1241 1 : if (cashFlowCounter <= elcc->numCashFlow) {
1242 : // put the monthly energy costs into the cashflow prior to adjustments
1243 : // energy costs (a.k.a. resource costs) start at the start of service and repeat
1244 : // until the end of the study total
1245 13 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1246 12 : elcc->CashFlow[cashFlowCounter].mnAmount(monthsBaseToService + jMonth) = resourceCosts[jMonth][iResource];
1247 : }
1248 1 : elcc->CashFlow[cashFlowCounter].orginalCost = resourceCostAnnual[iResource];
1249 229 : for (int jMonth = monthsBaseToService + 13; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1250 : // use the cost from a year earlier
1251 228 : elcc->CashFlow[cashFlowCounter].mnAmount(jMonth) = elcc->CashFlow[cashFlowCounter].mnAmount(jMonth - 12);
1252 : }
1253 : // add in the impact of inflation
1254 265 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1255 264 : elcc->CashFlow[cashFlowCounter].mnAmount(jMonth) *= monthlyInflationFactor(jMonth);
1256 : }
1257 : // now factor in adjustments
1258 : // need to find the correct adjustment to use for the current resource
1259 1 : found = 0;
1260 2 : for (jAdj = 1; jAdj <= elcc->numUseAdjustment; ++jAdj) {
1261 1 : if (elcc->UseAdjustment(jAdj).resource == static_cast<Constant::eResource>(iResource)) {
1262 0 : found = jAdj;
1263 0 : break;
1264 : }
1265 : }
1266 : // if any adjustments were found for that resource apply the multiplier
1267 1 : if (found != 0) {
1268 0 : for (kYear = 1; kYear <= elcc->lengthStudyYears;
1269 : ++kYear) { // if service period is later than base period then this will go too far
1270 0 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1271 0 : month = (kYear - 1) * 12 + jMonth;
1272 0 : if (month > elcc->lengthStudyTotalMonths) break;
1273 0 : elcc->CashFlow[cashFlowCounter].mnAmount(month) *= elcc->UseAdjustment(found).Adjustment(kYear);
1274 : }
1275 : }
1276 : }
1277 : }
1278 : }
1279 : }
1280 : // put cashflows into categories
1281 34 : for (jCost = 0; jCost < CostCategory::Num; ++jCost) {
1282 32 : elcc->CashFlow[jCost].Category = static_cast<CostCategory>(jCost); // make each category the type indicated
1283 32 : elcc->CashFlow[jCost].SourceKind = SourceKindType::Sum;
1284 : }
1285 : // add the cashflows by category
1286 12 : for (jCost = CostCategory::Num - 1; jCost < elcc->numCashFlow; ++jCost) {
1287 10 : curCategory = elcc->CashFlow[jCost].Category;
1288 10 : if ((curCategory < CostCategory::Num) && (curCategory >= 0)) {
1289 2722 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1290 2712 : elcc->CashFlow[curCategory].mnAmount(jMonth) += elcc->CashFlow[jCost].mnAmount(jMonth);
1291 : }
1292 : }
1293 : }
1294 : // create total categories
1295 566 : for (int jMonth = 1; jMonth <= elcc->lengthStudyTotalMonths; ++jMonth) {
1296 564 : elcc->CashFlow[CostCategory::TotEnergy].mnAmount(jMonth) = elcc->CashFlow[CostCategory::Energy].mnAmount(jMonth);
1297 564 : elcc->CashFlow[CostCategory::TotOper].mnAmount(jMonth) =
1298 564 : elcc->CashFlow[CostCategory::Maintenance].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Repair].mnAmount(jMonth) +
1299 564 : elcc->CashFlow[CostCategory::Operation].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Replacement].mnAmount(jMonth) +
1300 564 : elcc->CashFlow[CostCategory::MinorOverhaul].mnAmount(jMonth) + elcc->CashFlow[CostCategory::MajorOverhaul].mnAmount(jMonth) +
1301 564 : elcc->CashFlow[CostCategory::OtherOperational].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Water].mnAmount(jMonth) +
1302 564 : elcc->CashFlow[CostCategory::Energy].mnAmount(jMonth);
1303 1128 : elcc->CashFlow[CostCategory::TotCaptl].mnAmount(jMonth) = elcc->CashFlow[CostCategory::Construction].mnAmount(jMonth) +
1304 564 : elcc->CashFlow[CostCategory::Salvage].mnAmount(jMonth) +
1305 564 : elcc->CashFlow[CostCategory::OtherCapital].mnAmount(jMonth);
1306 564 : elcc->CashFlow[CostCategory::TotGrand].mnAmount(jMonth) =
1307 564 : elcc->CashFlow[CostCategory::TotOper].mnAmount(jMonth) + elcc->CashFlow[CostCategory::TotCaptl].mnAmount(jMonth);
1308 : }
1309 : // convert all monthly cashflows into yearly cashflows
1310 42 : for (jCost = 0; jCost < elcc->numCashFlow; ++jCost) {
1311 971 : for (kYear = 1; kYear <= elcc->lengthStudyYears; ++kYear) {
1312 931 : annualCost = 0.0;
1313 12103 : for (int jMonth = 1; jMonth <= 12; ++jMonth) {
1314 11172 : month = (kYear - 1) * 12 + jMonth;
1315 11172 : if (month <= elcc->lengthStudyTotalMonths) {
1316 11172 : annualCost += elcc->CashFlow[jCost].mnAmount(month);
1317 : }
1318 : }
1319 931 : elcc->CashFlow[jCost].yrAmount(kYear) = annualCost;
1320 : }
1321 : }
1322 : // generate a warning if resource referenced was not used
1323 10 : for (int nUsePriceEsc = 1; nUsePriceEsc <= elcc->numUsePriceEscalation; ++nUsePriceEsc) {
1324 8 : Constant::eResource curResource = elcc->UsePriceEscalation(nUsePriceEsc).resource;
1325 8 : if (!resourceCostNotZero[static_cast<int>(curResource)] && state.dataGlobal->DoWeathSim) {
1326 0 : ShowWarningError(state,
1327 0 : format("The resource referenced by LifeCycleCost:UsePriceEscalation= \"{}\" has no energy cost. ",
1328 0 : elcc->UsePriceEscalation(nUsePriceEsc).name));
1329 0 : ShowContinueError(state, "... It is likely that the wrong resource is used. The resource should match the meter used in Utility:Tariff.");
1330 : }
1331 : }
1332 2 : }
1333 :
1334 2 : void ComputeEscalatedEnergyCosts(EnergyPlusData &state)
1335 : {
1336 : // J. Glazer - August 2019
1337 : int nUsePriceEsc;
1338 :
1339 2 : auto &elcc(state.dataEconLifeCycleCost);
1340 :
1341 42 : for (int iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1342 40 : if (elcc->CashFlow[iCashFlow].pvKind == PrValKind::Energy) {
1343 : // make sure this is not water
1344 1 : Constant::eResource curResource = elcc->CashFlow[iCashFlow].Resource;
1345 2 : if (elcc->CashFlow[iCashFlow].Resource == Constant::eResource::Water ||
1346 1 : (elcc->CashFlow[iCashFlow].Resource >= Constant::eResource::OnSiteWater &&
1347 0 : elcc->CashFlow[iCashFlow].Resource <= Constant::eResource::Condensate)) {
1348 0 : continue;
1349 : }
1350 1 : if ((curResource != Constant::eResource::Invalid)) {
1351 1 : int found = 0;
1352 3 : for (nUsePriceEsc = 1; nUsePriceEsc <= elcc->numUsePriceEscalation; ++nUsePriceEsc) {
1353 3 : if (elcc->UsePriceEscalation(nUsePriceEsc).resource == curResource) {
1354 1 : found = nUsePriceEsc;
1355 1 : break;
1356 : }
1357 : }
1358 1 : if (found > 0) {
1359 23 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1360 22 : elcc->EscalatedEnergy[jYear][static_cast<int>(curResource)] =
1361 22 : elcc->CashFlow[iCashFlow].yrAmount(jYear) * elcc->UsePriceEscalation(found).Escalation(jYear);
1362 : }
1363 : } else { // if no escalation than just store the original energy cost
1364 0 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1365 0 : elcc->EscalatedEnergy[jYear][static_cast<int>(curResource)] = elcc->CashFlow[iCashFlow].yrAmount(jYear);
1366 : }
1367 : }
1368 : }
1369 : }
1370 : }
1371 98 : for (int kResource = 0; kResource < static_cast<int>(Constant::eResource::Num); ++kResource) {
1372 2352 : for (int jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1373 2256 : elcc->EscalatedTotEnergy(jYear) += elcc->EscalatedEnergy[jYear][kResource];
1374 : }
1375 : }
1376 2 : }
1377 :
1378 2 : void ComputePresentValue(EnergyPlusData &state)
1379 : {
1380 : // SUBROUTINE INFORMATION:
1381 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1382 : // DATE WRITTEN August 2010
1383 : // MODIFIED na
1384 : // RE-ENGINEERED na
1385 :
1386 : // PURPOSE OF THIS SUBROUTINE:
1387 : // For each cashflow, compute the present value
1388 :
1389 : // METHODOLOGY EMPLOYED:
1390 :
1391 : // REFERENCES:
1392 : // na
1393 :
1394 : // USE STATEMENTS:
1395 :
1396 : // Locals
1397 : // SUBROUTINE ARGUMENT DEFINITIONS:
1398 : // na
1399 :
1400 : // SUBROUTINE PARAMETER DEFINITIONS:
1401 : // na
1402 :
1403 : // INTERFACE BLOCK SPECIFICATIONS
1404 : // na
1405 :
1406 : // DERIVED TYPE DEFINITIONS
1407 : // na
1408 :
1409 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1410 : Real64 totalPV;
1411 : Real64 curDiscountRate;
1412 : int iCashFlow;
1413 : int jYear;
1414 : int nUsePriceEsc;
1415 : Real64 effectiveYear;
1416 :
1417 2 : auto &elcc(state.dataEconLifeCycleCost);
1418 :
1419 : // identify how each cashflow should be treated
1420 42 : for (iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1421 40 : switch (elcc->CashFlow[iCashFlow].SourceKind) {
1422 1 : case SourceKindType::Resource: {
1423 : // only for real fuels purchased such as electricity, natural gas, etc..
1424 2 : if ((elcc->CashFlow[iCashFlow].Resource >= Constant::eResource::Electricity) &&
1425 1 : (elcc->CashFlow[iCashFlow].Resource <= Constant::eResource::ElectricitySurplusSold)) {
1426 1 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::Energy;
1427 : } else {
1428 0 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::NonEnergy;
1429 : }
1430 1 : break;
1431 : }
1432 7 : case SourceKindType::Recurring:
1433 : case SourceKindType::Nonrecurring: {
1434 7 : if (elcc->CashFlow[iCashFlow].Category == CostCategory::Energy) {
1435 0 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::Energy;
1436 : } else {
1437 7 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::NonEnergy;
1438 : }
1439 7 : break;
1440 : }
1441 32 : case SourceKindType::Sum:
1442 : default: {
1443 32 : elcc->CashFlow[iCashFlow].pvKind = PrValKind::NotComputed;
1444 32 : break;
1445 : }
1446 : }
1447 : }
1448 : // compute the Single Present Value factors based on the discount rate
1449 2 : elcc->SPV.allocate(elcc->lengthStudyYears); // Should this be energySPV?
1450 49 : for (int year = 1; year <= elcc->lengthStudyYears; ++year) {
1451 47 : elcc->energySPV[year] = std::array<Real64, static_cast<int>(Constant::eResource::Num)>();
1452 47 : std::fill(elcc->energySPV[year].begin(), elcc->energySPV[year].end(), 0.0);
1453 : }
1454 :
1455 : // Depending on if using Constant or Current Dollar analysis
1456 : // use the appropriate discount rate
1457 2 : if (elcc->inflationApproach == InflAppr::ConstantDollar) {
1458 2 : curDiscountRate = elcc->realDiscountRate;
1459 0 : } else if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1460 0 : curDiscountRate = elcc->nominalDiscountRate;
1461 : }
1462 : // compute single present values based on real discount rates
1463 2 : constexpr std::array<Real64, static_cast<int>(DiscConv::Num)> DiscConv2EffectiveYearAdjustment = {1.0, 0.5, 0.0};
1464 49 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1465 : // NIST 155 D.2.1.1 - Single Present Value (SPV) formula
1466 47 : effectiveYear = double(jYear) - DiscConv2EffectiveYearAdjustment[static_cast<int>(elcc->discountConvention)];
1467 47 : elcc->SPV(jYear) = 1.0 / std::pow(1.0 + curDiscountRate, effectiveYear);
1468 : }
1469 : // use SPV as default values for all energy types
1470 49 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1471 2303 : for (int iResource = 0; iResource < static_cast<int>(Constant::eResource::Num); ++iResource) {
1472 2256 : elcc->energySPV[jYear][iResource] = elcc->SPV(jYear);
1473 : }
1474 : }
1475 : // loop through the resources and if they match a UseEscalation use those values instead
1476 10 : for (nUsePriceEsc = 1; nUsePriceEsc <= elcc->numUsePriceEscalation; ++nUsePriceEsc) {
1477 8 : Constant::eResource curResource = elcc->UsePriceEscalation(nUsePriceEsc).resource;
1478 8 : if (curResource != Constant::eResource::Invalid) {
1479 199 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1480 : // the following is based on UPV* formula from NIST 135 supplement but is for a single year
1481 191 : effectiveYear = double(jYear) - DiscConv2EffectiveYearAdjustment[static_cast<int>(elcc->discountConvention)];
1482 191 : elcc->energySPV[jYear][static_cast<int>(curResource)] =
1483 191 : elcc->UsePriceEscalation(nUsePriceEsc).Escalation(jYear) / std::pow(1.0 + curDiscountRate, effectiveYear);
1484 : }
1485 : }
1486 : }
1487 42 : for (iCashFlow = 0; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1488 40 : switch (elcc->CashFlow[iCashFlow].pvKind) {
1489 7 : case PrValKind::NonEnergy: {
1490 7 : totalPV = 0.0;
1491 164 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1492 157 : elcc->CashFlow[iCashFlow].yrPresVal(jYear) = elcc->CashFlow[iCashFlow].yrAmount(jYear) * elcc->SPV(jYear);
1493 157 : totalPV += elcc->CashFlow[iCashFlow].yrPresVal(jYear);
1494 : }
1495 7 : elcc->CashFlow[iCashFlow].presentValue = totalPV;
1496 7 : break;
1497 : }
1498 1 : case PrValKind::Energy: {
1499 1 : Constant::eResource curResource = elcc->CashFlow[iCashFlow].Resource;
1500 1 : if (curResource != Constant::eResource::Invalid) {
1501 1 : totalPV = 0.0;
1502 23 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1503 22 : elcc->CashFlow[iCashFlow].yrPresVal(jYear) =
1504 22 : elcc->CashFlow[iCashFlow].yrAmount(jYear) * elcc->energySPV[jYear][static_cast<int>(curResource)];
1505 22 : totalPV += elcc->CashFlow[iCashFlow].yrPresVal(jYear);
1506 : }
1507 1 : elcc->CashFlow[iCashFlow].presentValue = totalPV;
1508 : }
1509 1 : break;
1510 : }
1511 : // case iPrValKind::NotComputed: included in default
1512 32 : default:
1513 32 : break; // do nothing
1514 : }
1515 : }
1516 : // sum by category
1517 34 : for (int i = 0; i < CostCategory::Num; ++i) {
1518 32 : elcc->CashFlow[i].presentValue = 0; // initialize value to zero before summing in next for loop
1519 : }
1520 10 : for (iCashFlow = CostCategory::Num; iCashFlow < elcc->numCashFlow; ++iCashFlow) {
1521 8 : int curCategory = elcc->CashFlow[iCashFlow].Category;
1522 8 : if ((curCategory < CostCategory::Num) && (curCategory >= 0)) {
1523 8 : elcc->CashFlow[curCategory].presentValue += elcc->CashFlow[iCashFlow].presentValue;
1524 187 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1525 179 : elcc->CashFlow[curCategory].yrPresVal(jYear) += elcc->CashFlow[iCashFlow].yrPresVal(jYear);
1526 : }
1527 : }
1528 : }
1529 : // create total categories
1530 2 : elcc->CashFlow[CostCategory::TotEnergy].presentValue = elcc->CashFlow[CostCategory::Energy].presentValue;
1531 2 : elcc->CashFlow[CostCategory::TotOper].presentValue =
1532 2 : elcc->CashFlow[CostCategory::Maintenance].presentValue + elcc->CashFlow[CostCategory::Repair].presentValue +
1533 2 : elcc->CashFlow[CostCategory::Operation].presentValue + elcc->CashFlow[CostCategory::Replacement].presentValue +
1534 2 : elcc->CashFlow[CostCategory::MinorOverhaul].presentValue + elcc->CashFlow[CostCategory::MajorOverhaul].presentValue +
1535 2 : elcc->CashFlow[CostCategory::OtherOperational].presentValue + elcc->CashFlow[CostCategory::Water].presentValue +
1536 2 : elcc->CashFlow[CostCategory::Energy].presentValue;
1537 4 : elcc->CashFlow[CostCategory::TotCaptl].presentValue = elcc->CashFlow[CostCategory::Construction].presentValue +
1538 2 : elcc->CashFlow[CostCategory::Salvage].presentValue +
1539 2 : elcc->CashFlow[CostCategory::OtherCapital].presentValue;
1540 2 : elcc->CashFlow[CostCategory::TotGrand].presentValue =
1541 2 : elcc->CashFlow[CostCategory::TotOper].presentValue + elcc->CashFlow[CostCategory::TotCaptl].presentValue;
1542 49 : for (jYear = 1; jYear <= elcc->lengthStudyYears; ++jYear) {
1543 47 : elcc->CashFlow[CostCategory::TotEnergy].yrPresVal(jYear) = elcc->CashFlow[CostCategory::Energy].yrPresVal(jYear);
1544 47 : elcc->CashFlow[CostCategory::TotOper].yrPresVal(jYear) =
1545 47 : elcc->CashFlow[CostCategory::Maintenance].yrPresVal(jYear) + elcc->CashFlow[CostCategory::Repair].yrPresVal(jYear) +
1546 47 : elcc->CashFlow[CostCategory::Operation].yrPresVal(jYear) + elcc->CashFlow[CostCategory::Replacement].yrPresVal(jYear) +
1547 47 : elcc->CashFlow[CostCategory::MinorOverhaul].yrPresVal(jYear) + elcc->CashFlow[CostCategory::MajorOverhaul].yrPresVal(jYear) +
1548 47 : elcc->CashFlow[CostCategory::OtherOperational].yrPresVal(jYear) + elcc->CashFlow[CostCategory::Water].yrPresVal(jYear) +
1549 47 : elcc->CashFlow[CostCategory::Energy].yrPresVal(jYear);
1550 94 : elcc->CashFlow[CostCategory::TotCaptl].yrPresVal(jYear) = elcc->CashFlow[CostCategory::Construction].yrPresVal(jYear) +
1551 47 : elcc->CashFlow[CostCategory::Salvage].yrPresVal(jYear) +
1552 47 : elcc->CashFlow[CostCategory::OtherCapital].yrPresVal(jYear);
1553 47 : elcc->CashFlow[CostCategory::TotGrand].yrPresVal(jYear) =
1554 47 : elcc->CashFlow[CostCategory::TotOper].yrPresVal(jYear) + elcc->CashFlow[CostCategory::TotCaptl].yrPresVal(jYear);
1555 : }
1556 2 : }
1557 :
1558 2 : void ComputeTaxAndDepreciation(EnergyPlusData &state)
1559 : {
1560 : // SUBROUTINE INFORMATION:
1561 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1562 : // DATE WRITTEN August 2010
1563 : // MODIFIED na
1564 : // RE-ENGINEERED na
1565 :
1566 : // PURPOSE OF THIS SUBROUTINE:
1567 : // Compute the present value after factoring in taxes
1568 : // and depreciation.
1569 :
1570 : // METHODOLOGY EMPLOYED:
1571 :
1572 : // REFERENCES:
1573 : // na
1574 :
1575 : // USE STATEMENTS:
1576 :
1577 : // Locals
1578 : // SUBROUTINE ARGUMENT DEFINITIONS:
1579 : // na
1580 :
1581 : // SUBROUTINE PARAMETER DEFINITIONS:
1582 :
1583 : // INTERFACE BLOCK SPECIFICATIONS
1584 : // na
1585 :
1586 : // DERIVED TYPE DEFINITIONS
1587 : // na
1588 :
1589 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1590 : Real64 curCapital;
1591 : int curDepYear;
1592 : int iYear;
1593 : int jYear;
1594 :
1595 2 : auto &elcc(state.dataEconLifeCycleCost);
1596 :
1597 2 : elcc->DepreciatedCapital.allocate(elcc->lengthStudyYears);
1598 2 : elcc->TaxableIncome.allocate(elcc->lengthStudyYears);
1599 2 : elcc->Taxes.allocate(elcc->lengthStudyYears);
1600 2 : elcc->AfterTaxCashFlow.allocate(elcc->lengthStudyYears);
1601 2 : elcc->AfterTaxPresentValue.allocate(elcc->lengthStudyYears);
1602 :
1603 : // Depreciation factors are based on IRS Publication 946 for 2009 "How to Depreciate Property"
1604 : // The MACRS valus are based on Modified Accelerated Cost Recovery System GDS for 3, 5, 7, 10 year
1605 : // property are based on 200% depreciation method shown in Appendix A using half year. 15 and 20 are
1606 : // based on 150% (Chart 1). For Straight Line depreciation GDS is used for 27 years (actually 27.5)
1607 : // 31 years (actually 31.5 years) and 39 years using mid-month. For 40 years ADS is used (chart 2)
1608 : // Table A-1 is used for 3, 4, 5, 10, 15 and 20 years. Table A-6 is for 27 years. Table A-7 for 31 years.
1609 : // Table A-7a for 39 years. Table A-13 for 40 years. These years are a classification of property
1610 : // and should not be confused with the length of the study. For 27 years, 31 years, 39 years and 40 years
1611 : // the June value was used.
1612 :
1613 : // convert construction costs (not salvage) into depreciation
1614 2 : elcc->DepreciatedCapital = 0.0; // set all years to zero
1615 49 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1616 47 : curCapital = elcc->CashFlow[CostCategory::Construction].yrAmount(iYear) + elcc->CashFlow[CostCategory::OtherCapital].yrAmount(iYear);
1617 1974 : for (jYear = 0; jYear < SizeDepr; ++jYear) {
1618 1927 : curDepYear = iYear + jYear; // start depreciating with the year that the capital was shown and go to years following
1619 1927 : if (curDepYear <= elcc->lengthStudyYears) {
1620 578 : elcc->DepreciatedCapital(curDepYear) +=
1621 578 : curCapital * (DepreciationPercentTable[static_cast<int>(elcc->depreciationMethod)][jYear] / 100);
1622 : }
1623 : }
1624 : }
1625 : // Using Newnan pg 3880
1626 : // before-tax cash flow
1627 : // depreciation
1628 : // taxable income (before-tax cash flow - depreciation)
1629 : // income taxes (taxable income x incremental tax rate)
1630 : // after-tax cash flow (before-tax cash flow - income taxes)
1631 49 : for (iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1632 47 : elcc->TaxableIncome(iYear) = elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear) - elcc->DepreciatedCapital(iYear);
1633 47 : elcc->Taxes(iYear) = elcc->TaxableIncome(iYear) * elcc->taxRate;
1634 47 : elcc->AfterTaxCashFlow(iYear) = elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear) - elcc->Taxes(iYear);
1635 : // the present value after taxes is pretax present value minus the present value of the taxes
1636 47 : elcc->AfterTaxPresentValue(iYear) = elcc->CashFlow[CostCategory::TotGrand].yrPresVal(iYear) - elcc->Taxes(iYear) * elcc->SPV(iYear);
1637 : }
1638 2 : }
1639 :
1640 : //======================================================================================================================
1641 : //======================================================================================================================
1642 :
1643 : // OUTPUT ROUTINES
1644 :
1645 : //======================================================================================================================
1646 : //======================================================================================================================
1647 :
1648 2 : void WriteTabularLifeCycleCostReport(EnergyPlusData &state)
1649 : {
1650 : // SUBROUTINE INFORMATION:
1651 : // AUTHOR Jason Glazer of GARD Analytics, Inc.
1652 : // DATE WRITTEN June 2010
1653 : // MODIFIED na
1654 : // RE-ENGINEERED na
1655 :
1656 : // PURPOSE OF THIS SUBROUTINE:
1657 : // Write the output report related to life-cycle costing
1658 : // to the tabular output file.
1659 :
1660 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1661 : // all arrays are in the format: (row, column)
1662 2 : Array1D_string columnHead;
1663 2 : Array1D_int columnWidth;
1664 2 : Array1D_string rowHead;
1665 2 : Array2D_string tableBody;
1666 :
1667 2 : auto const &elcc = state.dataEconLifeCycleCost;
1668 :
1669 2 : if (elcc->LCCparamPresent && state.dataOutRptTab->displayLifeCycleCostReport) {
1670 : //---------------------------------
1671 : // Life-Cycle Cost Verification and Results Report
1672 : //---------------------------------
1673 1 : OutputReportTabular::WriteReportHeaders(state, "Life-Cycle Cost Report", "Entire Facility", OutputProcessor::StoreType::Average);
1674 : //---- Life-Cycle Cost Parameters
1675 1 : rowHead.allocate(11);
1676 1 : columnHead.allocate(1);
1677 1 : columnWidth.allocate(1);
1678 1 : tableBody.allocate(1, 11);
1679 1 : tableBody = "";
1680 1 : rowHead(1) = "Name";
1681 1 : rowHead(2) = "Discounting Convention";
1682 1 : rowHead(3) = "Inflation Approach";
1683 1 : rowHead(4) = "Real Discount Rate";
1684 1 : rowHead(5) = "Nominal Discount Rate";
1685 1 : rowHead(6) = "Inflation";
1686 1 : rowHead(7) = "Base Date";
1687 1 : rowHead(8) = "Service Date";
1688 1 : rowHead(9) = "Length of Study Period in Years";
1689 1 : rowHead(10) = "Tax rate";
1690 1 : rowHead(11) = "Depreciation Method";
1691 1 : columnHead(1) = "Value";
1692 :
1693 1 : tableBody(1, 1) = elcc->LCCname;
1694 1 : tableBody(1, 2) = DiscConvNames[static_cast<int>(elcc->discountConvention)];
1695 1 : tableBody(1, 3) = InflApprNames[static_cast<int>(elcc->inflationApproach)];
1696 1 : if (elcc->inflationApproach == InflAppr::ConstantDollar) {
1697 1 : tableBody(1, 4) = OutputReportTabular::RealToStr(elcc->realDiscountRate, 4);
1698 : } else {
1699 0 : tableBody(1, 4) = "-- N/A --";
1700 : }
1701 1 : if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1702 0 : tableBody(1, 5) = OutputReportTabular::RealToStr(elcc->nominalDiscountRate, 4);
1703 : } else {
1704 1 : tableBody(1, 5) = "-- N/A --";
1705 : }
1706 1 : if (elcc->inflationApproach == InflAppr::CurrentDollar) {
1707 0 : tableBody(1, 6) = OutputReportTabular::RealToStr(elcc->inflation, 4);
1708 : } else {
1709 1 : tableBody(1, 6) = "-- N/A --";
1710 : }
1711 1 : tableBody(1, 7) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear);
1712 1 : tableBody(1, 8) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->serviceDateMonth)], elcc->serviceDateYear);
1713 1 : tableBody(1, 9) = fmt::to_string(elcc->lengthStudyYears);
1714 1 : tableBody(1, 10) = OutputReportTabular::RealToStr(elcc->taxRate, 4);
1715 1 : tableBody(1, 11) = DeprMethodNames[static_cast<int>(elcc->depreciationMethod)];
1716 :
1717 1 : columnWidth = 14; // array assignment - same for all columns
1718 1 : OutputReportTabular::WriteSubtitle(state, "Life-Cycle Cost Parameters");
1719 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1720 1 : if (state.dataSQLiteProcedures->sqlite) {
1721 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1722 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Life-Cycle Cost Parameters");
1723 : }
1724 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1725 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1726 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Life-Cycle Cost Parameters");
1727 : }
1728 :
1729 1 : columnHead.deallocate();
1730 1 : rowHead.deallocate();
1731 1 : columnWidth.deallocate();
1732 1 : tableBody.deallocate();
1733 : //---- Use Price Escalation
1734 1 : int numColumns = max(1, elcc->numUsePriceEscalation);
1735 1 : rowHead.allocate(elcc->lengthStudyYears + 2);
1736 1 : columnHead.allocate(numColumns);
1737 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1738 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears + 2);
1739 1 : tableBody = "";
1740 1 : columnHead = "none";
1741 1 : rowHead(1) = "Resource";
1742 1 : rowHead(2) = "Start Date";
1743 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1744 22 : rowHead(iYear + 2) = fmt::to_string(iYear);
1745 : }
1746 4 : for (int jObj = 1; jObj <= elcc->numUsePriceEscalation; ++jObj) { // loop through objects not columns to add names
1747 3 : columnHead(jObj) = elcc->UsePriceEscalation(jObj).name;
1748 3 : tableBody(jObj, 1) = Constant::eResourceNames[static_cast<int>(elcc->UsePriceEscalation(jObj).resource)];
1749 6 : tableBody(jObj, 2) = format("{} {}",
1750 3 : Util::MonthNamesCC[static_cast<int>(elcc->UsePriceEscalation(jObj).escalationStartMonth)],
1751 6 : elcc->UsePriceEscalation(jObj).escalationStartYear);
1752 : }
1753 4 : for (int jObj = 1; jObj <= elcc->numUsePriceEscalation; ++jObj) {
1754 69 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1755 66 : tableBody(jObj, iYear + 2) = OutputReportTabular::RealToStr(elcc->UsePriceEscalation(jObj).Escalation(iYear), 6);
1756 : }
1757 : }
1758 1 : OutputReportTabular::WriteSubtitle(state, "Use Price Escalation");
1759 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1760 1 : if (state.dataSQLiteProcedures->sqlite) {
1761 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1762 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Price Escalation");
1763 : }
1764 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1765 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1766 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Price Escalation");
1767 : }
1768 1 : columnHead.deallocate();
1769 1 : rowHead.deallocate();
1770 1 : columnWidth.deallocate();
1771 1 : tableBody.deallocate();
1772 : //---- Use Adjustment
1773 1 : if (elcc->numUseAdjustment >= 1) { // only create table if objects used
1774 1 : numColumns = max(1, elcc->numUseAdjustment);
1775 1 : int numYears = elcc->lengthStudyYears - (elcc->serviceDateYear - elcc->baseDateYear);
1776 1 : rowHead.allocate(numYears + 1);
1777 1 : columnHead.allocate(numColumns);
1778 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1779 1 : tableBody.allocate(numColumns, numYears + 1);
1780 1 : tableBody = "";
1781 1 : columnHead = "none";
1782 1 : rowHead(1) = "";
1783 21 : for (int iYear = 1; iYear <= numYears; ++iYear) {
1784 20 : rowHead(iYear + 1) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->serviceDateMonth)], elcc->serviceDateYear + iYear - 1);
1785 : }
1786 2 : for (int jObj = 1; jObj <= elcc->numUseAdjustment; ++jObj) { // loop through objects not columns to add names
1787 1 : columnHead(jObj) = elcc->UseAdjustment(jObj).name;
1788 1 : tableBody(jObj, 1) = Constant::eResourceNames[static_cast<int>(elcc->UseAdjustment(jObj).resource)];
1789 : }
1790 2 : for (int jObj = 1; jObj <= elcc->numUseAdjustment; ++jObj) {
1791 21 : for (int iYear = 1; iYear <= numYears; ++iYear) {
1792 20 : tableBody(jObj, iYear + 1) = OutputReportTabular::RealToStr(elcc->UseAdjustment(jObj).Adjustment(iYear), 6);
1793 : }
1794 : }
1795 1 : OutputReportTabular::WriteSubtitle(state, "Use Adjustment");
1796 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1797 1 : if (state.dataSQLiteProcedures->sqlite) {
1798 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1799 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Adjustment");
1800 : }
1801 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1802 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1803 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Use Adjustment");
1804 : }
1805 1 : columnHead.deallocate();
1806 1 : rowHead.deallocate();
1807 1 : columnWidth.deallocate();
1808 1 : tableBody.deallocate();
1809 : }
1810 : //---- Cash Flow for Recurring and Nonrecurring Costs
1811 1 : numColumns = max(1, elcc->numRecurringCosts + elcc->numNonrecurringCost);
1812 1 : rowHead.allocate(elcc->lengthStudyYears + 1);
1813 1 : columnHead.allocate(numColumns);
1814 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1815 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears + 1);
1816 1 : tableBody = "";
1817 1 : rowHead(1) = "";
1818 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1819 22 : rowHead(iYear + 1) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1820 : }
1821 7 : for (int jObj = 0; jObj < (elcc->numRecurringCosts + elcc->numNonrecurringCost); ++jObj) {
1822 6 : int curCashFlow = CostCategory::Num + jObj;
1823 6 : columnHead(jObj + 1) = elcc->CashFlow[curCashFlow].name;
1824 :
1825 6 : tableBody(jObj + 1, 1) = SourceKindTypeNames[static_cast<int>(elcc->CashFlow[curCashFlow].SourceKind)];
1826 :
1827 138 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1828 132 : tableBody(jObj + 1, iYear + 1) = OutputReportTabular::RealToStr(elcc->CashFlow[curCashFlow].yrAmount(iYear), 2);
1829 : }
1830 : }
1831 1 : OutputReportTabular::WriteSubtitle(state, "Cash Flow for Recurring and Nonrecurring Costs (Without Escalation)");
1832 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1833 1 : if (state.dataSQLiteProcedures->sqlite) {
1834 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(tableBody,
1835 : rowHead,
1836 : columnHead,
1837 : "Life-Cycle Cost Report",
1838 : "Entire Facility",
1839 : "Cash Flow for Recurring and Nonrecurring Costs (Without Escalation)");
1840 : }
1841 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1842 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1843 : tableBody,
1844 : rowHead,
1845 : columnHead,
1846 : "Life-Cycle Cost Report",
1847 : "Entire Facility",
1848 : "Cash Flow for Recurring and Nonrecurring Costs (Without Escalation)");
1849 : }
1850 1 : columnHead.deallocate();
1851 1 : rowHead.deallocate();
1852 1 : columnWidth.deallocate();
1853 1 : tableBody.deallocate();
1854 : //---- Energy and Water Cost Cash Flows (Without Escalation)
1855 1 : numColumns = max(1, elcc->numResourcesUsed + 1);
1856 1 : rowHead.allocate(elcc->lengthStudyYears);
1857 1 : columnHead.allocate(numColumns);
1858 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1859 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears);
1860 1 : tableBody = "";
1861 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1862 22 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1863 : }
1864 2 : for (int jObj = 0; jObj < elcc->numResourcesUsed; ++jObj) {
1865 1 : int curCashFlow = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost + jObj;
1866 1 : columnHead(jObj + 1) = elcc->CashFlow[curCashFlow].name;
1867 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1868 22 : tableBody(jObj + 1, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[curCashFlow].yrAmount(iYear), 2);
1869 : }
1870 : }
1871 1 : columnHead(numColumns) = Total;
1872 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1873 44 : tableBody(elcc->numResourcesUsed + 1, iYear) = OutputReportTabular::RealToStr(
1874 44 : elcc->CashFlow[CostCategory::TotEnergy].yrAmount(iYear) + elcc->CashFlow[CostCategory::Water].yrAmount(iYear), 2);
1875 : }
1876 1 : OutputReportTabular::WriteSubtitle(state, "Energy and Water Cost Cash Flows (Without Escalation)");
1877 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1878 1 : if (state.dataSQLiteProcedures->sqlite) {
1879 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1880 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (Without Escalation)");
1881 : }
1882 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1883 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1884 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (Without Escalation)");
1885 : }
1886 1 : columnHead.deallocate();
1887 1 : rowHead.deallocate();
1888 1 : columnWidth.deallocate();
1889 1 : tableBody.deallocate();
1890 : //---- Energy and Water Cost Cash Flows (With Escalation)
1891 1 : numColumns = max(1, elcc->numResourcesUsed + 1);
1892 1 : rowHead.allocate(elcc->lengthStudyYears);
1893 1 : columnHead.allocate(numColumns);
1894 1 : columnWidth.dimension(numColumns, 14); // array assignment - same for all columns
1895 1 : tableBody.allocate(numColumns, elcc->lengthStudyYears);
1896 1 : tableBody = "";
1897 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1898 22 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1899 : }
1900 2 : for (int jObj = 0; jObj < elcc->numResourcesUsed; ++jObj) {
1901 1 : int curCashFlow = CostCategory::Num + elcc->numRecurringCosts + elcc->numNonrecurringCost + jObj;
1902 1 : columnHead(jObj + 1) = elcc->CashFlow[curCashFlow].name;
1903 1 : Constant::eResource curResource = elcc->CashFlow[curCashFlow].Resource;
1904 1 : if (elcc->CashFlow[curCashFlow].Resource != Constant::eResource::Water) {
1905 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1906 22 : tableBody(jObj + 1, iYear) = OutputReportTabular::RealToStr(elcc->EscalatedEnergy[iYear][static_cast<int>(curResource)], 2);
1907 : }
1908 : } else { // for water just use the original cashflow since not involved in escalation
1909 0 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1910 0 : tableBody(jObj + 1, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[curCashFlow].yrAmount(iYear), 2);
1911 : }
1912 : }
1913 : }
1914 1 : columnHead(numColumns) = Total;
1915 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1916 22 : tableBody(elcc->numResourcesUsed + 1, iYear) =
1917 44 : OutputReportTabular::RealToStr(elcc->EscalatedTotEnergy(iYear) + elcc->CashFlow[CostCategory::Water].yrAmount(iYear), 2);
1918 : }
1919 1 : OutputReportTabular::WriteSubtitle(state, "Energy and Water Cost Cash Flows (With Escalation)");
1920 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1921 1 : if (state.dataSQLiteProcedures->sqlite) {
1922 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1923 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (With Escalation)");
1924 : }
1925 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1926 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1927 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Energy and Water Cost Cash Flows (With Escalation)");
1928 : }
1929 1 : columnHead.deallocate();
1930 1 : rowHead.deallocate();
1931 1 : columnWidth.deallocate();
1932 1 : tableBody.deallocate();
1933 :
1934 : //---- Capital Cash Flow by Category
1935 1 : rowHead.allocate(elcc->lengthStudyYears);
1936 1 : columnHead.allocate(4);
1937 1 : columnWidth.allocate(4);
1938 1 : columnWidth = 14; // array assignment - same for all columns
1939 1 : tableBody.allocate(4, elcc->lengthStudyYears);
1940 1 : tableBody = "";
1941 4 : for (int CostCategory = CostCategory::Construction, tableColumnIndex = 1; CostCategory <= CostCategory::OtherCapital;
1942 3 : ++tableColumnIndex, ++CostCategory) {
1943 3 : columnHead(tableColumnIndex) = CostCategoryNamesNoSpace[static_cast<int>(CostCategory)];
1944 : }
1945 1 : columnHead(4) = Total;
1946 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1947 22 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1948 110 : for (int CostCategory = CostCategory::Construction, tableColumnIndex = 1; CostCategory <= CostCategory::TotCaptl;
1949 88 : ++tableColumnIndex, ++CostCategory) {
1950 88 : tableBody(tableColumnIndex, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[CostCategory].yrAmount(iYear), 2);
1951 : }
1952 : }
1953 1 : OutputReportTabular::WriteSubtitle(state, "Capital Cash Flow by Category (Without Escalation)");
1954 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1955 1 : if (state.dataSQLiteProcedures->sqlite) {
1956 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1957 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Capital Cash Flow by Category (Without Escalation)");
1958 : }
1959 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1960 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1961 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Capital Cash Flow by Category (Without Escalation)");
1962 : }
1963 1 : columnHead.deallocate();
1964 1 : rowHead.deallocate();
1965 1 : columnWidth.deallocate();
1966 1 : tableBody.deallocate();
1967 : //---- Operating Cash Flow by Category (Without Escalation)
1968 1 : rowHead.allocate(elcc->lengthStudyYears);
1969 1 : columnHead.allocate(10);
1970 1 : columnWidth.allocate(10);
1971 1 : columnWidth = 14; // array assignment - same for all columns
1972 1 : tableBody.allocate(10, elcc->lengthStudyYears);
1973 1 : tableBody = "";
1974 10 : for (int CostCategory = CostCategory::Maintenance, tableColumnIndex = 1; CostCategory <= CostCategory::Energy;
1975 9 : ++tableColumnIndex, ++CostCategory) {
1976 9 : columnHead(tableColumnIndex) = CostCategoryNamesNoSpace[CostCategory];
1977 : }
1978 1 : columnHead(10) = Total;
1979 :
1980 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
1981 22 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
1982 242 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotOper; ++CashFlowCostCategory) {
1983 220 : tableBody(CashFlowCostCategory + 1, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[CashFlowCostCategory].yrAmount(iYear), 2);
1984 : }
1985 : }
1986 1 : OutputReportTabular::WriteSubtitle(state, "Operating Cash Flow by Category (Without Escalation)");
1987 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
1988 1 : if (state.dataSQLiteProcedures->sqlite) {
1989 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
1990 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (Without Escalation)");
1991 : }
1992 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
1993 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
1994 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (Without Escalation)");
1995 : }
1996 1 : columnHead.deallocate();
1997 1 : rowHead.deallocate();
1998 1 : columnWidth.deallocate();
1999 1 : tableBody.deallocate();
2000 : //---- Operating Cash Flow by Category (With Escalation)
2001 1 : rowHead.allocate(elcc->lengthStudyYears);
2002 1 : columnHead.allocate(10);
2003 1 : columnWidth.allocate(10);
2004 1 : columnWidth = 14; // array assignment - same for all columns
2005 1 : tableBody.allocate(10, elcc->lengthStudyYears);
2006 1 : tableBody = "";
2007 10 : for (int CostCategory = CostCategory::Maintenance, tableColumnIndex = 1; CostCategory <= CostCategory::Energy;
2008 9 : ++tableColumnIndex, ++CostCategory) {
2009 9 : columnHead(tableColumnIndex) = CostCategoryNamesNoSpace[CostCategory];
2010 : }
2011 1 : columnHead(10) = Total;
2012 :
2013 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2014 22 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2015 198 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::Water; ++CashFlowCostCategory) {
2016 176 : tableBody(CashFlowCostCategory + 1, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[CashFlowCostCategory].yrAmount(iYear), 2);
2017 : }
2018 22 : tableBody(9, iYear) = OutputReportTabular::RealToStr(elcc->EscalatedTotEnergy(iYear), 2);
2019 22 : Real64 yearly_total_cost = elcc->CashFlow[CostCategory::TotOper].yrAmount(iYear) + elcc->EscalatedTotEnergy(iYear) -
2020 22 : elcc->CashFlow[CostCategory::TotEnergy].yrAmount(iYear);
2021 22 : tableBody(10, iYear) = OutputReportTabular::RealToStr(yearly_total_cost, 2);
2022 : }
2023 1 : OutputReportTabular::WriteSubtitle(state, "Operating Cash Flow by Category (With Escalation)");
2024 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2025 1 : if (state.dataSQLiteProcedures->sqlite) {
2026 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2027 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (With Escalation)");
2028 : }
2029 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2030 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2031 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Operating Cash Flow by Category (With Escalation)");
2032 : }
2033 1 : columnHead.deallocate();
2034 1 : rowHead.deallocate();
2035 1 : columnWidth.deallocate();
2036 1 : tableBody.deallocate();
2037 : //---- DEBUG ONLY - Monthly Cash Flows
2038 : // bool showMonthlyCashFlows = false;
2039 : // if (showMonthlyCashFlows) {
2040 : // rowHead.allocate(elcc->lengthStudyTotalMonths);
2041 : // columnHead.allocate(elcc->numCashFlow);
2042 : // columnWidth.allocate(elcc->numCashFlow);
2043 : // tableBody.allocate(elcc->numCashFlow, elcc->lengthStudyTotalMonths);
2044 : // tableBody = "";
2045 : // columnHead(1) = "mnt";
2046 : // columnHead(2) = "rpr";
2047 : // columnHead(3) = "opr";
2048 : // columnHead(4) = "repl";
2049 : // columnHead(5) = "mOvhl";
2050 : // columnHead(6) = "MOvhl";
2051 : // columnHead(7) = "oOpr";
2052 : // columnHead(8) = "cons";
2053 : // columnHead(9) = "slvg";
2054 : // columnHead(10) = "oCap";
2055 : // columnHead(11) = "H2O";
2056 : // columnHead(12) = "ene";
2057 : // columnHead(13) = "tEne";
2058 : // columnHead(14) = "tOpr";
2059 : // columnHead(15) = "tCap";
2060 : // columnHead(16) = "Totl";
2061 : // for (int jObj = CostCategory::Num; jObj < elcc->numCashFlow; ++jObj) {
2062 : // columnHead(jObj + 1) = elcc->CashFlow[jObj].name;
2063 : // }
2064 : // for (int kMonth = 1; kMonth <= elcc->lengthStudyTotalMonths; ++kMonth) {
2065 : // rowHead(kMonth) = format("{} {}",
2066 : // Util::MonthNamesCC[static_cast<int>(1 + (kMonth + elcc->baseDateMonth - 2) % 12) - 1],
2067 : // elcc->baseDateYear + int((kMonth - 1) / 12));
2068 : // for (int jObj = 0; jObj < elcc->numCashFlow; ++jObj) {
2069 : // tableBody(jObj + 1, kMonth) = OutputReportTabular::RealToStr(elcc->CashFlow[jObj].mnAmount(kMonth), 2);
2070 : // }
2071 : // }
2072 : // OutputReportTabular::WriteSubtitle(state, "DEBUG ONLY - Monthly Cash Flows");
2073 : // OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2074 : // if (state.dataSQLiteProcedures->sqlite) {
2075 : // state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2076 : // tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "DEBUG ONLY - Monthly Cash Flows");
2077 : // }
2078 : // if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2079 : // state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2080 : // tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "DEBUG ONLY - Monthly Cash Flows");
2081 : // }
2082 : // columnHead.deallocate();
2083 : // rowHead.deallocate();
2084 : // columnWidth.deallocate();
2085 : // tableBody.deallocate();
2086 : //}
2087 : //---- Monthly Total Cash Flow
2088 1 : rowHead.allocate(elcc->lengthStudyYears);
2089 1 : columnHead.allocate(12);
2090 1 : columnWidth.allocate(12);
2091 1 : columnWidth = 14; // array assignment - same for all columns
2092 1 : tableBody.allocate(12, elcc->lengthStudyYears);
2093 1 : tableBody = "";
2094 13 : for (int kMonth = 1; kMonth <= 12; ++kMonth) {
2095 12 : columnHead(kMonth) = Util::MonthNamesCC[static_cast<int>(kMonth - 1)];
2096 : }
2097 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2098 22 : rowHead(iYear) = fmt::to_string(elcc->baseDateYear + iYear - 1);
2099 286 : for (int kMonth = 1; kMonth <= 12; ++kMonth) {
2100 264 : tableBody(kMonth, iYear) =
2101 528 : OutputReportTabular::RealToStr(elcc->CashFlow[CostCategory::TotGrand].mnAmount((iYear - 1) * 12 + kMonth), 2);
2102 : }
2103 : }
2104 1 : OutputReportTabular::WriteSubtitle(state, "Monthly Total Cash Flow (Without Escalation)");
2105 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2106 1 : if (state.dataSQLiteProcedures->sqlite) {
2107 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2108 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Monthly Total Cash Flow (Without Escalation)");
2109 : }
2110 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2111 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2112 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Monthly Total Cash Flow (Without Escalation)");
2113 : }
2114 1 : columnHead.deallocate();
2115 1 : rowHead.deallocate();
2116 1 : columnWidth.deallocate();
2117 1 : tableBody.deallocate();
2118 : //---- Present Value for Recurring, Nonrecurring and Energy Costs
2119 1 : int numRows = max(1, elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed);
2120 1 : rowHead.allocate(numRows + 1);
2121 1 : columnHead.allocate(5);
2122 1 : columnWidth.allocate(5);
2123 1 : columnWidth = 14; // array assignment - same for all columns
2124 1 : tableBody.allocate(5, numRows + 1);
2125 1 : tableBody = "";
2126 1 : columnHead(1) = "Category";
2127 1 : columnHead(2) = "Kind";
2128 1 : columnHead(3) = "Cost";
2129 1 : columnHead(4) = "Present Value";
2130 1 : columnHead(5) = "Present Value Factor";
2131 1 : Real64 totalPV = 0.0;
2132 1 : rowHead(numRows + 1) = TotalUC;
2133 8 : for (int jObj = 0; jObj < (elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed); ++jObj) {
2134 7 : int offset = CostCategory::Num;
2135 7 : rowHead(jObj + 1) = elcc->CashFlow[offset + jObj].name;
2136 7 : switch (elcc->CashFlow[offset + jObj].Category) {
2137 7 : case CostCategory::Maintenance:
2138 : case CostCategory::Repair:
2139 : case CostCategory::Operation:
2140 : case CostCategory::Replacement:
2141 : case CostCategory::MinorOverhaul:
2142 : case CostCategory::MajorOverhaul:
2143 : case CostCategory::OtherOperational:
2144 : case CostCategory::Construction:
2145 : case CostCategory::Salvage:
2146 : case CostCategory::OtherCapital:
2147 : case CostCategory::Water:
2148 : case CostCategory::Energy: {
2149 7 : tableBody(1, jObj + 1) = CostCategoryNames[static_cast<int>(elcc->CashFlow[offset + jObj].Category)];
2150 7 : break;
2151 : }
2152 0 : default:
2153 0 : tableBody(1, jObj + 1) = "-";
2154 0 : break;
2155 : }
2156 7 : switch (elcc->CashFlow[offset + jObj].SourceKind) {
2157 6 : case SourceKindType::Nonrecurring:
2158 : case SourceKindType::Recurring: {
2159 6 : tableBody(2, jObj + 1) = SourceKindTypeNames[static_cast<int>(elcc->CashFlow[offset + jObj].SourceKind)];
2160 6 : break;
2161 : }
2162 1 : case SourceKindType::Resource: {
2163 1 : if (elcc->CashFlow[offset + jObj].Category == CostCategory::Water) {
2164 0 : tableBody(2, jObj + 1) = ResourceCostCategoryNames[static_cast<int>(ResourceCostCategory::Water)];
2165 : } else {
2166 1 : tableBody(2, jObj + 1) = ResourceCostCategoryNames[static_cast<int>(ResourceCostCategory::Energy)];
2167 : }
2168 1 : break;
2169 : }
2170 0 : default: {
2171 0 : tableBody(2, jObj + 1) = "-";
2172 0 : break;
2173 : }
2174 : }
2175 7 : tableBody(3, jObj + 1) = OutputReportTabular::RealToStr(elcc->CashFlow[offset + jObj].orginalCost, 2);
2176 7 : tableBody(4, jObj + 1) = OutputReportTabular::RealToStr(elcc->CashFlow[offset + jObj].presentValue, 2);
2177 7 : totalPV += elcc->CashFlow[offset + jObj].presentValue;
2178 7 : if (elcc->CashFlow[offset + jObj].orginalCost != 0.0) {
2179 7 : tableBody(5, jObj + 1) =
2180 14 : OutputReportTabular::RealToStr(elcc->CashFlow[offset + jObj].presentValue / elcc->CashFlow[offset + jObj].orginalCost, 4);
2181 : } else {
2182 0 : tableBody(5, jObj + 1) = "-";
2183 : }
2184 : }
2185 1 : tableBody(4, numRows + 1) = OutputReportTabular::RealToStr(totalPV, 2);
2186 1 : OutputReportTabular::WriteSubtitle(state, "Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)");
2187 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2188 1 : if (state.dataSQLiteProcedures->sqlite) {
2189 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2190 : tableBody,
2191 : rowHead,
2192 : columnHead,
2193 : "Life-Cycle Cost Report",
2194 : "Entire Facility",
2195 : "Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)");
2196 : }
2197 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2198 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2199 : tableBody,
2200 : rowHead,
2201 : columnHead,
2202 : "Life-Cycle Cost Report",
2203 : "Entire Facility",
2204 : "Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)");
2205 : }
2206 1 : columnHead.deallocate();
2207 1 : rowHead.deallocate();
2208 1 : columnWidth.deallocate();
2209 1 : tableBody.deallocate();
2210 : //---- Present Value by Category
2211 1 : rowHead.allocate(16);
2212 1 : columnHead.allocate(1);
2213 1 : columnWidth.allocate(1);
2214 1 : columnWidth = 14; // array assignment - same for all columns
2215 1 : tableBody.allocate(1, 16);
2216 1 : tableBody = "";
2217 17 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotGrand; ++CashFlowCostCategory) {
2218 16 : rowHead(CashFlowCostCategory + 1) = CostCategoryNames[CashFlowCostCategory];
2219 : }
2220 1 : columnHead(1) = "Present Value";
2221 :
2222 17 : for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotGrand; ++CashFlowCostCategory) {
2223 16 : tableBody(1, CashFlowCostCategory + 1) = OutputReportTabular::RealToStr(elcc->CashFlow[CashFlowCostCategory].presentValue, 2);
2224 : }
2225 :
2226 1 : OutputReportTabular::WriteSubtitle(state, "Present Value by Category");
2227 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2228 1 : if (state.dataSQLiteProcedures->sqlite) {
2229 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2230 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Category");
2231 : }
2232 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2233 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2234 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Category");
2235 : }
2236 1 : columnHead.deallocate();
2237 1 : rowHead.deallocate();
2238 1 : columnWidth.deallocate();
2239 1 : tableBody.deallocate();
2240 : //---- Present Value by Year
2241 1 : rowHead.allocate(elcc->lengthStudyYears + 1);
2242 1 : columnHead.allocate(3);
2243 1 : columnWidth.allocate(3);
2244 1 : columnWidth = 14; // array assignment - same for all columns
2245 1 : tableBody.allocate(3, elcc->lengthStudyYears + 1);
2246 1 : tableBody = "";
2247 1 : columnHead(1) = "Total Cost (Without Escalation)";
2248 1 : columnHead(2) = "Total Cost (With Escalation)";
2249 1 : columnHead(3) = "Present Value of Costs";
2250 :
2251 1 : totalPV = 0.0;
2252 23 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2253 22 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2254 22 : tableBody(1, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear), 2);
2255 : // adjust for escalated energy costs
2256 22 : Real64 yearly_total_cost = elcc->CashFlow[CostCategory::TotGrand].yrAmount(iYear) + elcc->EscalatedTotEnergy(iYear) -
2257 22 : elcc->CashFlow[CostCategory::TotEnergy].yrAmount(iYear);
2258 22 : tableBody(2, iYear) = OutputReportTabular::RealToStr(yearly_total_cost, 2);
2259 22 : tableBody(3, iYear) = OutputReportTabular::RealToStr(elcc->CashFlow[CostCategory::TotGrand].yrPresVal(iYear), 2);
2260 22 : totalPV += elcc->CashFlow[CostCategory::TotGrand].yrPresVal(iYear);
2261 : }
2262 :
2263 1 : rowHead(elcc->lengthStudyYears + 1) = TotalUC;
2264 1 : tableBody(3, elcc->lengthStudyYears + 1) = OutputReportTabular::RealToStr(totalPV, 2);
2265 :
2266 1 : OutputReportTabular::WriteSubtitle(state, "Present Value by Year");
2267 1 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2268 1 : if (state.dataSQLiteProcedures->sqlite) {
2269 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2270 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Year");
2271 : }
2272 1 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2273 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2274 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "Present Value by Year");
2275 : }
2276 1 : columnHead.deallocate();
2277 1 : rowHead.deallocate();
2278 1 : columnWidth.deallocate();
2279 1 : tableBody.deallocate();
2280 : //---- After Tax Estimate
2281 1 : if (elcc->taxRate != 0.0) {
2282 0 : rowHead.allocate(elcc->lengthStudyYears + 1);
2283 0 : columnHead.allocate(5);
2284 0 : columnWidth.allocate(5);
2285 0 : columnWidth = 14; // array assignment - same for all columns
2286 0 : tableBody.allocate(5, elcc->lengthStudyYears + 1);
2287 0 : tableBody = "";
2288 0 : columnHead(1) = "Depreciated Capital";
2289 0 : columnHead(2) = "Taxable Income";
2290 0 : columnHead(3) = "Income Taxes";
2291 0 : columnHead(4) = "After Tax Cash Flow";
2292 0 : columnHead(5) = "After Tax Present Value";
2293 :
2294 0 : totalPV = 0.0;
2295 0 : for (int iYear = 1; iYear <= elcc->lengthStudyYears; ++iYear) {
2296 0 : rowHead(iYear) = format("{} {}", Util::MonthNamesCC[static_cast<int>(elcc->baseDateMonth)], elcc->baseDateYear + iYear - 1);
2297 0 : tableBody(1, iYear) = OutputReportTabular::RealToStr(elcc->DepreciatedCapital(iYear), 2);
2298 0 : tableBody(2, iYear) = OutputReportTabular::RealToStr(elcc->TaxableIncome(iYear), 2);
2299 0 : tableBody(3, iYear) = OutputReportTabular::RealToStr(elcc->Taxes(iYear), 2);
2300 0 : tableBody(4, iYear) = OutputReportTabular::RealToStr(elcc->AfterTaxCashFlow(iYear), 2);
2301 0 : tableBody(5, iYear) = OutputReportTabular::RealToStr(elcc->AfterTaxPresentValue(iYear), 2);
2302 0 : totalPV += elcc->AfterTaxPresentValue(iYear);
2303 : }
2304 :
2305 0 : rowHead(elcc->lengthStudyYears + 1) = TotalUC;
2306 0 : tableBody(5, elcc->lengthStudyYears + 1) = OutputReportTabular::RealToStr(totalPV, 2);
2307 :
2308 0 : OutputReportTabular::WriteSubtitle(state, "After Tax Estimate");
2309 0 : OutputReportTabular::WriteTable(state, tableBody, rowHead, columnHead, columnWidth);
2310 0 : if (state.dataSQLiteProcedures->sqlite) {
2311 0 : state.dataSQLiteProcedures->sqlite->createSQLiteTabularDataRecords(
2312 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "After Tax Estimate");
2313 : }
2314 0 : if (state.dataResultsFramework->resultsFramework->timeSeriesAndTabularEnabled()) {
2315 0 : state.dataResultsFramework->resultsFramework->TabularReportsCollection.addReportTable(
2316 : tableBody, rowHead, columnHead, "Life-Cycle Cost Report", "Entire Facility", "After Tax Estimate");
2317 : }
2318 0 : columnHead.deallocate();
2319 0 : rowHead.deallocate();
2320 0 : columnWidth.deallocate();
2321 0 : tableBody.deallocate();
2322 : }
2323 : }
2324 2 : }
2325 :
2326 : } // namespace EnergyPlus::EconomicLifeCycleCost
|