Skip to content

Commit acc22b1

Browse files
authored
Merge pull request #128 from PyPSA/vehicle-data
Add DEA transport data
2 parents 1d3eb62 + c714bf4 commit acc22b1

12 files changed

+1024
-3
lines changed

Snakefile

+2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ rule compile_cost_assumptions:
1111
fraunhofer_vehicles_costs = "inputs/Fraunhofer_ISE_vehicles_costs.csv",
1212
EWG_costs = "inputs/EWG_costs.csv",
1313
dea_transport = "inputs/energy_transport_data_sheet_dec_2017.xlsx",
14+
dea_vehicles = "inputs/data_sheets_for_commercial_freight_and_passenger_transport_0.xlsx",
1415
dea_renewable_fuels = "inputs/data_sheets_for_renewable_fuels.xlsx",
1516
dea_storage = "inputs/technology_data_catalogue_for_energy_storage.xlsx",
1617
dea_generation = "inputs/technology_data_for_el_and_dh.xlsx",
1718
dea_heating = "inputs/technologydatafor_heating_installations_marts_2018.xlsx",
1819
dea_industrial = "inputs/technology_data_for_industrial_process_heat.xlsx",
20+
dea_ship = "inputs/data_sheets_for_maritime_commercial_freight_and_passenger_transport.xlsx",
1921
dea_ccts = "inputs/technology_data_for_carbon_capture_transport_storage.xlsx",
2022
pnnl_energy_storage = "inputs/pnnl-energy-storage-database.xlsx",
2123
manual_input = "inputs/manual_input.csv"

docs/release_notes.rst

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Release Notes
55
.. Upcoming Release
66
.. ================
77
8+
* add heavy duty and shipping technology assumptions from DEA
89

910
Technology-Data 0.8.1 (28 February 2024)
1011
========================================
Binary file not shown.
Binary file not shown.

outputs/costs_2020.csv

+117
Large diffs are not rendered by default.

outputs/costs_2025.csv

+117
Large diffs are not rendered by default.

outputs/costs_2030.csv

+117
Large diffs are not rendered by default.

outputs/costs_2035.csv

+117
Large diffs are not rendered by default.

outputs/costs_2040.csv

+117
Large diffs are not rendered by default.

outputs/costs_2045.csv

+117
Large diffs are not rendered by default.

outputs/costs_2050.csv

+117
Large diffs are not rendered by default.

scripts/compile_cost_assumptions.py

+202-3
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@
242242
]
243243

244244

245-
# %% -------- FUNCTIONS ---------------------------------------------------
245+
# -------- FUNCTIONS ---------------------------------------------------
246246

247247
def get_excel_sheets(excel_files):
248248
""""
@@ -273,6 +273,197 @@ def get_sheet_location(tech, sheet_names, data_in):
273273
return None
274274

275275
#
276+
277+
def get_dea_maritime_data(fn, data):
278+
"""
279+
Get technology data for shipping from DEA.
280+
"""
281+
sheet_names = ['Container feeder, diesel',
282+
'Container feeder, methanol',
283+
'Container feeder, ammonia',
284+
'Container, diesel',
285+
'Container, methanol',
286+
'Container, ammonia',
287+
'Tank&bulk, diesel',
288+
'Tank&bulk, methanol',
289+
'Tankbulk, ammonia',
290+
]
291+
excel = pd.read_excel(fn,
292+
sheet_name=sheet_names,
293+
index_col=[0,1],
294+
usecols="A:F",
295+
na_values="N/A")
296+
297+
wished_index = ["Typical ship lifetime (years)",
298+
"Upfront ship cost (mill. €)",
299+
"Fixed O&M (€/year)",
300+
"Variable O&M (€/nm)",
301+
]
302+
303+
304+
for sheet in excel.keys():
305+
df = excel[sheet]
306+
df = df.iloc[1:,:].set_axis(df.iloc[0], axis=1)
307+
308+
assert "Typical operational speed" in df.index.get_level_values(1)[22]
309+
# in unit GJ/nm
310+
efficiency = df.iloc[22]
311+
312+
df = df[df.index.get_level_values(1).isin(wished_index)]
313+
df = df.droplevel(level=0)
314+
df.loc["efficiency (GJ/nm)"] = efficiency
315+
df = df.reindex(columns=pd.Index(years).union(df.columns))
316+
df = df.astype(float)
317+
df = df.interpolate(axis=1, limit_direction="both")
318+
df = df[years]
319+
320+
# dropna
321+
df = df.dropna(how="all", axis=0)
322+
# add column for units
323+
df["unit"] = (df.rename(index=lambda x:
324+
x[x.rfind("(")+1: x.rfind(")")]).index.values)
325+
df["unit"] = df.unit.str.replace("€", "EUR")
326+
# remove units from index
327+
df.index = df.index.str.replace(r" \(.*\)","", regex=True)
328+
329+
# convert million Euro -> Euro
330+
df_i = df[df.unit == 'mill. EUR'].index
331+
df.loc[df_i, years] *= 1e6
332+
df.loc[df_i, "unit"] = "EUR"
333+
334+
# convert FOM in % of investment/year
335+
if 'Fixed O&M' in df.index:
336+
df.loc['Fixed O&M', years] /= (df.loc['Upfront ship cost', years]
337+
* 100)
338+
df.loc['Fixed O&M', "unit"] = "%/year"
339+
340+
# convert nm in km
341+
# 1 Nautical Mile (nm) = 1.852 Kilometers (km)
342+
df_i = df[df.unit.str.contains('/nm')].index
343+
df.loc[df_i, years] /= 1.852
344+
df.loc[df_i, "unit"] = df.loc[df_i, "unit"].str.replace("/nm", "/km")
345+
346+
# 1 GJ = 1/3600 * 1e9 Wh = 1/3600 * 1e3 MWh
347+
df_i = df[df.unit.str.contains('GJ')].index
348+
df.loc[df_i, years] *= 1e3/3600
349+
df.loc[df_i, "unit"] = df.loc[df_i, "unit"].str.replace("GJ", "MWh")
350+
351+
# add source + cost year
352+
df["source"] = f"Danish Energy Agency, {fn}"
353+
# cost year is 2023 p.10
354+
df["currency_year"] = 2023
355+
# add sheet name
356+
df['further description'] = sheet
357+
358+
# FOM, VOM,efficiency, lifetime, investment
359+
rename = {'Typical ship lifetime': "lifetime",
360+
'Upfront ship cost': "investment",
361+
'Fixed O&M': "FOM",
362+
'Variable O&M': "VOM",
363+
}
364+
365+
df = df.rename(index=rename)
366+
367+
df = pd.concat([df], keys=[sheet], names=["technology", "parameter"])
368+
369+
data = pd.concat([data, df])
370+
371+
return data
372+
373+
374+
375+
def get_dea_vehicle_data(fn, data):
376+
"""
377+
Get heavy-duty vehicle data from DEA.
378+
"""
379+
sheet_names = ['Diesel L1', 'Diesel L2', 'Diesel L3',
380+
'Diesel B1', 'Diesel B2',
381+
'BEV L1', 'BEV L2', 'BEV L3',
382+
'BEV B1', 'BEV B2',
383+
'FCV L1', 'FCV L2', 'FCV L3',
384+
'FCV B1', 'FCV B2']
385+
excel = pd.read_excel(fn,
386+
sheet_name=sheet_names,
387+
index_col=0,
388+
usecols="A:F",
389+
na_values="no data")
390+
391+
wished_index = ["Typical vehicle lifetime (years)",
392+
"Upfront vehicle cost (€)",
393+
"Fixed maintenance cost (€/year)",
394+
"Variable maintenance cost (€/km)",
395+
"Motor size (kW)",
396+
]
397+
398+
# clarify DEA names
399+
types = {"L1": "Truck Solo max 26 tons",
400+
"L2": "Truck Trailer max 56 tons",
401+
"L3": "Truck Semi-Trailer max 50 tons",
402+
"B1": "Bus city",
403+
"B2": "Coach"}
404+
405+
for sheet in excel.keys():
406+
df = excel[sheet]
407+
tech = sheet.split()[0] + " " + types.get(sheet.split()[1], "")
408+
df = df.iloc[1:,:].set_axis(df.iloc[0], axis=1)
409+
# "Fuel energy - typical load (MJ/km)"
410+
# represents efficiency for average weight vehicle carries during normal
411+
# operation, currently assuming mean between urban, regional and long haul
412+
assert df.index[27] == 'Fuel energy - typical load (MJ/km)'
413+
efficiency = df.iloc[28:31].mean()
414+
df = df[df.index.isin(wished_index)]
415+
df.loc["efficiency (MJ/km)"] = efficiency
416+
df = df.reindex(columns=pd.Index(years).union(df.columns))
417+
df = df.interpolate(axis=1, limit_direction="both")
418+
df = df[years]
419+
420+
# add column for units
421+
df["unit"] = (df.rename(index=lambda x:
422+
x[x.rfind("(")+1: x.rfind(")")]).index.values)
423+
df["unit"] = df.unit.str.replace("€", "EUR")
424+
# remove units from index
425+
df.index = df.index.str.replace(r" \(.*\)","", regex=True)
426+
427+
# convert MJ in kWh -> 1 kWh = 3.6 MJ
428+
df_i = df.index[df.unit=="MJ/km"]
429+
df.loc[df_i, years] /= 3.6
430+
df.loc[df_i, "unit"] = "kWh/km"
431+
432+
# convert FOM in % of investment/year
433+
df.loc["Fixed maintenance cost", years] /= (df.loc["Upfront vehicle cost", years]
434+
* 100)
435+
df.loc["Fixed maintenance cost", "unit"] = "%/year"
436+
437+
# clarify costs are per vehicle
438+
df.loc["Upfront vehicle cost", "unit"] += "/vehicle"
439+
440+
# add source + cost year
441+
df["source"] = f"Danish Energy Agency, {fn}"
442+
# cost year is 2022 p.12
443+
df["currency_year"] = 2022
444+
# add sheet name
445+
df['further description'] = sheet
446+
447+
# FOM, VOM,efficiency, lifetime, investment
448+
rename = {'Typical vehicle lifetime': "lifetime",
449+
'Upfront vehicle cost': "investment",
450+
'Fixed maintenance cost': "FOM",
451+
'Variable maintenance cost': "VOM",
452+
}
453+
454+
df = df.rename(index=rename)
455+
456+
to_keep = ['Motor size', 'lifetime', "FOM", "VOM", "efficiency",
457+
"investment"]
458+
df = df[df.index.isin(to_keep)]
459+
460+
df = pd.concat([df], keys=[tech], names=["technology", "parameter"])
461+
462+
data = pd.concat([data, df])
463+
464+
return data
465+
466+
276467
def get_data_DEA(tech, data_in, expectation=None):
277468
"""
278469
interpolate cost for a given technology from DEA database sheet
@@ -2177,7 +2368,7 @@ def prepare_inflation_rate(fn):
21772368
years = snakemake.config['years']
21782369
inflation_rate = prepare_inflation_rate(snakemake.input.inflation_rate)
21792370

2180-
2371+
# p.77 Figure 51 share of vehicle-km driven by truck
21812372

21822373
# (1) DEA data
21832374
# (a)-------- get data from DEA excel sheets ----------------------------------
@@ -2214,7 +2405,7 @@ def prepare_inflation_rate(fn):
22142405
data = add_gas_storage(data)
22152406
# add carbon capture
22162407
data = add_carbon_capture(data, tech_data)
2217-
2408+
22182409
# adjust for inflation
22192410
for x in data.index.get_level_values("technology"):
22202411
if x in cost_year_2020:
@@ -2223,6 +2414,12 @@ def prepare_inflation_rate(fn):
22232414
data.at[x, "currency_year"] = 2019
22242415
else:
22252416
data.at[x, "currency_year"] = 2015
2417+
2418+
# add heavy duty assumptions, cost year is 2022
2419+
data = get_dea_vehicle_data(snakemake.input.dea_vehicles, data)
2420+
2421+
# add shipping data
2422+
data = get_dea_maritime_data(snakemake.input.dea_ship, data)
22262423

22272424
# %% (2) -- get data from other sources which need formatting -----------------
22282425
# (a) ---------- get old pypsa costs ---------------------------------------
@@ -2267,6 +2464,8 @@ def prepare_inflation_rate(fn):
22672464
data = add_SMR_data(data)
22682465
# add solar rooftop costs by taking the mean of commercial and residential
22692466
data = add_mean_solar_rooftop(data)
2467+
2468+
data.index.names = ["technology", "parameter"]
22702469
# %% (3) ------ add additional sources and save cost as csv ------------------
22712470
# [RTD-target-multiindex-df]
22722471
for year in years:

0 commit comments

Comments
 (0)