brightway-lca / brightway2-analyzer

BSD 3-Clause "New" or "Revised" License
7 stars 12 forks source link

Top activities not working for upstream exploration of hotspots #27

Open tjlca opened 12 months ago

tjlca commented 12 months ago

brightway2==2.3 bw-migrations==0.2 bw2analyzer==0.11.4 bw2calc==1.8.1 bw2data==3.6.2 bw2io==0.8.7 bw2parameters==0.6.6 python = 3.9 print(lca.top_activities())

File "/home/tghosh/.local/lib/python3.9/site-packages/bw2calc/lca.py", line 583, in top_activities return ContributionAnalysis().annotated_top_processes(self, **kwargs) File "/home/tghosh/.local/lib/python3.9/site-packages/bw2analyzer/contribution.py", line 133, in annotated_top_processes results = [ File "/home/tghosh/.local/lib/python3.9/site-packages/bw2analyzer/contribution.py", line 137, in lca.dicts.activity.reversed[int(index)], AttributeError: 'LCA' object has no attribute 'dicts'

bw2analyzer==0.10 does not have this issue.

tjlca commented 12 months ago

Top emissions not working with the following error message -

    print(lca.top_emissions())                 
  File "/home/tghosh/.local/lib/python3.9/site-packages/bw2calc/lca.py", line 575, in top_emissions
    return ContributionAnalysis().annotated_top_emissions(self, **kwargs)
  File "/home/tghosh/.conda/envs/gcam/lib/python3.9/site-packages/bw2analyzer/contribution.py", line 152, in annotated_top_emissions
    results = [
  File "/home/tghosh/.conda/envs/gcam/lib/python3.9/site-packages/bw2analyzer/contribution.py", line 153, in <listcomp>
    (score, lca.inventory[index, :].sum(), rb[index])
  File "/home/tghosh/.conda/envs/gcam/lib/python3.9/site-packages/scipy/sparse/_index.py", line 47, in __getitem__
    row, col = self._validate_indices(key)
  File "/home/tghosh/.conda/envs/gcam/lib/python3.9/site-packages/scipy/sparse/_index.py", line 152, in _validate_indices
    if isintlike(row):
  File "/home/tghosh/.conda/envs/gcam/lib/python3.9/site-packages/scipy/sparse/_sputils.py", line 233, in isintlike
    raise ValueError(msg)
ValueError: Inexact indices into sparse matrices are not allowed
cp: cannot stat ‘*’: No such file or directory
mfastudillo commented 11 months ago

It looks like you are using the legacy version of bw2calc. There were large changes in version 2. (https://github.com/brightway-lca/brightway2-calc/blob/main/CHANGES.md) including that way of accessing the dictionaries of the lca objects.

I'd think the requirements file needs to be adapted @cmutel

tngTUDOR commented 7 months ago

Hi @tjlca , you spotted it exactly as it is: 0.10 works fine, because it does not use the "lca.dicts" prop, but as of bw2analyzer > 0.10, it's broken.

bw2calc supplies the "LCA" class, and in the newer style of this class, there is no longer a dicts property: legacy branch:

        if self._fixed:
            # Already fixed - should be idempotent
            return False
        elif not mapping:
            # Don't have access to mapping
            return False
        rev_mapping = {v: k for k, v in mapping.items()}
        self._activity_dict = copy.deepcopy(self.activity_dict)
        self.activity_dict = {
            rev_mapping[k]: v for k, v in self.activity_dict.items()}
        self._product_dict = self.product_dict
        self.product_dict = {
            rev_mapping[k]: v for k, v in self.product_dict.items()}
        self._biosphere_dict = self.biosphere_dict
        self.biosphere_dict = {
            rev_mapping[k]: v for k, v in self.biosphere_dict.items()}
        self._fixed = True
        return True

latest main bw2calc 2.0.dev16 uses a DictionaryManager (so no dicts property).

legacy bw2calc 1.8.2 uses analyzer to calculate the top activities and emissions, but has no restrictions on the version of bw2analyzer to use, hence you can end up with a mix of bw2calc + bw2analyzer that are incompatible.

tngTUDOR commented 7 months ago

1ST ;) conclusion: if you have bw2calc from legacy, make sure you have bw2analyzer v 0.10 2nd conclusion: We must pin the deps of bw2analyzer to make sure it works for bw25

tngTUDOR commented 5 months ago

More for reproducibility here:

In a recent install of brightway2:

the bw2analyzer code can be used as follows:

import bw2analyzer as bwa
import bw2calc as bc
...
a_lca = bc.LCA({fu:1}, a_method)
a_lca.lci()
a_lca.lcia()
top_p = bwa.ContributionAnalysis().annotated_top_processes(a_lca)

but the corresponding function for top_emissions will not work, yielding:

--------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/miniforge3/envs/morirlento/lib/python3.11/site-packages/scipy/sparse/_sputils.py:226, in isintlike(x)
    225 try:
--> 226     operator.index(x)
    227 except (TypeError, ValueError):

TypeError: 'numpy.float64' object cannot be interpreted as an integer

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[28], line 1
----> 1 bwa.ContributionAnalysis().annotated_top_emissions(a_lca)

File ~/miniforge3/envs/morirlento/lib/python3.11/site-packages/bw2analyzer/contribution.py:152, in ContributionAnalysis.annotated_top_emissions(self, lca, names, **kwargs)
    146 """Get list of most damaging biosphere flows in an LCA, sorted by ``abs(direct impact)``.
    147 
    148 Returns a list of tuples: ``(lca score, inventory amount, activity)``. If ``names`` is False, they returns the process key as the last element.
    149 
    150 """
    151 ra, rp, rb = lca.reverse_dict()
--> 152 results = [
    153     (score, lca.inventory[index, :].sum(), rb[index])
    154     for score, index in self.top_emissions(
    155         lca.characterized_inventory, **kwargs
    156     )
    157 ]
    158 if names:
    159     results = [(x[0], x[1], get_activity(x[2])) for x in results]

File ~/miniforge3/envs/morirlento/lib/python3.11/site-packages/bw2analyzer/contribution.py:153, in <listcomp>(.0)
    146 """Get list of most damaging biosphere flows in an LCA, sorted by ``abs(direct impact)``.
    147 
    148 Returns a list of tuples: ``(lca score, inventory amount, activity)``. If ``names`` is False, they returns the process key as the last element.
    149 
    150 """
    151 ra, rp, rb = lca.reverse_dict()
    152 results = [
--> 153     (score, lca.inventory[index, :].sum(), rb[index])
    154     for score, index in self.top_emissions(
    155         lca.characterized_inventory, **kwargs
    156     )
    157 ]
    158 if names:
    159     results = [(x[0], x[1], get_activity(x[2])) for x in results]

File ~/miniforge3/envs/morirlento/lib/python3.11/site-packages/scipy/sparse/_index.py:46, in IndexMixin.__getitem__(self, key)
     45 def __getitem__(self, key):
---> 46     row, col = self._validate_indices(key)
     48     # Dispatch to specialized methods.
     49     if isinstance(row, INT_TYPES):

File ~/miniforge3/envs/morirlento/lib/python3.11/site-packages/scipy/sparse/_index.py:151, in IndexMixin._validate_indices(self, key)
    148 M, N = self.shape
    149 row, col = _unpack_index(key)
--> 151 if isintlike(row):
    152     row = int(row)
    153     if row < -M or row >= M:

File ~/miniforge3/envs/morirlento/lib/python3.11/site-packages/scipy/sparse/_sputils.py:234, in isintlike(x)
    232     if loose_int:
    233         msg = "Inexact indices into sparse matrices are not allowed"
--> 234         raise ValueError(msg)
    235     return loose_int
    236 return True

ValueError: Inexact indices into sparse matrices are not allowed

The erorr is at the moment of accessing the indices (that come out as np.float64). This seems to have already caused problem and got a fix by casting the index to be an int 👀 bw2-legacy branch of bw2analyzer

So, a quick-fix, before there's an actual commit fixing this in the said branch, would be to implement your own annotated_top_emissions function as follows:

import bw2data as bd
import bw2analyzer as bwa

def bw2_compat_annotated_top_emissions(lca, names=True, **kwargs):
    """Get list of most damaging biosphere flows in an LCA, sorted by ``abs(direct impact)``.

    Returns a list of tuples: ``(lca score, inventory amount, activity)``. If ``names`` is False, they returns the process key as the last element.

    """
    # This is a temporary fix, until https://github.com/brightway-lca/brightway2-analyzer/issues/27
    # gets correctly handled for bw2 branch
    # The only difference in

    print("Using compat mode annotated_top_emissions")

    ra, rp, rb = lca.reverse_dict()
    results = [
        (score, lca.inventory[int(index), :].sum(), rb[int(index)])
        for score, index in bwa.ContributionAnalysis().top_emissions(
            lca.characterized_inventory, **kwargs
        )
    ]
    if names:
        results = [(x[0], x[1], get_activity(x[2])) for x in results]
    return results

Edit: I added the actual type casting missing from the original comment 🫣 .