Imports / init¶
[1]:
# Those two lines are for dev only : they watch imported libraries for changes
#%load_ext autoreload
#%autoreload 2
import brightway2 as bw
import os
import lca_algebraic as agb
from sympy import init_printing
import bw2io
from dotenv import load_dotenv
# Pretty print for Sympy
init_printing()
Init brightway2 and databases¶
[2]:
# Set the current project
# Can be any name
bw.projects.set_current('MyProject')
# It's better to not leave credential in the code.
# Create a file named .env, that you will not share /commit, and contains the following :
# ECOINVENT_LOGIN=<your_login>
# ECOINVENT_PASSWORD=<your_password>
# This load .env file into os.environ
load_dotenv()
# This downloads ecoinvent and installs biopshere + technosphere + LCIA methods
if len(bw.databases) > 0:
print("Initial setup already done, skipping")
else:
# This is now the prefered method to init an Brightway2 with Ecoinvent
# It is not more tied to a specific version of bw2io
bw2io.import_ecoinvent_release(
version="3.9",
system_model="cutoff",
username=os.environ["ECOINVENT_LOGIN"], # Read for .env file
password=os.environ["ECOINVENT_PASSWORD"], # Read from .env file
use_mp=True)
Initial setup already done, skipping
[3]:
# We use a separate DB for defining our foreground model / activities
# Choose any name
USER_DB = 'MyForeground'
# This is better to cleanup the whole foreground model each time, and redefine it in the notebook (or a python file)
# instead of relying on a state or previous run.
# Any persistent state is prone to errors.
agb.resetDb(USER_DB)
# Parameters are stored at project level :
# Reset them also
# You may remove this line if you import a project and parameters from an external source (see loadParam(..))
agb.resetParams()
# Overview of the databases
agb.list_databases()
[WARNING] Db MyForeground was here. Reseting it
[3]:
backend | nb_activities | type | |
---|---|---|---|
name | |||
ecoinvent-3.9-biosphere | sqlite | 4709 | biosphere |
ecoinvent-3.9-cutoff | sqlite | 21255 | background |
MyForeground | sqlite | 0 | foreground |
Introduction to Numpy¶
Numpy is a python libray for symbolic calculus.
You write Sympy expression as you write standard python expressions, using sympy symbols in them.
The result is then a symbolic expression that can be manipulated, instead of a numeric value.
[4]:
from sympy import symbols
# create sympy symbol
x = symbols("x")
# Expressions are not directly evaluated
f = x * 2 + 4
f
[4]:
[5]:
# symbols can be replaced by values afterwards
f.subs(dict(x=3))
[5]:
In practice, you don’t need to care about Sympy. Just remember that : * The parameters defined below are instances of sympy symbols * Any valid python expression containing a sympy symbol will create a sympy symbolic expression
Define input parameters¶
First, we define the input parameters of the model together with their distribution.
The numeric parameters are instances of sympy ‘Symbol’.
Thus, any python arithmetic expression composed of parameters will result in a symbolic expression to be used later in the definition of the model, rather than a static numeric result.
[6]:
# Example of 'float' parameters
a = agb.newFloatParam(
'a',
default=0.5, min=0.2, max=2,
distrib=agb.DistributionType.TRIANGLE, # Distribution type, linear by default
description="hello world",
label="extended label for a")
b = agb.newFloatParam(
'b',
default=0.5, # Fixed if no min /max provided
distrib=agb.DistributionType.FIXED,
description="foo bar")
share_recycled_aluminium = agb.newFloatParam(
'share_recycled_aluminium',
default=0.6,
min=0, max=1, std=0.2,
distrib=agb.DistributionType.NORMAL, # Normal distrib, with std dev
description="Share of reycled aluminium")
c = agb.newFloatParam(
'c',
default=0.6, std=0.2,
distrib=agb.DistributionType.LOGNORMAL)
beta = agb.newFloatParam(
'beta',
default=0.6, std=0.2, a=2, b=5,
distrib=agb.DistributionType.BETA)
# You can define boolean parameters, taking only discrete values 0 or 1
bool_param = agb.newBoolParam(
'bool_param',
default=1)
# Example 'enum' parameter, acting like a switch between several possibilities
# Enum parameters are not Symbol themselves
# They are a facility to represent many boolean parameters at once '<paramName>_<enumValue>'
# and should be used with the 'newSwitchAct' method
elec_switch_param = agb.newEnumParam(
'elec_switch_param',
values=["us", "eu"], # If provided as list, all possibilities have te same probability
default="us",
description="Switch on electricty mix")
# Another example enum param
techno_param = agb.newEnumParam(
'techno_param',
values={
"technoA":0.4,
"technoB":0.1,
"technoC":0.5}, # You can provide a statistical weight for each value, by using a dict
default="technoA",
description="Choice of technology")
Persistance of parameters¶
By default, new parameters are kept in memory but also persisted in the project (unless save=False).
You can persist parameters afterwards with persistParams()
.
You can load also load parameters from an existing database with loadParams()
.
The persistance of parameters and the distribution is compatible with Brightway2 and Activity Browser see documentation of stat_arrays
[7]:
# Load parameters previously persisted in the dabatase.
agb.loadParams()
[WARNING] [ParamRegistry] Param a was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param a was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param b was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param b was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param share_recycled_aluminium was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param share_recycled_aluminium was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param c was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param c was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param beta was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param beta was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param bool_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param bool_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param elec_switch_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param elec_switch_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param techno_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param techno_param was already defined in '<project>' : overriding.
Manage several databases¶
lca_algebraic supports several foreground / background datasets. Background datasets are considered static / non parametrized by the library : they use standard LCA method of Brightway2.
Foreground databases are considered parametric and their activities are developped as functions of parameters and background activities.
Set status of a database¶
The functions setForeground(…) and setBackground(…) change the status of a database.
[8]:
agb.setForeground(USER_DB)
agb.list_databases()
[8]:
backend | nb_activities | type | |
---|---|---|---|
name | |||
ecoinvent-3.9-biosphere | sqlite | 4709 | biosphere |
ecoinvent-3.9-cutoff | sqlite | 21255 | background |
MyForeground | sqlite | 0 | foreground |
Import / export¶
lca_algebraic
extends BW2Package, adding persistence of parameters.
[9]:
# Save database and parameters as Bzipped JSON
agb.export_db(USER_DB, "tmp/db.bw2")
[10]:
# Reimport DB
agb.import_db("tmp/db.bw2")
[WARNING] [ParamRegistry] Param a was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param a was already defined in '<project>' : overriding.
[WARNING] Variable 'a' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param b was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param b was already defined in '<project>' : overriding.
[WARNING] Variable 'b' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param share_recycled_aluminium was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param share_recycled_aluminium was already defined in '<project>' : overriding.
[WARNING] Variable 'share_recycled_aluminium' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param c was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param c was already defined in '<project>' : overriding.
[WARNING] Variable 'c' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param beta was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param beta was already defined in '<project>' : overriding.
[WARNING] Variable 'beta' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param bool_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param bool_param was already defined in '<project>' : overriding.
[WARNING] Variable 'bool_param' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param elec_switch_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param elec_switch_param was already defined in '<project>' : overriding.
[WARNING] Variable 'elec_switch_param' was already defined : overidding it with param.
[WARNING] [ParamRegistry] Param techno_param was already defined in '<project>' : overriding.
[WARNING] [ParamRegistry] Param techno_param was already defined in '<project>' : overriding.
[WARNING] Variable 'techno_param' was already defined : overidding it with param.
[10]:
Brightway2 SQLiteBackend: MyForeground
Freeze¶
A foreground database can be “frozen” to be used as a background database for a specific scenario : the parametrized amounts in the exhanges are computed for a given configuration of the parameters, and replaced by their value. The formulas are still stored in the database and not lost : the database can still be used as a foreground database until its status is changed with setBackground(...)
.
This feature is useful for studies requiring several datasets to be used as background by other ones. It also enables to use standard Brightway2 tools, not aware of parametrization.
[11]:
agb.freezeParams(
USER_DB, # Name of database to freeze
a=1, b=2) # custom parameter values
Get references to background activities¶
We provide two functions for easy and fast (indexed) search of activities in reference databases : * findBioAct : Search activity in biosphere3 db * findTechAct : Search activity in ecoinvent db
Those methods are faster and safer than using traditionnal “list-comprehension” search : They will fail with an error if more than one activity matches, preventing the model to be based on a random selection of one activity.
[12]:
# Biosphere activities
ground_occupuation = agb.findBioAct('Occupation, industrial area') # Search by name
heat = agb.findBioAct('Heat, waste', categories=['air']) # Add category selector
# Technosphere activities
# You can add an optionnal location selector
alu = agb.findTechAct("aluminium alloy production, AlMg3", loc="RER")
alu_scrap = agb.findTechAct('aluminium scrap, new, Recycled Content cut-off')
# Elec
eu_elec = agb.findTechAct("market group for electricity, medium voltage", 'ENTSO-E')
us_elec = agb.findTechAct("market group for electricity, medium voltage", 'US')
chromium = agb.findTechAct("market for chromium oxide, flakes")
Define the model¶
The model is defined as a nested combination of background activities with amounts.
Amounts are defined either as constant float values or algebric formulas implying the parameters defined above.
Create new activities¶
[13]:
# Create a new activity
activity1 = agb.newActivity(USER_DB, # We define foreground activities in our own DB
"first foreground activity", # Name of the activity
"kg", # Unit
exchanges= { # We define exhanges as a dictionarry of 'activity : amount'
ground_occupuation:3 * b, # Amount can be a fixed value
heat: b + 0.2 # Amount can be a Sympy expression (any arithmetic expression of Parameters)
})
# You can create a virtual "switch" activity combining several activities with an Enum parameter
elec_mix = agb.newSwitchAct(USER_DB,
"elect mix", # Name
elec_switch_param, # Sith parameter
{ # Dictionnary of enum values / activities
"us" : us_elec, # By default associated amount is 1
"eu" : (eu_elec, 0.8) # You can also provide custom amout or formula with a tuple
})
Copy and update existing activity¶
You can copy and update an existing background activity.
Several new helper methods have been added to the class Activity for easy update of exchanges.
[14]:
alu2 = agb.copyActivity(
USER_DB, # The copy of a background activity is done in our own DB, so that we can safely update it
alu, # Initial activity : won't be altered
"Aluminium 2") # New name
# Update exchanges by their name
alu2.updateExchanges({
# Update amount : the special symbol *old_amount* references the previous amount of this exchange
"aluminium, cast alloy": agb.old_amount * (1 - share_recycled_aluminium),
# Update input activity. Note also that you can use '*' wildcard in exchange name
"electricity*": elec_mix,
# Update both input activity and amount.
# Note that you can use '#' for specifying the location of exchange (useful for duplicate exchange names)
"chromium#GLO" : dict(amount=4.0, input=chromium)
})
# Add exchanges
alu2.addExchanges({alu_scrap : 12})
Final model¶
[15]:
total_inventory = agb.newActivity(USER_DB, "total_inventory", "kg", {
activity1 : b * 5 + a + 1, # Reference the activity we just created
alu2: 3 * share_recycled_aluminium,
alu:0.4 * a})
Or load existing model /activities from database¶
Alternatively, you may not define the model again, but load it from the USER DB.
[16]:
activity1 = agb.findActivity("first foreground activity", db_name=USER_DB)
total_inventory = agb.findActivity("total_inventory", db_name=USER_DB)
alu2 = agb.findActivity("Aluminium 2", db_name=USER_DB)
Display activities¶
printAct displays the list of all exchanges of an activity.
Note that symbolic expressions have not been evaluated at this stage
[17]:
# Print_act displays activities as tables
agb.printAct(activity1)
[17]:
first foreground activity (1.000000 kg) | |||
---|---|---|---|
input | amount | unit | |
Heat, waste | Heat, waste | b + 0.2 | megajoule |
Occupation, industrial area | Occupation, industrial area | 3*b | square meter-year |
[18]:
agb.printAct(total_inventory)
[18]:
total_inventory (1.000000 kg) | |||
---|---|---|---|
input | amount | unit | |
Aluminium 2 | Aluminium 2[RER]{FG} | 3*share_recycled_aluminium | kilogram |
aluminium alloy production, AlMg3 | aluminium alloy production, AlMg3[RER] | 0.4*a | kilogram |
first foreground activity | first foreground activity{FG} | a + 5*b + 1 | kg |
[19]:
# You can also compute amounts by replacing parameters with a float value
agb.printAct(activity1, b=1.5)
[19]:
first foreground activity (1.000000 kg) | |||
---|---|---|---|
input | amount | unit | |
Heat, waste | Heat, waste | 1.70000000000000 | megajoule |
Occupation, industrial area | Occupation, industrial area | 4.50000000000000 | square meter-year |
[20]:
# You can print several activities at once to compare them
agb.printAct(alu, alu2)
[20]:
aluminium alloy production, AlMg3[RER] (1.000000 kilogram) | Aluminium 2[RER] (1.000000 kilogram) | |||||
---|---|---|---|---|---|---|
input | amount | unit | input | amount | unit | |
aluminium scrap, new, Recycled Content cut-off | nan | nan | nan | aluminium scrap, new, Recycled Content cut-off | 12 | kilogram |
aluminium, cast alloy | market for aluminium, cast alloy | 0.965000 | kilogram | market for aluminium, cast alloy | 0.965 - 0.965*share_recycled_aluminium | kilogram |
cast iron | market for cast iron | 0.004060 | kilogram | market for cast iron | 0.004060 | kilogram |
chromium | market for chromium | 0.003050 | kilogram | market for chromium oxide, flakes | 4.000000 | kilogram |
copper, cathode | market for copper, cathode | 0.001020 | kilogram | market for copper, cathode | 0.001020 | kilogram |
electricity, medium voltage | market group for electricity, medium voltage[RER] | 1.590000 | kilowatt hour | elect mix{FG} | 1.590000 | kilowatt hour |
magnesium | market for magnesium | 0.030500 | kilogram | market for magnesium | 0.030500 | kilogram |
manganese | market for manganese | 0.005080 | kilogram | market for manganese | 0.005080 | kilogram |
silicon, metallurgical grade | market for silicon, metallurgical grade | 0.004060 | kilogram | market for silicon, metallurgical grade | 0.004060 | kilogram |
zinc | market for zinc | 0.002030 | kilogram | market for zinc | 0.002030 | kilogram |
Select the impacts to consider¶
[21]:
# List of impacts to consider
impacts = agb.findMethods("climate change", mainCat="EF v3.0")
impacts
[21]:
[('EF v3.0', 'climate change', 'global warming potential (GWP100)'),
('EF v3.0', 'climate change: biogenic', 'global warming potential (GWP100)'),
('EF v3.0', 'climate change: fossil', 'global warming potential (GWP100)'),
('EF v3.0',
'climate change: land use and land use change',
'global warming potential (GWP100)')]
Impacts¶
Define functional unit¶
The functional unit is a quantity that can be parametrized
[22]:
functional_value = a + 5
Compute impacts¶
[23]:
agb.compute_impacts(
# Root activity of our inventory
total_inventory,
# list of impacts to consider
impacts,
# The impaxts will be divided by the functional unit
functional_unit=functional_value,
# Parameters of the model
a=1.0,
elec_switch_param="us",
share_recycled_aluminium=0.4)
[INFO] Db changed recently, clearing cache expr
[INFO] Db changed recently, clearing cache lcia
[23]:
climate change - global warming potential (GWP100)[kg CO2-Eq] | climate change: biogenic - global warming potential (GWP100)[kg CO2-Eq] | climate change: fossil - global warming potential (GWP100)[kg CO2-Eq] | climate change: land use and land use change - global warming potential (GWP100)[kg CO2-Eq] | |
---|---|---|---|---|
total_inventory | 6.4928 | 0.0143764 | 6.47243 | 0.00598809 |
[24]:
# You can compute several LCAs at a time and compare them:
agb.compute_impacts(
[alu, alu2], # The models
impacts, # Impacts
# Parameters of the model
share_recycled_aluminium=0.3,
elec_switch_param="us")
[INFO] Db changed recently, clearing cache expr
[24]:
climate change - global warming potential (GWP100)[kg CO2-Eq] | climate change: biogenic - global warming potential (GWP100)[kg CO2-Eq] | climate change: fossil - global warming potential (GWP100)[kg CO2-Eq] | climate change: land use and land use change - global warming potential (GWP100)[kg CO2-Eq] | |
---|---|---|---|---|
aluminium alloy production, AlMg3[RER] | 7.30913 | 0.015893 | 7.27595 | 0.0172853 |
Aluminium 2[RER] | 30.5944 | 0.0676333 | 30.5011 | 0.0256965 |
Fast computation of many parameter values¶
[25]:
# Fast computation for millions of separate samples
agb.compute_impacts(
total_inventory, # The model
impacts, # Impacts
functional_unit = functional_value,
# Parameters of the model
a=list(range(1, 100000)), # All lists should have the same size
share_recycled_aluminium=1, # Those parameters are fixed
elec_switch_param="eu")
[25]:
climate change - global warming potential (GWP100)[kg CO2-Eq] | climate change: biogenic - global warming potential (GWP100)[kg CO2-Eq] | climate change: fossil - global warming potential (GWP100)[kg CO2-Eq] | climate change: land use and land use change - global warming potential (GWP100)[kg CO2-Eq] | |
---|---|---|---|---|
a | ||||
1 | 13.6393 | 0.0323603 | 13.5979 | 0.00906581 |
2 | 12.1085 | 0.0286456 | 12.0711 | 0.00875842 |
3 | 10.9604 | 0.0258596 | 10.926 | 0.00852789 |
4 | 10.0674 | 0.0236926 | 10.0354 | 0.00834858 |
5 | 9.35303 | 0.0219591 | 9.32287 | 0.00820513 |
... | ... | ... | ... | ... |
99995 | 2.92429 | 0.00635876 | 2.91102 | 0.00691425 |
99996 | 2.92429 | 0.00635876 | 2.91102 | 0.00691425 |
99997 | 2.92429 | 0.00635876 | 2.91102 | 0.00691425 |
99998 | 2.92429 | 0.00635876 | 2.91102 | 0.00691425 |
99999 | 2.92429 | 0.00635876 | 2.91102 | 0.00691425 |
99999 rows × 4 columns
Split impacts along axis¶
It is possible to tag activities and then ventilate the impacts according to the value of this “tag”. This is useful to split impact by phase or sub module.
[26]:
# Tag activities with a custom attribute : 'phase' in this case
alu2.updateMeta(phase= "phase a")
activity1.updateMeta(phase= "phase b")
[27]:
# Provide the name of the custom attribute as 'axis'
# The impacts are split between those
agb.compute_impacts(
total_inventory, # The model
impacts, # Impacts
functional_unit = functional_value,
axis="phase",
# Parameters
a=1.0,
elec_switch_param="us",
share_recycled_aluminium=0.4)
[INFO] Db changed recently, clearing cache expr
[INFO] Db changed recently, clearing cache lcia
[27]:
climate change - global warming potential (GWP100)[kg CO2-Eq] | climate change: biogenic - global warming potential (GWP100)[kg CO2-Eq] | climate change: fossil - global warming potential (GWP100)[kg CO2-Eq] | climate change: land use and land use change - global warming potential (GWP100)[kg CO2-Eq] | |
---|---|---|---|---|
phase | ||||
_other_ | 0.487275 | 0.00105953 | 0.485063 | 0.00115235 |
phase_a | 6.00552 | 0.0133169 | 5.98737 | 0.00483573 |
phase_b | 0 | 0 | 0 | 0 |
*sum* | 6.4928 | 0.0143764 | 6.47243 | 0.00598809 |
# Sensitivity analysis
## One at a time
We provide several functions for computing statistics for local variations of parameters (one at a time).
### oat_matrix(model, impacts)
Shows a matrix of impacts x parameters colored according to the variation of the impact in the bounds of the parameter.
[28]:
agb.oat_matrix(
total_inventory,
impacts,
functional_unit=functional_value)
This functions draws a dashboard showing : * A dropdown list, for choosing a parameter * Several graphs of evolution of impacts for this parameter * Full table of data * A graph of “bars” representing the variation of each impact for this parameter (similar to the information given in oat_matrix)
[29]:
agb.oat_dashboard_interact(
total_inventory,
impacts,
functional_unit=functional_value,
# Optionnal layout parameters
figspace=(0.5,0.5),
figsize=(15, 15),
sharex=True)
Monte-carlo methods & Sobol indices¶
Here we leverage fast computation of monte-carlo approches.
We compute global sensivity analysis (GSA). Not only local ones.
Similar to OAT matrix, we compute Sobol indices. they represent the ratio between the variance due to a given parameter and the total variance.
for easier comparison, we translate those relative sobol indices into “deviation / mean” importance :
[30]:
# Show sobol indices
agb.incer_stochastic_matrix(
total_inventory,
impacts,
functional_unit=functional_value)
Generating samples ...
Transforming samples ...
Processing Sobol indices ...
Processing sobol for ('EF v3.0', 'climate change', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: biogenic', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: fossil', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: land use and land use change', 'global warming potential (GWP100)')
We provide a dashboard showing violin graphs : the exact probabilistic distribution for each impact. Together with medians of the impacts.
[31]:
agb.incer_stochastic_violin(
total_inventory, impacts,
functional_unit=functional_value,
# Optionnal layout parameters
figspace=(0.5,0.5),
figsize=(15, 15),
sharex=True,
nb_cols=3)
[WARNING] Param 'b' is marked as FIXED, but passed in parameters : ignored
Generating samples ...
Transforming samples ...
[32]:
##### Alternatively, graphs can be shown horizontally, together with a box of statistical outcomes
agb.distrib(
total_inventory, impacts,
functional_unit=functional_value,
# Optionnal layout parameters
height=7, width=15,
nb_cols=2,
percentiles=[5, 95])
Generating samples ...
Transforming samples ...
[WARNING] Param 'b' is marked as FIXED, but passed in parameters : ignored
[32]:
climate change - global warming potential (GWP100) [kg CO2-Eq / kWh] | climate change: biogenic - global warming potential (GWP100) [kg CO2-Eq / kWh] | climate change: fossil - global warming potential (GWP100) [kg CO2-Eq / kWh] | climate change: land use and land use change - global warming potential (GWP100) [kg CO2-Eq / kWh] | |
---|---|---|---|---|
median | 9.20969 | 0.0209857 | 9.18056 | 0.0076141 |
std | 2.62946 | 0.00616566 | 2.62203 | 0.00135006 |
p | [4.661296192674368, 13.295579122765872] | [0.01047596849769584, 0.03075347545386549] | [4.646365014916029, 13.255641748517466] | [0.0046951461108982245, 0.008892238955949797] |
mean | 9.12342 | 0.0208455 | 9.09526 | 0.00731335 |
var | 0.28821 | 0.295779 | 0.288286 | 0.184602 |
A dashboard groups all this information in a single interface with tabs.
It also shows total variation of impacts. This last graph could be improved by showing stacked colored bars with the contribution of each parameter to this variation, according to Sobol indices.
[33]:
agb.incer_stochastic_dashboard(
model=total_inventory,
methods=impacts,
functional_unit=functional_value)
[WARNING] Param 'b' is marked as FIXED, but passed in parameters : ignored
Generating samples ...
Transforming samples ...
Processing Sobol indices ...
Processing sobol for ('EF v3.0', 'climate change', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: biogenic', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: fossil', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: land use and land use change', 'global warming potential (GWP100)')
Producing simplified models¶
One of te outcome of the statisticall analysis above would be to identify main input parameters and produce simplidied models, fixing the minor ones.
We provide several functions for doing this.
Explore initial algrebraic model¶
[34]:
# First, let's look at the full expression defining our model
expr, _ = agb.actToExpression(total_inventory)
expr
[34]:
Compute simplified models¶
We provide some method to automatically select a subset of parameters, based on the sobol indices, and then compute simplified models for it.
We also round numerical expression to 3 digits, and we remove terms in sums that are less than 1% of total.
[35]:
simplified = agb.sobol_simplify_model(
total_inventory, # The model
impacts, # Impacts to consider
functional_unit=functional_value,
n=10000, # For large model, you may test other value and ensure ST and sum(S1) are close to 1.0
fixed_mode = agb.FixedParamMode.MEDIAN, # We replace minor parameters by median by default,
min_ratio=0.8, # Min ratio of variability to explain
num_digits=3)
Generating samples ...
/home/rjolivet/lca_algebraic/.venv/lib/python3.9/site-packages/scipy/stats/_qmc.py:958: UserWarning: The balance properties of Sobol' points require n to be a power of 2.
sample = self._random(n, workers=workers)
Transforming samples ...
[WARNING] Param 'b' is marked as FIXED, but passed in parameters : ignored
Processing sobol for ('EF v3.0', 'climate change', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: biogenic', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: fossil', 'global warming potential (GWP100)')
Processing sobol for ('EF v3.0', 'climate change: land use and land use change', 'global warming potential (GWP100)')
> Method : climate change - global warming potential (GWP100)
S1: 0.995048982989199
S2: 0.0022916719684489845
ST: 1.0031510935848316
Selected params : ['share_recycled_aluminium'] explains: 0.9712731972816963
> Method : climate change: biogenic - global warming potential (GWP100)
S1: 0.9948707545799679
S2: 0.0017073402807030727
ST: 1.0034883702074027
Selected params : ['share_recycled_aluminium'] explains: 0.9681595352302657
> Method : climate change: fossil - global warming potential (GWP100)
S1: 0.9950470992594116
S2: 0.0022964389961461387
ST: 1.0031515621316118
Selected params : ['share_recycled_aluminium'] explains: 0.9712429262872392
> Method : climate change: land use and land use change - global warming potential (GWP100)
S1: 0.9959868912968689
S2: -0.002553059830429213
ST: 1.0052410330177808
Selected params : ['share_recycled_aluminium'] explains: 0.9882528867286657
[36]:
# Let's look at the expression for first impact again
# much simpler !
simplified[0].expr
[36]:
Compare simplified model with full model¶
Finally, we can compare the distribution of those simplified model against the full model. We provide a function for graphical display of it, and compuation of de R-Square score.
[37]:
agb.compare_simplified(
total_inventory,
impacts,
simplified,
functional_unit=functional_value)
[INFO] Required param 'elec_switch_param' was missing, replacing by default value : us
[INFO] Required param 'a' was missing, replacing by default value : 0.5
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
Generating samples ...
Transforming samples ...
/home/rjolivet/lca_algebraic/lca_algebraic/stats.py:1233: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead
"R² : %0.3g" % r_value,
[INFO] Required param 'elec_switch_param' was missing, replacing by default value : us
[INFO] Required param 'a' was missing, replacing by default value : 0.5
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
Generating samples ...
Transforming samples ...
/home/rjolivet/lca_algebraic/lca_algebraic/stats.py:1233: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead
"R² : %0.3g" % r_value,
[INFO] Required param 'elec_switch_param' was missing, replacing by default value : us
[INFO] Required param 'a' was missing, replacing by default value : 0.5
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
Generating samples ...
Transforming samples ...
/home/rjolivet/lca_algebraic/lca_algebraic/stats.py:1233: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead
"R² : %0.3g" % r_value,
[INFO] Required param 'elec_switch_param' was missing, replacing by default value : us
[INFO] Required param 'a' was missing, replacing by default value : 0.5
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
[INFO] Required param 'share_recycled_aluminium' was missing, replacing by default value : 0.6
Generating samples ...
Transforming samples ...
/home/rjolivet/lca_algebraic/lca_algebraic/stats.py:1233: FutureWarning: Calling float on a single element Series is deprecated and will raise a TypeError in the future. Use float(ser.iloc[0]) instead
"R² : %0.3g" % r_value,
[37]: