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_log option routes solver output (HiGHS, Gurobi, etc.) through the flixopt.solver Python 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 by CONFIG.debug(), CONFIG.exploring(), CONFIG.production(), and CONFIG.notebook() presets. (#606)
  • progress parameter: solve(), optimize(), and rolling_horizon() now accept a progress parameter (default True) to control the tqdm progress bar independently of CONFIG settings.
  • log_fn parameter: solve() now accepts a log_fn parameter to persist the solver log to a file.

♻️ Changed

  • Presets: CONFIG.debug() and CONFIG.exploring() now set log_to_console=False (solver output is routed through the Python logger instead of native console output).
  • CONFIG.Solving.log_to_console now exclusively controls the solver's native console output. It no longer affects the tqdm progress bar (use the progress parameter instead).

🐛 Fixed

  • Clustering IO: cluster_weight is now preserved during NetCDF roundtrip for manually constructed clustered FlowSystems (i.e. FlowSystem(..., clusters=..., cluster_weight=...)). Previously, cluster_weight was silently dropped to None during save->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 from 3.0.0 to 3.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 the optimize fixture:
    • test_flow.py — flow bounds, merit order, relative min/max, on/off hours
    • test_flow_invest.py — investment sizing, fixed-size, optional invest, piecewise invest
    • test_flow_status.py — startup costs, switch-on/off constraints, status penalties
    • test_bus.py — bus balance, excess/shortage penalties
    • test_effects.py — effect aggregation, periodic/temporal effects, multi-effect objectives
    • test_components.py — SourceAndSink, converters, links, combined heat-and-power
    • test_conversion.py — linear converter balance, multi-input/output, efficiency
    • test_piecewise.py — piecewise-linear efficiency, segment selection
    • test_storage.py — charge/discharge, SOC tracking, final charge state, losses
    • test_multi_period.py — period weights, invest across periods
    • test_scenarios.py — scenario weights, scenario-independent flows
    • test_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

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_log option routes solver output (HiGHS, Gurobi, etc.) through the flixopt.solver Python 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 by CONFIG.debug(), CONFIG.exploring(), CONFIG.production(), and CONFIG.notebook() presets. (#606)
  • progress parameter: solve(), optimize(), and rolling_horizon() now accept a progress parameter (default True) to control the tqdm progress bar independently of CONFIG settings.
  • log_fn parameter: solve() now accepts a log_fn parameter to persist the solver log to a file.

♻️ Changed

  • Presets: CONFIG.debug() and CONFIG.exploring() now set log_to_console=False (solver output is routed through the Python logger instead of native console output).
  • CONFIG.Solving.log_to_console now exclusively controls the solver's native console output. It no longer affects the tqdm progress bar (use the progress parameter instead).

🐛 Fixed

  • Clustering IO: cluster_weight is now preserved during NetCDF roundtrip for manually constructed clustered FlowSystems (i.e. FlowSystem(..., clusters=..., cluster_weight=...)). Previously, cluster_weight was silently dropped to None during save->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 the optimize fixture:
    • test_flow.py — flow bounds, merit order, relative min/max, on/off hours
    • test_flow_invest.py — investment sizing, fixed-size, optional invest, piecewise invest
    • test_flow_status.py — startup costs, switch-on/off constraints, status penalties
    • test_bus.py — bus balance, excess/shortage penalties
    • test_effects.py — effect aggregation, periodic/temporal effects, multi-effect objectives
    • test_components.py — SourceAndSink, converters, links, combined heat-and-power
    • test_conversion.py — linear converter balance, multi-input/output, efficiency
    • test_piecewise.py — piecewise-linear efficiency, segment selection
    • test_storage.py — charge/discharge, SOC tracking, final charge state, losses
    • test_multi_period.py — period weights, invest across periods
    • test_scenarios.py — scenario weights, scenario-independent flows
    • test_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

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 component coordinate becoming (case, contributor) shaped after concatenation in Comparison class. Non-index coordinates are now properly merged before concat in solution, inputs, and all statistics properties. Added warning when coordinate mappings conflict (#599)

📝 Docs

  • Docs Workflow: Added workflow_dispatch inputs for manual docs deployment with version selection (#599)

👷 Development

  • Updated dev dependencies to newer versions

What's Changed

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 array when clustering multi-period or multi-scenario systems with ExtremeConfig. 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 ExtremeConfig with multi-period or multi-scenario systems, only method='replace' is now allowed. Using method='new_cluster' or method='append' will raise a ValueError. 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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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 comparison
  • data_only parameter: Get data without generating plots in Comparison methods
  • threshold parameter: Filter small values when comparing

Plotting Enhancements

  • threshold parameter: Added to all plotting methods to filter values below a threshold (default: 1e-5)
  • round_decimals parameter: Control decimal precision in balance(), carrier_balance(), and storage() plots
  • flow_colors property: 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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • stats.plot.balance(), carrier_balance(), and storage() now use xarray_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 → Use flow_system.stats (shorter, more convenient)

Classes (use FlowSystem methods instead):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.stats
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)
  • Component.inputs/outputs and Bus.inputs/outputs are now FlowContainer (dict-like). Use .values() to iterate flows.

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'
  • statistics.plot.balance(), carrier_balance(), and storage() now use xarray_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):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'

🗑️ Deprecated

The following items are deprecated and will be removed in v7.0.0:

Classes (use FlowSystem methods instead):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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, a SOC_boundary variable 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_version attribute 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, or animation_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 StatisticsAccessor properties (flow_rates, flow_hours, sizes, charge_states, temporal_effects, periodic_effects, total_effects)
  • Mirrors all StatisticsPlotAccessor methods (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_weights are now always normalized to sum to 1 when set (including after .sel() subsetting)

♻️ Changed

  • FlowSystem.weights returns dict[str, xr.DataArray] (unit weights instead of 1.0 float fallback)
  • FlowSystemDimensions type now includes 'cluster'

🗑️ Deprecated

The following items are deprecated and will be removed in v7.0.0:

Classes (use FlowSystem methods instead):

  • Optimization class → Use flow_system.optimize(solver)
  • SegmentedOptimization class → Use flow_system.optimize.rolling_horizon()
  • Results class → Use flow_system.solution and flow_system.statistics
  • SegmentedResults class → Use segment FlowSystems directly

FlowSystem methods (use transform or topology accessor instead):

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot()
  • flow_system.start_network_app() → Use flow_system.topology.start_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_app()
  • flow_system.network_infos() → Use flow_system.topology.infos()

Parameters:

  • normalize_weights parameter in create_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):

  • ClusteredOptimization class - Use flow_system.transform.cluster() then optimize()
  • ClusteringParameters class - Parameters are now passed directly to transform.cluster()
  • flixopt/clustering.py module - Restructured to flixopt/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_weight and sum_temporal() now use consistent implementation
  • FlowSystem.from_old_results() now sets previous_flow_rate=0 for flows of components with status_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.py helper 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 storage cluster_mode options
  • TestInterclusterStorageLinking: Tests for SOC_boundary variable and expansion logic
  • TestMultiPeriodClustering: Tests for clustering with periods and scenarios dimensions
  • TestPeakSelection: Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters

New Test Suites for Other Features:

  • test_clustering_io.py - Tests for clustering serialization roundtrip
  • test_sel_isel_single_selection.py - Tests for transform selection methods

What's Changed

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-material to v9.7.1
  • Updated mkdocstrings-python to v1.19.0
  • Updated ruff to v0.14.10
  • Updated pymdown-extensions to v10.19.1
  • Updated werkzeug to v3.1.4

👷 Development

  • Updated actions/checkout action to v6

What's Changed

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 via progress=False in solve calls
  • Downgraded "FlowSystem not connected" message from WARNING to INFO (auto-connects anyway)

🐛 Fixed

  • Fixed notebooks triggering unnecessary warnings (removed relative_minimum without status_parameters)

📝 Docs

  • Consolidated verbose print statements into concise single-line summaries across all tutorial notebooks
  • Added output suppression (;) to optimize() calls for cleaner cell output
  • Fixed notebook 07 parameters so CHP investment produces interesting results (was 0 kW, now 100 kW)

What's Changed

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 to by=None for aggregated totals; use by='component' for the previous behavior

What's Changed

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=False parameter 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 data
  • figure: A Plotly Figure object

Component color parameter: Components now accept a color parameter for consistent visualization styling.

💥 Breaking Changes

Renamed OnOffParametersStatusParameters: 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_hourimbalance_penalty_per_flow_hour
  • Default changed from 1e5 to None (strict balance)
  • with_excessallows_imbalance
  • excess_inputvirtual_supply
  • excess_outputvirtual_demand

Storage charge_state changes:

  • charge_state no 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_inputvirtual_supply and BusModel.excess_outputvirtual_demand for clearer semantics
  • Renamed Bus.excess_penalty_per_flow_hourimbalance_penalty_per_flow_hour
  • Renamed Bus.with_excessallows_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 → Use flow_system.optimize(solver)
  • ClusteredOptimization → Use flow_system.transform.cluster() then optimize()
  • SegmentedOptimization → Use flow_system.optimize.rolling_horizon()
  • Results → Use flow_system.solution and flow_system.statistics
  • SegmentedResults → Use segment FlowSystems directly

FlowSystem methods deprecated:

  • flow_system.sel() → Use flow_system.transform.sel()
  • flow_system.isel() → Use flow_system.transform.isel()
  • flow_system.resample() → Use flow_system.transform.resample()
  • flow_system.plot_network() → Use flow_system.topology.plot_network()
  • flow_system.start_network_app() → Use flow_system.topology.start_network_app()
  • flow_system.stop_network_app() → Use flow_system.topology.stop_network_app()
  • flow_system.network_infos() → Use flow_system.topology.network_infos()

Results methods deprecated:

  • results.flow_rates() → Use flow_system.statistics.flow_rates
  • results.flow_hours() → Use flow_system.statistics.flow_hours

Migration helpers (temporary, also deprecated):

  • FlowSystem.from_old_results() → For migrating old result files
  • Results.convert_to_flow_system() → For converting Results objects

Plotting parameters deprecated:

  • indexer parameter → Use select instead
  • heatmap_timeframes parameter → Use reshape_time=(timeframes, timesteps_per_frame) instead
  • heatmap_timesteps_per_frame parameter → Use reshape_time=(timeframes, timesteps_per_frame) instead
  • color_map parameter → Use colors instead

🔥 Removed

Python version changes:

  • Dropped Python 3.10 support
  • Added Python 3.14 support

Classes removed (already renamed/deprecated in v4.x):

  • OnOffParameters → Use StatusParameters
  • Calculation → Use Optimization (deprecated) or flow_system.optimize() (recommended)
  • FullCalculation → Use Optimization (deprecated) or flow_system.optimize() (recommended)
  • AggregatedCalculation → Use ClusteredOptimization (deprecated) or flow_system.transform.cluster() (recommended)
  • SegmentedCalculation → Use SegmentedOptimization (deprecated) or flow_system.optimize.rolling_horizon() (recommended)
  • Aggregation → Use Clustering
  • AggregationParameters → Use ClusteringParameters
  • AggregationModel → Use ClusteringModel
  • CalculationResults → Use Results (deprecated) or flow_system.solution (recommended)
  • SegmentedCalculationResults → Use SegmentedResults (deprecated)

Modules removed:

  • calculation.py module → Use optimization.py (deprecated) or FlowSystem methods (recommended)

Functions removed:

  • change_logging_level() → Use CONFIG.Logging.enable_console()

Properties removed:

  • FlowSystem.all_elements → Use dict-like interface (flow_system['label'], .keys(), .values(), .items())
  • FlowSystem.weights → Use scenario_weights

📝 Docs

Comprehensive Tutorial Notebooks - 12 new Jupyter notebooks covering all major use cases:

  1. 01-Quickstart - Minimal working example
  2. 02-Heat System - District heating with storage
  3. 03-Investment Optimization - Optimal equipment sizing
  4. 04-Operational Constraints - Startup costs, uptime/downtime
  5. 05-Multi-Carrier System - CHP producing electricity and heat
  6. 06a-Time-Varying Parameters - Temperature-dependent COP
  7. 06b-Piecewise Conversion - Load-dependent efficiency
  8. 06c-Piecewise Effects - Economies of scale
  9. 07-Scenarios and Periods - Multi-year planning
  10. 08-Large-Scale Optimization - Resampling and two-stage
  11. 09-Plotting and Data Access - Visualization guide
  12. 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 behavior
  • test_solution_and_plotting.py - Statistics accessor and plotting
  • test_solution_persistence.py - Solution save/load
  • test_io_conversion.py - Old file format conversion
  • test_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

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

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.json file for Zenodo metadata configuration
    • Repository now ready for DOI assignment upon next release

What's Changed

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)'] and results.solution['Penalty(periodic)'] if needed

📝 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

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

  • overwrite parameter 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:

  • FullCalculationOptimization
  • AggregatedCalculationClusteredOptimization
  • SegmentedCalculationSegmentedOptimization
  • CalculationResultsResults
  • SegmentedCalculationResultsSegmentedResults
  • AggregationClustering
  • AggregationParametersClusteringParameters
  • AggregationModelClusteringModel
  • Module: calculation.pyoptimization.py
  • Module: aggregation.pyclustering.py

Old names remain available with deprecation warnings (removed in v5.0.0).

🐛 Fixed

  • Fixed fix_sizes() docstring/implementation inconsistency for optional ds parameter

👷 Development

  • Fixed active_timesteps type annotation to include None
  • Fixed xarray truth-value ambiguity in main_results buses with excess filter
  • Added validation for nr_of_previous_values in SegmentedOptimization to prevent silent indexing bugs

What's Changed

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

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

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 with logger.log(SUCCESS_LEVEL, ...)
  • Added proper deprecation tests

♻️ Changed

  • logger coloring improved

👷 Development

  • Fixed Deprecation warnings in tests

What's Changed

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

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 colors
  • CONFIG.Logging.enable_file(level, path, max_bytes, backup_count) - File logging with rotation
  • CONFIG.Logging.disable() - Disable all logging
  • CONFIG.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 + optional colorlog for colors
  • Configuration immediate (no CONFIG.apply() needed)
  • Log format: [dimmed timestamp] [colored level] │ message
  • Logs to stdout by default (configurable)
  • SUCCESS level preserved (green, level 25)
  • Performance: Expensive operations guarded with logger.isEnabledFor() checks

🗑️ Deprecated

  • change_logging_level(level) → Use CONFIG.Logging.enable_console(level). Removal in v5.0.0.

🔥 Removed

CONFIG methods/attributes:

  • CONFIG.apply() → Use helper methods directly
  • CONFIG.Logging.level, .console, .file → Use enable_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

  • TypeError in check_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.Logging docstring
  • Color customization examples
  • Migration guide with before/after code

Until here -->

What's Changed

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_periods and maximum_over_periods
  • Flow: flow_hours_max_over_periods and flow_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_timestep for time)
  • Weights correctly recalculate when using .sel() or .isel() on periods
  • Separate tracking of period_weights, scenario_weights, and combined weights

Simplified workflow:

  • Calculation.solve() now automatically calls do_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:

  1. Pass only scenario weights to scenario_weights
  2. Period weights will be computed automatically from your periods index

🗑️ Deprecated Parameters

Linear converters (Boiler, CHP, HeatPump, etc.) - descriptive names replace abbreviations:

  • Flow: Q_fufuel_flow, P_elelectrical_flow, Q_ththermal_flow, Q_abheat_source_flow
  • Efficiency: etathermal_efficiency, eta_ththermal_efficiency, eta_elelectrical_efficiency, COPcop (lowercase)

Constraint parameters - removed redundant _total suffix:

  • Flow: flow_hours_total_maxflow_hours_max, flow_hours_total_minflow_hours_min
  • OnOffParameters: on_hours_total_maxon_hours_max, on_hours_total_minon_hours_min, switch_on_total_maxswitch_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_effects
  • OnOffParameters: on_hours_total_min, on_hours_total_max, switch_on_total_max
  • Flow: 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

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/uv to v0.9.8
  • Updated mkdocs-git-revision-date-localized-plugin to v1.5.0

📝 Docs

  • Improved type specifications in flixopt/types.py for better documentation generation
  • Fixed minor mkdocs warnings in flixopt/io.py and mkdocs.yml

What's Changed

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_S for numeric data with Time/Period/Scenario dimensions
    • Added Bool_TPS, Bool_PS, Bool_S for boolean data with dimension support
    • Added Effect_TPS, Effect_PS, Effect_S for effect dictionaries with dimension support
    • Added Scalar type for scalar-only numeric values
    • Added NumericOrBool utility type for internal use
    • Type system supports scalars, numpy arrays, pandas Series/DataFrames, and xarray DataArrays
  • Lazy logging evaluation - expensive log operations only execute when log level is active
  • CONFIG.Logging.verbose_tracebacks option for detailed debugging with variable values

💥 Breaking Changes

  • Logging framework: Migrated to loguru
    • Removed CONFIG.Logging parameters: rich, Colors, date_format, format, console_width, show_path, show_logger_name
    • For advanced formatting, use loguru's API directly after CONFIG.apply()

♻️ Changed

  • Code structure: Removed commons.py module and moved all imports directly to __init__.py for cleaner code organization (no public API changes)
  • Type handling improvements: Updated internal data handling to work seamlessly with the new type system

🐛 Fixed

  • Fixed ShareAllocationModel inconsistency 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-material to v9.6.23
  • Replaced rich >= 13.0.0 with loguru >= 0.7.0 for logging

📝 Docs

  • Enhanced documentation in flixopt/types.py with 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.py explaining parameter handling and transformation
  • Improved OnOffParameters attribute documentation
  • Updated getting-started guide with loguru examples
  • Updated config.py docstrings for loguru integration

👷 Development

  • Added test for FlowSystem resampling

What's Changed

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() and FlowSystem.__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

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

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.Solving configuration 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 use CONFIG.Solving defaults for parameters, allowing global configuration
  • Solver parameters can still be explicitly overridden when creating solver instances
  • New log_to_console parameter in all Solver classes

♻️ Changed

  • Individual solver output is now hidden in SegmentedCalculation. To return to the prior behaviour, set show_individual_solves=True in do_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

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.yaml to use a compacted list representation for periods and scenarios

🐛 Fixed

  • Using switch_on_total_max with periods or scenarios failed

📝 Docs

  • Add more comprehensive CONTRIBUTE.md
  • Improve logical structure in User Guide

What's Changed

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(), and flow_system.items() for unified element access
  • Specialized containers (components, buses, effects, flows) offer type-specific access with helpful error messages

🗑️ Deprecated

  • FlowSystem.all_elements property is deprecated in favor of dict-like interface (flow_system['label'], .keys(), .values(), .items()). Will be removed in v4.0.0.

What's Changed

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_timestep and hours_of_previous_timesteps properly

👷 Development

  • Improved issue templates

What's Changed

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 for CalculationResults and SegmentedCalculationResults to 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
  • 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, and backend_kwargs parameters
  • New CONFIG.Plotting configuration section:
    • default_show: Control default plot visibility
    • default_engine: Choose 'plotly' or 'matplotlib'
    • default_dpi: Set resolution for saved plots
    • default_facet_cols: Configure default faceting columns
    • default_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.Dataset as 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

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

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_by and animate_by parameters to create subplot grids and animations from multidimensional data (scenarios, periods, etc.). Plotly only.
  • Flexible data selection with select parameter: Select data using single values, lists, slices, or index arrays for precise control over what gets plotted
  • Heatmap fill control: New fill parameter 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 select parameter 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.py with better demonstration of new features
  • Robust validation: Improved dimension validation in plot_heatmap() with clearer error messages

🗑️ Deprecated

  • indexer parameter: Use the new select parameter instead. The indexer parameter will be removed in v4.0.0
  • heatmap_timeframes and heatmap_timesteps_per_frame parameters: Use the new reshape_time=(timeframes, timesteps_per_frame) parameter instead in heatmap plotting methods
  • color_map parameter: Use the new colors parameter 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

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 mode parameter in plotting instead of new style
  • Renamed new mode parameter in plotting methods to unit_type

📝 Docs

  • Updated Migration Guide and added missing entries.
  • Improved Changelog of v3.0.0

What's Changed

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

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

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_sizes and scenario_independent_flow_rates parameters
    • By default, investment sizes are shared across scenarios while flow rates vary per scenario

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 FlowResults class and precomputed DataArrays for sizes/flow_rates/flow_hours
  • effects_per_component dataset for component impact evaluation, including all indirect effects through effect shares

Other additions:

  • Balanced storage - charging and discharging sizes can be forced equal via balanced parameter
  • New Storage parameters: relative_minimum_final_charge_state and relative_maximum_final_charge_state for final state control
  • Improved filter methods in results
  • Example for 2-stage investment decisions leveraging FlowSystem resampling

💥 Breaking Changes

  • relative_minimum_charge_state and relative_maximum_charge_state don't have an extra timestep anymore.
  • Renamed class SystemModel to FlowSystemModel
  • Renamed class Model to Submodel
  • Renamed mode parameter in plotting methods to style
  • Renamed investment binary variable is_invested to invested in InvestmentModel
  • Calculation.do_modeling() now returns the Calculation object instead of its linopy.Model. Callers that previously accessed the linopy model directly should now use calculation.do_modeling().model instead of calculation.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 SegmentedCalculation now has its own distinct FlowSystem object
  • 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.py more compact and easier to read
  • Improved format handling in configuration module
  • Enhanced console output to support both stdout and stderr stream selection
  • Added show_logger_name parameter to CONFIG.Logging for displaying logger names in messages

🗑️ Deprecated

  • The agg_group and agg_weight parameters of TimeSeriesData are deprecated and will be removed in a future version. Use aggregation_group and aggregation_weight instead.
  • The active_timesteps parameter of Calculation is deprecated and will be removed in a future version. Use the new sel(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_effectseffects_of_investment
    • specific_effectseffects_of_investment_per_size
    • divest_effectseffects_of_retirement
    • piecewise_effectspiecewise_effects_of_investment
  • Effect parameters renamed:
    • minimum_investmentminimum_periodic
    • maximum_investmentmaximum_periodic
    • minimum_operationminimum_temporal
    • maximum_operationmaximum_temporal
    • minimum_operation_per_hourminimum_per_hour
    • maximum_operation_per_hourmaximum_per_hour
  • Component parameters renamed:
    • Source.sourceSource.outputs
    • Sink.sinkSink.inputs
    • SourceAndSink.sourceSourceAndSink.outputs
    • SourceAndSink.sinkSourceAndSink.inputs
    • SourceAndSink.prevent_simultaneous_sink_and_sourceSourceAndSink.prevent_simultaneous_flow_rates

🔥 Removed

  • Effect share parameters: The old specific_share_to_other_effects_* parameters were replaced WITHOUT DEPRECATION
    • specific_share_to_other_effects_operationshare_from_temporal (with inverted direction)
    • specific_share_to_other_effects_investshare_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.py to 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, updated mkdocs.yml for 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 to Interface base 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_collection pattern 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 .modeling that 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 alignment
    • fit_effects_to_model_coords() method for effect data processing
    • connect_and_transform() method replacing several operations
  • Testing improvements: Eliminated warnings during test execution
    • Updated deprecated code patterns in tests and examples (e.g., sink/sourceinputs/outputs, 'H''h' frequency)
    • Refactored plotting logic to handle test environments explicitly with non-interactive backends
    • Added comprehensive warning filters in __init__.py and pyproject.toml to suppress third-party library warnings
    • Improved test fixtures with proper figure cleanup to prevent memory leaks
    • Enhanced backend detection and handling in plotting.py for both Matplotlib and Plotly
    • Always run dependent tests in order

What's Changed

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_size and CONFIG.Logging.backup_count
  • Added configurable log format settings: CONFIG.Logging.date_format and CONFIG.Logging.format
  • Added configurable console settings: CONFIG.Logging.console_width and CONFIG.Logging.show_path
  • Added CONFIG.Logging.Colors nested 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 of CONFIG.Logging.level and CONFIG.apply(). Will be removed in version 3.0.0.

🔥 Removed

  • Removed unused config.merge_configs function from configuration module

👷 Development

  • Greatly expanded test coverage for config.py module
  • Added @pytest.mark.xdist_group to TestConfigModule tests to prevent global config interference

What's Changed

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 h5netcdf instead of netCDF4 for dataset I/O operations. This follows the update in xarray==2025.09.01

🐛 Fixed

  • Fix charge_state Constraint in Storage leading to incorrect losses in discharge and therefore incorrect charge states and discharge values.

📦 Dependencies

  • Updated renovate.config to treat CalVer packages (xarray and dask) with more care

What's Changed

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

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, and core.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_losses in Transmission is now 0 instead of None
  • Setter of COP in HeatPumpWithSource now 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 HeatPumpWithSource returning and setting wrong conversion factors.
  • Fix custom compression levels in io.save_dataset_to_netcdf
  • Fix total_max did 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

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

New Contributors

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, Source and SourceAndSink now accept multiple flows as inputs and outputs instead of just one. This enables to model more use cases using these classes. [#291 by @FBumann]
  • Further, both Sink and Source now have a prevent_simultaneous_flow_rates argument to prevent simultaneous flow rates of more than one of their Flows. [#291 by @FBumann]

Added

  • Added FlowSystem.start_network_app() and FlowSystem.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, Source and SourceAndSink: .sink, .source and .prevent_simultaneous_sink_and_source are deprecated in favor of the new arguments inputs, outputs and prevent_simultaneous_flow_rates. [#291 by @FBumann]

Fixed

  • Fixed testing issue with new linopy version 0.5.6 [#296 by @FBumann]

Energy Systems - Energy System Modeling Frameworks - Python
Published by github-actions[bot] 10 months ago

flixOpt - v2.1.5

Fixed

  • Fixed Docs deployment

Energy Systems - Energy System Modeling Frameworks - Python
Published by github-actions[bot] 11 months ago

flixOpt - v2.1.4

Fixed

  • Fixing release notes of 2.1.3, as well as documentation build.

Energy Systems - Energy System Modeling Frameworks - Python
Published by github-actions[bot] 11 months ago

flixOpt - v2.1.3

Fixed

  • Using Effect.maximum_operation_per_hour raised an error, needing an extra timestep. This has been fixed thanks to @PRse4.

Energy Systems - Energy System Modeling Frameworks - Python
Published by github-actions[bot] 11 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

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

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

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_rate when 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

New Contributors

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 Calculation has a Calculation.results attribute, 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 CalculationResults class 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

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 profile in combination with no fixed 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_min and consecutive_off_hours_min were used. Now, the initial state is only fixed if the previous value is greater than zero.

Others

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.6

Only correcting the version info in pyproject.toml

Energy Systems - Energy System Modeling Frameworks - Python
Published by FBumann over 1 year ago

flixOpt - v1.0.5

What's Changed

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.
  • length gekürzt auf len
    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

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.toml instead of setup.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.DataFrames or pd.Series objects, but instead convert them to numpy.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