Download this example as a Jupyter notebook
or a
Python script
.
Perform a BoM sustainability summary query#
The following supporting files are required for this example:
Info:
This example requires uses a 24/12 XML BoM, which requires Granta MI Restricted Substances and Sustainability Reports 2025 R2 or later. Running this example as-is results in an error due to an unsupported BoM version.
To run this example with an older version of the reports bundle, use sustainability-bom-2301.xml instead. Some sections of this example will produce different results from the published example when this BoM is used.
Run a BoM sustainability summary query#
First, connect to Granta MI.
[1]:
from ansys.grantami.bomanalytics import Connection
[2]:
server_url = "http://my_grantami_server/mi_servicelayer"
cxn = Connection(server_url).with_credentials("user_name", "password").connect()
Next, create a sustainability summary query. The query accepts a single BoM as argument and an optional configuration for units. If a unit is not specified, the default unit is used. Default units for the analysis are MJ
for energy, kg
for mass, and km
for distance.
[3]:
xml_file_path = "supporting-files/sustainability-bom-2412.xml"
with open(xml_file_path) as f:
bom = f.read()
from ansys.grantami.bomanalytics import queries
MASS_UNIT = "kg"
ENERGY_UNIT = "MJ"
DISTANCE_UNIT = "km"
sustainability_summary_query = (
queries.BomSustainabilitySummaryQuery()
.with_bom(bom)
.with_units(mass=MASS_UNIT, energy=ENERGY_UNIT, distance=DISTANCE_UNIT)
)
[4]:
sustainability_summary = cxn.run(sustainability_summary_query)
sustainability_summary
[4]:
<BomSustainabilitySummaryQueryResult>
The BomSustainabilitySummaryQueryResult
object that is returned implements a messages
property and properties showing the environmental impact of the items included in the BoM. Log messages are sorted by decreasing severity. The same messages are available in the MI Service Layer log file and are logged using the standard logging
module. The next sections show examples of visualizations for the results of the sustainability summary query.
Summary per phase#
The sustainability summary result object contains a phases_summary
property. This property summarizes the environmental impact contributions by lifecycle phase: materials, processes, and transport phases. The results for each phase include their absolute and relative contributions to the product as a whole.
[5]:
sustainability_summary.phases_summary
[5]:
[<SustainabilityPhaseSummaryResult('Material', EE%=56.265697714729, CC%=66.69384302032732)>,
<SustainabilityPhaseSummaryResult('Processes', EE%=26.889534104581585, CC%=18.791305748560912)>,
<SustainabilityPhaseSummaryResult('Transport', EE%=16.8447681806894, CC%=14.514851231111756)>]
Use the pandas and plotly libraries to visualize the results. First, the data is translated from the BoM Analytics BomSustainabilitySummaryQueryResult
to a pandas Dataframe
object.
[6]:
import pandas as pd
EE_HEADER = f"EE [{ENERGY_UNIT}]"
CC_HEADER = f"CC [{MASS_UNIT}]"
phases_df = pd.DataFrame.from_records(
[
{
"Name": item.name,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
}
for item in sustainability_summary.phases_summary
]
)
phases_df
[6]:
Name | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|
0 | Material | 56.265698 | 333.680522 | 66.693843 | 32.013029 |
1 | Processes | 26.889534 | 159.466854 | 18.791306 | 9.019822 |
2 | Transport | 16.844768 | 99.896940 | 14.514851 | 6.967125 |
[7]:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def plot_impact(df, title, textinfo="percent+label", hoverinfo="value+name", labels=True):
fig = make_subplots(
rows=1,
cols=2,
specs=[[{"type": "domain"}, {"type": "domain"}]],
subplot_titles=["Embodied Energy", "Climate Change"],
)
fig.add_trace(go.Pie(labels=df["Name"], values=df[EE_HEADER], name=ENERGY_UNIT), 1, 1)
fig.add_trace(go.Pie(labels=df["Name"], values=df[CC_HEADER], name=MASS_UNIT), 1, 2)
fig.update_layout(title_text=title, legend=dict(orientation="h"))
if labels:
fig.update_traces(textposition="inside", textinfo=textinfo, hoverinfo=hoverinfo)
fig.show()
plot_impact(phases_df, "BoM sustainability summary - By phase")
Transport phase#
The environmental contribution from the transport phase is summarized in the transport_details
property. Results include the individual environmental impact for each transport stage included in the input BoM. A BoM may include many transport stages, each describing transportation throughout the product lifecycle. Print the first three only.
[8]:
sustainability_summary.transport_details[:3]
[8]:
[<TransportSummaryResult('Component 11A raw material', EE%=3.7006354945461486, CC%=3.5405664742758214)>,
<TransportSummaryResult('Component 11A as-cast to machining shop', EE%=0.5550953241819223, CC%=0.5310849711413731)>,
<TransportSummaryResult('Finished component 11A to warehouse', EE%=1.6938432542209843, CC%=1.6205769650676105)>]
Convert all to a DataFrame and describe the result
[9]:
DISTANCE_HEADER = f"Distance [{DISTANCE_UNIT}]"
transport_df_full = pd.DataFrame.from_records(
[
{
"Name": item.name,
DISTANCE_HEADER: item.distance.value,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
}
for item in sustainability_summary.transport_details
]
)
transport_df_full.describe()
[9]:
Distance [km] | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|
count | 23.000000 | 23.000000 | 23.000000 | 23.000000 | 23.000000 |
mean | 792.391304 | 4.347826 | 4.343345 | 4.347826 | 0.302918 |
std | 1616.453659 | 10.551622 | 10.540748 | 10.719638 | 0.746851 |
min | 50.000000 | 0.013185 | 0.013171 | 0.012614 | 0.000879 |
25% | 150.000000 | 0.362618 | 0.362244 | 0.346933 | 0.024171 |
50% | 400.000000 | 1.372837 | 1.371422 | 1.313456 | 0.091510 |
75% | 700.000000 | 2.505972 | 2.503390 | 2.397578 | 0.167042 |
max | 8000.000000 | 47.529731 | 47.480747 | 48.247186 | 3.361442 |
Most of these transport stages contribute little to the overall sustainability impact. To make a visualization more insightful, group all transport stages that contribute less than 5% of embodied energy or climate change in a single ‘Other’ transport stage.
[10]:
# Define the criterion
criterion = (transport_df_full["EE%"] < 5.0) | (transport_df_full["CC%"] < 5.0)
# Aggregate all rows that meet the criterion
transport_df_below_5_pct = transport_df_full.loc[criterion].sum(numeric_only=True).to_frame().T
transport_df_below_5_pct["Name"] = "Other"
# Sort all rows that do not meet the criterion by embodied energy
transport_df_over_5_pct = transport_df_full.loc[~(criterion)].sort_values(by="EE%", ascending=False)
# Concatenate the rows together
transport_df = pd.concat([transport_df_over_5_pct, transport_df_below_5_pct], ignore_index=True)
transport_df
[10]:
Name | Distance [km] | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|---|
0 | Finished component 11B to warehouse | 8000.0 | 47.529731 | 47.480747 | 48.247186 | 3.361442 |
1 | Product from warehouse to distributor (air) | 500.0 | 23.278252 | 23.254261 | 23.629634 | 1.646306 |
2 | Product from warehouse to distributor (truck 1) | 350.0 | 5.273438 | 5.268003 | 5.045338 | 0.351515 |
3 | Other | 9375.0 | 23.918579 | 23.893929 | 23.077842 | 1.607862 |
[11]:
plot_impact(transport_df, "Transport stages - environmental impact", labels=False)
Transport impact per unit distance#
In some situations, it might be useful to calculate the environmental impact per distance travelled and add the results as new columns in the dataframe.
[12]:
EE_PER_DISTANCE = f"EE [{ENERGY_UNIT}/{DISTANCE_UNIT}]"
CC_PER_DISTANCE = f"CC [{MASS_UNIT}/{DISTANCE_UNIT}]"
transport_df[EE_PER_DISTANCE] = transport_df.apply(lambda row: row[EE_HEADER] / row[DISTANCE_HEADER], axis=1)
transport_df[CC_PER_DISTANCE] = transport_df.apply(lambda row: row[CC_HEADER] / row[DISTANCE_HEADER], axis=1)
transport_df
[12]:
Name | Distance [km] | EE% | EE [MJ] | CC% | CC [kg] | EE [MJ/km] | CC [kg/km] | |
---|---|---|---|---|---|---|---|---|
0 | Finished component 11B to warehouse | 8000.0 | 47.529731 | 47.480747 | 48.247186 | 3.361442 | 0.005935 | 0.000420 |
1 | Product from warehouse to distributor (air) | 500.0 | 23.278252 | 23.254261 | 23.629634 | 1.646306 | 0.046509 | 0.003293 |
2 | Product from warehouse to distributor (truck 1) | 350.0 | 5.273438 | 5.268003 | 5.045338 | 0.351515 | 0.015051 | 0.001004 |
3 | Other | 9375.0 | 23.918579 | 23.893929 | 23.077842 | 1.607862 | 0.002549 | 0.000172 |
[13]:
fig = make_subplots(
rows=1, cols=2, specs=[[{"type": "domain"}, {"type": "domain"}]], subplot_titles=[EE_PER_DISTANCE, CC_PER_DISTANCE]
)
fig.add_trace(
go.Pie(labels=transport_df["Name"], values=transport_df[EE_PER_DISTANCE], name=f"{ENERGY_UNIT}/{DISTANCE_UNIT}"),
1,
1,
)
fig.add_trace(
go.Pie(labels=transport_df["Name"], values=transport_df[CC_PER_DISTANCE], name=f"{MASS_UNIT}/{DISTANCE_UNIT}"), 1, 2
)
fig.update_layout(
title_text="Transport stages impact - Relative to distance travelled",
legend=dict(orientation="h")
)
fig.show()
Transport impact aggregated by category#
Info:
This section requires Granta MI Restricted Substances and Sustainability Reports 2025 R2. Results will differ from published examples if older software versions are used.
The environmental impacts from transportation associated with distribution and manufacturing phases are summarized in the distribution_transport_summary
and manufacturing_transport_summary
properties.
[14]:
sustainability_summary.distribution_transport_summary
[14]:
<TransportSummaryByCategoryResult(EE%=30.435059969423882, CC%=30.476878034635334)>
[15]:
dist_summary = sustainability_summary.distribution_transport_summary
distribution = {
"Name": "Distribution",
DISTANCE_HEADER: dist_summary.distance.value,
"EE%": dist_summary.embodied_energy_percentage,
EE_HEADER: dist_summary.embodied_energy.value,
"CC%": dist_summary.climate_change_percentage,
CC_HEADER: dist_summary.climate_change.value,
}
manuf_summary = sustainability_summary.manufacturing_transport_summary
manufacturing = {
"Name": "Manufacturing",
DISTANCE_HEADER: manuf_summary.distance.value,
"EE%": manuf_summary.embodied_energy_percentage,
EE_HEADER: manuf_summary.embodied_energy.value,
"CC%": manuf_summary.climate_change_percentage,
CC_HEADER: manuf_summary.climate_change.value,
}
transport_by_category_df = pd.DataFrame.from_records([distribution, manufacturing])
transport_by_category_df
[15]:
Name | Distance [km] | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|---|
0 | Distribution | 975.0 | 30.43506 | 30.403694 | 30.476878 | 2.123362 |
1 | Manufacturing | 17250.0 | 69.56494 | 69.493247 | 69.523122 | 4.843763 |
[16]:
plot_impact(transport_by_category_df, "Transport impact - grouped by category")
Transport impact aggregated by part#
Info:
This section requires Granta MI Restricted Substances and Sustainability Reports 2025 R2. Results will differ from published examples if older software versions are used.
The environmental contributions from transportation are summarized by the associated part in the transport_details_aggregated_by_part
property. This property groups parts that contribute less than 5% embodied energy or climate change automatically.
[17]:
sustainability_summary.transport_details_aggregated_by_part
[17]:
[<TransportSummaryByPartResult('Component 11B', EE%=49.00062821794582, CC%=49.65446009256159)>,
<TransportSummaryByPartResult('Assembly', EE%=30.435059969423882, CC%=30.476878034635334)>,
<TransportSummaryByPartResult('Component 1C', EE%=6.475059185865298, CC%=6.194983944301699)>,
<TransportSummaryByPartResult('Component 11A', EE%=5.949574072949056, CC%=5.692228410484804)>,
<TransportSummaryByPartResult('Other', EE%=8.139678553815914, CC%=7.981449518016565)>]
[18]:
transport_by_part_df = pd.DataFrame.from_records(
[
{
"Name": item.part_name,
"Parent part name": item.parent_part_name,
DISTANCE_HEADER: item.distance.value,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
"Transport types": "; ".join(item.transport_types),
}
for item in sustainability_summary.transport_details_aggregated_by_part
]
)
transport_by_part_df
[18]:
Name | Parent part name | Distance [km] | EE% | EE [MJ] | CC% | CC [kg] | Transport types | |
---|---|---|---|---|---|---|---|---|
0 | Component 11B | Subassembly | 8750.0 | 49.000628 | 48.950128 | 49.654460 | 3.459488 | Aircraft, long haul dedicated-freight; Truck 7... |
1 | Assembly | None | 975.0 | 30.435060 | 30.403694 | 30.476878 | 2.123362 | Aircraft, long haul dedicated-freight; Truck 7... |
2 | Component 1C | Assembly | 1700.0 | 6.475059 | 6.468386 | 6.194984 | 0.431612 | Truck 7.5-16t, EURO 3 |
3 | Component 11A | Subassembly | 1650.0 | 5.949574 | 5.943442 | 5.692228 | 0.396585 | Truck 7.5-16t, EURO 3 |
4 | Other | None | 5150.0 | 8.139679 | 8.131290 | 7.981450 | 0.556078 | Train, diesel; Truck 7.5-16t, EURO 3 |
[19]:
plot_impact(transport_by_part_df, "Transport impact - grouped by part")
Materials phase#
The environmental contribution from the material phase is summarized in the material_details
property. The results are aggregated: each item in material_details
represents the total environmental impact of a material summed from all its occurrences in the BoM. Listed materials contribute more than 2% of the total impact for the material phase. Materials that do not contribute at least 2% of the total are aggregated under the Other
item.
[20]:
sustainability_summary.material_details
[20]:
[<MaterialSummaryResult('stainless-astm-cn-7ms-cast', EE%=45.864330109954146, CC%=37.36950817938181)>,
<MaterialSummaryResult('beryllium-beralcast191-cast', EE%=35.276841577106666, CC%=48.52278822564834)>,
<MaterialSummaryResult('steel-1010-annealed', EE%=18.85882831293918, CC%=14.107703594969825)>]
[21]:
materials_df = pd.DataFrame.from_records(
[
{
"Name": item.identity,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
f"Mass before processing [{MASS_UNIT}]": item.mass_before_processing.value,
f"Mass after processing [{MASS_UNIT}]": item.mass_after_processing.value,
}
for item in sustainability_summary.material_details
]
)
materials_df
[21]:
Name | EE% | EE [MJ] | CC% | CC [kg] | Mass before processing [kg] | Mass after processing [kg] | |
---|---|---|---|---|---|---|---|
0 | stainless-astm-cn-7ms-cast | 45.864330 | 153.040336 | 37.369508 | 11.963111 | 1.54595 | 1.450 |
1 | beryllium-beralcast191-cast | 35.276842 | 117.711949 | 48.522788 | 15.533614 | 0.02700 | 0.024 |
2 | steel-1010-annealed | 18.858828 | 62.928237 | 14.107704 | 4.516303 | 2.74574 | 2.640 |
[22]:
plot_impact(materials_df, "Aggregated materials impact")
Mass before and mass after secondary processing can help determine if the material mass removed during processing contributes a significant fraction of the impact of the overall material phase.
[23]:
fig = go.Figure(
data=[
go.Bar(
name="Mass before secondary processing",
x=materials_df["Name"],
y=materials_df[f"Mass before processing [{MASS_UNIT}]"],
),
go.Bar(
name="Mass after secondary processing",
x=materials_df["Name"],
y=materials_df[f"Mass after processing [{MASS_UNIT}]"],
),
],
layout=go.Layout(
xaxis=go.layout.XAxis(title="Materials"),
yaxis=go.layout.YAxis(title=f"Mass [{MASS_UNIT}]"),
legend=dict(orientation="h")
),
)
fig.show()
Material processing phase#
The environmental contributions from primary and secondary processing (applied to materials) and the joining and finishing processes (applied to parts) are summarized in the primary_processes_details
, secondary_processes_details
, and joining_and_finishing_processes_details
properties respectively. Each of these properties lists the unique process-material pairs (for primary and secondary processing) or individual processes (for joining and finishing) that contribute at least 5% of
the total impact for that category of process. The percentage contributions are relative to the total contribution of all processes from the same category. Processes that do not meet the contribution threshold are aggregated under the Other
item, with the material set to None
.
Primary processing#
[24]:
sustainability_summary.primary_processes_details
[24]:
[<ProcessSummaryResult(process='Primary processing, Casting', material='stainless-astm-cn-7ms-cast', EE%=49.0042889613395, CC%=51.948983142283026)>,
<ProcessSummaryResult(process='Primary processing, Casting', material='steel-1010-annealed', EE%=33.529443947403486, CC%=28.692313238852535)>,
<ProcessSummaryResult(process='Primary processing, Metal extrusion, hot', material='steel-1010-annealed', EE%=17.028777013489147, CC%=19.102795993793322)>,
<ProcessSummaryResult(process='Other', material='None', EE%=0.4374900777678725, CC%=0.25590762507113685)>]
[25]:
primary_process_df = pd.DataFrame.from_records(
[
{
"Process name": item.process_name,
"Material name": item.material_identity,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
}
for item in sustainability_summary.primary_processes_details
]
)
primary_process_df
[25]:
Process name | Material name | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|---|
0 | Primary processing, Casting | stainless-astm-cn-7ms-cast | 49.004289 | 74.659872 | 51.948983 | 4.501414 |
1 | Primary processing, Casting | steel-1010-annealed | 33.529444 | 51.083366 | 28.692313 | 2.486208 |
2 | Primary processing, Metal extrusion, hot | steel-1010-annealed | 17.028777 | 25.943981 | 19.102796 | 1.655270 |
3 | Other | None | 0.437490 | 0.666533 | 0.255908 | 0.022175 |
Add a Name
to each item that represents the process-material pair name.
[26]:
primary_process_df["Name"] = primary_process_df.apply(
lambda row: f"{row['Process name']} - {row['Material name']}", axis=1
)
plot_impact(
primary_process_df, "Aggregated primary processes impact", textinfo="percent", hoverinfo="value+name+label"
)
Secondary processing#
[27]:
sustainability_summary.secondary_processes_details
[27]:
[<ProcessSummaryResult(process='Secondary processing, Grinding', material='steel-1010-annealed', EE%=44.54415461216754, CC%=41.27956869646294)>,
<ProcessSummaryResult(process='Secondary processing, Machining, coarse', material='stainless-astm-cn-7ms-cast', EE%=30.788425634110617, CC%=28.53198005630229)>,
<ProcessSummaryResult(process='Machining, fine', material='steel-1010-annealed', EE%=15.02826066688355, CC%=13.926858057768948)>,
<ProcessSummaryResult(process='Secondary processing, Machining, fine', material='stainless-astm-cn-7ms-cast', EE%=7.789400344621524, CC%=14.54740097421245)>,
<ProcessSummaryResult(process='Other', material='None', EE%=1.8497587422167667, CC%=1.7141922152533655)>]
[28]:
secondary_process_df = pd.DataFrame.from_records(
[
{
"Process name": item.process_name,
"Material name": item.material_identity,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
}
for item in sustainability_summary.secondary_processes_details
]
)
secondary_process_df
[28]:
Process name | Material name | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|---|
0 | Secondary processing, Grinding | steel-1010-annealed | 44.544155 | 1.758458 | 41.279569 | 0.058501 |
1 | Secondary processing, Machining, coarse | stainless-astm-cn-7ms-cast | 30.788426 | 1.215427 | 28.531980 | 0.040435 |
2 | Machining, fine | steel-1010-annealed | 15.028261 | 0.593267 | 13.926858 | 0.019737 |
3 | Secondary processing, Machining, fine | stainless-astm-cn-7ms-cast | 7.789400 | 0.307500 | 14.547401 | 0.020617 |
4 | Other | None | 1.849759 | 0.073022 | 1.714192 | 0.002429 |
Add a Name
to each item that represents the process-material pair name.
[29]:
secondary_process_df["Name"] = secondary_process_df.apply(
lambda row: f"{row['Process name']} - {row['Material name']}", axis=1
)
plot_impact(
secondary_process_df, "Aggregated secondary processes impact", textinfo="percent", hoverinfo="value+name+label"
)
Joining and finishing#
Joining and finishing processes apply to parts or assemblies and therefore don’t include a material identity.
[30]:
sustainability_summary.joining_and_finishing_processes_details
[30]:
[<ProcessSummaryResult(process='Joining and finishing, Welding, electric', material='None', EE%=100.0, CC%=100.0)>]
[31]:
joining_and_finishing_processes_df = pd.DataFrame.from_records(
[
{
"Name": item.process_name,
"EE%": item.embodied_energy_percentage,
EE_HEADER: item.embodied_energy.value,
"CC%": item.climate_change_percentage,
CC_HEADER: item.climate_change.value,
}
for item in sustainability_summary.joining_and_finishing_processes_details
]
)
joining_and_finishing_processes_df
[31]:
Name | EE% | EE [MJ] | CC% | CC [kg] | |
---|---|---|---|---|---|
0 | Joining and finishing, Welding, electric | 100.0 | 3.165429 | 100.0 | 0.213035 |
[32]:
plot_impact(
joining_and_finishing_processes_df, "Aggregated secondary processes impact",
textinfo="percent", hoverinfo="value+name+label"
)
Hierarchical view#
Finally, aggregate the sustainability summary results into a single dataframe and present it in a hierarchical chart. This highlights the largest contributors at each level. In this example, two levels are defined: first the phase and then the contributors in the phase.
First, rename the processes Other
rows, so that they remain distinguishable after all processes have been grouped under a general Processes
.
Use assign
to add a parent
column to each dataframe being concatenated. The join
argument value inner
specifies that only columns common to all dataframes are kept in the result.
[33]:
primary_process_df.loc[(primary_process_df["Name"] == "Other - None"), "Name"] = "Other primary processes"
secondary_process_df.loc[(secondary_process_df["Name"] == "Other - None"), "Name"] = "Other secondary processes"
joining_and_finishing_processes_df.loc[
(joining_and_finishing_processes_df["Name"] == "Other - None"), "Name"] = "Other joining and finishing processes"
summary_df = pd.concat(
[
phases_df.assign(Parent=""),
transport_df.assign(Parent="Transport"),
materials_df.assign(Parent="Material"),
primary_process_df.assign(Parent="Processes"),
secondary_process_df.assign(Parent="Processes"),
joining_and_finishing_processes_df.assign(Parent="Processes"),
],
join="inner",
)
summary_df
# A sunburst chart presents hierarchical data radially.
fig = go.Figure(
go.Sunburst(
labels=summary_df["Name"],
parents=summary_df["Parent"],
values=summary_df[EE_HEADER],
branchvalues="total",
),
layout_title_text=f"Embodied Energy [{ENERGY_UNIT}]",
)
fig.show()
# An icicle chart presents hierarchical data as rectangular sectors.
fig = go.Figure(
go.Icicle(
labels=summary_df["Name"],
parents=summary_df["Parent"],
values=summary_df[EE_HEADER],
branchvalues="total",
),
layout_title_text=f"Embodied Energy [{ENERGY_UNIT}]",
)
fig.show()