Skip to content

fluxopt.contributions

Per-contributor effect breakdown.

Decomposes solver effect totals into per-contributor (flow/storage) parts, split into temporal (per-timestep) and lump (sizing + investment costs) domains — matching the model's own temporal/lump structure.

Two views are supported via the cross_effects parameter on compute_effect_contributions:

  • with cross-effects (default): propagates contribution_from chains via the Leontief inverse — total = (I - C)^-1 * direct — so each contributor is charged the full priced-in cost (e.g. CO₂ → cost).
  • direct: skips Leontief; each contributor shows only effects it directly emits.

Functions:

Name Description
compute_effect_contributions

Compute per-contributor effect breakdown from solved values.

compute_effect_contributions

compute_effect_contributions(
    solution: Dataset, data: ModelData, *, cross_effects: bool = True
) -> Dataset

Compute per-contributor effect breakdown from solved values.

Decomposes effect totals into per-contributor parts on a unified contributor dimension (flow IDs + storage IDs).

Parameters:

Name Type Description Default

solution

Dataset

Solved variable dataset from Result.solution.

required

data

ModelData

Model data used to build the optimization.

required

cross_effects

bool

When True (default), propagates effects along contribution_from chains via the Leontief inverse so each contributor is charged the full priced-in cost (e.g. CO₂ → cost). When False, returns direct contributions only — each contributor shows only effects it directly emits, ignoring cross-effects.

True

Returns:

Type Description
Dataset

Dataset with:

Dataset
  • temporal (contributor, effect, time) — per-timestep contributions
Dataset
  • lump (contributor, effect) — lump contributions (flows + storages)
Dataset
  • total (contributor, effect) — temporal summed over time + lump

Raises:

Type Description
ValueError

if cross_effects=True and the contributions don't match solver totals (a sanity check on the breakdown).

Source code in src/fluxopt/contributions.py
def compute_effect_contributions(
    solution: xr.Dataset,
    data: ModelData,
    *,
    cross_effects: bool = True,
) -> xr.Dataset:
    """Compute per-contributor effect breakdown from solved values.

    Decomposes effect totals into per-contributor parts on a unified
    ``contributor`` dimension (flow IDs + storage IDs).

    Args:
        solution: Solved variable dataset from ``Result.solution``.
        data: Model data used to build the optimization.
        cross_effects: When True (default), propagates effects along
            ``contribution_from`` chains via the Leontief inverse so each
            contributor is charged the full priced-in cost (e.g. CO₂ → cost).
            When False, returns *direct* contributions only — each contributor
            shows only effects it directly emits, ignoring cross-effects.

    Returns:
        Dataset with:
        - ``temporal`` (contributor, effect, time) — per-timestep contributions
        - ``lump`` (contributor, effect) — lump contributions (flows + storages)
        - ``total`` (contributor, effect) — temporal summed over time + lump

    Raises:
        ValueError: if ``cross_effects=True`` and the contributions don't
            match solver totals (a sanity check on the breakdown).
    """
    temporal, lump, all_ids = _compute_direct(solution, data)
    direct = _finalize(temporal, lump, all_ids, data)
    if cross_effects:
        return _with_cross_effects(direct, data, solution)
    return direct