Source code for climada.entity.disc_rates.base

"""
This file is part of CLIMADA.

Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.

CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free
Software Foundation, version 3.

CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.

---

Define DiscRates class.
"""

__all__ = ['DiscRates']

import copy
from array import array
import logging
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import xlsxwriter

import climada.util.checker as check
from climada.entity.tag import Tag
import climada.util.finance as u_fin
import climada.util.hdf5_handler as hdf5

LOGGER = logging.getLogger(__name__)

DEF_VAR_MAT = {'sup_field_name': 'entity',
               'field_name': 'discount',
               'var_name': {'year' : 'year',
                            'disc' : 'discount_rate'
                           }
              }
""" MATLAB variable names """

DEF_VAR_EXCEL = {'sheet_name': 'discount',
                 'col_name': {'year' : 'year',
                              'disc' : 'discount_rate'
                             }
                }
""" Excel variable names """

[docs]class DiscRates(): """Defines discount rates and basic methods. Loads from files with format defined in FILE_EXT. Attributes: tag (Tag): information about the source data years (np.array): years rates (np.array): discount rates for each year (between 0 and 1) """
[docs] def __init__(self): """Empty initialization. Examples: Fill discount rates with values and check consistency data: >>> disc_rates = DiscRates() >>> disc_rates.years = np.array([2000, 2001]) >>> disc_rates.rates = np.array([0.02, 0.02]) >>> disc_rates.check() Read discount rates from year_2050.mat and checks consistency data. >>> disc_rates = DiscRates(ENT_TEMPLATE_XLS) """ self.clear()
[docs] def clear(self): """Reinitialize attributes.""" self.tag = Tag() # Following values are given for each defined year self.years = np.array([], int) self.rates = np.array([], float)
[docs] def check(self): """Check attributes consistency. Raises: ValueError """ check.size(len(self.years), self.rates, 'DiscRates.rates')
[docs] def select(self, year_range): """Select discount rates in given years. Parameters: year_range (np.array): continuous sequence of selected years. Returns: DiscRates """ pos_year = np.isin(year_range, self.years) if not np.all(pos_year): LOGGER.info('No discount rates for given years.') return None pos_year = np.isin(self.years, year_range) sel_disc = self.__class__() sel_disc.tag = self.tag sel_disc.years = self.years[pos_year] sel_disc.rates = self.rates[pos_year] return sel_disc
[docs] def append(self, disc_rates): """Check and append discount rates to current DiscRates. Overwrite discount rate if same year. Parameters: disc_rates (DiscRates): DiscRates instance to append Raises: ValueError """ disc_rates.check() if self.years.size == 0: self.__dict__ = copy.deepcopy(disc_rates.__dict__) return self.tag.append(disc_rates.tag) new_year = array('l') new_rate = array('d') for year, rate in zip(disc_rates.years, disc_rates.rates): found = np.where(year == self.years)[0] if found.size > 0: self.rates[found[0]] = rate else: new_year.append(year) new_rate.append(rate) self.years = np.append(self.years, new_year).astype(int, copy=False) self.rates = np.append(self.rates, new_rate)
[docs] def net_present_value(self, ini_year, end_year, val_years): """Compute net present value between present year and future year. Parameters: ini_year (float): initial year end_year (float): end year val_years (np.array): cash flow at each year btw ini_year and end_year (both included) Returns: float """ year_range = np.arange(ini_year, end_year+1) if year_range.size != val_years.size: LOGGER.error('Wrong size of yearly values.') raise ValueError sel_disc = self.select(year_range) if sel_disc is None: LOGGER.error('No information of discount rates for provided years:'\ ' %s - %s', ini_year, end_year) raise ValueError return u_fin.net_present_value(sel_disc.years, sel_disc.rates, val_years)
[docs] def plot(self, axis=None, **kwargs): """Plot discount rates per year. Parameters: axis (matplotlib.axes._subplots.AxesSubplot, optional): axis to use kwargs (optional): arguments for plot matplotlib function, e.g. marker='x' Returns: matplotlib.axes._subplots.AxesSubplot """ if not axis: _, axis = plt.subplots(1, 1) axis.set_title('Discount rates') axis.set_xlabel('Year') axis.set_ylabel('discount rate (%)') axis.plot(self.years, self.rates*100, **kwargs) axis.set_xlim((self.years.min(), self.years.max())) return axis
[docs] def read_mat(self, file_name, description='', var_names=DEF_VAR_MAT): """Read MATLAB file generated with previous MATLAB CLIMADA version. Parameters: file_name (str): absolute file name description (str, optional): description of the data var_names (dict, optional): name of the variables in the file """ disc = hdf5.read(file_name) self.clear() self.tag.file_name = file_name self.tag.description = description try: disc = disc[var_names['sup_field_name']] except KeyError: pass try: disc = disc[var_names['field_name']] self.years = np.squeeze(disc[var_names['var_name']['year']]). \ astype(int, copy=False) self.rates = np.squeeze(disc[var_names['var_name']['disc']]) except KeyError as err: LOGGER.error("Not existing variable: %s", str(err)) raise err
[docs] def read_excel(self, file_name, description='', var_names=DEF_VAR_EXCEL): """Read excel file following template and store variables. Parameters: file_name (str): absolute file name description (str, optional): description of the data var_names (dict, optional): name of the variables in the file """ dfr = pd.read_excel(file_name, var_names['sheet_name']) self.clear() self.tag.file_name = file_name self.tag.description = description try: self.years = dfr[var_names['col_name']['year']].values. \ astype(int, copy=False) self.rates = dfr[var_names['col_name']['disc']].values except KeyError as err: LOGGER.error("Not existing variable: %s", str(err)) raise err
[docs] def write_excel(self, file_name, var_names=DEF_VAR_EXCEL): """ Write excel file following template. Parameters: file_name (str): absolute file name to write var_names (dict, optional): name of the variables in the file """ disc_wb = xlsxwriter.Workbook(file_name) disc_ws = disc_wb.add_worksheet(var_names['sheet_name']) header = [var_names['col_name']['year'], var_names['col_name']['disc']] for icol, head_dat in enumerate(header): disc_ws.write(0, icol, head_dat) for i_yr, (disc_yr, disc_rt) in enumerate(zip(self.years, self.rates), 1): disc_ws.write(i_yr, 0, disc_yr) disc_ws.write(i_yr, 1, disc_rt) disc_wb.close()