bw2calc.partitioned_lca ======================= .. py:module:: bw2calc.partitioned_lca Classes ------- .. autoapisummary:: bw2calc.partitioned_lca.PartitionedMonteCarloLCA Functions --------- .. autoapisummary:: bw2calc.partitioned_lca._find_production_exchanges Module Contents --------------- .. py:class:: PartitionedMonteCarloLCA(demand: dict[int, float], static_databases: list, data_objs: list, seed_override: Optional[int] = None) Bases: :py:obj:`collections.abc.Iterator` Monte Carlo LCA that pre-solves a static background system once. Splits the full system into a static (background) part and a stochastic (foreground) part. The static system is solved deterministically for each product demanded across the static/stochastic boundary (interface products), producing aggregated biosphere vectors. These are stored in an in-memory dynamic datapackage that is combined with the stochastic packages for each Monte Carlo iteration. This avoids rebuilding and solving the (typically large) background matrix on every iteration — only the foreground matrix is resampled. :param demand: Functional unit: ``{activity_or_product_id: amount}``. Must be in the stochastic system. :type demand: dict :param static_databases: Names of databases to treat as static (e.g. ``["biosphere3", "ecoinvent 3.10"]``). :type static_databases: list[str] :param data_objs: All datapackages: stochastic LCI + static LCI + LCIA method. Packages for databases listed in ``static_databases`` are identified by their ``metadata["name"]`` field, which must equal ``bw_processing.clean_datapackage_name(database_name)``. :type data_objs: list :param seed_override: RNG seed passed to the inner stochastic LCA. :type seed_override: int, optional .. rubric:: Notes All LCI datapackages must contain a ``database_dependencies`` key in their metadata, which is written by ``bw2data >= 4.7``. Design note: composition vs. subclassing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This class uses **composition**: it builds an internal ``._lca`` instance from the stochastic packages plus the pre-solved dynamic datapackage, and delegates properties to it. An alternative would be to subclass ``LCA`` and override ``load_lci_data()`` to perform the same partitioning — classify packages, pre-solve the static system, inject the dynamic datapackage, replace ``self.packages``, then call ``super().load_lci_data()``. That approach would give full inheritance of all current and future ``LCA`` attributes without explicit delegation. The reason composition was chosen instead: * **Package list mutation.** The override would need to replace ``self.packages`` (set from ``data_objs`` in ``__init__``) with the filtered stochastic+dynamic list mid-lifecycle, which is non-obvious and makes ``self.packages`` inconsistent with ``self.data_objs``. * **Matrix semantics.** Whether composed or subclassed, ``technosphere_matrix`` is the *reduced* aggregated-proxy matrix, not the full combined static+stochastic system. Subclassing makes this less visible rather than resolving it. .. py:method:: _build_dynamic_datapackage() -> bw_processing.DatapackageBase Solve the static system for each interface product; return dynamic datapackage. .. py:method:: _check_for_cycles(graph: dict) -> None :staticmethod: .. py:method:: _classify_packages(packages) Classify packages into static LCI, stochastic LCI, and method buckets. Classification happens at the resource-group level so that a single datapackage containing groups for different matrices or different databases is split correctly. Each resource group has a single matrix label, so we use ``dp.groups`` to iterate and ``dp.exclude({"group": name})`` to produce filtered sub-packages. Group-to-database matching uses the bw2data naming convention where a group is named ``clean_datapackage_name(db_name + " " + matrix_type)`` — i.e. it starts with ``clean_datapackage_name(db_name)`` followed by ``_``. If no group-name match is found, the check falls back to the package-level ``metadata["name"]``. .. py:method:: _validate() -> None .. py:method:: keep_first_iteration() -> None .. py:method:: lci() -> None Pre-solve the static system, build the dynamic datapackage, and run the first LCI. .. py:method:: lcia() -> None .. py:property:: biosphere_matrix .. py:property:: characterization_matrix .. py:property:: characterized_inventory .. py:attribute:: demand .. py:property:: dicts .. py:property:: inventory .. py:property:: score :type: float .. py:attribute:: seed_override :value: None .. py:attribute:: static_databases .. py:property:: supply_array .. py:property:: technosphere_matrix .. py:function:: _find_production_exchanges(mm: matrix_utils.MappedMatrix) Run all production-exchange heuristics and return whatever was found. Same logic as ``bw_graph_tools.guess_production_exchanges`` but does not raise ``UnclearProductionExchange`` when some columns are unresolved. This is intentional: we call this on a non-square stochastic-only matrix whose unresolved rows are exactly the interface products we want to identify. Retains the other checks from ``guess_production_exchanges``: raises ``ValueError`` for an empty matrix or mismatched row/column result arrays. Returns ``(row_indices, col_indices)`` — integer arrays of matrix row/column indices where production exchanges were found. Unresolved columns are simply absent.