Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions inputs/manual_input.csv
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,7 @@ H2 production biomass gasification CC,VOM,2020,0.46,EUR/MWh_H2,2010,"JRC, 01_JRC
grey methanol synthesis,efficiency,2030,0.569,MWh_MeOH/MWh_gas,2020,"From https://www.sciencedirect.com/science/article/pii/S0016236119311767, table 2, Methanol Synthesis with integrated autothermal reforming. Efficiency is the exergetic efficiency ",
grey methanol synthesis,carbondioxide-output,2030,0.0567742,t_CO2/MWh_gas,2020,"Calculated from the carbon content of gas (0.198 t_CO2/MWh) compared to the carbon content of MeOH (0.2482 t_CO2/MWh)",
grey methanol synthesis,investment,2030,1001188,EUR/MW_MeOH,2020,"Calculated from https://www.irena.org/-/media/Files/IRENA/Agency/Publication/2021/Jan/IRENA_Innovation_Renewable_Methanol_2021.pdf) Table 25 with an exchange rate of 0.8775 EUR/USD and energy content of 5.528 MWh_MeOH/t_MeOH",
Enhanced Weathering,investment,2020,110099,EUR/tCO2,2014," Jessica Strefler et al 2018 Environ. Res. Lett. 13 034010 https://doi.org/10.1088/1748-9326/aaa9c4, conversion 0.7541 EUR/US (2014)",
Enhanced Weathering,VOM,2020,166.15,EUR/h/tCO2,2014," Jessica Strefler et al 2018 Environ. Res. Lett. 13 034010 https://doi.org/10.1088/1748-9326/aaa9c4, conversion 0.7541 EUR/US (2014)",
Enhanced Weathering,electricity-input,2020,0.1852,MWh/tCO2 ,, Jessica Strefler et al 2018 Environ. Res. Lett. 13 034010 https://doi.org/10.1088/1748-9326/aaa9c4,
Enhanced Weathering,lifetime,2020,1,years,,costs are given per year,
4,378 changes: 0 additions & 4,378 deletions outputs/US/costs_2020.csv

This file was deleted.

4,448 changes: 0 additions & 4,448 deletions outputs/US/costs_2025.csv

This file was deleted.

4,640 changes: 0 additions & 4,640 deletions outputs/US/costs_2030.csv

This file was deleted.

4,664 changes: 0 additions & 4,664 deletions outputs/US/costs_2035.csv

This file was deleted.

4,664 changes: 0 additions & 4,664 deletions outputs/US/costs_2040.csv

This file was deleted.

4,664 changes: 0 additions & 4,664 deletions outputs/US/costs_2045.csv

This file was deleted.

4,664 changes: 0 additions & 4,664 deletions outputs/US/costs_2050.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2020.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2025.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2030.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2035.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2040.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2045.csv

This file was deleted.

1,243 changes: 0 additions & 1,243 deletions outputs/costs_2050.csv

This file was deleted.

202 changes: 202 additions & 0 deletions scripts/compile_cost_assumptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"biochar pyrolysis": "105 Slow pyrolysis, Straw",
"electrolysis small": "86 AEC 10 MW",
"gas storage": "150 Underground Storage of Gas",
"biomethanation": "106 Biomethanation of biogas",
}
# [DEA-sheet-names]

Expand Down Expand Up @@ -976,6 +977,9 @@ def get_data_DEA(
if "biochar pyrolysis" in tech_name:
df = biochar_pyrolysis_harmonise_dea(df)

if "biomethanation" in tech_name:
df = biomethanation_dea(df)

elif tech_name == "central geothermal heat source":
# we need to convert from costs per MW of the entire system (including heat pump)
# to costs per MW of the geothermal heat source only
Expand Down Expand Up @@ -1308,6 +1312,93 @@ def unify_diw(cost_dataframe: pd.DataFrame) -> pd.DataFrame:
return cost_dataframe


def biomethanation_dea(df):
"""
This function does:
- import DEA data for biomethanation (4H2 + CO2 -> CH4 + 2H2O)
- recalculates cost and inputs per MW of H2 added (bus 0 is H2)
"""

CO2_density = 1.98 / 1000 # kg/Nm3
CH4_vol = 0.58 # biogas vol%, from DEA source for biomethanation
CO2_vol = 0.42 # biogas vol%, from DEA source for biomethanation
CH4_lhv = 35.8 / 3600 # MWh/Nm3
CO2_biogas = CO2_vol / CH4_vol / CH4_lhv * CO2_density # t_CO2/MWh_biogas

# Find index labels directly
idx = df.index[df.index.str.contains("Total Input")]
idx2 = df.index[df.index.str.contains("Hydrogen Consumption")]
idx3 = df.index[df.index.str.contains("CO2 Consumption")]
idx4 = df.index[df.index.str.contains("Methane Output")]
idx5 = df.index[df.index.str.contains("EUR")]

# H2/CH4 ratio (MW/MW)
CH4_H2_ratio = df.loc[idx4].astype(float) / df.loc[idx2[0]].astype(float)

# Adjust costs from €/MWh CH4 to €/MW_H2
df.loc[idx5] = df.loc[idx5].astype(float).mul(CH4_H2_ratio.values.flatten(), axis=1)
df.index = [
i.replace("MW", "MW_H2").replace("MWh", "MWh_H2") if i in idx5 else i
for i in df.index
]

# Normalize all inputs & outputs to MW of hydrogen
df.loc[idx] = df.loc[idx].astype(float) / df.loc[idx2[0]].astype(float)

# Convert CO2 input from Nm3 to tons
df.loc[idx3[0]] = df.loc[idx3[0]].astype(float) * CO2_density # tCO2 / h / MW_H2

# Biogas input in MWh/MWh_H2
df.loc["Biogas Consumption, [MWh_th/MWh_H2]"] = (
df.loc[idx3[0]].astype(float) / CO2_biogas
)

# Add biogas back to methane output (correct total output)
df.loc[idx4] = (
df.loc[idx4].astype(float) + df.loc["Biogas Consumption, [MWh_th/MWh_H2]"]
)

# change unit to H2 basis
df.index = df.index.str.replace(" Total Input", "_H2")

# Rename indices and update units
replacements = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make the renames more consistent with the most commonly used names?

hydrogen-input
co2-input
electricity-input etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the names to the most commonly used names (eg. methane-output insect of Methane Output).
for the final DF and csv file.
Note that the index names set in biomethanation_dea (like "El-Input", "H-Output") (the lines you mentioned) are only used as search keys by str.contains() in the filtering at lines 2155–2204 and 2312–2340. They never appear in the final CSV.

"Hydrogen Consumption": "Hydrogen Input",
"CO2 Consumption": "CO2 Input",
"Electricity Consumption": "El-Input",
"Methane Output": "Methane Output",
"Heat Output": "H-Output",
}

old_units = {
"Hydrogen Consumption": "MWh/",
"CO2 Consumption": "Nm3",
"Electricity Consumption": "MWh/",
"Methane Output": "MWh/",
"Heat Output": "MWh/",
}

new_units = {
"Hydrogen Consumption": "MWh_H2/",
"CO2 Consumption": "t_CO2",
"Electricity Consumption": "MWh_e/",
"Methane Output": "MWh_CH4/",
"Heat Output": "MWh_th/",
}

for old_label, new_label in replacements.items():
matches = df.index[df.index.str.contains(old_label)]
if not matches.empty:
old_index = matches[0]
updated_index = old_index.replace(old_label, new_label)
updated_index = updated_index.replace(
old_units[old_label], new_units[old_label]
)
df.rename(index={old_index: updated_index}, inplace=True)

return df


def biochar_pyrolysis_harmonise_dea(df: pd.DataFrame) -> pd.DataFrame:
"""
The function harmonises biochar and pyrolysis costs.
Expand Down Expand Up @@ -1926,6 +2017,7 @@ def order_data(years: list, technology_dataframe: pd.DataFrame) -> pd.DataFrame:
| (df.unit == "EUR/MW input")
| (df.unit == "EUR/MW-methanol")
| (df.unit == "EUR/t_N2/h") # air separation unit
| (df.unit == "EUR/MW_H2")
| (df.unit == "EUR/MW_biochar")
)
].copy()
Expand Down Expand Up @@ -1960,6 +2052,7 @@ def order_data(years: list, technology_dataframe: pd.DataFrame) -> pd.DataFrame:
| (df.unit == "EUR/MW_MeOH/year")
| (df.unit == "EUR/MW_CH4/year")
| (df.unit == "EUR/MW_biochar/year")
| (df.unit == "EUR/MW_H2/year")
| (df.unit == "% of specific investment/year")
| (df.unit == investment.unit.str.split(" ").iloc[0][0] + "/year")
)
Expand Down Expand Up @@ -2008,6 +2101,7 @@ def order_data(years: list, technology_dataframe: pd.DataFrame) -> pd.DataFrame:
| (df.unit == "EUR/MWhoutput")
| (df.unit == "EUR/MWh_CH4")
| (df.unit == "EUR/MWh_biochar")
| (df.unit == "EUR/MWh_H2")
| (tech_name == "biogas upgrading")
)
].copy()
Expand Down Expand Up @@ -2074,6 +2168,15 @@ def order_data(years: list, technology_dataframe: pd.DataFrame) -> pd.DataFrame:
| (df.index.str.contains("District Heat Output,"))
| (df.index.str.contains("Bio SNG"))
| (df.index.str.contains("biochar"))
| (df.index.str.contains("biomethanation"))
| (df.index.str.contains("H-Output"))
| (df.index.str.contains("Hydrogen Input"))
| (df.index.str.contains("CO2 Input"))
| (df.index.str.contains("SNG Output"))
| (df.index.str.contains("Biogas Consumption"))
| (df.index.str.contains("Methane Output"))
| (df.index.str.contains("Biomass Input"))
| (df.index.str.contains("El-Input"))
| (df.index == ("Hydrogen"))
)
& (
Expand All @@ -2091,6 +2194,12 @@ def order_data(years: list, technology_dataframe: pd.DataFrame) -> pd.DataFrame:
| df.unit.str.contains("MWh_biochar/MWh_feedstock")
| df.unit.str.contains("ton biochar/MWh_feedstock")
| df.unit.str.contains("MWh_CH4/MWh_H2")
| df.unit.str.contains("t_CO2/MWh_H2")
| df.unit.str.contains("MWh_e/MWh_H2")
| df.unit.str.contains("MWh_CH4/MWh_H2")
| df.unit.str.contains("MWh/MWh_H2")
| df.unit.str.contains("MWh_th/MWh_H2")
| df.unit.str.contains("/MWh_H2")
| df.unit.str.contains("% MWh_feedstock")
)
].copy()
Expand Down Expand Up @@ -2200,6 +2309,36 @@ def order_data(years: list, technology_dataframe: pd.DataFrame) -> pd.DataFrame:
efficiency_heat["parameter"] = "efficiency-heat"
clean_df[tech_name] = pd.concat([clean_df[tech_name], efficiency_heat])

elif tech_name == "biomethanation":
h2_input = efficiency[
efficiency.index.str.contains("Hydrogen Input")
].copy()
h2_input["parameter"] = "hydrogen-input"
clean_df[tech_name] = pd.concat([clean_df[tech_name], h2_input])
co2_input = efficiency[efficiency.index.str.contains("CO2 Input")].copy()
co2_input["parameter"] = "CO2-input"
clean_df[tech_name] = pd.concat([clean_df[tech_name], co2_input])
efficiency_heat_out = efficiency[
efficiency.index.str.contains("H-Output")
].copy()
efficiency_heat_out["parameter"] = "heat-output"
clean_df[tech_name] = pd.concat([clean_df[tech_name], efficiency_heat_out])
biomass_input = efficiency[
efficiency.index.str.contains("Methane Output")
].copy()
biomass_input["parameter"] = "methane-output"
clean_df[tech_name] = pd.concat([clean_df[tech_name], biomass_input])
electricity_input = efficiency[
efficiency.index.str.contains("El-Input")
].copy()
electricity_input["parameter"] = "electricity-input"
clean_df[tech_name] = pd.concat([clean_df[tech_name], electricity_input])
biogas_input = efficiency[
efficiency.index.str.contains("Biogas Consumption")
].copy()
biogas_input["parameter"] = "biogas-input"
clean_df[tech_name] = pd.concat([clean_df[tech_name], biogas_input])

elif len(efficiency) != 1:
switch = True
if len(efficiency) == 0 or not any(
Expand Down Expand Up @@ -2709,6 +2848,67 @@ def add_carbon_capture(
return new_technology_dataframe


def add_biomethanation_CO2(
years: list,
sheet_names_dict: dict,
new_technology_dataframe: pd.DataFrame,
technology_dataframe: pd.DataFrame,
) -> pd.DataFrame:
"""
Biomethanation from pure CO2 and H2, it is obtained from the biomethanation (biogas)
technology with scaling of cost for the different inlet volumen flow and recalculation of energy and mass balance for the different inlet.

Parameters
----------
years : list
Years for which a cost assumption is provided (e.g. [2020, 2025, ...]).
sheet_names_dict : dict
Dictionary having the technology name as keys and source/sheet references as values.
(Used for "further description" to match repository conventions.)
new_technology_dataframe : pandas.DataFrame
DataFrame to be filled/updated with the new technology data.
technology_dataframe : pandas.DataFrame
Existing technology data cost assumptions (used to reference e.g. biogas CAPEX).

Returns
-------
pandas.DataFrame
Updated technology data with "perennials gbr".
"""

tech_name = "biomethanation CO2"
base_tech_name = "biomethanation"

# Cost scaling assumptions:
# - Cost proportional to reactor volume
# - Residence time is the same between BG and CO2 cases
# - Volumetric flow (product) is proportional to reactor volume
# - T, P conditions for product (biomethane) do not vary between the two cases
# - Investment cost scales by the CH4 output ratio between the two cases
output_ratio = (1 - technology_dataframe.loc[(base_tech_name, "biogas-input"), years]
/ technology_dataframe.loc[(base_tech_name, "methane-output"), years])

# Copy all entries from base technology unchanged (source, descriptions, units, etc.)
for param in technology_dataframe.loc[base_tech_name].index:
new_technology_dataframe.loc[(tech_name, param), :] = technology_dataframe.loc[(base_tech_name, param), :]

# Override only the two entries that differ (scaled by volumetric flow ratio)
new_technology_dataframe.loc[(tech_name, "methane-output"), years] = (
technology_dataframe.loc[(base_tech_name, "methane-output"), years] * output_ratio
)
new_technology_dataframe.loc[(tech_name, "investment"), years] = (
technology_dataframe.loc[(base_tech_name, "investment"), years] * output_ratio
)
new_technology_dataframe.loc[(tech_name, "investment"), "further description"] = (
"scaled from biomethanation based on volumetric flow"
)

# Remove "Biogas Input" as it is not applicable for the pure CO2 case
new_technology_dataframe.drop((tech_name, "biogas-input"), inplace=True)

return new_technology_dataframe


def rename_pypsa_old(cost_dataframe_pypsa: pd.DataFrame) -> pd.DataFrame:
"""
The function renames old technology names to new ones to compare converts units from water tanks to compare.
Expand Down Expand Up @@ -4088,6 +4288,8 @@ def add_energy_storage_database(
data = convert_units(years_list, data)
# add carbon capture
data = add_carbon_capture(years_list, dea_sheet_names, data, tech_data)
# add biomethanation from pure CO2
data = add_biomethanation_CO2(years_list, dea_sheet_names, data, data)

# adjust for inflation
for x in data.index.get_level_values("technology"):
Expand Down