Skip to content

feat: Result enhancements — topology, duals, and viz API #47

@FBumann

Description

@FBumann

Background

Design decisions for Result enhancements and the fluxopt-plot viz API.


Package Separation — Done

Core (fluxopt) is lean. Viz lives in fluxopt-plot (separate package, own versioning). Stats accessor and contributions live in core. No changes needed here.


Result Enhancements

Topology

A topology cached property on Result, derived from ModelData (no new dependencies).

Returns a simple dict:

result.topology
# {
#     "carriers": {
#         "electricity": {
#             "inputs": ["grid(electricity)", "pv(electricity)"],
#             "outputs": ["hp(electricity)"],
#         },
#         "heat": {
#             "inputs": ["hp(heat)", "boiler(heat)"],
#             "outputs": ["demand(heat)"],
#         },
#     },
#     "converters": {
#         "hp": {
#             "inputs": ["hp(electricity)"],
#             "outputs": ["hp(heat)"],
#         },
#     },
# }

Derived from CarriersData.flow_coeff signs (+1 = input to carrier, -1 = output from carrier) and ConvertersData pair arrays. fluxopt-plot can build a Sankey or nx.DiGraph from this if needed — that's a viz concern, not core.

Dual Values

Linopy stores duals in model.dual as xr.Dataset but Result currently ignores them. For pure LP this gives marginal costs on carrier balance constraints (e.g., locational marginal prices).

Add Result.duals: xr.Dataset extracted from model.m.dual in from_model. Empty Dataset when not available (MIP).


Viz API Design (fluxopt-plot)

Methods named after questions users ask, not chart types. period is a first-class parameter on all time-series methods.

Bus / Node

result.plot.bus_balance(bus, period=None, mode="stacked_bar")
result.plot.bus_balance_pie(bus, period=None)

bus_balance smart defaults (from discussion):

  • All flows as bars by default, outputs negated
  • lines=["HeatDemand"] — override specific flows to line style (implies sign flip)
  • Flows with fixed_profile auto-detected as lines
  • Users think in component/flow labels, not variable names — accessor resolves internally

Component

result.plot.component_operation(component, period=None)
result.plot.component_duration_curve(component, variable, period=None)

Effects

result.plot.effect_breakdown(effect, by="component")
result.plot.effect_over_periods(effect)

Investment

result.plot.investment_sizes(group=None)
result.plot.investment_over_periods()

System-Level

result.plot.sankey(period=None, mode="flow_hours")
result.plot.system_comparison(variable, across="components")

Multi-Period

result.plot.period_comparison(variable, element)
result.plot.heatmap(variable, reshape=("D", "h"))

Not in Scope

  • VariableMeta / ConstraintMeta dicts — structural info lives in ModelData
  • nx.DiGraph on core — viz can build one from topology dict if needed
  • Stability tiers — documentation concern, not code
  • dims / metadata dicts — already on the Dataset

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions