Skip to content

Commit

Permalink
added lca_excel.py and select_values.py
Browse files Browse the repository at this point in the history
  • Loading branch information
Nair, Rahul Ramesh committed Nov 6, 2024
1 parent 48c2d89 commit d16860a
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 7 deletions.
5 changes: 5 additions & 0 deletions bwutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@
except importlib.metadata.PackageNotFoundError:
brightway_version = "Not installed"

if brightway_version != "Not installed" and not brightway_version.startswith("2.4"):
print(
f"Warning: bwutils is designed for Brightway2 version 2.4.x. "
f"Detected version {brightway_version}. You may experience compatibility issues."
)
__brightway_version__ = brightway_version
80 changes: 80 additions & 0 deletions bwutils/lca_excel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
lca_excel.py
This module provides functions for exporting Life Cycle Assessment (LCA) calculation results
to an Excel format, with each result from an impact categort stored in a separate sheet, which is
a recurring theme in lca-related calculations.
Functions:
- clean_sheet_name(name, max_length=30): Cleans and truncates sheet names for Excel compatibility.
- result_to_excel(results, methods_of_interest, filename): Exports LCA results to an Excel file
with a final sheet summarizing impact assessment methods.
Dependencies:
- pandas
- openpyxl (since pandas does not automatically install this)
"""

import re
import os
import pandas as pd


def clean_sheet_name(name, max_length=30):
"""Cleans and truncates a sheet name to make it compatible with Excel's requirements.
This function removes invalid characters, trims leading and trailing underscores,
and ensures that the name does not exceed the specified maximum length.
Args:
name (str): The original sheet name that may contain invalid characters.
max_length (int): Maximum allowed length for the sheet name. Defaults to 30.
Returns:
str: A cleaned and truncated version of the sheet name, compatible with Excel.
"""
# Remove invalid characters and trim whitespace, quotes, and brackets
name = re.sub(
r"[\\/*?:\[\]']", "_", name
) # Replace invalid characters with underscores
name = re.sub(r"[(),]", "", name) # Remove parentheses and commas
name = name.strip("_") # Remove leading/trailing underscores
return name[:max_length] # Ensure the name does not exceed the maximum length


def result_to_excel(results, methods_of_interest, filename):
"""Exports LCA calculation results to an Excel file, with each result in a separate sheet.
This function takes a dictionary of LCA calculation results, each associated with a specific
impact assessment method, and writes them to separate sheets in an Excel file. Additionally,
it creates a final sheet with details of the impact assessment methods such as
method names, impact categories, and units.
Args:
results (dict): A dictionary where keys are method names (or other identifiers) and
values are pandas DataFrames containing LCA results for each method.
methods_of_interest (list of tuples): A list of tuples, each containing:
- Method Name (str): The name of the impact assessment method.
- Impact Category (str): The impact category associated with the method.
- Unit (str): The unit of measurement for the impact category.
filename (str): The path where the Excel file should be saved.
Returns:
str: The absolute path to the saved Excel file.
"""
impact_assessment_methods_df = pd.DataFrame(
methods_of_interest,
columns=["Method Name", "Impact category (i.e sheet name here)", "Unit"],
)
with pd.ExcelWriter(filename) as writer:
for method, df in results.items():
sheet_name = clean_sheet_name(str(method))
df.to_excel(writer, sheet_name=sheet_name, index=False)
# Add impact assessment methods as the final sheet
impact_assessment_methods_df.to_excel(
writer, sheet_name="IA Methods", index=False
)
file_path = os.path.abspath(filename)
print(f"Results written to {file_path}")
118 changes: 118 additions & 0 deletions bwutils/select_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
select_values.py
This module provides some functions for LCA workflows in Jupyter notebook
which require numerous user inputs such as selecting or changing impact assessment methods.
"""

import ipywidgets as widgets
from IPython.display import display
import os


def result_folders(folder_path, result_direcs):
"""Creates folders for storing results if they do not already exist.
Args:
folder_path (str): The base folder path where result directories should be created.
In Windows, use double backslash to enter the path. Eg. "D:\\test"
result_direcs (list of str): A list of folder/directory names to create within the base folder.
Prints:
str: A message indicating the location of folders that were created.
"""

if not os.path.exists(folder_path):
os.mkdir(folder_path)
print(f"Base folder created at: {os.path.abspath(folder_path)}")
else:
print(f"Base folder already exists at: {os.path.abspath(folder_path)}")
# creates directories for storing results with in the base folder.
for direc in result_direcs:
sub_folder_path = os.path.join(folder_path, direc)
if not os.path.exists(sub_folder_path):
os.mkdir(sub_folder_path)
print(f"Subfolder created at: {os.path.abspath(sub_folder_path)}")
else:
print(f"Subfolder already exists at: {os.path.abspath(sub_folder_path)}")


def single_select_dropdown(names):
"""Creates a single-selection dropdown widget for users to select an option.
Args:
names (list of str): A list of names/options to be displayed in the dropdown.
Returns:
dict: A dictionary containing the selected value with the key 'value'.
"""

if not names:
raise ValueError("The 'names' list cannot be empty.")

dropdown = widgets.Dropdown(
options=names,
value=names[0], # Default value
description="Assessment methodology:",
disabled=False,
)

# Display the dropdown widget
display(dropdown)

# Variable to store the selected value
selected_value = {"value": dropdown.value}

# Function to update the selected value
def on_change(change):
selected_value["value"] = change["new"]
print(f"You selected: {selected_value['value']}")

# Attach the function to the dropdown
dropdown.observe(on_change, names="value")

return selected_value


def multi_select_dropdown(names):
"""Creates a multi-selection dropdown widget for users to select multiple options.
Args:
names (list of str): A list of names/options to be displayed in the multi-select widget.
Returns:
dict: A dictionary containing the selected values with the key 'values'.
"""

if not names:
raise ValueError("The 'names' list cannot be empty.")

height_of_each_dropdown_item = (
20 # sets a default height in pixel for dropdown items
)
dropdown_box_height = (
len(names) * height_of_each_dropdown_item + 20
) # estimates the total height of the drop down window based on the total number of items.
multi_select = widgets.SelectMultiple(
options=names,
value=[], # Default value, an empty list
description="Impact Categories:",
disabled=False,
layout=widgets.Layout(width="100%", height=f"{dropdown_box_height}px"),
)

# Display the multiple selection widget
display(multi_select)

# Variable to store the selected values
selected_values = {"values": multi_select.value}

# Function to update the selected values
def on_change_multiple_selection(change):
selected_values["values"] = list(change["new"])
print(f"You selected: {selected_values['values']}")

# Attach the function to the multiple selection widget
multi_select.observe(on_change_multiple_selection, names="value")

return selected_values
172 changes: 166 additions & 6 deletions notebooks/contribution.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,183 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import bwutils\n",
"\n",
"print(\n",
" bwutils.__brightway_version__\n",
") # checks the active brightway version. It should be 2.4.x"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from bwutils import select_values\n",
"\n",
"base_folder_name = \"D:\\\\test1.1\"\n",
"res_directory_names = [\"test1\", \"test2\"]\n",
"select_values.result_folders(base_folder_name, res_directory_names)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "5f287766a312468f97f1904926095da5",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Dropdown(description='Assessment methodology:', options=('gum', 'dom', 'hola', 'hellow', 'meow'), value='gum')"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"{'value': 'gum'}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"2.4.7\n"
"You selected: hellow\n"
]
}
],
"source": [
"import bwutils\n",
"from bwutils import select_values\n",
"\n",
"print(\n",
" bwutils.__brightway_version__\n",
") # checks the active brightway version. It should be 2.4.x"
"random_names = [\"gum\", \"dom\", \"hola\", \"hellow\", \"meow\"]\n",
"select_values.single_select_dropdown(random_names)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"method_names = ia_methods = [\n",
" (\"EF v3.1\", \"acidification\", \"accumulated exceedance (AE)\"),\n",
" (\"EF v3.1\", \"climate change\", \"global warming potential (GWP100)\"),\n",
" (\n",
" \"EF v3.1\",\n",
" \"ecotoxicity: freshwater\",\n",
" \"comparative toxic unit for ecosystems (CTUe)\",\n",
" ),\n",
" (\n",
" \"EF v3.1\",\n",
" \"energy resources: non-renewable\",\n",
" \"abiotic depletion potential (ADP): fossil fuels\",\n",
" ),\n",
" (\n",
" \"EF v3.1\",\n",
" \"eutrophication: freshwater\",\n",
" \"fraction of nutrients reaching freshwater end compartment (P)\",\n",
" ),\n",
" (\n",
" \"EF v3.1\",\n",
" \"eutrophication: marine\",\n",
" \"fraction of nutrients reaching marine end compartment (N)\",\n",
" ),\n",
" (\"EF v3.1\", \"eutrophication: terrestrial\", \"accumulated exceedance (AE)\"),\n",
" (\n",
" \"EF v3.1\",\n",
" \"human toxicity: carcinogenic\",\n",
" \"comparative toxic unit for human (CTUh)\",\n",
" ),\n",
" (\n",
" \"EF v3.1\",\n",
" \"ionising radiation: human health\",\n",
" \"human exposure efficiency relative to u235\",\n",
" ),\n",
" (\"EF v3.1\", \"land use\", \"soil quality index\"),\n",
" (\n",
" \"EF v3.1\",\n",
" \"material resources: metals/minerals\",\n",
" \"abiotic depletion potential (ADP): elements (ultimate reserves)\",\n",
" ),\n",
" (\"EF v3.1\", \"ozone depletion\", \"ozone depletion potential (ODP)\"),\n",
" (\"EF v3.1\", \"particulate matter formation\", \"impact on human health\"),\n",
" (\n",
" \"EF v3.1\",\n",
" \"photochemical oxidant formation: human health\",\n",
" \"tropospheric ozone concentration increase\",\n",
" ),\n",
" (\n",
" \"EF v3.1\",\n",
" \"water use\",\n",
" \"user deprivation potential (deprivation-weighted water consumption)\",\n",
" ),\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4c986201f5cc47df869eaf0be30fa014",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"SelectMultiple(description='Impact Categories:', layout=Layout(height='320px', width='100%'), options=(('EF v3…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"{'values': ()}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"You selected: [('EF v3.1', 'climate change', 'global warming potential (GWP100)')]\n",
"You selected: [('EF v3.1', 'ecotoxicity: freshwater', 'comparative toxic unit for ecosystems (CTUe)')]\n",
"You selected: [('EF v3.1', 'energy resources: non-renewable', 'abiotic depletion potential (ADP): fossil fuels')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)'), ('EF v3.1', 'eutrophication: marine', 'fraction of nutrients reaching marine end compartment (N)')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)'), ('EF v3.1', 'eutrophication: marine', 'fraction of nutrients reaching marine end compartment (N)'), ('EF v3.1', 'eutrophication: terrestrial', 'accumulated exceedance (AE)')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)'), ('EF v3.1', 'eutrophication: marine', 'fraction of nutrients reaching marine end compartment (N)'), ('EF v3.1', 'eutrophication: terrestrial', 'accumulated exceedance (AE)'), ('EF v3.1', 'material resources: metals/minerals', 'abiotic depletion potential (ADP): elements (ultimate reserves)')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)'), ('EF v3.1', 'eutrophication: marine', 'fraction of nutrients reaching marine end compartment (N)'), ('EF v3.1', 'eutrophication: terrestrial', 'accumulated exceedance (AE)'), ('EF v3.1', 'material resources: metals/minerals', 'abiotic depletion potential (ADP): elements (ultimate reserves)'), ('EF v3.1', 'particulate matter formation', 'impact on human health')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)'), ('EF v3.1', 'eutrophication: marine', 'fraction of nutrients reaching marine end compartment (N)'), ('EF v3.1', 'eutrophication: terrestrial', 'accumulated exceedance (AE)'), ('EF v3.1', 'material resources: metals/minerals', 'abiotic depletion potential (ADP): elements (ultimate reserves)'), ('EF v3.1', 'ozone depletion', 'ozone depletion potential (ODP)'), ('EF v3.1', 'particulate matter formation', 'impact on human health')]\n",
"You selected: [('EF v3.1', 'eutrophication: freshwater', 'fraction of nutrients reaching freshwater end compartment (P)'), ('EF v3.1', 'eutrophication: marine', 'fraction of nutrients reaching marine end compartment (N)'), ('EF v3.1', 'eutrophication: terrestrial', 'accumulated exceedance (AE)'), ('EF v3.1', 'material resources: metals/minerals', 'abiotic depletion potential (ADP): elements (ultimate reserves)'), ('EF v3.1', 'ozone depletion', 'ozone depletion potential (ODP)'), ('EF v3.1', 'particulate matter formation', 'impact on human health'), ('EF v3.1', 'photochemical oxidant formation: human health', 'tropospheric ozone concentration increase')]\n"
]
}
],
"source": [
"select_values.multi_select_dropdown(method_names)"
]
}
],
Expand Down
Loading

0 comments on commit d16860a

Please sign in to comment.