Source code for pyH2A.Discounted_Cash_Flow

import copy
import numbers
from functools import lru_cache
import numpy as np

from pyH2A.Utilities.input_modification import convert_input_to_dictionary, process_input, process_table, insert, read_textfile, set_by_path, execute_plugin
import pyH2A.Utilities.find_nearest as fn

[docs]def numpy_npv(rate, values): '''Calculation of net present value. ''' values = np.asarray(values) return (values / (1+rate)**np.arange(0, len(values))).sum(axis=0)
[docs]@lru_cache(maxsize = None) def get_idx(diagonal_number, axis0, axis1): '''Calculation of index for MACRS calculation. Uses ``lru_cache`` for repeated calculations. ''' a = np.arange(0, diagonal_number) b = a[::-1] c = np.c_[a, b] idx = c[(c[:,0] <= axis0 - 1) & (c[:,1] <= axis1 - 1)] idx = (np.array(idx[:,0]), np.array(idx[:,1])) return idx
[docs]def MACRS_depreciation(plant_years, depreciation_length, annual_depreciable_capital): '''Calculation of MACRS depreciations. Parameters ---------- plant_years : ndarray Array of plant years. depreciation_length : int Depreciation length. annual_depreicable_capital : ndarray Depreciable capital by year. Returns ------- annual_charge : ndarray Charge by year. ''' end_idx = len(plant_years) original_macrs = read_textfile('pyH2A.Lookup_Tables~MACRS.csv', delimiter = ' ') macrs = np.copy(original_macrs) macrs[1:,1:] = macrs[1:,1:]/100. idx_macrs = fn.find_nearest(macrs[0][1:], depreciation_length)[0] macrs_values = macrs[1:,1:][:,idx_macrs] macrs_values = macrs_values[macrs_values != 0] depreciation = np.outer(annual_depreciable_capital, macrs_values) charge = [] diagonals = sum(depreciation.shape) + 1 for i in range(1, diagonals): idx = get_idx(i, depreciation.shape[0], depreciation.shape[1]) diagonal = depreciation[idx] charge.append(np.sum(diagonal)) charge = np.asarray(charge) annual_charge = charge[:end_idx] annual_charge[-1] += np.sum(charge[end_idx:]) return annual_charge
[docs]def discounted_cash_flow_function(inp, values, parameters, attribute = 'h2_cost', plugin = None, plugin_attr = None): '''Wrapper function for ``Discounted_Cash_Flow``, substituting provided values at specified parameter positions and returning desired attribute of ``Discounted_Cash_Flow`` object. Parameters ---------- inp : dict or str Dictionary containing input information. If `inp` is a file path, the provided file is converted to a dictionary using ``convert_input_to_dictionary``. values : ndarray 1D (in case of one parameter) or 2D array (in case of multiple parameters) containing the values which are to be used. parameters : ndarray 1D or 2D array containing the parameter specifications (location within inp); Format: [top_key, middle_key, bottom_key]. attribute : str, optional Desired attribute of ``Discounted_Cash_Flow`` object, which should be returned. If the attribute is `plugs`, the `.plugs` dictionary attribute is accessed, which contains information of all used plugins (see `plugin` and `plugin_attr`). Defaults to `h2_cost`. plugin : str, optional If `attribute` is set to `plugs`, a `plugin` has to be specified, which should be accessed. Furthermore, a corresponding attribute of the plugin needs to be provided, see `plugin_attr`. plugin_attr : str, optional If `attribute` is set to `plugs`, `plugin_attr` controls which attribute of the specified `plugin` is accessed. Returns ------- results : ndarray For each value (1D array) or set of values (2D array), the values are substituted in inp, Discounted_Cash_Flow (dcf) is executed and the dcf object is generated. Then, the requested attribute is stored in results, which is finally returned. ''' if isinstance(inp, str): inp = convert_input_to_dictionary(inp) results = [] for value_set in values: input_dict = copy.deepcopy(inp) if isinstance(value_set, numbers.Number): set_by_path(input_dict, parameters, value_set) else: for value, parameter in zip(value_set, parameters): set_by_path(input_dict, parameter, value) dcf = Discounted_Cash_Flow(input_dict, print_info = False) result = getattr(dcf, attribute) if attribute == 'plugs': result = result[plugin] result = getattr(result, plugin_attr) results.append(result) return results
[docs]class Discounted_Cash_Flow: '''Class to perform discounted cash flow analysis. Parameters ---------- input_file : str or dict Path to input file or dictionary containing input file data. print_info : bool Boolean flag to control if detailed info on action of plugins is printed. check_processing : bool Boolean flag to control if `check_processing` is run at the end of discounted cash flow analysis, which checks if all tables in input file have been processed during run. Returns ------- Discounted_Cash_Flow : object Discounted cash flow analysis object. Attributes ---------- h2_cost : float Levelized H2 cost per kg. contributions : dict Cost contributions to H2 price. plugs : dict Dictionary containing plugin class objects used during analysis. Notes ----- **Numerical inputs** Numbers use decimal points. Commas can be used as thousands seperator, they are removed from numbers during processing. the "%" can be used after a number, indicating that it will be divided by 100 before being used. **Special symbols** Tables in the input file are formatted using GitHub flavoured Markdown. This means that "#" at the beginning of a line indicates a header. "|" is used to seperate columns. "---" is used on its own line to seperate table headers from table entries. Paths to locations in the input file/in self.inp are specified using ">". Paths are always composed of three levels: top key > middle key > bottom key. File name paths are specified using "/". In cases where multiple numbers are used in one field (e.g. during sensitivity analysis), these numbers are seperated using ";". **Order in the input file** Order matters in the following cases: 1. For a group of ``sum_all_tables()`` processed tables (sharing the specified part of their key, e.g. "Direct Capital Cost"), they are processed in their provided order. 2. Within a table, the first column will be used to generate the "middle key" of self.inp. The order of the other columns is not important. **Input** Processed input cells can contain either a number or path(s) (if multiple paths are used, they have to be seperated by ";") to other cells. The use of process_input() (and hence, process_table(), sum_table() and sum_all_tables()) also allows for the value of an input cell to be multiplied by another cell by including path(s) in an additiona column (column name typically "Path"). **Workflow** Workflow specifies which functions and plugins are used and in which order they are executed. The listed five functions have to be executed in the specified order for pyH2A to work. Plugins can be inserted at appropiate positions (Type: "plugin"). Plugins have to be located in the ./Plugins/ directory. Execcution order is determined by the "Position" input. If the specified position is equal to an already exisiting one, the function/plugin will be executed after the already specified one. If multiple plugins/function are specified with the same position, they will be executed in the order in which they are listed in the input file. **Plugins** Plugins needs to be composed of a class with the same name as the plugin file name. This class uses two inputs, a discounted cash flow object (usually indicated by "self") and "print_info", which controls the printing of run time statements. Within the __init__ function of the class the actions of the plugins are specified, typically call(s) of the "insert()" function to modify the discounted cash flow object's "inp" dictionary (self.inp). ''' def __init__(self, input_file, print_info = True, check_processing = True): if isinstance(input_file, str): self.inp = convert_input_to_dictionary(input_file) else: self.inp = input_file self.print_info = print_info process_table(self.inp, 'Financial Input Values', 'Value') self.fin = self.inp['Financial Input Values'] self.npv_dict = {} self.plugs = {} self.pre_workflow() self.workflow(self.inp, self.npv_dict, self.plugs) # execution of all functions and plugins specified in "Workflow" self.post_workflow() if check_processing is True: self.check_processing()
[docs] def pre_workflow(self): '''Functions executed before workflow. Parameters ---------- Workflow > initial_equity_depreciable_capital > Type : function Initial equity depreciable capital function. Workflow > initial_equity_depreciable_capital > Position : int Position of initial equity depreciable capital function. Workflow > non_depreciable_capital_costs > Type : function Non-depreciable capital costs function. Workflow > non_depreciable_capital_costs > Position : int Position of non-depreciable capital costs function. Workflow > replacement_costs > Type : function Replacement costs function. Workflow > replacement_costs > Position : int Position of replacement costs function. Workflow > fixed_operating_costs > Type : function Fixed operating costs function. Workflow > fixed_operating_costs > Position : int Position of fixed operating costs function. Workflow > variable_operating_costs > Type : function Variable operating costs function. Workflow > variable_operating_costs > Position : int Position of variable operating costs function. Workflow > [...] > Type : plugin, optional Plugin to be executed. Workflow > [...] > Position : int, optional Position of plugin to be executed. Financial Input Values > ref year > Value : int Financial reference year. Financial Input Values > startup year > Value : int Startup year for plant. Financial Input Values > basis year > Value : int Financial basis year. Financial Input Values > current year capital costs > Value : int Current year for capital costs. Financial Input Values > plant life > Value : int Plant life in years. Financial Input Values > inflation > Value : float Inflation rate. Construction > [...] > Value : float Percentage of capital spent in given year of construction. Number of entries determines construction period in year (each entry corresponds to one year). Have to be in order (first entry corresponds to first construction year etc.). Values of all entries have to sum to 100%. Returns ------- Financial Input Values > construction time > Value : int Construction time in years. ''' self.time() self.inflation()
[docs] def workflow(self, inp, npv_dict, plugs_dict): '''Executing plugins and functions for discounted cash flow. ''' sorted_keys = sorted(inp['Workflow'], key = lambda x: inp['Workflow'][x]['Position']) for key in sorted_keys: if inp['Workflow'][key]['Type'] == 'function': self.execute_function(key, npv_dict) else: execute_plugin(key, plugs_dict, print_info = self.print_info, dcf = self)
[docs] def post_workflow(self): '''Functions executed after workflow. Parameters ---------- Financial Input Values > depreciation length > Value : int Depreciation length in years. Financial Input Values > depreciation type > Value : str Type of depreciation, currently only MACRS is implemented. Financial Input Values > interest > Value : float Interest rate on debt. Financial Input Values > debt > Value : str Debt period, currently only constant debt is implemented. Financial Input Values > startup revenues > Value : float Percentage of revenues during start-up. Financial Input Values > decommissioning > Value : float Decomissioning cost in percentage of depreciable capital investment. Financial Input Values > salvage > Value : float Salvage value in percentage of total capital investment. Financial Input Values > state tax > Value : float State tax. Financial Input Values > federal tax > Value : float Federal tax. Financial Input Values > working capital > Value : float Working capital as percentage of yearly change in operating costs. ''' self.npv_dict['salvage'], self.npv_dict['decomissioning'] = self.salvage_decommissioning() self.npv_dict['working_capital_reserve'] = self.working_capital_reserve_calc() self.npv_dict['interest'], self.npv_dict['principal_payment'] = self.debt_financing() self.npv_dict['depreciation_charge'] = self.depreciation_charge() self.npv_dict['h2_sales'] = self.h2_sales() self.h2_cost() self.npv_dict['revenue'] = self.h2_revenue() self.npv_dict['pre_depreciation_income'], self.npv_dict['taxable_income'], self.npv_dict['taxes'], self.npv_dict['after_tax_income'] = self.income() self.cash_flow() self.cost_contribution()
[docs] def execute_function(self, function_name, npv_dict): '''Execute class function named `function_name` and store output in `npv_dict`''' called_function = getattr(self, function_name) output = called_function() npv_dict[function_name] = output
[docs] def time(self): '''Creating time scale information for discounted cash flow analysis. ''' insert(self, 'Financial Input Values', 'construction time', 'Value', len(self.inp['Construction']), __name__, print_info = self.print_info) construction_start = self.fin['startup year']['Value'] - self.fin['construction time']['Value'] end_of_life = self.fin['startup year']['Value'] + self.fin['plant life']['Value'] self.years = np.arange(construction_start, end_of_life) self.analysis_years = np.arange(0, self.fin['construction time']['Value'] + self.fin['plant life']['Value']) self.plant_years = np.arange(-self.fin['construction time']['Value'], self.fin['plant life']['Value']) self.operation_years = np.arange(0, self.fin['plant life']['Value'])
[docs] def inflation(self): '''Calculate inflation correction and inflators for specific commodities. ''' inflation_rate = 1 + self.fin['inflation']['Value'] self.inflation_factor = inflation_rate ** self.plant_years self.inflation_correction = inflation_rate ** (self.fin['startup year']['Value'] - self.fin['ref year']['Value']) plant_cost = read_textfile('pyH2A.Lookup_Tables~Plant_Cost_Index.csv', delimiter = ' ') gdp_deflator_price = read_textfile('pyH2A.Lookup_Tables~GDP_Implicit_Deflator_Price_Index.csv', delimiter = ' ') labor_price = read_textfile('pyH2A.Lookup_Tables~Labor_Index.csv', delimiter = ' ') chemical_price = read_textfile('pyH2A.Lookup_Tables~SRI_Chemical_Price_Index.csv', delimiter = ' ') plant_idx = fn.find_nearest(plant_cost, [self.fin['current year capital costs']['Value'], self.fin['basis year']['Value']]) gdp_idx = fn.find_nearest(gdp_deflator_price, [self.fin['ref year']['Value'], self.fin['current year capital costs']['Value']]) labor_idx = fn.find_nearest(labor_price, [self.fin['ref year']['Value'], self.fin['basis year']['Value']]) chemical_idx = fn.find_nearest(chemical_price, [self.fin['ref year']['Value'], self.fin['basis year']['Value']]) self.cepci_inflator = plant_cost[:,1][plant_idx[0]]/plant_cost[:,1][plant_idx[1]] self.ci_inflator = gdp_deflator_price[:,1][gdp_idx[0]]/gdp_deflator_price[:,1][gdp_idx[1]] self.combined_inflator = self.cepci_inflator * self.ci_inflator self.labor_inflator = labor_price[:,1][labor_idx[0]]/labor_price[:,1][labor_idx[1]] self.chemical_inflator = chemical_price[:,1][chemical_idx[0]]/chemical_price[:,1][chemical_idx[1]]
[docs] def production_scaling(self): '''Get plant outpuer per year at gate. Parameters ---------- Technical Operating Parameters and Specifications > Output per Year at Gate > Value : float Output per year at gate in kg. ''' self.output_per_year_at_gate = process_input(self.inp, 'Technical Operating Parameters and Specifications', 'Output per Year at Gate', 'Value') return 0.
[docs] def initial_equity_depreciable_capital(self): '''Calculate initial equity depreciable capital. Parameters ---------- Financial Input Values > equity > Value : float Percentage of equity financing. Financial Input Values > irr > Value : float After tax real internal rate of return. Depreciable Capital Costs > Inflated > Value : float Inflated depreciable capital costs. ''' self.depreciable_capital = process_input(self.inp, 'Depreciable Capital Costs', 'Inflated', 'Value') self.depreciable_capital_inflation = self.depreciable_capital * self.inflation_correction process_table(self.inp, 'Construction', 'Value') construction_years = [] for counter, key in enumerate(self.inp['Construction']): cost = self.inp['Construction'][key]['Value'] * self.fin['equity']['Value'] * self.depreciable_capital_inflation * self.inflation_factor[counter] construction_years.append(cost) self.initial_depreciable_capital = np.sum(construction_years) self.annual_initial_depreciable_capital = np.zeros(len(self.inflation_factor)) self.annual_initial_depreciable_capital[:self.fin['construction time']['Value']] = construction_years self.after_tax_nominal_irr = (1 + self.fin['irr']['Value']) * (1 + self.fin['inflation']['Value']) - 1 return numpy_npv(self.after_tax_nominal_irr, construction_years)
[docs] def non_depreciable_capital_costs(self): '''Calculate non-depreciable capital costs. Parameters ---------- Non-Depreciable Capital Costs > Inflated > Value : float Inflated non-depreciable capital costs. ''' self.non_depreciable_capital = process_input(self.inp, 'Non-Depreciable Capital Costs', 'Inflated', 'Value') self.non_depreciable_capital_inflated = self.non_depreciable_capital * self.inflation_correction non_depreciable_capital_inflation_corrected = self.non_depreciable_capital_inflated * self.inflation_factor[0] self.annual_non_depreciable_capital = np.zeros(len(self.inflation_factor)) self.annual_non_depreciable_capital[0] = non_depreciable_capital_inflation_corrected return non_depreciable_capital_inflation_corrected
[docs] def replacement_costs(self): '''Calculate replacement costs. Parameters ---------- Replacement > Total > Value : ndarray Total replacement costs. ''' yearly_costs = self.inp['Replacement']['Total']['Value'] self.start_idx = fn.find_nearest(self.plant_years, 0)[0] yearly_costs[:self.start_idx] = 0 self.annual_replacement_costs = yearly_costs return numpy_npv(self.after_tax_nominal_irr, yearly_costs)
[docs] def fixed_operating_costs(self): '''Calculate fixed operating costs. Parameters ---------- Financial Input Values > startup time > Value : int Startup time in years. Financial Input Values > startup cost fixed > Value : float Percentage of fixed operating costs during start-up. Fixed Operating Costs > Total > Value : float Total fixed operating costs. ''' fixed_operating = process_input(self.inp, 'Fixed Operating Costs', 'Total', 'Value') fixed_operating_inflated = fixed_operating * self.inflation_correction self.start_up_time_idx = self.start_idx + self.fin['startup time']['Value'] yearly_costs = fixed_operating_inflated * self.inflation_factor yearly_costs[:self.start_up_time_idx] = yearly_costs[:self.start_up_time_idx] * self.fin['startup cost fixed']['Value'] yearly_costs[:self.start_idx] = 0 self.fixed_operating_costs = yearly_costs return numpy_npv(self.after_tax_nominal_irr, yearly_costs)
[docs] def variable_operating_costs(self): '''Calculate variable operating costs. Parameters ---------- Financial Input Values > startup cost variable > Value : float Percentage of variable operating costs during start-up. Variable Operating Costs > Total > Value : ndarray Total variable operating costs. ''' variable_operating_costs = self.inflation_factor * self.inp['Variable Operating Costs']['Total']['Value'] variable_operating_costs[:self.start_up_time_idx] = variable_operating_costs[:self.start_up_time_idx] * self.fin['startup cost variable']['Value'] variable_operating_costs[:self.start_idx] = 0 self.variable_operating_costs = variable_operating_costs return numpy_npv(self.after_tax_nominal_irr, variable_operating_costs)
[docs] def salvage_decommissioning(self): '''Calculate salvage and decomissioning costs. ''' self.total_capital_inflated = self.depreciable_capital_inflation + self.non_depreciable_capital_inflated decommissioning = self.depreciable_capital_inflation * self.fin['decommissioning']['Value'] salvage = self.total_capital_inflated * self.fin['salvage']['Value'] self.decommissioning_costs = np.zeros(len(self.plant_years)) self.decommissioning_costs[-1] = decommissioning * self.inflation_factor[-1] self.salvage_income = np.zeros(len(self.plant_years)) self.salvage_income[-1] = salvage * self.inflation_factor[-1] return numpy_npv(self.after_tax_nominal_irr, self.salvage_income), numpy_npv(self.after_tax_nominal_irr, self.decommissioning_costs)
[docs] def working_capital_reserve_calc(self): '''Calculate working capital reserve. ''' sum_variable_fixed_operating_costs = self.variable_operating_costs + self.fixed_operating_costs self.working_capital_reserve = -self.fin['working capital']['Value'] * np.diff(sum_variable_fixed_operating_costs) self.working_capital_reserve[-1] = -np.sum(self.working_capital_reserve[:-1]) self.working_capital_reserve = np.r_[np.zeros(1), self.working_capital_reserve] return -numpy_npv(self.after_tax_nominal_irr, self.working_capital_reserve)
[docs] def debt_financing(self): '''Calculate constant debt financing. ''' self.debt_financed_capital = self.depreciable_capital_inflation * (1 - self.fin['equity']['Value']) * self.inflation_factor[0] interest = self.debt_financed_capital * self.fin['interest']['Value'] self.interest_per_year = np.ones(len(self.inflation_factor)) * interest self.principal_payment = np.zeros(len(self.inflation_factor)) self.principal_payment[-1] = self.debt_financed_capital return numpy_npv(self.after_tax_nominal_irr, self.interest_per_year), numpy_npv(self.after_tax_nominal_irr, self.principal_payment)
[docs] def depreciation_charge(self): '''Calculate depreciation charge. ''' total_initial_depreciable_capital = self.debt_financed_capital + self.initial_depreciable_capital annual_depreciable_capital = np.copy(self.annual_replacement_costs) annual_depreciable_capital[self.start_idx] += total_initial_depreciable_capital self.annual_charge = MACRS_depreciation(self.plant_years, self.fin['depreciation length']['Value'], annual_depreciable_capital) return numpy_npv(self.after_tax_nominal_irr, self.annual_charge)
[docs] def h2_sales(self): '''Calculate H2 sales. ''' self.annual_sales = np.ones(len(self.inflation_factor)) * self.output_per_year_at_gate self.annual_sales[:self.start_up_time_idx] = self.annual_sales[:self.start_up_time_idx] * self.fin['startup revenues']['Value'] self.annual_sales[:self.start_idx] = 0 return numpy_npv(self.fin['irr']['Value'], self.annual_sales)
[docs] def h2_cost(self): '''Calculate levelized H2 cost. ''' self.total_tax_rate = self.fin['federal tax']['Value'] + self.fin['state tax']['Value'] * (1. - self.fin['federal tax']['Value']) lcoe_capital_costs = self.npv_dict['initial_equity_depreciable_capital'] + self.npv_dict['non_depreciable_capital_costs'] + self.npv_dict['replacement_costs'] + self.npv_dict['working_capital_reserve'] lcoe_depreciation = -self.npv_dict['depreciation_charge'] * self.total_tax_rate lcoe_principal_payment = self.npv_dict['principal_payment'] lcoe_operating_costs = (-self.npv_dict['salvage'] + self.npv_dict['decomissioning'] + self.npv_dict['fixed_operating_costs'] + self.npv_dict['variable_operating_costs'] + self.npv_dict['interest']) * (1. - self.total_tax_rate) lcoe_h2_sales = self.npv_dict['h2_sales'] * (1. - self.total_tax_rate) self.h2_cost_nominal = (lcoe_capital_costs + lcoe_depreciation + lcoe_principal_payment + lcoe_operating_costs)/lcoe_h2_sales * (1. + self.fin['inflation']['Value']) ** self.fin['construction time']['Value'] self.h2_cost = self.h2_cost_nominal/self.inflation_correction
[docs] def h2_revenue(self): '''Calculate H2 sales revenue. ''' self.annual_revenue = self.annual_sales * self.h2_cost_nominal * self.inflation_factor return numpy_npv(self.after_tax_nominal_irr, self.annual_revenue)
[docs] def income(self): '''Calculate total income. ''' self.annual_pre_depreciation_income = self.annual_revenue + self.salvage_income - self.decommissioning_costs - self.fixed_operating_costs - self.variable_operating_costs - self.interest_per_year self.taxable_income = self.annual_pre_depreciation_income - self.annual_charge self.annual_taxes = self.taxable_income * self.total_tax_rate self.after_tax_income = self.annual_pre_depreciation_income - self.annual_taxes return numpy_npv(self.after_tax_nominal_irr, self.annual_pre_depreciation_income), numpy_npv(self.after_tax_nominal_irr, self.taxable_income), numpy_npv(self.after_tax_nominal_irr, self.annual_taxes), numpy_npv(self.after_tax_nominal_irr, self.after_tax_income)
[docs] def cash_flow(self): '''Calculate cash flow. ''' pre_tax_cash_flow = -self.annual_initial_depreciable_capital - self.annual_replacement_costs + self.working_capital_reserve - self.annual_non_depreciable_capital + self.annual_pre_depreciation_income - self.principal_payment after_tax_post_depreciation_cash_flow = pre_tax_cash_flow - self.annual_taxes npv_after_tax_post_depreciation = numpy_npv(self.after_tax_nominal_irr, after_tax_post_depreciation_cash_flow) if abs(npv_after_tax_post_depreciation) > 1e-6: print('Warning: NPV of After tax post-depreciation cash flow is not 0, possible error. NPV: {0}'.format(npv_after_tax_post_depreciation)) cummulative_cash_flow = np.cumsum(after_tax_post_depreciation_cash_flow) return numpy_npv(self.after_tax_nominal_irr, cummulative_cash_flow)
[docs] def cost_contribution(self): '''Compile contributions to H2 cost. ''' revenue = self.expenses_per_kg_H2(self.npv_dict['revenue']) self.contributions = {'Data': {'Initial equity depreciable capital': self.expenses_per_kg_H2(self.npv_dict['initial_equity_depreciable_capital']), 'Non depreciable capital' : self.expenses_per_kg_H2(self.npv_dict['non_depreciable_capital_costs']), 'Replacement costs' : self.expenses_per_kg_H2(self.npv_dict['replacement_costs']), 'Salvage' : -self.expenses_per_kg_H2(self.npv_dict['salvage']), 'Decomissioning' : self.expenses_per_kg_H2(self.npv_dict['decomissioning']), 'Fixed operating costs' : self.expenses_per_kg_H2(self.npv_dict['fixed_operating_costs']), 'Variable operating costs' : self.expenses_per_kg_H2(self.npv_dict['variable_operating_costs']), 'Working capital reserve' : self.expenses_per_kg_H2(self.npv_dict['working_capital_reserve']), 'Interest' : self.expenses_per_kg_H2(self.npv_dict['interest']), 'Principal payment' : self.expenses_per_kg_H2(self.npv_dict['principal_payment']), 'Taxes' : self.expenses_per_kg_H2(self.npv_dict['taxes'])} } self.contributions['Total'] = self.h2_cost self.contributions['Table Group'] = 'Total cost of hydrogen'
[docs] def expenses_per_kg_H2(self, value): '''Calculate expenses per kg H2. ''' return value/self.npv_dict['h2_sales'] * (1. + self.fin['inflation']['Value']) ** self.fin['construction time']['Value'] / self.inflation_correction
[docs] def check_processing(self): '''Check whether all tables in input file were used. Notes ----- 'Workflow' and 'Display Parameters' tables are exempted. Furthermore, all tables that have the term 'Analysis' in their name are also exempted. ''' exceptions = ['Workflow', 'Display Parameters'] for top_key in self.inp: if top_key not in exceptions and 'Analysis' not in top_key: for middle_key in self.inp[top_key]: if 'Processed' not in self.inp[top_key][middle_key]: print('Warning: "{0} > {1}" has not been processed'.format(top_key, middle_key))