Source code for equilibrium.run_simulations

# -*- coding: utf-8 -*-
"""



"""

import copy
import numpy as np

import equilibrium.functions_dynamic as eqdyn
import equilibrium.compute_equilibrium as eqcmp
import inputs.parameters_and_options as inpprm
import inputs.data as inpdt


[docs]def run_simulation(t, options, param, grid, initial_state_utility, initial_state_error, initial_state_households, initial_state_households_housing_types, initial_state_housing_supply, initial_state_household_centers, initial_state_average_income, initial_state_rent, initial_state_dwelling_size, initial_state_capital_land, fraction_capital_destroyed, amenities, housing_limit, spline_estimate_RDP, spline_land_constraints, spline_land_backyard, spline_land_RDP, spline_land_informal, income_class_by_housing_type, precalculated_transport, spline_RDP, spline_agricultural_price, spline_interest_rate, spline_population_income_distribution, spline_inflation, spline_income_distribution, spline_population, spline_income, spline_minimum_housing_supply, spline_fuel, income_baseline): """ Run simulations over several years according to exogenous scenarios. After accounting for the changes in time-moving exogenous variables each period, the function returns key equilibrium values (computed with equilibrium.compute_equilibrium) under a housing supply constraint described in equilibrium.functions_dynamic and the technical documentation. This allows to account for inertia in formal private developers' actions and return more realistic simulated outputs. Parameters ---------- t : int Year for which we want to run the function options : dict Dictionary of default options param : dict Dictionary of default parameters grid : DataFrame Table yielding, for each grid cell (24,014), its x and y (centroid) coordinates, and its distance (in km) to the city centre initial_state_utility : ndarray(float64) Utility levels for each income group (4) at baseline year (2011) initial_state_error : ndarray(float64) Ratio (in %) of simulated number of households per income group over target population per income group at baseline year (2011) initial_state_households : ndarray(float64, ndim=3) Number of households per grid cell in each income group (4) and each housing type (4) at baseline year (2011) initial_state_households_housing_types : ndarray(float64, ndim=2) Number of households per grid cell in each housing type (4) at baseline year (2011) initial_state_housing_supply : ndarray(float64, ndim=2) Housing supply per unit of available land (in m² per km²) for each housing type (4) in each grid cell at baseline year (2011) initial_state_household_centers : ndarray(float64, ndim=2) Number of households per grid cell in each income group (4) at baseline year (2011) initial_state_average_income : ndarray(float64) Not an output of the model per se : it is just the average median income for each income group in the model (4), that may change over time initial_state_rent : ndarray(float64, ndim=2) Average annual rent (in rands) per grid cell for each housing type (4) at baseline year (2011) initial_state_dwelling_size : ndarray(float64, ndim=2) Average dwelling size (in m²) per grid cell in each housing type (4) at baseline year (2011) fraction_capital_destroyed : DataFrame Data frame of expected fractions of capital destroyed, for housing structures and contents in different housing types, in each grid cell (24,014) amenities : ndarray(float64) Normalized amenity index (relative to the mean) for each grid cell (24,014) housing_limit : Series Maximum housing supply (in m² per km²) in each grid cell (24,014) spline_estimate_RDP : interp1d Linear interpolation for the grid-level number of formal subsidized dwellings over the years (baseline year set at 0) spline_land_constraints : interp1d Linear interpolation for the grid-level overall land availability, (in %) over the years (baseline year set at 0) spline_land_backyard : interp1d Linear interpolation for the grid-level land availability (in %) for informal backyards over the years (baseline year set at 0) spline_land_RDP : interp1d Linear interpolation for the grid-level land availability (in %) for formal subsidized housing over the years (baseline year set at 0) spline_land_informal : interp1d Linear interpolation for the grid-level land availability (in %) for informal settlements over the years (baseline year set at 0) income_class_by_housing_type : DataFrame Set of dummies coding for housing market access (across 4 housing submarkets) for each income group (4, from poorest to richest) precalculated_transport : str Path for precalcuted transport inputs (intermediate outputs from commuting choice model) spline_RDP : interp1d Linear interpolation for the total number of formal subsidized dwellings over the years (baseline year set at 0) spline_agricultural_price : interp1d Linear interpolation for the agricultural land price (in rands) over the years (baseline year set at 0) spline_interest_rate : interp1d Linear interpolation for the interest rate (in %) over the years spline_population_income_distribution : interp1d Linear interpolation for total population per income group in the data (12) over the years (baseline year set at 0) spline_inflation : interp1d Linear interpolation for inflation rate (in base 100 relative to baseline year) over the years (baseline year set at 0) spline_income_distribution : interp1d Linear interpolation for median annual income (in rands) per income group in the data (12) over the years (baseline year set at 0) spline_population : interp1d Linear interpolation for total population over the years (baseline year set at 0) spline_income : interp1d Linear interpolation for overall average (annual) income over the years (baseline year set at 0), used to avoid money illusion in future simulations when computing the housing supply (see equilibrium.run_simulations) spline_minimum_housing_supply : interp1d Linear interpolation for minimum housing supply (in m²) over the years (baseline year set at 0) spline_fuel : interp1d Linear interpolation for fuel price (in rands per km) over the years (baseline year set at 0) income_baseline : DataFrame Table summarizing, for each income group in the data (12, including people out of employment), the number of households living in each endogenous housing type (3), their total number at baseline year (2011) in retrospect (2001), as well as the distribution of their average income (at baseline year) Returns ------- simulation_households_center : ndarray(float64, ndim=3) Number of households per grid cell (24,014) in each income group (4) over all simulation years (30) simulation_households_housing_type : ndarray(float64, ndim=3) Number of households per grid cell (24,014) in each housing type (4) over all simulation years (30) simulation_dwelling_size : ndarray(float64, ndim=3) Average dwelling size (in m²) per grid cell (24,014) in each housing type (4) over all simulation years (30) simulation_rent : ndarray(float64, ndim=3) Average annual rent (in rands) per grid cell (24,014) for each housing type (4) over all simulation years (30) simulation_households : ndarray(float64, ndim=4) Number of households per grid cell (24,014) in each income group (4) and each housing type (4) at baseline year (2011) over all simulation years (30) simulation_error : ndarray(float64, ndim=2) Ratio (in %) of simulated number of households per income group over target population per income group over all simulation years (30) simulation_housing_supply : ndarray(float64, ndim=3) Housing supply per unit of available land (in m² per km²) for each housing type (4) in each grid cell (24,014) over all simulation years (30) simulation_utility : ndarray(float64, ndim=2) Utility levels for each income group (4) over all simulation years (30) simulation_deriv_housing : ndarray(float64, ndim=2) Difference between simulated next and current period values for housing supply per unit of available land (in m² per km²) per grid cell (24,014), for all simulation years (30). simulation_T : ndarray(float64) Years (relative to baseline set at 0) used for the simulations """ # Number of iterations per year freq_iter = param["iter_calc_lite"] # Here, we are going to run separate simulations with and without # dynamics, hence the need for a new parameter options["adjust_housing_init"] = copy.deepcopy( options["adjust_housing_supply"]) options["adjust_housing_supply"] = 0 # Setting the timeline years_simulations = np.arange( t[0], t[len(t) - 1] + 1, (t[1] - t[0])/freq_iter) # Preallocating outputs simulation_dwelling_size = np.zeros((len(t), 4, len(grid.dist))) simulation_rent = np.zeros((len(t), 4, len(grid.dist))) simulation_households = np.zeros((len(t), 4, 4, len(grid.dist))) simulation_housing_supply = np.zeros((len(t), 4, len(grid.dist))) simulation_households_housing_type = np.zeros((len(t), 4, len(grid.dist))) simulation_households_center = np.zeros((len(t), 4, len(grid.dist))) simulation_error = np.zeros((len(t), 4)) simulation_utility = np.zeros((len(t), 4)) simulation_deriv_housing = np.zeros((len(t), len(grid.dist))) simulation_capital_land = np.zeros((len(t), 4, len(grid.dist))) # Starting the simulation for index_iter in range(0, len(years_simulations)): print(index_iter) year_temp = copy.deepcopy(years_simulations[index_iter]) stat_temp_housing_supply = copy.deepcopy(initial_state_housing_supply) stat_temp_rent = copy.deepcopy(initial_state_rent) if index_iter > 0: if index_iter == len(t): print('stop') # SIMULATION WITH EQUILIBRIUM HOUSING STOCK print('Simulation without constraint') options["adjust_housing_supply"] = 1 # All that changes (average_income, households_per_income_class ) = eqdyn.compute_average_income( spline_population_income_distribution, spline_income_distribution, param, year_temp) income_net_of_commuting_costs = np.load( precalculated_transport + "GRID_incomeNetOfCommuting_" + str(int(year_temp)) + ".npy") (param["subsidized_structure_value"] ) = (param["subsidized_structure_value_ref"] * (spline_inflation(year_temp) / spline_inflation(0))) (param["informal_structure_value"] ) = (param["informal_structure_value_ref"] * (spline_inflation(year_temp) / spline_inflation(0))) mean_income = spline_income(year_temp) interest_rate = eqdyn.interpolate_interest_rate( spline_interest_rate, year_temp) population = spline_population(year_temp) total_RDP = spline_RDP(year_temp) minimum_housing_supply = spline_minimum_housing_supply(year_temp) number_properties_RDP = spline_estimate_RDP(year_temp) # Scale factor needs to move to avoid monetary illusion in the # model, e.g. housing supply should not change when currency # changes and all prices move: see technical documentation for # math formula construction_param = ( (mean_income / param["income_year_reference"]) ** (- param["coeff_b"]) * param["coeff_A"] ) coeff_land = inpdt.import_coeff_land( spline_land_constraints, spline_land_backyard, spline_land_informal, spline_land_RDP, param, year_temp) agricultural_rent = inpprm.compute_agricultural_rent( spline_agricultural_price(year_temp), construction_param, interest_rate, param, options) # We compute a new static equilibrium for next period (tmpi_utility, tmpi_error, tmpi_simulated_jobs, tmpi_households_housing_types, tmpi_household_centers, tmpi_households, tmpi_dwelling_size, tmpi_housing_supply, tmpi_rent, tmpi_rent_matrix, tmpi_capital_land, tmpi_average_income, tmpi_limit_city) = eqcmp.compute_equilibrium( fraction_capital_destroyed, amenities, param, housing_limit, population, households_per_income_class, total_RDP, coeff_land, income_net_of_commuting_costs, grid, options, agricultural_rent, interest_rate, number_properties_RDP, average_income, mean_income, income_class_by_housing_type, minimum_housing_supply, construction_param, income_baseline) # Estimation of the housing supply difference between t and t+1 # (only for formal private housing) deriv_housing_temp = eqdyn.evolution_housing_supply( housing_limit, param, years_simulations[index_iter], years_simulations[index_iter - 1], tmpi_housing_supply[0, :], stat_temp_housing_supply[0, :]) # We update the initial housing parameter as it will give the # housing supply when developers do not adjust param["housing_in"] = stat_temp_housing_supply[0, :] + deriv_housing_temp # RUN A NEW SIMULATION WITH FIXED HOUSING # This allows to get a constrained "dynamic" equilibrium after # taking inertia and depreciation into account print('Simulation with constraint') options["adjust_housing_supply"] = 0 (initial_state_utility, initial_state_error, initial_state_simulated_jobs, initial_state_households_housing_types, initial_state_household_centers, initial_state_households, initial_state_dwelling_size, initial_state_housing_supply, initial_state_rent, initial_state_rent_matrix, initial_state_capital_land, initial_state_average_income, initial_state_limit_city) = eqcmp.compute_equilibrium( fraction_capital_destroyed, amenities, param, housing_limit, population, households_per_income_class, total_RDP, coeff_land, income_net_of_commuting_costs, grid, options, agricultural_rent, interest_rate, number_properties_RDP, average_income, mean_income, income_class_by_housing_type, minimum_housing_supply, construction_param, income_baseline) stat_temp_deriv_housing = copy.deepcopy(deriv_housing_temp) # We initialize the output vector else: stat_temp_deriv_housing = np.zeros(len(stat_temp_rent[0, :])) # The next condition was added to consider more than one simulation per year. # This is not used in practice and was not fully coded, hence the function # breaks down for frequencies bigger than one. if ((index_iter - 1) / param["iter_calc_lite"] - np.floor((index_iter - 1) / param["iter_calc_lite"])) == 0: # We retain the new constrained equilibrium with dynamic housing # supply as an output simulation_capital_land[int( (index_iter - 1) / param["iter_calc_lite"] + 1), :, : ] = copy.deepcopy(initial_state_capital_land) simulation_households_center[int( (index_iter - 1) / param["iter_calc_lite"] + 1), :, : ] = copy.deepcopy(initial_state_household_centers) simulation_households_housing_type[int( (index_iter - 1) / param["iter_calc_lite"] + 1), :, : ] = copy.deepcopy(initial_state_households_housing_types) simulation_dwelling_size[int( (index_iter - 1) / param["iter_calc_lite"] + 1), :, : ] = copy.deepcopy(initial_state_dwelling_size) simulation_rent[ int((index_iter - 1) / param["iter_calc_lite"] + 1), :, : ] = copy.deepcopy(initial_state_rent) simulation_households[ int((index_iter - 1) / param["iter_calc_lite"] + 1), :, :, : ] = copy.deepcopy(initial_state_households) simulation_error[ int((index_iter - 1) / param["iter_calc_lite"] + 1), : ] = copy.deepcopy(initial_state_error) simulation_housing_supply[ int((index_iter - 1) / param["iter_calc_lite"] + 1), :, : ] = copy.deepcopy(initial_state_housing_supply) simulation_utility[ int((index_iter - 1) / param["iter_calc_lite"] + 1), : ] = copy.deepcopy(initial_state_utility) simulation_deriv_housing[ int((index_iter - 1) / param["iter_calc_lite"] + 1), : ] = copy.deepcopy(stat_temp_deriv_housing) # In case we have more than one simulation per year, we collapse timeline # to yearly if len(t) < len(years_simulations): T = copy.deepcopy(t) # Else, we just keep it as it is else: T = copy.deepcopy(years_simulations) # We retain this timeline as an output for our plots simulation_T = copy.deepcopy(T) # We also reinitialize parameter values options["adjust_housing_supply"] = copy.deepcopy( options["adjust_housing_init"]) return (simulation_households_center, simulation_households_housing_type, simulation_dwelling_size, simulation_rent, simulation_households, simulation_error, simulation_housing_supply, simulation_utility, simulation_deriv_housing, simulation_T, simulation_capital_land)