Source code for bw2calc.fast_scores

import warnings

import numpy as np
import xarray

from bw2calc import PYPARDISO, UMFPACK
from bw2calc.fast_supply_arrays import FastSupplyArraysMixin
from bw2calc.multi_lca import MultiLCA


[docs] class FastScoresOnlyMultiLCA(MultiLCA, FastSupplyArraysMixin): """Use chunking and pre-calculate as much as possible to optimize speed for multiple LCA calculations. If using pardiso via pypardiso: - Feed multiple demands at once as a tensor into the solver function - Skip some identity checks on the technosphere matrix """ def __init__(self, *args, chunk_size: int = 50, **kwargs): # Extract chunk_size before passing to super() to avoid it being consumed # by MultiLCA.__init__, then manually initialize mixin attributes super().__init__(*args, **kwargs) self.set_chunk_size(chunk_size) if UMFPACK: warnings.warn( """Using UMFPACK - the speedups in `FastSupplyArraysMixin` work better when using PARDISO""" # noqa: E501 )
[docs] def lci(self) -> None: raise NotImplementedError( "LCI and LCIA aren't separate in `FastScoresOnlyMultiLCA`; use `next()` to calculate scores." # noqa: E501 )
[docs] def lci_calculation(self) -> None: raise NotImplementedError( "LCI and LCIA aren't separate in `FastScoresOnlyMultiLCA`; use `next()` to calculate scores." # noqa: E501 )
[docs] def lcia(self) -> None: raise NotImplementedError( "LCI and LCIA aren't separate in `FastScoresOnlyMultiLCA`; use `next()` to calculate scores." # noqa: E501 )
[docs] def lcia_calculation(self) -> None: raise NotImplementedError( "LCI and LCIA aren't separate in `FastScoresOnlyMultiLCA`; use `next()` to calculate scores." # noqa: E501 )
[docs] def build_precalculated(self) -> None: """Multiply the characterization, and normalization and weighting matrices if present, by the biosphere matrix. When done outside the calculation loop, this only needs to be done once.""" self.precalculated = self.characterization_matrices @ self.biosphere_matrix if hasattr(self, "normalization_matrices"): self.precalculated = self.normalization_matrices @ self.precalculated if hasattr(self, "weighting_matrices"): self.precalculated = self.weighting_matrices @ self.precalculated self.precalculated = { key: np.asarray(matrix.sum(axis=0)) for key, matrix in self.precalculated.items() }
[docs] def _calculation(self) -> xarray.DataArray: # Calls lci_calculation() and lcia_calculation in parent class, but we don't have # these as separate methods, so need to override to change behaviour. return self.calculate()
[docs] def _load_datapackages(self) -> None: self.load_lci_data() self.build_demand_array() self.load_lcia_data() if self.config.get("normalizations"): self.load_normalization_data() if self.config.get("weightings"): self.load_weighting_data()
[docs] def calculate(self) -> xarray.DataArray: """The actual LCI calculation. Separated from ``lci`` to be reusable in cases where the matrices are already built, e.g. ``redo_lci`` and Monte Carlo classes. """ if not (PYPARDISO or UMFPACK): raise ValueError( "`FastScoresOnlyMultiLCA` only supported with PARDISO and UMFPACK solvers" ) if not hasattr(self, "technosphere_matrix"): self._load_datapackages() self.build_precalculated() self.supply_array = self.calculate_supply_arrays(list(self.demand_arrays.values())) lcia_array = np.vstack(list(self.precalculated.values())) scores = lcia_array @ self.supply_array self._set_scores( xarray.DataArray( scores, coords=[[str(x) for x in self.precalculated], list(self.demand_arrays)], dims=["LCIA", "processes"], ) ) return self._scores
[docs] def _get_scores(self) -> xarray.DataArray: if not hasattr(self, "_scores"): raise ValueError("Scores not calculated yet") return self._scores
[docs] def _set_scores(self, arr: xarray.DataArray) -> None: self._scores = arr
[docs] scores = property(fget=_get_scores, fset=_set_scores)