172 lines
5.5 KiB
Python
172 lines
5.5 KiB
Python
"""Build industrial energy demand per country."""
|
|
|
|
import pandas as pd
|
|
import multiprocessing as mp
|
|
from tqdm import tqdm
|
|
|
|
ktoe_to_twh = 0.011630
|
|
|
|
# name in JRC-IDEES Energy Balances
|
|
sector_sheets = {'Integrated steelworks': 'cisb',
|
|
'Electric arc': 'cise',
|
|
'Alumina production': 'cnfa',
|
|
'Aluminium - primary production': 'cnfp',
|
|
'Aluminium - secondary production': 'cnfs',
|
|
'Other non-ferrous metals': 'cnfo',
|
|
'Basic chemicals': 'cbch',
|
|
'Other chemicals': 'coch',
|
|
'Pharmaceutical products etc.': 'cpha',
|
|
'Basic chemicals feedstock': 'cpch',
|
|
'Cement': 'ccem',
|
|
'Ceramics & other NMM': 'ccer',
|
|
'Glass production': 'cgla',
|
|
'Pulp production': 'cpul',
|
|
'Paper production': 'cpap',
|
|
'Printing and media reproduction': 'cprp',
|
|
'Food, beverages and tobacco': 'cfbt',
|
|
'Transport Equipment': 'ctre',
|
|
'Machinery Equipment': 'cmae',
|
|
'Textiles and leather': 'ctel',
|
|
'Wood and wood products': 'cwwp',
|
|
'Mining and quarrying': 'cmiq',
|
|
'Construction': 'ccon',
|
|
'Non-specified': 'cnsi',
|
|
}
|
|
|
|
|
|
fuels = {'All Products': 'all',
|
|
'Solid Fuels': 'solid',
|
|
'Total petroleum products (without biofuels)': 'liquid',
|
|
'Gases': 'gas',
|
|
'Nuclear heat': 'heat',
|
|
'Derived heat': 'heat',
|
|
'Biomass and Renewable wastes': 'biomass',
|
|
'Wastes (non-renewable)': 'waste',
|
|
'Electricity': 'electricity'
|
|
}
|
|
|
|
eu28 = ['FR', 'DE', 'GB', 'IT', 'ES', 'PL', 'SE', 'NL', 'BE', 'FI',
|
|
'DK', 'PT', 'RO', 'AT', 'BG', 'EE', 'GR', 'LV', 'CZ',
|
|
'HU', 'IE', 'SK', 'LT', 'HR', 'LU', 'SI', 'CY', 'MT']
|
|
|
|
jrc_names = {"GR": "EL", "GB": "UK"}
|
|
|
|
|
|
def industrial_energy_demand_per_country(country):
|
|
|
|
jrc_dir = snakemake.input.jrc
|
|
jrc_country = jrc_names.get(country, country)
|
|
fn = f'{jrc_dir}/JRC-IDEES-2015_EnergyBalance_{jrc_country}.xlsx'
|
|
|
|
sheets = list(sector_sheets.values())
|
|
df_dict = pd.read_excel(fn, sheet_name=sheets, index_col=0)
|
|
|
|
def get_subsector_data(sheet):
|
|
|
|
df = df_dict[sheet][year].groupby(fuels).sum()
|
|
|
|
df['other'] = df['all'] - df.loc[df.index != 'all'].sum()
|
|
|
|
return df
|
|
|
|
df = pd.concat({sub: get_subsector_data(sheet)
|
|
for sub, sheet in sector_sheets.items()}, axis=1)
|
|
|
|
sel = ['Mining and quarrying', 'Construction', 'Non-specified']
|
|
df['Other Industrial Sectors'] = df[sel].sum(axis=1)
|
|
df['Basic chemicals'] += df['Basic chemicals feedstock']
|
|
|
|
df.drop(columns=sel+['Basic chemicals feedstock'], index='all', inplace=True)
|
|
|
|
df *= ktoe_to_twh
|
|
|
|
return df
|
|
|
|
|
|
def add_ammonia_energy_demand(demand):
|
|
|
|
# MtNH3/a
|
|
fn = snakemake.input.ammonia_production
|
|
ammonia = pd.read_csv(fn, index_col=0)[str(year)] / 1e3
|
|
|
|
def ammonia_by_fuel(x):
|
|
|
|
fuels = {'gas': config['MWh_CH4_per_tNH3_SMR'],
|
|
'electricity': config['MWh_elec_per_tNH3_SMR']}
|
|
|
|
return pd.Series({k: x*v for k,v in fuels.items()})
|
|
|
|
ammonia = ammonia.apply(ammonia_by_fuel).T
|
|
|
|
demand['Ammonia'] = ammonia.unstack().reindex(index=demand.index, fill_value=0.)
|
|
|
|
demand['Basic chemicals (without ammonia)'] = demand["Basic chemicals"] - demand["Ammonia"]
|
|
|
|
demand['Basic chemicals (without ammonia)'].clip(lower=0, inplace=True)
|
|
|
|
demand.drop(columns='Basic chemicals', inplace=True)
|
|
|
|
return demand
|
|
|
|
|
|
def add_non_eu28_industrial_energy_demand(demand):
|
|
|
|
# output in MtMaterial/a
|
|
fn = snakemake.input.industrial_production_per_country
|
|
production = pd.read_csv(fn, index_col=0) / 1e3
|
|
|
|
#recombine HVC, Chlorine and Methanol to Basic chemicals (without ammonia)
|
|
chemicals = ["HVC", "Chlorine", "Methanol"]
|
|
production["Basic chemicals (without ammonia)"] = production[chemicals].sum(axis=1)
|
|
production.drop(columns=chemicals, inplace=True)
|
|
|
|
eu28_production = production.loc[eu28].sum()
|
|
eu28_energy = demand.groupby(level=1).sum()
|
|
eu28_averages = eu28_energy / eu28_production
|
|
|
|
non_eu28 = production.index.symmetric_difference(eu28)
|
|
|
|
demand_non_eu28 = pd.concat({k: v * eu28_averages
|
|
for k, v in production.loc[non_eu28].iterrows()})
|
|
|
|
return pd.concat([demand, demand_non_eu28])
|
|
|
|
|
|
def industrial_energy_demand(countries):
|
|
|
|
nprocesses = snakemake.threads
|
|
func = industrial_energy_demand_per_country
|
|
tqdm_kwargs = dict(ascii=False, unit=' country', total=len(countries),
|
|
desc="Build industrial energy demand")
|
|
with mp.Pool(processes=nprocesses) as pool:
|
|
demand_l = list(tqdm(pool.imap(func, countries), **tqdm_kwargs))
|
|
|
|
demand = pd.concat(demand_l, keys=countries)
|
|
|
|
return demand
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if 'snakemake' not in globals():
|
|
from helper import mock_snakemake
|
|
snakemake = mock_snakemake('build_industrial_energy_demand_per_country_today')
|
|
|
|
config = snakemake.config['industry']
|
|
year = config.get('reference_year', 2015)
|
|
|
|
demand = industrial_energy_demand(eu28)
|
|
|
|
demand = add_ammonia_energy_demand(demand)
|
|
|
|
demand = add_non_eu28_industrial_energy_demand(demand)
|
|
|
|
# for format compatibility
|
|
demand = demand.stack(dropna=False).unstack(level=[0,2])
|
|
|
|
# style and annotation
|
|
demand.index.name = 'TWh/a'
|
|
demand.sort_index(axis=1, inplace=True)
|
|
|
|
fn = snakemake.output.industrial_energy_demand_per_country_today
|
|
demand.to_csv(fn)
|