diff --git a/.gitignore b/.gitignore
index 4431552f..ce902749 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,8 @@
/project/config/validation/
/project/test
/project/input/stock/buildingstock_sdes2018_medium_3.csv
+/project/input/stock/20241205_buildingstock_sdes2023_medium_3.csv
+/project/input/stock/20241205_buildingstock_sdes2023_update.csv
/project/analysis/
/project/input/macro/_old/
/project/input/policies/_old/
@@ -26,6 +28,9 @@
/project/input/revealed_data/_old/
/project/config/coupling/old/
/project/input/investment/_old/
+/project/input/sfec/
+/project/config/sfec/
+/project/input/stock/
# Byte-compiled / optimized / DLL files
@@ -33,3 +38,6 @@ __pycache__/
**/__pycache__
*.py[cod]
/project/config/hidden_cost/
+
+/venv/*
+/.vscode/launch.json
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..796a0daf
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,18 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+ "name": "Python Debugger: Current File",
+ "type": "debugpy",
+ "request": "launch",
+ "console": "integratedTerminal",
+ "cwd": "${workspaceFolder}",
+ "module": "project.main",
+ "args": ["--config","project/config/config.json"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/project/building.py b/project/building.py
index a85d1f35..1537bb84 100644
--- a/project/building.py
+++ b/project/building.py
@@ -75,7 +75,7 @@ class ThermalBuildings:
"""
def __init__(self, stock, surface, ratio_surface, efficiency, income, path=None, year=2018,
- resources_data=None, detailed_output=None, figures=None, residual_rate=0, temp_sink=None):
+ resources_data=None, detailed_output=None, figures=None, residual_rate=0, temp_sink=None, pef_elec=None):
# default values
self.hi_threshold = None
@@ -145,6 +145,7 @@ def __init__(self, stock, surface, ratio_surface, efficiency, income, path=None,
'certificate_renovation': Series(dtype='float'),
}
+ self.pef_elec = pef_elec
self.stock = stock
if self.path_ini is not None:
stock = self.add_certificate(stock).groupby('Performance').sum() / 10 ** 6
@@ -169,6 +170,14 @@ def year(self, year):
if self._temp_sink_yrs is not None:
self._temp_sink = self._temp_sink_yrs.loc[year]
+ @property
+ def pef_elec(self):
+ return self._pef_elec
+
+ @pef_elec.setter
+ def pef_elec(self, values):
+ self._pef_elec = values
+
@property
def stock(self):
return self._stock
@@ -196,8 +205,8 @@ def stock(self, stock):
self.energy = self.to_energy(stock).astype('category')
self._resources_data['index']['Energy'] = [i for i in self._resources_data['index']['Energy'] if
i in self.energy.unique()]
-
- consumption_sd, _, certificate = self.consumption_heating_store(stock.index)
+ pef_elec = self.pef_elec.loc[self.year]
+ consumption_sd, _, certificate = self.consumption_heating_store(stock.index, pef_elec=pef_elec)
self.certificate = reindex_mi(certificate, stock.index).astype('category')
@property
@@ -328,9 +337,30 @@ def size_heater(self, index=None):
return size_heating_system / 1e3
+ def reset_consumption_store(self):
+ """
+ Reset the internal cache for consumption and certificates.
+
+ This method initializes empty pandas Series for all stored
+ consumption and certificate variables, including those related
+ to renovations.
+
+ Returns
+ -------
+ None
+ """
+ self._consumption_store = {
+ 'consumption': pd.Series(dtype='float'),
+ 'consumption_3uses': pd.Series(dtype='float'),
+ 'certificate': pd.Series(dtype='float'),
+ 'consumption_renovation': pd.Series(dtype='float'),
+ 'consumption_3uses_renovation': pd.Series(dtype='float'),
+ 'certificate_renovation': pd.Series(dtype='float'),
+ }
+
def consumption_heating(self, index=None, freq='year', climate=None, smooth=False,
full_output=False, efficiency_hour=False, level_heater='Heating system',
- method='5uses', hourly_profile=None, temp_sink=None):
+ method='5uses', hourly_profile=None, temp_sink=None, pef_elec=None):
"""Calculation consumption standard of the current building stock [kWh/m2.a].
Parameters
@@ -375,7 +405,9 @@ def consumption_heating(self, index=None, freq='year', climate=None, smooth=Fals
certificate, consumption_3uses = thermal.conventional_energy_3uses(wall, floor, roof, windows,
self._ratio_surface.copy(),
efficiency, _index,
- method=method)
+ method=method,
+ pef_elec=pef_elec
+ )
certificate = reindex_mi(certificate, index)
consumption_3uses = reindex_mi(consumption_3uses, index)
@@ -383,7 +415,7 @@ def consumption_heating(self, index=None, freq='year', climate=None, smooth=Fals
else:
return consumption
- def consumption_heating_store(self, index, level_heater='Heating system', full_output=True):
+ def consumption_heating_store(self, index, level_heater='Heating system', full_output=True, pef_elec=None):
"""Pre-calculate space energy consumption based only on relevant levels.
@@ -414,7 +446,7 @@ def consumption_heating_store(self, index, level_heater='Heating system', full_o
if not idx.empty:
consumption, certificate, consumption_3uses = self.consumption_heating(index=idx, freq='year', climate=None,
- full_output=True)
+ full_output=True, pef_elec=pef_elec)
self._consumption_store['consumption'] = concat((self._consumption_store['consumption'], consumption))
self._consumption_store['consumption'].index = MultiIndex.from_tuples(
@@ -446,7 +478,7 @@ def consumption_heating_store(self, index, level_heater='Heating system', full_o
return consumption_sd
def to_heating_intensity(self, index, prices, consumption=None, level_heater='Heating system', bill_rebate=0,
- full_output=False):
+ full_output=False, pef_elec=None):
"""Calculate heating intensity of index based on energy prices.
Parameters
@@ -463,7 +495,7 @@ def to_heating_intensity(self, index, prices, consumption=None, level_heater='He
Heating intensity
"""
if consumption is None:
- consumption = reindex_mi(self.consumption_heating_store(index, full_output=False), index) * reindex_mi(
+ consumption = reindex_mi(self.consumption_heating_store(index, full_output=False, pef_elec=pef_elec), index) * reindex_mi(
self._surface, index)
energy_bill = AgentBuildings.energy_bill(prices, consumption, level_heater=level_heater,
bill_rebate=bill_rebate)
@@ -482,7 +514,7 @@ def to_heating_intensity(self, index, prices, consumption=None, level_heater='He
else:
return heating_intensity, budget_share
- def consumption_actual(self, prices, consumption=None, full_output=False, bill_rebate=0):
+ def consumption_actual(self, prices, consumption=None, full_output=False, bill_rebate=0, pef_elec=None):
"""Space heating consumption based on standard space heating consumption and heating intensity (kWh/building.a).
@@ -504,14 +536,15 @@ def consumption_actual(self, prices, consumption=None, full_output=False, bill_r
if consumption is None:
index = self.stock.index
- consumption = self.consumption_heating_store(index, full_output=False)
+ consumption = self.consumption_heating_store(index, full_output=False, pef_elec=pef_elec)
consumption = reindex_mi(consumption, index) * reindex_mi(self._surface, index)
else:
consumption = consumption.copy()
index = consumption.index
heating_intensity, budget_share = self.to_heating_intensity(index, prices, consumption=consumption,
- full_output=True, bill_rebate=bill_rebate)
+ full_output=True, bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
consumption = consumption * heating_intensity
if full_output is False:
@@ -521,7 +554,7 @@ def consumption_actual(self, prices, consumption=None, full_output=False, bill_r
def consumption_agg(self, prices=None, freq='year', climate=None, smooth=False,
standard=False, efficiency_hour=False, existing=False, agg='all', bill_rebate=0,
- hourly_profile=None):
+ hourly_profile=None, pef_elec=None):
"""Aggregated final energy consumption (TWh final energy).
Parameters
@@ -547,7 +580,7 @@ def consumption_agg(self, prices=None, freq='year', climate=None, smooth=False,
if standard is True:
if freq == 'year':
- consumption = self.consumption_heating(freq=freq, climate=None)
+ consumption = self.consumption_heating(freq=freq, climate=None, pef_elec=pef_elec)
consumption = reindex_mi(consumption, self.stock.index) * self.surface * self.stock
if existing is True:
consumption = consumption[consumption.index.get_level_values('Existing')]
@@ -563,11 +596,11 @@ def consumption_agg(self, prices=None, freq='year', climate=None, smooth=False,
if standard is False:
if freq == 'year':
# TODO: if climate is none consumption_heating_store ?
- consumption = self.consumption_heating(freq=freq, climate=climate, temp_sink=self._temp_sink)
+ consumption = self.consumption_heating(freq=freq, climate=climate, temp_sink=self._temp_sink, pef_elec=pef_elec)
consumption = reindex_mi(consumption, self.stock.index) * self.surface
if existing is True:
consumption = consumption[consumption.index.get_level_values('Existing')]
- consumption = self.consumption_actual(prices, consumption=consumption, bill_rebate=bill_rebate) * self.stock
+ consumption = self.consumption_actual(prices, consumption=consumption, bill_rebate=bill_rebate, pef_elec=pef_elec) * self.stock
if agg == 'all':
consumption = self.apply_calibration(consumption, agg='energy') / 10 ** 9
@@ -580,11 +613,12 @@ def consumption_agg(self, prices=None, freq='year', climate=None, smooth=False,
if freq == 'hour':
consumption = self.consumption_heating(freq=freq, climate=climate, smooth=smooth,
efficiency_hour=efficiency_hour, hourly_profile=hourly_profile,
- temp_sink=self._temp_sink)
+ temp_sink=self._temp_sink, pef_elec=pef_elec)
consumption = (reindex_mi(consumption, self.stock.index).T * self.surface).T
heating_intensity = self.to_heating_intensity(consumption.index, prices,
consumption=consumption.sum(axis=1),
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
consumption = (consumption.T * heating_intensity * self.stock).T
consumption = self.apply_calibration(consumption)
return consumption
@@ -625,7 +659,7 @@ def apply_calibration(self, consumption, level_heater='Heating system', agg='ene
return _consumption_energy
- def calibration_consumption(self, prices, consumption_ini, health_cost_income, health_cost_dpe, climate=None):
+ def calibration_consumption(self, prices, consumption_ini, health_cost_income, health_cost_dpe, climate=None, pef_elec=None):
"""Calculate energy indicators.
Parameters
@@ -640,7 +674,7 @@ def calibration_consumption(self, prices, consumption_ini, health_cost_income, h
"""
if self.coefficient_global is None:
- consumption, certificate, consumption_3uses = self.consumption_heating(climate=climate, full_output=True)
+ consumption, certificate, consumption_3uses = self.consumption_heating(climate=climate, full_output=True, pef_elec=pef_elec)
s = self.stock.groupby(consumption.index.names).sum()
if self.path_ini is not None:
df = concat((consumption_3uses, s), axis=1, keys=['Consumption', 'Stock'])
@@ -662,10 +696,11 @@ def calibration_consumption(self, prices, consumption_ini, health_cost_income, h
_consumption_actual, heating_intensity, budget_share = self.consumption_actual(prices,
consumption=consumption,
- full_output=True)
+ full_output=True,
+ pef_elec=pef_elec)
# calibration health_cost on heating intensity
total_health_cost = self.health_cost(health_cost_dpe, health_cost_income, prices,
- method_health_cost='epc')
+ method_health_cost='epc', pef_elec=pef_elec)
"""_, certificate, _ = self.consumption_heating(method='3uses', full_output=True)
temp = concat((heating_intensity, self.stock), axis=1, keys=['Heating intensity', 'Stock'])
temp = concat((temp, reindex_mi(certificate, temp.index).rename('Performance')), axis=1)
@@ -831,7 +866,7 @@ def energy_bill(prices, consumption, level_heater='Heating system', bill_rebate=
# * reindex_mi(self._surface, index)
return (reindex_mi(consumption, index).T * prices - bill_rebate).T
- def optimal_temperature(self, prices):
+ def optimal_temperature(self, prices, pef_elec=None):
"""Find indoor temperature based on energy prices, housing performance and income level.
Parameters
@@ -844,18 +879,18 @@ def optimal_temperature(self, prices):
"""
- def func(temp, consumption, index):
- consumption_temp = self.consumption_heating(temp_indoor=temp)
+ def func(temp, consumption, index, pef_elec):
+ consumption_temp = self.consumption_heating(temp_indoor=temp, pef_elec=pef_elec)
consumption_temp = reindex_mi(consumption_temp, index) * self.surface
return consumption - consumption_temp
- consumption_actual = self.consumption_actual(prices)
- consumption_sd = self.consumption_heating(temp_indoor=None)
+ consumption_actual = self.consumption_actual(prices, pef_elec=pef_elec)
+ consumption_sd = self.consumption_heating(temp_indoor=None, pef_elec=pef_elec)
consumption_sd = reindex_mi(consumption_sd, self.stock.index) * self.surface
temp_optimal = {}
for i, v in consumption_actual.iteritems():
- temp_optimal.update({i: fsolve(func, 19, args=(consumption_actual.loc[i], i))[0]})
+ temp_optimal.update({i: fsolve(func, 19, args=(consumption_actual.loc[i], i, pef_elec))[0]})
temp_optimal = Series(temp_optimal)
temp = concat((consumption_actual, consumption_sd, temp_optimal), axis=1,
@@ -863,7 +898,7 @@ def func(temp, consumption, index):
return temp_optimal
- def store_consumption(self, prices, carbon_content, bill_rebate=0):
+ def store_consumption(self, prices, carbon_content, bill_rebate=0, pef_elec=None):
"""Store energy consumption.
@@ -876,14 +911,14 @@ def store_consumption(self, prices, carbon_content, bill_rebate=0):
bill_rebate
"""
output = dict()
- temp = self.consumption_agg(freq='year', standard=True, existing=True, agg='energy')
+ temp = self.consumption_agg(freq='year', standard=True, existing=True, agg='energy', pef_elec=pef_elec)
temp = temp.reindex(prices.index).fillna(0)
output.update({'Consumption standard (TWh)': temp.sum()})
temp.index = temp.index.map(lambda x: 'Consumption standard {} (TWh)'.format(x))
output.update(temp)
temp = self.consumption_agg(prices=prices, freq='year', standard=False, climate=None, smooth=False,
- existing=True, agg='energy', bill_rebate=bill_rebate)
+ existing=True, agg='energy', bill_rebate=bill_rebate, pef_elec=pef_elec)
temp = temp.reindex(prices.index).fillna(0)
output.update({'Consumption (TWh)': temp.sum()})
emission = (temp * carbon_content).sum() / 10 ** 3
@@ -954,11 +989,11 @@ def __init__(self, stock, surface, ratio_surface, efficiency, income, preference
rational_behavior_insulation=None, rational_behavior_heater=None,
resources_data=None, detailed_output=True, figures=None,
method_health_cost=None, residual_rate=0, constraint_heat_pumps=True,
- variable_size_heater=True, temp_sink=None
+ variable_size_heater=True, temp_sink=None, pef_elec=None
):
super().__init__(stock, surface, ratio_surface, efficiency, income, path=path, year=year,
resources_data=resources_data, detailed_output=detailed_output, figures=figures,
- residual_rate=residual_rate, temp_sink=temp_sink)
+ residual_rate=residual_rate, temp_sink=temp_sink, pef_elec=pef_elec)
if logger is None:
logger = logging.getLogger()
@@ -1064,6 +1099,19 @@ def __init__(self, stock, surface, ratio_surface, efficiency, income, preference
method_health_cost = 'epc'
self.method_health_cost = method_health_cost
+ self.sum_performance_insulation = None
+ self.sum_performance_insulation_obligation = None
+ self.flow_by_certificate_couples_insulation = None
+ self.flow_by_certificate_couples_obligation = None
+ self.flow_by_certificate_couples_ampleur_insulation = None
+ self.flow_by_certificate_couples_ampleur_obligation = None
+ self.flow_by_operation_insulation = None
+ self.renovation_details_long = None
+ self.renovation_details_long_obligation = None
+ self.merged_df_heater = None
+ self.flow_by_certificate_couples_heater = None
+ self.sum_performance_changes_heater = None
+
@property
def year(self):
return self._year
@@ -1094,8 +1142,10 @@ def year(self, year):
'subsidies_count': {},
'subsidies_average': {},
'cost_average': {},
- 'replacement_eligible': {}
- }
+ 'replacement_eligible': {},
+ 'replacement_eligible_income': {}
+
+ }
for k, item in ini.items():
self._heater_store[k] = item
@@ -1315,7 +1365,7 @@ def select_deep_renovation(certificate_after):
return condition
def prepare_consumption(self, choice_insulation=None, performance_insulation=None, index=None, method_epc='5uses',
- level_heater='Heating system', full_output=True, store=True, climate=None):
+ level_heater='Heating system', full_output=True, store=True, climate=None, pef_elec=None):
"""Standard energy consumption and energy performance certificate for each renovation works option.
Standard energy consumption only depends on building characteristics.
@@ -1403,10 +1453,12 @@ def prepare_consumption(self, choice_insulation=None, performance_insulation=Non
if climate is not None or method_epc == '3uses':
consumption, certificate, consumption_3uses = self.consumption_heating(index=index, climate=climate,
level_heater='Heating system',
- method=method_epc, full_output=True)
+ method=method_epc, full_output=True,
+ pef_elec=pef_elec)
else:
consumption, consumption_3uses, certificate = self.consumption_heating_store(index,
- level_heater='Heating system')
+ level_heater='Heating system',
+ pef_elec=pef_elec)
rslt = dict()
@@ -1914,6 +1966,15 @@ def calibration(x, _ms, _utility_ini, _flow, _idx, _ref, _u_shock, _target,
utility_subsidies = subsidies_total * self.preferences_heater['subsidy'] / 1000
cost_heater = cost_heater.reindex(index).reindex(choice_heater, axis=1)
+
+ #Florian test: on augmente la TVA sur le prix des chaudière gaz
+ if self.year>2024:
+ # print('Cout chaudiere')
+ # print(cost_heater['Natural gas-Performance boiler'])
+ # cost_heater['Natural gas-Collective boiler']*=1.2/1.055
+ cost_heater['Natural gas-Performance boiler']*=1.2/1.055
+ # print(cost_heater['Natural gas-Performance boiler'])
+
pref_investment = reindex_mi(self.preferences_heater['cost'], index)
utility_cost = (pref_investment * cost_heater.T).T / 1000
@@ -1977,7 +2038,7 @@ def calibration(x, _ms, _utility_ini, _flow, _idx, _ref, _u_shock, _target,
return market_share, error_conditional
- def exogenous_market_share_heater(self, index, choice_heater_idx):
+ def exogenous_market_share_heater(self, index, choice_heater_idx, pef_elec=None):
"""Define exogenous market-share.
Market-share is defined by _market_share_exogenous attribute.
@@ -2014,9 +2075,9 @@ def exogenous_market_share_heater(self, index, choice_heater_idx):
temp = Series(0, index=index, dtype='float').to_frame().dot(
Series(0, index=choice_heater_idx, dtype='float').to_frame().T)
index_final = temp.stack().index
- _, _, certificate = self.consumption_heating_store(index_final, level_heater='Heating system final')
+ _, _, certificate = self.consumption_heating_store(index_final, level_heater='Heating system final', pef_elec=pef_elec)
certificate = reindex_mi(certificate.unstack('Heating system final'), index)
- certificate_before = self.consumption_heating_store(index)[2]
+ certificate_before = self.consumption_heating_store(index, pef_elec=pef_elec)[2]
certificate_before = reindex_mi(certificate_before, index)
self._heater_store['epc_upgrade'] = - certificate.replace(EPC2INT).sub(
@@ -2133,6 +2194,9 @@ def store_information_heater(self, cost_heater, subsidies_total, bill_saved, sub
self._heater_store['replacement_eligible'].update(
{key: replacement_eligible.groupby('Housing type').sum()})
+ self._heater_store['replacement_eligible_income'].update(
+ {key: replacement_eligible.groupby('Income owner').sum()})
+
if eligible.sum().sum() > 0:
self._heater_store['subsidies_average'].update({key: sub.sum().sum() / replacement_eligible.sum()})
self._heater_store['cost_average'].update({key: cost.sum().sum() / replacement_eligible.sum()})
@@ -2143,7 +2207,7 @@ def store_information_heater(self, cost_heater, subsidies_total, bill_saved, sub
def heater_replacement(self, stock, prices, cost_heater, policies_heater, calib_heater=None,
step=1, financing_cost=None, district_heating=None, premature_replacement=None,
prices_before=None, supply=None, store_information=True, bill_rebate=0,
- carbon_content=None, carbon_value=None):
+ carbon_content=None, carbon_value=None, pef_elec=None):
"""Function returns building stock updated after switching heating system.
@@ -2237,7 +2301,7 @@ def apply_rational_choice(_consumption_saved, _subsidies_total, _cost_total, _bi
condition.columns.names = ['Heating system final']
if self._constraint_heat_pumps:
if isinstance(self._constraint_heat_pumps, list):
- _, certificate, _ = self.consumption_heating(method='3uses', full_output=True)
+ _, certificate, _ = self.consumption_heating(method='3uses', full_output=True, pef_elec=pef_elec)
condition = concat((condition, reindex_mi(certificate.rename('Performance'), condition.index)), axis=1)
condition = condition.set_index('Performance', append=True)
idx = (condition.index.get_level_values('Performance').isin(['F', 'G'])) & (
@@ -2282,12 +2346,12 @@ def apply_rational_choice(_consumption_saved, _subsidies_total, _cost_total, _bi
temp = Series(0, index=index, dtype='float').to_frame().dot(Series(0, index=choice_heater_idx, dtype='float').to_frame().T)
index_final = temp.stack().index
- consumption, _, certificate = self.consumption_heating_store(index_final, level_heater='Heating system final')
+ consumption, _, certificate = self.consumption_heating_store(index_final, level_heater='Heating system final', pef_elec=pef_elec)
consumption = reindex_mi(consumption.unstack('Heating system final'), index)
prices_re = prices.reindex(energy).set_axis(consumption.columns)
bill = ((consumption * prices_re).T * reindex_mi(self._surface, index)).T
- consumption_before = self.consumption_heating_store(index, level_heater='Heating system')[0]
+ consumption_before = self.consumption_heating_store(index, level_heater='Heating system', pef_elec=pef_elec)[0]
consumption_before = reindex_mi(consumption_before, index) * reindex_mi(self._surface, index)
emission_before = AgentBuildings.energy_bill(carbon_content, consumption_before)
bill_before = AgentBuildings.energy_bill(prices, consumption_before)
@@ -2295,7 +2359,7 @@ def apply_rational_choice(_consumption_saved, _subsidies_total, _cost_total, _bi
bill_saved = - bill.sub(bill_before, axis=0)
certificate = reindex_mi(certificate.unstack('Heating system final'), index)
- certificate_before = self.consumption_heating_store(index)[2]
+ certificate_before = self.consumption_heating_store(index, pef_elec=pef_elec)[2]
certificate_before = reindex_mi(certificate_before, index)
consumption = (reindex_mi(self._surface, consumption.index) * consumption.T).T
@@ -2335,7 +2399,8 @@ def apply_rational_choice(_consumption_saved, _subsidies_total, _cost_total, _bi
heating_intensity_before = self.to_heating_intensity(consumption_before.index, prices_before,
consumption=consumption_before,
level_heater='Heating system',
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
consumption_before *= heating_intensity_before
consumption = self.add_attribute(consumption, 'Income tenant')
@@ -2345,7 +2410,8 @@ def apply_rational_choice(_consumption_saved, _subsidies_total, _cost_total, _bi
heating_intensity_after = self.to_heating_intensity(consumption.index, prices_before,
consumption=consumption,
level_heater='Heating system final',
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
consumption_actual = (consumption * heating_intensity_after).unstack('Heating system final')
consumption_no_rebound = (consumption.unstack('Heating system final').T * heating_intensity_before).T
@@ -2388,7 +2454,7 @@ def apply_rational_choice(_consumption_saved, _subsidies_total, _cost_total, _bi
discount_social=0.032)
else:
- market_share = self.exogenous_market_share_heater(index, cost_heater.columns)
+ market_share = self.exogenous_market_share_heater(index, cost_heater.columns, pef_elec=pef_elec)
assert (market_share.sum(axis=1).round(0) == 1).all(), 'Market-share issue'
@@ -3019,7 +3085,8 @@ def apply_regulation(idx_target, idx_replace, level):
def endogenous_renovation(self, stock, prices, subsidies_total, cost_insulation, lifetime,
calib_renovation=None, min_performance=None, subsidies_details=None,
cost_financing=None, supply=None, discount=None,
- carbon_value=None, credit_constraint=None, performance_gap=1):
+ carbon_value=None, credit_constraint=None, performance_gap=1,
+ pef_elec=None):
"""Calculate endogenous retrofit based on discrete choice model.
@@ -3787,12 +3854,12 @@ def indicator_renovation_rate(_stock, _cost_insulation, _bill_saved, _subsidies_
proba_replacement = 1 / lifetime
- consumption_before = self.consumption_heating_store(index, level_heater='Heating system final')[0]
+ consumption_before = self.consumption_heating_store(index, level_heater='Heating system final', pef_elec=pef_elec)[0]
consumption_before = reindex_mi(consumption_before, index) * reindex_mi(self._surface, index)
energy_bill_before = AgentBuildings.energy_bill(prices, consumption_before, level_heater='Heating system final')
consumption_after = self.prepare_consumption(self._choice_insulation, index=index,
- level_heater='Heating system final', full_output=False)
+ level_heater='Heating system final', full_output=False, pef_elec=pef_elec)
consumption_after = reindex_mi(consumption_after, index).reindex(self._choice_insulation, axis=1)
consumption_after = (consumption_after.T * reindex_mi(self._surface, index)).T
consumption_saved = (consumption_before - consumption_after.T).T
@@ -3955,12 +4022,309 @@ def store_information_insulation(self, condition, cost_insulation_raw, tax, cost
'hidden_cost': hidden_cost
})
+ def certificate_flow_insulation(self, stock, renovation_rate, market_share, certificate_before_heater, certificate_before, certificate_after, call_from_obligation=False):
+ """ Calculates the renovation flow for each possible pair of certificates, and the sum of high-performance renovations.
+ Take certificates into account before changing heating systems, but the flows are those of insulation.
+
+ Parameters
+ ----------
+ stock: Series
+ renovation_rate: Series
+ market_share: DataFrame
+ certificate_before_heater: Series
+ certificate_after: DataFrame
+ call_from_obligation : Boolean (Optional)
+
+
+ Returns
+ -------
+ None
+ """
+
+ # mapping_operations = {"False False False True" : "Wi",
+ # "False False True False" : "other",
+ # "False False True True": "other",
+ # "False True False False": "other",
+ # "False True False True": "other",
+ # "False True True False": "other",
+ # "False True True True": "other",
+ # "True False False False": "other",
+ # "True False False True": "other",
+ # "True False True False": "other",
+ # "True False True True": "other",
+ # "True True False False": "other",
+ # "True True False True": "other",
+ # "True True True False": "other",
+ # "True True True True": "other"}
+
+ mapping_operations = {"Wi" : "Windows",
+ "R" : "othersingleinsulation",
+ "RWi" : "2insulations",
+ "F" : "othersingleinsulation",
+ "FWi": "2insulations",
+ "FR": "2insulations",
+ "FRWi": "3insulations",
+ "Wa": "othersingleinsulation",
+ "WaWi": "2insulations",
+ "WR": "2insulations",
+ "WaRWi":"3insulations",
+ "WaF": "2insulations",
+ "WaFWi": "3insulations",
+ "WaFR": "3insulations",
+ "WaFRWi": "4insulations"}
+
+ # mapping_operations_2 = {0 : "Wi",
+ # 1 : "R",
+ # 2 : "RWi",
+ # 3 : "F",
+ # 4 : "FWi",
+ # 5 : "FR",
+ # 6 : "FRWi",
+ # 7 : "Wa",
+ # 8 : "WaWi",
+ # 9 : "WR",
+ # 10 : "WaRWi",
+ # 11: "WaF",
+ # 12: "WaFWi",
+ # 13: "WaFR",
+ # 14: "WaFRWi"}
+
+ # mapping_operations_2 = {0 : "Wi",
+ # 1 : "other",
+ # 2 : "other",
+ # 3 : "other",
+ # 4 : "other",
+ # 5 : "other",
+ # 6 : "other",
+ # 7 : "other",
+ # 8 : "other",
+ # 9 : "other",
+ # 10 : "other",
+ # 11: "other",
+ # 12: "other",
+ # 13: "other",
+ # 14: "other"}
+
+ mapping_operations_2 = {0 : "Wi",
+ 1 : "R",
+ 2 : "RWi",
+ 3 : "F",
+ 4 : "FWi",
+ 5 : "FR",
+ 6 : "FRWi",
+ 7 : "Wa",
+ 8 : "WaWi",
+ 9 : "WR",
+ 10 : "WaRWi",
+ 11: "WaF",
+ 12: "WaFWi",
+ 13: "WaFR",
+ 14: "WaFRWi"}
+
+
+ # In obligation_flow everyone in the replaced_by df renovates
+ if call_from_obligation:
+ renovation_rate.values.fill(1)
+
+ # Calculate the flows for each possible renovation choice
+ renovation_flow = stock * renovation_rate
+
+ market_flow = market_share.copy()
+ for choice in market_share.columns:
+ market_flow[choice] = renovation_flow.values * market_share[choice].values
+ market_flow = market_flow.fillna(0)
+
+ # Replace columns names with four rows by columns names Choices_0, Choices_1 etc.
+ market_flow_tmp = pd.DataFrame()
+ flow_by_operation = pd.Series()
+ certificate_after_tmp = pd.DataFrame()
+ i = 0
+ for col in market_flow.columns:
+ res = ' '.join(str(val) for val in col)
+ flow_by_operation[res] = market_flow[col].sum()
+ flow_by_operation.rename(mapping_operations, inplace=True)
+ market_flow_tmp["Flow_Choice_{f}".format(f=i)] = market_flow[col]
+ certificate_after_tmp["Certif_after_Choice_{f}".format(f=i)] = certificate_after[col]
+ i += 1
+
+ # Merge in a df : the flows for each possible renovation choice, the certificates after for each possible renovation choice, and certificate_before
+ merged_df = market_flow_tmp.merge(certificate_after_tmp, left_index=True, right_index=True, how='inner')
+ certificate_before_heater = certificate_before_heater.rename('Certificate_before_heater')
+ certificate_before = certificate_before.rename('Certificate_before')
+ merged_df = merged_df.merge(certificate_before_heater, left_index=True, right_index=True, how='inner')
+ merged_df = merged_df.merge(certificate_before, left_index=True, right_index=True, how='inner')
+
+ # Calculate the renovation flow for each possible couple of certificates
+ flow_by_certificate_couples = {}
+ flow_by_certificate_couples_ampleur = {}
+ certificate_diffs=[]
+ certificate_diffs_results = pd.DataFrame()
+
+ ########## Adding df renovations ###################################################################
+
+ # dictionary, `mapping_deciles`, which groups income deciles (D1–D10) into broader quintile categories (Q1–Q5).
+ mapping_deciles = {"D1" : "Q1",
+ "D2" : "Q1",
+ "D3" : "Q2",
+ "D4" : "Q2",
+ "D5": "Q3",
+ "D6": "Q3",
+ "D7": "Q4",
+ "D8": "Q4",
+ "D9": "Q5",
+ "D10": "Q5"}
+
+ # Iteration over the columns numbers of a DataFrame called `market_flow`(each insulation combination)
+ for index, value in enumerate(market_flow.columns):
+ category_after_col = f'Certif_after_Choice_{index}'
+ flow_choice_col = f'Flow_Choice_{index}'
+ flow_choice_diff = f'Flow_Choice_diff{index}'
+
+ merged_df_reset = merged_df.reset_index(level=['Heater replacement', 'Housing type', 'Occupancy status', 'Income owner'])
+
+ # mapping deciles with quintiles, removing deciles columns
+ merged_df_reset = merged_df_reset.rename(columns={'Income owner': 'Income_owner_D'})
+ # merged_df_reset['Income owner'] = merged_df_reset['Income_owner_D'].map(mapping_deciles)
+ # merged_df_reset = merged_df_reset.drop('Income_owner_D', axis=1)
+
+ mask_decile = merged_df_reset['Income_owner_D'].str.startswith("D")
+ merged_df_reset['Income owner'] = merged_df_reset['Income_owner_D'] # copie par défaut
+ merged_df_reset.loc[mask_decile, 'Income owner'] = merged_df_reset.loc[mask_decile, 'Income_owner_D'].map(mapping_deciles)
+
+ # creating a description of all renovations with characteristics
+ merged_df_reset[flow_choice_diff] = merged_df_reset['Occupancy status'] + "_" + merged_df_reset['Housing type'] + "_" + merged_df_reset['Heater replacement'].astype(str) + "_" + merged_df_reset['Certificate_before_heater'] + "_" + merged_df_reset['Certificate_before'] + "_" + merged_df_reset[category_after_col] + "_" + merged_df_reset['Income owner']
+
+
+ certificate_diffs.append(merged_df_reset.groupby(flow_choice_diff)[flow_choice_col].sum())
+
+ for category_before, category_after, flow_choice in zip(merged_df['Certificate_before_heater'], merged_df[category_after_col], merged_df[flow_choice_col]):
+ category_change = (category_before, category_after)
+ flow_by_certificate_couples[category_change] = flow_by_certificate_couples.get(category_change, 0) + flow_choice
+
+ # renovations by option (0 to 14) and characteristics (socio-economic, certificate before heating, after heating, after insulation)
+ certificate_diffs_results = pd.concat(certificate_diffs, axis=1)
+ certificate_diffs_results = certificate_diffs_results.reset_index()
+
+ def transform_certificate_diffs(certificate_diffs_results, mapping_operations_2):
+ """
+ Transform certificate differences results into long format with operation details.
+
+ Args:
+ certificate_diffs_results (pd.DataFrame): Input DataFrame with certificate differences
+ mapping_operations_2 (dict): Mapping dictionary for operations
+
+ Returns:
+ pd.DataFrame: Transformed DataFrame with operation details
+ """
+ try:
+ transformed_df = (
+ pd.wide_to_long(
+ certificate_diffs_results,
+ stubnames='Flow_Choice_',
+ i='index',
+ j='operation'
+ )
+ .reset_index(level=['operation']))
+ transformed_df['operation_map'] = transformed_df['operation'].map(mapping_operations_2)
+ transformed_df = transformed_df.reset_index()
+ transformed_df['operation_details'] = transformed_df['index'] + "_" + transformed_df['operation_map']
+ transformed_df = transformed_df.drop(['operation', 'index','operation_map'], axis=1)
+ transformed_df = transformed_df.set_index(['operation_details'])
+
+ return transformed_df
+
+ except KeyError as e:
+ raise KeyError(f"Missing mapping for operation: {e}")
+ except Exception as e:
+ raise RuntimeError(f"Error transforming certificate diffs: {e}")
+
+ # Reshape certificate_diffs_results to long format and add operation details
+ try:
+ renovation_details_long = transform_certificate_diffs(certificate_diffs_results, mapping_operations_2)
+ except Exception as e:
+ raise Exception(f"Failed to transform certificate differences: {e}")
+
+ ########## End adding df renovations ###################################################################
+
+
+ # Check that the sum of the flows for each possible pair of certificates equals the sum of renovation_flow.
+ sum_check = 0
+ for key in flow_by_certificate_couples:
+ sum_check += flow_by_certificate_couples[key]
+ assert round(sum_check, 0) == round(sum(renovation_flow), 0), 'Flow between certificate pairs problem'
+
+ # Calculate the number of high-performance renovations
+ condition_reno_performante = "(category_before in ['C', 'D', 'E', 'F', 'G'] and category_after in ['A', 'B']) or (category_before in ['F', 'G'] and category_after=='C' )"
+ sum_performance_insulation = 0
+
+ for category_change, sum_value in flow_by_certificate_couples.items():
+ category_before, category_after = category_change
+ if eval(condition_reno_performante):
+ sum_performance_insulation += sum_value
+
+ # Put results in a Series instead of a Dict
+ flow_by_certificate_couples = pd.Series(flow_by_certificate_couples)
+ flow_by_certificate_couples = flow_by_certificate_couples.sort_index(level=[0, 1])
+
+ # Put the results in the buildings object's attributes.
+ if not call_from_obligation:
+ self.flow_by_certificate_couples_insulation = flow_by_certificate_couples
+ self.sum_performance_insulation = sum_performance_insulation
+ else:
+ self.flow_by_certificate_couples_obligation = flow_by_certificate_couples
+ self.sum_performance_insulation_obligation = sum_performance_insulation
+
+ for i in [2,4,5,6,8,9,10,11,12,13,14]:
+ category_after_col = f'Certif_after_Choice_{i}'
+ flow_choice_col = f'Flow_Choice_{i}'
+
+ for category_before, category_after, flow_choice in zip(merged_df['Certificate_before_heater'], merged_df[category_after_col], merged_df[flow_choice_col]):
+ category_change = (category_before, category_after)
+ flow_by_certificate_couples_ampleur[category_change] = flow_by_certificate_couples_ampleur.get(category_change, 0) + flow_choice
+
+ # Check that the sum of the flows for each possible pair of certificates equals the sum of renovation_flow.
+ sum_check = 0
+ for key in flow_by_certificate_couples_ampleur:
+ sum_check += flow_by_certificate_couples_ampleur[key]
+ # assert round(sum_check, 0) == round(sum(renovation_flow), 0), 'Flow between certificate pairs problem'
+
+ # Calculate the number of high-performance renovations
+ condition_reno_ampleur = "(category_diff >= 2)"
+ sum_performance_insulation_ampleur = 0
+
+ category_mapping= {"A" : 7, "B" : 6, "C" : 5, "D" : 4, "E" : 3, "F" : 2, "G" : 1}
+
+ for category_change, sum_value in flow_by_certificate_couples_ampleur.items():
+ category_before, category_after = category_change
+ category_before_number = category_mapping[category_before]
+ category_after_number = category_mapping[category_after]
+ category_diff = category_after_number - category_before_number
+ if eval(condition_reno_ampleur):
+ sum_performance_insulation_ampleur += sum_value
+
+ # Put results in a Series instead of a Dict
+ flow_by_certificate_couples_ampleur = pd.Series(flow_by_certificate_couples_ampleur)
+ flow_by_certificate_couples_ampleur = flow_by_certificate_couples_ampleur.sort_index(level=[0, 1])
+
+ # Put the results in the buildings object's attributes.
+ if not call_from_obligation:
+ self.flow_by_certificate_couples_ampleur_insulation = flow_by_certificate_couples_ampleur
+ self.sum_performance_insulation_ampleur = sum_performance_insulation_ampleur
+ self.flow_by_operation_insulation = flow_by_operation
+ self.renovation_details_long = renovation_details_long
+ else:
+ self.flow_by_certificate_couples_ampleur_obligation = flow_by_certificate_couples_ampleur
+ self.sum_performance_insulation_ampleur_obligation = sum_performance_insulation_ampleur
+ self.renovation_details_long_obligation = renovation_details_long
+
+ return None
+
def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetime_insulation,
policies_insulation=None, financing_cost=None,
calib_renovation=None, min_performance=None,
exogenous_social=None, prices_before=None, supply=None, carbon_value=None,
carbon_content=None, calculate_condition=True, bill_rebate=0,
- credit_constraint=True):
+ credit_constraint=True, call_from_obligation=False, pef_elec=None):
"""Calculate insulation retrofit in the dwelling stock.
1. Intensive margin
@@ -4005,17 +4369,18 @@ def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetim
if not stock.empty:
# select index that can undertake insulation replacement
- _, _, certificate_before_heater = self.consumption_heating_store(index, level_heater='Heating system')
+ _, consumption_3uses_before_heater, certificate_before_heater = self.consumption_heating_store(index, level_heater='Heating system', pef_elec=pef_elec)
# before include the change of heating system
- consumption_before, consumption_3uses_before, certificate_before = self.consumption_heating_store(index, level_heater='Heating system final')
+ consumption_before, consumption_3uses_before, certificate_before = self.consumption_heating_store(index, level_heater='Heating system final', pef_elec=pef_elec)
surface = reindex_mi(self._surface, index)
# calculation of energy_saved_3uses after heating system final
consumption_after, consumption_3uses, certificate_after = self.prepare_consumption(self._choice_insulation,
index=index,
- level_heater='Heating system final')
- energy_saved_3uses = ((consumption_3uses_before - consumption_3uses.T) / consumption_3uses_before).T
+ level_heater='Heating system final',
+ pef_elec=pef_elec)
+ energy_saved_3uses = ((consumption_3uses_before_heater - consumption_3uses.T) / consumption_3uses_before_heater).T
energy_saved_3uses.dropna(inplace=True)
consumption_saved = (consumption_before - consumption_after.T).T
@@ -4076,7 +4441,8 @@ def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetim
credit_constraint=credit_constraint,
performance_gap=round(
self._heating_intensity_avg,
- 1))
+ 1),
+ pef_elec=pef_elec)
if exogenous_social is not None:
index = renovation_rate[
@@ -4181,7 +4547,8 @@ def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetim
heating_intensity = self.to_heating_intensity(consumption_before.index, prices,
consumption=consumption_before,
level_heater='Heating system final',
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
consumption_before *= heating_intensity
@@ -4192,7 +4559,8 @@ def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetim
heating_intensity_after = self.to_heating_intensity(consumption_after.index, prices_before,
consumption=consumption_after,
level_heater='Heating system final',
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
consumption_saved_actual = (consumption_before - (consumption_after * heating_intensity_after).T).T
consumption_saved_no_rebound = (consumption_before - consumption_after.T * heating_intensity).T
@@ -4202,7 +4570,8 @@ def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetim
consumption_saved_actual, consumption_saved_no_rebound,
amount_debt, amount_saving, discount, subsidies_loan, eligible,
hidden_cost)
-
+
+ self.certificate_flow_insulation(stock, renovation_rate, market_share, certificate_before_heater, certificate_before, certificate_after, call_from_obligation)
return renovation_rate, market_share
else:
renovation_rate = Series(0, index=stock_ini.index)
@@ -4211,11 +4580,62 @@ def insulation_replacement(self, stock_ini, prices, cost_insulation_raw, lifetim
return renovation_rate, market_share
+ def certificate_flow_heater(self, pef_elec=None):
+ """ Calculates the flow for each possible pair of certificates for those who change their heater but do not renovate.
+
+ Parameters
+ ----------
+ pef_elec : float
+ Primary energy factor for electricity
+
+
+ Returns
+ -------
+ None
+ """
+
+ # Creation of 3 Series : flow of heater replacement, certificates before and after heater replacement
+ flow = self._only_heater
+ index = flow.index
+ _, _, certificate_before_heater = self.consumption_heating_store(index, level_heater='Heating system', pef_elec=pef_elec)
+ _, _, certificate_after_heater = self.consumption_heating_store(index, level_heater='Heating system final', pef_elec=pef_elec)
+
+ # Merging flow of heater replacement only with certificates before and after heater replacement
+ flow = flow.rename('Flow')
+ certificate_before_heater = certificate_before_heater.rename("certificate_before_heater")
+ certificate_after_heater = certificate_after_heater.rename("certificate_after_heater")
+
+ merged_df = pd.DataFrame(flow).merge(certificate_before_heater, left_index=True, right_index=True, how='inner')
+ merged_df = merged_df.merge(certificate_after_heater, left_index=True, right_index=True, how='inner')
+
+ merged_df_heater = merged_df
+
+ # Flow grouped by certificates couples
+ flow_by_certificate_couples = merged_df.set_index(['certificate_before_heater', 'certificate_after_heater'])
+ flow_by_certificate_couples = flow_by_certificate_couples.groupby(['certificate_before_heater', 'certificate_after_heater']).sum()
+ flow_by_certificate_couples = pd.Series(flow_by_certificate_couples['Flow'], index=flow_by_certificate_couples.index)
+
+ # Calculate the number of high-performance changes
+ condition_reno_performante = "(category_before in ['C', 'D', 'E', 'F', 'G'] and category_after in ['A', 'B']) or (category_before in ['F', 'G'] and category_after=='C' )"
+ sum_performance_changes = 0
+ dict_series = dict(zip(flow_by_certificate_couples.index.map(lambda x: (x[0], x[1])), flow_by_certificate_couples))
+
+ for category_change, sum_value in dict_series.items():
+ category_before, category_after = category_change
+ if eval(condition_reno_performante):
+ sum_performance_changes += sum_value
+
+ self.flow_by_certificate_couples_heater = flow_by_certificate_couples
+ self.sum_performance_changes_heater = sum_performance_changes
+ self.merged_df_heater = merged_df_heater
+
+ return None
+
def flow_retrofit(self, prices, cost_heater, cost_insulation, lifetime_insulation,
policies_heater=None, policies_insulation=None, calib_heater=None, district_heating=None,
financing_cost=None, calib_renovation=None,
step=1, exogenous_social=None, premature_replacement=None, prices_before=None, supply=None,
- carbon_value_kwh=None, carbon_value=None, bill_rebate=0, carbon_content=None):
+ carbon_value_kwh=None, carbon_value=None, bill_rebate=0, carbon_content=None, pef_elec=None):
"""Compute heater replacement and insulation retrofit.
@@ -4250,7 +4670,7 @@ def flow_retrofit(self, prices, cost_heater, cost_insulation, lifetime_insulatio
# calculate average heating intensity
temp = concat((self.stock_mobile,
- self.to_heating_intensity(self.stock_mobile.index, prices, level_heater='Heating system')),
+ self.to_heating_intensity(self.stock_mobile.index, prices, level_heater='Heating system', pef_elec=pef_elec)),
axis=1, keys=['Stock', 'Heating intensity'])
self._heating_intensity_avg = temp['Stock'].mul(temp['Heating intensity']).sum() / temp['Stock'].sum()
@@ -4260,7 +4680,8 @@ def flow_retrofit(self, prices, cost_heater, cost_insulation, lifetime_insulatio
calib_heater=calib_heater, step=1, financing_cost=financing_cost,
district_heating=district_heating, premature_replacement=premature_replacement,
prices_before=prices_before, bill_rebate=bill_rebate,
- carbon_content=carbon_content, carbon_value=carbon_value)
+ carbon_content=carbon_content, carbon_value=carbon_value,
+ pef_elec=pef_elec)
if supply is not None:
if supply['heater']:
@@ -4287,13 +4708,14 @@ def func(x, index):
demand_ini = Series(root[index.shape[0]:], index=index)
self.cost_curve_heater = (alpha, demand_ini)
- def supply_demand_heater_equilibrium(_prices_heater, _prices_index):
+ def supply_demand_heater_equilibrium(_prices_heater, _prices_index, pef_elec):
_prices_heater = pd.Series(_prices_heater, index=_prices_index)
_stock = self.heater_replacement(stock_mobile, prices, _prices_heater, policies_heater,
financing_cost=financing_cost,
district_heating=district_heating,
premature_replacement=premature_replacement,
+ pef_elec=pef_elec
)
demand = stock.xs(True, level='Heater replacement').groupby('Heating system final').sum()
@@ -4307,7 +4729,7 @@ def supply_demand_heater_equilibrium(_prices_heater, _prices_index):
stock = self.heater_replacement(stock_mobile, prices, prices_heater, policies_heater,
calib_heater=calib_heater, step=1, financing_cost=financing_cost,
district_heating=district_heating, premature_replacement=premature_replacement,
- prices_before=prices_before)
+ prices_before=prices_before, pef_elec=pef_elec)
assert ~stock.index.duplicated().any(), 'Duplicated index after heater replacement'
self.logger.info('Number of agents that can insulate: {:,.0f}'.format(stock.shape[0]))
@@ -4326,7 +4748,8 @@ def supply_demand_heater_equilibrium(_prices_heater, _prices_index):
supply=supply_insulation,
carbon_value=carbon_value_kwh,
carbon_content=carbon_content,
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
cost_curve_insulation = False
if cost_curve_insulation:
@@ -4347,7 +4770,8 @@ def supply_demand_insulation_equilibrium(_prices_insulation):
policies_insulation=policies_insulation,
financing_cost=financing_cost,
exogenous_social=exogenous_social,
- calculate_condition=True)
+ calculate_condition=True,
+ pef_elec=pef_elec)
_demand = (stock * _renovation_rate * _market_share.T).T.sum()
_demand = pd.Series({i: _demand.xs(True, level=i).sum() / 10 ** 3 for i in _demand.index.names})
@@ -4364,7 +4788,8 @@ def supply_demand_insulation_equilibrium(_prices_insulation):
exogenous_social=exogenous_social,
prices_before=prices_before,
supply=supply['insulation'],
- carbon_value=carbon_value)
+ carbon_value=carbon_value,
+ pef_elec=pef_elec)
self.logger.info('Formatting and storing replacement')
renovation_rate = renovation_rate.reindex(stock.index).fillna(0)
@@ -4375,7 +4800,7 @@ def supply_demand_insulation_equilibrium(_prices_insulation):
# approximation
stock = self.heater_replacement(stock_mobile, prices, cost_heater, policies_heater,
calib_heater=calib_heater, step=step, financing_cost=financing_cost,
- district_heating=district_heating, supply=supply)
+ district_heating=district_heating, supply=supply, pef_elec=pef_elec)
flow_insulation = flow_insulation.where(flow_insulation < stock, stock)
flow_only_heater = stock - flow_insulation
@@ -4399,6 +4824,7 @@ def supply_demand_insulation_equilibrium(_prices_insulation):
self.logger.debug('Store information retrofit')
self._replaced_by = replaced_by.copy()
self._only_heater = only_heater.copy()
+ self.certificate_flow_heater(pef_elec=pef_elec)
# removing heater replacement level
replaced_by = replaced_by.groupby(
@@ -4445,7 +4871,7 @@ def supply_demand_insulation_equilibrium(_prices_insulation):
return flow_retrofit
- def flow_obligation(self, policies_insulation, prices, cost_insulation, financing_cost=True):
+ def flow_obligation(self, policies_insulation, prices, cost_insulation, financing_cost=True, pef_elec=None):
"""Account for flow obligation if defined in policies_insulation.
Parameters
@@ -4535,7 +4961,8 @@ def flow_obligation(self, policies_insulation, prices, cost_insulation, financin
policies_insulation=policies_insulation,
financing_cost=financing_cost,
min_performance=obligation.min_performance,
- credit_constraint=False)
+ credit_constraint=False, call_from_obligation=True,
+ pef_elec=pef_elec)
if obligation.intensive == 'market_share':
# market_share endogenously calculated by insulation_replacement
@@ -4568,7 +4995,7 @@ def flow_obligation(self, policies_insulation, prices, cost_insulation, financin
return flows_obligation
def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
- lifetime_insulation=30, social_discount_rate=0.032, bill_rebate=0):
+ lifetime_insulation=30, social_discount_rate=0.032, bill_rebate=0, pef_elec=None):
"""Parse output.
Renovation : envelope
@@ -4603,6 +5030,9 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
stock = self.simplified_stock()
output = dict()
+ df_renovations = pd.DataFrame(columns=["Occupancy status", "Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation","steps_heater","steps_insulation","steps_insulation_with_heater", "Operation type","Year","Value"])
+ df_renovations_obligation = pd.DataFrame(columns=["Occupancy status", "Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation","steps_heater","steps_insulation","steps_insulation_with_heater", "Operation type","Year","Value"])
+ merged_df_heater = pd.DataFrame(columns=["Occupancy status","Housing type","Heating system final","Heating system","Flow","certificate_before_heater","certificate_after_heater","steps_heater","Year"])
output['Stock (Million)'] = stock.sum() / 10 ** 6
output['Stock existing (Million)'] = self.stock.xs(True, level='Existing').sum() / 10 ** 6
stock_new = 0
@@ -4630,31 +5060,32 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
output.update(temp.T)
output['Consumption standard (TWh)'] = self.consumption_agg(prices=prices, freq='year', climate=climate,
- standard=True, agg='all')
+ standard=True, agg='all', pef_elec=pef_elec)
output['Consumption standard (kWh/m2)'] = (output['Consumption standard (TWh)'] * 10 ** 9) / (
output['Surface (Million m2)'] * 10 ** 6)
consumption_energy = self.consumption_agg(prices=prices, freq='year', climate=None, standard=False,
- agg='energy', bill_rebate=bill_rebate)
+ agg='energy', bill_rebate=bill_rebate, pef_elec=pef_elec)
output['Consumption (TWh)'] = consumption_energy.sum()
self.store_over_years[self.year].update({'Consumption (TWh)': output['Consumption (TWh)']})
output['Consumption (kWh/m2)'] = (output['Consumption (TWh)'] * 10 ** 9) / (
output['Surface (Million m2)'] * 10 ** 6)
output['Consumption existing (TWh)'] = self.consumption_agg(prices=prices, freq='year', existing=True,
- agg='all', bill_rebate=bill_rebate)
+ agg='all', bill_rebate=bill_rebate, pef_elec=pef_elec)
output['Consumption new (TWh)'] = output['Consumption (TWh)'] - output['Consumption existing (TWh)']
output['Consumption existing (kWh/m2)'] = (output['Consumption existing (TWh)'] * 10 ** 9) / (
output['Surface existing (Million m2)'] * 10 ** 6)
- output['Consumption PE (TWh)'] = thermal.final2primary(consumption_energy, Series(consumption_energy.index, index=consumption_energy.index)).sum()
+ output['Consumption PE (TWh)'] = thermal.final2primary(consumption_energy, Series(consumption_energy.index, index=consumption_energy.index), pef_elec=pef_elec).sum()
if surface_new > 0:
output['Consumption new (kWh/m2)'] = (output['Consumption new (TWh)'] * 10 ** 9) / (
output['Surface new (Million m2)'] * 10 ** 6)
heating_intensity, budget_share = self.to_heating_intensity(self.stock.index, prices,
- full_output=True, bill_rebate=bill_rebate)
+ full_output=True, bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
condition_poverty = self.stock.index.get_level_values('Income tenant').isin(['D1', 'D2', 'D3', 'C1', 'C2']) & (
budget_share >= 0.08)
@@ -4668,7 +5099,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
output.update(temp.T)
temp = self.consumption_agg(prices=prices, freq='year', climate=None, standard=False,
- agg='heater', bill_rebate=bill_rebate).dropna()
+ agg='heater', bill_rebate=bill_rebate, pef_elec=pef_elec).dropna()
consumption_hp = sum([temp.loc[i] for i in self._resources_data['index']['Heat pumps'] if i in temp.index])
temp.index = temp.index.map(lambda x: 'Consumption {} (TWh)'.format(x))
output.update(temp.T)
@@ -4680,7 +5111,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
consumption_energy_climate = None
if climate is not None:
consumption_energy_climate = self.consumption_agg(prices=prices, freq='year', climate=climate,
- standard=False, agg='energy', bill_rebate=bill_rebate)
+ standard=False, agg='energy', bill_rebate=bill_rebate, pef_elec=pef_elec)
output['Consumption climate (TWh)'] = consumption_energy_climate.sum()
temp = consumption_energy_climate.copy()
temp.index = temp.index.map(lambda x: 'Consumption {} climate (TWh)'.format(x))
@@ -4689,7 +5120,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
if False:
consumption_hourly = self.consumption_agg(prices=prices, freq='hour', standard=False, climate=2006,
- efficiency_hour=True, hourly_profile='power')
+ efficiency_hour=True, hourly_profile='power', pef_elec=pef_elec)
# format_x datetime hourly
temp = consumption_hourly.loc['Electricity']
@@ -4704,7 +5135,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
save=os.path.join(self.path, 'consumption_day.png'),
format_y=lambda y, _: '{:.0f}'.format(y / 1e6), integer=False, legend=False)
- consumption = self.consumption_actual(prices) * self.stock
+ consumption = self.consumption_actual(prices, pef_elec=pef_elec) * self.stock
consumption_calib = consumption * self.coefficient_global
# correct that do consider secondary heating system
temp = consumption_calib.groupby('Existing').sum()
@@ -4716,7 +5147,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
temp.index = temp.index.map(lambda x: 'Consumption {} (TWh)'.format(x))
output.update(temp.T / 10 ** 9)
- temp = self.consumption_agg(agg='heater', standard=True, freq='year')
+ temp = self.consumption_agg(agg='heater', standard=True, freq='year', pef_elec=pef_elec)
temp.index = temp.index.map(lambda x: 'Consumption standard {} (TWh)'.format(x))
output.update(temp.T)
@@ -4769,10 +5200,106 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
temp.index = temp.index.map(lambda x: 'Stock {} (Million)'.format(x))
output.update(temp.T / 10 ** 6)
+ ###############################################################################################################
+ ####################### Adding consumption standard real ######################################################
+ ###############################################################################################################
+
+ consumption_std = self.consumption_heating(freq="year", climate=None, pef_elec=pef_elec)
+ consumption_std_2 = reindex_mi(consumption_std, self.stock.index) * self.surface * self.stock
+ consumption_std_3 = consumption_std_2.reset_index()
+ consumption_std_3.rename(columns={0: "consumption_standard"})
+
+ consumption_real = self.consumption_heating(freq="year", climate=climate, temp_sink=self._temp_sink, pef_elec=pef_elec)
+ consumption_real_2 = reindex_mi(consumption_real, self.stock.index) * self.surface
+ consumption_real_3 = self.consumption_actual(prices, consumption=consumption_real_2, bill_rebate=bill_rebate, pef_elec=pef_elec) * self.stock
+ consumption_real_4 = consumption_real_3.reset_index()
+ consumption_real_4.rename(columns={0: "consumption_real"}, inplace=True)
+
+ stock_output = self.stock.reset_index()
+
+ certificate_output = self.certificate.reset_index()
+ certificate_output.rename(columns={0: "epc"}, inplace=True)
+
+ # 1. Fusionner les DataFrames sur les colonnes communes (par exemple, tous les niveaux descriptifs)
+ df = stock_output.merge(
+ certificate_output[["epc"]], left_index=True, right_index=True, how="left"
+ )
+ df = df.merge(
+ consumption_std_3.rename(columns={0: "consumption_standard"}),
+ left_index=True, right_index=True, how="left"
+ )
+ df = df.merge(
+ consumption_real_4.rename(columns={0: "consumption_real"}),
+ left_index=True, right_index=True, how="left"
+ )
+
+ if 0 in df.columns:
+ df.rename(columns={0: "Stock buildings"}, inplace=True)
+
+ # 2. Garder uniquement les colonnes d'intérêt
+ df_final = df[["epc", "Heating system", "Stock buildings", "consumption_standard", "consumption_real"]]
+
+ # 3. Supprimer les lignes sans valeurs de consommation si besoin
+ df_final = df_final.dropna(subset=["consumption_standard", "consumption_real"])
+
+ df_final["consumption_real"] *= self.coefficient_global
+
+ coefficient_heater_2 = coefficient_heater.reset_index()
+ coefficient_heater_2 = coefficient_heater_2[["Heating system",0]]
+ coefficient_heater_2 = coefficient_heater_2.groupby("Heating system").mean()
+
+ df_final = df_final.merge(
+ coefficient_heater_2.reset_index(),
+ left_on="Heating system",
+ right_on="Heating system",
+ how="left")
+
+ df_final.rename(columns={0: "coefficient_heater"}, inplace = True)
+
+ df_final = df_final[["epc", "Heating system", "Stock buildings", "consumption_standard", "consumption_real", "coefficient_heater"]]
+
+ mapping_system_energy = {"Electricity-Heat pump water" : "Electricity",
+ "Heating-District heating" : "District heating",
+ "Natural gas-Performance boiler" : "Gas" ,
+ "Electricity-Performance boiler" : "Electricity",
+ "Wood fuel-Performance boiler": "Wood",
+ "Electricity-Heat pump": "Electricity",
+ "Oil fuel-Performance boiler": "Oil",
+ "Oil fuel-Standard boiler": "Oil",
+ "Oil fuel-Collective boiler": "Oil",
+ "Natural gas-Standard boiler": "Gas",
+ "Natural gas-Collective boiler": "Gas",
+ "Wood fuel-Standard boiler": "Wood",
+ "Electricity-Heat pump air": "Electricity"}
+
+ # Ajout de la colonne "Energy" à df_final en utilisant mapping_system_energy
+ df_final["Energy"] = df_final["Heating system"].map(mapping_system_energy)
+
+ df_final.drop('coefficient_heater', axis=1, inplace=True)
+
+ df_final["year"] = self.year
+
+ df_final_grouped = df_final.groupby(["epc", "Heating system", "Energy", "year"]).sum(numeric_only=True)
+
+ df_final_grouped = df_final_grouped.loc[(df_final_grouped!=0).any(axis=1)]
+
+ df_final_grouped["consumption_standard"] = df_final_grouped["consumption_standard"] / 10 ** 9
+ df_final_grouped["consumption_real"] = df_final_grouped["consumption_real"] / 10 ** 9
+
+ df_final_grouped.rename(columns={"consumption_standard": "Consumption standard (TWh)","consumption_real": "Consumption real (TWh)" }, inplace = True)
+
+ df_final_grouped = df_final_grouped.reset_index()
+
+ ###############################################################################################################
+ ####################### End adding consumption standard_real ###################################################
+ ###############################################################################################################
+
+
output['Stock efficient (Million)'] = output.get('Stock A (Million)', 0) + output.get('Stock B (Million)', 0)
output['Stock low-efficient (Million)'] = output.get('Stock G (Million)', 0) + output.get('Stock F (Million)', 0)
output['Stock to renovate (Million)'] = output['Stock low-efficient (Million)'] + output.get('Stock E (Million)', 0) + \
output.get('Stock D (Million)', 0)
+
temp = self.stock.groupby('Heating system').sum()
@@ -4794,6 +5321,10 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
temp.index = temp.index.map(lambda x: 'Stock {} (Million)'.format(x))
output.update(temp.T / 10 ** 6)
+ temp = self.stock.groupby(['Heating system', 'Housing type']).sum()
+ temp.index = ['Stock {} '.format(y) + '{} (Million)'.format(x) for (x,y) in temp.index]
+ output.update(temp.T / 10 ** 6)
+
# energy expenditures : do we really need it ?
prices_reindex = prices.reindex(self.energy).set_axis(self.stock.index, axis=0)
coefficient_heater = reindex_mi(self.coefficient_heater, consumption_calib.index)
@@ -4829,7 +5360,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
output['Taxes expenditure (Billion euro)'] = taxes_expenditures.sum() / step
output['Carbon value (Billion euro)'] = (consumption_energy * carbon_value_kwh).sum()
- output['Health cost (Billion euro)'] = self.health_cost(inputs['health_cost_dpe'], inputs['health_cost_income'], prices)
+ output['Health cost (Billion euro)'] = self.health_cost(inputs['health_cost_dpe'], inputs['health_cost_income'], prices, pef_elec=pef_elec)
output['Health expenditure (Billion euro)'] = 0 # temp['Health expenditure (Billion euro)']
self.store_over_years[self.year].update({'Health cost (Billion euro)': output['Health cost (Billion euro)']})
@@ -4856,7 +5387,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
cost = reindex_mi(cost, replacement.index)
consumption_before, certificate_before, _ = self.consumption_heating(index=replacement.index, method='3uses',
- full_output=True)
+ full_output=True, pef_elec=pef_elec)
s = concat([Series(index=replacement.index, dtype=float)] * len(replacement.columns), axis=1).set_axis(replacement.columns, axis=1)
# choice_insulation = choice_insulation.drop(no_insulation) # only for
@@ -4874,7 +5405,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
{'Housing type': 'string', 'Wall': 'float', 'Floor': 'float', 'Roof': 'float', 'Windows': 'float',
'Heating system': 'string'})
index = MultiIndex.from_frame(temp)
- consumption_after, certificate_after, _ = self.consumption_heating(index=index, method='3uses', full_output=True)
+ consumption_after, certificate_after, _ = self.consumption_heating(index=index, method='3uses', full_output=True, pef_elec=pef_elec)
certificate_after = reindex_mi(certificate_after, index).droplevel(['Wall', 'Floor', 'Roof', 'Windows']).unstack(
['{} bool'.format(i) for i in ['Wall', 'Floor', 'Roof', 'Windows']])
@@ -4932,7 +5463,7 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
# consumption saving
if self.consumption_before_retrofit is not None:
consumption_before_retrofit = self.consumption_before_retrofit
- consumption_after_retrofit = self.store_consumption(prices, emission, bill_rebate=bill_rebate)
+ consumption_after_retrofit = self.store_consumption(prices, emission, bill_rebate=bill_rebate, pef_elec=pef_elec)
temp = {'{} saving (TWh/year)'.format(k.split(' (TWh)')[0]): consumption_before_retrofit[k] -
consumption_after_retrofit[k]
for k in consumption_before_retrofit.keys() if 'TWh' in k}
@@ -4978,7 +5509,8 @@ def parse_output_run(self, prices, inputs, climate=None, step=1, taxes=None,
{'Emission saving insulation (MtCO2/year)': self.to_emission(temp, emission).sum() / 10 ** 12})
consumption = self.consumption_heating_store(self._renovation_store['consumption_saved_households'].index,
- full_output=False, level_heater='Heating system final')
+ full_output=False, level_heater='Heating system final',
+ pef_elec=pef_elec)
consumption = reindex_mi(consumption, self._renovation_store['consumption_saved_households'].index)
consumption *= reindex_mi(self._surface, consumption.index)
@@ -5110,7 +5642,7 @@ def condition_decarbonizing(x):
temp.index = temp.index.map(lambda x: 'Rate Single-family - Owner-occupied {} (%)'.format(x))
output.update(temp.T)
- _, _, certificate = self.consumption_heating_store(self._stock_ref.index)
+ _, _, certificate = self.consumption_heating_store(self._stock_ref.index, pef_elec=pef_elec)
temp = concat((self._replaced_by, reindex_mi(certificate.rename('Performance'), self._replaced_by.index)), axis=1)
temp = temp.set_index('Performance', append=True).set_axis(self._replaced_by.columns, axis=1)
s = concat((self._stock_ref, reindex_mi(certificate.rename('Performance'), self._stock_ref.index)), axis=1)
@@ -5422,13 +5954,13 @@ def condition_decarbonizing(x):
annuities_cumulated = sum([self.expenditure_store[y]['annuities'] for y in years])
annuities_cumulated += annuities_year
- consumption_std = reindex_mi(self.consumption_heating(full_output=False), self.stock.index)
+ consumption_std = reindex_mi(self.consumption_heating(full_output=False, pef_elec=pef_elec), self.stock.index)
consumption_std *= reindex_mi(self._surface, self.stock.index)
energy_exp_std = self.energy_bill(prices, consumption_std, bill_rebate=0)
energy_exp_std *= self.stock
energy_exp_std = energy_exp_std.groupby(lvls).sum()
- consumption = self.consumption_actual(prices, bill_rebate=bill_rebate)
+ consumption = self.consumption_actual(prices, bill_rebate=bill_rebate, pef_elec=pef_elec)
energy_exp = self.energy_bill(prices, consumption, bill_rebate=bill_rebate)
energy_exp *= self.stock
energy_exp = energy_exp.groupby(lvls).sum()
@@ -5601,7 +6133,7 @@ def condition_decarbonizing(x):
self._balance_state_ini = output['Balance state (Billion euro)']
# subsidies - details: policies amount and number of beneficiaries
- subsidies_details_renovation, replacement_eligible_renovation, subsidies_average_renovation, cost_average_renovation = {}, {}, {}, {}
+ subsidies_details_renovation, replacement_eligible_renovation, replacement_eligible_renovation_income, subsidies_average_renovation, cost_average_renovation = {}, {}, {}, {}, {}
energy_saved_renovation = {}
for key, sub in self._renovation_store['subsidies_details_households'].items():
subsidies_details_renovation[key] = (
@@ -5616,6 +6148,7 @@ def condition_decarbonizing(x):
replacement_eligible = self._replaced_by.fillna(0).sum(axis=1) * eligible
replacement_eligible_renovation[key] = replacement_eligible.groupby('Housing type').sum()
+ replacement_eligible_renovation_income[key] = replacement_eligible.groupby('Income owner').sum()
if eligible.sum().sum() == 0:
subsidies_average_renovation[key] = 0
@@ -5630,18 +6163,35 @@ def condition_decarbonizing(x):
energy_saved = ((energy_saved * self._replaced_by.fillna(0)).T * eligible).T
energy_saved_renovation[key] = energy_saved.sum().sum() / replacement_eligible.sum()
+ # subsidies - details: policies amount and number of beneficiaries, by decile
+# if key in ['mpr_performance', 'mpr_multifamily', 'mpr_multifamily_updated', 'mpr_multifamily_deep']:
+ if key in ['mpr_performance']:
+ amount_tmp = subsidies_details_renovation[key]
+ subsidies_details_by_decile = amount_tmp.groupby(['Income owner']).sum()
+ amount_by_decile = subsidies_details_by_decile.T.sum()
+ amount_by_decile = amount_by_decile / 10 ** 6 / step
+ output.update({'{} '.format(key.capitalize().replace('_', ' ')) + ' {} (Million euro)'.format(i): amount_by_decile.loc[i] for i in amount_by_decile.index})
+
+ eligible = self._renovation_store['eligible'][key]
+ count_tmp = self._replaced_by.fillna(0).sum(axis=1) * eligible
+ count_by_decile = count_tmp.groupby(['Income owner']).sum()
+ count_by_decile = count_by_decile / 1e3 / step
+ output.update({'{} '.format(key.capitalize().replace('_', ' ')) + ' {} (Thousand households)'.format(i): count_by_decile.loc[i] for i in count_by_decile.index})
+
del self._renovation_store['subsidies_details_households']
gc.collect()
- subsidies, replacement_eligible, sub_count, cost_average, energy_average = None, None, None, None, None
+ subsidies, replacement_eligible, sub_count, sub_count_income, cost_average, energy_average = None, None, None, None, None, None
for gest, subsidies_details in {'heater': self._heater_store['subsidies_details'],
'insulation': subsidies_details_renovation}.items():
if gest == 'heater':
sub_count = DataFrame(self._heater_store['replacement_eligible'], dtype=float)
+ sub_count_income = DataFrame(self._heater_store['replacement_eligible_income'], dtype=float)
cost_average = Series(self._heater_store['cost_average'], dtype=float)
elif gest == 'insulation':
sub_count = DataFrame(replacement_eligible_renovation, dtype=float)
+ sub_count_income = DataFrame(replacement_eligible_renovation_income, dtype=float)
cost_average = Series(cost_average_renovation, dtype=float)
energy_average = Series(energy_saved_renovation, dtype=float)
@@ -5653,14 +6203,27 @@ def condition_decarbonizing(x):
use_subsidies = inputs['use_subsidies'].loc['{} {}'.format(i, gest)]
subsidies_details[i] *= use_subsidies
sub_count[i] *= use_subsidies
+ sub_count_income[i] *= use_subsidies
temp = sub_count[i].copy()
+ temp_income = sub_count_income[i].copy()
+
temp.index = temp.index.map(
lambda x: '{} {} {} (Thousand households)'.format(i.capitalize().replace('_', ' '), gest, x))
output.update(temp.T / 10 ** 3 / step)
+ temp_income.index = temp_income.index.map(
+ lambda x: '{} {} {} (Thousand households)'.format(i.capitalize().replace('_', ' '), gest, x))
+ output.update(temp_income.T / 10 ** 3 / step)
+
output.update({'{} {} (Thousand households)'.format(i.capitalize().replace('_', ' '), gest):
sub_count[i].sum() / 1e3 / step})
+
+ output['Average cost {} {} (euro)'.format(i.capitalize().replace('_', ' '), gest)] = cost_average.loc[i]
+
+ output.update({'{} {} (Thousand households)'.format(i.capitalize().replace('_', ' '), gest):
+ sub_count_income[i].sum() / 1e3 / step})
+
output['Average cost {} {} (euro)'.format(i.capitalize().replace('_', ' '), gest)] = cost_average.loc[i]
if gest == 'insulation' and i in energy_average.keys():
@@ -5711,6 +6274,13 @@ def condition_decarbonizing(x):
temp.index = temp.index.map(lambda x: 'Subsidies total {} - {} (Million euro)'.format(x[0], x[1]))
output.update(temp.T / 10 ** 6 / step)
+ temp = subsidies_total.groupby(['Income owner']).sum()
+ temp.index = temp.index.map(lambda x: 'Subsidies total {} (Million euro)'.format(x))
+ output.update(temp.T / 10 ** 6 / step)
+
+ temp = subsidies_total[subsidies_total.index.get_level_values('Occupancy status') == 'Social-housing'].sum() / subsidies_total.sum()
+ output['Share of subsidies going to Social-housing (Million euro)'] = temp.sum()
+
self.store_over_years[self.year].update(
{'Annuities heater (Billion euro/year)': output['Annuities heater (Billion euro/year)'],
'Annuities insulation (Billion euro/year)': output['Annuities insulation (Billion euro/year)'],
@@ -5790,17 +6360,175 @@ def condition_decarbonizing(x):
output['Cost-benefits analysis (Billion euro)'] = output['CBA benefits (Billion euro)'] + output['CBA cost (Billion euro)']
+ tmp1, tmp2, tmp3 = 0, 0, 0
+
+ if self.flow_by_certificate_couples_insulation is not None:
+ tmp1 = self.sum_performance_insulation / 10 ** 3
+ output['High-performance renovation (Thousand households)'] = tmp1
+ flow_by_certificate_couples = self.flow_by_certificate_couples_insulation / 10 ** 3
+ output.update({'Renovation from {} to '.format(i) + '{} (Thousand households)'.format(j): flow_by_certificate_couples.loc[(i,j)] for (i,j) in flow_by_certificate_couples.index})
+
+ if self.flow_by_certificate_couples_obligation is not None:
+ tmp2 = self.sum_performance_insulation_obligation / 10 ** 3
+ output['Obligatory High-performance renovation (Thousand households)'] = tmp2
+ flow_by_certificate_couples_obligation = self.flow_by_certificate_couples_obligation / 10 ** 3
+ output.update({'Obligatory renovation from {} to '.format(i) + '{} (Thousand households)'.format(j): flow_by_certificate_couples_obligation.loc[(i,j)] for (i,j) in flow_by_certificate_couples_obligation.index})
+
+ if self.flow_by_certificate_couples_heater is not None:
+ tmp3 = self.sum_performance_changes_heater / 10 ** 3
+ output['High-performance flow for heater replacement only (Thousand households)'] = tmp3
+ flow_by_certificate_couples = self.flow_by_certificate_couples_heater / 10 ** 3
+ output.update({'Heater replacement only - {} to '.format(i) + '{} (Thousand households)'.format(j): flow_by_certificate_couples.loc[(i,j)] for (i,j) in flow_by_certificate_couples.index})
+
+ temp = tmp1 + tmp2 + tmp3
+
+ if temp > 0:
+ output['Total High-performance renovation (Thousand households)'] = temp
+
+ tmp1, tmp2= 0, 0
+
+ if self.flow_by_certificate_couples_ampleur_insulation is not None:
+ tmp1 = self.sum_performance_insulation_ampleur / 10 ** 3
+ output['Renovation ampleur (Thousand households)'] = tmp1
+ flow_by_certificate_couples_ampleur = self.flow_by_certificate_couples_ampleur_insulation / 10 ** 3
+ output.update({'Renovation >= 2 operations from {} to '.format(i) + '{} (Thousand households)'.format(j): flow_by_certificate_couples_ampleur.loc[(i,j)] for (i,j) in flow_by_certificate_couples_ampleur.index})
+
+ if self.flow_by_certificate_couples_ampleur_obligation is not None:
+ tmp2 = self.sum_performance_insulation_ampleur_obligation / 10 ** 3
+ output['Obligatory Renovation ampleur (Thousand households)'] = tmp2
+ flow_by_certificate_couples_ampleur_obligation = self.flow_by_certificate_couples_ampleur_obligation / 10 ** 3
+ output.update({'Obligatory renovation ampleur from {} to '.format(i) + '{} (Thousand households)'.format(j): flow_by_certificate_couples_ampleur_obligation.loc[(i,j)] for (i,j) in flow_by_certificate_couples_ampleur_obligation.index})
+
+ temp = tmp1 + tmp2
+
+ if temp > 0:
+ output['Total Renovation ampleur (Thousand households)'] = temp
+
+ flow_by_operation = self.flow_by_operation_insulation / 10**3
+
+ ###############################################################################################################
+ ####################### Adding df_renovations #################################################################
+ ###############################################################################################################
+
+ renovation_details_long = self.renovation_details_long.fillna(0)
+ if self.flow_by_certificate_couples_obligation is not None:
+ renovation_details_long_obligation = self.renovation_details_long_obligation.fillna(0)
+ df_renovations_obligation_3 = pd.DataFrame(columns=["Occupancy status","Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation", "Income", "Operation type","Year","Value"])
+ for i in renovation_details_long_obligation.squeeze().index :
+
+ i_columns = i.split('_')
+ i_columns.append(self.year)
+ value = renovation_details_long_obligation.squeeze().loc[(i)]
+ i_columns.append(value)
+
+ df_renovations_obligation_2 = pd.DataFrame([i_columns], columns=["Occupancy status","Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation","Income", "Operation type","Year","Value"])
+
+ df_renovations_obligation_3 = pd.concat([df_renovations_obligation_3, df_renovations_obligation_2], ignore_index=True)
+
+ df_renovations_obligation = pd.concat([df_renovations_obligation, df_renovations_obligation_3], ignore_index=True)
+
+# output.update({'Renovation {} (Thousand households)'.format(i): flow_by_operation.loc[(i)] for (i) in flow_by_operation.index})
+
+# output.update({'Renovation {} (Thousand households)'.format(i): renovation_details_long.squeeze().loc[(i)] for (i) in renovation_details_long.squeeze().index})
+
+ if self.merged_df_heater is not None:
+ merged_df_heater = self.merged_df_heater
+ merged_df_heater = merged_df_heater.reset_index()
+ merged_df_heater.drop(['Wall', 'Floor', 'Roof','Windows','Existing','Income owner'], axis=1, inplace=True)
+ merged_df_heater = merged_df_heater.groupby(['Occupancy status','Housing type',"Heating system final","Heating system","certificate_before_heater","certificate_after_heater"]).sum()
+ merged_df_heater.reset_index(inplace=True)
+ merged_df_heater["epc before heater"] = merged_df_heater["certificate_before_heater"].replace(EPC2INT)
+ merged_df_heater["epc after heater"] = merged_df_heater["certificate_after_heater"].replace(EPC2INT)
+ merged_df_heater["steps_heater"] = - (merged_df_heater["epc after heater"] - merged_df_heater["epc before heater"])
+ merged_df_heater["Year"] = self.year
+ merged_df_heater.drop(["epc before heater", "epc after heater"], axis=1, inplace=True)
+
+ else :
+ merged_df_heater = pd.DataFrame(columns=["Occupancy status","Housing type","Heating system final","Heating system","Flow","certificate_before_heater","certificate_after_heater","steps_heater","Year"])
+
+ df_renovations_3 = pd.DataFrame(columns=["Occupancy status","Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation", "Income", "Operation type","Year","Value"])
+
+ mapping_final = {"Wi" : "Windows",
+ "R" : "othersingleinsulation",
+ "RWi" : "2insulations",
+ "F" : "othersingleinsulation",
+ "FWi": "2insulations",
+ "FR": "2insulations",
+ "FRWi": "3insulations",
+ "Wa": "othersingleinsulation",
+ "WaWi": "2insulations",
+ "WR": "2insulations",
+ "WaRWi":"3insulations",
+ "WaF": "2insulations",
+ "WaFWi": "3insulations",
+ "WaFR": "3insulations",
+ "WaFRWi": "4insulations"}
+
+ renovation_details_long_bis = renovation_details_long.reset_index()
+ renovation_details_long_bis["operation_details_2"] = renovation_details_long_bis["operation_details"]
+ renovation_details_long_bis[['1','2','3','4','5','6','7','8']] = renovation_details_long_bis['operation_details_2'].str.split('_', n=7, expand=True)
+ renovation_details_long_bis['category'] = renovation_details_long_bis['8'].apply(lambda x: mapping_final.get(x, 'Unknown'))
+
+ renovation_details_cleaned = renovation_details_long_bis.drop(['operation_details','operation_details_2', '8'], axis=1)
+ grouped_renovation_details = renovation_details_cleaned.groupby(['1','2','3','4','5','6','7','category']).agg(
+ Flow_Choice_=('Flow_Choice_', 'sum'))
+ grouped_renovation_details = grouped_renovation_details.reset_index()
+ grouped_renovation_details["operation_details"] = grouped_renovation_details.apply(lambda row: "_".join([str(row[str(i)]) for i in range(1, 8)] + [str(row['category'])]), axis=1)
+ grouped_renovation_details = grouped_renovation_details.set_index('operation_details')
+ cols_to_keep = [col for col in grouped_renovation_details.columns if col not in ['1','2','3','4','5','6','7','category']]
+ grouped_renovation_details = grouped_renovation_details[cols_to_keep]
+
+ for i,val in enumerate(grouped_renovation_details.squeeze().index) :
+
+ i_columns = val.split('_')
+ i_columns.append(self.year)
+ value = grouped_renovation_details.squeeze().loc[(val)]
+ i_columns.append(value)
+
+ df_renovations_2 = pd.DataFrame([i_columns], columns=["Occupancy status","Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation", "Income", "Operation type","Year","Value"])
+
+ df_renovations_3 = pd.concat([df_renovations_3, df_renovations_2], ignore_index=True)
+
+ df_renovations = pd.concat([df_renovations, df_renovations_3], ignore_index=True)
+
+ df_renovations["epc before heater"] = df_renovations["Category before heater"].replace(EPC2INT)
+ df_renovations["epc before insulation"] = df_renovations["Category before insulation"].replace(EPC2INT)
+ df_renovations["epc after insulation"] = df_renovations["Category after insulation"].replace(EPC2INT)
+ df_renovations["steps_heater"] = - (df_renovations["epc before insulation"] - df_renovations["epc before heater"])
+ df_renovations["steps_insulation"] = - (df_renovations["epc after insulation"] - df_renovations["epc before insulation"])
+ df_renovations["steps_insulation_with_heater"] = - (df_renovations["epc after insulation"] - df_renovations["epc before heater"])
+ df_renovations.drop(['epc before heater', 'epc before insulation', 'epc after insulation'], axis=1, inplace=True)
+ df_renovations["Obligation"] = "No"
+
+ if self.flow_by_certificate_couples_obligation is not None:
+ df_renovations_obligation["epc before heater"] = df_renovations_obligation["Category before heater"].replace(EPC2INT)
+ df_renovations_obligation["epc before insulation"] = df_renovations_obligation["Category before insulation"].replace(EPC2INT)
+ df_renovations_obligation["epc after insulation"] = df_renovations_obligation["Category after insulation"].replace(EPC2INT)
+ df_renovations_obligation["steps_heater"] = - (df_renovations_obligation["epc before insulation"] - df_renovations_obligation["epc before heater"])
+ df_renovations_obligation["steps_insulation"] = - (df_renovations_obligation["epc after insulation"] - df_renovations_obligation["epc before insulation"])
+ df_renovations_obligation["steps_insulation_with_heater"] = - (df_renovations_obligation["epc after insulation"] - df_renovations_obligation["epc before heater"])
+ df_renovations_obligation.drop(['epc before heater', 'epc before insulation', 'epc after insulation'], axis=1, inplace=True)
+ df_renovations_obligation["Obligation"] = "Yes"
+ df_renovations_obligation['Operation type'] = df_renovations_obligation['Operation type'].apply(lambda x: mapping_final.get(x, 'Unknown'))
+
+ df_renovations_total = pd.concat([df_renovations, df_renovations_obligation], ignore_index=True)
+
+ ###############################################################################################################
+ ####################### End adding_df_renovations #################################################################
+ ###############################################################################################################
+
output = Series(output).rename(self.year)
stock = stock.rename(self.year)
- return stock, output
+
+ return stock, output, df_renovations_total, merged_df_heater, df_final_grouped
- def parse_output_run_cba(self, prices, inputs, step=1, taxes=None, bill_rebate=0):
+ def parse_output_run_cba(self, prices, inputs, step=1, taxes=None, bill_rebate=0, pef_elec=None):
output = dict()
# emission
emission = inputs['carbon_emission'].loc[self.year, :]
consumption_energy = self.consumption_agg(prices=prices, freq='year', climate=None, standard=False, agg='energy',
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate, pef_elec=pef_elec)
temp = consumption_energy * emission
output['Emission (MtCO2)'] = temp.sum() / 10 ** 3
@@ -5816,7 +6544,7 @@ def parse_output_run_cba(self, prices, inputs, step=1, taxes=None, bill_rebate=0
output['VAT heater (Billion euro)'] = self._heater_store['vat'] / 10 ** 9 / step
output['Investment heater WT (Billion euro)'] = investment_heater - output['VAT heater (Billion euro)']
- output['Health cost (Billion euro)'] = self.health_cost(inputs['health_cost_dpe'], inputs['health_cost_income'], prices)
+ output['Health cost (Billion euro)'] = self.health_cost(inputs['health_cost_dpe'], inputs['health_cost_income'], prices, pef_elec=inputs['pef_elec'].loc[self.year])
output['VAT (Billion euro)'] = output['VAT insulation (Billion euro)'] + output['VAT heater (Billion euro)']
output['Health expenditure (Billion euro)'] = 0 # temp['Health expenditure (Billion euro)']
@@ -5827,7 +6555,7 @@ def parse_output_run_cba(self, prices, inputs, step=1, taxes=None, bill_rebate=0
if taxes is not None:
consumption_energy = self.consumption_agg(prices=prices, freq='year', climate=None, standard=False,
- agg='energy', bill_rebate=bill_rebate)
+ agg='energy', bill_rebate=bill_rebate, pef_elec=inputs['pef_elec'].loc[self.year])
taxes_expenditures = dict()
total_taxes = Series(0, index=prices.index)
@@ -5884,9 +6612,9 @@ def parse_output_run_cba(self, prices, inputs, step=1, taxes=None, bill_rebate=0
return output
- def parse_output_consumption(self, prices, bill_rebate=0):
+ def parse_output_consumption(self, prices, bill_rebate=0, pef_elec=None):
output = self.consumption_agg(prices=prices, freq='year', climate=None, standard=False, agg='energy',
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate, pef_elec=pef_elec)
output.index = output.index.map(lambda x: 'Consumption {} (TWh)'.format(x))
temp = prices.T
temp.index = temp.index.map(lambda x: 'Prices {} (euro/kWh)'.format(x))
@@ -5973,7 +6701,7 @@ def calculate_indicators_insulation():
def calibration_exogenous(self, coefficient_global=None, coefficient_heater=None, constant_heater=None,
scale_heater=None, constant_insulation_intensive=None, constant_insulation_extensive=None,
scale_insulation=None, energy_prices=None, rational_hidden_cost=None,
- number_firms_insulation=None, number_firms_heater=None, hi_threshold=None):
+ number_firms_insulation=None, number_firms_heater=None, hi_threshold=None, pef_elec=None):
"""Function calibrating buildings object with exogenous data.
@@ -5993,7 +6721,7 @@ def calibration_exogenous(self, coefficient_global=None, coefficient_heater=None
# calibration energy consumption first year
if (coefficient_global is None) and (energy_prices is not None):
- self.calibration_consumption(energy_prices.loc[self.first_year, :], None)
+ self.calibration_consumption(energy_prices.loc[self.first_year, :], None, pef_elec=pef_elec.loc[self.first_year])
else:
self.coefficient_global = coefficient_global
self.coefficient_heater = coefficient_heater
@@ -6061,7 +6789,7 @@ def flow_demolition(self, demolition_rate, step=1):
flow_demolition = (stock_demolition * demolition_total).dropna()
return flow_demolition.reorder_levels(self.stock.index.names)
- def health_cost(self, health_cost_dpe, health_cost_income, prices, stock=None, method_health_cost=None):
+ def health_cost(self, health_cost_dpe, health_cost_income, prices, stock=None, method_health_cost=None, pef_elec=None):
if method_health_cost is None:
method_health_cost = self.method_health_cost
@@ -6070,7 +6798,7 @@ def health_cost(self, health_cost_dpe, health_cost_income, prices, stock=None, m
stock = self.stock
if method_health_cost == 'epc':
- _, certificate, _ = self.consumption_heating(method='3uses', full_output=True)
+ _, certificate, _ = self.consumption_heating(method='3uses', full_output=True, pef_elec=pef_elec)
temp = concat((stock, reindex_mi(certificate, stock.index).rename('Performance')), axis=1)
temp.set_index('Performance', append=True, inplace=True)
temp = temp.squeeze()
@@ -6078,7 +6806,7 @@ def health_cost(self, health_cost_dpe, health_cost_income, prices, stock=None, m
elif method_health_cost == 'heating_intensity':
- heating_intensity = self.to_heating_intensity(stock.index, prices)
+ heating_intensity = self.to_heating_intensity(stock.index, prices, pef_elec=pef_elec)
stock = concat((stock, heating_intensity), axis=1, keys=['Stock', 'Heating intensity'])
stock_health = stock.loc[stock['Heating intensity'] <= self.hi_threshold, 'Stock']
@@ -6087,7 +6815,7 @@ def health_cost(self, health_cost_dpe, health_cost_income, prices, stock=None, m
def marginal_abatement_cost(self, consumption_saved, emission_saved, cost_insulation, stock, prices,
certificate_after, certificate_after_3uses, lifetime=30,
discount_rate=0.05, measures='deep_renovation', plot=False, carbon_saved=None,
- health_cost=None, cash_flow_option=True):
+ health_cost=None, cash_flow_option=True, pef_elec=None):
"""Calculate the marginal abatement cost of insulation measures.
Parameters
@@ -6133,7 +6861,7 @@ def marginal_abatement_cost(self, consumption_saved, emission_saved, cost_insula
_output_statistics = {}
health_cost_saved = None
if health_cost is not None:
- _, certificate_before, _ = self.consumption_heating(index=index, method='3uses', full_output=True)
+ _, certificate_before, _ = self.consumption_heating(index=index, method='3uses', full_output=True, pef_elec=pef_elec)
df = concat((certificate_before, stock.loc[index]), keys=['Performance', 'Stock'], axis=1).dropna().set_index(
'Performance', append=True).squeeze()
health_cost_before = reindex_mi(health_cost, df.index).droplevel('Performance').fillna(0)
@@ -6346,7 +7074,8 @@ def closest(df, value):
def make_static_analysis(self, cost_insulation, cost_heater, prices, discount_rate,
implicit_discount_rate, health_cost, carbon_content,
- path_out=None, carbon_value=50, selected_options=None, sufix=''):
+ path_out=None, carbon_value=50, selected_options=None, sufix='',
+ pef_elec=None):
# select only stock mobile and existing before the first year
if path_out is None:
path_out = self.path_ini
@@ -6356,12 +7085,13 @@ def make_static_analysis(self, cost_insulation, cost_heater, prices, discount_ra
stock = stock.droplevel('Performance')
index = stock.index
- consumption_before = self.consumption_heating_store(index, full_output=False)
+ consumption_before = self.consumption_heating_store(index, full_output=False, pef_elec=pef_elec)
consumption_before = reindex_mi(consumption_before, index)
temp = consumption_before * reindex_mi(self._surface, consumption_before.index)
heating_intensity_before = self.to_heating_intensity(temp.index, prices,
consumption=temp,
- level_heater='Heating system')
+ level_heater='Heating system',
+ pef_elec=pef_elec)
consumption_before *= heating_intensity_before
c_content = carbon_content.reindex(self.to_energy(consumption_before)).set_axis(consumption_before.index)
@@ -6379,19 +7109,22 @@ def make_static_analysis(self, cost_insulation, cost_heater, prices, discount_ra
consumption_after, _, certificate_after = self.prepare_consumption(self._choice_insulation,
index=s.index,
level_heater='Heating system final',
- full_output=True)
+ full_output=True,
+ pef_elec=pef_elec)
consumption_after = reindex_mi(consumption_after, s.index)
_, _, certificate_after_3uses = self.prepare_consumption(self._choice_insulation,
index=s.index,
level_heater='Heating system final',
full_output=True,
- method_epc='3uses')
+ method_epc='3uses',
+ pef_elec=pef_elec)
temp = (consumption_after.T * reindex_mi(self._surface, consumption_after.index)).T
heating_intensity_after = self.to_heating_intensity(temp.index, prices,
consumption=temp,
- level_heater='Heating system final')
+ level_heater='Heating system final',
+ pef_elec=pef_elec)
consumption_after *= heating_intensity_after
c_content = carbon_content.reindex(self.to_energy(consumption_after, level_heater='Heating system final')).set_axis(consumption_after.index)
@@ -6546,16 +7279,16 @@ def make_static_analysis(self, cost_insulation, cost_heater, prices, discount_ra
dict_rslt, dict_stats = {}, {}
for key, option in options.items():
temp = self.marginal_abatement_cost(consumption_saved, emission_saved, cost, self._stock_ref,
- prices, certificate_after, certificate_after_3uses, lifetime=25, **option)
+ prices, certificate_after, certificate_after_3uses, lifetime=25, **option, pef_elec=pef_elec)
dict_rslt.update({key: temp[0]})
dict_stats.update({key: temp[1]})
dict_rslt = reverse_dict(dict_rslt)
- c_before = self.consumption_heating_store(self._stock_ref.index, full_output=False)
+ c_before = self.consumption_heating_store(self._stock_ref.index, full_output=False, pef_elec=pef_elec)
c_before = reindex_mi(c_before, self._stock_ref.index) * self._stock_ref * reindex_mi(self._surface,
self._stock_ref.index)
- heating_intensity_before = self.to_heating_intensity(c_before.index, prices)
+ heating_intensity_before = self.to_heating_intensity(c_before.index, prices, pef_elec=pef_elec)
c_before *= heating_intensity_before
c_content = carbon_content.reindex(self.to_energy(c_before)).set_axis(c_before.index)
diff --git a/project/config/config.json b/project/config/config.json
index c960ae5e..b52c6a06 100644
--- a/project/config/config.json
+++ b/project/config/config.json
@@ -11,7 +11,7 @@
"add_policies": null,
"remove_policies": null,
"policies_scenarios": null,
- "no_natural_replacement": null
+ "no_natural_replacement": false
},
"sensitivity": {
"activated": false
@@ -32,7 +32,7 @@
"Reference": {
"file": "project/config/reference.json",
"step": 1,
- "end": 2020,
+ "end": 2025,
"policies": {
"file": "project/input/policies/policies_2024.json",
"restriction_gas": {
@@ -44,7 +44,8 @@
}
},
"simple": {
- "no_policy_heater": false
+ "no_policy_heater": false,
+ "quintiles": false
}
}
-}
\ No newline at end of file
+}
diff --git a/project/config/reference.json b/project/config/reference.json
index 5979d26c..f522978a 100644
--- a/project/config/reference.json
+++ b/project/config/reference.json
@@ -2,7 +2,7 @@
"end": 2051,
"step": 1,
"output": "full",
- "start": 2017,
+ "start": 2022,
"building_stock": "project/input/stock/buildingstock_sdes2018_medium_3.csv",
"financing_cost": {
"activated": true,
@@ -103,7 +103,7 @@
"income": "project/input/macro/income.csv",
"income_rate": 0.008,
"rotation_rate": "project/input/macro/rotation_rate.csv",
- "consumption_ini": "project/input/macro/consumption_ini.csv",
+ "consumption_ini": "project/input/macro/consumption_ini_agg.csv",
"present_discount_rate": "project/input/investment/present_discount_rate.csv",
"population": null,
"surface_built": null,
@@ -118,7 +118,7 @@
"health_cost_income": "project/input/policies/health_cost_income.csv",
"carbon_value": "project/input/policies/carbon_value.csv",
"policies": {
- "file": "project/input/policies/policies_2021.json"
+ "file": "project/policies/policies_2024.json"
},
"simple": {
"figures": true,
@@ -134,12 +134,12 @@
"income_constant": false,
"prices_constant": false,
"heating_system": {
- "Oil fuel-Standard boiler": "Oil fuel-Performance boiler",
- "Natural gas-Standard boiler": "Natural gas-Performance boiler",
- "Wood fuel-Standard boiler": "Wood fuel-Performance boiler",
- "Natural gas-Collective boiler": "Natural gas-Performance boiler",
- "Oil fuel-Collective boiler": "Oil fuel-Performance boiler",
- "Electricity-Heat pump air": "Electricity-Heat pump water"
+ "Oil fuel-Standard boiler": "Oil fuel-Performance boiler",
+ "Natural gas-Standard boiler": "Natural gas-Performance boiler",
+ "Wood fuel-Standard boiler": "Wood fuel-Performance boiler",
+ "Natural gas-Collective boiler": "Natural gas-Performance boiler",
+ "Oil fuel-Collective boiler": "Oil fuel-Performance boiler",
+ "Electricity-Heat pump air": "Electricity-Heat pump water"
},
"insulation": false,
"no_heating_switch": false,
diff --git a/project/coupling.py b/project/coupling.py
index 7c41909b..89bb8eb7 100644
--- a/project/coupling.py
+++ b/project/coupling.py
@@ -84,7 +84,7 @@ def ini_res_irf(config=None, path=None, level_logger='DEBUG'):
output = pd.DataFrame()
# run first year - consumption
- _, o = buildings.parse_output_run(energy_prices.loc[buildings.first_year, :], inputs_dynamics['post_inputs'])
+ _, o = buildings.parse_output_run(energy_prices.loc[buildings.first_year, :], inputs_dynamics['post_inputs'], pef_elec=inputs_dynamics['pef_elec'].loc[buildings.first_year])
output = pd.concat((output, o), axis=1)
if config['simple'].get('no_policy_insulation'):
diff --git a/project/input/energy/energy_prices_wt_ame2024.csv b/project/input/energy/energy_prices_wt_ame2024.csv
new file mode 100644
index 00000000..bb99093b
--- /dev/null
+++ b/project/input/energy/energy_prices_wt_ame2024.csv
@@ -0,0 +1,35 @@
+,Oil fuel,Natural gas,Electricity,Wood fuel,Heating
+2017,0.0619,0.05937,0.1151,0.056,0.0746
+2018,0.0619,0.05937,0.1151,0.056,0.0746
+2019,0.0619,0.05937,0.1151,0.056,0.0746
+2020, 0.0452,0.048668936,0.115147572,0.0565708,0.0735
+2021,0.0487,0.056204489,0.116998057,0.057266139,0.074403424
+2022,0.0523,0.063740042,0.118848543,0.057961477,0.075306847
+2023,0.0559,0.071275596,0.120699029,0.058656816,0.076210271
+2024,0.0595,0.078811149,0.122549514,0.059352155,0.077113694
+2025,0.0631,0.086346702,0.1244,0.060047493,0.078017118
+2026,0.0642,0.085784187,0.125518041,0.060785566,0.078976063
+2027,0.0653,0.085221672,0.126636083,0.061523638,0.079935009
+2028,0.0664,0.084659157,0.127754124,0.06226171,0.080893954
+2029,0.0675,0.084096642,0.128872166,0.062999783,0.0818529
+2030,0.0687,0.083534127,0.129990207,0.063737855,0.082811846
+2031,0.0698,0.082642417,0.129990207,0.064521288,0.083829726
+2032,0.0709,0.081750708,0.129990207,0.06530472,0.084847605
+2033,0.0720,0.080858998,0.129990207,0.066088152,0.085865485
+2034,0.0731,0.079967288,0.129990207,0.066871585,0.086883365
+2035,0.0742,0.079075578,0.129990207,0.067655017,0.087901245
+2036,0.0745,0.081413004,0.129990207,0.068486597,0.088981681
+2037,0.0748,0.083750431,0.129990207,0.069318177,0.090062117
+2038,0.0751,0.086087858,0.129990207,0.070149757,0.091142553
+2039,0.0754,0.088425284,0.129990207,0.070981337,0.09222299
+2040,0.0757,0.090762711,0.129990207,0.071812917,0.093303426
+2041,0.0768,0.090594567,0.129990207,0.072695604,0.094450263
+2042,0.0778,0.090426423,0.129990207,0.073578291,0.095597099
+2043,0.0789,0.090258279,0.129990207,0.074460978,0.096743936
+2044,0.0799,0.090090134,0.129990207,0.075343665,0.097890773
+2045,0.0810,0.08992199,0.129990207,0.076226351,0.09903761
+2046,0.0828,0.089637982,0.129990207,0.077163286,0.100254929
+2047,0.0847,0.089353973,0.129990207,0.07810022,0.101472247
+2048,0.0865,0.089069964,0.129990207,0.079037155,0.102689566
+2049,0.0884,0.088785956,0.129990207,0.079974089,0.103906884
+2050,0.0903,0.088501947,0.129990207,0.080911024,0.105124203
\ No newline at end of file
diff --git a/project/input/energy/primary_energy_factor_electricity.csv b/project/input/energy/primary_energy_factor_electricity.csv
new file mode 100644
index 00000000..b21c93cd
--- /dev/null
+++ b/project/input/energy/primary_energy_factor_electricity.csv
@@ -0,0 +1,3 @@
+Year,Electricity
+2023,2.3
+2026,1.9
\ No newline at end of file
diff --git a/project/input/macro/consumption_ini_agg.csv b/project/input/macro/consumption_ini_agg.csv
new file mode 100644
index 00000000..23194767
--- /dev/null
+++ b/project/input/macro/consumption_ini_agg.csv
@@ -0,0 +1,6 @@
+Heating energy,
+Electricity,50
+Natural gas,91
+Oil fuel,31
+Wood fuel,77
+Heating,12
diff --git a/project/input/macro/energy_taxes_ame2021.csv b/project/input/macro/energy_taxes_ame2021.csv
new file mode 100644
index 00000000..f58490c4
--- /dev/null
+++ b/project/input/macro/energy_taxes_ame2021.csv
@@ -0,0 +1,34 @@
+,Oil fuel,Natural gas,Electricity,Wood fuel,Heating
+2018,0.0156,0.0111,0.0358,0,0
+2019,0.0156,0.0111,0.0358,0,0
+2020,0.0156,0.0111,0.0358,0,0
+2021,0.0156,0.0111,0.0358,0,0
+2022,0.0156,0.0111,0.0358,0,0
+2023,0.0156,0.0111,0.0358,0,0
+2024,0.0156,0.0111,0.0358,0,0
+2025,0.0156,0.0111,0.0358,0,0
+2026,0.0156,0.0111,0.0358,0,0
+2027,0.0156,0.0111,0.0358,0,0
+2028,0.0156,0.0111,0.0358,0,0
+2029,0.0156,0.0111,0.0358,0,0
+2030,0.0156,0.0111,0.0358,0,0
+2031,0.0156,0.0111,0.0358,0,0
+2032,0.0156,0.0111,0.0358,0,0
+2033,0.0156,0.0111,0.0358,0,0
+2034,0.0156,0.0111,0.0358,0,0
+2035,0.0156,0.0111,0.0358,0,0
+2036,0.0156,0.0111,0.0358,0,0
+2037,0.0156,0.0111,0.0358,0,0
+2038,0.0156,0.0111,0.0358,0,0
+2039,0.0156,0.0111,0.0358,0,0
+2040,0.0156,0.0111,0.0358,0,0
+2041,0.0156,0.0111,0.0358,0,0
+2042,0.0156,0.0111,0.0358,0,0
+2043,0.0156,0.0111,0.0358,0,0
+2044,0.0156,0.0111,0.0358,0,0
+2045,0.0156,0.0111,0.0358,0,0
+2046,0.0156,0.0111,0.0358,0,0
+2047,0.0156,0.0111,0.0358,0,0
+2048,0.0156,0.0111,0.0358,0,0
+2049,0.0156,0.0111,0.0358,0,0
+2050,0.0156,0.0111,0.0358,0,0
\ No newline at end of file
diff --git a/project/input/macro/energy_taxes_ams_run3.csv b/project/input/macro/energy_taxes_ams_run3.csv
new file mode 100644
index 00000000..235677fe
--- /dev/null
+++ b/project/input/macro/energy_taxes_ams_run3.csv
@@ -0,0 +1,34 @@
+,Oil fuel,Natural gas,Electricity,Wood fuel,Heating
+2018,0.0156,0.011,0.038,0,0
+2019,0.011,0.014,017,0,0
+2020,0.009,0.014,0.018,0,0
+2021,0.011,0.013,0.019,0,0
+2022,0.017,0.017,0.20,0
+2023,0.015,0.020,0.023,0,0
+2024,0.016,0.019,0.026,0,0
+2025,0.016,0.019,0.037,0,0
+2026,0.016,0.019,0.037,0,0
+2027,0.016,0.019,0.037,0,0
+2028,0.016,0.019,0.037,0,0
+2029,0.016,0.019,0.037,0,0
+2030,0.016,0.019,0.037,0,0
+2031,0.016,0.019,0.037,0,0
+2032,0.016,0.019,0.037,0,0
+2033,0.016,0.019,0.037,0,0
+2034,0.016,0.019,0.037,0,0
+2035,0.016,0.019,0.037,0,0
+2036,0.016,0.019,0.037,0,0
+2037,0.016,0.019,0.038,0,0
+2038,0.016,0.019,0.038,0,0
+2039,0.016,0.019,0.038,0,0
+2040,0.016,0.019,0.038,0,0
+2041,0.016,0.019,0.038,0,0
+2042,0.016,0.019,0.038,0,0
+2043,0.016,0.019,0.038,0,0
+2044,0.016,0.019,0.038,0,0
+2045,0.016,0.019,0.038,0,0
+2046,0.016,0.019,0.038,0,0
+2047,0.016,0.019,0.038,0,0
+2048,0.016,0.019,0.038,0,0
+2049,0.016,0.019,0.038,0,0
+2050,0.016,0.019,0.038,0,0
\ No newline at end of file
diff --git a/project/input/macro/energy_vta.csv b/project/input/macro/energy_vta.csv
new file mode 100644
index 00000000..7c314463
--- /dev/null
+++ b/project/input/macro/energy_vta.csv
@@ -0,0 +1,5 @@
+Oil fuel,0.2
+Natural gas,0.15
+Electricity,0.15
+Wood fuel,0.1
+Heating,0
\ No newline at end of file
diff --git a/project/input/macro/energy_vta_ams_run3.csv b/project/input/macro/energy_vta_ams_run3.csv
new file mode 100644
index 00000000..7c314463
--- /dev/null
+++ b/project/input/macro/energy_vta_ams_run3.csv
@@ -0,0 +1,5 @@
+Oil fuel,0.2
+Natural gas,0.15
+Electricity,0.15
+Wood fuel,0.1
+Heating,0
\ No newline at end of file
diff --git a/project/input/policies/current/mpr_insulation.csv b/project/input/policies/current/mpr_insulation.csv
index 22cd0e5b..e82c3dce 100644
--- a/project/input/policies/current/mpr_insulation.csv
+++ b/project/input/policies/current/mpr_insulation.csv
@@ -5,7 +5,7 @@ Single-family,D3,60,20,0,80
Single-family,D4,60,20,0,80
Single-family,D5,40,15,0,40
Single-family,D6,40,15,0,40
-Single-family,D7,40,15,0,40
-Single-family,D8,15,7,0,0
-Single-family,D9,15,7,0,0
-Single-family,D10,15,7,0,0
\ No newline at end of file
+Single-family,D7,0,0,0,0
+Single-family,D8,0,0,0,0
+Single-family,D9,0,0,0,0
+Single-family,D10,0,7,0,0
\ No newline at end of file
diff --git a/project/input/policies/policies_2024.json b/project/input/policies/policies_2024.json
index 7c2d8c3c..edd3f118 100644
--- a/project/input/policies/policies_2024.json
+++ b/project/input/policies/policies_2024.json
@@ -125,7 +125,8 @@
"mpr_efficacite",
"mpr_serenite",
"mpr_performance",
- "mpr_multifamily"
+ "mpr_multifamily",
+ "mpr_multifamily_updated"
],
"target": "energy_condition_50",
"policy": "subsidy_ad_valorem"
@@ -139,6 +140,7 @@
"mpr_serenite",
"mpr_performance",
"mpr_multifamily",
+ "mpr_multifamily_updated",
"mpr_multifamily_deep",
"mpr",
"mpr_efficacite",
@@ -163,6 +165,7 @@
"mpr_performance",
"mpr_multifamily",
"mpr_multifamily_deep",
+ "mpr_multifamily_updated",
"mpr",
"mpr_efficacite",
"cite",
diff --git a/project/model.py b/project/model.py
index 6951cb79..f523e931 100644
--- a/project/model.py
+++ b/project/model.py
@@ -343,7 +343,8 @@ def initialize(inputs, stock, year, taxes, path=None, config=None, logger=None,
residual_rate=config['technical'].get('residual_rate'),
constraint_heat_pumps=config['technical'].get('constraint_heat_pumps', True),
variable_size_heater=config['technical'].get('variable_size_heater', True),
- temp_sink=parsed_inputs['temp_sink'])
+ temp_sink=parsed_inputs['temp_sink'],
+ pef_elec=parsed_inputs['pef_elec'])
technical_progress = None
if 'technical_progress' in parsed_inputs.keys():
@@ -372,7 +373,8 @@ def initialize(inputs, stock, year, taxes, path=None, config=None, logger=None,
'health_cost_dpe': parsed_inputs['health_cost_dpe'],
'health_cost_income': parsed_inputs['health_cost_income'],
'output': config['output'],
- 'hourly_profile': parsed_inputs.get('hourly_profile')
+ 'hourly_profile': parsed_inputs.get('hourly_profile'),
+ 'pef_elec': parsed_inputs.get('pef_elec')
}
return inputs_dynamic
@@ -382,7 +384,7 @@ def stock_turnover(buildings, prices, taxes, cost_heater, cost_insulation, lifet
post_inputs, calib_heater=None, calib_renovation=None, financing_cost=None,
prices_before=None, climate=None, district_heating=None, step=1, demolition_rate=None, memory=False,
exogenous_social=None, output_options='full', premature_replacement=None, supply=None,
- carbon_content=None, carbon_content_before=None):
+ carbon_content=None, carbon_content_before=None, pef_elec=None):
"""Update stock vintage due to renovation, demolition and construction.
@@ -437,7 +439,8 @@ def stock_turnover(buildings, prices, taxes, cost_heater, cost_insulation, lifet
if output_options == 'full':
buildings.consumption_before_retrofit = buildings.store_consumption(prices_before,
carbon_content_before,
- bill_rebate=bill_rebate_before)
+ bill_rebate=bill_rebate_before,
+ pef_elec=pef_elec)
flow_retrofit = buildings.flow_retrofit(prices, cost_heater, cost_insulation, lifetime_insulation,
financing_cost=financing_cost,
@@ -450,7 +453,8 @@ def stock_turnover(buildings, prices, taxes, cost_heater, cost_insulation, lifet
carbon_value_kwh=post_inputs['carbon_value_kwh'].loc[year, :],
carbon_value=post_inputs['carbon_value'].loc[year],
carbon_content=carbon_content,
- bill_rebate=bill_rebate)
+ bill_rebate=bill_rebate,
+ pef_elec=pef_elec)
"""if memory:
memory_dict = {'Memory': '{:.1f} MiB'.format(psutil.Process().memory_info().rss / (1024 * 1024)),
@@ -461,7 +465,8 @@ def stock_turnover(buildings, prices, taxes, cost_heater, cost_insulation, lifet
buildings.add_flows([flow_retrofit])
flows_obligation = buildings.flow_obligation(p_insulation, prices, cost_insulation,
- financing_cost=financing_cost)
+ financing_cost=financing_cost,
+ pef_elec=pef_elec)
if flows_obligation is not None:
buildings.add_flows(flows_obligation)
@@ -473,16 +478,16 @@ def stock_turnover(buildings, prices, taxes, cost_heater, cost_insulation, lifet
buildings.logger.info('Writing output')
if output_options == 'full':
buildings.logger.debug('Full output')
- stock, output = buildings.parse_output_run(prices, post_inputs, climate=climate, step=step, taxes=taxes,
- bill_rebate=bill_rebate)
+ stock, output, df_renovations, merged_df_heater, df_final_grouped = buildings.parse_output_run(prices, post_inputs, climate=climate, step=step, taxes=taxes,
+ bill_rebate=bill_rebate, pef_elec=pef_elec)
elif output_options == 'cost_benefit':
buildings.logger.debug('Cost-benefit output')
stock = buildings.simplified_stock().rename(year)
- output = buildings.parse_output_run_cba(prices, post_inputs, step=step, taxes=taxes, bill_rebate=bill_rebate)
+ output = buildings.parse_output_run_cba(prices, post_inputs, step=step, taxes=taxes, bill_rebate=bill_rebate, pef_elec=pef_elec)
elif output_options == 'consumption':
buildings.logger.debug('Consumption output')
stock = buildings.simplified_stock().rename(year)
- output = buildings.parse_output_consumption(prices, bill_rebate=bill_rebate)
+ output = buildings.parse_output_consumption(prices, bill_rebate=bill_rebate, pef_elec=pef_elec)
else:
raise NotImplemented('output_options should be full, cost_benefit or consumption')
@@ -519,7 +524,7 @@ def stock_turnover(buildings, prices, taxes, cost_heater, cost_insulation, lifet
heating = buildings.stock.groupby('Heating system').sum()
temp = pd.concat((heating, buildings.heater_vintage.sum(axis=1)), axis=1)
- return buildings, stock, output
+ return buildings, stock, output, df_renovations, merged_df_heater, df_final_grouped
def res_irf(config, path, level_logger='DEBUG'):
@@ -580,10 +585,12 @@ def res_irf(config, path, level_logger='DEBUG'):
buildings.calibration_consumption(energy_prices.loc[buildings.first_year, :],
inputs_dynamics['consumption_ini'],
inputs_dynamics['health_cost_income'],
- inputs_dynamics['health_cost_dpe'])
+ inputs_dynamics['health_cost_dpe'],
+ pef_elec=inputs_dynamics['pef_elec'].loc[buildings.first_year]
+ )
- s, o = buildings.parse_output_run(energy_prices.loc[buildings.first_year, :], inputs_dynamics['post_inputs'],
- taxes=taxes)
+ s, o, df_renovations, merged_df_heater, df_final_grouped = buildings.parse_output_run(energy_prices.loc[buildings.first_year, :], inputs_dynamics['post_inputs'],
+ taxes=taxes, pef_elec=inputs_dynamics['pef_elec'].loc[buildings.first_year])
stock = pd.concat((stock, s), axis=1)
output = pd.concat((output, o), axis=1)
@@ -607,6 +614,9 @@ def res_irf(config, path, level_logger='DEBUG'):
if p.variable:
p.end = config['start'] + 2
+ df_renovations_final = pd.DataFrame(columns=["Occupancy status", "Housing type","Heater replacement","Category before heater","Category before insulation", "Category after insulation", "Income", "steps_heater","steps_insulation","steps_insulation_with_heater", "Operation type","Year","Value","Obligation"])
+ merged_df_heater_final = pd.DataFrame(columns=["Occupancy status","Housing type","Heating system final","Heating system","Flow","certificate_before_heater","certificate_after_heater","steps_heater","Year"])
+ df_final_grouped_final = pd.DataFrame(columns=["epc", "Heating system", "Energy", "year", "Stock buildings", "Consumption standard (TWh)", "Consumption real (TWh)"])
for k, year in enumerate(years):
start = time()
@@ -647,7 +657,40 @@ def res_irf(config, path, level_logger='DEBUG'):
heat_pump = [i for i in resources_data['index']['Heat pumps'] if i in inputs_dynamics['cost_heater'].index]
inputs_dynamics['cost_heater'].loc[heat_pump] *= (1 + technical_progress['heater'].loc[year])**step
- buildings, s, o = stock_turnover(buildings, prices, taxes,
+ # Import renovation calibration if path provided in config
+ if year == buildings.first_year + 1 and config.get('load_calibration_renovation') is not None:
+ try:
+ with open(config['load_calibration_renovation'], 'rb') as f:
+ c = load(f)
+ except FileNotFoundError:
+ buildings.logger.error(f"[Calibration] Fichier introuvable : {config['load_calibration_renovation']}")
+ raise
+ except Exception as e:
+ buildings.logger.exception(f"[Calibration] Erreur d'ouverture/lecture : {config['load_calibration_renovation']}")
+ raise
+
+ buildings.coefficient_global = c['coefficient_global']
+ buildings.coefficient_backup = c['coefficient_backup']
+ buildings.constant_insulation_extensive = c['constant_insulation_extensive']
+ buildings.constant_insulation_intensive = c['constant_insulation_intensive']
+ buildings.constant_heater = c['constant_heater']
+ buildings.scale_insulation = c['scale_insulation']
+ buildings.apply_scale(buildings.scale_insulation, gest='insulation')
+ buildings.scale_heater = c['scale_heater']
+
+ try:
+ buildings.apply_scale(buildings.scale_heater, gest='heater')
+ except Exception:
+ buildings.logger.debug("[Calibration] apply_scale('heater') non utilisé/pas nécessaire")
+
+ buildings.logger.info(f"[Calibration] Rénovation importée depuis {config['load_calibration_renovation']}")
+
+ # Reset consumption store if pef_elec changed
+ if year != buildings.first_year :
+ if inputs_dynamics['pef_elec'].loc[year] != inputs_dynamics['pef_elec'].loc[year - 1]:
+ buildings.reset_consumption_store()
+
+ buildings, s, o, df_renovations, merged_df_heater, df_final_grouped = stock_turnover(buildings, prices, taxes,
inputs_dynamics['cost_heater'],
inputs_dynamics['cost_insulation'],
inputs_dynamics['lifetime_insulation'],
@@ -666,8 +709,12 @@ def res_irf(config, path, level_logger='DEBUG'):
prices_before=prices_before,
carbon_content=carbon_content,
carbon_content_before=carbon_content_before,
- step=step)
+ step=step,
+ pef_elec=inputs_dynamics['pef_elec'].loc[year])
+ df_renovations_final = pd.concat([df_renovations_final, df_renovations], ignore_index=True)
+ merged_df_heater_final = pd.concat([merged_df_heater_final, merged_df_heater], ignore_index=True)
+ df_final_grouped_final = pd.concat([df_final_grouped_final, df_final_grouped], ignore_index=True)
stock = pd.concat((stock, s), axis=1)
stock.index.names = s.index.names
output = pd.concat((output, o), axis=1)
@@ -678,7 +725,8 @@ def res_irf(config, path, level_logger='DEBUG'):
buildings.make_static_analysis(inputs_dynamics['cost_insulation'], inputs_dynamics['cost_heater'],
prices, 0.05, 0.05, inputs_dynamics['post_inputs']['health_cost_dpe'],
inputs_dynamics['post_inputs']['carbon_emission'].loc[year, :],
- carbon_value=50)
+ carbon_value=50,
+ pef_elec=inputs_dynamics['pef_elec'].loc[year])
with open(os.path.join(buildings.path_calibration, 'calibration.pkl'), 'wb') as file:
dump({
@@ -691,6 +739,34 @@ def res_irf(config, path, level_logger='DEBUG'):
'scale_heater': buildings.scale_heater
}, file)
+ # Export renovation calibration if requested (calculated during the stock_turnover process)
+ if year == buildings.first_year + 1 and config.get('export_calibration_renovation') is not None:
+ os.makedirs(os.path.dirname(config['export_calibration_renovation']), exist_ok=True)
+
+ payload = {
+ 'schema_version': 1,
+ # Rénovation
+ 'constant_insulation_extensive': getattr(buildings, 'constant_insulation_extensive', None),
+ 'constant_insulation_intensive': getattr(buildings, 'constant_insulation_intensive', None),
+ 'scale_insulation': getattr(buildings, 'scale_insulation', None),
+ # Chauffage (si dispo)
+ 'constant_heater': getattr(buildings, 'constant_heater', None),
+ 'scale_heater': getattr(buildings, 'scale_heater', None),
+ # Coeffs globaux/backup (selon ton usage)
+ 'coefficient_global': getattr(buildings, 'coefficient_global', None),
+ 'coefficient_backup': getattr(buildings, 'coefficient_backup', None),
+ }
+
+ with open(config['export_calibration_renovation'], 'wb') as f:
+ dump(payload, f)
+
+ buildings.logger.info(f"[Calibration] Rénovation exportée vers {config['export_calibration_renovation']}")
+
+ if config.get('stop_after_calibration_export') is True:
+ buildings.logger.info('[Calibration] Arrêt demandé après export de calibration.')
+ import sys
+ sys.exit(0)
+
if path is not None:
buildings.logger.info('Writing output in {}'.format(path))
@@ -698,6 +774,9 @@ def res_irf(config, path, level_logger='DEBUG'):
pd.DataFrame(buildings.memory).to_csv(os.path.join(path, 'memory.csv'))
output.round(3).to_csv(os.path.join(path, 'output.csv'))
+ df_renovations_final.round(3).to_csv(os.path.join(path, 'df_renovations_final.csv'))
+ merged_df_heater_final.round(3).to_csv(os.path.join(path, 'df_heaters_final.csv'))
+ df_final_grouped_final.round(3).to_csv(os.path.join(path, 'conso_dpe_reel.csv'))
buildings.logger.info('Dumping output in {}'.format(os.path.join(path, 'output.csv')))
if config['output'] == 'full':
@@ -740,11 +819,12 @@ def calibration_res_irf(path, config=None, level_logger='DEBUG'):
buildings.calibration_consumption(energy_prices.loc[buildings.first_year, :],
inputs_dynamics['consumption_ini'],
inputs_dynamics['health_cost_income'],
- inputs_dynamics['health_cost_dpe']
+ inputs_dynamics['health_cost_dpe'],
+ pef_elec=inputs_dynamics['pef_elec'].loc[buildings.first_year]
)
output = pd.DataFrame()
- _, o = buildings.parse_output_run(energy_prices.loc[buildings.first_year, :], inputs_dynamics['post_inputs'])
+ _, o, df_renovations, merged_df_heater = buildings.parse_output_run(energy_prices.loc[buildings.first_year, :], inputs_dynamics['post_inputs'], pef_elec=inputs_dynamics['pef_elec'].loc[buildings.first_year])
output = pd.concat((output, o), axis=1)
year = buildings.first_year + 1
@@ -757,7 +837,7 @@ def calibration_res_irf(path, config=None, level_logger='DEBUG'):
flow_district_heating = inputs_dynamics['flow_district_heating'].loc[year]
carbon_content = inputs_dynamics['post_inputs']['carbon_emission'].loc[year, :]
- buildings, s, o = stock_turnover(buildings, prices, taxes,
+ buildings, s, o, df_renovations, merged_df_heater, df_final_grouped = stock_turnover(buildings, prices, taxes,
inputs_dynamics['cost_heater'],
inputs_dynamics['cost_insulation'], inputs_dynamics['lifetime_insulation'],
p_heater, p_insulation, f_built, year, inputs_dynamics['post_inputs'],
diff --git a/project/read_input.py b/project/read_input.py
index c9a98f76..ffeb566e 100644
--- a/project/read_input.py
+++ b/project/read_input.py
@@ -1 +1,1325 @@
-# 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 .
#
# Original author Lucas Vivier
import copy
import os
import pandas as pd
from pandas import Series, DataFrame, concat, MultiIndex, Index
from numpy.random import normal
from numpy.testing import assert_almost_equal
from project.utils import reindex_mi, get_pandas, make_plot, get_series, reverse_dict, select
from project.dynamic import stock_need, share_multi_family, share_type_built
from project.input.param import generic_input
from project.input.resources import resources_data
class PublicPolicy:
"""Public policy parent class.
Attributes
----------
name : str
Name of the policy.
start : int
Year policy starts.
end : int
Year policy ends.
value: float
policy : {'energy_taxes', 'subsidies'}
"""
def __init__(self, name, start, end, value, policy, gest=None, cap=None, target=None, cost_min=None, cost_max=None,
new=None, by='index', non_cumulative=None, frequency=None, intensive=None, min_performance=None,
bonus=False, social_housing=True, duration=None, recycling=None, proportional=None,
recycling_ini=None, year_stop=None, years_stop=None, public_cost=None, variable=True):
self.name = name
self.start = start
self.end = end
self.value = value
self.policy = policy
self.gest = gest
self.cap = cap
self.target = target
self.cost_max = cost_max
self.cost_min = cost_min
self.new = new
self.by = by
self.non_cumulative = non_cumulative
self.frequency = frequency
self.intensive = intensive
self.min_performance = min_performance
self.bonus = bonus
self.social_housing = social_housing
self.duration = duration
self.recycling = recycling
self.recycling_ini = recycling_ini
self.proportional = proportional
self.public_cost = public_cost
self.year_stop = year_stop
self.years_stop = years_stop
self.variable = variable
self.incentive = False
if policy in ['subsidy_ad_valorem', 'subsidy_target', 'subsidy_proportional', 'bonus', 'reduced_vat',
'zero_interest_loan']:
self.incentive = True
if (year_stop or years_stop) and self.incentive:
if not isinstance(value, dict):
self.value = {k: value for k in range(self.start, self.end)}
if year_stop:
if self.incentive:
if year_stop < end:
self.apply_year_stop(year_stop, self.value)
else:
self.end = min(year_stop, end)
if years_stop:
if self.incentive:
for y in years_stop:
if y < end:
self.apply_year_stop(y, self.value)
else:
self.end = min(years_stop[0], end)
def __repr__(self):
return self.name
def __str__(self):
return self.name
def cost_targeted(self, cost_insulation, target_subsidies=None):
"""
Gives the amount of the cost of a gesture for a segment over which the subvention applies.
If self.new, cost global is the amount loaned for gestures which are considered as 'global renovations',
and thus caped by the proper maximum zil amount taking the heater replacement into account.
Also, cost_no_global are the amount loaned for unique or bunch renovations actions.
Parameters
----------
cost_insulation: pd.DataFrame
Cost of an insulation gesture
target_subsidies: pd.DataFrame
Boolean values. If self.new it corresponds to the global renovations
Returns
-------
cost: pd.DataFrame
Each cell of the DataFrame corresponds to the cost after subventions of a specific gesture and segment
"""
cost = cost_insulation.copy()
if self.target is not None and target_subsidies is not None:
if isinstance(target_subsidies, Series):
target_subsidies = pd.concat([target_subsidies] * cost.shape[1], axis=1, keys=cost.columns)
cost = cost[target_subsidies.astype(bool)].fillna(0)
if self.cost_max is not None:
cost_max = reindex_mi(self.cost_max, cost.index)
cost_max = pd.concat([cost_max] * cost.shape[1], axis=1).set_axis(cost.columns, axis=1)
cost[cost > cost_max] = cost_max
if self.cost_min is not None:
cost_min = reindex_mi(self.cost_min, cost.index)
cost_min = pd.concat([cost_min] * cost.shape[1], axis=1).set_axis(
cost.columns, axis=1)
cost[cost < cost_min] = 0
return cost
def apply_year_stop(self, year_stop, value):
"""Put values of an incentive to 1e-4 to assess the impact of removing the incentive.
Rationale is to keep calculating the number of eligible households that renovate without the incentive.
Parameters
----------
year_stop
value
Returns
-------
"""
if self.policy == 'zero_interest_loan':
if not isinstance(self.cost_max, dict):
self.cost_max = {k: self.cost_max for k in range(self.start, self.end)}
self.cost_max[year_stop] = 1e-4
else:
if self.policy == 'reduced_vat':
val = 0.1 - 1e-4
else:
val = 1e-4
if isinstance(value[year_stop], (pd.Series, pd.DataFrame)):
temp = value[year_stop].mask(value[year_stop] > 0, val)
else:
temp = val
value[year_stop] = temp
self.value = value
def read_stock(config):
"""Read initial building stock.
Parameters
----------
config: dict
Returns
-------
pd.Series
MultiIndex Series with building stock attributes as levels.
"""
stock = get_pandas(config['building_stock'], lambda x: pd.read_csv(x, index_col=[0, 1, 2, 3, 4, 5, 6, 7, 8]).squeeze()).rename('Stock buildings')
stock_sum = stock.sum()
stock = stock.reset_index('Heating system')
# replace wood fuel multi-family by natural gas in building stock
idx = (stock['Heating system'] == 'Wood fuel-Standard boiler') & (stock.index.get_level_values('Housing type') == 'Multi-family')
stock.loc[idx, 'Heating system'] = 'Natural gas-Standard boiler'
idx = (stock['Heating system'] == 'Wood fuel-Performance boiler') & (stock.index.get_level_values('Housing type') == 'Multi-family')
stock.loc[idx, 'Heating system'] = 'Natural gas-Performance boiler'
assert_almost_equal(stock['Stock buildings'].sum(), stock_sum)
# specify heat-pump
repartition = 0.8
idx = stock['Heating system'] == 'Electricity-Heat pump'
hp_water = stock.loc[idx, :].copy()
hp_water['Stock buildings'] *= repartition
hp_water['Heating system'] = hp_water['Heating system'].str.replace('Electricity-Heat pump', 'Electricity-Heat pump water')
hp_air = stock.loc[idx, :].copy()
hp_air['Stock buildings'] *= (1 - repartition)
hp_air['Heating system'] = hp_air['Heating system'].str.replace('Electricity-Heat pump', 'Electricity-Heat pump air')
stock = stock.loc[~idx, :]
stock = pd.concat((stock, hp_water, hp_air), axis=0)
if config['simple'].get('collective_boiler'):
multi_family = stock.index.get_level_values('Housing type') == 'Multi-family'
oil_fuel = stock['Heating system'].isin(['Oil fuel-Performance boiler', 'Oil fuel-Standard boiler'])
stock.loc[multi_family & oil_fuel, 'Heating system'] = 'Oil fuel-Collective boiler'
repartition = 0.5
idx_gas = stock['Heating system'].isin(['Natural gas-Performance boiler', 'Natural gas-Standard boiler']) & multi_family
collective_gas = stock.loc[idx_gas, :].copy()
collective_gas['Stock buildings'] *= repartition
collective_gas['Heating system'] = collective_gas['Heating system'].str.replace('Natural gas-Performance boiler', 'Natural gas-Collective boiler')
collective_gas['Heating system'] = collective_gas['Heating system'].str.replace('Natural gas-Standard boiler', 'Natural gas-Collective boiler')
individual_gas = stock.loc[idx_gas, :].copy()
individual_gas['Stock buildings'] *= (1 - repartition)
stock = stock.loc[~idx_gas, :]
stock = pd.concat((stock, collective_gas, individual_gas), axis=0)
stock = stock.set_index('Heating system', append=True).squeeze()
stock = stock.groupby(stock.index.names).sum()
stock = pd.concat([stock], keys=[True], names=['Existing'])
idx_names = ['Existing', 'Occupancy status', 'Income owner', 'Income tenant', 'Housing type',
'Heating system', 'Wall', 'Floor', 'Roof', 'Windows']
stock = stock.reorder_levels(idx_names)
assert_almost_equal(stock.sum(), stock_sum)
return stock
def read_policies(config):
# TODO: replace names to clarify the definition of policies (index = households, columns = technologies).
# TODO: target should be a list with combination of simple condition.
def read_mpr(data):
l = list()
heater = get_pandas(data['heater'],
lambda x: pd.read_csv(x, index_col=[0, 1]).squeeze().unstack('Heating system final'))
if data.get('growth_heater'):
growth_heater = get_pandas(data['growth_heater'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
heater = {k: i * heater for k, i in growth_heater.items()}
insulation = get_pandas(data['insulation'], lambda x: pd.read_csv(x, index_col=[0, 1]))
if data.get('growth_insulation'):
growth_insulation = get_pandas(data['growth_insulation'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
insulation = {k: i * insulation for k, i in growth_insulation.items()}
if data.get('deep_renovation'):
deep_renovation = get_pandas(data['deep_renovation'],
lambda x: pd.read_csv(x, index_col=[0]).squeeze())
l.append(PublicPolicy(data['name'], data['start'], data['end'], deep_renovation, 'subsidy_target',
gest='insulation', target='deep_renovation', year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
if data['bonus']:
bonus_best = get_pandas(data['bonus'], lambda x: pd.read_csv(x, index_col=[0, 1]).squeeze())
bonus_worst = get_pandas(data['bonus'], lambda x: pd.read_csv(x, index_col=[0, 1]).squeeze())
l.append(PublicPolicy(data['name'], data['start'], data['end'], bonus_best, 'bonus', gest='insulation',
target='reach_best', year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
l.append(PublicPolicy(data['name'], data['start'], data['end'], bonus_worst, 'bonus', gest='insulation',
target='out_worst', year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
l.append(PublicPolicy(data['name'], data['start'], data['end'], heater, 'subsidy_target', gest='heater',
year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
l.append(PublicPolicy(data['name'], data['start'], data['end'], insulation, 'subsidy_target', gest='insulation',
target=data.get('target'), year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
return l
def read_mpr_serenite(data):
"""Create MPR Serenite PublicPolicy instance.
MaPrimeRénov' Sérénité (formerly Habiter Mieux Sérénité) for major energy renovation work in your home.
To do so, your work must result in an energy gain of at least 35%.
The amount of the bonus varies according to the amount of your resources.
Parameters
----------
data
Returns
-------
list
"""
l = list()
value = get_series(data['insulation'])
cap = None
if data.get('cap') is not None:
cap = get_series(data['cap'])
if data.get('growth_insulation'):
growth_insulation = get_series(data['growth_insulation'])
value = {k: i * value for k, i in growth_insulation.items()}
cap = {k: i * cap for k, i in growth_insulation.items()}
l.append(PublicPolicy(data['name'], data['start'], data['end'], value, data['policy'],
target=data.get('target'), gest='insulation', non_cumulative=data.get('non_cumulative'),
cap=cap, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
if data.get('bonus') is not None:
bonus = get_series(data['bonus'])
l.append(PublicPolicy(data['name'], data['start'], data['end'], bonus, 'bonus', gest='insulation',
year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
return l
def read_cee(data):
l = list()
if isinstance(data['value'], str):
cee_value = get_series(data['value'])
elif isinstance(data['value'], (int, float)):
cee_value = pd.Series(data['value'], index=range(data['start'], data['end'] + 1))
else:
raise NotImplemented
cumac_heater = get_series(data['cumac_heater'])
cee_heater = cumac_heater * cee_value / 1000
cee_heater = cee_heater.unstack('Heating system final')
cee_heater = {y: cee_heater.loc[cee_heater.index.get_level_values('Year') == y, :].droplevel('Year') for y in
cee_heater.index.get_level_values('Year').unique()}
cumac_insulation = get_series(data['cumac_insulation'])
cee_insulation = cumac_insulation * cee_value / 1000
cee_insulation = cee_insulation.unstack('Insulation').rename_axis(None, axis=1)
cee_insulation = {y: cee_insulation.loc[cee_insulation.index.get_level_values('Year') == y, :].squeeze() for y
in cee_insulation.index.get_level_values('Year').unique()}
bonus_heater = get_series(data['bonus_heater']['value']).unstack('Heating system final')
bonus_heater = bonus_heater.reindex(cee_heater[data['start']].columns, axis=1).fillna(0)
end = min(data['bonus_heater']['end'], data['end'])
l.append(PublicPolicy('cee', data['bonus_heater']['start'], end, bonus_heater, 'bonus', gest='heater',
social_housing=True, year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
bonus_insulation = get_pandas(data['bonus_insulation']['value'], lambda x: pd.read_csv(x, index_col=[0]))
end = min(data['bonus_heater']['end'], data['end'])
l.append(PublicPolicy('cee', data['bonus_insulation']['start'], end, bonus_insulation, 'bonus',
gest='insulation', social_housing=True, year_stop=data.get('year_stop'),
years_stop=data.get('years_stop')))
l.append(PublicPolicy('cee', data['start'], data['end'], cee_heater, 'subsidy_target', gest='heater',
social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
l.append(PublicPolicy('cee', data['start'], data['end'], cee_insulation, 'subsidy_target', gest='insulation',
social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
coefficient_obligation = get_pandas(data['coefficient_obligation'], lambda x: pd.read_csv(x, index_col=[0])).rename_axis('Energy', axis=1)
cee_tax = (coefficient_obligation.T * cee_value).T / 1000
end = data['end']
if data.get('year_stop') is not None:
end = data['year_stop']
cee_tax = cee_tax.loc[data['start']:end - 1, :]
if data.get('years_stop') is not None:
if data['years_stop']:
cee_tax.loc[data['years_stop'], :] = 0
l.append(PublicPolicy('cee', data['start'], end, cee_tax, 'tax'))
return l
def read_cap(data):
l = list()
if 'insulation' in data.keys():
l.append(PublicPolicy('subsidies_cap', data['start'], data['end'], get_series(data['insulation']),
'subsidies_cap', gest='insulation', target=data.get('target_insulation')))
if 'heater' in data.keys():
l.append(PublicPolicy('subsidies_cap', data['start'], data['end'], get_series(data['heater']),
'subsidies_cap', gest='heater', target=data.get('target_heater')))
return l
def read_carbon_tax(data):
tax = get_series(data['tax'], header=None)
emission = get_pandas(data['emission'], lambda x: pd.read_csv(x, index_col=[0]).squeeze())
tax = (tax * emission.T).T.fillna(0) / 10 ** 6
tax = tax.loc[(tax != 0).any(axis=1)]
recycling = None
if data.get('recycling') is not None:
if isinstance(data['recycling'], str):
recycling = get_series(data['recycling'], header=0)
else:
recycling = data['recycling']
end = data['end']
if data.get('year_stop') is not None:
end = data['year_stop']
tax = tax.loc[data['start']:end - 1, :]
if data.get('years_stop') is not None:
if data['years_stop']:
tax.loc[data['years_stop'], :] = 0
return [PublicPolicy('carbon_tax', data['start'], end, tax, 'tax',
recycling=recycling, recycling_ini=data.get('recycling_ini'))]
def read_cite(data):
"""Creates the income tax credit PublicPolicy instance.
Oil fuel-Performant Boiler exempted.
Parameters
----------
data
Returns
-------
list
"""
l = list()
if data['heater'] is not None:
heater = get_series(data['heater']).unstack('Heating system final')
l.append(PublicPolicy('cite', data['start'], data['end'], heater, 'subsidy_ad_valorem', gest='heater',
cap=data['cap'], year_stop=data.get('year_stop'),
years_stop=data.get('years_stop'))
)
if data['insulation'] is not None:
insulation = get_pandas(data['insulation'], lambda x: pd.read_csv(x, index_col=[0]))
l.append(
PublicPolicy('cite', data['start'], data['end'], insulation, 'subsidy_ad_valorem', gest='insulation',
cap=data['cap'], target=data.get('target'), year_stop=data.get('year_stop'),
years_stop=data.get('years_stop'))
)
return l
def read_zil(data):
l = list()
if isinstance(data['gest'], str):
data['gest'] = [data['gest']]
for gest in data['gest']:
l.append(PublicPolicy('zero_interest_loan', data['start'], data['end'], data['value'], 'zero_interest_loan',
cost_max=data.get('cost_max'), gest=gest, duration=data['duration'],
year_stop=data.get('year_stop'), years_stop=data.get('years_stop'),
public_cost=data.get('public_cost'), target=data.get('target')))
return l
def read_reduced_vat(data):
l = list()
l.append(PublicPolicy('reduced_vat', data['start'], data['end'], data['value'], 'reduced_vat', gest='heater',
social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
l.append(
PublicPolicy('reduced_vat', data['start'], data['end'], data['value'], 'reduced_vat', gest='insulation',
social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
return l
def read_ad_valorem(data):
l = list()
value = data['value']
if isinstance(value, str):
value = get_series(data['value'])
by = 'index'
if data.get('index') is not None:
mask = get_series(data['index'], header=0)
value *= mask
if data.get('columns') is not None:
mask = get_pandas(data['columns'], lambda x: pd.read_csv(x, index_col=[0]).squeeze()).rename(None)
if isinstance(value, (float, int)):
value *= mask
else:
value = value.to_frame().dot(mask.to_frame().T)
by = 'columns'
if data.get('growth'):
growth = get_series(data['growth'], header=None)
value = {k: i * value for k, i in growth.items()}
name = 'sub_ad_valorem'
if data.get('name') is not None:
name = data['name']
if isinstance(data['gest'], str):
data['gest'] = [data['gest']]
for gest in data['gest']:
l.append(PublicPolicy(name, data['start'], data['end'], value, 'subsidy_ad_valorem',
gest=gest, by=by, target=data.get('target'), cap=data.get('cap'),
year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
return l
def read_proportional(data):
l = list()
value = data['value']
if isinstance(value, str):
value = get_series(data['value'])
by = 'index'
if data.get('index') is not None:
mask = get_series(data['index'], header=0)
value *= mask
if data.get('columns') is not None:
mask = get_pandas(data['columns'], lambda x: pd.read_csv(x, index_col=[0]).squeeze()).rename(None)
if isinstance(value, (float, int)):
value *= mask
else:
value = value.to_frame().dot(mask.to_frame().T)
by = 'columns'
if data.get('growth'):
growth = get_series(data['growth'], header=None)
value = {k: i * value for k, i in growth.items()}
name = 'sub_proportional'
if data.get('name') is not None:
name = data['name']
proportional = 'MWh_cumac' # tCO2_cumac
if data.get('proportional') is not None:
proportional = data['proportional']
if isinstance(data['gest'], str):
data['gest'] = [data['gest']]
for gest in data['gest']:
l.append(PublicPolicy(name, data['start'], data['end'], value, 'subsidy_proportional',
gest=gest, by=by, target=data.get('target'), proportional=proportional,
year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
return l
def restriction_energy(data):
return [PublicPolicy(data['name'], data['start'], data['end'], data['value'],
'restriction_energy', gest='heater', target=data.get('target'),
variable=data.get('variable', True))]
def restriction_heater(data):
return [PublicPolicy(data['name'], data['start'], data['end'], data['value'],
'restriction_heater', gest='heater', target=data.get('target'),
variable=data.get('variable', True))]
def premature_heater(data):
return [PublicPolicy(data['name'], data['start'], data['end'], data['value'],
'premature_heater', gest='heater', target=data.get('target'))]
def read_obligation(data):
l = list()
banned_performance = get_pandas(data['value'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze()).dropna()
start = min(banned_performance.index)
if data['start'] > start:
start = data['start']
frequency = data['frequency']
if frequency is not None:
frequency = pd.Series(frequency['value'], index=pd.Index(frequency['index'], name=frequency['name']))
target = None
if data.get('target') is not None:
target = pd.Series(True, index=pd.Index(data['target']['value'], name=data['target']['name']))
end = data['end']
if data.get('year_stop') is not None:
end = data['year_stop']
if data.get('years_stop') is not None:
end = data['years_stop'][0]
l.append(PublicPolicy(data['name'], start, end, banned_performance, 'obligation',
gest='insulation', frequency=frequency, intensive=data['intensive'],
min_performance=data['minimum_performance'], target=target))
if data.get('sub_obligation') is not None:
value = get_pandas(data['sub_obligation'], lambda x: pd.read_csv(x, index_col=[0]).squeeze())
l.append(PublicPolicy('sub_obligation', start, data['end'], value, 'subsidy_ad_valorem',
gest='insulation'))
return l
def read_regulation(data):
return [PublicPolicy(data['name'], data['start'], data['end'], None, 'regulation', gest=data['gest'])]
read = {'mpr': read_mpr,
'mpr_variant': read_mpr,
'mpr_efficacite': read_mpr,
'mpr_serenite': read_mpr_serenite,
'mpr_performance': read_mpr_serenite,
'mpr_serenite_variant': read_mpr_serenite,
'mpr_serenite_high_income': read_mpr_serenite,
'mpr_serenite_low_income': read_mpr_serenite,
'mpr_multifamily': read_mpr_serenite,
"mpr_multifamily_updated": read_mpr_serenite,
'mpr_multifamily_deep': read_mpr_serenite,
'mpr_serenite_multifamily_variant': read_mpr_serenite,
'cee': read_cee,
'cee_variant': read_cee,
'cap': read_cap,
'cap_updated': read_cap,
'cap_variant': read_cee,
'carbon_tax': read_carbon_tax,
'carbon_tax_variant': read_carbon_tax,
'cite': read_cite,
'cite_insulation': read_cite,
'cite_heater': read_cite,
'reduced_vat': read_reduced_vat,
'reduced_vat_variant': read_reduced_vat,
'zero_interest_loan': read_zil}
list_policies = list()
for key, item in config['policies'].items():
item['name'] = key
if key in read.keys():
list_policies += read[key](item)
else:
if item.get('policy') == 'subsidy_ad_valorem':
list_policies += read_ad_valorem(item)
elif item.get('policy') == 'subsidy_proportional':
list_policies += read_proportional(item)
elif item.get('policy') == 'zero_interest_loan':
list_policies += read_zil(item)
elif item.get('policy') == 'premature_heater':
list_policies += premature_heater(item)
elif item.get('policy') == 'restriction_energy':
list_policies += restriction_energy(item)
elif item.get('policy') == 'restriction_heater':
list_policies += restriction_heater(item)
elif item.get('policy') == 'obligation':
list_policies += read_obligation(item)
elif item.get('policy') == 'regulation':
list_policies += read_regulation(item)
else:
print('{} reading function is not implemented'.format(key))
policies_heater = [p for p in list_policies if p.gest == 'heater']
policies_insulation = [p for p in list_policies if p.gest == 'insulation']
taxes = [p for p in list_policies if p.policy == 'tax']
return policies_heater, policies_insulation, taxes
def read_inputs(config, other_inputs=generic_input):
"""Read all inputs in Python object and concatenate in one dict.
Parameters
----------
config: dict
Configuration dictionary with path to data.
other_inputs: dict
Other inputs that are manually inserted in param.py
Returns
-------
dict
"""
inputs = dict()
idx = range(config['start'], config['end'])
inputs.update(other_inputs)
if isinstance(config['energy']['energy_prices'], str):
energy_prices = get_pandas(config['macro']['energy_prices'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year').rename_axis('Energy', axis=1))
elif isinstance(config['energy']['energy_prices'], dict):
energy_prices = get_pandas(config['energy']['energy_prices']['ini'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year').rename_axis('Energy', axis=1))
energy_prices = energy_prices.loc[config['start'], :]
rate = Series(config['energy']['energy_prices']['rate']).rename_axis('Energy')
if config['energy']['energy_prices'].get('factor') is not None:
rate *= config['energy']['energy_prices']['factor']
temp = range(config['start'] + 1, 2051)
rate = concat([(1 + rate) ** n for n in range(len(temp))], axis=1, keys=temp)
rate = concat((pd.Series(1, index=rate.index, name=config['start']), rate), axis=1)
energy_prices = rate.T * energy_prices
if config['energy']['energy_prices'].get('shock') is not None:
temp = config['energy']['energy_prices']['shock']
energy_prices.loc[temp['start']:, :] *= temp['factor']
else:
raise NotImplemented
inputs.update({'energy_prices': energy_prices})
energy_taxes = get_pandas(config['energy']['energy_taxes'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year').rename_axis('Heating energy', axis=1))
inputs.update({'energy_taxes': energy_taxes})
inputs.update({'energy_vat': get_series(config['energy']['energy_vat'], header=None)})
inputs.update({'cost_heater': get_series(config['technical']['cost_heater'], header=[0])})
inputs.update({'efficiency': get_series(config['technical']['efficiency'], header=[0])})
inputs.update({'temp_sink': get_series(config['technical']['temp_sink'], header=None)})
inputs.update({'lifetime_heater': get_series(config['technical']['lifetime_heater'], header=[0])})
inputs.update({'cost_insulation': get_series(config['technical']['cost_insulation'], header=[0])})
inputs.update({'lifetime_insulation': config['renovation']['lifetime_insulation']})
inputs.update({'performance_insulation_renovation': get_series(config['technical']['performance_insulation_renovation'], header=None).to_dict()})
inputs.update({'performance_insulation_construction': get_series(config['technical']['performance_insulation_construction'], header=None).to_dict()})
"""bill_saving_preferences = get_pandas(config['macro']['preferences_saving'],
lambda x: pd.read_csv(x, index_col=[0]))
preferences_insulation.update({'bill_saved': bill_saving_preferences.loc[:, 'Insulation']})
preferences_heater.update({'bill_saved': bill_saving_preferences.loc[:, 'Heater']})"""
preferences_insulation = get_series(config['renovation']['preferences_insulation'], header=None).to_dict()
preferences_insulation.update({'present_discount_rate': get_series(config['macro']['present_discount_rate'])})
preferences_heater = get_series(config['switch_heater']['preferences_heater'], header=None).to_dict()
preferences_heater.update({'present_discount_rate': get_series(config['macro']['present_discount_rate'])})
inputs.update({'preferences': {'insulation': preferences_insulation,
'heater': preferences_heater}})
consumption_ini = get_series(config['macro']['consumption_ini'], header=[0])
inputs.update({'consumption_ini': consumption_ini})
ms_heater = get_pandas(config['switch_heater']['ms_heater'], lambda x: pd.read_csv(x, index_col=[0, 1]))
ms_heater.columns.set_names('Heating system final', inplace=True)
calibration_heater = {
'ms_heater': ms_heater,
'scale': config['switch_heater']['scale']
}
inputs.update({'calibration_heater': calibration_heater})
if config['switch_heater'].get('district_heating') is not None:
district_heating = get_series(config['switch_heater']['district_heating'], header=None)
inputs.update({'flow_district_heating': district_heating.diff().fillna(0)})
calibration_renovation = None
if config['renovation']['endogenous']:
renovation_rate_ini = get_series(config['renovation']['renovation_rate_ini']).round(decimals=3)
scale_calibration = config['renovation']['scale']
ms_insulation_ini = get_pandas(config['renovation']['ms_insulation']['ms_insulation_ini'], lambda x: pd.read_csv(x, index_col=[0, 1, 2, 3]).squeeze().rename(None).round(decimals=3))
minimum_performance = config['renovation']['ms_insulation']['minimum_performance']
calibration_renovation = {'renovation_rate_ini': renovation_rate_ini,
'scale': scale_calibration,
'threshold_indicator': config['renovation'].get('threshold'),
'ms_insulation_ini': ms_insulation_ini,
'minimum_performance': minimum_performance}
inputs.update({'calibration_renovation': calibration_renovation})
if config['renovation'].get('exogenous_social'):
exogenous_social = get_pandas(config['renovation']['exogenous_social'],
lambda x: pd.read_csv(x, index_col=[0, 1]))
exogenous_social.columns = exogenous_social.columns.astype(int)
inputs.update({'exogenous_social': exogenous_social})
temp = None
if config['renovation'].get('rational_behavior') is not None:
if config['renovation']['rational_behavior']['activated']:
temp = {'calibration': config['renovation']['rational_behavior']['calibration'],
'social': config['renovation']['rational_behavior']['social'],
}
inputs.update({'rational_behavior_insulation': temp})
temp = None
if config['switch_heater'].get('rational_behavior') is not None:
if config['switch_heater']['rational_behavior']['activated']:
temp = {
'social': config['switch_heater']['rational_behavior']['social'],
}
inputs.update({'rational_behavior_heater': temp})
if config['macro'].get('population') is not None:
population = get_pandas(config['macro']['population'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
inputs.update({'population': population.loc[:config['end']]})
if config['macro'].get('stock_ini') is not None:
inputs.update({'stock_ini': config['macro']['stock_ini']})
if config['macro'].get('pop_housing') is None:
inputs.update({'pop_housing_min': other_inputs['pop_housing_min']})
inputs.update({'factor_pop_housing': other_inputs['factor_pop_housing']})
else:
pop_housing = get_pandas(config['macro']['pop_housing'],
lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
inputs.update({'pop_housing': pop_housing.loc[:config['end']]})
if config['macro'].get('share_single_family_construction') is not None:
temp = get_pandas(config['macro']['share_single_family_construction'],
lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
inputs.update({'share_single_family_construction': temp})
else:
if config['macro'].get('share_multi_family') is None:
inputs.update({'factor_multi_family': other_inputs['factor_multi_family']})
else:
_share_multi_family = get_pandas(config['macro']['share_multi_family'],
lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
inputs.update({'share_multi_family': _share_multi_family})
if config['macro']['available_income'] is not None:
inputs.update({'available_income': config['macro']['available_income']})
inputs.update({'income_rate': config['macro']['income_rate']})
income = get_series(config['macro']['income'], header=[0])
inputs.update({'income': income})
if isinstance(config['macro']['demolition_rate'], (float, int)):
demolition_rate = config['macro']['demolition_rate']
elif config['macro'].get('demolition_rate') is not None:
demolition_rate = get_series(config['macro']['demolition_rate'], header=None)
else:
demolition_rate = None
inputs.update({'demolition_rate': demolition_rate})
rotation_rate = get_pandas(config['macro']['rotation_rate'], lambda x: pd.read_csv(x, index_col=[0])).squeeze().rename(None)
inputs.update({'rotation_rate': rotation_rate})
surface = get_pandas(config['technical']['surface'], lambda x: pd.read_csv(x, index_col=[0, 1, 2]).squeeze().rename(None))
inputs.update({'surface': surface})
ratio_surface = get_pandas(config['technical']['ratio_surface'], lambda x: pd.read_csv(x, index_col=[0]))
inputs.update({'ratio_surface': ratio_surface})
if config['macro']['surface_built'] is None:
inputs.update({'surface_max': other_inputs['surface_max']})
inputs.update({'surface_elasticity': other_inputs['surface_elasticity']})
else:
surface_built = get_pandas(config['macro']['surface_built'], lambda x: pd.read_csv(x, index_col=[0]).squeeze().rename(None))
inputs.update({'surface_built': surface_built})
if config['macro'].get('flow_construction') is not None:
flow_construction = get_pandas(config['macro']['flow_construction'],
lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
inputs.update({'flow_construction': flow_construction})
temp = get_series(config['switch_heater']['ms_heater_built'], header=[0])
# Create a dictionary with the year as key and the share of each heating system as value
if 'Year' in temp.index.names:
# Interpolation over year using last known value
start = temp.index.get_level_values('Year').min()
ms_heater_built = temp.unstack('Year').reindex(range(start, config['end']), axis=1)
temp_idx = ms_heater_built.index.names
ms_heater_built = ms_heater_built.reset_index().interpolate(axis=1, method='pad').set_index(temp_idx)
# ms_heater_built = temp.unstack('Heating system').fillna(0)
inputs.update({'ms_heater_built': ms_heater_built.fillna(0)})
inputs.update({'health_cost_dpe': get_series(config['health_cost_dpe'])})
inputs.update({'health_cost_income': get_series(config['health_cost_income'])})
carbon_value = get_pandas(config['carbon_value'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
inputs.update({'carbon_value': carbon_value.loc[config['start']:]})
carbon_emission = get_pandas(config['energy']['carbon_emission'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year'))
inputs.update({'carbon_emission': carbon_emission.loc[config['start']:, :] * 1000})
renewable_gas = None
if isinstance(config['energy']['renewable_gas'], str):
renewable_gas = get_series(config['energy']['renewable_gas'], header=None)
renewable_gas = renewable_gas.loc[config['start']:]
# set carbon emission of natural gas constant
inputs['carbon_emission'].loc[:, 'Natural gas'] = inputs['carbon_emission'].loc[config['start'], 'Natural gas']
inputs.update({'renewable_gas': renewable_gas})
footprint_built = get_series(config['technical']['footprint_construction'], header=None)
inputs.update({'footprint_built': footprint_built})
footprint_renovation = get_pandas(config['technical']['footprint_renovation'], lambda x: pd.read_csv(x, index_col=[0]))
inputs.update({'footprint_renovation': footprint_renovation})
if config['macro'].get('use_subsidies'):
temp = get_pandas(config['macro']['use_subsidies'], lambda x: pd.read_csv(x, index_col=[0]).squeeze())
inputs.update({'use_subsidies': temp})
else:
inputs.update({'use_subsidies': pd.Series(dtype=float)})
if 'implicit_discount_rate' in config.keys():
inputs['implicit_discount_rate'] = get_series(config['implicit_discount_rate'])
if 'hourly_profile' in config['technical'].keys():
temp = get_series(config['technical']['hourly_profile'], header=None)
temp.index = pd.TimedeltaIndex(range(0, 24), unit='h')
inputs.update({'hourly_profile': temp})
inputs['input_financing'].update(config['financing_cost'])
if inputs['input_financing']['activated'] is False:
temp_idx = get_series(inputs['input_financing']['upfront_max']).index
inputs['input_financing']['upfront_max'] = pd.Series(100000, index=temp_idx)
inputs['input_financing']['saving_rate'] = pd.Series(0, index=idx)
inputs['input_financing']['interest_rate'] = pd.Series(0, index=idx)
else:
inputs['input_financing']['upfront_max'] = get_series(inputs['input_financing']['upfront_max'])
inputs['input_financing']['saving_rate'] = get_series(inputs['input_financing']['saving_rate'], header=None)
inputs['input_financing']['interest_rate'] = get_series(inputs['input_financing']['interest_rate'], header=None)
return inputs
def parse_inputs(inputs, taxes, config, stock):
"""Macro module : run exogenous dynamic parameters.
Parameters
----------
inputs: dict
Raw inputs read as Python object.
taxes: list
config: dict
Configuration file.
stock: Series
Building stock.
Returns
-------
dict
Parsed input
"""
idx = range(config['start'], config['end'])
parsed_inputs = copy.deepcopy(inputs)
if config['technical'].get('technical_progress') is not None:
parsed_inputs['technical_progress'] = dict()
if 'insulation' in config['technical']['technical_progress'].keys():
if config['technical']['technical_progress']['insulation']['activated']:
value = config['technical']['technical_progress']['insulation']['value_end']
start = config['technical']['technical_progress']['insulation']['start']
end = config['technical']['technical_progress']['insulation']['end']
value = round((1 + value) ** (1 / (end - start + 1)) - 1, 5)
parsed_inputs['technical_progress']['insulation'] = Series(value, index=range(start, end + 1)).reindex(idx).fillna(0)
if 'heater' in config['technical']['technical_progress'].keys():
if config['technical']['technical_progress']['heater']['activated']:
value = config['technical']['technical_progress']['heater']['value_end']
start = config['technical']['technical_progress']['heater']['start']
end = config['technical']['technical_progress']['heater']['end']
value = round((1 + value) ** (1 / (end - start + 1)) - 1, 5)
parsed_inputs['technical_progress']['heater'] = Series(value, index=range(start, end + 1)).reindex(idx).fillna(0)
if 'Year' in inputs['efficiency'].index.names:
s = inputs['efficiency'].index.get_level_values('Year').min()
df = inputs['efficiency'].copy()
df = df.unstack('Heating system').reindex(range(s, config['end'])).fillna(method='ffill')
parsed_inputs['efficiency'] = df.T
s = inputs['temp_sink'].index.min()
parsed_inputs['temp_sink'] = inputs['temp_sink'].reindex(range(s, config['end'])).fillna(method='ffill')
if isinstance(inputs['demolition_rate'], (float, int)):
parsed_inputs['demolition_rate'] = pd.Series(inputs['demolition_rate'], index=idx[1:])
elif isinstance(inputs['demolition_rate'], Series):
s = inputs['demolition_rate'].index.min()
parsed_inputs['demolition_rate'] = inputs['demolition_rate'].reindex(range(s, config['end'])).fillna(method='ffill')
parsed_inputs['demolition_rate'] = parsed_inputs['demolition_rate'].loc[idx[1:]]
if parsed_inputs['demolition_rate'] is None:
parsed_inputs['flow_demolition'] = 0
else:
parsed_inputs['flow_demolition'] = parsed_inputs['demolition_rate'] * stock.sum()
if 'flow_construction' not in parsed_inputs.keys():
if 'population' in inputs.keys():
parsed_inputs['population_total'] = inputs['population']
parsed_inputs['sizing_factor'] = stock.sum() / inputs['stock_ini']
parsed_inputs['population'] = inputs['population'] * parsed_inputs['sizing_factor']
if 'pop_housing' in parsed_inputs.keys():
parsed_inputs['stock_need'] = parsed_inputs['population'] / parsed_inputs['pop_housing']
else:
parsed_inputs['stock_need'], parsed_inputs['pop_housing'] = stock_need(parsed_inputs['population'],
parsed_inputs['population'][
config['start']] / stock.sum(),
inputs['pop_housing_min'],
config['start'],
inputs['factor_pop_housing'])
parsed_inputs['flow_need'] = parsed_inputs['stock_need'] - parsed_inputs['stock_need'].shift(1)
# if 'flow_construction' not in parsed_inputs.keys():
parsed_inputs['flow_construction'] = parsed_inputs['flow_need'] + parsed_inputs['flow_demolition']
parsed_inputs['available_income'] = pd.Series(
[inputs['available_income'] * (1 + config['macro']['income_rate']) ** (i - idx[0]) for i in idx], index=idx)
else:
s = inputs['flow_construction'].index.min()
parsed_inputs['flow_construction'] = inputs['flow_construction'].reindex(range(s, config['end'])).fillna(method='ffill')
parsed_inputs['flow_construction'] = parsed_inputs['flow_construction'].loc[idx]
parsed_inputs['surface'] = pd.concat([parsed_inputs['surface']] * len(idx), axis=1, keys=idx)
if 'share_single_family_construction' in inputs.keys():
s = inputs['share_single_family_construction'].index.min()
parsed_inputs['share_single_family_construction'] = inputs['share_single_family_construction'].reindex(range(s, config['end'])).fillna(method='ffill')
parsed_inputs['share_single_family_construction'] = parsed_inputs['share_single_family_construction'].loc[idx]
temp = pd.concat((parsed_inputs['share_single_family_construction'], (1 - parsed_inputs['share_single_family_construction'])),
axis=1, keys=['Single-family', 'Multi-family'], names=['Housing type'])
type_built = parsed_inputs['flow_construction'] * temp.T
else:
if 'share_multi_family' not in inputs.keys():
parsed_inputs['share_multi_family'] = share_multi_family(parsed_inputs['stock_need'],
inputs['factor_multi_family'])
type_built = share_type_built(parsed_inputs['stock_need'], parsed_inputs['share_multi_family'],
parsed_inputs['flow_construction']) * parsed_inputs['flow_construction']
# multiply by the rate of income from start to end
income = parsed_inputs['income'].copy()
income = pd.concat([income] * len(idx), axis=1, keys=idx, names=['Year'])
rate = pd.Series([(1 + parsed_inputs['income_rate']) ** (i - idx[0]) for i in idx], index=idx)
parsed_inputs['income'] = income * rate
share_decision_maker = stock.groupby(
['Occupancy status', 'Housing type', 'Income owner', 'Income tenant']).sum().unstack(
['Occupancy status', 'Income owner', 'Income tenant'])
share_decision_maker = (share_decision_maker.T / share_decision_maker.sum(axis=1)).T
share_decision_maker = pd.concat([share_decision_maker] * type_built.shape[1], keys=type_built.columns, axis=1)
construction = (reindex_mi(type_built, share_decision_maker.columns, axis=1) * share_decision_maker).stack(
['Occupancy status', 'Income owner', 'Income tenant']).fillna(0)
construction.rename_axis('Year', axis=1, inplace=True)
construction = construction.loc[:, idx]
construction = construction.stack()
ms_heater_built = inputs['ms_heater_built'].stack('Year').unstack('Heating system').fillna(0)
restriction = [k for k, p in config['policies'].items() if p.get('policy') in ['restriction_energy', 'restriction_heater']]
for policy in restriction:
if config['policies'][policy]['policy'] == 'restriction_energy':
heating_system = [i for i, energy in resources_data['heating2heater'].items() if energy == config['policies'][policy]['value']]
else:
heating_system = config['policies'][policy]['value']
heating_system = [i for i in heating_system if i in ms_heater_built.columns]
start_policy = config['policies'][policy]['start']
ms_heater_built.loc[ms_heater_built.index.get_level_values('Year') >= start_policy, heating_system] = 0
ms_heater_built = (ms_heater_built.T / ms_heater_built.sum(axis=1)).T
ms_heater_built = reindex_mi(ms_heater_built, construction.index)
temp = construction.copy()
construction = (reindex_mi(construction, ms_heater_built.index) * ms_heater_built.T).T
construction = construction.loc[(construction != 0).any(axis=1)]
construction = construction.stack('Heating system').unstack('Year')
assert round(temp.sum() - construction.sum().sum(), 0) == 0, 'Construction is not equal to the sum of the heating system'
construction_dh = select(construction, {'Heating system': 'Heating-District heating'}).sum()
parsed_inputs['flow_district_heating'] = parsed_inputs['flow_district_heating'] - construction_dh
parsed_inputs['flow_district_heating'][parsed_inputs['flow_district_heating'] < 0] = 0
performance_insulation = pd.concat([pd.Series(inputs['performance_insulation_construction'])] * construction.shape[0], axis=1,
keys=construction.index).T
parsed_inputs['flow_built'] = pd.concat((construction, performance_insulation), axis=1).set_index(
list(performance_insulation.keys()), append=True)
parsed_inputs['flow_built'] = pd.concat([parsed_inputs['flow_built']], keys=[False],
names=['Existing']).reorder_levels(stock.index.names)
if not config['macro']['construction']:
parsed_inputs['flow_built'][parsed_inputs['flow_built'] > 0] = 0
"""
parsed_inputs['health_expenditure'] = df['Health expenditure']
parsed_inputs['mortality_cost'] = df['Social cost of mortality']
parsed_inputs['loss_well_being'] = df['Loss of well-being']"""
parsed_inputs['carbon_value_kwh'] = (parsed_inputs['carbon_value'] * parsed_inputs['carbon_emission'].T).T.dropna() / 10**6
parsed_inputs['embodied_energy_built'] = inputs['footprint_built'].loc['Grey energy (kWh/m2)']
parsed_inputs['carbon_footprint_built'] = inputs['footprint_built'].loc['Carbon content (kgCO2/m2)']
# carbon footprint of renovation
parsed_inputs['embodied_energy_renovation'] = inputs['footprint_renovation'].loc['Grey energy (kWh/m2)', :]
parsed_inputs['carbon_footprint_renovation'] = inputs['footprint_renovation'].loc['Carbon content (kgCO2/m2)', :]
temp = parsed_inputs['surface'].xs(False, level='Existing', drop_level=True)
temp = (parsed_inputs['flow_built'].groupby(temp.index.names).sum() * temp).sum() / 10**6
parsed_inputs['Surface construction (Million m2)'] = temp
parsed_inputs['Carbon footprint construction (MtCO2)'] = (parsed_inputs['Surface construction (Million m2)'] * parsed_inputs['carbon_footprint_built']) / 10**3
parsed_inputs['Embodied energy construction (TWh PE)'] = (parsed_inputs['Surface construction (Million m2)'] * parsed_inputs['embodied_energy_built']) / 10**3
energy_prices = parsed_inputs['energy_prices'].copy()
parsed_inputs['energy_prices_wt'] = energy_prices.copy()
energy_taxes = parsed_inputs['energy_taxes'].copy()
if config['simple']['prices_constant']:
energy_prices = pd.concat([energy_prices.loc[config['start'], :]] * energy_prices.shape[0], keys=energy_prices.index,
axis=1).T
total_taxes = pd.DataFrame(0, index=energy_prices.index, columns=energy_prices.columns)
export_prices = dict()
export_prices.update({'energy_prices': energy_prices})
for t in taxes:
total_taxes = total_taxes.add(t.value, fill_value=0)
export_prices.update({t.name: t.value})
if energy_taxes is not None:
total_taxes = total_taxes.add(energy_taxes, fill_value=0)
taxes += [PublicPolicy('energy_taxes', energy_taxes.index[0], energy_taxes.index[-1], energy_taxes, 'tax')]
export_prices.update({'energy_taxes': energy_taxes})
if config['simple']['prices_constant']:
total_taxes = pd.concat([total_taxes.loc[config['start'], :]] * total_taxes.shape[0], keys=total_taxes.index,
axis=1).T
# energy_vat = energy_prices * (inputs['energy_vat'] / (1 - inputs['energy_vat']))
energy_vat = energy_prices * inputs['energy_vat']
export_prices.update({'energy_vat': energy_vat})
taxes += [PublicPolicy('energy_vat', energy_vat.index[0], energy_vat.index[-1], energy_vat, 'tax')]
total_taxes += energy_vat
parsed_inputs['taxes'] = taxes
parsed_inputs['total_taxes'] = total_taxes
energy_prices = energy_prices.add(total_taxes, fill_value=0)
parsed_inputs['energy_prices'] = energy_prices
export_prices = reverse_dict({k: item.to_dict() for k, item in export_prices.items()})
export_prices = concat([pd.DataFrame(item) for k, item in export_prices.items()], axis=1, keys=export_prices.keys())
parsed_inputs.update({'export_prices': export_prices})
supply = {'insulation': None, 'heater': None}
if config.get('supply') is not None:
if config['supply']['activated_insulation']:
supply.update({'insulation': {'markup_insulation': config['supply']['markup_insulation']}})
if config['supply']['activated_heater']:
supply.update({'heater': {'markup_heater': config['supply']['markup_heater']}})
parsed_inputs.update({'supply': supply})
premature_replacement = None
if config['switch_heater'].get('premature_replacement') is not None:
premature_replacement = {'time': config['switch_heater']['premature_replacement'],
'information_rate': config['switch_heater']['information_rate']}
parsed_inputs.update({'premature_replacement': premature_replacement})
return parsed_inputs
def dump_inputs(parsed_inputs, path, figures=None):
"""Create summary input DataFrame.
Parameters
----------
parsed_inputs: dict
Returns
-------
DataFrame
"""
summary_input = dict()
if 'sizing_factor' in parsed_inputs.keys():
summary_input['Sizing factor (%)'] = pd.Series(parsed_inputs['sizing_factor'], index=parsed_inputs['population'].index)
summary_input['Total population (Millions)'] = parsed_inputs['population'] / 10**6
summary_input['Income (Billions euro)'] = parsed_inputs['available_income'] * parsed_inputs['sizing_factor'] / 10**9
summary_input['Buildings stock (Millions)'] = parsed_inputs['stock_need'] / 10**6
summary_input['Person by housing'] = parsed_inputs['pop_housing']
summary_input['Buildings additional (Thousands)'] = parsed_inputs['flow_need'] / 10**3
summary_input['Buildings built (Thousands)'] = parsed_inputs['flow_construction'] / 10**3
summary_input['Buildings demolished (Thousands)'] = parsed_inputs['flow_demolition'] / 10**3
temp = parsed_inputs['surface'].xs(True, level='Existing', drop_level=True)
temp.index = temp.index.map(lambda x: 'Surface existing {} - {} (m2/dwelling)'.format(x[0], x[1]))
summary_input.update(temp.T)
temp = parsed_inputs['surface'].xs(False, level='Existing', drop_level=True)
temp.index = temp.index.map(lambda x: 'Surface construction {} - {} (m2/dwelling)'.format(x[0], x[1]))
summary_input.update(temp.T)
summary_input['Surface construction (Million m2)'] = parsed_inputs['Surface construction (Million m2)']
summary_input['Carbon footprint construction (MtCO2)'] = parsed_inputs['Carbon footprint construction (MtCO2)']
summary_input['Embodied energy construction (TWh PE)'] = parsed_inputs['Embodied energy construction (TWh PE)']
summary_input = pd.DataFrame(summary_input)
t = parsed_inputs['total_taxes'].copy()
t.columns = t.columns.map(lambda x: 'Taxes {} (euro/kWh)'.format(x))
temp = parsed_inputs['energy_prices'].copy()
if figures is not False:
make_plot(temp.dropna(), 'Prices (euro/kWh)', format_y=lambda y, _: '{:.2f}'.format(y),
colors=resources_data['colors'], save=os.path.join(path, 'energy_prices.png'))
temp.columns = temp.columns.map(lambda x: 'Prices {} (euro/kWh)'.format(x))
summary_input = pd.concat((summary_input, t, temp), axis=1)
temp = parsed_inputs['income'].copy()
temp.index = temp.index.map(lambda x: 'Income {} (euro/year)'.format(x))
summary_input = pd.concat((summary_input, temp.T), axis=1)
summary_input.T.round(3).to_csv(os.path.join(path, 'input.csv'))
parsed_inputs['export_prices'].round(4).to_csv(os.path.join(path, 'energy_prices.csv'))
return summary_input
def dict2data_inputs(inputs):
"""Grouped all inputs in the same DataFrame.
Process is useful to implement a global sensitivity analysis.
Returns
-------
DataFrame
"""
data = DataFrame(columns=['variables', 'index', 'value'])
metadata = DataFrame(columns=['variables', 'type', 'name', 'index', 'columns'])
for key, item in inputs.items():
i = True
if isinstance(item, dict):
metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__})), axis=1).T
i = False
item = Series(item)
if isinstance(item, (float, int)):
data = concat((data.T, Series({'variables': key, 'value': item})), axis=1).T
metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__})), axis=1).T
if isinstance(item, DataFrame):
metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__,
'index': item.index.names.copy(),
'columns': item.columns.names.copy()})), axis=1).T
i = False
item = item.stack(item.columns.names)
if isinstance(item, Series):
if i:
metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__, 'name': item.name,
'index': item.index.names.copy()})), axis=1).T
if isinstance(item.index, MultiIndex):
item.index = item.index.to_flat_index()
item.index = item.index.rename('index')
df = concat([item.rename('value').reset_index()], keys=[key], names=['variables']).reset_index('variables')
data = concat((data, df), axis=0)
data = data.astype({'variables': 'string', 'value': 'float64'})
data.reset_index(drop=True, inplace=True)
return data
def data2dict_inputs(data, metadata):
"""Parse aggregate data pandas and return dict fill with several inputs.
Parameters
----------
data: DataFrame
Model data input.
metadata: DataFrame
Additional information to find out how to parse data.
Returns
-------
dict
"""
def parse_index(n, index_values):
if len(n) == 1:
idx = Index(index_values, name=n[0])
else:
idx = MultiIndex.from_tuples(index_values)
idx.names = n
return idx
parsed_input = dict()
for variables, df in data.groupby('variables'):
meta = metadata[metadata['variables'] == variables]
if meta['type'].iloc[0] == 'int':
parsed_input.update({variables: int(df['value'].iloc[0])})
elif meta['type'].iloc[0] == 'float':
parsed_input.update({variables: float(df['value'].iloc[0])})
elif meta['type'].iloc[0] == 'Series':
idx = parse_index(meta['index'].iloc[0], df['index'].values)
parsed_input.update({variables: Series(df['value'].values, name=str(meta['name'].iloc[0]), index=idx)})
elif meta['type'].iloc[0] == 'DataFrame':
idx = parse_index(meta['index'].iloc[0] + meta['columns'].iloc[0], df['index'].values)
parsed_input.update({variables: Series(df['value'].values, name=str(meta['name'].iloc[0]), index=idx).unstack(
meta['columns'].iloc[0])})
elif meta['type'].iloc[0] == 'dict':
parsed_input.update({variables: Series(df['value'].values, index=df['index'].values).to_dict()})
return parsed_input
def create_simple_policy(start, end, value=0.3, gest='insulation'):
return PublicPolicy('sub_ad_valorem', start, end, value, 'subsidy_ad_valorem',
gest=gest)
\ No newline at end of file
+# 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 .
+#
+# Original author Lucas Vivier
+
+import copy
+import os
+import pandas as pd
+from pandas import Series, DataFrame, concat, MultiIndex, Index
+from numpy.random import normal
+from numpy.testing import assert_almost_equal
+
+from project.utils import reindex_mi, get_pandas, make_plot, get_series, reverse_dict, select
+from project.dynamic import stock_need, share_multi_family, share_type_built
+from project.input.param import generic_input
+from project.input.resources import resources_data
+
+
+class PublicPolicy:
+ """Public policy parent class.
+
+ Attributes
+ ----------
+ name : str
+ Name of the policy.
+ start : int
+ Year policy starts.
+ end : int
+ Year policy ends.
+ value: float
+ policy : {'energy_taxes', 'subsidies'}
+ """
+ def __init__(self, name, start, end, value, policy, gest=None, cap=None, target=None, cost_min=None, cost_max=None,
+ new=None, by='index', non_cumulative=None, frequency=None, intensive=None, min_performance=None,
+ bonus=False, social_housing=True, duration=None, recycling=None, proportional=None,
+ recycling_ini=None, year_stop=None, years_stop=None, public_cost=None, variable=True):
+ self.name = name
+ self.start = start
+ self.end = end
+ self.value = value
+ self.policy = policy
+ self.gest = gest
+ self.cap = cap
+ self.target = target
+ self.cost_max = cost_max
+ self.cost_min = cost_min
+ self.new = new
+ self.by = by
+ self.non_cumulative = non_cumulative
+ self.frequency = frequency
+ self.intensive = intensive
+ self.min_performance = min_performance
+ self.bonus = bonus
+ self.social_housing = social_housing
+ self.duration = duration
+ self.recycling = recycling
+ self.recycling_ini = recycling_ini
+ self.proportional = proportional
+ self.public_cost = public_cost
+ self.year_stop = year_stop
+ self.years_stop = years_stop
+ self.variable = variable
+
+ self.incentive = False
+ if policy in ['subsidy_ad_valorem', 'subsidy_target', 'subsidy_proportional', 'bonus', 'reduced_vat',
+ 'zero_interest_loan']:
+ self.incentive = True
+
+ if (year_stop or years_stop) and self.incentive:
+ if not isinstance(value, dict):
+ self.value = {k: value for k in range(self.start, self.end)}
+
+ if year_stop:
+ if self.incentive:
+ if year_stop < end:
+ self.apply_year_stop(year_stop, self.value)
+ else:
+ self.end = min(year_stop, end)
+
+ if years_stop:
+ if self.incentive:
+ for y in years_stop:
+ if y < end:
+ self.apply_year_stop(y, self.value)
+ else:
+ self.end = min(years_stop[0], end)
+
+ def __repr__(self):
+ return self.name
+
+ def __str__(self):
+ return self.name
+
+ def cost_targeted(self, cost_insulation, target_subsidies=None):
+ """
+ Gives the amount of the cost of a gesture for a segment over which the subvention applies.
+
+ If self.new, cost global is the amount loaned for gestures which are considered as 'global renovations',
+ and thus caped by the proper maximum zil amount taking the heater replacement into account.
+ Also, cost_no_global are the amount loaned for unique or bunch renovations actions.
+
+
+ Parameters
+ ----------
+ cost_insulation: pd.DataFrame
+ Cost of an insulation gesture
+ target_subsidies: pd.DataFrame
+ Boolean values. If self.new it corresponds to the global renovations
+
+
+ Returns
+ -------
+ cost: pd.DataFrame
+ Each cell of the DataFrame corresponds to the cost after subventions of a specific gesture and segment
+ """
+ cost = cost_insulation.copy()
+ if self.target is not None and target_subsidies is not None:
+ if isinstance(target_subsidies, Series):
+ target_subsidies = pd.concat([target_subsidies] * cost.shape[1], axis=1, keys=cost.columns)
+ cost = cost[target_subsidies.astype(bool)].fillna(0)
+ if self.cost_max is not None:
+ cost_max = reindex_mi(self.cost_max, cost.index)
+ cost_max = pd.concat([cost_max] * cost.shape[1], axis=1).set_axis(cost.columns, axis=1)
+ cost[cost > cost_max] = cost_max
+ if self.cost_min is not None:
+ cost_min = reindex_mi(self.cost_min, cost.index)
+ cost_min = pd.concat([cost_min] * cost.shape[1], axis=1).set_axis(
+ cost.columns, axis=1)
+ cost[cost < cost_min] = 0
+ return cost
+
+ def apply_year_stop(self, year_stop, value):
+ """Put values of an incentive to 1e-4 to assess the impact of removing the incentive.
+
+
+ Rationale is to keep calculating the number of eligible households that renovate without the incentive.
+
+ Parameters
+ ----------
+ year_stop
+ value
+
+ Returns
+ -------
+
+ """
+ if self.policy == 'zero_interest_loan':
+ if not isinstance(self.cost_max, dict):
+ self.cost_max = {k: self.cost_max for k in range(self.start, self.end)}
+ self.cost_max[year_stop] = 1e-4
+
+ else:
+ if self.policy == 'reduced_vat':
+ val = 0.1 - 1e-4
+ else:
+ val = 1e-4
+
+ if isinstance(value[year_stop], (pd.Series, pd.DataFrame)):
+ temp = value[year_stop].mask(value[year_stop] > 0, val)
+ else:
+ temp = val
+
+ value[year_stop] = temp
+ self.value = value
+
+
+def read_stock(config):
+ """Read initial building stock.
+
+ Parameters
+ ----------
+ config: dict
+
+ Returns
+ -------
+ pd.Series
+ MultiIndex Series with building stock attributes as levels.
+ """
+
+ stock = get_pandas(config['building_stock'], lambda x: pd.read_csv(x, index_col=[0, 1, 2, 3, 4, 5, 6, 7, 8]).squeeze()).rename('Stock buildings')
+ stock_sum = stock.sum()
+
+ stock = stock.reset_index('Heating system')
+
+ # replace wood fuel multi-family by natural gas in building stock
+ idx = (stock['Heating system'] == 'Wood fuel-Standard boiler') & (stock.index.get_level_values('Housing type') == 'Multi-family')
+ stock.loc[idx, 'Heating system'] = 'Natural gas-Standard boiler'
+ idx = (stock['Heating system'] == 'Wood fuel-Performance boiler') & (stock.index.get_level_values('Housing type') == 'Multi-family')
+ stock.loc[idx, 'Heating system'] = 'Natural gas-Performance boiler'
+
+ assert_almost_equal(stock['Stock buildings'].sum(), stock_sum)
+
+ # specify heat-pump
+ repartition = 0.8
+ idx = stock['Heating system'] == 'Electricity-Heat pump'
+ hp_water = stock.loc[idx, :].copy()
+ hp_water['Stock buildings'] *= repartition
+ hp_water['Heating system'] = hp_water['Heating system'].str.replace('Electricity-Heat pump', 'Electricity-Heat pump water')
+
+ hp_air = stock.loc[idx, :].copy()
+ hp_air['Stock buildings'] *= (1 - repartition)
+ hp_air['Heating system'] = hp_air['Heating system'].str.replace('Electricity-Heat pump', 'Electricity-Heat pump air')
+ stock = stock.loc[~idx, :]
+ stock = pd.concat((stock, hp_water, hp_air), axis=0)
+
+ if config['simple'].get('collective_boiler'):
+
+ multi_family = stock.index.get_level_values('Housing type') == 'Multi-family'
+ oil_fuel = stock['Heating system'].isin(['Oil fuel-Performance boiler', 'Oil fuel-Standard boiler'])
+ stock.loc[multi_family & oil_fuel, 'Heating system'] = 'Oil fuel-Collective boiler'
+
+ repartition = 0.5
+ idx_gas = stock['Heating system'].isin(['Natural gas-Performance boiler', 'Natural gas-Standard boiler']) & multi_family
+ collective_gas = stock.loc[idx_gas, :].copy()
+ collective_gas['Stock buildings'] *= repartition
+ collective_gas['Heating system'] = collective_gas['Heating system'].str.replace('Natural gas-Performance boiler', 'Natural gas-Collective boiler')
+ collective_gas['Heating system'] = collective_gas['Heating system'].str.replace('Natural gas-Standard boiler', 'Natural gas-Collective boiler')
+ individual_gas = stock.loc[idx_gas, :].copy()
+ individual_gas['Stock buildings'] *= (1 - repartition)
+ stock = stock.loc[~idx_gas, :]
+ stock = pd.concat((stock, collective_gas, individual_gas), axis=0)
+
+ stock = stock.set_index('Heating system', append=True).squeeze()
+ stock = stock.groupby(stock.index.names).sum()
+ stock = pd.concat([stock], keys=[True], names=['Existing'])
+ idx_names = ['Existing', 'Occupancy status', 'Income owner', 'Income tenant', 'Housing type',
+ 'Heating system', 'Wall', 'Floor', 'Roof', 'Windows']
+
+ stock = stock.reorder_levels(idx_names)
+ assert_almost_equal(stock.sum(), stock_sum)
+
+ return stock
+
+
+def read_policies(config):
+
+ # TODO: replace names to clarify the definition of policies (index = households, columns = technologies).
+ # TODO: target should be a list with combination of simple condition.
+ def read_mpr(data):
+ l = list()
+ heater = get_pandas(data['heater'],
+ lambda x: pd.read_csv(x, index_col=[0, 1]).squeeze().unstack('Heating system final'))
+ if data.get('growth_heater'):
+ growth_heater = get_pandas(data['growth_heater'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ heater = {k: i * heater for k, i in growth_heater.items()}
+
+ insulation = get_pandas(data['insulation'], lambda x: pd.read_csv(x, index_col=[0, 1]))
+ if data.get('growth_insulation'):
+ growth_insulation = get_pandas(data['growth_insulation'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ insulation = {k: i * insulation for k, i in growth_insulation.items()}
+
+ if data.get('deep_renovation'):
+ deep_renovation = get_pandas(data['deep_renovation'],
+ lambda x: pd.read_csv(x, index_col=[0]).squeeze())
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], deep_renovation, 'subsidy_target',
+ gest='insulation', target='deep_renovation', year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+
+ if data['bonus']:
+ bonus_best = get_pandas(data['bonus'], lambda x: pd.read_csv(x, index_col=[0, 1]).squeeze())
+ bonus_worst = get_pandas(data['bonus'], lambda x: pd.read_csv(x, index_col=[0, 1]).squeeze())
+
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], bonus_best, 'bonus', gest='insulation',
+ target='reach_best', year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], bonus_worst, 'bonus', gest='insulation',
+ target='out_worst', year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], heater, 'subsidy_target', gest='heater',
+ year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], insulation, 'subsidy_target', gest='insulation',
+ target=data.get('target'), year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+
+ return l
+
+ def read_mpr_serenite(data):
+ """Create MPR Serenite PublicPolicy instance.
+
+ MaPrimeRénov' Sérénité (formerly Habiter Mieux Sérénité) for major energy renovation work in your home.
+ To do so, your work must result in an energy gain of at least 35%.
+ The amount of the bonus varies according to the amount of your resources.
+
+ Parameters
+ ----------
+ data
+
+ Returns
+ -------
+ list
+ """
+ l = list()
+
+ value = get_series(data['insulation'])
+ cap = None
+ if data.get('cap') is not None:
+ cap = get_series(data['cap'])
+
+ if data.get('growth_insulation'):
+ growth_insulation = get_series(data['growth_insulation'])
+ value = {k: i * value for k, i in growth_insulation.items()}
+ cap = {k: i * cap for k, i in growth_insulation.items()}
+
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], value, data['policy'],
+ target=data.get('target'), gest='insulation', non_cumulative=data.get('non_cumulative'),
+ cap=cap, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+
+ if data.get('bonus') is not None:
+ bonus = get_series(data['bonus'])
+ l.append(PublicPolicy(data['name'], data['start'], data['end'], bonus, 'bonus', gest='insulation',
+ year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+ return l
+
+ def read_cee(data):
+ l = list()
+ if isinstance(data['value'], str):
+ cee_value = get_series(data['value'])
+ elif isinstance(data['value'], (int, float)):
+ cee_value = pd.Series(data['value'], index=range(data['start'], data['end'] + 1))
+ else:
+ raise NotImplemented
+
+ cumac_heater = get_series(data['cumac_heater'])
+ cee_heater = cumac_heater * cee_value / 1000
+ cee_heater = cee_heater.unstack('Heating system final')
+ cee_heater = {y: cee_heater.loc[cee_heater.index.get_level_values('Year') == y, :].droplevel('Year') for y in
+ cee_heater.index.get_level_values('Year').unique()}
+
+ cumac_insulation = get_series(data['cumac_insulation'])
+ cee_insulation = cumac_insulation * cee_value / 1000
+ cee_insulation = cee_insulation.unstack('Insulation').rename_axis(None, axis=1)
+ cee_insulation = {y: cee_insulation.loc[cee_insulation.index.get_level_values('Year') == y, :].squeeze() for y
+ in cee_insulation.index.get_level_values('Year').unique()}
+
+ bonus_heater = get_series(data['bonus_heater']['value']).unstack('Heating system final')
+ bonus_heater = bonus_heater.reindex(cee_heater[data['start']].columns, axis=1).fillna(0)
+ end = min(data['bonus_heater']['end'], data['end'])
+ l.append(PublicPolicy('cee', data['bonus_heater']['start'], end, bonus_heater, 'bonus', gest='heater',
+ social_housing=True, year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+
+ bonus_insulation = get_pandas(data['bonus_insulation']['value'], lambda x: pd.read_csv(x, index_col=[0]))
+
+ end = min(data['bonus_heater']['end'], data['end'])
+ l.append(PublicPolicy('cee', data['bonus_insulation']['start'], end, bonus_insulation, 'bonus',
+ gest='insulation', social_housing=True, year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop')))
+
+ l.append(PublicPolicy('cee', data['start'], data['end'], cee_heater, 'subsidy_target', gest='heater',
+ social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+ l.append(PublicPolicy('cee', data['start'], data['end'], cee_insulation, 'subsidy_target', gest='insulation',
+ social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+
+ coefficient_obligation = get_pandas(data['coefficient_obligation'], lambda x: pd.read_csv(x, index_col=[0])).rename_axis('Energy', axis=1)
+ cee_tax = (coefficient_obligation.T * cee_value).T / 1000
+
+ end = data['end']
+ if data.get('year_stop') is not None:
+ end = data['year_stop']
+ cee_tax = cee_tax.loc[data['start']:end - 1, :]
+
+ if data.get('years_stop') is not None:
+ if data['years_stop']:
+ cee_tax.loc[data['years_stop'], :] = 0
+
+ l.append(PublicPolicy('cee', data['start'], end, cee_tax, 'tax'))
+
+ return l
+
+ def read_cap(data):
+ l = list()
+ if 'insulation' in data.keys():
+ l.append(PublicPolicy('subsidies_cap', data['start'], data['end'], get_series(data['insulation']),
+ 'subsidies_cap', gest='insulation', target=data.get('target_insulation')))
+ if 'heater' in data.keys():
+ l.append(PublicPolicy('subsidies_cap', data['start'], data['end'], get_series(data['heater']),
+ 'subsidies_cap', gest='heater', target=data.get('target_heater')))
+ return l
+
+ def read_carbon_tax(data):
+ tax = get_series(data['tax'], header=None)
+ emission = get_pandas(data['emission'], lambda x: pd.read_csv(x, index_col=[0]).squeeze())
+ tax = (tax * emission.T).T.fillna(0) / 10 ** 6
+ tax = tax.loc[(tax != 0).any(axis=1)]
+
+ recycling = None
+ if data.get('recycling') is not None:
+ if isinstance(data['recycling'], str):
+ recycling = get_series(data['recycling'], header=0)
+ else:
+ recycling = data['recycling']
+
+ end = data['end']
+ if data.get('year_stop') is not None:
+ end = data['year_stop']
+
+ tax = tax.loc[data['start']:end - 1, :]
+ if data.get('years_stop') is not None:
+ if data['years_stop']:
+ tax.loc[data['years_stop'], :] = 0
+
+ return [PublicPolicy('carbon_tax', data['start'], end, tax, 'tax',
+ recycling=recycling, recycling_ini=data.get('recycling_ini'))]
+
+ def read_cite(data):
+ """Creates the income tax credit PublicPolicy instance.
+
+ Oil fuel-Performant Boiler exempted.
+
+ Parameters
+ ----------
+ data
+
+ Returns
+ -------
+ list
+ """
+ l = list()
+ if data['heater'] is not None:
+ heater = get_series(data['heater']).unstack('Heating system final')
+ l.append(PublicPolicy('cite', data['start'], data['end'], heater, 'subsidy_ad_valorem', gest='heater',
+ cap=data['cap'], year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop'))
+ )
+ if data['insulation'] is not None:
+ insulation = get_pandas(data['insulation'], lambda x: pd.read_csv(x, index_col=[0]))
+ l.append(
+ PublicPolicy('cite', data['start'], data['end'], insulation, 'subsidy_ad_valorem', gest='insulation',
+ cap=data['cap'], target=data.get('target'), year_stop=data.get('year_stop'),
+ years_stop=data.get('years_stop'))
+ )
+ return l
+
+ def read_zil(data):
+ l = list()
+ if isinstance(data['gest'], str):
+ data['gest'] = [data['gest']]
+ for gest in data['gest']:
+ l.append(PublicPolicy('zero_interest_loan', data['start'], data['end'], data['value'], 'zero_interest_loan',
+ cost_max=data.get('cost_max'), gest=gest, duration=data['duration'],
+ year_stop=data.get('year_stop'), years_stop=data.get('years_stop'),
+ public_cost=data.get('public_cost'), target=data.get('target')))
+ return l
+
+ def read_reduced_vat(data):
+ l = list()
+ l.append(PublicPolicy('reduced_vat', data['start'], data['end'], data['value'], 'reduced_vat', gest='heater',
+ social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+ l.append(
+ PublicPolicy('reduced_vat', data['start'], data['end'], data['value'], 'reduced_vat', gest='insulation',
+ social_housing=True, year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+ return l
+
+ def read_ad_valorem(data):
+ l = list()
+ value = data['value']
+
+ if isinstance(value, str):
+ value = get_series(data['value'])
+
+ by = 'index'
+ if data.get('index') is not None:
+ mask = get_series(data['index'], header=0)
+ value *= mask
+
+ if data.get('columns') is not None:
+ mask = get_pandas(data['columns'], lambda x: pd.read_csv(x, index_col=[0]).squeeze()).rename(None)
+ if isinstance(value, (float, int)):
+ value *= mask
+ else:
+ value = value.to_frame().dot(mask.to_frame().T)
+ by = 'columns'
+
+ if data.get('growth'):
+ growth = get_series(data['growth'], header=None)
+ value = {k: i * value for k, i in growth.items()}
+
+ name = 'sub_ad_valorem'
+ if data.get('name') is not None:
+ name = data['name']
+
+ if isinstance(data['gest'], str):
+ data['gest'] = [data['gest']]
+
+ for gest in data['gest']:
+ l.append(PublicPolicy(name, data['start'], data['end'], value, 'subsidy_ad_valorem',
+ gest=gest, by=by, target=data.get('target'), cap=data.get('cap'),
+ year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+ return l
+
+ def read_proportional(data):
+ l = list()
+
+ value = data['value']
+ if isinstance(value, str):
+ value = get_series(data['value'])
+
+ by = 'index'
+ if data.get('index') is not None:
+ mask = get_series(data['index'], header=0)
+ value *= mask
+
+ if data.get('columns') is not None:
+ mask = get_pandas(data['columns'], lambda x: pd.read_csv(x, index_col=[0]).squeeze()).rename(None)
+ if isinstance(value, (float, int)):
+ value *= mask
+ else:
+ value = value.to_frame().dot(mask.to_frame().T)
+ by = 'columns'
+
+ if data.get('growth'):
+ growth = get_series(data['growth'], header=None)
+ value = {k: i * value for k, i in growth.items()}
+
+ name = 'sub_proportional'
+ if data.get('name') is not None:
+ name = data['name']
+
+ proportional = 'MWh_cumac' # tCO2_cumac
+ if data.get('proportional') is not None:
+ proportional = data['proportional']
+
+ if isinstance(data['gest'], str):
+ data['gest'] = [data['gest']]
+ for gest in data['gest']:
+ l.append(PublicPolicy(name, data['start'], data['end'], value, 'subsidy_proportional',
+ gest=gest, by=by, target=data.get('target'), proportional=proportional,
+ year_stop=data.get('year_stop'), years_stop=data.get('years_stop')))
+
+ return l
+
+ def restriction_energy(data):
+ return [PublicPolicy(data['name'], data['start'], data['end'], data['value'],
+ 'restriction_energy', gest='heater', target=data.get('target'),
+ variable=data.get('variable', True))]
+
+ def restriction_heater(data):
+ return [PublicPolicy(data['name'], data['start'], data['end'], data['value'],
+ 'restriction_heater', gest='heater', target=data.get('target'),
+ variable=data.get('variable', True))]
+
+ def premature_heater(data):
+ return [PublicPolicy(data['name'], data['start'], data['end'], data['value'],
+ 'premature_heater', gest='heater', target=data.get('target'))]
+
+ def read_obligation(data):
+ l = list()
+ banned_performance = get_pandas(data['value'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze()).dropna()
+ start = min(banned_performance.index)
+ if data['start'] > start:
+ start = data['start']
+ frequency = data['frequency']
+ if frequency is not None:
+ frequency = pd.Series(frequency['value'], index=pd.Index(frequency['index'], name=frequency['name']))
+
+ target = None
+ if data.get('target') is not None:
+ target = pd.Series(True, index=pd.Index(data['target']['value'], name=data['target']['name']))
+
+ end = data['end']
+ if data.get('year_stop') is not None:
+ end = data['year_stop']
+ if data.get('years_stop') is not None:
+ end = data['years_stop'][0]
+
+ l.append(PublicPolicy(data['name'], start, end, banned_performance, 'obligation',
+ gest='insulation', frequency=frequency, intensive=data['intensive'],
+ min_performance=data['minimum_performance'], target=target))
+
+ if data.get('sub_obligation') is not None:
+ value = get_pandas(data['sub_obligation'], lambda x: pd.read_csv(x, index_col=[0]).squeeze())
+ l.append(PublicPolicy('sub_obligation', start, data['end'], value, 'subsidy_ad_valorem',
+ gest='insulation'))
+ return l
+
+ def read_regulation(data):
+ return [PublicPolicy(data['name'], data['start'], data['end'], None, 'regulation', gest=data['gest'])]
+
+ read = {'mpr': read_mpr,
+ 'mpr_variant': read_mpr,
+ 'mpr_efficacite': read_mpr,
+ 'mpr_serenite': read_mpr_serenite,
+ 'mpr_performance': read_mpr_serenite,
+ 'mpr_serenite_variant': read_mpr_serenite,
+ 'mpr_serenite_high_income': read_mpr_serenite,
+ 'mpr_serenite_low_income': read_mpr_serenite,
+ 'mpr_multifamily': read_mpr_serenite,
+ "mpr_multifamily_updated": read_mpr_serenite,
+ 'mpr_multifamily_deep': read_mpr_serenite,
+ 'mpr_serenite_multifamily_variant': read_mpr_serenite,
+ 'cee': read_cee,
+ 'cee_variant': read_cee,
+ 'cap': read_cap,
+ 'cap_updated': read_cap,
+ 'cap_variant': read_cee,
+ 'carbon_tax': read_carbon_tax,
+ 'carbon_tax_variant': read_carbon_tax,
+ 'cite': read_cite,
+ 'cite_insulation': read_cite,
+ 'cite_heater': read_cite,
+ 'reduced_vat': read_reduced_vat,
+ 'reduced_vat_variant': read_reduced_vat,
+ 'zero_interest_loan': read_zil}
+
+ list_policies = list()
+ for key, item in config['policies'].items():
+ item['name'] = key
+ if key in read.keys():
+ list_policies += read[key](item)
+ else:
+ if item.get('policy') == 'subsidy_ad_valorem':
+ list_policies += read_ad_valorem(item)
+ elif item.get('policy') == 'subsidy_proportional':
+ list_policies += read_proportional(item)
+ elif item.get('policy') == 'zero_interest_loan':
+ list_policies += read_zil(item)
+ elif item.get('policy') == 'premature_heater':
+ list_policies += premature_heater(item)
+ elif item.get('policy') == 'restriction_energy':
+ list_policies += restriction_energy(item)
+ elif item.get('policy') == 'restriction_heater':
+ list_policies += restriction_heater(item)
+ elif item.get('policy') == 'obligation':
+ list_policies += read_obligation(item)
+ elif item.get('policy') == 'regulation':
+ list_policies += read_regulation(item)
+ else:
+ print('{} reading function is not implemented'.format(key))
+
+ policies_heater = [p for p in list_policies if p.gest == 'heater']
+ policies_insulation = [p for p in list_policies if p.gest == 'insulation']
+ taxes = [p for p in list_policies if p.policy == 'tax']
+
+ return policies_heater, policies_insulation, taxes
+
+
+def read_inputs(config, other_inputs=generic_input):
+ """Read all inputs in Python object and concatenate in one dict.
+
+ Parameters
+ ----------
+ config: dict
+ Configuration dictionary with path to data.
+ other_inputs: dict
+ Other inputs that are manually inserted in param.py
+
+ Returns
+ -------
+ dict
+ """
+
+ inputs = dict()
+ idx = range(config['start'], config['end'])
+
+ inputs.update(other_inputs)
+
+ if isinstance(config['energy']['energy_prices'], str):
+ energy_prices = get_pandas(config['macro']['energy_prices'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year').rename_axis('Energy', axis=1))
+ elif isinstance(config['energy']['energy_prices'], dict):
+ energy_prices = get_pandas(config['energy']['energy_prices']['ini'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year').rename_axis('Energy', axis=1))
+ energy_prices = energy_prices.loc[config['start'], :]
+ rate = Series(config['energy']['energy_prices']['rate']).rename_axis('Energy')
+ if config['energy']['energy_prices'].get('factor') is not None:
+ rate *= config['energy']['energy_prices']['factor']
+ temp = range(config['start'] + 1, 2051)
+ rate = concat([(1 + rate) ** n for n in range(len(temp))], axis=1, keys=temp)
+ rate = concat((pd.Series(1, index=rate.index, name=config['start']), rate), axis=1)
+ energy_prices = rate.T * energy_prices
+ if config['energy']['energy_prices'].get('shock') is not None:
+ temp = config['energy']['energy_prices']['shock']
+ energy_prices.loc[temp['start']:, :] *= temp['factor']
+ else:
+ raise NotImplemented
+
+ inputs.update({'energy_prices': energy_prices})
+
+ energy_taxes = get_pandas(config['energy']['energy_taxes'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year').rename_axis('Heating energy', axis=1))
+ inputs.update({'energy_taxes': energy_taxes})
+
+ inputs.update({'energy_vat': get_series(config['energy']['energy_vat'], header=None)})
+
+ inputs.update({'cost_heater': get_series(config['technical']['cost_heater'], header=[0])})
+
+ inputs.update({'efficiency': get_series(config['technical']['efficiency'], header=[0])})
+
+ inputs.update({'temp_sink': get_series(config['technical']['temp_sink'], header=None)})
+
+ inputs.update({'lifetime_heater': get_series(config['technical']['lifetime_heater'], header=[0])})
+
+ inputs.update({'cost_insulation': get_series(config['technical']['cost_insulation'], header=[0])})
+
+ inputs.update({'lifetime_insulation': config['renovation']['lifetime_insulation']})
+
+ inputs.update({'performance_insulation_renovation': get_series(config['technical']['performance_insulation_renovation'], header=None).to_dict()})
+
+ inputs.update({'performance_insulation_construction': get_series(config['technical']['performance_insulation_construction'], header=None).to_dict()})
+
+ """bill_saving_preferences = get_pandas(config['macro']['preferences_saving'],
+ lambda x: pd.read_csv(x, index_col=[0]))
+ preferences_insulation.update({'bill_saved': bill_saving_preferences.loc[:, 'Insulation']})
+ preferences_heater.update({'bill_saved': bill_saving_preferences.loc[:, 'Heater']})"""
+
+ preferences_insulation = get_series(config['renovation']['preferences_insulation'], header=None).to_dict()
+ preferences_insulation.update({'present_discount_rate': get_series(config['macro']['present_discount_rate'])})
+
+ preferences_heater = get_series(config['switch_heater']['preferences_heater'], header=None).to_dict()
+ preferences_heater.update({'present_discount_rate': get_series(config['macro']['present_discount_rate'])})
+
+ inputs.update({'preferences': {'insulation': preferences_insulation,
+ 'heater': preferences_heater}})
+
+ consumption_ini = get_series(config['macro']['consumption_ini'], header=[0])
+ inputs.update({'consumption_ini': consumption_ini})
+
+ ms_heater = get_pandas(config['switch_heater']['ms_heater'], lambda x: pd.read_csv(x, index_col=[0, 1]))
+ ms_heater.columns.set_names('Heating system final', inplace=True)
+ calibration_heater = {
+ 'ms_heater': ms_heater,
+ 'scale': config['switch_heater']['scale']
+ }
+ inputs.update({'calibration_heater': calibration_heater})
+
+ if config['switch_heater'].get('district_heating') is not None:
+ district_heating = get_series(config['switch_heater']['district_heating'], header=None)
+ inputs.update({'flow_district_heating': district_heating.diff().fillna(0)})
+
+ calibration_renovation = None
+ if config['renovation']['endogenous']:
+ renovation_rate_ini = get_series(config['renovation']['renovation_rate_ini']).round(decimals=3)
+ scale_calibration = config['renovation']['scale']
+ ms_insulation_ini = get_pandas(config['renovation']['ms_insulation']['ms_insulation_ini'], lambda x: pd.read_csv(x, index_col=[0, 1, 2, 3]).squeeze().rename(None).round(decimals=3))
+ minimum_performance = config['renovation']['ms_insulation']['minimum_performance']
+
+ calibration_renovation = {'renovation_rate_ini': renovation_rate_ini,
+ 'scale': scale_calibration,
+ 'threshold_indicator': config['renovation'].get('threshold'),
+ 'ms_insulation_ini': ms_insulation_ini,
+ 'minimum_performance': minimum_performance}
+ inputs.update({'calibration_renovation': calibration_renovation})
+
+ if config['renovation'].get('exogenous_social'):
+ exogenous_social = get_pandas(config['renovation']['exogenous_social'],
+ lambda x: pd.read_csv(x, index_col=[0, 1]))
+ exogenous_social.columns = exogenous_social.columns.astype(int)
+
+ inputs.update({'exogenous_social': exogenous_social})
+
+ temp = None
+ if config['renovation'].get('rational_behavior') is not None:
+ if config['renovation']['rational_behavior']['activated']:
+ temp = {'calibration': config['renovation']['rational_behavior']['calibration'],
+ 'social': config['renovation']['rational_behavior']['social'],
+ }
+ inputs.update({'rational_behavior_insulation': temp})
+ temp = None
+ if config['switch_heater'].get('rational_behavior') is not None:
+ if config['switch_heater']['rational_behavior']['activated']:
+ temp = {
+ 'social': config['switch_heater']['rational_behavior']['social'],
+ }
+ inputs.update({'rational_behavior_heater': temp})
+
+ if config['macro'].get('population') is not None:
+ population = get_pandas(config['macro']['population'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ inputs.update({'population': population.loc[:config['end']]})
+
+ if config['macro'].get('stock_ini') is not None:
+ inputs.update({'stock_ini': config['macro']['stock_ini']})
+
+ if config['macro'].get('pop_housing') is None:
+ inputs.update({'pop_housing_min': other_inputs['pop_housing_min']})
+ inputs.update({'factor_pop_housing': other_inputs['factor_pop_housing']})
+ else:
+ pop_housing = get_pandas(config['macro']['pop_housing'],
+ lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ inputs.update({'pop_housing': pop_housing.loc[:config['end']]})
+
+ if config['macro'].get('share_single_family_construction') is not None:
+ temp = get_pandas(config['macro']['share_single_family_construction'],
+ lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ inputs.update({'share_single_family_construction': temp})
+
+ else:
+ if config['macro'].get('share_multi_family') is None:
+ inputs.update({'factor_multi_family': other_inputs['factor_multi_family']})
+ else:
+ _share_multi_family = get_pandas(config['macro']['share_multi_family'],
+ lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ inputs.update({'share_multi_family': _share_multi_family})
+
+ if config['macro']['available_income'] is not None:
+ inputs.update({'available_income': config['macro']['available_income']})
+ inputs.update({'income_rate': config['macro']['income_rate']})
+ income = get_series(config['macro']['income'], header=[0])
+ inputs.update({'income': income})
+
+ if isinstance(config['macro']['demolition_rate'], (float, int)):
+ demolition_rate = config['macro']['demolition_rate']
+ elif config['macro'].get('demolition_rate') is not None:
+ demolition_rate = get_series(config['macro']['demolition_rate'], header=None)
+ else:
+ demolition_rate = None
+ inputs.update({'demolition_rate': demolition_rate})
+
+ rotation_rate = get_pandas(config['macro']['rotation_rate'], lambda x: pd.read_csv(x, index_col=[0])).squeeze().rename(None)
+ inputs.update({'rotation_rate': rotation_rate})
+
+ surface = get_pandas(config['technical']['surface'], lambda x: pd.read_csv(x, index_col=[0, 1, 2]).squeeze().rename(None))
+ inputs.update({'surface': surface})
+
+ ratio_surface = get_pandas(config['technical']['ratio_surface'], lambda x: pd.read_csv(x, index_col=[0]))
+ inputs.update({'ratio_surface': ratio_surface})
+
+ if config['macro']['surface_built'] is None:
+ inputs.update({'surface_max': other_inputs['surface_max']})
+ inputs.update({'surface_elasticity': other_inputs['surface_elasticity']})
+ else:
+ surface_built = get_pandas(config['macro']['surface_built'], lambda x: pd.read_csv(x, index_col=[0]).squeeze().rename(None))
+ inputs.update({'surface_built': surface_built})
+
+ if config['macro'].get('flow_construction') is not None:
+ flow_construction = get_pandas(config['macro']['flow_construction'],
+ lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ inputs.update({'flow_construction': flow_construction})
+
+ temp = get_series(config['switch_heater']['ms_heater_built'], header=[0])
+ # Create a dictionary with the year as key and the share of each heating system as value
+ if 'Year' in temp.index.names:
+ # Interpolation over year using last known value
+ start = temp.index.get_level_values('Year').min()
+ ms_heater_built = temp.unstack('Year').reindex(range(start, config['end']), axis=1)
+ temp_idx = ms_heater_built.index.names
+ ms_heater_built = ms_heater_built.reset_index().interpolate(axis=1, method='pad').set_index(temp_idx)
+ # ms_heater_built = temp.unstack('Heating system').fillna(0)
+
+ inputs.update({'ms_heater_built': ms_heater_built.fillna(0)})
+
+ inputs.update({'health_cost_dpe': get_series(config['health_cost_dpe'])})
+ inputs.update({'health_cost_income': get_series(config['health_cost_income'])})
+
+ carbon_value = get_pandas(config['carbon_value'], lambda x: pd.read_csv(x, index_col=[0], header=None).squeeze())
+ inputs.update({'carbon_value': carbon_value.loc[config['start']:]})
+
+ carbon_emission = get_pandas(config['energy']['carbon_emission'], lambda x: pd.read_csv(x, index_col=[0]).rename_axis('Year'))
+ inputs.update({'carbon_emission': carbon_emission.loc[config['start']:, :] * 1000})
+
+ renewable_gas = None
+ if isinstance(config['energy']['renewable_gas'], str):
+ renewable_gas = get_series(config['energy']['renewable_gas'], header=None)
+ renewable_gas = renewable_gas.loc[config['start']:]
+ # set carbon emission of natural gas constant
+ inputs['carbon_emission'].loc[:, 'Natural gas'] = inputs['carbon_emission'].loc[config['start'], 'Natural gas']
+ inputs.update({'renewable_gas': renewable_gas})
+
+ footprint_built = get_series(config['technical']['footprint_construction'], header=None)
+ inputs.update({'footprint_built': footprint_built})
+ footprint_renovation = get_pandas(config['technical']['footprint_renovation'], lambda x: pd.read_csv(x, index_col=[0]))
+ inputs.update({'footprint_renovation': footprint_renovation})
+
+ if config['macro'].get('use_subsidies'):
+ temp = get_pandas(config['macro']['use_subsidies'], lambda x: pd.read_csv(x, index_col=[0]).squeeze())
+ inputs.update({'use_subsidies': temp})
+ else:
+ inputs.update({'use_subsidies': pd.Series(dtype=float)})
+
+ if 'implicit_discount_rate' in config.keys():
+ inputs['implicit_discount_rate'] = get_series(config['implicit_discount_rate'])
+
+ if 'hourly_profile' in config['technical'].keys():
+ temp = get_series(config['technical']['hourly_profile'], header=None)
+ temp.index = pd.TimedeltaIndex(range(0, 24), unit='h')
+ inputs.update({'hourly_profile': temp})
+
+ inputs['input_financing'].update(config['financing_cost'])
+ if inputs['input_financing']['activated'] is False:
+ temp_idx = get_series(inputs['input_financing']['upfront_max']).index
+ inputs['input_financing']['upfront_max'] = pd.Series(100000, index=temp_idx)
+ inputs['input_financing']['saving_rate'] = pd.Series(0, index=idx)
+ inputs['input_financing']['interest_rate'] = pd.Series(0, index=idx)
+
+ else:
+ inputs['input_financing']['upfront_max'] = get_series(inputs['input_financing']['upfront_max'])
+ inputs['input_financing']['saving_rate'] = get_series(inputs['input_financing']['saving_rate'], header=None)
+ inputs['input_financing']['interest_rate'] = get_series(inputs['input_financing']['interest_rate'], header=None)
+
+ if config['energy'].get('pef_elec'):
+ df = pd.read_csv(config['energy']['pef_elec'])
+ pef_elec = pd.Series(df['Electricity'].values, index=pd.Index(df['Year'].values, name='Year'), name=None)
+ inputs['pef_elec'] = pef_elec
+
+ return inputs
+
+
+def parse_inputs(inputs, taxes, config, stock):
+ """Macro module : run exogenous dynamic parameters.
+
+
+ Parameters
+ ----------
+ inputs: dict
+ Raw inputs read as Python object.
+ taxes: list
+ config: dict
+ Configuration file.
+ stock: Series
+ Building stock.
+
+ Returns
+ -------
+ dict
+ Parsed input
+ """
+
+ # Fill missing years in a Pandas DataFrame or Series by copying values from the previous year.
+ def fill_missing_years(data, start_year=config['start'], end_year=config['end']):
+
+ if isinstance(data, pd.Series) and data.index.name == 'Year':
+ full_years = list(range(start_year, end_year + 1))
+ df = data.reindex(full_years)
+ df = df.ffill()
+ return df
+
+ if 'Year' in data.index.names:
+ df = data.unstack(level='Year')
+ full_years = list(range(start_year, end_year + 1))
+ df = df.reindex(columns=full_years)
+ df = df.ffill(axis=1)
+ df = df.stack('Year')
+ return df
+
+ raise ValueError("L'index doit contenir 'Year'.")
+
+ idx = range(config['start'], config['end'])
+
+ parsed_inputs = copy.deepcopy(inputs)
+
+ if config['technical'].get('technical_progress') is not None:
+ parsed_inputs['technical_progress'] = dict()
+ if 'insulation' in config['technical']['technical_progress'].keys():
+ if config['technical']['technical_progress']['insulation']['activated']:
+ value = config['technical']['technical_progress']['insulation']['value_end']
+ start = config['technical']['technical_progress']['insulation']['start']
+ end = config['technical']['technical_progress']['insulation']['end']
+ value = round((1 + value) ** (1 / (end - start + 1)) - 1, 5)
+ parsed_inputs['technical_progress']['insulation'] = Series(value, index=range(start, end + 1)).reindex(idx).fillna(0)
+ if 'heater' in config['technical']['technical_progress'].keys():
+ if config['technical']['technical_progress']['heater']['activated']:
+ value = config['technical']['technical_progress']['heater']['value_end']
+ start = config['technical']['technical_progress']['heater']['start']
+ end = config['technical']['technical_progress']['heater']['end']
+ value = round((1 + value) ** (1 / (end - start + 1)) - 1, 5)
+ parsed_inputs['technical_progress']['heater'] = Series(value, index=range(start, end + 1)).reindex(idx).fillna(0)
+
+ if 'Year' in inputs['efficiency'].index.names:
+ s = inputs['efficiency'].index.get_level_values('Year').min()
+ df = inputs['efficiency'].copy()
+ df = df.unstack('Heating system').reindex(range(s, config['end'])).fillna(method='ffill')
+ parsed_inputs['efficiency'] = df.T
+
+ s = inputs['temp_sink'].index.min()
+ parsed_inputs['temp_sink'] = inputs['temp_sink'].reindex(range(s, config['end'])).fillna(method='ffill')
+
+ if isinstance(inputs['demolition_rate'], (float, int)):
+ parsed_inputs['demolition_rate'] = pd.Series(inputs['demolition_rate'], index=idx[1:])
+ elif isinstance(inputs['demolition_rate'], Series):
+ s = inputs['demolition_rate'].index.min()
+ parsed_inputs['demolition_rate'] = inputs['demolition_rate'].reindex(range(s, config['end'])).fillna(method='ffill')
+ parsed_inputs['demolition_rate'] = parsed_inputs['demolition_rate'].loc[idx[1:]]
+
+ if parsed_inputs['demolition_rate'] is None:
+ parsed_inputs['flow_demolition'] = 0
+ else:
+ parsed_inputs['flow_demolition'] = parsed_inputs['demolition_rate'] * stock.sum()
+
+ if 'flow_construction' not in parsed_inputs.keys():
+
+ if 'population' in inputs.keys():
+ parsed_inputs['population_total'] = inputs['population']
+ parsed_inputs['sizing_factor'] = stock.sum() / inputs['stock_ini']
+ parsed_inputs['population'] = inputs['population'] * parsed_inputs['sizing_factor']
+
+ if 'pop_housing' in parsed_inputs.keys():
+ parsed_inputs['stock_need'] = parsed_inputs['population'] / parsed_inputs['pop_housing']
+ else:
+ parsed_inputs['stock_need'], parsed_inputs['pop_housing'] = stock_need(parsed_inputs['population'],
+ parsed_inputs['population'][
+ config['start']] / stock.sum(),
+ inputs['pop_housing_min'],
+ config['start'],
+ inputs['factor_pop_housing'])
+
+ parsed_inputs['flow_need'] = parsed_inputs['stock_need'] - parsed_inputs['stock_need'].shift(1)
+ # if 'flow_construction' not in parsed_inputs.keys():
+ parsed_inputs['flow_construction'] = parsed_inputs['flow_need'] + parsed_inputs['flow_demolition']
+
+ parsed_inputs['available_income'] = pd.Series(
+ [inputs['available_income'] * (1 + config['macro']['income_rate']) ** (i - idx[0]) for i in idx], index=idx)
+ else:
+ s = inputs['flow_construction'].index.min()
+ parsed_inputs['flow_construction'] = inputs['flow_construction'].reindex(range(s, config['end'])).fillna(method='ffill')
+ parsed_inputs['flow_construction'] = parsed_inputs['flow_construction'].loc[idx]
+ parsed_inputs['surface'] = pd.concat([parsed_inputs['surface']] * len(idx), axis=1, keys=idx)
+
+ if 'share_single_family_construction' in inputs.keys():
+ s = inputs['share_single_family_construction'].index.min()
+ parsed_inputs['share_single_family_construction'] = inputs['share_single_family_construction'].reindex(range(s, config['end'])).fillna(method='ffill')
+ parsed_inputs['share_single_family_construction'] = parsed_inputs['share_single_family_construction'].loc[idx]
+ temp = pd.concat((parsed_inputs['share_single_family_construction'], (1 - parsed_inputs['share_single_family_construction'])),
+ axis=1, keys=['Single-family', 'Multi-family'], names=['Housing type'])
+ type_built = parsed_inputs['flow_construction'] * temp.T
+ else:
+ if 'share_multi_family' not in inputs.keys():
+ parsed_inputs['share_multi_family'] = share_multi_family(parsed_inputs['stock_need'],
+ inputs['factor_multi_family'])
+ type_built = share_type_built(parsed_inputs['stock_need'], parsed_inputs['share_multi_family'],
+ parsed_inputs['flow_construction']) * parsed_inputs['flow_construction']
+
+ # multiply by the rate of income from start to end
+ income = parsed_inputs['income'].copy()
+ income = pd.concat([income] * len(idx), axis=1, keys=idx, names=['Year'])
+
+ rate = pd.Series([(1 + parsed_inputs['income_rate']) ** (i - idx[0]) for i in idx], index=idx)
+ parsed_inputs['income'] = income * rate
+
+ share_decision_maker = stock.groupby(
+ ['Occupancy status', 'Housing type', 'Income owner', 'Income tenant']).sum().unstack(
+ ['Occupancy status', 'Income owner', 'Income tenant'])
+ share_decision_maker = (share_decision_maker.T / share_decision_maker.sum(axis=1)).T
+ share_decision_maker = pd.concat([share_decision_maker] * type_built.shape[1], keys=type_built.columns, axis=1)
+ construction = (reindex_mi(type_built, share_decision_maker.columns, axis=1) * share_decision_maker).stack(
+ ['Occupancy status', 'Income owner', 'Income tenant']).fillna(0)
+ construction.rename_axis('Year', axis=1, inplace=True)
+ construction = construction.loc[:, idx]
+ construction = construction.stack()
+
+ ms_heater_built = inputs['ms_heater_built'].stack('Year').unstack('Heating system').fillna(0)
+ restriction = [k for k, p in config['policies'].items() if p.get('policy') in ['restriction_energy', 'restriction_heater']]
+ for policy in restriction:
+ if config['policies'][policy]['policy'] == 'restriction_energy':
+ heating_system = [i for i, energy in resources_data['heating2heater'].items() if energy == config['policies'][policy]['value']]
+ else:
+ heating_system = config['policies'][policy]['value']
+ heating_system = [i for i in heating_system if i in ms_heater_built.columns]
+ start_policy = config['policies'][policy]['start']
+ ms_heater_built.loc[ms_heater_built.index.get_level_values('Year') >= start_policy, heating_system] = 0
+
+ ms_heater_built = (ms_heater_built.T / ms_heater_built.sum(axis=1)).T
+
+ ms_heater_built = reindex_mi(ms_heater_built, construction.index)
+ temp = construction.copy()
+ construction = (reindex_mi(construction, ms_heater_built.index) * ms_heater_built.T).T
+ construction = construction.loc[(construction != 0).any(axis=1)]
+ construction = construction.stack('Heating system').unstack('Year')
+ assert round(temp.sum() - construction.sum().sum(), 0) == 0, 'Construction is not equal to the sum of the heating system'
+
+ construction_dh = select(construction, {'Heating system': 'Heating-District heating'}).sum()
+ parsed_inputs['flow_district_heating'] = parsed_inputs['flow_district_heating'] - construction_dh
+ parsed_inputs['flow_district_heating'][parsed_inputs['flow_district_heating'] < 0] = 0
+
+ performance_insulation = pd.concat([pd.Series(inputs['performance_insulation_construction'])] * construction.shape[0], axis=1,
+ keys=construction.index).T
+
+ parsed_inputs['flow_built'] = pd.concat((construction, performance_insulation), axis=1).set_index(
+ list(performance_insulation.keys()), append=True)
+
+ parsed_inputs['flow_built'] = pd.concat([parsed_inputs['flow_built']], keys=[False],
+ names=['Existing']).reorder_levels(stock.index.names)
+
+ if not config['macro']['construction']:
+ parsed_inputs['flow_built'][parsed_inputs['flow_built'] > 0] = 0
+
+ """
+ parsed_inputs['health_expenditure'] = df['Health expenditure']
+ parsed_inputs['mortality_cost'] = df['Social cost of mortality']
+ parsed_inputs['loss_well_being'] = df['Loss of well-being']"""
+
+ parsed_inputs['carbon_value_kwh'] = (parsed_inputs['carbon_value'] * parsed_inputs['carbon_emission'].T).T.dropna() / 10**6
+
+ parsed_inputs['embodied_energy_built'] = inputs['footprint_built'].loc['Grey energy (kWh/m2)']
+ parsed_inputs['carbon_footprint_built'] = inputs['footprint_built'].loc['Carbon content (kgCO2/m2)']
+
+ # carbon footprint of renovation
+ parsed_inputs['embodied_energy_renovation'] = inputs['footprint_renovation'].loc['Grey energy (kWh/m2)', :]
+ parsed_inputs['carbon_footprint_renovation'] = inputs['footprint_renovation'].loc['Carbon content (kgCO2/m2)', :]
+
+ temp = parsed_inputs['surface'].xs(False, level='Existing', drop_level=True)
+ temp = (parsed_inputs['flow_built'].groupby(temp.index.names).sum() * temp).sum() / 10**6
+ parsed_inputs['Surface construction (Million m2)'] = temp
+
+ parsed_inputs['Carbon footprint construction (MtCO2)'] = (parsed_inputs['Surface construction (Million m2)'] * parsed_inputs['carbon_footprint_built']) / 10**3
+ parsed_inputs['Embodied energy construction (TWh PE)'] = (parsed_inputs['Surface construction (Million m2)'] * parsed_inputs['embodied_energy_built']) / 10**3
+
+ energy_prices = parsed_inputs['energy_prices'].copy()
+ parsed_inputs['energy_prices_wt'] = energy_prices.copy()
+
+ energy_taxes = parsed_inputs['energy_taxes'].copy()
+
+ if config['simple']['prices_constant']:
+ energy_prices = pd.concat([energy_prices.loc[config['start'], :]] * energy_prices.shape[0], keys=energy_prices.index,
+ axis=1).T
+
+ total_taxes = pd.DataFrame(0, index=energy_prices.index, columns=energy_prices.columns)
+ export_prices = dict()
+ export_prices.update({'energy_prices': energy_prices})
+
+ for t in taxes:
+ total_taxes = total_taxes.add(t.value, fill_value=0)
+ export_prices.update({t.name: t.value})
+
+ if energy_taxes is not None:
+ total_taxes = total_taxes.add(energy_taxes, fill_value=0)
+ taxes += [PublicPolicy('energy_taxes', energy_taxes.index[0], energy_taxes.index[-1], energy_taxes, 'tax')]
+ export_prices.update({'energy_taxes': energy_taxes})
+
+ if config['simple']['prices_constant']:
+ total_taxes = pd.concat([total_taxes.loc[config['start'], :]] * total_taxes.shape[0], keys=total_taxes.index,
+ axis=1).T
+
+ # energy_vat = energy_prices * (inputs['energy_vat'] / (1 - inputs['energy_vat']))
+ energy_vat = energy_prices * inputs['energy_vat']
+ export_prices.update({'energy_vat': energy_vat})
+
+ taxes += [PublicPolicy('energy_vat', energy_vat.index[0], energy_vat.index[-1], energy_vat, 'tax')]
+ total_taxes += energy_vat
+ parsed_inputs['taxes'] = taxes
+ parsed_inputs['total_taxes'] = total_taxes
+
+ energy_prices = energy_prices.add(total_taxes, fill_value=0)
+ parsed_inputs['energy_prices'] = energy_prices
+
+ export_prices = reverse_dict({k: item.to_dict() for k, item in export_prices.items()})
+ export_prices = concat([pd.DataFrame(item) for k, item in export_prices.items()], axis=1, keys=export_prices.keys())
+ parsed_inputs.update({'export_prices': export_prices})
+
+ supply = {'insulation': None, 'heater': None}
+ if config.get('supply') is not None:
+ if config['supply']['activated_insulation']:
+ supply.update({'insulation': {'markup_insulation': config['supply']['markup_insulation']}})
+
+ if config['supply']['activated_heater']:
+ supply.update({'heater': {'markup_heater': config['supply']['markup_heater']}})
+
+ parsed_inputs.update({'supply': supply})
+
+ premature_replacement = None
+ if config['switch_heater'].get('premature_replacement') is not None:
+ premature_replacement = {'time': config['switch_heater']['premature_replacement'],
+ 'information_rate': config['switch_heater']['information_rate']}
+ parsed_inputs.update({'premature_replacement': premature_replacement})
+
+ if inputs.get('pef_elec') is not None:
+ parsed_inputs['pef_elec'] = fill_missing_years(inputs['pef_elec'], config['start'], config['end'])
+
+ return parsed_inputs
+
+
+def dump_inputs(parsed_inputs, path, figures=None):
+ """Create summary input DataFrame.
+
+ Parameters
+ ----------
+ parsed_inputs: dict
+
+ Returns
+ -------
+ DataFrame
+ """
+
+ summary_input = dict()
+ if 'sizing_factor' in parsed_inputs.keys():
+ summary_input['Sizing factor (%)'] = pd.Series(parsed_inputs['sizing_factor'], index=parsed_inputs['population'].index)
+ summary_input['Total population (Millions)'] = parsed_inputs['population'] / 10**6
+ summary_input['Income (Billions euro)'] = parsed_inputs['available_income'] * parsed_inputs['sizing_factor'] / 10**9
+ summary_input['Buildings stock (Millions)'] = parsed_inputs['stock_need'] / 10**6
+ summary_input['Person by housing'] = parsed_inputs['pop_housing']
+ summary_input['Buildings additional (Thousands)'] = parsed_inputs['flow_need'] / 10**3
+ summary_input['Buildings built (Thousands)'] = parsed_inputs['flow_construction'] / 10**3
+ summary_input['Buildings demolished (Thousands)'] = parsed_inputs['flow_demolition'] / 10**3
+
+ temp = parsed_inputs['surface'].xs(True, level='Existing', drop_level=True)
+ temp.index = temp.index.map(lambda x: 'Surface existing {} - {} (m2/dwelling)'.format(x[0], x[1]))
+ summary_input.update(temp.T)
+
+ temp = parsed_inputs['surface'].xs(False, level='Existing', drop_level=True)
+ temp.index = temp.index.map(lambda x: 'Surface construction {} - {} (m2/dwelling)'.format(x[0], x[1]))
+ summary_input.update(temp.T)
+
+ summary_input['Surface construction (Million m2)'] = parsed_inputs['Surface construction (Million m2)']
+ summary_input['Carbon footprint construction (MtCO2)'] = parsed_inputs['Carbon footprint construction (MtCO2)']
+ summary_input['Embodied energy construction (TWh PE)'] = parsed_inputs['Embodied energy construction (TWh PE)']
+
+ summary_input = pd.DataFrame(summary_input)
+
+ t = parsed_inputs['total_taxes'].copy()
+ t.columns = t.columns.map(lambda x: 'Taxes {} (euro/kWh)'.format(x))
+ temp = parsed_inputs['energy_prices'].copy()
+ if figures is not False:
+ make_plot(temp.dropna(), 'Prices (euro/kWh)', format_y=lambda y, _: '{:.2f}'.format(y),
+ colors=resources_data['colors'], save=os.path.join(path, 'energy_prices.png'))
+ temp.columns = temp.columns.map(lambda x: 'Prices {} (euro/kWh)'.format(x))
+ summary_input = pd.concat((summary_input, t, temp), axis=1)
+
+ temp = parsed_inputs['income'].copy()
+ temp.index = temp.index.map(lambda x: 'Income {} (euro/year)'.format(x))
+ summary_input = pd.concat((summary_input, temp.T), axis=1)
+
+ summary_input.T.round(3).to_csv(os.path.join(path, 'input.csv'))
+
+ parsed_inputs['export_prices'].round(4).to_csv(os.path.join(path, 'energy_prices.csv'))
+
+ return summary_input
+
+
+def dict2data_inputs(inputs):
+ """Grouped all inputs in the same DataFrame.
+
+ Process is useful to implement a global sensitivity analysis.
+
+ Returns
+ -------
+ DataFrame
+ """
+
+ data = DataFrame(columns=['variables', 'index', 'value'])
+ metadata = DataFrame(columns=['variables', 'type', 'name', 'index', 'columns'])
+ for key, item in inputs.items():
+ i = True
+
+ if isinstance(item, dict):
+ metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__})), axis=1).T
+ i = False
+ item = Series(item)
+
+ if isinstance(item, (float, int)):
+ data = concat((data.T, Series({'variables': key, 'value': item})), axis=1).T
+ metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__})), axis=1).T
+
+ if isinstance(item, DataFrame):
+ metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__,
+ 'index': item.index.names.copy(),
+ 'columns': item.columns.names.copy()})), axis=1).T
+ i = False
+ item = item.stack(item.columns.names)
+
+ if isinstance(item, Series):
+ if i:
+ metadata = concat((metadata.T, Series({'variables': key, 'type': type(item).__name__, 'name': item.name,
+ 'index': item.index.names.copy()})), axis=1).T
+
+ if isinstance(item.index, MultiIndex):
+ item.index = item.index.to_flat_index()
+
+ item.index = item.index.rename('index')
+ df = concat([item.rename('value').reset_index()], keys=[key], names=['variables']).reset_index('variables')
+ data = concat((data, df), axis=0)
+
+ data = data.astype({'variables': 'string', 'value': 'float64'})
+ data.reset_index(drop=True, inplace=True)
+ return data
+
+
+def data2dict_inputs(data, metadata):
+ """Parse aggregate data pandas and return dict fill with several inputs.
+
+ Parameters
+ ----------
+ data: DataFrame
+ Model data input.
+ metadata: DataFrame
+ Additional information to find out how to parse data.
+
+ Returns
+ -------
+ dict
+ """
+
+ def parse_index(n, index_values):
+ if len(n) == 1:
+ idx = Index(index_values, name=n[0])
+ else:
+ idx = MultiIndex.from_tuples(index_values)
+ idx.names = n
+ return idx
+
+ parsed_input = dict()
+ for variables, df in data.groupby('variables'):
+ meta = metadata[metadata['variables'] == variables]
+ if meta['type'].iloc[0] == 'int':
+ parsed_input.update({variables: int(df['value'].iloc[0])})
+ elif meta['type'].iloc[0] == 'float':
+ parsed_input.update({variables: float(df['value'].iloc[0])})
+ elif meta['type'].iloc[0] == 'Series':
+ idx = parse_index(meta['index'].iloc[0], df['index'].values)
+ parsed_input.update({variables: Series(df['value'].values, name=str(meta['name'].iloc[0]), index=idx)})
+ elif meta['type'].iloc[0] == 'DataFrame':
+ idx = parse_index(meta['index'].iloc[0] + meta['columns'].iloc[0], df['index'].values)
+ parsed_input.update({variables: Series(df['value'].values, name=str(meta['name'].iloc[0]), index=idx).unstack(
+ meta['columns'].iloc[0])})
+
+ elif meta['type'].iloc[0] == 'dict':
+ parsed_input.update({variables: Series(df['value'].values, index=df['index'].values).to_dict()})
+
+ return parsed_input
+
+
+
+
+def create_simple_policy(start, end, value=0.3, gest='insulation'):
+ return PublicPolicy('sub_ad_valorem', start, end, value, 'subsidy_ad_valorem',
+ gest=gest)
diff --git a/project/thermal.py b/project/thermal.py
index 8d9b8bbd..9c4d68cb 100644
--- a/project/thermal.py
+++ b/project/thermal.py
@@ -25,7 +25,7 @@
"""LOGISTIC_COEFFICIENT = pd.read_csv('project/input/logistic_regression_coefficient_epc.csv', index_col=[0])
LOGISTIC_COEFFICIENT.columns = ['Intercept', 'Proxy_conso_square']
LOGISTIC_COEFFICIENT.index.names = ['Performance']"""
-CONVERSION = 2.3
+
HDD = 55706
CERTIFICATE_3USES_BOUNDARIES = {
'A': [0, 50],
@@ -503,7 +503,8 @@ def conventional_dhw_final(index):
def conventional_energy_3uses(u_wall, u_floor, u_roof, u_windows, ratio_surface, efficiency, index,
th_bridging='Medium', vent_types='Ventilation naturelle', infiltration='Medium',
- air_rate=None, unobserved=None, method='3uses'
+ air_rate=None, unobserved=None, method='3uses',
+ pef_elec=None
):
"""Space heating conventional, and energy performance certificate.
@@ -531,6 +532,9 @@ def conventional_energy_3uses(u_wall, u_floor, u_roof, u_windows, ratio_surface,
"""
+ if pef_elec is None:
+ raise ValueError("The pef_elec factor is required but has not been provided.")
+
heating_final = conventional_heating_final(u_wall, u_floor, u_roof, u_windows, ratio_surface, efficiency,
th_bridging=th_bridging, vent_types=vent_types,
infiltration=infiltration, air_rate=air_rate, unobserved=unobserved
@@ -543,10 +547,10 @@ def conventional_energy_3uses(u_wall, u_floor, u_roof, u_windows, ratio_surface,
else:
energy_carrier = index.get_level_values('Energy')
- energy_primary = final2primary(energy_final, energy_carrier)
+ energy_primary = final2primary(energy_final, energy_carrier, pef_elec=pef_elec)
other_consumptions = None
if method == '5uses':
- other_consumptions = (CONSUMPTION_LIGHT + AUXILIARY_CONSUMPTION) * CONVERSION
+ other_consumptions = (CONSUMPTION_LIGHT + AUXILIARY_CONSUMPTION) * pef_elec
performance = find_certificate(energy_primary, method=method, other_consumptions=other_consumptions)
@@ -616,15 +620,19 @@ def find_certificate(primary_consumption, other_consumptions=None, method='3uses
raise NotImplementedError
-def final2primary(heat_consumption, energy, conversion=CONVERSION):
+def final2primary(heat_consumption, energy, pef_elec=None):
+
+ if pef_elec is None:
+ raise ValueError("The pef_elec factor is required but has not been provided.")
+
if isinstance(heat_consumption, pd.Series):
primary_heat_consumption = heat_consumption.copy()
- primary_heat_consumption[energy == 'Electricity'] = primary_heat_consumption * conversion
+ primary_heat_consumption[energy == 'Electricity'] = primary_heat_consumption * pef_elec
return primary_heat_consumption
elif isinstance(heat_consumption, float):
if energy == 'Electricity':
- return heat_consumption * conversion
+ return heat_consumption * pef_elec
else:
return heat_consumption
@@ -632,12 +640,12 @@ def final2primary(heat_consumption, energy, conversion=CONVERSION):
# index
if energy.index.equals(heat_consumption.index):
primary_heat_consumption = heat_consumption.copy()
- primary_heat_consumption.loc[energy == 'Electricity', :] = primary_heat_consumption * conversion
+ primary_heat_consumption.loc[energy == 'Electricity', :] = primary_heat_consumption * pef_elec
return primary_heat_consumption
# columns
elif energy.index.equals(heat_consumption.columns):
primary_heat_consumption = heat_consumption.copy()
- primary_heat_consumption.loc[:, energy == 'Electricity'] = primary_heat_consumption * conversion
+ primary_heat_consumption.loc[:, energy == 'Electricity'] = primary_heat_consumption * pef_elec
return primary_heat_consumption
else:
raise 'Energy DataFrame do not match indexes and columns'
@@ -646,7 +654,7 @@ def final2primary(heat_consumption, energy, conversion=CONVERSION):
def primary_heating_consumption(u_wall, u_floor, u_roof, u_windows, efficiency, energy, ratio_surface, hdd,
- conversion=CONVERSION):
+ pef_elec=None):
"""Convert final to primary heating consumption.
Parameters
@@ -659,14 +667,18 @@ def primary_heating_consumption(u_wall, u_floor, u_roof, u_windows, efficiency,
efficiency
energy
ratio_surface
- conversion
+ pef_elec
Returns
-------
"""
+
+ if pef_elec is None:
+ raise ValueError("The pef_elec factor is required but has not been provided.")
+
# data = pd.concat([u_wall, u_floor, u_roof, u_windows], axis=1, keys=['Wall', 'Floor', 'Roof', 'Windows'])
heat_consumption = stat_heating_consumption(u_wall, u_floor, u_roof, u_windows, efficiency, ratio_surface, hdd)
- return final2primary(heat_consumption, energy, conversion=conversion)
+ return final2primary(heat_consumption, energy, pef_elec)
def heat_intensity(budget, method='v4'):
@@ -767,7 +779,7 @@ def stat_certificate(df):
if (primary_consumption > item[0]) & (primary_consumption <= item[1]):
return key
-def certificate_buildings(u_wall, u_floor, u_roof, u_windows, hdd, efficiency, energy, ratio_surface):
+def certificate_buildings(u_wall, u_floor, u_roof, u_windows, hdd, efficiency, energy, ratio_surface, pef_elec=None):
"""Returns energy performance certificate.
Parameters
@@ -789,8 +801,12 @@ def certificate_buildings(u_wall, u_floor, u_roof, u_windows, hdd, efficiency, e
Certificates for all buildings in the stock.
"""
+
+ if pef_elec is None:
+ raise ValueError("The pef_elec factor is required but has not been provided.")
+
primary_heat_consumption = primary_heating_consumption(u_wall, u_floor, u_roof, u_windows, hdd, efficiency,
- energy, ratio_surface, conversion=CONVERSION)
+ energy, ratio_surface, pef_elec=pef_elec)
return primary_heat_consumption, stat_certificate(primary_heat_consumption)
diff --git a/project/utils.py b/project/utils.py
index 8f5da803..05b38a0e 100644
--- a/project/utils.py
+++ b/project/utils.py
@@ -1254,10 +1254,13 @@ def make_stacked_bar_subplot(df, format_y=lambda y, _: '{:.0f}€'.format(y), fo
# group.sum(axis=1)
# Adjust legend
- if ncol is None:
- ncol = len(labels)
- fig.legend(handles, labels, loc='lower center', ncol=ncol, fontsize=fonttick, frameon=False,
- bbox_to_anchor=(0.5, 0))
+ try:
+ if ncol is None:
+ ncol = len(labels)
+ fig.legend(handles, labels, loc='lower center', ncol=ncol, fontsize=fonttick, frameon=False,
+ bbox_to_anchor=(0.5, 0))
+ except UnboundLocalError:
+ pass
plt.tight_layout()
plt.subplots_adjust(bottom=bottom) # Adjust the bottom margin