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