Plotting¶
Access optimization results and create visualizations.
This notebook covers:
- Loading saved FlowSystems from NetCDF files
- 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 pathlib import Path
import flixopt as fx
fx.CONFIG.notebook()
flixopt.config.CONFIG
Generate Example Data¶
First, run the script that generates three example FlowSystems with solutions:
# Run the generation script (only needed once, or to regenerate)
!python data/generate_example_systems.py > /dev/null 2>&1
1. Loading Saved FlowSystems¶
FlowSystems can be saved to and loaded from NetCDF files, preserving the full structure and solution:
DATA_DIR = Path('data')
# Load the three example systems
simple = fx.FlowSystem.from_netcdf(DATA_DIR / 'simple_system.nc4')
complex_sys = fx.FlowSystem.from_netcdf(DATA_DIR / 'complex_system.nc4')
multiperiod = fx.FlowSystem.from_netcdf(DATA_DIR / 'multiperiod_system.nc4')
print('Loaded 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)}')
Loaded systems:
simple: 4 components, 2 buses
complex_sys: 9 components, 3 buses
multiperiod: 4 components, dims={'scenario': 2, 'period': 3, 'time': 49}
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.statistics.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.statistics.plot.balance('Heat', show=False)
# Convert to DataFrame for easy viewing/export
df = result.data.to_dataframe()
df.head(10)
| Boiler(Heat) | ThermalStorage(Discharge) | ThermalStorage(Charge) | Office(Heat) | |
|---|---|---|---|---|
| time | ||||
| 2024-01-15 00:00:00 | -32.483571 | -0.000000e+00 | 0.000000e+00 | 32.483571 |
| 2024-01-15 01:00:00 | -29.308678 | 1.758414e-13 | 4.797958e-14 | 29.308678 |
| 2024-01-15 02:00:00 | -33.238443 | -5.311128e-15 | 1.758414e-13 | 33.238443 |
| 2024-01-15 03:00:00 | -101.411593 | -3.516828e-13 | 6.379644e+01 | 37.615149 |
| 2024-01-15 04:00:00 | -128.829233 | 8.455459e-13 | 1.000000e+02 | 28.829233 |
| 2024-01-15 05:00:00 | -128.829315 | -2.813462e-12 | 1.000000e+02 | 28.829315 |
| 2024-01-15 06:00:00 | -0.000000 | -3.789606e+01 | 1.055048e-12 | 37.896064 |
| 2024-01-15 07:00:00 | -0.000000 | -8.383717e+01 | -2.110097e-12 | 83.837174 |
| 2024-01-15 08:00:00 | -0.000000 | -7.765263e+01 | 6.394885e-13 | 77.652628 |
| 2024-01-15 09:00:00 | -0.000000 | -8.271280e+01 | 0.000000e+00 | 82.712800 |
Energy Totals¶
Get total energy by flow using flow_hours:
import pandas as pd
# Total energy per flow
totals = {var: float(simple.statistics.flow_hours[var].sum()) for var in simple.statistics.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] | 8936.665406 | 8936.665406 | 8221.732173 | 3457.182735 | 3242.788948 | 8007.338386 |
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.statistics.plot.balance('ThermalStorage')
3.2 Carrier Balance¶
Shows all flows of a specific carrier across the entire system:
complex_sys.statistics.plot.carrier_balance('heat')
complex_sys.statistics.plot.carrier_balance('electricity')
3.3 Flow Rates¶
Plot multiple flow rates together:
# All flows
simple.statistics.plot.flows()
# Flows filtered by component
simple.statistics.plot.flows(component='Boiler')
3.4 Storage Plot¶
Combined view of storage charge state and flows:
simple.statistics.plot.storage('ThermalStorage')
3.5 Charge States Plot¶
Plot charge state time series directly:
simple.statistics.plot.charge_states('ThermalStorage')
4. Aggregated Plots¶
4.1 Sizes Plot¶
Bar chart of component/flow sizes:
multiperiod.statistics.plot.sizes()
4.2 Effects Plot¶
Bar chart of effect totals by component:
simple.statistics.plot.effects(effect='costs')
# Multi-effect system: compare costs and CO2
complex_sys.statistics.plot.effects(effect='costs')
complex_sys.statistics.plot.effects(effect='CO2')
4.3 Duration Curve¶
Shows how often each power level is reached:
simple.statistics.plot.duration_curve('Boiler(Heat)')
# Multiple variables
complex_sys.statistics.plot.duration_curve(['CHP(Heat)', 'HeatPump(Heat)', 'BackupBoiler(Heat)'])
5. Heatmaps¶
Heatmaps reshape time series into 2D grids (e.g., hour-of-day vs day):
# Auto-reshape based on data frequency
simple.statistics.plot.heatmap('Boiler(Heat)')
# Storage charge state heatmap
simple.statistics.plot.heatmap('ThermalStorage')
# Custom colorscale
simple.statistics.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.statistics.plot.sankey.flows()
# Complex system with multiple carriers
complex_sys.statistics.plot.sankey.flows()
6.2 Sizes Sankey¶
Capacity/size allocation:
multiperiod.statistics.plot.sankey.sizes()
6.3 Peak Flow Sankey¶
Maximum flow rates (peak power):
simple.statistics.plot.sankey.peak_flow()
6.4 Effects Sankey¶
Cost/emission allocation:
simple.statistics.plot.sankey.effects(select={'effect': 'costs'})
# CO2 allocation in complex system
complex_sys.statistics.plot.sankey.effects(select={'effect': 'CO2'})
6.5 Filtering with select¶
Filter Sankey to specific buses or carriers:
# Only heat flows
complex_sys.statistics.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: {'scenario': 2, 'period': 3, 'time': 49}
# Balance plot with faceting by scenario
multiperiod.statistics.plot.balance('Heat')
# Filter to specific scenario/period
multiperiod.statistics.plot.balance('Heat', select={'scenario': 'high_demand', 'period': 2024})
# Sankey aggregates across all dimensions by default
multiperiod.statistics.plot.sankey.flows()
9. Color Customization¶
Colors can be customized in multiple ways:
# Using a colorscale name
simple.statistics.plot.balance('Heat', colors='Set2')
# Using a list of colors
simple.statistics.plot.balance('Heat', colors=['#e41a1c', '#377eb8', '#4daf4a', '#984ea3'])
# Using a dictionary for specific labels
simple.statistics.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.statistics.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)', 'ThermalStorage(Discharge)', 'ThermalStorage(Charge)', 'Office(Heat)'] figure: Figure
# Export data to pandas DataFrame
df = result.data.to_dataframe()
df.head()
| Boiler(Heat) | ThermalStorage(Discharge) | ThermalStorage(Charge) | Office(Heat) | |
|---|---|---|---|---|
| time | ||||
| 2024-01-15 00:00:00 | -32.483571 | -0.000000e+00 | 0.000000e+00 | 32.483571 |
| 2024-01-15 01:00:00 | -29.308678 | 1.758414e-13 | 4.797958e-14 | 29.308678 |
| 2024-01-15 02:00:00 | -33.238443 | -5.311128e-15 | 1.758414e-13 | 33.238443 |
| 2024-01-15 03:00:00 | -101.411593 | -3.516828e-13 | 6.379644e+01 | 37.615149 |
| 2024-01-15 04:00:00 | -128.829233 | 8.455459e-13 | 1.000000e+02 | 28.829233 |
# 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 |