11. Techno-economic analysis#
TEA objects can perform cashflow analysis on a System object and arguments for cashflow analysis. These include arguments such as operating days, lang factor, and income tax, as well as arguments for taking into account startup operation, construction schedule, and capital cost financing.
11.1. Inheriting from TEA objects#
Depending on the rigour and flexibility of the techno-economic analysis, different parameters may be needed to calculate total depreciable capital (TDC), fixed capital investment (FCI), and fixed operating cost (FOC). For this reason, the TEA object is left as an abstract class with abstract methods _TDC
, _FCI
, and _FOC
. Here is an example TEA subclass from the sugarcane biorefinery design for the production of ethanol:
[1]:
import biosteam as bst
import numpy as np
bst.nbtutorial()
class SugarcaneTEA(bst.TEA):
"""
Create a SugarcaneTEA object for techno-economic analysis of a biorefinery [1]_.
Parameters
----------
system : System
Should contain feed and product streams.
IRR : float
Internal rate of return (fraction).
duration : tuple[int, int]
Start and end year of venture (e.g. (2018, 2038)).
depreciation : str
'MACRS' + number of years (e.g. 'MACRS7').
operating_days : float
Number of operating days per year.
income_tax : float
Combined federal and state income tax rate (fraction).
lang_factor : float
Lang factor for getting fixed capital investment from
total purchase cost. If no lang factor, estimate capital investment
using bare module factors.
startup_schedule : tuple[float]
Startup investment fractions per year
(e.g. (0.5, 0.5) for 50% capital investment in the first year and 50%
investment in the second).
WC_over_FCI : float
Working capital as a fraction of fixed capital investment.
labor_cost : float
Total labor cost (USD/yr).
fringe_benefits : float
Cost of fringe benefits as a fraction of labor cost.
property_tax : float
Fee as a fraction of fixed capital investment.
property_insurance : float
Fee as a fraction of fixed capital investment.
supplies : float
Yearly fee as a fraction of labor cost.
maintenance : float
Yearly fee as a fraction of fixed capital investment.
administration : float
Yearly fee as a fraction of fixed capital investment.
References
----------
.. [1] Huang, H., Long, S., & Singh, V. (2016). Techno-economic analysis of biodiesel
and ethanol co-production from lipid-producing sugarcane. Biofuels, Bioproducts
and Biorefining, 10(3), 299–315. https://doi.org/10.1002/bbb.1640
"""
def __init__(self, system, IRR, duration, depreciation, income_tax,
operating_days, lang_factor, construction_schedule, WC_over_FCI,
labor_cost, fringe_benefits, property_tax,
property_insurance, supplies, maintenance, administration):
# Huang et. al. does not take into account financing or startup
# so these parameters are 0 by default
super().__init__(system, IRR, duration, depreciation, income_tax,
operating_days, lang_factor, construction_schedule,
startup_months=0, startup_FOCfrac=0, startup_VOCfrac=0,
startup_salesfrac=0, finance_interest=0, finance_years=0,
finance_fraction=0, WC_over_FCI=WC_over_FCI)
self.labor_cost = labor_cost
self.fringe_benefits = fringe_benefits
self.property_tax = property_tax
self.property_insurance = property_insurance
self.supplies= supplies
self.maintenance = maintenance
self.administration = administration
# The abstract _DPI method should take installed equipment cost
# and return the direct permanent investment. Huang et. al. assume
# these values are equal
def _DPI(self, installed_equipment_cost):
return installed_equipment_cost
# The abstract _TDC method should take direct permanent investment
# and return the total depreciable capital. Huang et. al. assume
# these values are equal
def _TDC(self, DPI):
return DPI
# The abstract _FCI method should take total depreciable capital
# and return the fixed capital investment. Again, Huang et. al.
# assume these values are equal.
def _FCI(self, TDC):
return TDC
# The abstract _FOC method should take fixed capital investment
# and return the fixed operating cost.
def _FOC(self, FCI):
return (FCI*(self.property_tax + self.property_insurance
+ self.maintenance + self.administration)
+ self.labor_cost*(1+self.fringe_benefits+self.supplies))
11.2. Cash flow analysis and results#
Create a TEA object from a system:
[2]:
from biorefineries import sugarcane as sc
tea = SugarcaneTEA(system=sc.sugarcane_sys,
IRR=0.15,
duration=(2018, 2038),
depreciation='MACRS7',
income_tax=0.21, # Previously 35% in published study
operating_days=200,
lang_factor=3,
construction_schedule=(0.4, 0.6),
WC_over_FCI=0.05,
labor_cost=2.5e6,
fringe_benefits=0.4,
property_tax=0.001,
property_insurance=0.005,
supplies=0.20,
maintenance=0.01,
administration=0.005)
tea.show() # Print TEA summary at current options
# Ignore the warnings, these are taken care of internally.
SugarcaneTEA: sugarcane_sys
NPV: 5,200,749 USD at 15.0% IRR
Retrieve complete cashflow analysis as a DataFrame object:
[3]:
tea.get_cashflow_table()
[3]:
Depreciable capital [MM$] | Fixed capital investment [MM$] | Working capital [MM$] | Depreciation [MM$] | Loan [MM$] | ... | Net earnings [MM$] | Cash flow [MM$] | Discount factor | Net present value (NPV) [MM$] | Cumulative NPV [MM$] | |
---|---|---|---|---|---|---|---|---|---|---|---|
2016 | 79.5 | 79.5 | 0 | 0 | 0 | ... | 0 | -79.5 | 1.15 | -91.5 | -91.5 |
2017 | 119 | 119 | 9.94 | 0 | 0 | ... | 0 | -129 | 1 | -129 | -221 |
2018 | 0 | 0 | 0 | 28.4 | 0 | ... | 9.95 | 38.4 | 0.87 | 33.4 | -187 |
2019 | 0 | 0 | 0 | 48.7 | 0 | ... | -7.69 | 41 | 0.756 | 31 | -156 |
2020 | 0 | 0 | 0 | 34.8 | 0 | ... | 4.92 | 39.7 | 0.658 | 26.1 | -130 |
2021 | 0 | 0 | 0 | 24.8 | 0 | ... | 12.8 | 37.6 | 0.572 | 21.5 | -109 |
2022 | 0 | 0 | 0 | 17.8 | 0 | ... | 18.4 | 36.1 | 0.497 | 18 | -90.8 |
2023 | 0 | 3.19 | 0 | 17.7 | 0 | ... | 18.4 | 32.9 | 0.432 | 14.2 | -76.6 |
2024 | 0 | 0 | 0 | 17.8 | 0 | ... | 18.4 | 36.1 | 0.376 | 13.6 | -63 |
2025 | 0 | 0 | 0 | 8.87 | 0 | ... | 25.4 | 34.3 | 0.327 | 11.2 | -51.8 |
2026 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.284 | 9.21 | -42.6 |
2027 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.247 | 8.01 | -34.6 |
2028 | 0 | 3.19 | 0 | 0 | 0 | ... | 32.4 | 29.2 | 0.215 | 6.28 | -28.3 |
2029 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.187 | 6.06 | -22.2 |
2030 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.163 | 5.27 | -17 |
2031 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.141 | 4.58 | -12.4 |
2032 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.123 | 3.98 | -8.41 |
2033 | 0 | 3.19 | 0 | 0 | 0 | ... | 32.4 | 29.2 | 0.107 | 3.12 | -5.29 |
2034 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.0929 | 3.01 | -2.28 |
2035 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.0808 | 2.62 | 0.337 |
2036 | 0 | 0 | 0 | 0 | 0 | ... | 32.4 | 32.4 | 0.0703 | 2.28 | 2.61 |
2037 | 0 | 0 | -9.94 | 0 | 0 | ... | 32.4 | 42.3 | 0.0611 | 2.59 | 5.2 |
22 rows × 17 columns
Find production cost:
[4]:
products = [bst.main_flowsheet('ethanol')]
costs = tea.production_costs(products)# USD/yr
np.round(costs / 1e6) # million USD / yr
[4]:
array([57.])
Solve for the price of a stream at the break even point:
[5]:
feed = bst.main_flowsheet('sugarcane')
price = tea.solve_price(feed) # USD/kg
round(price, 5)
[5]:
0.03519
Solve for the IRR at the break even point:
[6]:
tea.IRR = tea.solve_IRR()
tea.show()
SugarcaneTEA: sugarcane_sys
NPV: -0 USD at 15.4% IRR
Save stream tables, utility requirements, itemized costs, TEA results, and a cash flow table:
[7]:
# Try this on your computer and open excel:
# tea.save_report('report.xlsx')
# Ignore the warning. The flowsheet is saved on the excel file
# as a really big image and Python thinks it could be a
# malicious file cause its so big.
11.3. Incentives#
Inclusion of policy incentives in TEA is useful to evaluate their efficacy. Results can be used by researchers to determine the degree to which incentives may improve biorefinery economics, and by policymakers to develop better incentives. The BioSTEAM Location-Specific Evaluation library (BLocS) allows users to simulate 20 existing state-level tax incentives, and also includes state-level tax rate data for use in TEA. Information on the tax incentives available for simulation can be found in incentives_info.xlsx, and state-specific tax rate data is available in state_scenarios_for_import.xlsx.
In the following example, we evaluate a sugarcane biorefinery operating in Louisiana with state specific data from the BLocS library:
[8]:
import blocs as blc # pip install blocs first (or clone from github)
tea = blc.create_incentivized_tea(
system=sc.sugarcane_sys,
incentive_numbers=[13, 14], # Incentive number as described in incentives_info.xlsx
state='Louisiana',
isconventional=True,
cogeneration_unit=sc.BT,
feedstock=sc.sugarcane,
ethanol_product=sc.ethanol,
IRR=0.15,
duration=(2018, 2038),
depreciation='MACRS7',
federal_income_tax=0.21,
operating_days=180,
lang_factor=3,
construction_schedule=(0.4, 0.6),
WC_over_FCI=0.05,
labor_cost=2.5e6,
fringe_benefits=0.4,
property_tax=0.001,
property_insurance=0.005,
supplies=0.20,
maintenance=0.01,
administration=0.005,
)
tea.IRR = tea.solve_IRR()
tea.show()
ConventionalIncentivesTEA: sugarcane_sys
NPV: -0 USD at 14.6% IRR
Incentive cashflows can be found in the cash flow table:
[9]:
df = tea.get_cashflow_table()
df['Incentives [MM$]']
[9]:
2016 0
2017 0
2018 0.721
2019 0.6
2020 0.513
2021 0.452
2022 0.407
2023 0.363
2024 0.319
2025 0
2026 1
2027 1
2028 0
2029 0
2030 0
2031 0
2032 0
2033 0
2034 0
2035 0
2036 0
2037 0
Name: Incentives [MM$], dtype: float64