LCA Calculations#

Single Functional Unit and LCIA Set#

How do I define a functional unit?#

A functional unit is some amount of one or more products. Because chimaera process+products act like products (see the overview documentation), they can also be used as functional units.

Brightway wants functional units to be provided as a dictionary, with keys given the product Nodes, and values giving the amount of each product. A single functional unit can have one or more products.

{my_node: 42, my_other_node: 7}

How do I calculate an life cycle inventory?#

my_functional_unit, data_objs, _ = bd.prepare_lca_inputs({my_node: 42})
my_lca = bc.LCA(demand=my_functional_unit, data_objs=data_objs)
my_lca.lci()

This will create my_lca.inventory, a matrix with the life cycle inventory data, with rows of biosphere flows and columns of processes.

How do I calculate a characterized inventory?#

my_functional_unit, data_objs, _ = bd.prepare_lca_inputs(
    {my_node: 42},
    method=('<impact>', '<category>')
)
my_lca = bc.LCA(demand=my_functional_unit, data_objs=data_objs)
my_lca.lci()
my_lca.lcia()

This will create my_lca.characterized_inventory, a matrix with the life cycle inventory data, with rows or biosphere flows and columns of processes.

How do I calculate a normalized inventory?#

my_functional_unit, data_objs, _ = bd.prepare_lca_inputs(
    {my_node: 42},
    method=('<impact>', '<category>'),
    normalization=('<normalization>', '<factors>')
)
my_lca = bc.LCA(demand=my_functional_unit, data_objs=data_objs)
my_lca.lci()
my_lca.lcia()

This will create my_lca.normalized_inventory, a matrix with the normalized life cycle inventory data, with rows or biosphere flows and columns of processes.

How do I calculate a weighted inventory?#

my_functional_unit, data_objs, _ = bd.prepare_lca_inputs(
    {my_node: 42},
    method=('<impact>', '<category>'),
    weighting=('<weighting>', '<factors>')
)
my_lca = bc.LCA(demand=my_functional_unit, data_objs=data_objs)
my_lca.lci()
my_lca.lcia()

This will create my_lca.weighted_inventory, a matrix with the weighted life cycle inventory data, with rows or biosphere flows and columns of processes.

Weighting can be done with normalization, but does not require normalization.

How do I get the LCA score after calculating a characterized inventory, possibly including normalization and/or weighting?#

my_lca.score

How do I see the most important elements in the result matrix?#

my_lca.to_dataframe(matrix_label="characterized_inventory")

See bw2calc.lca.LCA.to_dataframe for more information on this functionality.

How do I filter down to a row in the inventory or <any>_inventory matrix?#

Rows in these matrices are biosphere flows.

row_index = lca.dicts.biosphere[my_biosphere_flow_node.id]
filtered_matrix = lca.inventory[row_index, :]
filtered_matrix = lca.characterized_inventory[row_index, :]
filtered_matrix = lca.normalized_inventory[row_index, :]
filtered_matrix = lca.weighted_inventory[row_index, :]

How do I filter down to a column in the inventory or characterized_inventory matrix?#

Columns in these matrices are processes.

col_index = lca.dicts.activity[my_process_node.id]
filtered_matrix = lca.inventory[:, col_index]
filtered_matrix = lca.characterized_inventory[:, col_index]
filtered_matrix = lca.normalized_inventory[:, col_index]
filtered_matrix = lca.weighted_inventory[:, col_index]

How do I go from matrix indices to Node objects?#

First, you need to find out what kind of object you need to look up. Assuming you have created an LCA instance lca_instance:

  • In the technosphere_matrix, inventory, and all <any>_inventory matrices, columns are processes. Use lca_instance.dicts.activity.

  • In the technosphere_matrix, and in the demand_array, rows are products. Use lca_instance.dicts.product.

  • In the biosphere_matrix, inventory, and all <any>_inventory matrices, rows are biosphere flows. Use lca_instance.dicts.biosphere.

  • The characterization_matrix, normalization_matrix, and weighting_matrix are all diagonal matrices, with both rows and columns being biosphere flows. Use lca_instance.dicts.biosphere.

You can then use dicts.<foo>.reversed to go from row or column indices to Nodes:

bd.get_node(id=my_lca.dicts.activity.reversed[my_column_index])
bd.get_node(id=my_lca.dicts.product.reversed[my_row_index])

Multiple Functional Units and LCIA Sets#

How do I calculate an life cycle inventory for multiple functional units?#

For the MultiLCA class, we need to label each functional unit:

functional_units = {
    "Ξ³": {node_1.id: 3.9},
    "Ξ΅": {node_2.id: 6.2},
}
data_objs = bd.get_multilca_data_objs(functional_units, {})
lca = bc.MultiLCA(
    demands=functional_units,
    method_config={},
    data_objs=data_objs,
)
lca.lci()

Functional unit data types

In MultiLCA functional units, the keys must be integer ids, not Node instances

If your functional units will only have a single product node, you can use the Node object itself as the dictionary key:

functional_units = {
    node_1: {node_1.id: 4.88},
    node_2: {node_2.id: 11},
}

This will create lca.inventories, a dictionary which gives inventory matrices for each combination of functional unit label and impact category.

{
    ("Ξ³",): sparse_matrix,
    ("Ξ΅",): sparse_matrix,
}

How do I calculate a life cycle impact assessment for multiple functional units and a combination of LCIA impact categories, normalizations, and weightings?#

We start by describing how the impact categories, normalization, and weightings are related. The safest way to do this is by creating an instance of bw2calc.MethodConfig; the bw2calc.method_config.MethodConfig documentation describes how to provide this data.

method_config = bc.MethodConfig(<some_data>)

For the MultiLCA class, we need to label each functional unit in a dictionary:

functional_units = {
    "Ξ³": {node_1.id: 1},
    "Ξ΅": {node_2.id: 2},
}

Finally, we need to get the datapackages needed for all inventory databases and LCIA steps. We can then create a MultiLCA instance:

data_objs = bd.get_multilca_data_objs(functional_units, method_config)
lca = bc.MultiLCA(
    demands=functional_units,
    method_config=impact_categories,
    data_objs=data_objs,
)
lca.lci()
lca.lcia()

You should also do lca.normalization() and lca.weighting() if those steps have data given in method_config.

The results are provided as dictionaries, with keys showing which weighting, normalization, and characterization methods were applied for each functional unit. For example, if method_config was:

method_config = bd.MethodConfig(
    impact_categories=[
        ('<impact_category>', '<a>'),
        ('<impact_category>', '<b>'),
    ]
)

Then lca.characterized_inventories would be the following dictionary:

{
    (('<impact_category>', '<a>'), "Ξ³"): sparse_matrix,
    (('<impact_category>', '<a>'), "Ξ΅"): sparse_matrix,
    (('<impact_category>', '<b>'), "Ξ³"): sparse_matrix,
    (('<impact_category>', '<b>'), "Ξ΅"): sparse_matrix,
}

Additional elements would be added to the dictionary keys if normalization and weighting were included.

As these dictionaries have multiple matrices at each calculation step, the MultiLCA attributes are plural:

  • supply_array ➟ supply_arrays

  • inventory ➟ inventories

  • characterized_inventory ➟ characterized_inventories

  • normalized_inventory ➟ normalized_inventories

  • weighted_inventory ➟ weighted_inventories

  • score ➟ scores

Stochastic LCA Calculations (Monte Carlo)#

Both LCA and MultiLCA classes support stochastic calculations using Monte Carlo. New values for all matrices can be generated from probability distribution functions, or from pre-calculated arrays of sample or population values.

How do I use probability distribution functions in stochastic LCA?#

bc.LCA(..., use_distributions=True)
bc.MultiLCA(..., use_distributions=True)

How do I use presampled values from arrays in my datapackages?#

bc.LCA(..., use_arrays=True)
bc.MultiLCA(..., use_arrays=True)

You can use both use_distributions and use_arrays in a calculation.

How do I generate a new set of results when doing stochastic LCA?#

next(lca_object)

This will draw new data samples and generate new versions of all matrices defined, including characterization, normalization, and weighting, solve the linear system, and generate all result matrices.

You can also use this in anything that needs the iterator protocol, like list comprehensions:

results = [lca.score for _ in zip(lca, range(10))]

But you can also advance the lca object manually:

for _ in range(10):
    next(lca)
    do_something_with_lca_result_matrices(lca)