1
+ from __future__ import annotations
2
+
1
3
import math
2
4
import sys
5
+ # noinspection PyPackageRequirements
3
6
import numpy as np
4
7
import numpy_financial as npf
5
8
from pint .facets .plain import PlainQuantity
@@ -349,29 +352,50 @@ def CalculateFinancialPerformance(plantlifetime: int,
349
352
return NPV , IRR , VIR , MOIC
350
353
351
354
352
- def CalculateLCOELCOHLCOC (econ , model : Model ) -> tuple :
355
+ def CalculateLCOELCOHLCOC (econ , model : Model ) -> tuple [ float , float , float ] :
353
356
"""
354
357
CalculateLCOELCOH calculates the levelized cost of electricity and heat for the project.
355
358
:param econ: Economics object
356
359
:type econ: :class:`~geophires_x.Economics.Economics`
357
360
:param model: The model object
358
361
:type model: :class:`~geophires_x.Model.Model`
359
362
:return: LCOE: The levelized cost of electricity and LCOH: The levelized cost of heat and LCOC: The levelized cost of cooling
360
- :rtype: tuple
363
+ :rtype: tuple[float, float, float]
361
364
"""
362
365
LCOE = LCOH = LCOC = 0.0
363
366
CCap_elec = (econ .CCap .value * econ .CAPEX_heat_electricity_plant_ratio .value )
364
367
Coam_elec = (econ .Coam .value * econ .CAPEX_heat_electricity_plant_ratio .value )
365
368
CCap_heat = (econ .CCap .value * (1.0 - econ .CAPEX_heat_electricity_plant_ratio .value ))
366
369
Coam_heat = (econ .Coam .value * (1.0 - econ .CAPEX_heat_electricity_plant_ratio .value ))
370
+
371
+ def _capex_total_plus_construction_inflation () -> float :
372
+ # TODO unit conversions
373
+ # TODO should be return value instead of mutating econ
374
+ econ .inflation_cost_during_construction .value = econ .CCap .value * econ .inflrateconstruction .value
375
+
376
+ return econ .CCap .value + econ .inflation_cost_during_construction .value
377
+
378
+ def _construction_inflation_cost_elec_heat () -> tuple [float , float ]:
379
+ # TODO unit conversions
380
+ construction_inflation_cost_elec = CCap_elec * econ .inflrateconstruction .value
381
+ construction_inflation_cost_heat = CCap_heat * econ .inflrateconstruction .value
382
+
383
+ # TODO should be return value instead of mutating econ
384
+ econ .inflation_cost_during_construction .value = (construction_inflation_cost_elec
385
+ + construction_inflation_cost_heat )
386
+
387
+ return CCap_elec + construction_inflation_cost_elec , CCap_heat + construction_inflation_cost_heat
388
+
367
389
# Calculate LCOE/LCOH/LCOC
368
390
if econ .econmodel .value == EconomicModel .FCR :
391
+ capex_total_plus_infl = _capex_total_plus_construction_inflation ()
392
+
369
393
if model .surfaceplant .enduse_option .value == EndUseOptions .ELECTRICITY :
370
- LCOE = (econ .FCR .value * ( 1 + econ . inflrateconstruction . value ) * econ . CCap . value + econ .Coam .value ) / \
394
+ LCOE = (econ .FCR .value * capex_total_plus_infl + econ .Coam .value ) / \
371
395
np .average (model .surfaceplant .NetkWhProduced .value ) * 1E8 # cents/kWh
372
396
elif (model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and
373
397
model .surfaceplant .plant_type .value not in [PlantType .ABSORPTION_CHILLER , PlantType .HEAT_PUMP , PlantType .DISTRICT_HEATING ]):
374
- LCOH = (econ .FCR .value * ( 1 + econ . inflrateconstruction . value ) * econ . CCap . value + econ .Coam .value +
398
+ LCOH = (econ .FCR .value * capex_total_plus_infl + econ .Coam .value +
375
399
econ .averageannualpumpingcosts .value ) / np .average (
376
400
model .surfaceplant .HeatkWhProduced .value ) * 1E8 # cents/kWh
377
401
LCOH = LCOH * 2.931 # $/Million Btu
@@ -382,38 +406,40 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple:
382
406
EndUseOptions .COGENERATION_BOTTOMING_EXTRA_HEAT ,
383
407
EndUseOptions .COGENERATION_PARALLEL_EXTRA_HEAT ,
384
408
EndUseOptions .COGENERATION_PARALLEL_EXTRA_ELECTRICITY ]:
385
- LCOE = (econ .FCR .value * (1 + econ .inflrateconstruction .value ) * CCap_elec + Coam_elec ) / np .average (model .surfaceplant .NetkWhProduced .value ) * 1E8 # cents/kWh
386
- LCOH = (econ .FCR .value * (1 + econ .inflrateconstruction .value ) * CCap_heat + Coam_heat + econ .averageannualpumpingcosts .value ) / np .average (model .surfaceplant .HeatkWhProduced .value ) * 1E8 # cents/kWh
409
+ capex_elec_plus_infl , capex_heat_plus_infl = _construction_inflation_cost_elec_heat ()
410
+ LCOE = (econ .FCR .value * capex_elec_plus_infl + Coam_elec ) / np .average (model .surfaceplant .NetkWhProduced .value ) * 1E8 # cents/kWh
411
+ LCOH = (econ .FCR .value * capex_heat_plus_infl + Coam_heat + econ .averageannualpumpingcosts .value ) / np .average (model .surfaceplant .HeatkWhProduced .value ) * 1E8 # cents/kWh
387
412
LCOH = LCOH * 2.931 # $/Million Btu
388
413
389
414
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and model .surfaceplant .plant_type .value == PlantType .ABSORPTION_CHILLER :
390
- LCOC = (econ .FCR .value * (
391
- 1 + econ .inflrateconstruction .value ) * econ .CCap .value + econ .Coam .value + econ .averageannualpumpingcosts .value ) / np .average (
415
+ LCOC = (econ .FCR .value * capex_total_plus_infl + econ .Coam .value + econ .averageannualpumpingcosts .value ) / np .average (
392
416
model .surfaceplant .cooling_kWh_Produced .value ) * 1E8 # cents/kWh
393
417
LCOC = LCOC * 2.931 # $/Million Btu
394
418
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and model .surfaceplant .plant_type .value == PlantType .HEAT_PUMP :
395
- LCOH = (econ .FCR .value * (
396
- 1 + econ . inflrateconstruction . value ) * econ . CCap . value + econ .Coam .value + econ .averageannualpumpingcosts .value + econ .averageannualheatpumpelectricitycost .value ) / np .average (
419
+ LCOH = (econ .FCR .value * capex_total_plus_infl
420
+ + econ .Coam .value + econ .averageannualpumpingcosts .value + econ .averageannualheatpumpelectricitycost .value ) / np .average (
397
421
model .surfaceplant .HeatkWhProduced .value ) * 1E8 # cents/kWh
398
422
LCOH = LCOH * 2.931 # $/Million Btu
399
423
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and model .surfaceplant .plant_type .value == PlantType .DISTRICT_HEATING :
400
- LCOH = (econ .FCR .value * (
401
- 1 + econ . inflrateconstruction . value ) * econ . CCap . value + econ .Coam .value + econ .averageannualpumpingcosts .value + econ .averageannualngcost .value ) / model .surfaceplant .annual_heating_demand .value * 1E2 # cents/kWh
424
+ LCOH = (econ .FCR .value * capex_total_plus_infl
425
+ + econ .Coam .value + econ .averageannualpumpingcosts .value + econ .averageannualngcost .value ) / model .surfaceplant .annual_heating_demand .value * 1E2 # cents/kWh
402
426
LCOH = LCOH * 2.931 # $/Million Btu
403
427
elif econ .econmodel .value == EconomicModel .STANDARDIZED_LEVELIZED_COST :
404
428
discount_vector = 1. / np .power (1 + econ .discountrate .value ,
405
429
np .linspace (0 , model .surfaceplant .plant_lifetime .value - 1 ,
406
430
model .surfaceplant .plant_lifetime .value ))
431
+ capex_total_plus_infl = _capex_total_plus_construction_inflation ()
432
+
407
433
if model .surfaceplant .enduse_option .value == EndUseOptions .ELECTRICITY :
408
- LCOE = (( 1 + econ . inflrateconstruction . value ) * econ . CCap . value + np .sum (
434
+ LCOE = (capex_total_plus_infl + np .sum (
409
435
econ .Coam .value * discount_vector )) / np .sum (
410
436
model .surfaceplant .NetkWhProduced .value * discount_vector ) * 1E8 # cents/kWh
411
437
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and \
412
438
model .surfaceplant .plant_type .value not in [PlantType .ABSORPTION_CHILLER , PlantType .HEAT_PUMP , PlantType .DISTRICT_HEATING ]:
413
439
econ .averageannualpumpingcosts .value = np .average (
414
440
model .surfaceplant .PumpingkWh .value ) * model .surfaceplant .electricity_cost_to_buy .value / 1E6 # M$/year
415
- LCOH = (( 1 + econ . inflrateconstruction . value ) * econ . CCap . value + np .sum ((
416
- econ .Coam .value + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 ) * discount_vector )) / np .sum (
441
+ LCOH = (capex_total_plus_infl + np .sum ((
442
+ econ .Coam .value + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 ) * discount_vector )) / np .sum (
417
443
model .surfaceplant .HeatkWhProduced .value * discount_vector ) * 1E8 # cents/kWh
418
444
LCOH = LCOH * 2.931 # $/MMBTU
419
445
@@ -424,24 +450,30 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple:
424
450
EndUseOptions .COGENERATION_BOTTOMING_EXTRA_HEAT ,
425
451
EndUseOptions .COGENERATION_PARALLEL_EXTRA_HEAT ,
426
452
EndUseOptions .COGENERATION_PARALLEL_EXTRA_ELECTRICITY ]:
427
- LCOE = ((1 + econ .inflrateconstruction .value ) * CCap_elec + np .sum (Coam_elec * discount_vector )) / np .sum (model .surfaceplant .NetkWhProduced .value * discount_vector ) * 1E8 # cents/kWh
428
- LCOH = ((1 + econ .inflrateconstruction .value ) * CCap_heat +
453
+ capex_elec_plus_infl , capex_heat_plus_infl = _construction_inflation_cost_elec_heat ()
454
+
455
+ LCOE = (capex_elec_plus_infl + np .sum (Coam_elec * discount_vector )) / np .sum (model .surfaceplant .NetkWhProduced .value * discount_vector ) * 1E8 # cents/kWh
456
+ LCOH = (capex_heat_plus_infl * CCap_heat +
429
457
np .sum ((Coam_heat + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 ) * discount_vector )) / np .sum (model .surfaceplant .HeatkWhProduced .value * discount_vector ) * 1E8 # cents/kWh
430
458
LCOH = LCOH * 2.931 # $/MMBTU
431
459
432
460
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and model .surfaceplant .plant_type .value == PlantType .ABSORPTION_CHILLER :
433
- LCOC = ((1 + econ .inflrateconstruction .value ) * econ .CCap .value + np .sum ((
434
- econ .Coam .value + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 ) * discount_vector )) / np .sum (
461
+ capex_total_plus_infl = _capex_total_plus_construction_inflation ()
462
+
463
+ LCOC = (capex_total_plus_infl + np .sum ((
464
+ econ .Coam .value + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 ) * discount_vector )) / np .sum (
435
465
model .surfaceplant .cooling_kWh_Produced .value * discount_vector ) * 1E8 # cents/kWh
436
466
LCOC = LCOC * 2.931 # $/Million Btu
437
467
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and model .surfaceplant .plant_type .value == PlantType .HEAT_PUMP :
438
- LCOH = ((1 + econ .inflrateconstruction .value ) * econ .CCap .value + np .sum (
468
+ capex_total_plus_infl = _capex_total_plus_construction_inflation ()
469
+ LCOH = (capex_total_plus_infl + np .sum (
439
470
(econ .Coam .value + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 +
440
471
model .surfaceplant .heat_pump_electricity_kwh_used .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 ) * discount_vector )) / np .sum (
441
472
model .surfaceplant .HeatkWhProduced .value * discount_vector ) * 1E8 # cents/kWh
442
473
LCOH = LCOH * 2.931 # $/Million Btu
443
474
elif model .surfaceplant .enduse_option .value == EndUseOptions .HEAT and model .surfaceplant .plant_type .value == PlantType .DISTRICT_HEATING :
444
- LCOH = ((1 + econ .inflrateconstruction .value ) * econ .CCap .value + np .sum (
475
+ capex_total_plus_infl = _capex_total_plus_construction_inflation ()
476
+ LCOH = (capex_total_plus_infl + np .sum (
445
477
(econ .Coam .value + model .surfaceplant .PumpingkWh .value * model .surfaceplant .electricity_cost_to_buy .value / 1E6 +
446
478
econ .annualngcost .value ) * discount_vector )) / np .sum (
447
479
model .surfaceplant .annual_heating_demand .value * discount_vector ) * 1E2 # cents/kWh
@@ -457,10 +489,12 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple:
457
489
CRF = i_ave / (1 - np .power (1 + i_ave , - model .surfaceplant .plant_lifetime .value ))
458
490
inflation_vector = np .power (1 + econ .RINFL .value , np .linspace (1 , model .surfaceplant .plant_lifetime .value , model .surfaceplant .plant_lifetime .value ))
459
491
discount_vector = 1. / np .power (1 + i_ave , np .linspace (1 , model .surfaceplant .plant_lifetime .value , model .surfaceplant .plant_lifetime .value ))
460
- NPV_cap = np .sum ((1 + econ .inflrateconstruction .value ) * econ .CCap .value * CRF * discount_vector )
461
- NPV_fc = np .sum ((1 + econ .inflrateconstruction .value ) * econ .CCap .value * econ .PTR .value * inflation_vector * discount_vector )
462
- NPV_it = np .sum (econ .CTR .value / (1 - econ .CTR .value ) * ((1 + econ .inflrateconstruction .value ) * econ .CCap .value * CRF - econ .CCap .value / model .surfaceplant .plant_lifetime .value ) * discount_vector )
463
- NPV_itc = (1 + econ .inflrateconstruction .value ) * econ .CCap .value * econ .RITC .value / (1 - econ .CTR .value )
492
+ capex_total_plus_infl = _capex_total_plus_construction_inflation ()
493
+
494
+ NPV_cap = np .sum (capex_total_plus_infl * CRF * discount_vector )
495
+ NPV_fc = np .sum (capex_total_plus_infl * econ .PTR .value * inflation_vector * discount_vector )
496
+ NPV_it = np .sum (econ .CTR .value / (1 - econ .CTR .value ) * (capex_total_plus_infl * CRF - econ .CCap .value / model .surfaceplant .plant_lifetime .value ) * discount_vector )
497
+ NPV_itc = capex_total_plus_infl * econ .RITC .value / (1 - econ .CTR .value )
464
498
465
499
if model .surfaceplant .enduse_option .value == EndUseOptions .ELECTRICITY :
466
500
NPV_oandm = np .sum (econ .Coam .value * inflation_vector * discount_vector )
@@ -479,21 +513,22 @@ def CalculateLCOELCOHLCOC(econ, model: Model) -> tuple:
479
513
EndUseOptions .COGENERATION_BOTTOMING_EXTRA_HEAT ,
480
514
EndUseOptions .COGENERATION_PARALLEL_EXTRA_HEAT ,
481
515
EndUseOptions .COGENERATION_PARALLEL_EXTRA_ELECTRICITY ]:
516
+ capex_elec_plus_infl , capex_heat_plus_infl = _construction_inflation_cost_elec_heat ()
482
517
483
- NPVcap_elec = np .sum (( 1 + econ . inflrateconstruction . value ) * CCap_elec * CRF * discount_vector )
484
- NPVfc_elec = np .sum (( 1 + econ . inflrateconstruction . value ) * CCap_elec * econ .PTR .value * inflation_vector * discount_vector )
485
- NPVit_elec = np .sum (econ .CTR .value / (1 - econ .CTR .value ) * (( 1 + econ . inflrateconstruction . value ) * CCap_elec * CRF - CCap_elec / model .surfaceplant .plant_lifetime .value ) * discount_vector )
486
- NPVitc_elec = ( 1 + econ . inflrateconstruction . value ) * CCap_elec * econ .RITC .value / (1 - econ .CTR .value )
518
+ NPVcap_elec = np .sum (capex_elec_plus_infl * CRF * discount_vector )
519
+ NPVfc_elec = np .sum (capex_elec_plus_infl * econ .PTR .value * inflation_vector * discount_vector )
520
+ NPVit_elec = np .sum (econ .CTR .value / (1 - econ .CTR .value ) * (capex_elec_plus_infl * CRF - CCap_elec / model .surfaceplant .plant_lifetime .value ) * discount_vector )
521
+ NPVitc_elec = capex_elec_plus_infl * econ .RITC .value / (1 - econ .CTR .value )
487
522
NPVoandm_elec = np .sum (Coam_elec * inflation_vector * discount_vector )
488
523
NPVgrt_elec = econ .GTR .value / (1 - econ .GTR .value ) * (NPVcap_elec + NPVoandm_elec + NPVfc_elec + NPVit_elec - NPVitc_elec )
489
524
490
525
LCOE = ((NPVcap_elec + NPVoandm_elec + NPVfc_elec + NPVit_elec + NPVgrt_elec - NPVitc_elec ) /
491
526
np .sum (model .surfaceplant .NetkWhProduced .value * inflation_vector * discount_vector ) * 1E8 )
492
527
493
- NPV_cap_heat = np .sum (( 1 + econ . inflrateconstruction . value ) * CCap_heat * CRF * discount_vector )
528
+ NPV_cap_heat = np .sum (capex_heat_plus_infl * CRF * discount_vector )
494
529
NPV_fc_heat = np .sum ((1 + econ .inflrateconstruction .value ) * (econ .CCap .value * (1.0 - econ .CAPEX_heat_electricity_plant_ratio .value )) * econ .PTR .value * inflation_vector * discount_vector )
495
- NPV_it_heat = np .sum (econ .CTR .value / (1 - econ .CTR .value ) * (( 1 + econ . inflrateconstruction . value ) * CCap_heat * CRF - CCap_heat / model .surfaceplant .plant_lifetime .value ) * discount_vector )
496
- NPV_itc_heat = ( 1 + econ . inflrateconstruction . value ) * CCap_heat * econ .RITC .value / (1 - econ .CTR .value )
530
+ NPV_it_heat = np .sum (econ .CTR .value / (1 - econ .CTR .value ) * (capex_heat_plus_infl * CRF - CCap_heat / model .surfaceplant .plant_lifetime .value ) * discount_vector )
531
+ NPV_itc_heat = capex_heat_plus_infl * econ .RITC .value / (1 - econ .CTR .value )
497
532
NPV_oandm_heat = np .sum ((econ .Coam .value * (1.0 - econ .CAPEX_heat_electricity_plant_ratio .value )) * inflation_vector * discount_vector )
498
533
NPV_grt_heat = econ .GTR .value / (1 - econ .GTR .value ) * (NPV_cap_heat + NPV_oandm_heat + NPV_fc_heat + NPV_it_heat - NPV_itc_heat )
499
534
0 commit comments