Plotting¶
Access optimization results and create visualizations.
This notebook covers:
- Accessing data (flow rates, sizes, effects, charge states)
- Time series plots (balance, flows, storage)
- Aggregated plots (sizes, effects, duration curves)
- Heatmaps with time reshaping
- Sankey diagrams
- Topology visualization
- Color customization and export
Setup¶
from data.generate_example_systems import create_complex_system, create_multiperiod_system, create_simple_system
import flixopt as fx
fx.CONFIG.notebook()
flixopt.config.CONFIG
Generate Example Systems¶
First, create three example FlowSystems with solutions:
# Create and optimize the example systems
solver = fx.solvers.HighsSolver(mip_gap=0.01, log_to_console=False)
simple = create_simple_system()
simple.optimize(solver)
complex_sys = create_complex_system()
complex_sys.optimize(solver)
multiperiod = create_multiperiod_system()
multiperiod.optimize(solver)
print('Created systems:')
print(f' simple: {len(simple.components)} components, {len(simple.buses)} buses')
print(f' complex_sys: {len(complex_sys.components)} components, {len(complex_sys.buses)} buses')
print(f' multiperiod: {len(multiperiod.components)} components, dims={dict(multiperiod.solution.sizes)}')
HighsMipSolverData::transformNewIntegerFeasibleSolution tmpSolver.run();
Created systems:
simple: 4 components, 2 buses
complex_sys: 9 components, 3 buses
multiperiod: 4 components, dims={'period': 3, 'scenario': 2, 'time': 337}
2. Quick Overview: Balance Plot¶
Let's start with the most common visualization - a balance plot showing energy flows:
# Balance plot for the Heat bus - shows all inflows and outflows
simple.stats.plot.balance('Heat')
Accessing Plot Data¶
Every plot returns a PlotResult with both the figure and underlying data. Use .data.to_dataframe() to get a pandas DataFrame:
# Get plot result and access the underlying data
result = simple.stats.plot.balance('Heat', show=False)
# Convert to DataFrame for easy viewing/export
df = result.data.to_dataframe()
df.head(10)
| Boiler(Heat) | Office(Heat) | ThermalStorage(Charge) | ThermalStorage(Discharge) | |
|---|---|---|---|---|
| time | ||||
| 2020-01-15 00:00:00 | -0.000000 | 74.814983 | 0.0 | -74.814983 |
| 2020-01-15 01:00:00 | -75.497186 | 75.497186 | -0.0 | 0.000000 |
| 2020-01-15 02:00:00 | -0.000000 | 75.724587 | 0.0 | -75.724587 |
| 2020-01-15 03:00:00 | -88.004250 | 88.004250 | 0.0 | -0.000000 |
| 2020-01-15 04:00:00 | -104.149733 | 104.149733 | 0.0 | 0.000000 |
| 2020-01-15 05:00:00 | -104.604535 | 104.604535 | -0.0 | 0.000000 |
| 2020-01-15 06:00:00 | -123.478832 | 123.478832 | 0.0 | -0.000000 |
| 2020-01-15 07:00:00 | -127.117250 | 127.117250 | -0.0 | 0.000000 |
| 2020-01-15 08:00:00 | -122.569227 | 122.569227 | 0.0 | -0.000000 |
| 2020-01-15 09:00:00 | -117.111599 | 117.111599 | 0.0 | -0.000000 |
Energy Totals¶
Get total energy by flow using flow_hours:
import pandas as pd
# Total energy per flow
totals = {var: float(simple.stats.flow_hours[var].sum()) for var in simple.stats.flow_hours.data_vars}
pd.Series(totals, name='Energy [kWh]').to_frame().T
| GasGrid(Gas) | Boiler(Gas) | Boiler(Heat) | ThermalStorage(Charge) | ThermalStorage(Discharge) | Office(Heat) | |
|---|---|---|---|---|---|---|
| Energy [kWh] | 16317.686404 | 16317.686404 | 15012.271491 | 162.811061 | 150.53957 | 15000.0 |
3. Time Series Plots¶
3.1 Balance Plot¶
Shows inflows (positive) and outflows (negative) for a bus or component:
# Component balance (all flows of a component)
simple.stats.plot.balance('ThermalStorage')
3.2 Carrier Balance¶
Shows all flows of a specific carrier across the entire system, aggregated by component.
- Components that only supply or demand show as a single entry (e.g.,
Boiler) - Components with both supply and demand show separate entries (e.g.,
Storage (supply)andStorage (demand))
simple.stats.plot.carrier_balance('heat')
complex_sys.stats.plot.carrier_balance('electricity')
3.3 Flow Rates¶
Plot multiple flow rates together:
# All flows
simple.stats.plot.flows()
# Flows filtered by component
simple.stats.plot.flows(component='Boiler')
3.4 Storage Plot¶
Combined view of storage charge state and flows:
simple.stats.plot.storage('ThermalStorage')
3.5 Charge States Plot¶
Plot charge state time series directly:
simple.stats.plot.charge_states('ThermalStorage')
4. Aggregated Plots¶
4.1 Sizes Plot¶
Bar chart of component/flow sizes:
multiperiod.stats.plot.sizes()
4.2 Effects Plot¶
Bar chart of effect totals by component:
simple.stats.plot.effects(effect='costs')
# Multi-effect system: compare costs and CO2
complex_sys.stats.plot.effects(effect='costs')
complex_sys.stats.plot.effects(effect='CO2')
4.3 Duration Curve¶
Shows how often each power level is reached:
simple.stats.plot.duration_curve('Boiler(Heat)')
# Multiple variables
complex_sys.stats.plot.duration_curve(['CHP(Heat)', 'HeatPump(Heat)', 'BackupBoiler(Heat)'], threshold=None)
5. Heatmaps¶
Heatmaps reshape time series into 2D grids (e.g., hour-of-day vs day):
# Auto-reshape based on data frequency
simple.stats.plot.heatmap('Boiler(Heat)')
# Storage charge state heatmap
simple.stats.plot.heatmap('ThermalStorage')
# Custom colorscale
simple.stats.plot.heatmap('Office(Heat)', color_continuous_scale='Blues', title='Heat Demand Pattern')
6. Sankey Diagrams¶
Sankey diagrams visualize energy flows through the system.
6.1 Flow Sankey¶
Total energy flows:
simple.stats.plot.sankey.flows()
# Complex system with multiple carriers
complex_sys.stats.plot.sankey.flows()
6.2 Sizes Sankey¶
Capacity/size allocation:
multiperiod.stats.plot.sankey.sizes()
6.3 Peak Flow Sankey¶
Maximum flow rates (peak power):
simple.stats.plot.sankey.peak_flow()
6.4 Effects Sankey¶
Cost/emission allocation:
simple.stats.plot.sankey.effects(select={'effect': 'costs'})
# CO2 allocation in complex system
complex_sys.stats.plot.sankey.effects(select={'effect': 'CO2'})
6.5 Filtering with select¶
Filter Sankey to specific buses or carriers:
# Only heat flows
complex_sys.stats.plot.sankey.flows(select={'bus': 'Heat'})
7. Topology Visualization¶
Visualize the system structure (no solution data required).
7.1 Topology Plot¶
Sankey-style network diagram:
simple.topology.plot()
complex_sys.topology.plot(title='Complex System Topology')
7.2 Topology Info¶
Get node and edge information programmatically:
nodes, edges = simple.topology.infos()
print('Nodes:')
for label, info in nodes.items():
print(f' {label}: {info["class"]}')
print('\nEdges (flows):')
for label, info in edges.items():
print(f' {info["start"]} -> {info["end"]}: {label}')
Nodes: GasGrid: Component Boiler: Component ThermalStorage: Component Office: Component Gas: Bus Heat: Bus Edges (flows): Gas -> Boiler: Boiler(Gas) Boiler -> Heat: Boiler(Heat) GasGrid -> Gas: GasGrid(Gas) Heat -> Office: Office(Heat) Heat -> ThermalStorage: ThermalStorage(Charge) ThermalStorage -> Heat: ThermalStorage(Discharge)
8. Multi-Period/Scenario Data¶
Working with multi-dimensional results:
print('Multiperiod system dimensions:')
print(f' Periods: {list(multiperiod.periods)}')
print(f' Scenarios: {list(multiperiod.scenarios)}')
print(f' Solution dims: {dict(multiperiod.solution.sizes)}')
Multiperiod system dimensions:
Periods: [2024, 2025, 2026]
Scenarios: ['high_demand', 'low_demand']
Solution dims: {'period': 3, 'scenario': 2, 'time': 337}
# Balance plot with faceting by scenario
multiperiod.stats.plot.balance('Heat')
# Filter to specific scenario/period
multiperiod.stats.plot.balance('Heat', select={'scenario': 'high_demand', 'period': 2024})
# Sankey aggregates across all dimensions by default
multiperiod.stats.plot.sankey.flows()
9. Color Customization¶
Colors can be customized in multiple ways:
# Using a colorscale name
simple.stats.plot.balance('Heat', colors='Set2')
# Using a list of colors
simple.stats.plot.balance('Heat', colors=['#e41a1c', '#377eb8', '#4daf4a', '#984ea3'])
# Using a dictionary for specific labels
simple.stats.plot.balance(
'Heat',
colors={
'Boiler(Heat)': 'orangered',
'ThermalStorage(Charge)': 'steelblue',
'ThermalStorage(Discharge)': 'lightblue',
'Office(Heat)': 'forestgreen',
},
)
10. Exporting Results¶
Plots return a PlotResult with data and figure that can be exported:
# Get plot result
result = simple.stats.plot.balance('Heat')
print('PlotResult contains:')
print(f' data: {type(result.data).__name__} with vars {list(result.data.data_vars)}')
print(f' figure: {type(result.figure).__name__}')
PlotResult contains: data: Dataset with vars ['Boiler(Heat)', 'Office(Heat)', 'ThermalStorage(Charge)', 'ThermalStorage(Discharge)'] figure: Figure
# Export data to pandas DataFrame
df = result.data.to_dataframe()
df.head()
| Boiler(Heat) | Office(Heat) | ThermalStorage(Charge) | ThermalStorage(Discharge) | |
|---|---|---|---|---|
| time | ||||
| 2020-01-15 00:00:00 | -0.000000 | 74.814983 | 0.0 | -74.814983 |
| 2020-01-15 01:00:00 | -75.497186 | 75.497186 | -0.0 | 0.000000 |
| 2020-01-15 02:00:00 | -0.000000 | 75.724587 | 0.0 | -75.724587 |
| 2020-01-15 03:00:00 | -88.004250 | 88.004250 | 0.0 | -0.000000 |
| 2020-01-15 04:00:00 | -104.149733 | 104.149733 | 0.0 | 0.000000 |
# Export figure to HTML (interactive)
# result.figure.write_html('balance_plot.html')
# Export figure to image
# result.figure.write_image('balance_plot.png', scale=2)
Summary¶
Data Access¶
| Property | Description |
|---|---|
statistics.flow_rates | Time series of flow rates (power) |
statistics.flow_hours | Energy values (rate × duration) |
statistics.sizes | Component/flow capacities |
statistics.charge_states | Storage charge levels |
statistics.temporal_effects | Effects per timestep |
statistics.periodic_effects | Effects per period |
statistics.total_effects | Aggregated effect totals |
topology.carrier_colors | Cached carrier color mapping |
topology.component_colors | Cached component color mapping |
topology.bus_colors | Cached bus color mapping |
Plot Methods¶
| Method | Description |
|---|---|
plot.balance(node) | Stacked bar of in/outflows |
plot.carrier_balance(carrier) | Balance for all flows of a carrier |
plot.flows(variables) | Time series line/area plot |
plot.storage(component) | Combined charge state and flows |
plot.charge_states(component) | Charge state time series |
plot.sizes() | Bar chart of sizes |
plot.effects(effect) | Bar chart of effect contributions |
plot.duration_curve(variables) | Sorted duration curve |
plot.heatmap(variable) | 2D time-reshaped heatmap |
plot.sankey.flows() | Energy flow Sankey |
plot.sankey.sizes() | Capacity Sankey |
plot.sankey.peak_flow() | Peak power Sankey |
plot.sankey.effects(effect) | Effect allocation Sankey |
topology.plot() | System structure diagram |