Monte Carlo simulation in BW25

91 Views Asked by At

Beginner-type question here.

I have a function to run a Monte Carlo LCA that I used to use in BW2, but apparently, the old Monte Carlo LCA class is Deprecated from what I understood, and I use the normal LCA class.

I am using the latest version of brightway25.

def get_multiImpactMonteCarloLCA(iterations=20):
    myMethods = methods
    list_of_activities = activities
    myDict = {}
    for act in list_of_activities:
        print("running Monte Carlo LCA for: ", act)
        temp_act_dict = {str(act): []}
        myDict.update(temp_act_dict)
        MC_lca = bc.LCA({act: 1})
        MC_lca.lci()
        C_matrices = {}
        for method in myMethods:
            MC_lca.switch_method(method)
            C_matrices[method] = MC_lca.characterization_matrix
        results = np.empty((len(myMethods), iterations))
        for iteration in range(iterations):
            next(MC_lca)
            for method_index, method in enumerate(myMethods):
                results[method_index, iteration] = (
                    C_matrices[method]*MC_lca.inventory).sum()
        myDict[str(act)].append(results)
        return myDict
    print("Monte Carlo LCA calculation finished")

Running it gives me the results like a static LCA, and nothing changes between iterations. A) What am I missing here? B) is there a better way to structure this? Because it's kind of slow in how I'm doing it now.

1

There are 1 best solutions below

1
On BEST ANSWER

To get better performance, try to:

  • Reuse the LCA object whenever possible
  • Make sure you have pypardiso installed (not available on ARM Macs)
  • Don't recreate matrices more than once, i.e. save created matrices

Also, you need to pass use_distributions=True to actually use the probability distributions during LCA calculations.

We can then rewrite your function:

import bw2data as bd
import bw2calc as bc


def MultiLCA(
    demands: list[dict[bd.Node, float]], methods: list[tuple], iterations: int = 20
) -> dict[int : dict[tuple, list[float]]]:
    # Create all possible demands
    all_demands = {k: 1 for demand in demands for k in demand}

    # Create a single LCA object and use uncertainty distributions
    lca = bc.LCA(demand=all_demands, method=methods[0], use_distributions=True)
    lca.lci()

    # Create a list of characterization matrices
    C_matrices = {}

    for method in methods:
        lca.switch_method(method)
        C_matrices[method] = lca.characterization_matrix.copy()

    # Create container for results
    results = {
        index: {method: [] for method in methods} for index, _ in enumerate(demands)
    }

    # Do one monte carlo iteration for all functional units and impact categories
    for _ in range(iterations):
        # Resample all matrices
        next(lca)
        for index, demand in enumerate(demands):
            # Convert to integer ids instead of `bd.Node` objects
            lca.lci({key.id: value for key, value in demand.items()})
            for method in methods:
                results[index][method].append(
                    (C_matrices[method] * lca.inventory).sum()
                )

    return results

And use it like:

import random

demands = [
    {bd.Database("ecoinvent-3.7.1-apos").random(): random.random()} 
    for _ in range(5)
]
methods = [bd.methods.random() for _ in range(5)]
results = MultiLCA(demands=demands, methods=methods, iterations=5)

This function isn't perfect - the indexing into demands isn't great, and the results should probably be converted to numpy arrays, but it is a start.