Recent Releases of flixOpt
flixOpt - v6.1.0
Summary: Adds solver log capture through the Python logging system, exposes progress and log_fn parameters on solve/optimize, and fixes cluster_weight loss during NetCDF roundtrip.
✨ Added
- Solver log capture: New
CONFIG.Solving.capture_solver_logoption routes solver output (HiGHS, Gurobi, etc.) through theflixopt.solverPython logger at INFO level. This allows capturing solver output in any Python log handler (console, file, or both) and filtering it independently from flixopt application logs. Enabled automatically byCONFIG.debug(),CONFIG.exploring(),CONFIG.production(), andCONFIG.notebook()presets. (#606) progressparameter:solve(),optimize(), androlling_horizon()now accept aprogressparameter (defaultTrue) to control the tqdm progress bar independently of CONFIG settings.log_fnparameter:solve()now accepts alog_fnparameter to persist the solver log to a file.
♻️ Changed
- Presets:
CONFIG.debug()andCONFIG.exploring()now setlog_to_console=False(solver output is routed through the Python logger instead of native console output). CONFIG.Solving.log_to_consolenow exclusively controls the solver's native console output. It no longer affects the tqdm progress bar (use theprogressparameter instead).
🐛 Fixed
- Clustering IO:
cluster_weightis now preserved during NetCDF roundtrip for manually constructed clustered FlowSystems (i.e.FlowSystem(..., clusters=..., cluster_weight=...)). Previously,cluster_weightwas silently dropped toNoneduringsave->reload->solve, causing incorrect objective values. Systems created via.transform.cluster()were not affected.
📦 Dependencies
- tsam: Updated bounds to
>= 3.1.2, < 4(was>= 3.0.0, < 4, != 3.1.0). tsam 3.0.0 and 3.1.0 were yanked from PyPI. Dev pin updated from3.0.0to3.1.2.
👷 Development
- New
test_math/test suite: Comprehensive mathematical correctness tests with exact, hand-calculated assertions. Each test runs in 3 IO modes (solve, save→reload→solve, solve→save→reload) via theoptimizefixture:test_flow.py— flow bounds, merit order, relative min/max, on/off hourstest_flow_invest.py— investment sizing, fixed-size, optional invest, piecewise investtest_flow_status.py— startup costs, switch-on/off constraints, status penaltiestest_bus.py— bus balance, excess/shortage penaltiestest_effects.py— effect aggregation, periodic/temporal effects, multi-effect objectivestest_components.py— SourceAndSink, converters, links, combined heat-and-powertest_conversion.py— linear converter balance, multi-input/output, efficiencytest_piecewise.py— piecewise-linear efficiency, segment selectiontest_storage.py— charge/discharge, SOC tracking, final charge state, lossestest_multi_period.py— period weights, invest across periodstest_scenarios.py— scenario weights, scenario-independent flowstest_clustering.py— exact per-timestep flow_rates, effects, and charge_state in clustered systems (incl. non-equal cluster weights to cover IO roundtrip)test_validation.py— plausibility checks and error messages
What's Changed
- Add mathematical correctness test suite by @FBumann in https://github.com/flixOpt/flixopt/pull/596
- feat: Route solver output through Python logging by @FBumann in https://github.com/flixOpt/flixopt/pull/606
- chore(deps): update dependency xarray to v2026 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/607
- chore(deps): update dependency mkdocs-include-markdown-plugin to v7.2.1 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/604
- chore(deps): update dependency astral-sh/uv to v0.9.30 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/503
- chore(deps): update dependency mkdocs-git-revision-date-localized-plugin to v1.5.1 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/603
- chore(deps): update actions/cache action to v5 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/568
- fix(deps): update dependency pymdown-extensions to v10.20.1 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/567
- chore(deps): update astral-sh/setup-uv action to v7 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/565
- fix(deps): update dependency mkdocs-gen-files to v0.6.0 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/504
- Add combination tests for piecewise, status, investment & effects interactions by @FBumann in https://github.com/flixOpt/flixopt/pull/608
- chore(deps): update dependency astral-sh/uv to v0.10.9 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/612
- fix(deps): update dependency linopy to >=0.5.1, <0.7 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/615
- fix(deps): update dependency xarray to >=2024.2.0, <2026.3 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/617
- fix(deps): update tsam bounds after yanked releases by @FBumann in https://github.com/flixOpt/flixopt/pull/622
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.2...v6.1.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 3 months ago
flixOpt - v6.1.0rc0
Summary: Adds solver log capture through the Python logging system, exposes progress and log_fn parameters on solve/optimize, and fixes cluster_weight loss during NetCDF roundtrip.
✨ Added
- Solver log capture: New
CONFIG.Solving.capture_solver_logoption routes solver output (HiGHS, Gurobi, etc.) through theflixopt.solverPython logger at INFO level. This allows capturing solver output in any Python log handler (console, file, or both) and filtering it independently from flixopt application logs. Enabled automatically byCONFIG.debug(),CONFIG.exploring(),CONFIG.production(), andCONFIG.notebook()presets. (#606) progressparameter:solve(),optimize(), androlling_horizon()now accept aprogressparameter (defaultTrue) to control the tqdm progress bar independently of CONFIG settings.log_fnparameter:solve()now accepts alog_fnparameter to persist the solver log to a file.
♻️ Changed
- Presets:
CONFIG.debug()andCONFIG.exploring()now setlog_to_console=False(solver output is routed through the Python logger instead of native console output). CONFIG.Solving.log_to_consolenow exclusively controls the solver's native console output. It no longer affects the tqdm progress bar (use theprogressparameter instead).
🐛 Fixed
- Clustering IO:
cluster_weightis now preserved during NetCDF roundtrip for manually constructed clustered FlowSystems (i.e.FlowSystem(..., clusters=..., cluster_weight=...)). Previously,cluster_weightwas silently dropped toNoneduringsave->reload->solve, causing incorrect objective values. Systems created via.transform.cluster()were not affected.
👷 Development
- New
test_math/test suite: Comprehensive mathematical correctness tests with exact, hand-calculated assertions. Each test runs in 3 IO modes (solve, save→reload→solve, solve→save→reload) via theoptimizefixture:test_flow.py— flow bounds, merit order, relative min/max, on/off hourstest_flow_invest.py— investment sizing, fixed-size, optional invest, piecewise investtest_flow_status.py— startup costs, switch-on/off constraints, status penaltiestest_bus.py— bus balance, excess/shortage penaltiestest_effects.py— effect aggregation, periodic/temporal effects, multi-effect objectivestest_components.py— SourceAndSink, converters, links, combined heat-and-powertest_conversion.py— linear converter balance, multi-input/output, efficiencytest_piecewise.py— piecewise-linear efficiency, segment selectiontest_storage.py— charge/discharge, SOC tracking, final charge state, lossestest_multi_period.py— period weights, invest across periodstest_scenarios.py— scenario weights, scenario-independent flowstest_clustering.py— exact per-timestep flow_rates, effects, and charge_state in clustered systems (incl. non-equal cluster weights to cover IO roundtrip)test_validation.py— plausibility checks and error messages
What's Changed
- Add mathematical correctness test suite by @FBumann in https://github.com/flixOpt/flixopt/pull/596
- feat: Route solver output through Python logging by @FBumann in https://github.com/flixOpt/flixopt/pull/606
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.2...v6.1.0rc0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 4 months ago
flixOpt - v6.0.2
Summary: Patch release which improves Comparison coordinate handling.
🐛 Fixed
- Comparison Coordinates: Fixed
componentcoordinate becoming(case, contributor)shaped after concatenation inComparisonclass. Non-index coordinates are now properly merged before concat insolution,inputs, and all statistics properties. Added warning when coordinate mappings conflict (#599)
📝 Docs
- Docs Workflow: Added
workflow_dispatchinputs for manual docs deployment with version selection (#599)
👷 Development
- Updated dev dependencies to newer versions
What's Changed
- chore(deps): update dependency werkzeug to v3.1.5 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/564
- chore(deps): update dependency ruff to v0.14.14 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/563
- chore(deps): update dependency netcdf4 to >=1.6.1, <1.7.5 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/583
- chore(deps): update dependency pre-commit to v4.5.1 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/532
- Fix JSON encoding, Comparison coords, and docs warnings by @FBumann in https://github.com/flixOpt/flixopt/pull/599
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.1...v6.0.2
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 4 months ago
flixOpt - v6.0.1
Summary: Bugfix release addressing clustering issues with multi-period systems and ExtremeConfig.
🐛 Fixed
- Multi-period clustering with ExtremeConfig - Fixed
ValueError: cannot reshape arraywhen clustering multi-period or multi-scenario systems withExtremeConfig. The fix uses pandas.unstack()instead of manual reshape for robustness. - Consistent cluster count validation - Added validation to detect inconsistent cluster counts across periods/scenarios, providing clear error messages.
💥 Breaking Changes
- ExtremeConfig method restriction for multi-period systems - When using
ExtremeConfigwith multi-period or multi-scenario systems, onlymethod='replace'is now allowed. Usingmethod='new_cluster'ormethod='append'will raise aValueError. This works around a tsam bug where these methods can produce inconsistent cluster counts across slices.
📦 Dependencies
- Excluded tsam 3.1.0 from compatible versions due to clustering bug.
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0...v6.0.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 4 months ago
flixOpt - v6.0.0 (yanked)
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.stats.plot.balance('Heat')
comp.stats.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.stats.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
before_solve Callback
New callback parameter for optimize() and rolling_horizon() allows adding custom constraints before solving:
def add_constraints(fs):
model = fs.model
boiler = model.variables['Boiler(Q_th)|flow_rate']
model.add_constraints(boiler >= 10, name='min_boiler')
flow_system.optimize(solver, before_solve=add_constraints)
# Works with rolling_horizon too
flow_system.optimize.rolling_horizon(
solver,
horizon=168,
before_solve=add_constraints
)
cluster_mode for StatusParameters
New parameter to control status behavior at cluster boundaries:
fx.StatusParameters(
...,
cluster_mode='relaxed', # Default: no constraint at boundaries, prevents phantom startups
# cluster_mode='cyclic', # Each cluster's final status equals its initial status
)
Comparison Class Enhancements
Comparison.inputs: Compare inputs across FlowSystems for easy side-by-side input parameter comparisondata_onlyparameter: Get data without generating plots in Comparison methodsthresholdparameter: Filter small values when comparing
Plotting Enhancements
thresholdparameter: Added to all plotting methods to filter values below a threshold (default:1e-5)round_decimalsparameter: Control decimal precision inbalance(),carrier_balance(), andstorage()plotsflow_colorsproperty: Map flows to their component's colors for consistent visualization
FlowSystem.from_old_dataset()
New method for loading datasets saved with older flixopt versions:
fs = fx.FlowSystem.from_old_dataset(old_dataset)
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'stats.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)stats.plot.carrier_balance()now combines inputs and outputs to show net flow per component, and aggregates per component by default
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Accessor renamed:
flow_system.statistics→ Useflow_system.stats(shorter, more convenient)
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- feat: clustering of time series data by @FBumann in https://github.com/flixOpt/flixopt/pull/555
- Feature/minor improvements by @FBumann in https://github.com/flixOpt/flixopt/pull/556
- Feature/faster ci notebooks by @FBumann in https://github.com/flixOpt/flixopt/pull/561
- Remove fxplot accessor in favor of external ploty accessor by @FBumann in https://github.com/flixOpt/flixopt/pull/560
- perf: Keep data in minimal form (no pre-broadcasting) by @FBumann in https://github.com/flixOpt/flixopt/pull/575
- Feature/tsam v3+rework (#571) by @FBumann in https://github.com/flixOpt/flixopt/pull/584
- Add color parameter to components and bulk color assignment via topology accessor by @FBumann in https://github.com/flixOpt/flixopt/pull/585
- Use area chart instead of bar chart for better performance by @FBumann in https://github.com/flixOpt/flixopt/pull/577
- Feature/speed up plotting by @FBumann in https://github.com/flixOpt/flixopt/pull/586
- Add FlowContainer for inputs/outputs by @FBumann in https://github.com/flixOpt/flixopt/pull/587
- Fix clustering expansion bugs and add ExtremeConfig validation by @FBumann in https://github.com/flixOpt/flixopt/pull/590
- Fix/expand flow system rework by @FBumann in https://github.com/flixOpt/flixopt/pull/591
- Refactor clustering: clean keys, unified dims, and encapsulated expansion by @FBumann in https://github.com/flixOpt/flixopt/pull/592
- New _ReducedFlowSystemBuilder class (lines 82-381): by @FBumann in https://github.com/flixOpt/flixopt/pull/593
Full Changelog: https://github.com/flixOpt/flixopt/compare/v5.0.4...v6.0.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 4 months ago
flixOpt - v6.0.0rc17
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc16...v6.0.0rc17
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 4 months ago
flixOpt - v6.0.0rc16
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc15...v6.0.0rc16
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc15
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- New _ReducedFlowSystemBuilder class (lines 82-381): by @FBumann in https://github.com/flixOpt/flixopt/pull/593
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc14...v6.0.0rc15
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc14
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc13...v6.0.0rc14
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc13
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- Fix clustering expansion bugs and add ExtremeConfig validation by @FBumann in https://github.com/flixOpt/flixopt/pull/590
- Fix/expand flow system rework by @FBumann in https://github.com/flixOpt/flixopt/pull/591
- Refactor clustering: clean keys, unified dims, and encapsulated expansion by @FBumann in https://github.com/flixOpt/flixopt/pull/592
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc12...v6.0.0rc13
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc12
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc11...v6.0.0rc12
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc11
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc10...v6.0.0rc11
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc10
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc9...v6.0.0rc10
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc9
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
FlowContainer for Component Flows (#587)
Component.inputs, Component.outputs, and Component.flows now use FlowContainer (dict-like) with dual access by index or label: inputs[0] or inputs['Q_th'].
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)Component.inputs/outputsandBus.inputs/outputsare nowFlowContainer(dict-like). Use.values()to iterate flows.
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- Add FlowContainer for inputs/outputs by @FBumann in https://github.com/flixOpt/flixopt/pull/587
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc8...v6.0.0rc9
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc8
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'statistics.plot.balance(),carrier_balance(), andstorage()now usexarray_plotly.fast_bar()internally (styled stacked areas for better performance)
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- Feature/speed up plotting by @FBumann in https://github.com/flixOpt/flixopt/pull/586
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc7...v6.0.0rc8
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc7
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
Component Color Parameter (#585)
All component classes now accept a color parameter for visualization customization:
# Set color at instantiation
boiler = fx.Boiler('Boiler', ..., color='#D35400')
storage = fx.Storage('Battery', ..., color='green')
# Bulk assignment via topology accessor
flow_system.topology.set_component_colors({'Boiler': 'red', 'CHP': 'blue'})
flow_system.topology.set_component_colors({'Oranges': ['Solar1', 'Solar2']}) # Colorscale
flow_system.topology.set_component_colors('turbo', overwrite=False) # Only unset colors
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- Add color parameter to components and bulk color assignment via topology accessor by @FBumann in https://github.com/flixOpt/flixopt/pull/585
- Use area chart instead of bar chart for better performance by @FBumann in https://github.com/flixOpt/flixopt/pull/577
Full Changelog: https://github.com/flixOpt/flixopt/compare/v6.0.0rc6...v6.0.0rc7
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v6.0.0rc6
Summary: Major release featuring tsam v3 migration, complete rewrite of the clustering/aggregation system, 2-3x faster I/O for large systems, new plotly plotting accessor, FlowSystem comparison tools, and removal of deprecated v5.0 classes.
!!! warning "Breaking Changes"
This release removes ClusteredOptimization and ClusteringParameters which were deprecated in v5.0.0. Use flow_system.transform.cluster() instead. See Migration below.
The clustering API now uses tsam v3's configuration objects (`ClusterConfig`, `ExtremeConfig`) instead of individual parameters. See [tsam v3 Migration](#tsam-v3-migration) below.
Key Features
- tsam v3 Migration (#584) - Updated to tsam 3.0+ with new configuration-based API
- Clustering/Aggregation Rework (#549, #552, #584) - Complete rewrite with tsam integration, inter-cluster storage linking, segmentation support, and 4 storage modes
- I/O Performance (#584) - 2-3x faster NetCDF I/O for large systems via variable stacking
- plotly Plotting Accessor (#548) - Universal xarray plotting with automatic faceting
- Comparison Module (#550) - Compare multiple FlowSystems side-by-side
- Improved Notebooks (#542, #551) - Better tutorial data and faster CI execution
✨ Added
Time-Series Clustering (#549, #552, #584)
Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.
from tsam import ClusterConfig, ExtremeConfig
# Stage 1: Cluster and optimize (fast sizing)
fs_clustered = flow_system.transform.cluster(
n_clusters=12, # 12 typical days from a year
cluster_duration='1D', # Each cluster represents one day
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['HeatDemand(Q)|fixed_relative_profile']),
)
fs_clustered.optimize(solver)
# Stage 2: Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()
Storage Modes for Clustering: Control how storage behaves across clustered periods via Storage(cluster_mode=...):
| Mode | Description | Use Case |
|---|---|---|
'intercluster_cyclic' |
Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization |
'intercluster' |
Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint |
'cyclic' |
Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns |
'independent' |
Each cluster fully independent, free start/end | Fastest solve, no long-term storage value |
Clustering Parameters:
| Parameter | Description |
|---|---|
n_clusters |
Number of representative periods to create |
cluster_duration |
Duration of each cluster (e.g., '1D', '24h', or hours as float) |
weights |
Dict mapping variable names to importance weights for clustering |
cluster |
ClusterConfig object for clustering algorithm settings (method, representation, etc.) |
extremes |
ExtremeConfig object for peak/valley preservation settings |
predef_cluster_assignments |
Predefined cluster assignment for reproducibility |
**tsam_kwargs |
Additional arguments passed to tsam |
See tsam documentation for ClusterConfig and ExtremeConfig options.
Key Features:
- Inter-cluster storage linking: For
'intercluster'and'intercluster_cyclic'modes, aSOC_boundaryvariable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling - Self-discharge decay: Storage losses are correctly applied during solution expansion using the formula:
actual_SOC(t) = SOC_boundary × (1 - loss)^t + ΔE(t) - Multi-dimensional support: Works with periods, scenarios, and clusters dimensions simultaneously
- Solution expansion:
transform.expand()maps clustered results back to original timesteps with proper storage state reconstruction - Clustering IO: Save and load clustered FlowSystems with full state preservation via
to_netcdf()/from_netcdf()
Example: Seasonal Storage with Clustering:
# Configure storage for seasonal behavior
storage = fx.Storage(
'SeasonalPit',
capacity_in_flow_hours=5000,
cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering
relative_loss_per_hour=0.0001, # Small self-discharge
...
)
# Cluster, optimize, and expand
fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D')
fs_clustered.optimize(solver)
fs_expanded = fs_clustered.transform.expand()
# Full-resolution charge state now available
charge_state = fs_expanded.solution['SeasonalPit|charge_state']
!!! tip "Choosing the Right Storage Mode"
Use 'intercluster_cyclic' (default) for seasonal storage like pit storage or underground thermal storage.
Use 'cyclic' for short-term storage like batteries or hot water tanks where only daily patterns matter.
Use 'independent' for quick estimates when storage behavior isn't critical.
Time-Series Segmentation (#584)
New transform.segment() method for piecewise-constant time-series approximation. Useful for reducing problem size while preserving temporal structure:
# Segment time series into 24 segments per day
fs_segmented = flow_system.transform.segment(
segment_duration='1D',
n_segments=24,
)
fs_segmented.optimize(solver)
fs_expanded = fs_segmented.transform.expand()
I/O Performance Improvements (#584)
- Variable stacking: 2-3x faster NetCDF I/O for large systems by grouping variables with same dimensions
- Fast DataArray construction: Bypasses slow xarray internals (~15x faster per variable)
- Version tracking: Datasets now include
flixopt_versionattribute for compatibility checking
# Version is automatically stored
ds = flow_system.to_dataset()
print(ds.attrs['flixopt_version']) # e.g., '6.0.0'
Plotly Accessor (#548)
New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results.
import flixopt as fx # Registers accessors automatically
# Plot any xarray Dataset with automatic faceting
dataset.plotly.bar(x='component')
dataset.plotly.area(x='time')
dataset.plotly.imshow(x='time', y='component')
dataset.plotly.line(x='time', facet_col='scenario')
# DataArray support
data_array.plotly.line()
# Statistics transformations
dataset.fxstats.to_duration_curve()
Available Plot Methods:
| Method | Description |
|---|---|
.plotly.bar() |
Bar charts (use barmode='group' or 'relative' for stacked) |
.plotly.line() |
Line charts with faceting |
.plotly.area() |
Stacked area charts |
.plotly.imshow() |
Heatmap visualizations |
.plotly.scatter() |
Scatter plots |
.plotly.pie() |
Pie charts with faceting |
.fxstats.to_duration_curve() |
Transform to duration curve format |
Key Features:
- Auto-faceting: Automatically assigns extra dimensions (period, scenario, cluster) to
facet_col,facet_row, oranimation_frame - Smart x-axis: Intelligently selects x dimension based on priority (time > duration > period > scenario)
- Universal: Works on any xarray Dataset/DataArray, not limited to flixopt
- Configurable: Customize via
CONFIG.Plotting(colorscales, facet columns, line shapes)
FlowSystem Comparison (#550)
New Comparison class for comparing multiple FlowSystems side-by-side:
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])
# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])
# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.plotly.line()
# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset
# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
- Concatenates solutions and statistics from multiple FlowSystems with a
'case'dimension - Mirrors all
StatisticsAccessorproperties (flow_rates,flow_hours,sizes,charge_states,temporal_effects,periodic_effects,total_effects) - Mirrors all
StatisticsPlotAccessormethods (balance,carrier_balance,flows,sizes,duration_curve,effects,charge_states,heatmap,storage) - Existing plotting infrastructure automatically handles faceting by
'case'
💥 Breaking Changes
tsam v3 Migration
The clustering API now uses tsam v3's configuration objects instead of individual parameters:
# Old API (v5.x with tsam 2.x)
fs.transform.cluster(
n_clusters=8,
cluster_method='hierarchical',
time_series_for_high_peaks=['demand'],
)
# New API (v6.x with tsam 3.x)
from tsam import ClusterConfig, ExtremeConfig
fs.transform.cluster(
n_clusters=8,
cluster=ClusterConfig(method='hierarchical'),
extremes=ExtremeConfig(method='new_cluster', max_value=['demand']),
)
Parameter mapping:
| Old Parameter | New Parameter |
|---|---|
cluster_method |
cluster=ClusterConfig(method=...) |
representation_method |
cluster=ClusterConfig(representation=...) |
time_series_for_high_peaks |
extremes=ExtremeConfig(max_value=[...]) |
time_series_for_low_peaks |
extremes=ExtremeConfig(min_value=[...]) |
extreme_period_method |
extremes=ExtremeConfig(method=...) |
predef_cluster_order |
predef_cluster_assignments |
Other Breaking Changes
FlowSystem.scenario_weightsare now always normalized to sum to 1 when set (including after.sel()subsetting)
♻️ Changed
FlowSystem.weightsreturnsdict[str, xr.DataArray](unit weights instead of1.0float fallback)FlowSystemDimensionstype now includes'cluster'
🗑️ Deprecated
The following items are deprecated and will be removed in v7.0.0:
Classes (use FlowSystem methods instead):
Optimizationclass → Useflow_system.optimize(solver)SegmentedOptimizationclass → Useflow_system.optimize.rolling_horizon()Resultsclass → Useflow_system.solutionandflow_system.statisticsSegmentedResultsclass → Use segment FlowSystems directly
FlowSystem methods (use transform or topology accessor instead):
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot()flow_system.start_network_app()→ Useflow_system.topology.start_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_app()flow_system.network_infos()→ Useflow_system.topology.infos()
Parameters:
normalize_weightsparameter increate_model(),build_model(),optimize()
Topology method name simplifications (old names still work with deprecation warnings, removal in v7.0.0):
| Old (v5.x) | New (v6.0.0) |
|---|---|
topology.plot_network() |
topology.plot() |
topology.start_network_app() |
topology.start_app() |
topology.stop_network_app() |
topology.stop_app() |
topology.network_infos() |
topology.infos() |
Note: topology.plot() now renders a Sankey diagram. The old PyVis visualization is available via topology.plot_legacy().
🔥 Removed
Clustering classes removed (deprecated in v5.0.0):
ClusteredOptimizationclass - Useflow_system.transform.cluster()thenoptimize()ClusteringParametersclass - Parameters are now passed directly totransform.cluster()flixopt/clustering.pymodule - Restructured toflixopt/clustering/package with new classes
Migration from ClusteredOptimization
=== "v5.x (Old - No longer works)"
```python
from flixopt import ClusteredOptimization, ClusteringParameters
params = ClusteringParameters(hours_per_period=24, nr_of_periods=8)
calc = ClusteredOptimization('model', flow_system, params)
calc.do_modeling_and_solve(solver)
results = calc.results
```
=== "v6.0.0 (New)"
```python
# Cluster using transform accessor
fs_clustered = flow_system.transform.cluster(
n_clusters=8, # was: nr_of_periods
cluster_duration='1D', # was: hours_per_period=24
)
fs_clustered.optimize(solver)
# Results on the clustered FlowSystem
costs = fs_clustered.solution['costs'].item()
# Expand back to full resolution if needed
fs_expanded = fs_clustered.transform.expand()
```
🐛 Fixed
temporal_weightandsum_temporal()now use consistent implementationFlowSystem.from_old_results()now setsprevious_flow_rate=0for flows of components withstatus_parameters, fixing startup cost calculation mismatch when re-optimizing migrated v4 results
📝 Docs
New Documentation Pages:
- Time-Series Clustering Guide - Comprehensive guide to clustering workflows
- Cluster architecture design documentation (
docs/design/cluster_architecture.md)
New Jupyter Notebooks (#542):
- 08c-clustering.ipynb - Introduction to time-series clustering
- 08c2-clustering-storage-modes.ipynb - Comparison of all 4 storage cluster modes
- 08d-clustering-multiperiod.ipynb - Clustering with periods and scenarios
- 08e-clustering-internals.ipynb - Understanding clustering internals
Improved Tutorials:
- Added
tutorial_data.pyhelper module for cleaner notebook examples - Updated all existing notebooks to use new clustering and plotting APIs
👷 Development
CI Improvements (#551):
- Speedup notebook execution in documentation builds
New Test Suites for Clustering:
TestStorageClusterModes: Tests for all 4 storagecluster_modeoptionsTestInterclusterStorageLinking: Tests forSOC_boundaryvariable and expansion logicTestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensionsTestPeakSelection: Tests fortime_series_for_high_peaksandtime_series_for_low_peaksparameters
New Test Suites for Other Features:
test_clustering_io.py- Tests for clustering serialization roundtriptest_sel_isel_single_selection.py- Tests for transform selection methods
What's Changed
- feat: clustering of time series data by @FBumann in https://github.com/flixOpt/flixopt/pull/555
- Feature/minor improvements by @FBumann in https://github.com/flixOpt/flixopt/pull/556
- Feature/faster ci notebooks by @FBumann in https://github.com/flixOpt/flixopt/pull/561
- Remove fxplot accessor in favor of external ploty accessor by @FBumann in https://github.com/flixOpt/flixopt/pull/560
- perf: Keep data in minimal form (no pre-broadcasting) by @FBumann in https://github.com/flixOpt/flixopt/pull/575
- Feature/tsam v3+rework (#571) by @FBumann in https://github.com/flixOpt/flixopt/pull/584
Full Changelog: https://github.com/flixOpt/flixopt/compare/v5.0.4...v6.0.0rc6
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v5.0.4
Summary: Dependency updates.
🐛 Fixed
- Fixed netcdf dependency
📦 Dependencies
- Updated
mkdocs-materialto v9.7.1 - Updated
mkdocstrings-pythonto v1.19.0 - Updated
ruffto v0.14.10 - Updated
pymdown-extensionsto v10.19.1 - Updated
werkzeugto v3.1.4
👷 Development
- Updated
actions/checkoutaction to v6
What's Changed
- chore(deps): update actions/checkout action to v6 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/545
- chore(deps): update dependency werkzeug to v3.1.4 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/544
- chore(deps): update dependency pymdown-extensions to v10.19.1 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/538
- chore(deps): update dependency ruff to v0.14.10 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/539
- chore(deps): update dependency mkdocstrings-python to v1.19.0 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/512
- chore(deps): update dependency mkdocs-material to v9.7.1 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/511
Full Changelog: https://github.com/flixOpt/flixopt/compare/v5.0.3...v5.0.4
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 5 months ago
flixOpt - v5.0.3
Summary: Cleaner notebook outputs and improved CONFIG.notebook() preset.
♻️ Changed
CONFIG.notebook()now suppresses linopy progress bars viaprogress=Falsein solve calls- Downgraded "FlowSystem not connected" message from WARNING to INFO (auto-connects anyway)
🐛 Fixed
- Fixed notebooks triggering unnecessary warnings (removed
relative_minimumwithoutstatus_parameters)
📝 Docs
- Consolidated verbose print statements into concise single-line summaries across all tutorial notebooks
- Added output suppression (
;) tooptimize()calls for cleaner cell output - Fixed notebook 07 parameters so CHP investment produces interesting results (was 0 kW, now 100 kW)
What's Changed
- feat: better notebooks, removed obsolete warnings and remove progres bars when not logging solve to console by @FBumann in https://github.com/flixOpt/flixopt/pull/536
Full Changelog: https://github.com/flixOpt/flixopt/compare/v5.0.2...v5.0.3
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 6 months ago
flixOpt - v5.0.2
♻️ Changed
statistics.plot.effects()now defaults toby=Nonefor aggregated totals; useby='component'for the previous behavior
What's Changed
- Feature/plotting effects as total by @FBumann in https://github.com/flixOpt/flixopt/pull/535
Full Changelog: https://github.com/flixOpt/flixopt/compare/v5.0.1...v5.0.2
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 6 months ago
flixOpt - v5.0.1
👷 Development
- Fixed docs deployment in CI workflow
Full Changelog: https://github.com/flixOpt/flixopt/compare/v5.0.0...v5.0.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 6 months ago
flixOpt - v5.0.0
Summary: This is a major release that introduces the new FlowSystem-centric API, dramatically simplifying workflows by integrating optimization, results access, and visualization directly into the FlowSystem object. This release also completes the terminology standardization (OnOff → Status) and deprecates the old Optimization/Results workflow (to be removed in v6.0.0).
!!! tip "Migration Guide"
See the [Migration Guide v5](https://flixopt.github.io/flixopt/latest/user-guide/migration-guide-v5/) for step-by-step upgrade instructions.
✨ Added
FlowSystem-Centric Architecture: The FlowSystem is now the central hub for all operations:
import flixopt as fx
# Create and configure your system
flow_system = fx.FlowSystem(timesteps)
flow_system.add_elements(boiler, heat_bus, costs)
# Optimize directly on FlowSystem
flow_system.optimize(fx.solvers.HighsSolver())
# Access results via solution Dataset
total_costs = flow_system.solution['costs'].item()
flow_rate = flow_system.solution['Boiler(Q_th)|flow_rate'].values
# Plot with new accessor API
flow_system.statistics.plot.balance('HeatBus')
flow_system.statistics.plot.sankey.flows()
New Accessor-Based API: Four accessor patterns provide organized, discoverable interfaces:
| Accessor | Purpose | Example |
|---|---|---|
flow_system.statistics |
Data access (flow rates, sizes, effects) | flow_system.statistics.flow_rates |
flow_system.statistics.plot |
Visualization methods | flow_system.statistics.plot.balance('Bus') |
flow_system.transform |
FlowSystem transformations | flow_system.transform.cluster(params) |
flow_system.topology |
Network structure & visualization | flow_system.topology.plot_network() |
Statistics Accessor: Access aggregated results data with clean, consistent naming:
stats = flow_system.statistics
# Flow data (clean labels, no |flow_rate suffix needed)
stats.flow_rates['Boiler(Q_th)']
stats.flow_hours['Boiler(Q_th)']
stats.sizes['Boiler(Q_th)']
stats.charge_states['Battery']
# Effect breakdown by contributor
stats.temporal_effects['costs'] # Per timestep, per contributor
stats.periodic_effects['costs'] # Investment costs per contributor
stats.total_effects['costs'] # Total per contributor
Comprehensive Plotting API: All plots return PlotResult objects with chainable methods:
# Balance plots for buses and components
flow_system.statistics.plot.balance('ElectricityBus')
flow_system.statistics.plot.balance('Boiler', mode='area')
# Storage visualization with charge state
flow_system.statistics.plot.storage('Battery')
# Heatmaps with automatic time reshaping
flow_system.statistics.plot.heatmap('Boiler(Q_th)|flow_rate', reshape=('D', 'h'))
# Flow-based Sankey diagrams
flow_system.statistics.plot.sankey.flows()
flow_system.statistics.plot.sankey.flows(select={'bus': 'ElectricityBus'})
# Effect contribution Sankey
flow_system.statistics.plot.sankey.effects('costs')
# Method chaining for customization and export
flow_system.statistics.plot.balance('Bus') \
.update(title='Custom Title', height=600) \
.to_html('plot.html') \
.to_csv('data.csv') \
.show()
Carrier Management: New Carrier class for consistent styling across visualizations:
# Define custom carriers
electricity = fx.Carrier('electricity', '#FFD700', 'kW', 'Electrical power')
district_heat = fx.Carrier('district_heat', '#FF6B6B', 'kW_th')
# Register with FlowSystem
flow_system.add_carrier(electricity)
# Use with buses (reference by name)
elec_bus = fx.Bus('MainGrid', carrier='electricity')
# Or use predefined carriers from CONFIG
fx.CONFIG.Carriers.electricity
fx.CONFIG.Carriers.heat
Transform Accessor: Transformations that create new FlowSystem instances:
# Time selection and resampling
fs_subset = flow_system.transform.sel(time=slice('2023-01-01', '2023-06-30'))
fs_resampled = flow_system.transform.resample(time='4h', method='mean')
# Clustered optimization
params = fx.ClusteringParameters(hours_per_period=24, nr_of_periods=8)
clustered_fs = flow_system.transform.cluster(params)
clustered_fs.optimize(solver)
Rolling Horizon Optimization: Decompose large operational problems into sequential segments:
# Solve with rolling horizon
segments = flow_system.optimize.rolling_horizon(
solver,
horizon=192, # Timesteps per segment
overlap=48, # Lookahead for storage optimization
)
# Combined solution available on original FlowSystem
total_cost = flow_system.solution['costs'].item()
# Individual segments also available
for seg in segments:
print(seg.solution['costs'].item())
Solution Persistence: FlowSystem now stores and persists solutions:
# Optimize and save with solution
flow_system.optimize(solver)
flow_system.to_netcdf('results/my_model.nc4')
# Load FlowSystem with solution intact
loaded_fs = fx.FlowSystem.from_netcdf('results/my_model.nc4')
print(loaded_fs.solution['costs'].item()) # Solution is available!
Migration Helper for Old Results (deprecated, temporary):
# Migrate old result files to new FlowSystem format
fs = fx.FlowSystem.from_old_results('results_folder', 'my_model')
# Or convert Results object directly
fs = results.convert_to_flow_system()
FlowSystem Locking: FlowSystem automatically locks after optimization to prevent accidental modifications:
flow_system.optimize(solver)
# This would raise an error:
# flow_system.add_elements(new_component) # Locked!
# Call reset() to unlock for modifications
flow_system.reset()
flow_system.add_elements(new_component) # Now works
NetCDF Improvements:
- Default compression level 5 for smaller files
overwrite=Falseparameter to prevent accidental overwrites- Solution data included in FlowSystem NetCDF files
- Automatic name assignment from filename
PlotResult Class: All plotting methods return a PlotResult object containing both:
data: An xarray Dataset with the prepared datafigure: A Plotly Figure object
Component color parameter: Components now accept a color parameter for consistent visualization styling.
💥 Breaking Changes
Renamed OnOffParameters → StatusParameters: Complete terminology update to align with industry standards (PyPSA, unit commitment). Old NetCDF files with OnOffParameters are automatically converted on load.
| Old Term | New Term |
|---|---|
OnOffParameters |
StatusParameters |
on_off_parameters |
status_parameters |
on variable |
status |
switch_on |
startup |
switch_off |
shutdown |
switch_on_nr |
startup_count |
on_hours_total |
active_hours |
consecutive_on_hours |
uptime |
consecutive_off_hours |
downtime |
effects_per_switch_on |
effects_per_startup |
effects_per_running_hour |
effects_per_active_hour |
consecutive_on_hours_min |
min_uptime |
consecutive_on_hours_max |
max_uptime |
consecutive_off_hours_min |
min_downtime |
consecutive_off_hours_max |
max_downtime |
switch_on_total_max |
startup_limit |
force_switch_on |
force_startup_tracking |
on_hours_min |
active_hours_min |
on_hours_max |
active_hours_max |
Bus imbalance terminology and default changed:
excess_penalty_per_flow_hour→imbalance_penalty_per_flow_hour- Default changed from
1e5toNone(strict balance) with_excess→allows_imbalanceexcess_input→virtual_supplyexcess_output→virtual_demand
Storage charge_state changes:
charge_stateno longer has an extra timestep- Final charge state is now a separate variable:
charge_state|final
Effect.description now defaults to '' (empty string) instead of None.
Stricter I/O: NetCDF loading is stricter to prevent silent errors. Missing or corrupted data now raises explicit errors.
Validation: Component with status_parameters now validates that all flows have sizes (required for big-M constraints).
♻️ Changed
- Renamed
BusModel.excess_input→virtual_supplyandBusModel.excess_output→virtual_demandfor clearer semantics - Renamed
Bus.excess_penalty_per_flow_hour→imbalance_penalty_per_flow_hour - Renamed
Bus.with_excess→allows_imbalance
🗑️ Deprecated
All deprecated items will be removed in v6.0.0.
Old Optimization Workflow - Use FlowSystem methods instead:
# Old (deprecated, still works with warning)
optimization = fx.Optimization('model', flow_system)
optimization.do_modeling()
optimization.solve(solver)
results = optimization.results
costs = results.model['costs'].solution.item()
# New (recommended)
flow_system.optimize(solver)
costs = flow_system.solution['costs'].item()
Classes deprecated:
Optimization→ Useflow_system.optimize(solver)ClusteredOptimization→ Useflow_system.transform.cluster()thenoptimize()SegmentedOptimization→ Useflow_system.optimize.rolling_horizon()Results→ Useflow_system.solutionandflow_system.statisticsSegmentedResults→ Use segment FlowSystems directly
FlowSystem methods deprecated:
flow_system.sel()→ Useflow_system.transform.sel()flow_system.isel()→ Useflow_system.transform.isel()flow_system.resample()→ Useflow_system.transform.resample()flow_system.plot_network()→ Useflow_system.topology.plot_network()flow_system.start_network_app()→ Useflow_system.topology.start_network_app()flow_system.stop_network_app()→ Useflow_system.topology.stop_network_app()flow_system.network_infos()→ Useflow_system.topology.network_infos()
Results methods deprecated:
results.flow_rates()→ Useflow_system.statistics.flow_ratesresults.flow_hours()→ Useflow_system.statistics.flow_hours
Migration helpers (temporary, also deprecated):
FlowSystem.from_old_results()→ For migrating old result filesResults.convert_to_flow_system()→ For converting Results objects
Plotting parameters deprecated:
indexerparameter → Useselectinsteadheatmap_timeframesparameter → Usereshape_time=(timeframes, timesteps_per_frame)insteadheatmap_timesteps_per_frameparameter → Usereshape_time=(timeframes, timesteps_per_frame)insteadcolor_mapparameter → Usecolorsinstead
🔥 Removed
Python version changes:
- Dropped Python 3.10 support
- Added Python 3.14 support
Classes removed (already renamed/deprecated in v4.x):
OnOffParameters→ UseStatusParametersCalculation→ UseOptimization(deprecated) orflow_system.optimize()(recommended)FullCalculation→ UseOptimization(deprecated) orflow_system.optimize()(recommended)AggregatedCalculation→ UseClusteredOptimization(deprecated) orflow_system.transform.cluster()(recommended)SegmentedCalculation→ UseSegmentedOptimization(deprecated) orflow_system.optimize.rolling_horizon()(recommended)Aggregation→ UseClusteringAggregationParameters→ UseClusteringParametersAggregationModel→ UseClusteringModelCalculationResults→ UseResults(deprecated) orflow_system.solution(recommended)SegmentedCalculationResults→ UseSegmentedResults(deprecated)
Modules removed:
calculation.pymodule → Useoptimization.py(deprecated) or FlowSystem methods (recommended)
Functions removed:
change_logging_level()→ UseCONFIG.Logging.enable_console()
Properties removed:
FlowSystem.all_elements→ Use dict-like interface (flow_system['label'],.keys(),.values(),.items())FlowSystem.weights→ Usescenario_weights
📝 Docs
Comprehensive Tutorial Notebooks - 12 new Jupyter notebooks covering all major use cases:
- 01-Quickstart - Minimal working example
- 02-Heat System - District heating with storage
- 03-Investment Optimization - Optimal equipment sizing
- 04-Operational Constraints - Startup costs, uptime/downtime
- 05-Multi-Carrier System - CHP producing electricity and heat
- 06a-Time-Varying Parameters - Temperature-dependent COP
- 06b-Piecewise Conversion - Load-dependent efficiency
- 06c-Piecewise Effects - Economies of scale
- 07-Scenarios and Periods - Multi-year planning
- 08-Large-Scale Optimization - Resampling and two-stage
- 09-Plotting and Data Access - Visualization guide
- 10-Transmission - Connecting sites with pipelines/cables
New Documentation Pages:
- Migration Guide v5 - Step-by-step upgrade instructions
- Results & Plotting Guide - Comprehensive plotting documentation
- Building Models Guide - Component selection and modeling patterns
- FAQ - Common questions and answers
- Troubleshooting - Problem diagnosis and solutions
👷 Development
New Test Suites:
test_flow_system_locking.py- FlowSystem locking behaviortest_solution_and_plotting.py- Statistics accessor and plottingtest_solution_persistence.py- Solution save/loadtest_io_conversion.py- Old file format conversiontest_topology_accessor.py- Network visualization
CI Improvements:
- Separate docs build and deploy workflow
- Improved test organization with deprecated tests in separate folder
Migration Checklist
The old Optimization/Results workflow still works with deprecation warnings. Migrate at your own pace before v6.0.0.
| Task | Action |
|---|---|
Update OnOffParameters |
Rename to StatusParameters with new parameter names (breaking) |
Update on_off_parameters |
Rename to status_parameters (breaking) |
| Update Bus excess parameters | Use imbalance_penalty_per_flow_hour (breaking) |
Replace Optimization class |
Use flow_system.optimize(solver) (deprecated) |
Replace SegmentedOptimization |
Use flow_system.optimize.rolling_horizon(solver, ...) (deprecated) |
Replace Results access |
Use flow_system.solution['var_name'] (deprecated) |
| Update transform methods | Use flow_system.transform.sel/isel/resample() (deprecated) |
| Update I/O code | Use to_netcdf() / from_netcdf() on FlowSystem |
| Migrate old result files | Use FlowSystem.from_old_results(folder, name) (temporary helper) |
What's Changed
- optimize.rolling_horizon() by @FBumann in https://github.com/flixOpt/flixopt/pull/528
- drop python 3.10 support and add 3.14 by @FBumann in https://github.com/flixOpt/flixopt/pull/529
- Feature/flow system first by @FBumann in https://github.com/flixOpt/flixopt/pull/521
- Move examples into test folder and rely on notebooks by @FBumann in https://github.com/flixOpt/flixopt/pull/534
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.3.4...v5.0.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 6 months ago
flixOpt - v4.3.4
Summary: Fix zenodo again
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
What's Changed
- Register github-actions bot by @FBumann in https://github.com/flixOpt/flixopt/pull/497
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.3.3...v4.3.4
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.3.3
Summary: Fix zenodo again
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.3.2...v4.3.3
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.3.2
Summary: Fix zenodo
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.3.1...v4.3.2
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.3.1
Summary: Add zenodo for better citations and archiving.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
📝 Docs
- Added Zenodo DOI badge to README.md (placeholder, to be updated after first Zenodo release)
👷 Development
- Added Zenodo integration for automatic archival and citation
- Created
.zenodo.jsonfile for Zenodo metadata configuration - Repository now ready for DOI assignment upon next release
- Created
What's Changed
- Added Zenodo to repository for citations by @FBumann in https://github.com/flixOpt/flixopt/pull/433
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.3.0...v4.3.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.3.0
Summary: Penalty is now a first-class Effect - add penalty contributions anywhere (e.g., effects_per_flow_hour={'Penalty': 2.5}) and optionally define bounds as with any other effect.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
- Penalty as first-class Effect: Users can now add Penalty contributions anywhere effects are used:
fx.Flow('Q', 'Bus', effects_per_flow_hour={'Penalty': 2.5}) fx.InvestParameters(..., effects_of_investment={'Penalty': 100}) - User-definable Penalty: Optionally define custom Penalty with constraints (auto-created if not defined):
penalty = fx.Effect(fx.PENALTY_EFFECT_LABEL, unit='€', maximum_total=1e6) flow_system.add_elements(penalty)
♻️ Changed
- Penalty is now a standard Effect with temporal/periodic dimensions, and periodic weights in the objective
- Results structure: Penalty now has same structure as other effects in solution Dataset
- Use
results.solution['Penalty']for total penalty value (same as before, but now it's an effect variable) - Access components via
results.solution['Penalty(temporal)']andresults.solution['Penalty(periodic)']if needed
- Use
📝 Docs
- Updated mathematical notation for Penalty as Effect
👷 Development
- Unified interface: Penalty uses same
add_share_to_effects()as other effects (internal only)
What's Changed
- Feature/penalty as effect by @FBumann in https://github.com/flixOpt/flixopt/pull/486
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.2.0...v4.3.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.2.0
Summary: Renamed classes and parameters related to Calculation, Aggregation and Results. Fully backwards compatible
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
overwriteparameter when saving results to file. If True, overwrite existing files.
♻️ Changed
- Now creates the Results folder even if parents didnt exist
🗑️ Deprecated
Class and module renaming:
FullCalculation→OptimizationAggregatedCalculation→ClusteredOptimizationSegmentedCalculation→SegmentedOptimizationCalculationResults→ResultsSegmentedCalculationResults→SegmentedResultsAggregation→ClusteringAggregationParameters→ClusteringParametersAggregationModel→ClusteringModel- Module:
calculation.py→optimization.py - Module:
aggregation.py→clustering.py
Old names remain available with deprecation warnings (removed in v5.0.0).
🐛 Fixed
- Fixed
fix_sizes()docstring/implementation inconsistency for optionaldsparameter
👷 Development
- Fixed
active_timestepstype annotation to includeNone - Fixed xarray truth-value ambiguity in
main_resultsbuses with excess filter - Added validation for
nr_of_previous_valuesinSegmentedOptimizationto prevent silent indexing bugs
What's Changed
- chore(deps): update dependency dash to v3.3.0 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/491
- chore(deps): update dependency astral-sh/uv to v0.9.10 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/490
- Feature/rename calculation 2 by @FBumann in https://github.com/flixOpt/flixopt/pull/484
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.1.4...v4.2.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.1.4
Summary: Added file logging encoding to prevent issues
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
🐛 Fixed
- Issues with windows file system when logging to file due to non ASCII characters
What's Changed
- Fix/logging compatability by @FBumann in https://github.com/flixOpt/flixopt/pull/494
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.1.3...v4.1.4
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.1.3
Summary: Re-add mistakenly removed method for loading a config from file
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
🐛 Fixed
- Re-added
CONFIG.load_from_file()method that was accidentally removed
What's Changed
- Re-add mistakenly removed method for loading a config from file by @FBumann in https://github.com/flixOpt/flixopt/pull/492
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.1.2...v4.1.3
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.1.2
Summary:
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
- Exported SUCCESS log level (
SUCCESS_LEVEL) for use withlogger.log(SUCCESS_LEVEL, ...) - Added proper deprecation tests
♻️ Changed
- logger coloring improved
👷 Development
- Fixed Deprecation warnings in tests
What's Changed
- Improve logging coloring and replace logging.success() by @FBumann in https://github.com/flixOpt/flixopt/pull/489
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.1.1...v4.1.2
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.1.1
Summary: Finalize preparations for removal of all deprecated parameters in v5.0.0
✨ Added
- Added missing infos about removal to remaining deprecated parameters and methods
🐛 Fixed
- Missing release notes of v4.1.0
What's Changed
- Improve filterwarnings by @FBumann in https://github.com/flixOpt/flixopt/pull/485
- Fix Missing release notes of v4.1.0 in docs by @FBumann in https://github.com/flixOpt/flixopt/pull/488
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.1.0...v4.1.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.1.0
Summary: Logging migrated from loguru to standard Python logging for stability and security. Simpler API with convenient presets.
!!! info "Migration Required?"
Most users: No action needed (silent by default). Methods like CONFIG.exploring(), CONFIG.debug(), etc. continue to work exactly as before.
If you customized logging: Simple API update (see migration below).
If you used loguru directly: Breaking change (loguru only in v3.6.0-v4.0.0, ~4 days).
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
New logging presets:
CONFIG.production('app.log') # File-only, no console/plots
New logging methods:
CONFIG.Logging.enable_console(level, colored, stream)- Console output with colorsCONFIG.Logging.enable_file(level, path, max_bytes, backup_count)- File logging with rotationCONFIG.Logging.disable()- Disable all loggingCONFIG.Logging.set_colors(log_colors)- Customize colors
Enhanced formatting:
- Multi-line messages with box borders (┌─, │, └─)
- Exception tracebacks with proper indentation
- Timestamps:
2025-11-21 14:30:45.123
💥 Breaking Changes
Logging migration (edge cases only):
| Old (v3.6.0-v4.0.0) | New (v4.1.0+) |
|---|---|
CONFIG.Logging.level = 'INFO'CONFIG.Logging.console = TrueCONFIG.apply() |
CONFIG.Logging.enable_console('INFO')or CONFIG.exploring() |
CONFIG.Logging.file = 'app.log' |
CONFIG.Logging.enable_file('INFO', 'app.log') |
logger.opt(lazy=True) |
Built-in (automatic) |
Migration:
# Before (v3.6.0-v4.0.0)
CONFIG.Logging.level = 'INFO'
CONFIG.Logging.console = True
CONFIG.apply()
# After (v4.1.0+)
CONFIG.Logging.enable_console('INFO') # or CONFIG.exploring()
♻️ Changed
- Replaced loguru with Python
logging+ optionalcolorlogfor colors - Configuration immediate (no
CONFIG.apply()needed) - Log format:
[dimmed timestamp] [colored level] │ message - Logs to
stdoutby default (configurable) - SUCCESS level preserved (green, level 25)
- Performance: Expensive operations guarded with
logger.isEnabledFor()checks
🗑️ Deprecated
change_logging_level(level)→ UseCONFIG.Logging.enable_console(level). Removal in v5.0.0.
🔥 Removed
CONFIG methods/attributes:
CONFIG.apply()→ Use helper methods directlyCONFIG.Logging.level,.console,.file→ Useenable_console()/enable_file()CONFIG.Logging.verbose_tracebacks,.rich,.Colors,.date_format,.format,.console_width,.show_path,.show_logger_name→ Use standard logging- loguru features (
logger.opt(), etc.)
🐛 Fixed
TypeErrorincheck_bounds()with loguru-style formatting- Exception tracebacks not appearing in custom formatters
- Inconsistent formatting between console and file logs
🔒 Security
- Removed loguru dependency for reduced supply chain risk
📦 Dependencies
- Removed:
loguru >= 0.7.0 - Added:
colorlog >= 6.8.0, < 7(optional)
📝 Docs
- Preset comparison table in
CONFIG.Loggingdocstring - Color customization examples
- Migration guide with before/after code
Until here -->
What's Changed
- Feature/loguru cve2 by @FBumann in https://github.com/flixOpt/flixopt/pull/483
Full Changelog: https://github.com/flixOpt/flixopt/compare/v4.0.0...v4.1.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v4.0.0
Summary: This release introduces clearer parameter naming for linear converters and constraints, enhanced period handling with automatic weight computation, and new sum-over-all-periods constraints for multi-period optimization. All deprecated parameter names continue to work with warnings.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Key Features
Sum-over-all-periods constraints:
New constraint parameters enable limiting weighted totals across all periods:
Effect:minimum_over_periodsandmaximum_over_periodsFlow:flow_hours_max_over_periodsandflow_hours_min_over_periods
# Per-period: limits apply to EACH period individually
effect = fx.Effect('costs', maximum_total=1000) # ≤1000 per period
# Over-periods: limits apply to WEIGHTED SUM across ALL periods
# With periods=[2020, 2030, 2040] (weights: [10, 10, 10] from 10-year intervals)
effect = fx.Effect('costs', maximum_over_periods=25000) # 10×costs₂₀₂₀ + 10×costs₂₀₃₀ + 10×costs₂₀₄₀ ≤ 25000
Improved period weight handling:
- Period weights now computed automatically from period index (like
hours_per_timestepfor time) - Weights correctly recalculate when using
.sel()or.isel()on periods - Separate tracking of
period_weights,scenario_weights, and combinedweights
Simplified workflow:
Calculation.solve()now automatically callsdo_modeling()if needed
💥 Breaking Changes
FlowSystem weights parameter renamed:
# Old (v3.x)
fs = FlowSystem(..., weights=np.array([0.3, 0.5, 0.2]))
# New (v4.0)
fs = FlowSystem(..., scenario_weights=np.array([0.3, 0.5, 0.2]))
Period weights are now always computed from the period index.
Note: If you were previously passing period × scenario weights to weights, you now need to:
- Pass only scenario weights to
scenario_weights - Period weights will be computed automatically from your
periodsindex
🗑️ Deprecated Parameters
Linear converters (Boiler, CHP, HeatPump, etc.) - descriptive names replace abbreviations:
- Flow:
Q_fu→fuel_flow,P_el→electrical_flow,Q_th→thermal_flow,Q_ab→heat_source_flow - Efficiency:
eta→thermal_efficiency,eta_th→thermal_efficiency,eta_el→electrical_efficiency,COP→cop(lowercase)
Constraint parameters - removed redundant _total suffix:
Flow:flow_hours_total_max→flow_hours_max,flow_hours_total_min→flow_hours_minOnOffParameters:on_hours_total_max→on_hours_max,on_hours_total_min→on_hours_min,switch_on_total_max→switch_on_max
Storage:
initial_charge_state="lastValueOfSim"→initial_charge_state="equals_final"
All deprecated names continue working with warnings. They will be removed in v5.0.0.
Additional property deprecations now include removal version:
InvestParameters:fix_effects,specific_effects,divest_effects,piecewise_effectsOnOffParameters:on_hours_total_min,on_hours_total_max,switch_on_total_maxFlow:flow_hours_total_min,flow_hours_total_max
🐛 Fixed
- Fixed inconsistent boundary checks in linear converters with array-like inputs
👷 Development
- Eliminated circular dependencies with two-phase modeling pattern
- Enhanced validation for cross-element references and FlowSystem assignment
- Added helper methods for cleaner data transformation code
- Improved logging and cache invalidation
- Improved argument consistency in internal effect coordinate fitting
What's Changed
- Feature/simplify structure by @FBumann in https://github.com/flixOpt/flixopt/pull/464
- Feature/rename parameters linear converters by @FBumann in https://github.com/flixOpt/flixopt/pull/468
- Feature/sums over all periods by @FBumann in https://github.com/flixOpt/flixopt/pull/475
- Feature/deprecation removal by @FBumann in https://github.com/flixOpt/flixopt/pull/478
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.6.1...v4.0.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v3.6.1
Summary: Documentation improvements and dependency updates.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
📦 Dependencies
- Updated
astral-sh/uvto v0.9.8 - Updated
mkdocs-git-revision-date-localized-pluginto v1.5.0
📝 Docs
- Improved type specifications in
flixopt/types.pyfor better documentation generation - Fixed minor mkdocs warnings in
flixopt/io.pyandmkdocs.yml
What's Changed
- specify types directly for better docs by @FBumann in https://github.com/flixOpt/flixopt/pull/469
- Fix minor mkdocs warnings by @FBumann in https://github.com/flixOpt/flixopt/pull/470
- chore(deps): update dependency mkdocs-git-revision-date-localized-plugin to v1.5.0 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/472
- chore(deps): update dependency astral-sh/uv to v0.9.8 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/471
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.6.0...v3.6.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v3.6.0
Summary: Type system overhaul and migration to loguru for logging. If you are heavily using our logs, this might be breaking!
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
- New type system (
flixopt/types.py):- Introduced dimension-aware type aliases using suffix notation (
_TPS,_PS,_S) to clearly indicate which dimensions data can have - Added
Numeric_TPS,Numeric_PS,Numeric_Sfor numeric data with Time/Period/Scenario dimensions - Added
Bool_TPS,Bool_PS,Bool_Sfor boolean data with dimension support - Added
Effect_TPS,Effect_PS,Effect_Sfor effect dictionaries with dimension support - Added
Scalartype for scalar-only numeric values - Added
NumericOrBoolutility type for internal use - Type system supports scalars, numpy arrays, pandas Series/DataFrames, and xarray DataArrays
- Introduced dimension-aware type aliases using suffix notation (
- Lazy logging evaluation - expensive log operations only execute when log level is active
CONFIG.Logging.verbose_tracebacksoption for detailed debugging with variable values
💥 Breaking Changes
- Logging framework: Migrated to loguru
- Removed
CONFIG.Loggingparameters:rich,Colors,date_format,format,console_width,show_path,show_logger_name - For advanced formatting, use loguru's API directly after
CONFIG.apply()
- Removed
♻️ Changed
- Code structure: Removed
commons.pymodule and moved all imports directly to__init__.pyfor cleaner code organization (no public API changes) - Type handling improvements: Updated internal data handling to work seamlessly with the new type system
🐛 Fixed
- Fixed
ShareAllocationModelinconsistency where None/inf conversion happened in__init__instead of during modeling, which could cause issues with parameter validation - Fixed numerous type hint inconsistencies across the codebase
📦 Dependencies
- Updated
mkdocs-materialto v9.6.23 - Replaced
rich >= 13.0.0withloguru >= 0.7.0for logging
📝 Docs
- Enhanced documentation in
flixopt/types.pywith comprehensive examples and dimension explanation table - Clarified Effect type docstrings - Effect types are dicts, but single numeric values work through union types
- Added clarifying comments in
effects.pyexplaining parameter handling and transformation - Improved OnOffParameters attribute documentation
- Updated getting-started guide with loguru examples
- Updated
config.pydocstrings for loguru integration
👷 Development
- Added test for FlowSystem resampling
What's Changed
- chore(deps): update dependency mkdocs-material to v9.6.23 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/462
- chore(deps): update dependency astral-sh/uv to v0.9.7 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/461
- Feature/remove commons by @FBumann in https://github.com/flixOpt/flixopt/pull/466
- Feature/types direct by @FBumann in https://github.com/flixOpt/flixopt/pull/465
- Feature/logging by @FBumann in https://github.com/flixOpt/flixopt/pull/463
- Improve templating in class DataConverter by @FBumann in https://github.com/flixOpt/flixopt/pull/456
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.5.0...v3.6.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v3.5.0
Summary: Improve representations and improve resampling
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
- Added options to resample and select subsets of flowsystems without converting to and from Dataset each time. Use the new methods
FlowSystem.__dataset_resample(),FlowSystem.__dataset_sel()andFlowSystem.__dataset_isel(). All of them expect and return a dataset.
💥 Breaking Changes
♻️ Changed
- Truncate repr of FlowSystem and CalculationResults to only show the first 10 items of each category
- Greatly sped up the resampling of a FlowSystem again
What's Changed
- Feature/compact repr by @FBumann in https://github.com/flixOpt/flixopt/pull/457
- Feature/speed up resample again by @FBumann in https://github.com/flixOpt/flixopt/pull/458
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.4.1...v3.5.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 7 months ago
flixOpt - v3.4.1
Summary: Speed up resampling by 20-40 times.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
♻️ Changed
- Greatly sped up the resampling of a FlowSystem (x20 - x40) by converting to dataarray internally
What's Changed
- chore(deps): update dependency mkdocs-include-markdown-plugin to v7.2.0 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/453
- Feature/speed up resample by @FBumann in https://github.com/flixOpt/flixopt/pull/455
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.4.0...v3.4.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.4.0
Summary: Enhanced solver configuration with new CONFIG.Solving section for centralized solver parameter management.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
Solver configuration:
- New
CONFIG.Solvingconfiguration section for centralized solver parameter management:mip_gap: Default MIP gap tolerance for solver convergence (default: 0.01)time_limit_seconds: Default time limit in seconds for solver runs (default: 300)log_to_console: Whether solver should output to console (default: True)log_main_results: Whether to log main results after solving (default: True)
- Solvers (
HighsSolver,GurobiSolver) now useCONFIG.Solvingdefaults for parameters, allowing global configuration - Solver parameters can still be explicitly overridden when creating solver instances
- New
log_to_consoleparameter in all Solver classes
♻️ Changed
- Individual solver output is now hidden in SegmentedCalculation. To return to the prior behaviour, set
show_individual_solves=Trueindo_modeling_and_solve().
🐛 Fixed
- New compacted list representation for periods and scenarios also in results log and console print
📝 Docs
- Unified contributing guides in docs and on github
👷 Development
- Added type hints for submodel in all Interface classes
What's Changed
- Added type hint to all submodel attributes by @FBumann in https://github.com/flixOpt/flixopt/pull/424
- chore(deps): update dependency dash to v3.2.0 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/426
- Feature/402 feature silent framework by @FBumann in https://github.com/flixOpt/flixopt/pull/407
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.3.1...v3.4.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.3.1
Summary: Small Bugfix and improving readability
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
♻️ Changed
- Improved
summary.yamlto use a compacted list representation for periods and scenarios
🐛 Fixed
- Using
switch_on_total_maxwith periods or scenarios failed
📝 Docs
- Add more comprehensive
CONTRIBUTE.md - Improve logical structure in User Guide
What's Changed
- Feature/improve docs by @FBumann in https://github.com/flixOpt/flixopt/pull/449
- 447 bug error when using scenario dependend effects by @FBumann in https://github.com/flixOpt/flixopt/pull/450
- Feature/yaml representer by @FBumann in https://github.com/flixOpt/flixopt/pull/451
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.3.0...v3.3.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.3.0
Summary: Better access to Elements stored in the FLowSystem and better representations (repr)
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
♻️ Changed
Improved repr methods:
- Results classes (
ComponentResults,BusResults,FlowResults,EffectResults) now show concise header with key metadata followed by xarray Dataset repr - Element classes (
Component,Bus,Flow,Effect,Storage) now show one-line summaries with essential information (connections, sizes, capacities, constraints)
Container-based access:
- FlowSystem now provides dict-like access patterns for all elements
- Use
flow_system['element_label'],flow_system.keys(),flow_system.values(), andflow_system.items()for unified element access - Specialized containers (
components,buses,effects,flows) offer type-specific access with helpful error messages
🗑️ Deprecated
FlowSystem.all_elementsproperty is deprecated in favor of dict-like interface (flow_system['label'],.keys(),.values(),.items()). Will be removed in v4.0.0.
What's Changed
- Feature/containers-and-reprs-for-better-element-organization by @FBumann in https://github.com/flixOpt/flixopt/pull/443
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.2.1...v3.3.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.2.1
Summary:
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
🐛 Fixed
- Fixed resampling of FlowSystem to reset
hours_of_last_timestepandhours_of_previous_timestepsproperly
👷 Development
- Improved issue templates
What's Changed
- chore(deps): update dependency mkdocs-material to v9.6.22 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/445
- chore(deps): update dependency astral-sh/uv to v0.9.5 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/444
- Fix Bug in resample regarding the duration of the last timestep duration by @FBumann in https://github.com/flixOpt/flixopt/pull/442
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.2.0...v3.2.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.2.0
Summary: Enhanced plotting capabilities with consistent color management, custom plotting kwargs support, and centralized I/O handling.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
✨ Added
Color management:
setup_colors()method forCalculationResultsandSegmentedCalculationResultsto configure consistent colors across all plots- Group components by colorscales:
results.setup_colors({'CHP': 'reds', 'Storage': 'blues', 'Greys': ['Grid', 'Demand']}) - Automatically propagates to all segments in segmented calculations
- Colors persist across all plot calls unless explicitly overridden
- Group components by colorscales:
- Flexible color inputs: Supports colorscale names (e.g., 'turbo', 'plasma'), color lists, or label-to-color dictionaries
- Cross-backend compatibility: Seamless color handling for both Plotly and Matplotlib
Plotting customization:
- Plotting kwargs support: Pass additional arguments to plotting backends via
px_kwargs,plot_kwargs, andbackend_kwargsparameters - New
CONFIG.Plottingconfiguration section:default_show: Control default plot visibilitydefault_engine: Choose 'plotly' or 'matplotlib'default_dpi: Set resolution for saved plotsdefault_facet_cols: Configure default faceting columnsdefault_sequential_colorscale: Default for heatmaps (now 'turbo')default_qualitative_colorscale: Default for categorical plots (now 'plotly')
I/O improvements:
- Centralized JSON/YAML I/O with auto-format detection
- Enhanced NetCDF handling with consistent engine usage
- Better numeric formatting in YAML exports
♻️ Changed
- Default colorscale: Changed from 'viridis' to 'turbo' for better perceptual uniformity
- Color terminology: Standardized from "colormap" to "colorscale" throughout for Plotly consistency
- Plotting internals: Now use
xr.Datasetas primary data type (DataFrames automatically converted) - NetCDF engine: Switched back to netcdf4 engine following xarray updates and performance benchmarks
🔥 Removed
- Removed unused
plotting.pie_with_plotly()method
🐛 Fixed
- Improved error messages when using
engine='matplotlib'with multidimensional data - Better dimension validation in
results.plot_heatmap()
📝 Docs
- Enhanced examples demonstrating
setup_colors()usage - Updated terminology from "colormap" to "colorscale" in docstrings
👷 Development
- Fixed concurrency issue in CI
- Centralized color processing logic into dedicated module
- Refactored to function-based color handling for simpler API
What's Changed
- chore(deps): update dependency astral-sh/uv to v0.9.2 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/425
- Feature/plotting kwargs and streamline data conversion by @FBumann in https://github.com/flixOpt/flixopt/pull/439
- Feature/component colors by @FBumann in https://github.com/flixOpt/flixopt/pull/440
- Feature/centralized io by @FBumann in https://github.com/flixOpt/flixopt/pull/441
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.1.1...v3.2.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.1.1
Summary: Fixed a bug when acessing the effects_per_component dataset in results without periodic effects.
If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
🐛 Fixed
- Fixed ValueError in effects_per_component when all periodic effects are scalars/NaN by explicitly creating mode-specific templates (via _create_template_for_mode) with correct dimensions
👷 Development
- Converted all remaining numpy style docstrings to google style
What's Changed
- Fix/effects per component bug by @FBumann in https://github.com/flixOpt/flixopt/pull/430
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.1.0...v3.1.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.1.0
Summary: This release adds faceting and animation support for multidimensional plots and redesigns the documentation website. Plotting results across scenarios or periods is now significantly simpler (Plotly only).
If upgrading from v2.x, see the Migration Guide and v3.0.0 release notes.
✨ Added
- Faceting and animation for multidimensional plots: All plotting methods now support
facet_byandanimate_byparameters to create subplot grids and animations from multidimensional data (scenarios, periods, etc.). Plotly only. - Flexible data selection with
selectparameter: Select data using single values, lists, slices, or index arrays for precise control over what gets plotted - Heatmap fill control: New
fillparameter in heatmap methods controls how missing values are filled after reshaping ('ffill'or'bfill') - Smart line styling for mixed variables: Area plots now automatically style variables containing both positive and negative values with dashed lines, while stacking purely positive or negative variables
♻️ Changed
- Breaking: Selection behavior: Plotting methods no longer automatically select the first value for non-time dimensions. Use the
selectparameter for explicit selection of scenarios, periods, or other dimensions - Better error messages: Enhanced error messages when using Matplotlib with multidimensional data, with clearer guidance on dimension requirements and suggestions to use Plotly
- Improved examples: Enhanced
scenario_example.pywith better demonstration of new features - Robust validation: Improved dimension validation in
plot_heatmap()with clearer error messages
🗑️ Deprecated
indexerparameter: Use the newselectparameter instead. Theindexerparameter will be removed in v4.0.0heatmap_timeframesandheatmap_timesteps_per_frameparameters: Use the newreshape_time=(timeframes, timesteps_per_frame)parameter instead in heatmap plotting methodscolor_mapparameter: Use the newcolorsparameter instead in heatmap plotting methods
🐛 Fixed
- Fixed cryptic errors when working with empty buses by adding proper validation
- Added early validation for non-existent periods when using linked periods with tuples
📝 Documentation
- Redesigned documentation website with custom css
👷 Development
- Renamed internal
_apply_indexer_to_data()to_apply_selection_to_data()for consistency with new API naming
What's Changed
- Add error handling for empty buses by @FBumann in https://github.com/flixOpt/flixopt/pull/416
- Fix/409 bug error handling in investparameterslinked periods by @FBumann in https://github.com/flixOpt/flixopt/pull/415
- Feature/pretty docs with theme by @FBumann in https://github.com/flixOpt/flixopt/pull/413
- Feature/398 feature facet plots in results (#419) by @FBumann in https://github.com/flixOpt/flixopt/pull/422
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.0.3...v3.1.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.0.3
Summary: Hotfixing new plotting parameter style. Continue to use mode.
🐛 Fixed
- Reverted breaking change from v3.0.0: continue to use
modeparameter in plotting instead of newstyle - Renamed new
modeparameter in plotting methods tounit_type
📝 Docs
- Updated Migration Guide and added missing entries.
- Improved Changelog of v3.0.0
What's Changed
- Fix/update migration guide and changelog by @FBumann in https://github.com/flixOpt/flixopt/pull/411
- Fix/revert rename of mode to style in plotting by @FBumann in https://github.com/flixOpt/flixopt/pull/412
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.0.2...v3.0.3
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.0.2
Summary: This is a follow-up release to v3.0.0, improving the documentation.
Note: If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
📝 Docs
- Update the Readme
- Add a project roadmap to the docs
- Change Development status to "Production/Stable"
- Regroup parts in docs
What's Changed
- Feature/readme and vision by @FBumann in https://github.com/flixOpt/flixopt/pull/405
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.0.1...v3.0.2
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.0.1
Summary: This is a follow-up release to v3.0.0, adding a Migration Guide and bugfixing the docs.
Note: If upgrading from v2.x, see the v3.0.0 release notes and Migration Guide.
📝 Docs
- Fixed deployed docs
- Added Migration Guide for flixopt 3
👷 Development
- Added missing type hints
What's Changed
- chore(deps): update dependency ruff to v0.13.3 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/400
- chore(deps): update dependency mkdocs-material to v9.6.21 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/399
- chore(deps): update dependency astral-sh/uv to v0.8.23 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/368
- Fix docs and add migration guide by @FBumann in https://github.com/flixOpt/flixopt/pull/404
Full Changelog: https://github.com/flixOpt/flixopt/compare/v3.0.0...v3.0.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v3.0.0
Summary: This release introduces new model dimensions (periods and scenarios) for multi-period investments and stochastic modeling, along with a redesigned effect sharing system and enhanced I/O capabilities.
Note: If upgrading from v2.x, see the Migration Guide.
✨ Added
New model dimensions:
- Period dimension: Enables multi-period investment modeling with distinct decisions in each period for transformation pathway optimization
- Scenario dimension: Supports stochastic modeling with weighted scenarios for robust decision-making under uncertainty (demand, prices, weather)
- Control variable independence across scenarios via
scenario_independent_sizesandscenario_independent_flow_ratesparameters - By default, investment sizes are shared across scenarios while flow rates vary per scenario
- Control variable independence across scenarios via
Redesigned effect sharing system:
Effects now use intuitive share_from_* syntax that clearly shows contribution sources:
costs = fx.Effect('costs', '€', 'Total costs',
share_from_temporal={'CO2': 0.2}, # From temporal effects
share_from_periodic={'land': 100}) # From periodic effects
This replaces specific_share_to_other_effects_* parameters and inverts the direction for clearer relationships.
Enhanced I/O and data handling:
- NetCDF/JSON serialization for all Interface objects and FlowSystem with round-trip support
- FlowSystem manipulation:
sel(),isel(),resample(),copy(),__eq__()methods - Direct access to FlowSystem from results without manual restoring (lazily loaded)
- New
FlowResultsclass and precomputed DataArrays for sizes/flow_rates/flow_hours effects_per_componentdataset for component impact evaluation, including all indirect effects through effect shares
Other additions:
- Balanced storage - charging and discharging sizes can be forced equal via
balancedparameter - New Storage parameters:
relative_minimum_final_charge_stateandrelative_maximum_final_charge_statefor final state control - Improved filter methods in results
- Example for 2-stage investment decisions leveraging FlowSystem resampling
💥 Breaking Changes
relative_minimum_charge_stateandrelative_maximum_charge_statedon't have an extra timestep anymore.- Renamed class
SystemModeltoFlowSystemModel - Renamed class
ModeltoSubmodel - Renamed
modeparameter in plotting methods tostyle - Renamed investment binary variable
is_investedtoinvestedinInvestmentModel Calculation.do_modeling()now returns theCalculationobject instead of itslinopy.Model. Callers that previously accessed the linopy model directly should now usecalculation.do_modeling().modelinstead ofcalculation.do_modeling().
♻️ Changed
- FlowSystems cannot be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent
- Each Subcalculation in
SegmentedCalculationnow has its own distinctFlowSystemobject - Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity
- Enhanced FlowSystem interface with improved
__repr__()and__str__()methods - Improved Model Structure - Views and organisation is now divided into:
- Model: The main Model (linopy.Model) that is used to create and store the variables and constraints for the FlowSystem.
- Submodel: The base class for all submodels. Each is a subset of the Model, for simpler access and clearer code.
- Made docstrings in
config.pymore compact and easier to read - Improved format handling in configuration module
- Enhanced console output to support both
stdoutandstderrstream selection - Added
show_logger_nameparameter toCONFIG.Loggingfor displaying logger names in messages
🗑️ Deprecated
- The
agg_groupandagg_weightparameters ofTimeSeriesDataare deprecated and will be removed in a future version. Useaggregation_groupandaggregation_weightinstead. - The
active_timestepsparameter ofCalculationis deprecated and will be removed in a future version. Use the newsel(time=...)method on the FlowSystem instead. - The assignment of Bus Objects to Flow.bus is deprecated and will be removed in a future version. Use the label of the Bus instead.
- The usage of Effects objects in Dicts to assign shares to Effects is deprecated and will be removed in a future version. Use the label of the Effect instead.
- InvestParameters parameters renamed for improved clarity around investment and retirement effects:
fix_effects→effects_of_investmentspecific_effects→effects_of_investment_per_sizedivest_effects→effects_of_retirementpiecewise_effects→piecewise_effects_of_investment
- Effect parameters renamed:
minimum_investment→minimum_periodicmaximum_investment→maximum_periodicminimum_operation→minimum_temporalmaximum_operation→maximum_temporalminimum_operation_per_hour→minimum_per_hourmaximum_operation_per_hour→maximum_per_hour
- Component parameters renamed:
Source.source→Source.outputsSink.sink→Sink.inputsSourceAndSink.source→SourceAndSink.outputsSourceAndSink.sink→SourceAndSink.inputsSourceAndSink.prevent_simultaneous_sink_and_source→SourceAndSink.prevent_simultaneous_flow_rates
🔥 Removed
- Effect share parameters: The old
specific_share_to_other_effects_*parameters were replaced WITHOUT DEPRECATIONspecific_share_to_other_effects_operation→share_from_temporal(with inverted direction)specific_share_to_other_effects_invest→share_from_periodic(with inverted direction)
🐛 Fixed
- Enhanced NetCDF I/O with proper attribute preservation for DataArrays
- Improved error handling and validation in serialization processes
- Better type consistency across all framework components
- Added extra validation in
config.pyto improve error handling
📝 Docs
- Reorganized mathematical notation docs: moved to lowercase
mathematical-notation/with subdirectories (elements/,features/,modeling-patterns/) - Added comprehensive documentation pages:
dimensions.md(time/period/scenario),effects-penalty-objective.md, modeling patterns - Enhanced all element pages with implementation details, cross-references, and "See Also" sections
- Rewrote README and landing page with clearer vision, roadmap, and universal applicability emphasis
- Removed deprecated
docs/SUMMARY.md, updatedmkdocs.ymlfor new structure - Tightened docstrings in core modules with better cross-referencing
- Added recipes section to docs
🚧 Known Issues
- IO for single Interfaces/Elements to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arises from Numeric Data not being stored as xr.DataArray by the user. To avoid this, always use the
to_dataset()on Elements inside a FlowSystem that's connected and transformed.
👷 Development
- Centralized deprecation pattern: Added
_handle_deprecated_kwarg()helper method toInterfacebase class that provides reusable deprecation handling with consistent warnings, conflict detection, and optional value transformation. Applied across 5 classes (InvestParameters, Source, Sink, SourceAndSink, Effect) reducing deprecation boilerplate by 72%. - FlowSystem data management simplified - removed
time_series_collectionpattern in favor of direct timestep properties - Change modeling hierarchy to allow for more flexibility in future development. This leads to minimal changes in the access and creation of Submodels and their variables.
- Added new module
.modelingthat contains modeling primitives and utilities - Clearer separation between the main Model and "Submodels"
- Improved access to the Submodels and their variables, constraints and submodels
- Added
__repr__()for Submodels to easily inspect its content - Enhanced data handling methods
fit_to_model_coords()method for data alignmentfit_effects_to_model_coords()method for effect data processingconnect_and_transform()method replacing several operations
- Testing improvements: Eliminated warnings during test execution
- Updated deprecated code patterns in tests and examples (e.g.,
sink/source→inputs/outputs,'H'→'h'frequency) - Refactored plotting logic to handle test environments explicitly with non-interactive backends
- Added comprehensive warning filters in
__init__.pyandpyproject.tomlto suppress third-party library warnings - Improved test fixtures with proper figure cleanup to prevent memory leaks
- Enhanced backend detection and handling in
plotting.pyfor both Matplotlib and Plotly - Always run dependent tests in order
- Updated deprecated code patterns in tests and examples (e.g.,
What's Changed
- Add Multi-Period-modeling and stochastic modeling to flixopt by @FBumann in https://github.com/flixOpt/flixopt/pull/348
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.2.0...v3.0.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v2.2.0
Summary: This release is a Configuration and Logging management release.
✨ Added
- Added
CONFIG.reset()method to restore configuration to default values - Added configurable log file rotation settings:
CONFIG.Logging.max_file_sizeandCONFIG.Logging.backup_count - Added configurable log format settings:
CONFIG.Logging.date_formatandCONFIG.Logging.format - Added configurable console settings:
CONFIG.Logging.console_widthandCONFIG.Logging.show_path - Added
CONFIG.Logging.Colorsnested class for customizable log level colors using ANSI escape codes (works with both standard and Rich handlers)
♻️ Changed
- Logging and Configuration management changed
🗑️ Deprecated
change_logging_level()function is now deprecated in favor ofCONFIG.Logging.levelandCONFIG.apply(). Will be removed in version 3.0.0.
🔥 Removed
- Removed unused
config.merge_configsfunction from configuration module
👷 Development
- Greatly expanded test coverage for
config.pymodule - Added
@pytest.mark.xdist_grouptoTestConfigModuletests to prevent global config interference
What's Changed
- chore(deps): update dependency mkdocs-material to v9.6.20 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/369
- chore(deps): update dependency tsam to v2.3.9 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/379
- chore(deps): update dependency ruff to v0.13.2 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/378
- Feature/config by @FBumann in https://github.com/flixOpt/flixopt/pull/385
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.11...v2.2.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 8 months ago
flixOpt - v2.1.11
Summary: Important bugfix in Storage leading to wrong results due to incorrect discharge losses.
♻️ Changed
- Using
h5netcdfinstead ofnetCDF4for dataset I/O operations. This follows the update inxarray==2025.09.01
🐛 Fixed
- Fix
charge_stateConstraint inStorageleading to incorrect losses in discharge and therefore incorrect charge states and discharge values.
📦 Dependencies
- Updated
renovate.configto treat CalVer packages (xarray and dask) with more care
What's Changed
- Change to save netcdf files with 'h5netcdf' instead of netcdf4 by @FBumann in https://github.com/flixOpt/flixopt/pull/374
- Feature/h5netcdf update by @FBumann in https://github.com/flixOpt/flixopt/pull/375
- Fix
charge_stateConstraint inStorageleading to incorrect losses in discharge and therefore incorrect charge states and discharge values by @FBumann in https://github.com/flixOpt/flixopt/pull/347
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.10...v2.1.11
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 9 months ago
flixOpt - v2.1.10
Summary: This release is a Documentation and Development release.
📝 Docs
- Improved CHANGELOG.md formatting by adding better categories and formating by Gitmoji.
- Added a script to extract the release notes from the CHANGELOG.md file for better organized documentation.
👷 Development
- Improved
renovate.config - Sped up CI by not running examples in every run and using
pytest-xdist
What's Changed
- Feature/changelog by @FBumann in https://github.com/flixOpt/flixopt/pull/353
- Remove examples from default CI for speedup by @FBumann in https://github.com/flixOpt/flixopt/pull/359
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.9...v2.1.10
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 9 months ago
flixOpt - v2.1.9
Small Bugfix which was supposed to be fixed in 2.1.8
Fixed
- Fix error handling in network visualization if networkx is not installed.
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.8...v2.1.9
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 9 months ago
flixOpt - v2.1.8
This release focuses on code quality improvements, enhanced documentation, and bug fixes for heat pump components and visualization features.
Added
- Extra Check for HeatPumpWithSource.COP to be strictly > 1 to avoid division by zero
- Apply deterministic color assignment by using sorted() in
plotting.py - Add missing args in docstrings in
plotting.py,solvers.py, andcore.py.
Changed
- Greatly improved docstrings and documentation of all public classes
- Make path handling to be gentle about missing .html suffix in
plotting.py - Default for
relative_lossesinTransmissionis now 0 instead of None - Setter of COP in
HeatPumpWithSourcenow completely overwrites the conversion factors, which is safer. - Fix some docstrings in plotting.py
- Change assertions to raise Exceptions in
plotting.py
Fixed
- Fix color scheme selection in network_app; color pickers now update when a scheme is selected.
- Fix error handling in network visualization if networkx is not installed.
- Fix broken links in docs.
- Fix COP getter and setter of
HeatPumpWithSourcereturning and setting wrong conversion factors. - Fix custom compression levels in
io.save_dataset_to_netcdf - Fix
total_maxdid not work when total min was not used.
Development
- Pin dev dependencies to specific versions
- Improve CI workflows to run faster and smarter
What's Changed
- Add uv to CI by @FBumann in https://github.com/flixOpt/flixopt/pull/325
- chore(deps): update dependency ubuntu to v24 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/327
- Change UserWarning to DeprecationWarning for Bus objects which are st… by @FBumann in https://github.com/flixOpt/flixopt/pull/328
- chore(deps): update dependency plotly to v6 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/330
- Feature/bounds to all deps by @FBumann in https://github.com/flixOpt/flixopt/pull/331
- Fix/307 bug color scheme wahl funktioniert nicht by @FBumann in https://github.com/flixOpt/flixopt/pull/337
- Make networkx usage in type hints in "" by @FBumann in https://github.com/flixOpt/flixopt/pull/336
- Feature/332 improve docs by @FBumann in https://github.com/flixOpt/flixopt/pull/333
- Feature/312 improve and move docstrings by @FBumann in https://github.com/flixOpt/flixopt/pull/316
- chore(deps): update dependency dash-cytoscape to v1.0.2 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/345
- chore(deps): update dependency astral-sh/uv to v0.8.19 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/344
- Fix/small fixes by @FBumann in https://github.com/flixOpt/flixopt/pull/346
- Feature/many simple improvements by @FBumann in https://github.com/flixOpt/flixopt/pull/342
- Use | instead of Union and Optional in type hints everywhere by @FBumann in https://github.com/flixOpt/flixopt/pull/343
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.7...v2.1.8
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 9 months ago
flixOpt - v2.2.0rc2
Pre-release 2.2.0rc2
This is a pre-release version for testing and feedback.
Installation
pip install flixopt==2.2.0rc2 --pre
What's Changed
See the unreleased section in the changelog for upcoming features and changes.
Feedback
Please report any issues or feedback on GitHub Issues.
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.7...v2.2.0rc2
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 9 months ago
flixOpt - v2.1.7
This update is a maintenance release to improve Code Quality, CI and update the dependencies.
There are no changes or new features.
Added
- Added version to flixopt
Development
- ruff format the whole Codebase
- Added renovate config
- Added pre-commit
- lint and format in CI
- improved CI
- Updated Dependencies
- Updated Issue Templates
What's Changed
- Configure Renovate by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/309
- Feature/311 improve code quality by @FBumann in https://github.com/flixOpt/flixopt/pull/313
- Feature/improve ci by @FBumann in https://github.com/flixOpt/flixopt/pull/314
- Update actions/checkout action to v5 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/323
- Update actions/setup-python action to v6 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/324
- Update dependency pyvis to v0.3.2 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/321
- Update dependency linopy to >=0.5.7, <0.5.8 by @renovate[bot] in https://github.com/flixOpt/flixopt/pull/320
New Contributors
- @renovate[bot] made their first contribution in https://github.com/flixOpt/flixopt/pull/309
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.6...v2.1.7
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 9 months ago
flixOpt - v2.1.6
Changed
Sink,SourceandSourceAndSinknow accept multipleflowsasinputsandoutputsinstead of just one. This enables to model more use cases using these classes. [#291 by @FBumann]- Further, both
SinkandSourcenow have aprevent_simultaneous_flow_ratesargument to prevent simultaneous flow rates of more than one of their Flows. [#291 by @FBumann]
Added
- Added
FlowSystem.start_network_app()andFlowSystem.stop_network_app()to easily visualize the network structure of a flow system in an interactive dash web app. This is still experimental and might change in the future. [#293 by @FBumann]
Deprecated
- For the classes
Sink,SourceandSourceAndSink:.sink,.sourceand.prevent_simultaneous_sink_and_sourceare deprecated in favor of the new argumentsinputs,outputsandprevent_simultaneous_flow_rates. [#291 by @FBumann]
Fixed
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] 10 months ago
flixOpt - v2.1.2
Fixed
- Storage losses per hour where not calculated correctly, as mentioned by @brokenwings01. This might have lead to issues with modeling large losses and long timesteps.
- Old implementation: $c(\text{t}{i}) \cdot (1-\dot{\text{c}}\text{rel,loss}(\text{t}i)) \cdot \Delta \text{t}{i}$
- Correct implementation: $c(\text{t}{i}) \cdot (1-\dot{\text{c}}\text{rel,loss}(\text{t}i)) ^{\Delta \text{t}{i}}$
Known issues
- Just to mention: Plotly >= 6 may raise errors if "nbformat" is not installed. We pinned plotly to <6, but this may be fixed in the future.
Energy Systems - Energy System Modeling Frameworks
- Python
Published by github-actions[bot] about 1 year ago
flixOpt - v2.2.0b0
Beta Release of flixOpt with native scenario support
What's Changed
- Scenarios: Prepare DataModel by @FBumann in https://github.com/flixOpt/flixopt/pull/213
- Feature/scenarios data transform by @FBumann in https://github.com/flixOpt/flixopt/pull/215
- Feature/scenarios effects by @FBumann in https://github.com/flixOpt/flixopt/pull/216
- Feature/scenarios results by @FBumann in https://github.com/flixOpt/flixopt/pull/220
- Feature/scenarios dims order by @FBumann in https://github.com/flixOpt/flixopt/pull/219
- Feature/scenarios invest by @FBumann in https://github.com/flixOpt/flixopt/pull/227
- Feature/scenarios weights by @FBumann in https://github.com/flixOpt/flixopt/pull/228
- Feature/scenarios tests pandas by @FBumann in https://github.com/flixOpt/flixopt/pull/229
- Fix tests and docstrings by @FBumann in https://github.com/flixOpt/flixopt/pull/242
- Scenarios/io by @FBumann in https://github.com/flixOpt/flixopt/pull/244
- Scenarios/testing by @FBumann in https://github.com/flixOpt/flixopt/pull/246
- Scenarios/fixes by @FBumann in https://github.com/flixOpt/flixopt/pull/252
- Scenarios/filter by @FBumann in https://github.com/flixOpt/flixopt/pull/253
- Scenarios/drop suffix by @FBumann in https://github.com/flixOpt/flixopt/pull/251
- Scenarios/bar plot by @FBumann in https://github.com/flixOpt/flixopt/pull/254
- Scenarios/fixes by @FBumann in https://github.com/flixOpt/flixopt/pull/255
- Scenarios/effects by @FBumann in https://github.com/flixOpt/flixopt/pull/256
- Scenarios/datasets results by @FBumann in https://github.com/flixOpt/flixopt/pull/257
- Scenarios/deprecation by @FBumann in https://github.com/flixOpt/flixopt/pull/258
- Scenarios/model doc by @FBumann in https://github.com/flixOpt/flixopt/pull/259
- Scenarios/fix size by @FBumann in https://github.com/flixOpt/flixopt/pull/260
- Scenarios/plot network by @FBumann in https://github.com/flixOpt/flixopt/pull/262
- Fix DataConverter by @FBumann in https://github.com/flixOpt/flixopt/pull/263
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.0...v2.2.0b0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - 2.1.1
What's Changed
- Fix tests and docstrings by @FBumann in https://github.com/flixOpt/flixopt/pull/242
- Fixing bug in the
_ElementResults.constraintsnot returning the constraints but rather the variables
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.1.0...v2.1.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - v2.1.0
Release v2.1.0
Release Date: 2025-04-11
What's Changed
- Allow Python 3.13 by @FBumann in https://github.com/flixOpt/flixopt/pull/217
- Fixes/tests by @FBumann in https://github.com/flixOpt/flixopt/pull/234
- Fixes/tests more by @FBumann in https://github.com/flixOpt/flixopt/pull/239
- Feature/binary models by @FBumann in https://github.com/flixOpt/flixopt/pull/235
Improvements
- Add logger warning if relative_minimum is used without on_off_parameters in Flow, as this prevents the flow_rate from switching "OFF"
- Python 3.13 support added
- Greatly improved internal testing infrastructure by leveraging linopy's testing framework
Bug Fixes
- Bugfixing the lower bound of
flow_ratewhen using optional investments without OnOffParameters. - Fixes a Bug that prevented divest effects from working.
- added lower bounds of 0 to two unbounded vars (only numerical better)
Breaking Changes
- We restructured the modeling of the On/Off state of FLows or Components. This leads to slightly renaming of variables and constraints.
Variable renaming
- "...|consecutive_on_hours" is now "...|ConsecutiveOn|hours"
- "...|consecutive_off_hours" is now "...|ConsecutiveOff|hours"
Constraint renaming
- "...|consecutive_on_hours_con1" is now "...|ConsecutiveOn|con1"
- "...|consecutive_on_hours_con2a" is now "...|ConsecutiveOn|con2a"
- "...|consecutive_on_hours_con2b" is now "...|ConsecutiveOn|con2b"
- "...|consecutive_on_hours_initial" is now "...|ConsecutiveOn|initial"
- "...|consecutive_on_hours_minimum_duration" is now "...|ConsecutiveOn|minimum"
The same goes for "...|consecutive_off..." --> "...|ConsecutiveOff|..."
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.0.1...v2.1.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - v2.0.1
What's Changed
- Replace "|" with "__" by @PStange in https://github.com/flixOpt/flixopt/pull/225
- Add warning if relative_minimum is used without on_off_parameters by @FBumann in https://github.com/flixOpt/flixopt/pull/226
- Bugfix of load_factor? by @FBumann in https://github.com/flixOpt/flixopt/pull/231
New Contributors
- @PStange made their first contribution in https://github.com/flixOpt/flixopt/pull/225
Full Changelog: https://github.com/flixOpt/flixopt/compare/v2.0.0...v2.0.1
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - v2.0.0
Release v2.0.0
Release Date: March 29, 2025
🚀 Major Framework Changes
- Migration from Pyomo to Linopy: Completely rebuilt the optimization foundation to use Linopy instead of Pyomo
- Significant performance improvements, especially for large models
- Internal useage of linopys mathematical modeling language
- xarray-Based Data Architecture: Redesigned data handling to rely on xarray.Dataset throughout the package for:
- Improved solution representation and analysis
- Enhanced time series management
- Consistent serialization across all elements
- Reduced file size and improved performance
- Saving and restoring unsolved Models: The FlowSystem is now fully serializable and can be saved to a file.
- Share your work with others by saving the FlowSystem to a file
- Load a FlowSystem from a file to extend or modify your work
🔧 Key Improvements
Model Handling
- Extend every flixopt model with the linopy language: Add any additional constraint or variable to your flixopt model by using the linopy language.
- Full Model Export/Import: As a result of the migration to Linopy, the linopy.Model can be exported before or after the solve.
- Improved Infeasible Model Handling: Added better detection and reporting for infeasible optimization models
- Improved Model Documentation: Every model can be documented in a .yaml file, containing human readable mathematical formulations of all variables and constraints. THis is used to document every Calculation.
Calculation Results and documentation:
The CalculationResults class has been completely redesigned to provide a more streamlined and intuitive interface.
Accessing the results of a Calculation is now as simple as:
fx.FullCalculation('Sim1', flow_system)
calculation.solve(fx.solvers.HighsSolver())
calculation.results # This object can be entirely saved and reloaded to file without any information loss
This access doesn't change if you save and load the results to a file or use them in your script directly!
- Improved Documentation: The FlowSystem as well as a model Documentation is created for every model run.
- Results without saving to file: The results of a Calculation can now be properly accessed without saving the results to a file first.
- Unified Solution exploration: Every
Calculationhas aCalculation.resultsattribute, which accesses the solution. This can be saved and reloaded without any information loss. - Improved Calculation Results: The results of a Calculation are now more intuitive and easier to access. The
CalculationResultsclass has been completely redesigned to provide a more streamlined and intuitive interface.
Data Management & I/O
- Unified Serialization: Standardized serialization and deserialization across all elements
- Compression Support: Added data compression when saving results to reduce file size
- to_netcdf/from_netcdf Methods: Added for FlowSystem and other core components
Details
TimeSeries Enhancements
- xarray Integration: Redesigned TimeSeries to depend on xr.DataArray
- datatypes: Added support for more datatypes, with methods for conversion to TimeSeries
- Improved TimeSeriesCollection: Enhanced indexing, representation, and dataset conversion
- Simplified Time Management: Removed period concepts and focused on timesteps for more intuitive time handling
📚 Documentation
- Improved documentation of the FlixOpt API and mathematical formulations
- Google Style Docstrings: Updated all docstrings to Google style format
🔄 Dependencies
- Linopy: Added as the core dependency replacing Pyomo
- xarray: Now a critical dependency for data handling and file I/O
- netcdf4: Dependency for fast and efficient file I/O
Dropped Dependencies
- pyomo: Replaced by linopy as the modeling language
📋 Migration Notes
This version represents a significant architecture change. If you're upgrading:
- Code that directly accessed Pyomo models will need to be updated to work with Linopy
- Data handling now uses xarray.Dataset throughout, which may require changes in how you interact with results
- The way labels are constructed has changed throughout the system
- The results of calculations are now handled differently, and may require changes in how you access results
- The framework was renamed from flixOpt to flixopt. Use
import flixopt as fx.
For complete details, please refer to the full commit history.
Installation
pip install flixopt==2.0.0
Upgrading
pip install --upgrade flixopt
Full Changelog: https://github.com/flixOpt/flixopt/compare/v1.0.12...v2.0.0
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - Deploy docs
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - v1.0.12
Release flixOpt on Pypi
Finally, flixOpt is available on Pypi
Full Changelog: https://github.com/flixOpt/flixopt/compare/v1.0.11...v1.0.12
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann about 1 year ago
flixOpt - v1.0.11
What's Changed
- Make flixOpt work without having the package "tsam" installed. by @FBumann in https://github.com/flixOpt/flixopt/pull/175
Full Changelog: https://github.com/flixOpt/flixopt/compare/v1.0.10...v1.0.11
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.10
Bugfixes:
- Bug when using a Flow with
fixed relative profilein combination with nofixed size.
Others:
- Simplified logic in FlowModel
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.9...v1.0.10
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.9
Release Notes for v1.0.9
Bug Fixes
- BUGFIX: initial value of consecutive duration is now reliably fixed
- Fixed a bug that always fixed the first timestep of the binary on/off variable when consecutive on/off hours are used. This previously led to an infeasibility if both
consecutive_on_hours_minandconsecutive_off_hours_minwere used. Now, the initial state is only fixed if the previous value is greater than zero.
- Fixed a bug that always fixed the first timestep of the binary on/off variable when consecutive on/off hours are used. This previously led to an infeasibility if both
Others
- Introduce automatic version setting in pyproject.toml (#166) by @FBumann in https://github.com/flixOpt/flixOpt/pull/167
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.8...v1.0.9
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.8
Bugfixes and decreased json file size
What's changed
- Bugfix in OnOffModel in special cases. A threshold is ensured in the OnOffModel in all cases (even with relative minimum is 0 or minimum_size is 0 by @FBumann
- Bugfix in class SourceAndSink: prevent_simultanious_flows now works as expected by @FBumann
- Remove indents from json files depending on the length of the time_series (currently 50) by @FBumann in https://github.com/flixOpt/flixOpt/pull/155
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.7...v1.0.8
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.7
Add json export to FlowSystem
Based on the already existing dumping methods, a new method to write information about Elements and Flow System to .json files.
The Infos are stored in the .yaml file of the results, user "FlowSystem".
Energy Systems - Energy System Modeling Frameworks
- Python
Published by baumbude over 1 year ago
flixOpt - v1.0.5
What's Changed
- structure.py: bugfix in normalized_center_of_mass() by @baumbude in https://github.com/flixOpt/flixOpt/pull/149
- removed resolving paths to prevent errors by @FBumann in https://github.com/flixOpt/flixOpt/pull/150
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.4...v1.0.5
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.4
v1.0.4
Backwards incompatible changes
Parameter can_be_off of class Flow renamed to on_off_parameters.
Fixes: Information about OnOffParameters missing in data.json and .infos()
Improve representation of arrays in print()
- Integrated "normed center of mass" in str_representation of array. Dadurch werden [0, 0, 1] von [1, 0, 0] und [0, 1, 0] unterscheidbar.
lengthgekürzt auflen
Arrays in Elements are now printed as:
{'fixed_relative_profile': 'Array (min=0.00, max=110.00, mean=46.67, median=20.00, std=41.10, len=108, center=0.50)'}
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.3...v1.0.4
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - Bugfix in printing
Bugfix the printing of Components. Information about inputs and outputs was only the label of the Flows
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.2...v1.0.3
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - flixOpt 1.0.2
Improve Testing Infrastructure and Code quality
Linting and formating
Using ruff, the whole codebase was linted and formatted running ruff check . and ruff format
Linting was also added to CI-CD. This ensures code quality throughout the project.
Following the Linting rules is easy by running ruff check . --fix before every push.
The rules of listing and formatting are specified in pyproject.toml. ruff was added to the dev-dependencies
Tests
New functional_tests.py with a minimal but effective Test structure was created.
Consecutive hours
Considering previous_flow_rate when calculating the onsecutive_on_hours / off hours.
This increases the capabilities for short therm optimization, this would be a nice addition.
Especially for SegmentedCalculation, this can significantly impact results.
Improving the internal data structures
In order to make all elements in a FLowSystem more easily accessible, they are now stored in Dictionaries instead of lists.
Improved exporting
The Representations as dict and str are improved and the logic was simplified.
For the new improved printing, the package rich is used.
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.1
flixOpt 1.0.1
In Short
- Feature/66 change default values of network visualization by @FBumann in https://github.com/flixOpt/flixOpt/pull/117
- Feature/config by @FBumann in https://github.com/flixOpt/flixOpt/pull/121
- Devs/empty dicts by @FBumann in https://github.com/flixOpt/flixOpt/pull/119
- flixOpt 1.0.1 by @FBumann in https://github.com/flixOpt/flixOpt/pull/122
Full Changelog: https://github.com/flixOpt/flixOpt/compare/v1.0.0...v1.0.1
This patch includes the following changes:
- a config file for flixOpt, which the user can overwrite
- default values being stored as empty dicts or empty lists instead of None whoever possible
- default values in network visualization improved
- typo in attribute of class CoolingTower fixed
Adding a CONFIG to flixOpt
To control central behaviour of flixOpt without hard coding it, we introduce a CONFIG.
a default config is provided and loaded when importing the package.
The values are validated on import.
CUrrently there are Configs for:
Logging
- logging_level
- logging_file
- rich: use the rich logging (for terminal formatting)
modeling
- BIG
- EPSILON
- BIG_BINARY_BOUND
If the user desires to change these values, he has to provide his own config_file.
import flixOpt as fx
import numpy as np
if __name__ == '__main__':
fx.CONFIG.load_config('new_config.yaml')
# --- Define Thermal Load Profile ---
# Load profile (e.g., kW) for heating demand over time
thermal_load_profile = np.array([30., 0., 20.])
...
This has to be done at the beginning of your script, to work as expected.
This is currently the only way to reliably change the config values!
To change the logging level without a config file, use the existing function:
fx.change_logging_level('DEBUG')
Default values changed: {} / [] instead of None
Instead of using None to represent the absence of certain values, empty Lists and Dictionaries are now used (when applicable).
This enables iterating over them easier.
LinearConverter
- conversion_factors
- segmented_conversion_factors
Flow
- effects_per_flow_hour
InvestParameters
- fix_effects
- specific_effects
- divest_effects
OnOffParameters
- effects_per_switch_on
- effects_per_running_hour
aggregation.py
AggregationParameters
- time_series_for_high_peaks
- time_series_for_low_peaks
Aggregation
- weights
- time_series_for_high_peaks
- time_series_for_low_peaks
Other improvements:
Improved an Exception message in CalculationResults.to_dataframe(), regarding not found Elements
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v1.0.0
flixOpt v1.0.0
Months of work come to an end, resulting in a big new release of flixOpt
This rework targets the following issues of the flixOpt package:
Following PEP 8 for naming conventions
This lead to renaming pretty much all classes, functions and attributes. We are confident, that every renaming was done carefully and made the package easier to understand and more concise.
Highlights are:
- nominal_val was renamed to size
- val was renamed to flow_rate
- onHours to consecutive_on_hours
- ...
Simplifying the user interface
flixOpt is now meant to be imported as import flixOpt as fx. All relevant classes and functions will be available from there.
fx.Flow()
fx.Effect
fx.FullCalculation
fx.FlowSystem
fx.LinearConverter()
fx.Sink()
More is available through sub-modules:
fx.linear_converters.Boiler()
fx.linear_converters.CHP()
fx.solvers.HighsSolver()
explore by typing fx.and look for hints by your IDE
Changing the Hierarchy of classes
In conjunction with renaming, the package was restructured thoroughly.
Following highlights are to be named here:
- splitting different calculation types into several classes (FullCaluclation, AggregatedCalcultion, Segmented Calculation)
- Splitting the initiation of an Element (Flow, Bus, Effect, ...) form its mathematical modeling (FlowModel, BusModel, EffectModel). This greatly streamlines the interaction of Elements and Models, making the modeling much easier to understand. Further, this makes the implementation of new Features is way easier
- Grouping parameters into classes and removing **kwargs from all elements: New classes InvestmentParameters and OnOffParameters are introduced, to centralize the documentation of them. This makes optional kwargs obsolete
Decoupling of math_modeling.py
The core of flixOpt, handling Variables, Constraints and the translation to pyomo (or other modeling languages) was greatly improved. It was completely decoupled from the rest of the package, making it actually usable for other purposes. Further, The Interfaces to solvers where moved into individual classes, accessible through solvers.py
Saving results to .json
Prior to this rework, the results of a Calculation were saved as a .yaml file for metadata and a .pickle file for the actual results. The .pickle format brings several issues through its nature of deserializing python objects, leading to security issues and issues with opening the file without having the unmodified python source code for the classes. To resolve these issues, the results of a flixOpt Calculation are now saved as .json instead of .pickle
Further, the .yaml file and .json file are now clearly structured and constructed in an easy to understand matter. This emnables for easy adoptions and changes in the future, and paths the way for simpler automated evaluation of results.
The .yaml file now holds all input parameters of the constructed FlowSystem, improving later evaluation possibilities.
The structure of both files is greatly improved and streamlined, improving automated evaluations and human readability.
Evaluation of results
As the results are now saved as a json file, the results can be analyzed anywhere and anytime. But flixOpt provides a .results.py module, which simplifies the process, providing several functions and classed for analyzing results and creating nice plots. These plots can be used as a baseline, or further modified by the user.
The plotting.py module, which the .results.yp relies on, can be used for that. But the user can modify the plots in any way he likes
This rework further brings new functionality
Logging
- Proper Logging with pretty formatting is introduced, as well as saving the log to file
fx.setup_logging()for customizing the setup
Visualizing the FlowSystem as a Network
- using pyvis, the created FlowSystem can now be visualized as an interactive html and shown in the browser. This enables for quick debugging and documentation.
Changing attributes of Elements (Flow, Component, ...) after initiation
- The attributes of Elements are not transformed into other datatypes before FLowSystem is modeled. This enables the user to change attributes of Elements after creation without issues.
Testing
- No Changes except name changes were made in tests. All Tests passing
- examples were added to the test routine, to make sure they don't throw errors
Other changes
- flixOpt now uses the modern
pyproject.tomlinstead ofsetup.py - Output of HiGHS-Solver is printed to console (unfortunately only after the solve, and not in real time)
Bug fixes
- The were basically no Bigs in the mathematical modeling itself. The reworks main purpose was to simplify the modeling process, to enable new features to be introduced, as well as to improve user experience.
Known issues
- Solver output is not inserted into the flixOpt logger, and only printed to console.
- Solver interfaces only accept a few parameters
- Although type-hinted, no checks are performed to make sure the right datatypes are passed to Elements of flixOpt. We strongly recommend to make sure to not pass
pd.DataFramesorpd.Seriesobjects, but instead convert them tonumpy.array()by calling
series = pd.Series([1, 2, 3, 4])
array = series.to_numpy() # Output: [1 2 3 4]
# Or for DataFrames
df = pd.DataFrame({'A': [1, 2, 3, 4]})
array = df['A'].to_numpy() # Output: [1 2 3 4]
Removed Functionality
Some of the functionality of flixOpt were removed. Many of them will be readied in the future, but with a different implementation:
- Medium: Buses and Flows don't accept a Medium anymore. This was used for validation. We plan on reintroducing this with added functionality to the evaluation of results.
- plotting: The potting in general was made much simpler. Currently, we sacrificed some plots for this simpler interface (pie charts, sorted charts ('Jahresdauerlinie)).
Contributors
Felix Bumann
Reviewers
Peter Stange
Felix Panitz
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann over 1 year ago
flixOpt - v0.2.0 - Initial Release of flixOpt
Initial Release of flixOpt
After being in development, this marks the first official release of flixOpt.
flixOpt and all its dependencies (including a solver) are now easily installable via pip (see README.md)
This release is a crucial part in preparation of a major new release with breaking changes, like new class names, module names and more.
API Changes
- HiGHS-Solver API established - Open Source and faster than other open source solvers - https://highs.dev/#docs
- Tutorial as Python Notebook added
- Time-stamp labeling in .csv export of results enabled
New features
- Limits per Time step for effects introduced
- New Concept "exists" introduced.
- This Attribute allows for multi-period investments.
- Sets The upper limit for TS_Variables to 0 when Component or Flow does not exist.
- The existence of flows is limited by the existence of the Component it is assigned to.
- Vice versa, it's possible that a Flow does not yet exist while its component does (for ex. multiple grid connections).
- The concept is introduced but, some aspects are not yet perfectly implemented.
- str() and repr() methods added for major classes, which enables the printing of a system configuration for documentation and debugging purposes
- Type Hints are added, improving readability and ease of use
- New Module flixCompsExperimental.py with a experimental and early state implementation of a CHP-Plant with variable rate between heat and power
Bug fixes
- Fixed onHoursSum_max & min in Flows
- Fixed Segmented Modeling
- Fixed max_rel in combination with no Investment in class Storage
Other changes
- Proper tests are introduced for better and safer future developments
Known issues
- onHoursSum_max & min are not yet working in Components (raises Error). Use Flow instead.
- exists is still in early state:
- on single flows does not work with segmentsOfFlows. use exists in Component
- does not affect all properties that might be expected:
- cFlow: on_valuesBeforeBegin, valuesBeforeBegin are still before the begin of the model, not at the time step before existence
- cStorage: chargeState0_inFlowHours, charge_state_end_min, charge_state_end_max and fracLossPerHour are not affected by "exists". Only the available capacity is limited to 0
Contributors
Felix Bumann
Felix Panitz
Peter Stange
Energy Systems - Energy System Modeling Frameworks
- Python
Published by FBumann almost 2 years ago