Generation of LCA impact scores¶
This notebook aims to show how to generate Life-Cycle Assessment (LCA) impact scores to be used in EnergyScope. LCA impact scores can be used within modelling constraints, e.g., upper limit on life-cycle greenhouse gas emissions, or in the objective function, e.g., minimizing the total damage on human health. LCA impact scores are generated and integrated in EnergyScope using the Python package mescal ⧉. The latter applies several transformations on LCA data to ensure the alignement between (your version of) EnergyScope and LCA models. For instance, mescal performs double-counting removal (to avoid the overestimation of flows that are already represented in EnergyScope, e.g., energy flows), technological parameters harmonization (e.g., lifetime, efficiency, capacity factors), and normalization (to ease the solving process in EnergyScope).
In this notebook, we illustrate the use of mescal with the core model of EnergyScope. We employed a prospective LCA database from premise ⧉ (i.e., TIAM-UCL with scenario SSP2-RCP2.6), but the overall methodology is agnostic to the used LCA database and even to the version of EnergyScope you are using. You should simply adapt the input data files accordingly, following the example ones from the core model.
We show how to:
- create the ESM database (i.e., the database containing the datasets corresponding to the technologies and resources of the model) in your brightway2 project
- perform life-cycle impact assessment and contribution analyses
- create the .mod and .dat files for EnergyScope
- visualise the results using mescal's ⧉ visualisation features
%pip install mescal == 1.2.3
There is currently a dependency issue between energyscope library and mescal regarding numpy: energyscope requires numpy >= 2 while brightway2 (needed in mescal) requires numpy<2. However, you can keep numpy<2 in the present notebook as we do not use energyscope library.
Project setup¶
To be able to run the following script, you need to have already set up a Brightway2 project with ecoinvent and premise databases. You can refer to this documentation ⧉ to set up Brightway2 and this notebook ⧉ to set up premise databases in your Brightway2 project.
# Import the required libraries
from mescal import *
import pandas as pd
import bw2data as bd
location = 'BE' # Set the ecoinvent location code corresponding to your EnergyScope model here (e.g., 'BE' for Belgium)
year = 2050 # Set the year here (e.g., 2050)
# Set the name of your main LCI database (e.g., ecoinvent or premise database) here:
lca_db_name = f"ecoinvent_cutoff_3.10.1_tiam-ucl_SSP2-RCP26_{year}"
# Set the name of the new database with the ESM results
esm_db_name = f'EnergyScope_{location}_{year}'
# Set the list of LCIA methods for which you want indicators (they must be registered in your brightway project)
lcia_methods=['IMPACT World+ Midpoint 2.1 for ecoinvent v3.10', 'IMPACT World+ Damage 2.1 for ecoinvent v3.10']
Note: you can download IMPACT World+ methods in your brightway project following this notebook ⧉. Regionalized versions of ecoinvent and IMPACT World+ methods can be downloaded from this repository ⧉.
# Set up your Brightway project
bd.projects.set_current('ecoinvent3.10.1') # put the name of your brightway project here
# Load the LCI database from your brightway project
lca_db = Database(lca_db_name, create_pickle=True)
Getting activity data
100%|██████████| 36014/36014 [00:00<00:00, 79358.33it/s]
Adding exchange data to activities
100%|██████████| 1182964/1182964 [01:12<00:00, 16415.79it/s]
Filling out exchange data
100%|██████████| 36014/36014 [00:05<00:00, 6381.16it/s] 2025-12-11 12:31:58,132 - Database - INFO - Loaded ecoinvent_cutoff_3.10.1_tiam-ucl_SSP2-RCP26_2050 from brightway!
ecoinvent_cutoff_3.10.1_tiam-ucl_SSP2-RCP26_2050.pickle created!
# If necessary, add missing CPC categories to the database
lca_db.add_CPC_categories(overwrite_existing_CPC=True)
Input data¶
path_to_input_files = 'path/to/your/input/files/' # put here the path to the folder containing the input data files
Input data files can be retrieved from mescal GitHub repository ⧉
mapping = pd.read_csv(path_to_input_files+'mapping.csv')
unit_conversion = pd.read_excel(path_to_input_files+'unit_conversion.xlsx')
mapping_esm_flows_to_CPC = pd.read_csv(path_to_input_files+'mapping_esm_flows_to_CPC.csv')
model = pd.read_csv(path_to_input_files+'model.csv')
technology_compositions = pd.read_csv(path_to_input_files+'technology_compositions.csv')
technology_specifics = pd.read_csv(path_to_input_files+'technology_specifics.csv')
lifetime = pd.read_csv(path_to_input_files+'lifetime.csv')
efficiency = pd.read_csv(path_to_input_files+'efficiency.csv')
impact_abbrev = pd.read_csv(path_to_input_files+'impact_abbrev.csv')
mapping.Database = lca_db_name # set the database name in the mapping file
Initialize the ESM class¶
esm = ESM(
mapping=mapping,
unit_conversion=unit_conversion,
model=model,
mapping_esm_flows_to_CPC_cat=mapping_esm_flows_to_CPC,
main_database=lca_db,
esm_db_name=esm_db_name,
esm_location=location,
technology_compositions=technology_compositions,
tech_specifics=technology_specifics,
lifetime=lifetime,
efficiency=efficiency,
regionalize_foregrounds=['Operation', 'Resource'], # types of LCI datasets that will be regionalized
locations_ranking=['BE', 'RER', 'WEU', 'CEU', 'GLO', 'RoW'], # order of preference for locations when regionalizing
results_path_file='lca_results/',
remove_double_counting_to=['Operation', 'Construction'], # we mention Construction as end-of-life flows from construction are extracted separately
extract_eol_from_construction=True,
)
# Update mapping dataframe with better locations
esm.change_location_mapping_file()
esm.main_database.test_mapping_file(esm.mapping) # test the mapping file
2025-12-11 12:32:27,834 - Database - INFO - Mapping successfully linked to the database
[]
esm.clean_inputs()
esm.check_inputs()
2025-12-11 12:32:34,659 - Mescal - WARNING - There are duplicates in the unit_conversion dataframe. Please check your inputs. 2025-12-11 12:32:34,738 - Mescal - WARNING - List of technologies or resources that are in the model file but not in the mapping file. Their impact scores will be set to the default value: ['CO2_ATM', 'CO2_CAPTURED', 'CO2_EMISSIONS', 'CO2_INDUSTRY', 'RES_GEO', 'RES_HYDRO', 'RES_SOLAR', 'RES_WIND'] 2025-12-11 12:32:34,843 - Mescal - WARNING - Some technologies have no lifetime value for LCA in the lifetime file. Therefore, lifetime harmonization with the ESM will not be performed during the LCIA phase and capacity factor harmonization during the feedback of ESM results will not be performed either for those technologies: ['DEC_DIRECT_ELEC', 'DEC_SOLAR', 'DHN_DEEP_GEO', 'DHN_SOLAR', 'GEOTHERMAL', 'IND_DIRECT_ELEC']
Create the ESM database in your Brightway2 project¶
esm.create_esm_database()
2025-12-11 12:32:50,420 - Mescal - INFO - Starting to remove double-counted flows 100%|██████████| 109/109 [00:00<00:00, 703.93it/s] 25%|██▌ | 19/75 [00:01<00:02, 20.73it/s]2025-12-11 12:33:03,241 - Mescal - WARNING - No location found in your ranking for (aluminium, primary, ingot, aluminium, ingot, primary, import from Rest of Europe) in the database ecoinvent_cutoff_3.10.1_tiam-ucl_SSP2-RCP26_2050. Have to keep the initial location: IAI Area, EU27 & EFTA 100%|██████████| 75/75 [00:04<00:00, 15.46it/s] 2025-12-11 12:33:06,928 - Mescal - INFO - Double-counting removal done in 16.5 seconds 2025-12-11 12:33:07,155 - Mescal - INFO - Starting to correct efficiency differences 2025-12-11 12:33:07,203 - Mescal - WARNING - No flow found for type(s) ['WET_BIOMASS'] in BIOMETHANATION. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,235 - Mescal - WARNING - No flow found for type(s) ['METHANOL', 'TRANSPORT_FUEL'] in BOAT_FREIGHT_METHANOL. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,251 - Mescal - WARNING - No flow found for type(s) ['GAS', 'TRANSPORT_FUEL'] in BOAT_FREIGHT_NG. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,378 - Mescal - WARNING - No flow found for type(s) ['GAS'] in CCGT. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,426 - Mescal - WARNING - No flow found for type(s) ['GAS'] in DEC_ADVCOGEN_GAS. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,474 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in DEC_BOILER_WOOD. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,579 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in DHN_BOILER_WOOD. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,595 - Mescal - WARNING - No flow found for type(s) ['WET_BIOMASS'] in DHN_COGEN_BIO_HYDROLYSIS. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,627 - Mescal - WARNING - No flow found for type(s) ['WASTE'] in DHN_COGEN_WASTE. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,647 - Mescal - WARNING - No flow found for type(s) ['WET_BIOMASS'] in DHN_COGEN_WET_BIOMASS. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,659 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in DHN_COGEN_WOOD. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,691 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in H2_BIOMASS. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,755 - Mescal - WARNING - No flow found for type(s) ['WASTE'] in IND_BOILER_WASTE. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,771 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in IND_BOILER_WOOD. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,803 - Mescal - WARNING - No flow found for type(s) ['WASTE'] in IND_COGEN_WASTE. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,820 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in IND_COGEN_WOOD. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,867 - Mescal - WARNING - No flow found for type(s) ['WOOD'] in PYROLYSIS_TO_LFO. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:07,915 - Mescal - WARNING - No flow found for type(s) ['METHANOL'] in TRUCK_METHANOL. The efficiency of this technology cannot be adjusted. 2025-12-11 12:33:08,417 - Mescal - WARNING - No flow of type TRANSPORT_FUEL has been removed in BOAT_FREIGHT_DIESEL. 2025-12-11 12:33:08,433 - Mescal - WARNING - Flow of type TRANSPORT_FUEL found for BOAT_FREIGHT_DIESEL in efficiency file, but not in model file. 2025-12-11 12:33:08,449 - Mescal - WARNING - Flow of type TRANSPORT_FUEL found for BOAT_FREIGHT_DIESEL in efficiency file, but not in unit conversion file. 2025-12-11 12:33:08,611 - Mescal - INFO - Efficiency differences corrected in 1.5 seconds 2025-12-11 12:33:09,525 - Mescal - INFO - Starting to write database Writing activities to SQLite3 database: 0% [##############################] 100% | ETA: 00:00:00 Total time elapsed: 00:00:00
Title: Writing activities to SQLite3 database: Started: 12/11/2025 12:33:47 Finished: 12/11/2025 12:33:47 Total time elapsed: 00:00:00 CPU %: 93.10 Memory %: 2.17
2025-12-11 12:35:03,989 - Database - INFO - EnergyScope_BE_2050 written to Brightway! 2025-12-11 12:35:04,113 - Mescal - INFO - Database written in 114.6 seconds
Compute LCA impact scores and perform contribution analysis¶
impact_scores, contrib_analysis_res, _ = esm.compute_impact_scores(
methods=lcia_methods,
contribution_analysis='both',
)
Getting activity data
100%|██████████| 410/410 [00:00<?, ?it/s]
Adding exchange data to activities
100%|██████████| 8979/8979 [00:00<00:00, 18401.79it/s]
Filling out exchange data
100%|██████████| 410/410 [00:01<00:00, 224.93it/s] 2025-12-11 12:35:06,646 - Database - INFO - Loaded EnergyScope_BE_2050 from brightway! 280it [02:20, 1.99it/s]
impact_scores.to_csv(esm.results_path_file+'impact_scores.csv', index=False)
contrib_analysis_res.to_csv(esm.results_path_file+'contribution_analysis.csv', index=False)
impact_scores_direct_emissions, _, _ = esm.compute_impact_scores(
methods=lcia_methods,
assessment_type='direct emissions', # specific metrics for direct emissions during operation
)
Getting activity data
100%|██████████| 410/410 [00:00<?, ?it/s]
Adding exchange data to activities
100%|██████████| 8979/8979 [00:00<00:00, 24381.05it/s]
Filling out exchange data
100%|██████████| 410/410 [00:02<00:00, 185.72it/s] 2025-12-11 12:37:55,149 - Database - INFO - Loaded EnergyScope_BE_2050 from brightway! Writing activities to SQLite3 database: 0% [##############################] 100% | ETA: 00:00:00 Total time elapsed: 00:00:00
Title: Writing activities to SQLite3 database: Started: 12/11/2025 12:37:56 Finished: 12/11/2025 12:37:56 Total time elapsed: 00:00:00 CPU %: 76.30 Memory %: 4.03
2025-12-11 12:37:57,826 - Database - INFO - EnergyScope_BE_2050_direct_emissions written to Brightway! 75it [00:01, 74.15it/s]
impact_scores_direct_emissions.to_csv(esm.results_path_file+'impact_scores_direct_emissions.csv', index=False)
Create .dat and .mod files for AMPL integration¶
By default, the resulting .dat and .mod files are saved in the result directory specified in the ESM class initialization (here 'lca_results/').
We can select a few impact categories that we want to integrate in the model. Here, we select only two categories: TTHH (Total human health) and TTEQ (Total ecosystem quality). The abbreviations are defined in the impact_abbrev.csv file.
specific_lca_abbrev = ['TTHH', 'TTEQ']
esm.normalize_lca_metrics(
R=impact_scores,
mip_gap=1e-6,
lcia_methods=lcia_methods,
specific_lcia_abbrev=specific_lca_abbrev,
impact_abbrev=impact_abbrev,
file_name='techs_lca',
)
# specific for direct emissions metrics
esm.normalize_lca_metrics(
R=impact_scores,
R_direct=impact_scores_direct_emissions, # thus we specify this
mip_gap=1e-6,
lcia_methods=lcia_methods,
specific_lcia_abbrev=specific_lca_abbrev,
assessment_type='direct emissions', # and this
impact_abbrev=impact_abbrev,
file_name='techs_lca_direct',
)
esm.generate_mod_file_ampl(
lcia_methods=lcia_methods,
impact_abbrev=impact_abbrev,
specific_lcia_abbrev=specific_lca_abbrev,
file_name='objectives_lca',
energyscope_version='core',
)
# specific for direct emissions metrics
esm.generate_mod_file_ampl(
lcia_methods=lcia_methods,
specific_lcia_abbrev=specific_lca_abbrev,
assessment_type='direct emissions', # specify this
impact_abbrev=impact_abbrev,
file_name='objectives_lca_direct',
energyscope_version='core',
)
Visualize the LCA impact scores¶
plot = Plot(
df_impact_scores=impact_scores,
lifetime=lifetime, # used to visualise infrastructure impacts per kW.year
)
plot.plot_indicators_of_technologies_for_one_impact_category(
technologies_list=[
'PV',
'WIND_ONSHORE',
'CCGT',
'COAL_IGCC',
'NUCLEAR',
],
impact_category=(
'IMPACT World+ Midpoint 2.1 for ecoinvent v3.10',
'Midpoint',
'Climate change, short term',
),
metadata={
'operation_unit': 'kWh',
'construction_unit': 'kW',
'technologies_type': 'electricity production',
'impact_category_unit': 'kg CO2 eq (short)',
},
)
plot.plot_indicators_of_technologies_for_one_impact_category(
technologies_list=[
'PV',
'WIND_ONSHORE',
'CCGT',
'COAL_IGCC',
'NUCLEAR',
],
impact_category=(
'IMPACT World+ Damage 2.1 for ecoinvent v3.10',
'Human health',
'Total human health',
),
metadata={
'operation_unit': 'kWh',
'construction_unit': 'kW',
'technologies_type': 'electricity production',
'impact_category_unit': 'DALY',
},
contributions_total_score=True,
)
plot.plot_indicators_of_technologies_for_one_impact_category(
technologies_list=[
'PV',
'WIND_ONSHORE',
'CCGT',
'COAL_IGCC',
'NUCLEAR',
],
impact_category=(
'IMPACT World+ Damage 2.1 for ecoinvent v3.10',
'Ecosystem quality',
'Total ecosystem quality',
),
metadata={
'operation_unit': 'kWh',
'construction_unit': 'kW',
'technologies_type': 'electricity production',
'impact_category_unit': 'PDF.m2.yr',
},
contributions_total_score=True,
)
plot.plot_indicators_of_technologies_for_several_impact_categories(
technologies_list=[
'PV',
'WIND_ONSHORE',
'CCGT',
'COAL_IGCC',
'NUCLEAR',
],
impact_categories_units={
(
'IMPACT World+ Damage 2.1 for ecoinvent v3.10',
'Ecosystem quality',
'Total ecosystem quality',
): "PDF.m2.yr",
(
'IMPACT World+ Damage 2.1 for ecoinvent v3.10',
'Human health',
'Total human health',
): "DALY",
}
)
plot.plot_indicators_of_resources_for_several_impact_categories(
resources_list=['COAL', 'GAS', 'URANIUM'],
impact_categories_units={
(
'IMPACT World+ Damage 2.1 for ecoinvent v3.10',
'Ecosystem quality',
'Total ecosystem quality',
): "PDF.m2.yr",
(
'IMPACT World+ Damage 2.1 for ecoinvent v3.10',
'Human health',
'Total human health',
): "DALY",
}
)