Skip to content

fluxopt.results

Classes:

Name Description
Result

Optimization result with solution variables and model data.

Result dataclass

Optimization result with solution variables and model data.

Provides access to flow rates, storage levels, effect totals, and investment decisions. Key properties::

result.objective  # scalar objective value
result.flow_rates  # (flow, time) DataArray
result.flow_rate('id')  # single flow time series
result.storage_levels  # (storage, time) DataArray
result.effect_totals  # (effect,) DataArray
result.effects_temporal  # (effect, time) DataArray
result.effects_lump  # (effect,) DataArray
result.sizes  # (flow,) DataArray — invested sizes
result.storage_capacities  # (storage,) DataArray

Per-contributor effect breakdown is available via result.stats.

Parameters:

Name Type Description Default

solution

Dataset

Solved variable values as xr.Dataset.

required

data

ModelData

ModelData used to build the optimization.

required

duals

Dataset

Dual values (shadow prices) from the solver.

Dataset()

expressions

Dataset

Every quantity the model names, evaluated at this solution. A named expression is evaluated against a solve, so it can only be had here — carrying them is what makes a Result answer for the model rather than only for its variables. Read one with :meth:expression; the effect breakdown is a view over them (result.stats.effect_contributions).

Dataset()

Methods:

Name Description
flow_rate

Get flow rate time series for a single flow.

storage_level

Get charge state time series for a single storage.

expression

One quantity the model names, at this solution.

save

Write the result as a directory.

load

Read a result written by :meth:save.

Attributes:

Name Type Description
objective float

Objective function value.

objective_weights dict[str, float]

Effect weights the objective was minimized with (provenance).

flow_rates DataArray

All flow rates as (flow, time) DataArray.

storage_levels DataArray

All storage levels as (storage, time) DataArray.

sizes DataArray

Optimized flow sizes as (flow,) DataArray.

storage_capacities DataArray

Optimized storage capacities as (storage,) DataArray.

effect_totals DataArray

Total effect values as (effect,) DataArray.

effects_temporal DataArray

Per-timestep effect values as (effect, time) DataArray.

effects_lump DataArray

Non-temporal effect values as (effect,) DataArray.

topology dict[Literal['carriers', 'converters'], dict[str, dict[str, list[str]]]]

Carrier and converter connectivity derived from model data.

stats StatsAccessor

Post-processing statistics accessor.

plot PlotAccessor

Plotting accessor (requires fluxopt-plot).

objective property

objective: float

Objective function value.

objective_weights property

objective_weights: dict[str, float]

Effect weights the objective was minimized with (provenance).

Includes the built-in penalty effect (auto-added at 1.0 unless named in objective). Empty for results saved before this field existed.

flow_rates property

flow_rates: DataArray

All flow rates as (flow, time) DataArray.

storage_levels property

storage_levels: DataArray

All storage levels as (storage, time) DataArray.

sizes property

sizes: DataArray

Optimized flow sizes as (flow,) DataArray.

storage_capacities property

storage_capacities: DataArray

Optimized storage capacities as (storage,) DataArray.

effect_totals property

effect_totals: DataArray

Total effect values as (effect,) DataArray.

effects_temporal property

effects_temporal: DataArray

Per-timestep effect values as (effect, time) DataArray.

Reconstructed from flow rates and coefficients — the model carries no per-timestep effect variables (temporal closure).

effects_lump property

effects_lump: DataArray

Non-temporal effect values as (effect,) DataArray.

topology cached property

topology: dict[
    Literal['carriers', 'converters'], dict[str, dict[str, list[str]]]
]

Carrier and converter connectivity derived from model data.

Returns a dict with carriers and converters keys, each mapping element ids to their inputs (flows that produce into the element) and outputs (flows that consume from it).

stats cached property

Post-processing statistics accessor.

plot cached property

plot: PlotAccessor

Plotting accessor (requires fluxopt-plot).

flow_rate

flow_rate(flow_id: str) -> DataArray

Get flow rate time series for a single flow.

Parameters:

Name Type Description Default

flow_id

str

Qualified flow id.

required
Source code in src/fluxopt/results.py
def flow_rate(self, flow_id: str) -> xr.DataArray:
    """Get flow rate time series for a single flow.

    Args:
        flow_id: Qualified flow id.
    """
    return self.flow_rates.sel(flow=flow_id)

storage_level

storage_level(storage_id: str) -> DataArray

Get charge state time series for a single storage.

Parameters:

Name Type Description Default

storage_id

str

Storage id.

required
Source code in src/fluxopt/results.py
def storage_level(self, storage_id: str) -> xr.DataArray:
    """Get charge state time series for a single storage.

    Args:
        storage_id: Storage id.
    """
    return self.storage_levels.sel(storage=storage_id)

expression

expression(name: str) -> DataArray

One quantity the model names, at this solution.

Whatever the program declares under expressions: — including anything a caller added through optimize(math=...).

Parameters:

Name Type Description Default

name

str

The expression's name in the program.

required

Raises:

Type Description
KeyError

Naming what this Result does carry, since a model that declared it and a Result that kept it are different things.

Source code in src/fluxopt/results.py
def expression(self, name: str) -> xr.DataArray:
    """One quantity the model names, at this solution.

    Whatever the program declares under ``expressions:`` — including
    anything a caller added through ``optimize(math=...)``.

    Args:
        name: The expression's name in the program.

    Raises:
        KeyError: Naming what this Result does carry, since a model that
            declared it and a Result that kept it are different things.
    """
    if name not in self.expressions:
        available = ', '.join(sorted(str(n) for n in self.expressions.data_vars)) or 'nothing'
        msg = f'{name!r} is not among the expressions this Result carries ({available})'
        raise KeyError(msg)
    return self.expressions[name]

save

save(path: str | Path) -> None

Write the result as a directory.

The solution and the named quantities are labelled N-dimensional arrays, so they stay netCDF; the model data is tables and goes to parquet under model/.

Parameters:

Name Type Description Default

path

str | Path

Directory to write into. Created if absent.

required
Source code in src/fluxopt/results.py
def save(self, path: str | Path) -> None:
    """Write the result as a directory.

    The solution and the named quantities are labelled N-dimensional
    arrays, so they stay netCDF; the model data is tables and goes to
    parquet under ``model/``.

    Args:
        path: Directory to write into. Created if absent.
    """
    root = Path(path)
    root.mkdir(parents=True, exist_ok=True)
    self.solution.to_netcdf(root / 'solution.nc', mode='w', engine='netcdf4')
    if self.expressions.data_vars:
        self.expressions.to_netcdf(root / 'expressions.nc', mode='w', engine='netcdf4')
    self.data.save(root / 'model')

load classmethod

load(path: str | Path) -> Result

Read a result written by :meth:save.

Parameters:

Name Type Description Default

path

str | Path

The directory :meth:save wrote.

required

Raises:

Type Description
OSError

If the directory holds no solution.

Source code in src/fluxopt/results.py
@classmethod
def load(cls, path: str | Path) -> Result:
    """Read a result written by :meth:`save`.

    Args:
        path: The directory :meth:`save` wrote.

    Raises:
        OSError: If the directory holds no solution.
    """
    import warnings

    from fluxopt.model_data import ModelData

    root = Path(path)
    if not (root / 'solution.nc').is_file():
        raise OSError(f'No fluxopt result found in {root} (missing solution.nc)')
    solution = _read_netcdf(root / 'solution.nc')
    expressions = _read_netcdf(root / 'expressions.nc') if (root / 'expressions.nc').is_file() else xr.Dataset()
    if not expressions.data_vars:
        warnings.warn(
            f'{root} carries none of the quantities the model names — including the '
            'per-contributor effect breakdown. They are evaluated against a solve and '
            'cannot be recovered from the solution alone; re-solve, or re-save a Result '
            'that has them.',
            stacklevel=2,
        )
    return cls(solution=solution, data=ModelData.load(root / 'model'), expressions=expressions)