Source code for project.dynamic
# Copyright 2020-2021 Ecole Nationale des Ponts et Chaussées
#
# This file is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# Original author Lucas Vivier <vivier@centre-cired.fr>
import pandas as pd
import numpy as np
[docs]def stock_need(population, pop_housing_ini, pop_housing_min, start, factor):
"""
Parameters
----------
population
pop_housing_ini
pop_housing_min
start
factor
Returns
-------
Stock need (number of dwellings that need to be constructed) : pd.Series
Population by housing : pd.Series
"""
population_housing = dict()
population_housing[start] = pop_housing_ini
max_year = max(population.index)
stock_needed = dict()
stock_needed[start] = population[start] / population_housing[start]
for year in range(start + 1, max_year + 1):
if year > max_year:
break
population_housing[year] = population_housing_dynamic(population_housing[year - 1],
pop_housing_min,
pop_housing_ini,
factor)
stock_needed[year] = population[year] / population_housing[year]
return pd.Series(stock_needed), pd.Series(population_housing)
[docs]def population_housing_dynamic(pop_housing_prev, pop_housing_min, pop_housing_ini, factor):
"""Returns number of people by housing.
Number of people by housing decrease over the time.
Parameters
----------
pop_housing_prev: int
pop_housing_min: int
pop_housing_ini: int
factor: int
Returns
-------
int
"""
eps_pop_housing = (pop_housing_prev - pop_housing_min) / (
pop_housing_ini - pop_housing_min)
eps_pop_housing = max(0, min(1, eps_pop_housing))
factor_pop_housing = factor * eps_pop_housing
return max(pop_housing_min, pop_housing_prev * (1 + factor_pop_housing))
[docs]def share_multi_family(stock_need, factor):
"""Calculate share of multi-family buildings in the total stock
In Res-IRF 2.0, the share of single- and multi-family dwellings was held constant in both existing and new
housing stocks, but at different levels; it therefore evolved in the total stock by a simple composition
effect. These dynamics are now more precisely parameterized in Res-IRF 3.0 thanks to recent empirical
work linking the increase in the share of multi-family housing in the total stock to the rate of growth of
the total stock housing growth (Fisch et al., 2015).
This relationship in particular reflects urbanization effects.
Parameters
----------
stock_need: pd.Series
factor: float
Returns
-------
dict
Dictionary with year as keys and share of multi-family in the total stock as value.
{2012: 0.393, 2013: 0.394, 2014: 0.395}
"""
def func(stock, stock_ini, f):
"""Share of multi-family dwellings as a function of the growth rate of the dwelling stock.
Parameters
----------
stock: float
stock_ini: float
f: float
Returns
-------
float
"""
trend_housing = (stock - stock_ini) / stock_ini * 100
share = 0.1032 * np.log(10.22 * trend_housing / 10 + 79.43) * f
return share
share_multi_family_tot = {}
stock_needed_ini = stock_need.iloc[0]
for year in stock_need.index:
share_multi_family_tot[year] = func(stock_need.loc[year], stock_needed_ini, factor)
return pd.Series(share_multi_family_tot)
[docs]def evolution_surface_built(surface_ini, surface_max, elasticity_surface, income):
"""Evolution of new buildings area based on total available income. Function represents growth.
Parameters
----------
surface_ini: pd.Series
surface_max: pd.Series
elasticity_surface: pd.Series
income: pd.Series
Returns
-------
pd.DataFrame
"""
surface = {income.index[0]: surface_ini}
for year in income.index[1:]:
surface_max = surface_max.reorder_levels(surface_ini.index.names)
eps_area_new = (surface_max - surface[year - 1]) / (surface_max - surface_ini)
eps_area_new = eps_area_new.apply(lambda x: max(0, min(1, x)))
elasticity = eps_area_new.multiply(elasticity_surface)
factor = elasticity * max(0.0, income[year] / income.iloc[0] - 1.0)
surface[year] = pd.concat([surface_max, surface[year - 1] * (1 + factor)], axis=1).min(axis=1)
return pd.DataFrame(surface)
[docs]def share_type_built(stock_need, share_multi_family, flow_built):
"""Calculate multi-family buildings in the construction.
Parameters
----------
stock_need: pd.Series
share_multi_family: pd.Series
flow_built: pd.Series
Returns
-------
pd.Series
"""
share_multi_family_built = (stock_need * share_multi_family - stock_need.shift(1) * share_multi_family.shift(
1)) / flow_built
return pd.concat((share_multi_family_built, 1 - share_multi_family_built), axis=1,
keys=['Multi-family', 'Single-family'], names=['Housing type']).dropna().T