Skip to content

Conversation

@FBumann
Copy link
Member

@FBumann FBumann commented Sep 13, 2025

Drop suffixes in plots and add the option to drop suffixes to sanitize_dataset()

  • Scenarios/bar plot (Scenarios/bar plot #254)

  • Add stacked bar style to plotting methods

  • Rename mode to style (line, bar, area, ...)

  • Bugfix plotting

  • Fix example_calculation_types.py

  • Scenarios/fixes (Scenarios/fixes #255)

  • Fix indexing issue with only one scenario

  • Bugfix Cooling Tower

  • Add option for balanced Storage Flows (equalize size of charging and discharging)

  • Add option for balanced Storage Flows

  • Change error to warning (non-fixed size with piecewise conversion AND fixed_flow_rate with OnOff)

  • Bugfix in DataConverter

  • BUGFIX: Typo (total_max/total_min in Effect)

  • Bugfix in node_balance() (negating did not work when using flow_hours mode

  • Scenarios/effects (Scenarios/effects #256)

  • Add methods to track effect shares of components and Flows

  • Add option to include flows when retrieving effects

  • Add properties and methods to store effect results in a dataset

  • Reorder methods

  • Rename and improve docs

  • Bugfix test class name

  • Fix the Network algorithm to calculate the sum of parallel paths, and be independent on nr of nodes and complexity of the network

  • Add tests for the newtork chaining and the results of effect shares

  • Add methods to check for circular references

  • Add test to check for circular references

  • Update cycle checker to return the found cycles

  • Add checks in results to confirm effects are computed correctly

  • BUGFIX: Remove +1 from prior testing

  • Add option for grouped bars to plotting.with_plotly() and make lines of stacked bar plots invisible

  • Reconstruct FlowSystem in CalculationResults on demand. DEPRECATION in CalculationResults

  • ruff check

  • Bugfix: save flow_system data, not the flow_system

  • Update tests

  • Scenarios/datasets results (Scenarios/datasets results #257)

  • Use dataarray instead of dataset

  • Change effects dataset to dataarray and use nan when no share was found

  • Add method for flow_rates dataset

  • Add methods to get flow_rates and flow_hours as datasets

  • Rename the dataarrays to the flow

  • Preserve index order

  • Improve filter_edges_dataset()

  • Simplify _create_flow_rates_dataarray()

  • Add dataset for sizes of Flows

  • Extend results structure to contain flows AND start/end infos

  • Add FlowResults Object

  • BUGFIX:Typo in _ElementResults.constraints

  • Add flows to results of Nodes

  • Simplify dataarray creation and improve FlowResults

  • Add nice docstrings

  • Improve filtering of flow results

  • Improve filtering of flow results. Add attribute of component

  • Add big dataarray with all variables but indexed

  • Revert "Add big dataarray with all variables but indexed"

This reverts commit 08cd8a1.

  • Improve filtering method for coords filter and add error handling for restoring the flow system

  • Remove unnecessary methods in results .from_json()

  • Ensure consistent coord ordering in Effects dataarray

  • Rename get_effects_per_component()

  • Make effects_per_component() a dataset instead of a dataarray

  • Improve backwards compatability

  • ruff check

  • ruff check

  • Scenarios/deprecation (Scenarios/deprecation #258)

  • Deprecate .active_timesteps

  • Improve logger warning

  • Starting release notes

  • Bugfix in plausibility_check: Index 0

  • Set bargap to 0 in stacked bars

  • Ensure the size is always properly indexed in results.

  • ruff check

  • BUGFIX in extract data, that causes coords in linopy to be incorrect (scalar xarray.DataArrays)

  • Improve yaml formatting for model documentation (Scenarios/model doc #259)

  • Make the size/capacity a TimeSeries (Scenarios/fix size #260)

  • Scenarios/plot network (Scenarios/plot network #262)

  • Catch bug in plot_network with 2D arrays

  • Add plot_network() to test_io.py

  • Update deploy-docs.yaml: Run on Release publishing instead of creation
    and
    only run for stable releases (vx.y.z)

  • Bugfix DataConverter and add tests (Fix DataConverter #263)

  • Fix doc deployment to not publish on non stable releases

  • Remove unused code

  • Remove legend placing for better auto placing in plotly

  • Fix plotly dependency

  • Improve validation when adding new effects

  • Moved release notes to CHANGELOG.md

  • Try to add to_dataset to Elements

  • Remove TimeSeries

  • Remove TimeSeries

  • Rename conversion method to pattern: to_...

  • Move methods to FlowSystem

  • Drop nan values across time dimension if present

  • Allow lists of values to create DataArray

  • Update resolving of FlowSystem

  • Simplify TimeSeriesData

  • Move TImeSeriesData to Structure and simplyfy to inherrit from xarray.DataArray

  • Adjust IO

  • Move TimeSeriesData back to core.py and fix Conversion

  • Adjust IO to account for attrs of DataArrays in a Dataset

  • Rename transforming and connection methods in FlowSystem

  • Compacted IO methods

  • Remove infos()

  • remove from_dict() and to_dict()

  • Update str of Interface

  • Improve str and repr

  • Improve str and repr

  • Add docstring

  • Unify IO stuff in Interface class

  • Improve test tu utilize eq method

  • Make Interface class more robust and improve exceptions

  • Add option to copy Interfaces (And the FlowSystem)

  • Make a copy of a FLowSytsem that gets reused in a second Calculation

  • Remove test_timeseries.py

  • Reorganizing Datatypes

  • Remove TImeSeries and TimeSeriesCollection entirely

  • Remove old method

  • Add option to get structure with stats of dataarrays

  • Change str method

  • Remove old methods

  • remove old imports

  • Add isel, sel and resample methods to FlowSystem

  • Remove need for timeseries with extra timestep

  • Simplify IO of FLowSystem

  • Remove parameter timesteps from IO

  • Improve Exceptions and Docstrings

  • Improve isel sel and resample methods

  • Change test

  • Bugfix

  • Improve

  • Improve

  • Add test for Storage Bounds

  • Add test for Storage Bounds

  • CHANGELOG.md

  • ruff check

  • Improve types

  • CHANGELOG.md

  • Bugfix in Storage

  • Revert changes in example_calculation_types.py

  • Revert changes in simple_example.py

  • Add convenient access to Elements in FlowSystem

  • Get Aggregated Calculation Working

  • Segmented running with wrong results

  • Use new persistent FLowSystem to create Calculations upfront

  • Improve SegmentedCalcualtion

  • Improve SegmentedCalcualtion

  • Fix SegmentedResults IO

  • ruff check

  • Update example

  • Updated logger essages to use .label_full instead of .label

  • Re-add parameters. Use deprecation warning instead

  • Update changelog

  • Improve warning message

  • Merge

  • Merge

  • Fit scenario weights to model coords when transforming

  • Merge

  • Removing logic between minimum, maximum and fixed size from InvestParameters

  • Remove selected_timesteps

  • Improve TypeHints

  • New property on InvestParameters for min/max/fixed size

  • Move logic for InvestParameters in Transmission to from Model to Interface

  • Make transformation of data more hierarchical (Flows after Components)

  • Add scenario validation

  • Change Transmission to have a "balanced" attribute. Change Tests accordingly

  • Improve index validations

  • rename method in tests

  • Update DataConverter

  • Add DataFrame Support back

  • Add copy() to DataConverter

  • Update fit_to_model_coords to take a list of coords

  • Make the DataConverter more universal by accepting a list of coords/dims

  • Update DataConverter for n-d arrays

  • Update DataConverter for n-d arrays

  • Add extra tests for 3-dims

  • Add FLowSystemDimension Type

  • Revert some logic about the fit_to_model coords

  • Adjust FLowSystem IO for scenarios

  • BUGFIX: Raise Exception instead of logging

  • Change usage of TimeSeriesData

  • Adjust logic to handle non scalars

  • Adjust logic to _resolve_dataarray_reference into separate method

  • Update IO of FlowSystem

  • Improve get_coords()

  • Adjust FlowSystem init for correct IO

  • Add scenario to sel and isel methods, and dont normalize scenario weights

  • Improve scenario_weights_handling

  • Add warning for not scaled weights

  • Update test_scenarios.py

  • Improve util method

  • Add objective to solution dataset.

  • Update handling of scenario_weights update tests

  • Ruff check. Fix type hints

  • Fix type hints and improve None handling

  • Fix coords in AggregatedCalculation

  • Improve Error Messages of DataConversion

  • Allow multi dim data conversion and broadcasting by length

  • Improve DataConverter to handle multi-dim arrays

  • Rename methods and remove unused code

  • Improve DataConverter by better splitting handling per datatype. Series only matches index (for one dim). Numpy matches shape

  • Add test for error handling

  • Update scenario example

  • Fix Handling of TimeSeriesData

  • Improve DataConverter

  • Fix resampling of the FlowSystem

  • Improve Warning Message

  • Add example that leverages resampling

  • Add example that leverages resampling adn fixing of Investments

  • Add flag to Calculation if its modeled

  • Make flag for connected_and_transformed FLowSystem public

  • Make Calcualtion Methods return themselfes to make them chainable

  • Improve example

  • Improve Unreleased CHANGELOG.md

  • Add year coord to FlowSystem

  • Improve dimension handling

  • Change plotting to use an indexer instead

  • Change plotting to use an indexer instead

  • Use tuples to set dimensions in Models

  • Bugfix in validation logic and test

  • Improve Errors

  • Improve weights handling and rescaling if None

  • Fix typehint

  • Update Broadcasting in Storage Bounds and improve type hints

  • Make .get_model_coords() return an actual xr.Coordinates Object

  • Improve get_coords()

  • Rename SystemModel to FlowSystemModel

  • First steps

  • Improve Feature Patterns

  • Improve acess to variables via short names

  • Improve

  • Add naming options to big_m_binary_bounds()

  • Fix and improve FLowModeling with Investment

  • Improve

  • Tyring to improve the Methods for bounding variables in different scenarios

  • Improve BoundingPatterns

  • Improve BoundingPatterns

  • Improve BoundingPatterns

  • Fix duration Modeling

  • Fix On + Size

  • Fix InvestmentModel

  • Fix Models

  • Update constraint names in test

  • Fix OnOffModel for multiple Flows

  • Update constraint names in tests

  • Simplify

  • Improve handling of vars/cons and models

  • Revising the basic structure of a class Model

  • Revising the basic structure of a class Model

  • Simplify and focus more on own Model class

  • Update tests

  • Improve state computation in ModelingUtilities

  • Improve handling of previous flowrates

  • Imropove repr and submodel acess

  • Update access pattern in tests

  • Fix PiecewiseEffects and StorageModel

  • Fix StorageModel and Remove PreventSimultaniousUseModel

  • Fix Aggregation and SegmentedCalculation

  • Update tests

  • Loosen precision in tests

  • Update test_on_hours_computation.py and some types

  • Rename class Model to Submodel

  • rename sub_model to submodel everywhere

  • rename self.model to self.submodel everywhere

  • Rename .model with .submodel if its only a submodel

  • Rename .sub_models with .submodels

  • Improve repr

  • Improve repr

  • Include def do_modeling() into init() of models

  • Make properties private

  • Improve Inheritance of Models

  • V3.0.0/plotting (V3.0.0/plotting #285)

  • Use indexer to reliably plot solutions with and wihtout scenarios/years

  • ruff check

  • Improve typehints

  • Update CHANGELOG.md

  • Bugfix from renaming to .submodel

  • Bugfix from renaming to .submodel

  • Improve indexer in results plotting

  • rename register_submodel() to .add_submodels() adn add SUbmodels collection class

  • Add nice repr to FlowSystemModel and Submodel

  • Bugfix .variables and .constraints

  • Add type checks to modeling.py

  • Improve assertion in tests

  • Improve docstrings and register ElementModels directly in FlowSystemModel

  • Improve repr()

  • ruff check

  • Use new method to compare sets in tests

  • ruff check

  • Update Contribute.md, some dependencies and add pre-commit

  • Pre commit hook

  • Run Pre-Commit Hook for the first time

  • Fix link in README.md

  • Update Effect name in tests to be 'costs' instead of 'Costs' Everywhere Simplify testing by creating a Element Library

  • Improve some of the modeling and coord handling

  • Add tests with years and scenarios

  • Update tests to run with multiple coords

  • Fix Effects dataset computation in case of empty effects

  • Update Test for multiple dims Fix Dim order in scaled_bounds_with_state
    Bugfix logic in .use_switch_on

  • Fix test with multiple dims

  • Fix test with multiple dims

  • New test

  • New test for previous flow_rates

  • V3.0.0/main fit to model coords improve (V3.0.0/main fit to model coords improve #295)

  • Change fit_to_model_coords to work with a Collection of dims

  • Improve fit_to_model_coords

  • Improve CHANGELOG.md

Description

Brief description of the changes in this PR.

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Code refactoring

Related Issues

Closes #(issue number)

Testing

  • I have tested my changes
  • Existing tests still pass

Checklist

  • My code follows the project style
  • I have updated documentation if needed
  • I have added tests for new functionality (if applicable)

Summary by CodeRabbit

  • New Features

    • Scenarios and Years (investment periods), grouped/scenario-weighted modeling, new FlowResults, richer results filtering/plotting (indexers, grouped/stacked bars), dataset/JSON I/O, two-stage optimization and scenarios examples.
  • Breaking Changes

    • Class/term renames (e.g., SystemModel→FlowSystemModel, Model→Submodel), plotting mode→style, TimeSeries→TimeSeriesData, Calculation.do_modeling returns the Calculation, storage final-state parameter added, aggregation args renamed.
  • Improvements

    • Better data alignment/transforms, lazy FlowSystem restoration, balanced sizing options, clearer representations, color handling, serialization.
  • Bug Fixes

    • NetCDF attribute preservation, sturdier error handling and type consistency.
  • Documentation

    • New Investments math guide; examples updated.

* Bugfix plot_node_balance_pie()

* Scenarios/fixes (#252)

* BUGFIX missing conversion to TimeSeries

* BUGFIX missing conversion to TimeSeries

* Bugfix node_balance with flow_hours: Negate correctly

* Scenarios/filter (#253)

* Add containts and startswith to filter_solution

* Scenarios/drop suffix (#251)

Drop suffixes in plots and add the option to drop suffixes to sanitize_dataset()

* Scenarios/bar plot (#254)

* Add stacked bar style to plotting methods
* Rename mode to style  (line, bar, area, ...)

* Bugfix plotting

* Fix example_calculation_types.py

* Scenarios/fixes (#255)

* Fix indexing issue with only one scenario

* Bugfix Cooling Tower

* Add option for balanced Storage Flows (equalize size of charging and discharging)

* Add option for balanced Storage Flows

* Change error to warning (non-fixed size with piecewise conversion AND fixed_flow_rate with OnOff)

* Bugfix in DataConverter

* BUGFIX: Typo (total_max/total_min in Effect)

* Bugfix in node_balance() (negating did not work when using flow_hours mode

* Scenarios/effects (#256)

* Add methods to track effect shares of components and Flows

* Add option to include flows when retrieving effects

* Add properties and methods to store effect results in a dataset

* Reorder methods

* Rename and improve docs

* Bugfix test class name

* Fix the Network algorithm to calculate the sum of parallel paths, and be independent on nr of nodes and complexity of the network

* Add tests for the newtork chaining and the results of effect shares

* Add methods to check for circular references

* Add test to check for circular references

* Update cycle checker to return the found cycles

* Add checks in results to confirm effects are computed correctly

* BUGFIX: Remove +1 from prior testing

* Add option for grouped bars to plotting.with_plotly() and make lines of stacked bar plots invisible

* Reconstruct FlowSystem in CalculationResults on demand. DEPRECATION in CalculationResults

* ruff check

* Bugfix: save flow_system data, not the flow_system

* Update tests

* Scenarios/datasets results (#257)

* Use dataarray instead of dataset

* Change effects dataset to dataarray and use nan when no share was found

* Add method for flow_rates dataset

* Add methods to get flow_rates and flow_hours as datasets

* Rename the dataarrays to the flow

* Preserve index order

* Improve filter_edges_dataset()

* Simplify _create_flow_rates_dataarray()

* Add dataset for sizes of Flows

* Extend results structure to contain flows AND start/end infos

* Add FlowResults Object

* BUGFIX:Typo in _ElementResults.constraints

* Add flows to results of Nodes

* Simplify dataarray creation and improve FlowResults

* Add nice docstrings

* Improve filtering of flow results

* Improve filtering of flow results. Add attribute of component

* Add big dataarray with all variables but indexed

* Revert "Add big dataarray with all variables but indexed"

This reverts commit 08cd8a1.

* Improve filtering method for coords filter and add error handling for restoring the flow system

* Remove unnecessary methods in results .from_json()

* Ensure consistent coord ordering in Effects dataarray

* Rename get_effects_per_component()

* Make effects_per_component() a dataset instead of a dataarray

* Improve backwards compatability

* ruff check

* ruff check

* Scenarios/deprecation (#258)

* Deprecate .active_timesteps

* Improve logger warning

* Starting release notes

* Bugfix in plausibility_check: Index 0

* Set bargap to 0 in stacked bars

* Ensure the size is always properly indexed in results.

* ruff check

* BUGFIX in extract data, that causes coords in linopy to be incorrect (scalar xarray.DataArrays)

* Improve yaml formatting for model documentation (#259)

* Make the size/capacity a TimeSeries (#260)

* Scenarios/plot network (#262)

* Catch bug in plot_network with 2D arrays

* Add plot_network() to test_io.py

* Update deploy-docs.yaml:
Run on Release publishing instead of creation
and
only run for stable releases (vx.y.z)

* Bugfix DataConverter and add tests (#263)

* Fix doc deployment to not publish on non stable releases

* Remove unused code

* Remove legend placing for better auto placing in plotly

* Fix plotly dependency

* Improve validation when adding new effects

* Moved release notes to CHANGELOG.md

* Try to add to_dataset to Elements

* Remove TimeSeries

* Remove TimeSeries

* Rename conversion method to pattern: to_...

* Move methods to FlowSystem

* Drop nan values across time dimension if present

* Allow lists of values to create DataArray

* Update resolving of FlowSystem

* Simplify TimeSeriesData

* Move TImeSeriesData to Structure and simplyfy to inherrit from xarray.DataArray

* Adjust IO

* Move TimeSeriesData back to core.py and fix Conversion

* Adjust IO to account for attrs of DataArrays in a Dataset

* Rename transforming and connection methods in FlowSystem

* Compacted IO methods

* Remove infos()

* remove from_dict() and to_dict()

* Update __str__ of Interface

* Improve str and repr

* Improve str and repr

* Add docstring

* Unify IO stuff in Interface class

* Improve test tu utilize __eq__ method

* Make Interface class more robust and improve exceptions

* Add option to copy Interfaces (And the FlowSystem)

* Make a copy of a FLowSytsem that gets reused in a second Calculation

* Remove test_timeseries.py

* Reorganizing Datatypes

* Remove TImeSeries and TimeSeriesCollection entirely

* Remove old method

* Add option to get structure with stats of dataarrays

* Change __str__ method

* Remove old methods

* remove old imports

* Add isel, sel and resample methods to FlowSystem

* Remove need for timeseries with extra timestep

* Simplify IO of FLowSystem

* Remove parameter timesteps from IO

* Improve Exceptions and Docstrings

* Improve isel sel and resample methods

* Change test

* Bugfix

* Improve

* Improve

* Add test for Storage Bounds

* Add test for Storage Bounds

* CHANGELOG.md

* ruff check

* Improve types

* CHANGELOG.md

* Bugfix in Storage

* Revert changes in example_calculation_types.py

* Revert changes in simple_example.py

* Add convenient access to Elements in FlowSystem

* Get Aggregated Calculation Working

* Segmented running with wrong results

* Use new persistent FLowSystem to create Calculations upfront

* Improve SegmentedCalcualtion

* Improve SegmentedCalcualtion

* Fix SegmentedResults IO

* ruff check

* Update example

* Updated logger essages to use .label_full instead of .label

* Re-add parameters. Use deprecation warning instead

* Update changelog

* Improve warning message

* Merge

* Merge

* Fit scenario weights to model coords when transforming

* Merge

* Removing logic between minimum, maximum and fixed size from InvestParameters

* Remove selected_timesteps

* Improve TypeHints

* New property on InvestParameters for min/max/fixed size

* Move logic for InvestParameters in Transmission to from Model to Interface

* Make transformation of data more hierarchical (Flows after Components)

* Add scenario validation

* Change Transmission to have a "balanced" attribute. Change Tests accordingly

* Improve index validations

* rename method in tests

* Update DataConverter

* Add DataFrame Support back

* Add copy() to DataConverter

* Update fit_to_model_coords to take a list of coords

* Make the DataConverter more universal by accepting a list of coords/dims

* Update DataConverter for n-d arrays

* Update DataConverter for n-d arrays

* Add extra tests for 3-dims

* Add FLowSystemDimension Type

* Revert some logic about the fit_to_model coords

* Adjust FLowSystem IO for scenarios

* BUGFIX: Raise Exception instead of logging

* Change usage of TimeSeriesData

* Adjust logic to handle non scalars

* Adjust logic to _resolve_dataarray_reference into separate method

* Update IO of FlowSystem

* Improve get_coords()

* Adjust FlowSystem init for correct IO

* Add scenario to sel and isel methods, and dont normalize scenario weights

* Improve scenario_weights_handling

* Add warning for not scaled weights

* Update test_scenarios.py

* Improve util method

* Add objective to solution dataset.

* Update handling of scenario_weights update tests

* Ruff check. Fix type hints

* Fix type hints and improve None handling

* Fix coords in AggregatedCalculation

* Improve Error Messages of DataConversion

* Allow multi dim data conversion and broadcasting by length

* Improve DataConverter to handle multi-dim arrays

* Rename methods and remove unused code

* Improve DataConverter by better splitting handling per datatype. Series only matches index (for one dim). Numpy matches shape

* Add test for error handling

* Update scenario example

* Fix Handling of TimeSeriesData

* Improve DataConverter

* Fix resampling of the FlowSystem

* Improve Warning Message

* Add example that leverages resampling

* Add example that leverages resampling adn fixing of Investments

* Add flag to Calculation if its modeled

* Make flag for connected_and_transformed FLowSystem public

* Make Calcualtion Methods return themselfes to make them chainable

* Improve example

* Improve Unreleased CHANGELOG.md

* Add year coord to FlowSystem

* Improve dimension handling

* Change plotting to use an indexer instead

* Change plotting to use an indexer instead

* Use tuples to set dimensions in Models

* Bugfix in validation logic and test

* Improve Errors

* Improve weights handling and rescaling if None

* Fix typehint

* Update Broadcasting in Storage Bounds and improve type hints

* Make .get_model_coords() return an actual xr.Coordinates Object

* Improve get_coords()

* Rename SystemModel to FlowSystemModel

* First steps

* Improve Feature Patterns

* Improve acess to variables via short names

* Improve

* Add naming options to big_m_binary_bounds()

* Fix and improve FLowModeling with Investment

* Improve

* Tyring to improve the Methods for bounding variables in different scenarios

* Improve BoundingPatterns

* Improve BoundingPatterns

* Improve BoundingPatterns

* Fix duration Modeling

* Fix On + Size

* Fix InvestmentModel

* Fix Models

* Update constraint names in test

* Fix OnOffModel for multiple Flows

* Update constraint names in tests

* Simplify

* Improve handling of vars/cons and models

* Revising the basic structure of a class Model

* Revising the basic structure of a class Model

* Simplify and focus more on own Model class

* Update tests

* Improve state computation in ModelingUtilities

* Improve handling of previous flowrates

* Imropove repr and submodel acess

* Update access pattern in tests

* Fix PiecewiseEffects and StorageModel

* Fix StorageModel and Remove PreventSimultaniousUseModel

* Fix Aggregation and SegmentedCalculation

* Update tests

* Loosen precision in tests

* Update test_on_hours_computation.py and some types

* Rename class Model to Submodel

* rename sub_model to submodel everywhere

* rename self.model to self.submodel everywhere

* Rename .model with .submodel if its only a submodel

* Rename .sub_models with .submodels

* Improve repr

* Improve repr

* Include def  do_modeling() into __init__() of models

* Make properties private

* Improve Inheritance of Models

* V3.0.0/plotting (#285)

* Use indexer to reliably plot solutions with and wihtout scenarios/years

* ruff check

* Improve typehints

* Update CHANGELOG.md

* Bugfix from renaming to .submodel

* Bugfix from renaming to .submodel

* Improve indexer in results plotting

* rename register_submodel() to .add_submodels() adn add SUbmodels collection class

* Add nice repr to FlowSystemModel and Submodel

* Bugfix .variables and .constraints

* Add type checks to modeling.py

* Improve assertion in tests

* Improve docstrings and register ElementModels directly in FlowSystemModel

* Improve __repr__()

* ruff check

* Use new method to compare sets in tests

* ruff check

* Update Contribute.md, some dependencies and add pre-commit

* Pre commit hook

* Run Pre-Commit Hook for the first time

* Fix link in README.md

* Update Effect name in tests to be 'costs' instead of 'Costs' Everywhere
Simplify testing by creating a Element Library

* Improve some of the modeling and coord handling

* Add tests with years and scenarios

* Update tests to run with multiple coords

* Fix Effects dataset computation in case of empty effects

* Update Test for multiple dims
Fix Dim order in scaled_bounds_with_state
Bugfix logic in .use_switch_on

* Fix test with multiple dims

* Fix test with multiple dims

* New test

* New test for previous flow_rates

* V3.0.0/main fit to model coords improve (#295)

* Change fit_to_model_coords to work with a Collection of dims

* Improve fit_to_model_coords

* Improve CHANGELOG.md
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Massive refactor introduces FlowSystemModel/Submodel architecture, temporal vs non-temporal data types, new modeling utilities, and scenario/year dimensions. Core APIs, components, effects, features, and results handling are overhauled. Plotting switches mode→style. Calculation flows (full/aggregated/segmented) and IO/serialization are rewritten. Extensive tests and examples updated; new scenario and two‑stage examples added.

Changes

Cohort / File(s) Summary
Architecture & Structure
flixopt/structure.py, flixopt/flow_system.py, flixopt/interface.py
Replaces SystemModel/Model with FlowSystemModel/Submodel, adds submodel management, serialization (to/from dataset/json), coords handling (time/year/scenario), element registration via submodels, and richer repr/IO; Interface gains dataset-centric (de)serialization.
Core Data & Types
flixopt/core.py, flixopt/utils.py
Introduces TimeSeriesData (xr.DataArray-based), temporal/non-temporal type aliases, data conversion/broadcasting utilities, stats helpers, drop_constant_arrays; removes legacy TimeSeries; adds round_nested_floats; deprecates old agg_* aliases via warnings.
Modeling Primitives
flixopt/modeling.py
New module with ModelingUtilities, ModelingPrimitives, and BoundingPatterns for binary state, durations, constraints, and bounds; integrates with Submodel and project types.
Elements & Components
flixopt/elements.py, flixopt/components.py, flixopt/linear_converters.py
Migrates create_model to FlowSystemModel; switches to TemporalDataUser/NonTemporalDataUser; submodel accessors (flow_rate, investment, on_off); storage gains final-state bounds and balanced option; converters updated (types, piecewise handling).
Features (On/Off, Investment, Piecewise, Shares)
flixopt/features.py
Replaces legacy state models with OnOffModel; all features become Submodels; factory-style construction, label_of_model usage; add ShareAllocationModel with dims; piece/piecewise models refactored.
Effects System
flixopt/effects.py
Effects migrated to Submodel; temporal/non-temporal split; cross-effect shares via paths; add cycle detection and conversion-path utilities; updated objectives and dims.
Calculation & Aggregation
flixopt/calculation.py, flixopt/aggregation.py
Calculations return self; model type is FlowSystemModel; deprecate active_timesteps; segmented pipeline reworked to per-segment FullCalculations; aggregated pipeline uses datasets and new weight computation; AggregationModel inherits Submodel and uses component.submodel.
Results & IO
flixopt/results.py, flixopt/io.py
Results store flow_system_data (dataset) and lazily restore FlowSystem; add FlowResults; expand filtering/indexing and plotting indexer; NetCDF saves/restores attrs as JSON; YAML normalization improved; remove TimeSeries-specific helpers.
Plotting API
flixopt/plotting.py, tests/test_plots.py
Public parameter mode→style; add grouped_bar (Plotly); color processor returns mapping; update tests to 'stacked_bar'.
Documentation & Examples
CHANGELOG.md, docs/user-guide/Mathematical Notation/Investment.md, examples/*
Changelog notes breaking changes; new Investment math doc; scenario and two‑stage example scripts; example updates for plotting style and new storage final-state parameter; constructor/arg renames in examples.
Tests – Submodel/Coords Migration
tests/test_bus.py, tests/test_component.py, tests/test_flow.py, tests/test_storage.py, tests/test_linear_converter.py, tests/test_effect.py, tests/test_functional.py, tests/test_integration.py
Update assertions to use submodel, coords via model.get_coords; rename variables/constraints; add transmission/storage/on-off/previous-state scenarios; remove some legacy tests; segmented I/O test added.
Tests – Data & Utilities
tests/test_dataconverter.py, tests/test_on_hours_computation.py, tests/test_effects_shares_summation.py, tests/test_cycle_detection.py, tests/test_io.py, tests/run_all_tests.py, tests/conftest.py, tests/test_timeseries.py (removed)
New suites for broadcasting, cycle detection, conversion paths; switch to ModelingUtilities; expand IO tests (to_json/dataset); conftest introduces building blocks and FlowSystemModel; switch default runner to integration; remove TimeSeries tests.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant FS as FlowSystem
  participant FSM as FlowSystemModel
  participant SMs as Submodels (Flows/Components/Buses/Effects)
  participant Calc as Calculation
  participant Solver as LP Solver
  participant Res as CalculationResults

  User->>FS: define timesteps/years/scenarios + elements
  FS->>FS: connect_and_transform()
  User->>Calc: create FullCalculation(FS)
  Calc->>FS: create_model()
  FS-->>FSM: FlowSystemModel
  FSM->>SMs: add_submodels(create_model on elements)
  User->>Calc: do_modeling()
  Calc-->>Calc: returns self (fluent)
  User->>Calc: solve(Solver)
  Calc->>Solver: optimize(FSM)
  Solver-->>Calc: solution
  Calc->>Res: build from solution + flow_system_data
  Res-->>User: access components/flows/effects (lazy FS restore)
Loading
sequenceDiagram
  autonumber
  participant CalcS as SegmentedCalculation
  participant FS as FlowSystem
  participant Seg as FullCalculation (per segment)
  participant Solver as LP Solver

  CalcS->>FS: segment timesteps
  loop for each segment
    CalcS->>Seg: create sub-calculation
    Seg->>Seg: do_modeling()
    Seg->>Solver: solve()
    Seg-->>CalcS: results + end state
    CalcS->>CalcS: propagate start values
  end
  CalcS-->>CalcS: aggregate results
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Possibly related PRs

  • V3.0.0/main #284 — Implements the same FlowSystemModel/Submodel refactor, introduces flixopt.modeling, and updates Calculation/FlowSystem APIs; overlaps extensively with this PR’s architectural changes.

Poem

I hop through models, ears held high,
New dims of time and scenes go by.
Submodels bloom where Systems were,
Flows now sing in tidy fur.
Stack my bars, then solve the run—
Results restored, the carrots won! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description currently contains a long commit/change log which documents many edits but does not complete the repository's required template fields: the "Type of Change" checkboxes are not selected, "Related Issues" is empty, and the "Testing" section does not state what was run or the CI status. While the commit log is useful, the template requires a concise top-level summary, explicit change type, linked issue(s), and clear testing/verification information to be considered complete. Edit the PR description to follow the provided template: add a brief one-paragraph "Description" summarizing the primary changes and user-facing impacts, check the appropriate "Type of Change" box(es), list related issue numbers in "Related Issues", and fill "Testing" with concrete information (which tests were run, CI status, and any known test gaps). Also ensure the Checklist items accurately reflect documentation and test coverage and include a short migration note for the breaking API changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 67.21% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title "V3.0.0/main (#284)" is just a release tag/branch and PR metadata and does not describe the primary technical changes; it is too terse and therefore unclear to a reviewer scanning history about what the PR actually does (major API refactor, modelling/IO overhaul, plotting and results changes). Because it does not meaningfully summarize the main change, the title is inconclusive against the repository’s preferred single-sentence descriptive guideline. Please replace the title with a short, descriptive sentence that highlights the main change (for example: "v3.0.0 — FlowSystemModel/Submodel refactor; modelling, IO and results overhaul, and plotting API updates"). Keep it concise, avoid listing files or PR numbers, and ensure it reflects the top-level user-facing impact (breaking API changes, release version, or major feature).

Comment @coderabbitai help to get the list of available commands and usage tips.

@FBumann
Copy link
Member Author

FBumann commented Sep 13, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 13, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 41

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
flixopt/core.py (1)

456-470: drop_constant_arrays: boolean comparison on DataArray and “variance” mismatch

  • da.max(dim) == da.min(dim) yields a DataArray; truthiness is ambiguous and will error for multi-d arrays.
  • Docstring says “near-constant” but code checks strict equality.

Apply:

-def drop_constant_arrays(ds: xr.Dataset, dim='time', drop_arrays_without_dim: bool = True):
-    """Drop variables with very low variance (near-constant)."""
+def drop_constant_arrays(ds: xr.Dataset, dim='time', drop_arrays_without_dim: bool = True, atol: float = 0.0):
+    """Drop variables that are (near-)constant along `dim` (tolerance via `atol`)."""
     drop_vars = []

     for name, da in ds.data_vars.items():
         if dim in da.dims:
-            if da.max(dim) == da.min(dim):
-                drop_vars.append(name)
+            spread = (da.max(dim=dim) - da.min(dim=dim)).max()
+            try:
+                is_constant = float(spread.values) <= atol
+            except Exception:
+                is_constant = False
+            if is_constant:
+                drop_vars.append(name)
             continue
         elif drop_arrays_without_dim:
             drop_vars.append(name)

-    logger.debug(f'Dropping {len(drop_vars)} arrays with constant values')
+    logger.debug(f'Dropping {len(drop_vars)} arrays constant along {dim} (atol={atol})')
     return ds.drop_vars(drop_vars)
examples/03_Calculation_types/example_calculation_types.py (2)

2-3: Fix typos in the module docstring.

Spelling/casing issues in the first sentence.

Apply:

-This script demonstrates how to use the different calcualtion types in the flixOPt framework
-to model the same energy system. THe Results will be compared to each other.
+This script demonstrates how to use the different calculation types in the flixopt framework
+to model the same energy system. The results will be compared to each other.

140-144: Inconsistent effect keys: use CO2.label instead of the object.

Passing the Effect object as a dict key can break mapping logic; align with costs.label.

-        source=fx.Flow(
-            'P_el', bus='Strom', size=1000, effects_per_flow_hour={costs.label: TS_electricity_price_buy, CO2: 0.3}
-        ),
+        source=fx.Flow(
+            'P_el',
+            bus='Strom',
+            size=1000,
+            effects_per_flow_hour={costs.label: TS_electricity_price_buy, CO2.label: 0.3},
+        ),
flixopt/plotting.py (2)

515-536: Outdated docstring parameters (show/save/path) not in signature.

Docstring mentions args that aren’t part of heat_map_plotly’s signature. Please remove or reintroduce them to match behavior.

-        show: Wether to show the figure after creation. (This includes saving the figure)
-        save: Wether to save the figure after creation (without showing)
-        path: Path to save the figure.

641-654: strftime bug and resampling robustness in heat_map_data_from_df.

  • '%H:%MM' is invalid; should be '%H:%M'.
  • diff().min() can include NaT; drop NaT before min to avoid AttributeError on total_seconds().
  • Stray no-op line.
-    formats = {
+    formats = {
         ('YS', 'W'): ('%Y', '%W'),
         ('YS', 'D'): ('%Y', '%j'),  # day of year
         ('YS', 'h'): ('%Y', '%j %H:00'),
         ('MS', 'D'): ('%Y-%m', '%d'),  # day of month
         ('MS', 'h'): ('%Y-%m', '%d %H:00'),
         ('W', 'D'): ('%Y-w%W', '%w_%A'),  # week and day of week (with prefix for proper sorting)
         ('W', 'h'): ('%Y-w%W', '%w_%A %H:00'),
         ('D', 'h'): ('%Y-%m-%d', '%H:00'),  # Day and hour
-        ('D', '15min'): ('%Y-%m-%d', '%H:%MM'),  # Day and hour
+        ('D', '15min'): ('%Y-%m-%d', '%H:%M'),  # hour:minute within day
         ('h', '15min'): ('%Y-%m-%d %H:00', '%M'),  # minute of hour
         ('h', 'min'): ('%Y-%m-%d %H:00', '%M'),  # minute of hour
     }
-
-    minimum_time_diff_in_min = df.index.to_series().diff().min().total_seconds() / 60  # Smallest time_diff in minutes
+    minimum_time_diff_in_min = (
+        df.index.to_series().diff().dropna().min().total_seconds() / 60
+    )  # Smallest time_diff in minutes
@@
-    if time_intervals[steps_per_period] > minimum_time_diff_in_min:
-        time_intervals[steps_per_period]
+    if time_intervals[steps_per_period] > minimum_time_diff_in_min:
flixopt/io.py (1)

200-213: NetCDF compression param not honored; extension validation OK.

You gate compression on presence of netCDF4 (good), but then hardcode complevel=5 and ignore the compression argument value. Use it to control level.

-    ds.to_netcdf(
+    ds.to_netcdf(
         path,
-        encoding=None
-        if not apply_encoding
-        else {data_var: {'zlib': True, 'complevel': 5} for data_var in ds.data_vars},
+        encoding=(
+            None
+            if not apply_encoding
+            else {name: {'zlib': True, 'complevel': compression} for name in ds.data_vars}
+        ),
     )
🧹 Nitpick comments (36)
docs/user-guide/Mathematical Notation/Investment.md (1)

107-107: Consider tracking incremental investment size constraints

The document mentions restricting size increases/decreases per scenario, but doesn't provide detailed mathematical formulations for these constraints. This could be important for modeling realistic investment pathways.

Would you like me to generate the mathematical formulations for incremental size constraints between scenarios? This could include constraints like:

  • Maximum size increase per scenario: $V(s+1) - V(s) \leq \Delta V_{max}$
  • Minimum size increase when expanding: $V(s+1) - V(s) \geq \Delta V_{min} \cdot \beta_{expand}(s)$
CHANGELOG.md (2)

23-23: Remove trailing whitespace

Line 23 contains only a single asterisk with trailing whitespace, which appears to be a formatting artifact.

-*

11-12: Document the Storage API change clearly

The breaking change for Storage charge state parameters should clarify that this affects both relative_minimum_charge_state and relative_maximum_charge_state arrays, which now have one less element.

-* **BREAKING**: `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. The final charge state can now be constrained by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead
+* **BREAKING**: `relative_minimum_charge_state` and `relative_maximum_charge_state` arrays now require one less element (no longer include final timestep). The final charge state should now be constrained using the new parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state`
flixopt/core.py (2)

30-31: Docstring contradicts type alias

NonTemporalData is aliased to xr.DataArray, but the docstring claims it can be a Scalar. Clarify.

Apply:

-"""Internally used datatypes for non-temporal data. Can be a Scalar or an xr.DataArray."""
+"""Internally used datatype for non-temporal data: an xr.DataArray without a 'time' dimension."""

49-53: Consider xarray accessor instead of subclassing DataArray

Subclassing xr.DataArray is fragile; most ops return plain DataArray, dropping methods. An accessor (e.g., @xr.register_dataarray_accessor('ts')) keeps APIs available and metadata centralized in attrs.

tests/run_all_tests.py (1)

10-10: Make test path robust regardless of CWD

Hardcoding 'test_integration.py' can fail when run from repo root. Resolve relative to this file.

Apply:

-    pytest.main(['test_integration.py', '--disable-warnings'])
+    from pathlib import Path
+    test_file = Path(__file__).with_name('test_integration.py')
+    pytest.main([str(test_file), '--disable-warnings'])
tests/test_plots.py (1)

56-57: Switch looks good; avoid browser pop-ups in CI

Style rename to 'stacked_bar' is fine. Also set auto_open=False to keep tests non-interactive.

Apply:

-        plotly.offline.plot(plotting.with_plotly(data, 'stacked_bar'))
+        plotly.offline.plot(plotting.with_plotly(data, 'stacked_bar'), auto_open=False)
         plotting.with_matplotlib(data, 'stacked_bar')
...
-        plotly.offline.plot(plotting.with_plotly(data, 'stacked_bar'))
+        plotly.offline.plot(plotting.with_plotly(data, 'stacked_bar'), auto_open=False)
         plotting.with_matplotlib(data, 'stacked_bar')

Also applies to: 63-64

tests/test_results_plots.py (1)

62-62: Pass show/save for consistency

Other plot calls are gated by save/show; align plot_charge_state to avoid unintended UI actions.

Apply:

-    results['Speicher'].plot_charge_state(engine=plotting_engine)
+    results['Speicher'].plot_charge_state(engine=plotting_engine, save=save, show=show)
flixopt/utils.py (1)

50-53: Consider using numpy's rounding for consistency

The function uses Python's built-in round() for floats but np.round() for numpy arrays. This could lead to subtle differences in rounding behavior due to different tie-breaking rules (Python uses "round half to even" while numpy can be configured).

For consistency, consider using numpy's rounding for all numeric types:

 elif isinstance(obj, float):
-    return round(obj, decimals)
+    return float(np.round(obj, decimals))
 elif isinstance(obj, int):
     return obj
 elif isinstance(obj, np.ndarray):
     return np.round(obj, decimals).tolist()
 elif isinstance(obj, xr.DataArray):
-    return obj.round(decimals).values.tolist()
+    return np.round(obj.values, decimals).tolist()
tests/test_scenarios.py (2)

10-12: Remove unnecessary imports

The matplotlib.pyplot and xarray imports are not used in the test code.

-import matplotlib.pyplot as plt
 import numpy as np
 import pandas as pd
 import pytest
-import xarray as xr
 from linopy.testing import assert_linequal

291-313: Consider enabling the skipped IO persistence test

The test is skipped due to runtime and license constraints. Since this test validates critical IO persistence functionality, consider creating a lighter-weight version that can run in CI.

You could create a simpler flow system specifically for IO testing that solves quickly even with free solvers:

@pytest.mark.slow  # Mark as slow but don't skip
def test_io_persistence_lightweight():
    """Lightweight version of IO persistence test."""
    # Create a minimal flow system with just 2 scenarios and 3 timesteps
    flow_system = create_minimal_scenario_system()
    # Use lower mip_gap for faster solving
    calc = create_calculation_and_solve(
        flow_system,
        solver=fx.solvers.HighsSolver(mip_gap=0.01, time_limit_seconds=30),
        name='test_io_lightweight',
    )
    # Rest of the test logic...
tests/test_effects_shares_summation.py (1)

195-198: Fix variable reuse in path calculation

Line 195 calculates expected_a_to_n for I->N paths, but then line 196 uses += to add J->N paths to the same variable. This makes the calculation harder to follow and could lead to errors if the order changes.

Consider using separate variables for clarity:

-    expected_a_to_n = 1.5 * 3.0 * 0.7 * 2.3 + 2.0 * 0.8 * 0.7 * 2.3
-    expected_a_to_n += 1.5 * 3.0 * 1.4 * 1.9 + 2.0 * 0.8 * 1.4 * 1.9  # A->B->E->J->N + A->C->E->J->N
-    expected_a_to_n += 1.5 * 1.2 * 2.2 * 1.9  # A->B->F->J->N
+    # Paths through I->N
+    paths_through_i = 1.5 * 3.0 * 0.7 * 2.3 + 2.0 * 0.8 * 0.7 * 2.3
+    # Paths through J->N
+    paths_through_j = (1.5 * 3.0 * 1.4 * 1.9 + 2.0 * 0.8 * 1.4 * 1.9 + 
+                       1.5 * 1.2 * 2.2 * 1.9)
+    expected_a_to_n = paths_through_i + paths_through_j
tests/test_io.py (1)

40-41: Remove or conditionally execute plot_network calls

The plot_network() calls appear to be debug artifacts that will generate plots during test execution. This could slow down tests and create unwanted output in CI environments.

Consider removing these lines or making them conditional:

-    calculation_0.flow_system.plot_network()
+    # calculation_0.flow_system.plot_network()  # Uncomment for debugging

     calculation_0.results.to_file()
     paths = CalculationResultsPaths(calculation_0.folder, calculation_0.name)
     flow_system_1 = fx.FlowSystem.from_netcdf(paths.flow_system)

     calculation_1 = fx.FullCalculation('Loaded_IO', flow_system=flow_system_1)
     calculation_1.do_modeling()
     calculation_1.solve(highs_solver)
-    calculation_1.flow_system.plot_network()
+    # calculation_1.flow_system.plot_network()  # Uncomment for debugging

Also applies to: 49-49

examples/03_Calculation_types/example_calculation_types.py (1)

36-38: Normalize the slice upper bound datetime.

Minor readability nit; current string parses but is unconventional.

-filtered_data = data_import['2020-01-01':'2020-01-2 23:45:00']
+filtered_data = data_import['2020-01-01':'2020-01-02 23:45:00']
examples/05_Two-stage-optimization/two_stage_optimization.py (2)

127-131: Fix logging/error messages (were → were).

Minor polish.

-    if (calculation_dispatch.results.sizes().round(5) == calculation_sizing.results.sizes().round(5)).all():
-        logger.info('Sizes where correctly equalized')
+    if (calculation_dispatch.results.sizes().round(5) == calculation_sizing.results.sizes().round(5)).all():
+        logger.info('Sizes were correctly equalized')
     else:
-        raise RuntimeError('Sizes where not correctly equalized')
+        raise RuntimeError('Sizes were not correctly equalized')

134-137: Name the combined run distinctly for clarity.

Prevents confusion in artifacts/logs.

-calculation_combined = fx.FullCalculation('Sizing', flow_system)
+calculation_combined = fx.FullCalculation('Combined', flow_system)
tests/test_bus.py (1)

52-57: Avoid mutating the model in assertions.

model.add_variables(...) alters the model; assert shape/coords instead.

-        assert_var_equal(
-            model.variables['TestBus|excess_input'], model.add_variables(lower=0, coords=model.get_coords())
-        )
-        assert_var_equal(
-            model.variables['TestBus|excess_output'], model.add_variables(lower=0, coords=model.get_coords())
-        )
+        var_in = model.variables['TestBus|excess_input']
+        var_out = model.variables['TestBus|excess_output']
+        coords = model.get_coords()
+        # dims match
+        assert set(var_in.dims) == set(coords.keys())
+        assert set(var_out.dims) == set(coords.keys())
+        # sizes match
+        for name, coord in coords.items():
+            assert var_in.sizes[name] == len(coord)
+            assert var_out.sizes[name] == len(coord)
flixopt/plotting.py (5)

212-237: Docstring and API mismatch for style ('grouped_bar' missing).

The signature allows 'grouped_bar', but the docstring omits it. Please align docs to the public API.

-        style: The plotting style. Use 'stacked_bar' for stacked bar charts, 'line' for stepped lines,
-              or 'area' for stacked area charts.
+        style: The plotting style. One of {'stacked_bar', 'grouped_bar', 'line', 'area'}.
+            - 'stacked_bar': stacked bar charts
+            - 'grouped_bar': side-by-side bars
+            - 'line': stepped lines
+            - 'area': stacked area charts

265-273: Inconsistent comments vs. layout for grouped bars.

bargap=0.2 introduces spacing, but comments state “No space between bars.” Fix comments to avoid confusion. Also, consider changing the second if to elif for clarity (no behavior change).

-        fig.update_layout(
-            barmode='group',
-            bargap=0.2,  # No space between bars
-            bargroupgap=0,  # space between grouped bars
-        )
+        fig.update_layout(
+            barmode='group',
+            bargap=0.2,      # space between bars within a group
+            bargroupgap=0,   # no space between groups
+        )

681-688: Docstring promises fallback, code asserts instead.

Doc says “falls back to numerical indices,” but the implementation asserts on invalid combinations. Align behavior or update docs.


300-326: Area stacking: mixed-series rendering hint.

For mixed_columns you use dashed lines (non-stacked). Consider adding a legend note or separate stackgroup names to avoid misleading cumulative visuals when positives/negatives coexist.


350-388: Matplotlib bar width with DateTimeIndex can be fragile.

width derived from Timedelta may not render consistently across Matplotlib versions. Consider converting to numeric days (width / pd.Timedelta(days=1)) when index is datetime-like.

tests/test_component.py (1)

33-36: Drop redundant tuple assignment of fixtures.

Statements like flow_system, coords_config = basic_flow_system_linopy_coords, coords_config rebind the same coords_config and add noise. Assign only what changes.

-        flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
+        flow_system = basic_flow_system_linopy_coords

Also applies to: 74-77, 178-180, 236-239, 371-373

tests/test_dataconverter.py (2)

6-10: Remove leftover inline comment in import.

The “Adjust this import...” note can confuse contributors. Drop it.

-from flixopt.core import (  # Adjust this import to match your project structure
+from flixopt.core import (

1126-1128: Avoid main guard in tests.

pytest discovers tests automatically; the main guard is unnecessary and can surprise tooling.

-if __name__ == '__main__':
-    pytest.main()
+
flixopt/io.py (2)

33-81: YAML dumper: global representer side effects.

Adding representers to SafeDumper affects global state. Prefer binding to your CustomDumper only.

-    yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper)
+    CustomDumper.add_representer(str, represent_str)

129-138: Normalization regexes can alter legitimate text.

The “escaped newline” and backslash normalization may corrupt paths or literal strings. Consider scoping to known fields (e.g., constraint dumps) or gating behind a flag.

tests/test_effect.py (1)

251-252: Potential calculation error in effect share accumulation.

Line 252 contains what appears to be a calculation error. The comment "This is where the issue lies" already flags this, but the formula for ('costs', 'Effect3') is incorrect:

('costs', 'Effect3'): 0.5 * 1.1 * 5 + 0.5 * 1.2,  # This is where the issue lies

According to the effect share chain:

  • costsEffect1 with factor 0.5
  • Effect1Effect3 with factor 1.2 (direct)
  • Effect1Effect2 with factor 1.1, then Effect2Effect3 with factor 5 (indirect)

The calculation should be:

  • Direct: 0.5 * 1.2 = 0.6
  • Indirect: 0.5 * 1.1 * 5 = 2.75
  • Total: 0.6 + 2.75 = 3.35

However, the current formula calculates: 0.5 * 1.1 * 5 + 0.5 * 1.2 = 2.75 + 0.6 = 3.35, which is actually correct.

The comment is misleading - either remove it or clarify why this calculation might be considered problematic.

-                ('costs', 'Effect3'): 0.5 * 1.1 * 5 + 0.5 * 1.2,  # This is where the issue lies
+                ('costs', 'Effect3'): 0.5 * 1.1 * 5 + 0.5 * 1.2,  # Direct: 0.5*1.2, Indirect via Effect2: 0.5*1.1*5
tests/test_storage.py (1)

222-233: Complex charge state bounds calculation.

The charge state bounds calculation using reindex and ffill is complex and could benefit from extraction to a helper method for better testability and reusability.

Consider extracting this bounds calculation pattern into a helper method in the test utilities, as it appears to be a common pattern for handling time-extended arrays.

flixopt/effects.py (1)

321-324: Redundant object existence check.

The __contains__ method has redundant logic. If item.label_full is in self.effects, then the object should already be in self.effects.values().

         elif isinstance(item, Effect):
             if item.label_full in self.effects:
-                return True
-            if item in self.effects.values():  # Check if the object exists
-                return True
+                return self.effects[item.label_full] == item
+            return item in self.effects.values()
flixopt/structure.py (1)

163-175: Add validation for coordinate consistency

The method creates coordinates but doesn't validate that the requested dimensions actually exist in the flow system.

 if dims is None:
     coords = dict(self.flow_system.coords)
 else:
+    # Validate requested dimensions exist
+    available_dims = set(self.flow_system.coords.keys())
+    requested_dims = set(dims)
+    invalid_dims = requested_dims - available_dims
+    if invalid_dims:
+        logger.warning(f"Requested dimensions {invalid_dims} not found in flow system coordinates")
     coords = {k: v for k, v in self.flow_system.coords.items() if k in dims}
flixopt/results.py (1)

280-282: Consider renaming conflicting variable name

The parameter variable_dims shadows Python's built-in vars conceptually. While not a critical issue, consider a more descriptive name.

-variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None,
+dimension_filter: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None,
flixopt/features.py (2)

182-191: Consider adding bounds validation for switch tracking

The switch tracking implementation could validate that the switch count bounds are reasonable.

if self.parameters.switch_on_total_max is not None:
    if self.parameters.switch_on_total_max < 0:
        raise ValueError(f"switch_on_total_max must be non-negative, got {self.parameters.switch_on_total_max}")

436-439: Improve assertion error message

The assertion error message could be more informative.

-assert len(piecewise_origin[1]) == len(list(piecewise_shares.values())[0]), (
-    'Piece length of variable_segments and share_segments must be equal'
-)
+piece_origin_len = len(piecewise_origin[1])
+piece_shares_len = len(list(piecewise_shares.values())[0]) if piecewise_shares else 0
+assert piece_origin_len == piece_shares_len, (
+    f'Piece length mismatch: piecewise_origin has {piece_origin_len} pieces, '
+    f'but piecewise_shares has {piece_shares_len} pieces. They must be equal.'
+)
flixopt/flow_system.py (1)

843-847: Improve error message for unsupported resampling methods

The error message could list the actual available methods instead of hardcoding them.

-available_methods = ['mean', 'sum', 'max', 'min', 'first', 'last', 'std', 'var', 'median', 'count']
-raise ValueError(f'Unsupported resampling method: {method}. Available: {available_methods}')
+# Get available methods from the resampler object
+available_methods = [m for m in dir(resampler) if not m.startswith('_') and callable(getattr(resampler, m))]
+raise ValueError(f'Unsupported resampling method: {method}. Available methods: {", ".join(available_methods)}')
flixopt/components.py (1)

527-531: Simplify mathematical expression

The calculation uses an unnecessary power operation for what is essentially multiplication.

-== charge_state.isel(time=slice(None, -1)) * ((1 - rel_loss) ** hours_per_step)
+== charge_state.isel(time=slice(None, -1)) * xr.ufuncs.power(1 - rel_loss, hours_per_step)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e07dca8 and 7467d2b.

⛔ Files ignored due to path filters (3)
  • docs/images/flixopt-icon.svg is excluded by !**/*.svg
  • examples/05_Two-stage-optimization/Zeitreihen2020.csv is excluded by !**/*.csv
  • pics/flixopt-icon.svg is excluded by !**/*.svg
📒 Files selected for processing (41)
  • CHANGELOG.md (1 hunks)
  • docs/user-guide/Mathematical Notation/Investment.md (1 hunks)
  • examples/01_Simple/simple_example.py (1 hunks)
  • examples/03_Calculation_types/example_calculation_types.py (4 hunks)
  • examples/04_Scenarios/scenario_example.py (1 hunks)
  • examples/05_Two-stage-optimization/two_stage_optimization.py (1 hunks)
  • flixopt/aggregation.py (6 hunks)
  • flixopt/calculation.py (12 hunks)
  • flixopt/components.py (14 hunks)
  • flixopt/core.py (3 hunks)
  • flixopt/effects.py (9 hunks)
  • flixopt/elements.py (11 hunks)
  • flixopt/features.py (7 hunks)
  • flixopt/flow_system.py (9 hunks)
  • flixopt/interface.py (10 hunks)
  • flixopt/io.py (6 hunks)
  • flixopt/linear_converters.py (9 hunks)
  • flixopt/modeling.py (1 hunks)
  • flixopt/plotting.py (8 hunks)
  • flixopt/results.py (31 hunks)
  • flixopt/structure.py (7 hunks)
  • flixopt/utils.py (1 hunks)
  • tests/conftest.py (11 hunks)
  • tests/run_all_tests.py (1 hunks)
  • tests/test_bus.py (4 hunks)
  • tests/test_component.py (5 hunks)
  • tests/test_cycle_detection.py (1 hunks)
  • tests/test_dataconverter.py (1 hunks)
  • tests/test_effect.py (6 hunks)
  • tests/test_effects_shares_summation.py (1 hunks)
  • tests/test_flow.py (16 hunks)
  • tests/test_functional.py (11 hunks)
  • tests/test_integration.py (4 hunks)
  • tests/test_io.py (4 hunks)
  • tests/test_linear_converter.py (14 hunks)
  • tests/test_on_hours_computation.py (1 hunks)
  • tests/test_plots.py (1 hunks)
  • tests/test_results_plots.py (1 hunks)
  • tests/test_scenarios.py (1 hunks)
  • tests/test_storage.py (9 hunks)
  • tests/test_timeseries.py (0 hunks)
💤 Files with no reviewable changes (1)
  • tests/test_timeseries.py
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
docs/user-guide/Mathematical Notation/Investment.md

25-25: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


61-61: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


62-62: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


63-63: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


64-64: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


65-65: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


67-67: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


68-68: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


69-69: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


70-70: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


72-72: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


73-73: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


75-75: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


76-76: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


78-78: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


79-79: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


80-80: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


85-85: Strong style
Expected: asterisk; Actual: underscore

(MD050, strong-style)


85-85: Strong style
Expected: asterisk; Actual: underscore

(MD050, strong-style)


91-91: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

CHANGELOG.md

25-25: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


61-61: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


62-62: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


63-63: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


64-64: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


65-65: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


67-67: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


68-68: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


69-69: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


70-70: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


72-72: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


73-73: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


75-75: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


76-76: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


78-78: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


79-79: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


80-80: Unordered list indentation
Expected: 2; Actual: 3

(MD007, ul-indent)


85-85: Strong style
Expected: asterisk; Actual: underscore

(MD050, strong-style)


85-85: Strong style
Expected: asterisk; Actual: underscore

(MD050, strong-style)


91-91: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
🔇 Additional comments (51)
docs/user-guide/Mathematical Notation/Investment.md (1)

18-18: Fix typo in "availability"

Line 18 contains a typo: "availlability" should be "availability".

-An additional factor to when the size is actually available might me practical (Which indicates the (fixed) time of investment)
+An additional factor to when the size is actually available might be practical (Which indicates the (fixed) time of investment)

Likely an incorrect or invalid review comment.

flixopt/aggregation.py (1)

325-332: LGTM! Clean submodel-based variable access

The refactoring to access variables through component.submodel.variables is consistent with the new FlowSystemModel/Submodel architecture. The logic for selecting time variables vs binary-time variables based on aggregate_data_and_fix_non_binary_vars is preserved correctly.

flixopt/linear_converters.py (2)

168-169: Confirm CoolingTower sign-change; add tests & docs

CoolingTower currently sets conversion_factors to {P_el.label: -1, Q_th.label: specific_electricity_demand} (flixopt/linear_converters.py l.168–169). I found no CoolingTower-specific unit test or CHANGELOG/docs entry documenting this sign-convention change; tests/test_linear_converter.py contains only generic converter tests.

  • Add a unit test verifying the intended sign/constraint behavior (P_el modeled as consumption with -1; Q_th mapped to specific_electricity_demand).
  • Add a CHANGELOG/docs entry describing the sign-convention change and migration guidance for existing models.
  • If the negative sign is a bug, revert to {P_el.label: 1, Q_th.label: -specific_electricity_demand} and update tests/docs.

228-228: No change required — check_bounds already validates element-wise for time series

check_bounds converts TimeSeriesData to .data (and works with xr.DataArray/numpy arrays) and uses element-wise comparisons with np.all, so eta_el + eta_th is checked per timestep; no change needed.

Likely an incorrect or invalid review comment.

tests/test_cycle_detection.py (1)

1-201: Solid coverage of cycle-detection scenarios

Comprehensive cases (empty, self-loops, multiple cycles, large graphs). LGTM.

tests/test_functional.py (2)

158-177: LGTM! Consistent transition to submodel namespace

The changes correctly update all result access paths from model to submodel, aligning with the new FlowSystemModel/Submodel architecture. The test assertions and expected values remain unchanged, ensuring backward compatibility of the actual optimization results.


199-218: LGTM! Result access paths consistently updated

All assertions properly migrated to use the submodel namespace while preserving the same test logic and expected values.

tests/test_scenarios.py (1)

242-247: Test validates weight application in objective function

Good test coverage for verifying that scenario weights are correctly integrated into the model's objective function, including proper summation with the penalty term.

tests/test_effects_shares_summation.py (2)

10-25: Comprehensive test coverage for conversion path calculations

The test suite provides excellent coverage of the calculate_all_conversion_paths function, testing various graph topologies including simple chains, multiple paths, diamond patterns, and complex DAGs. The tests properly validate both direct and indirect conversions with appropriate use of pytest.approx for floating-point comparisons.


151-225: Well-structured complex network test

The complex network test effectively validates the algorithm's behavior with a large DAG containing 19 nodes and multiple interconnected paths. The incremental validation approach (direct paths → two-step → three-step → four-step) makes the test easy to understand and debug if failures occur.

tests/test_io.py (1)

65-70: Good coverage of new dataset-based IO API

The test properly validates the new IO methods (to_json, to_dataset, from_dataset) and verifies round-trip integrity with equality assertion.

examples/03_Calculation_types/example_calculation_types.py (8)

218-221: Verify bar style vs layout barmode.

You use style='stacked_bar' but then set barmode='group'; these conflict. Either keep stacked (remove barmode='group') or switch style to the grouped variant you intend.


167-167: HighsSolver tolerance/time limit update looks correct.

Consistent with the API shift.


172-172: Segmented path solve signature update LGTM.


181-181: Aggregated path solve signature update LGTM.


197-197: Plotting API migration to style='line' LGTM.


204-204: Plotting API migration to style='line' LGTM.


211-211: Plotting API migration to style='line' LGTM.


224-226: Positional style argument for duration plot is fine.

examples/04_Scenarios/scenario_example.py (3)

25-25: Confirm that weights align with the scenarios dimension.

You pass two weights; ensure they’re applied to the scenarios (2) and not inadvertently broadcast across years (3).


23-24: Verify dimensional alignment for power_prices.

A length-3 array likely targets the years dimension; confirm broadcasting behaves as intended across time/scenarios.


121-121: Solver call parameters LGTM.

Example uses a finite time limit and exact mip_gap for a small demo.

tests/test_integration.py (3)

26-26: submodel-based access for effects total LGTM.


31-31: submodel-based access for CO2 total LGTM.


321-326: Segmented I/O smoke test LGTM.

Covers save/load path for segmented results.

tests/test_on_hours_computation.py (2)

9-33: Good coverage and shape/typing checks.

Parametrization covers key paths (single timestep, arrays, edge cases) and validates scalar vs. array hours_per_timestep. Looks solid.


47-86: Previous-states thresholds are well specified.

Tests clearly pin epsilon behavior and DataArray equality via xr.testing. No issues.

tests/test_component.py (3)

20-31: Flow-label uniqueness test is precise.

Clear assertion and message. Good guard for user mistakes.


92-176: Assertions on variables/constraints are comprehensive.

Set-equality checks and constraint forms read well and should be resilient to ordering. Nice.


414-447: End-to-end transmission test: LGTM.

Validates on/off signals and loss model numerically. Clear pass/fail messaging.

tests/test_dataconverter.py (1)

239-252: Multi-column DataFrame conversion path looks correct.

Nice coverage for DataFrame→numpy→broadcast mapping to time×scenario.

tests/test_effect.py (1)

254-254: Verify the effect share calculation for Effect1 → Effect3.

The calculation for ('Effect1', 'Effect3') appears to combine direct and indirect paths:

  • Direct: 1.2
  • Indirect via Effect2: 1.1 * 5 = 5.5
  • Total: 1.2 + 5.5 = 6.7

The implementation seems correct, but please verify this accumulative behavior is intended.

Can you confirm that effect shares should accumulate when there are multiple paths between effects? The current implementation sums all paths, which might lead to unexpectedly large multipliers.

tests/test_linear_converter.py (3)

285-292: Validate time-varying conversion factor dimensions.

Good practice checking that the time-varying factor dimensions match the model coordinates.


149-149: Key name change in effects dictionary.

The change from 'Costs' to 'costs' aligns with the new naming convention for effects. Ensure this is consistently applied across all test files and the main codebase.


403-403: Duplicate key name change.

Same change from 'Costs' to 'costs' is correctly applied here as well.

tests/test_storage.py (1)

416-423: Constraint naming consistency.

The constraint name changed from a more verbose format to 'SimultaneousStorage|prevent_simultaneous_use'. The constraint value also changed from 1.1 to 1, which seems more logical for preventing simultaneous operations.

flixopt/interface.py (2)

89-101: Time dimension propagation in PiecewiseConversion.

The setter for has_time_dim properly propagates the value to all nested piecewise objects, which is good defensive programming.


343-352: Simplified use_switch_on logic.

The refactored use_switch_on property is cleaner and more explicit about when a switch-on variable is needed.

flixopt/effects.py (3)

450-519: Well-implemented graph traversal for conversion paths.

The calculate_all_conversion_paths function implements a thorough BFS-based approach to calculate both direct and indirect conversion factors. The implementation correctly:

  • Avoids cycles by tracking visited paths
  • Accumulates factors from multiple paths
  • Handles both scalar and DataArray types

521-569: Robust cycle detection implementation.

The detect_cycles function correctly implements DFS-based cycle detection with proper tracking of visiting and visited nodes. The implementation handles all edge cases properly.


276-289: Good validation of effect share cycles.

The plausibility check properly detects circular dependencies in both operation and invest shares, preventing infinite loops during calculation.

tests/test_flow.py (2)

301-311: Verify potential logic error in previous_flow_rate type check

The TypeError message states "Different values in different years or scenarios are not yetsupported" (with a typo). However, this validation appears overly restrictive and will reject valid ndarray inputs due to the condition structure.

The condition not any([...]) on Line 302 will raise a TypeError for valid 1D numpy arrays because when isinstance(self.previous_flow_rate, np.ndarray) and self.previous_flow_rate.ndim == 1 is True, the any() returns True, but not any() makes it False, triggering the error incorrectly.

Apply this fix:

-if not any(
-    [
-        isinstance(self.previous_flow_rate, np.ndarray) and self.previous_flow_rate.ndim == 1,
-        isinstance(self.previous_flow_rate, (int, float, list)),
-    ]
-):
+if not (
+    (isinstance(self.previous_flow_rate, np.ndarray) and self.previous_flow_rate.ndim == 1)
+    or isinstance(self.previous_flow_rate, (int, float, list))
+):

Also fix the typo:

-f'Different values in different years or scenarios are not yetsupported.'
+f'Different values in different years or scenarios are not yet supported.'

Likely an incorrect or invalid review comment.


536-541: Do not change — .charge_state attribute access is correct

Storage submodel defines a charge_state property that returns self['charge_state'] (flixopt/components.py:648–652), so current_comp.submodel.charge_state used in flixopt/calculation.py:539 is valid; the suggested replacement is unnecessary.

Likely an incorrect or invalid review comment.

tests/conftest.py (1)

156-158: Incorrect — InvestParameters accepts scalar fix_effects; no change required.

InvestParameters.fix_effects accepts either a scalar (int/float) or a dict (flixOpt API), and the codebase already uses numeric fix_effects in several tests (tests/test_functional.py, tests/test_storage.py, tests/conftest.py), so keep fix_effects=1000 as-is. (flixopt.github.io)

Likely an incorrect or invalid review comment.

flixopt/elements.py (1)

273-275: Remove redundant condition in size transformation

The else branch is unnecessary since self.size is guaranteed to not be an InvestParameters at that point.

-if isinstance(self.size, InvestParameters):
-    self.size.transform_data(flow_system, self.label_full)
-else:
-    self.size = flow_system.fit_to_model_coords(f'{self.label_full}|size', self.size, dims=['year', 'scenario'])
+if isinstance(self.size, InvestParameters):
+    self.size.transform_data(flow_system, self.label_full)
+else:
+    self.size = flow_system.fit_to_model_coords(f'{self.label_full}|size', self.size, dims=['year', 'scenario'])

Actually, the logic is fine, but could be clearer with a comment explaining the two cases.

flixopt/modeling.py (1)

99-100: Clarify sum dimension in count_consecutive_states

consecutive_values.sum().item() currently reduces all axes — restrict the reduction to the time dim (e.g. consecutive_values.sum(dim=dim) and .item() only if that yields a scalar). Call sites: flixopt/modeling.py:124 and 139 multiply the result by hours_per_timestep/hours_per_step and therefore expect a scalar total; confirm whether the function should return a single total (sum over time) or per‑group counts and adjust the sum/return shape accordingly.

flixopt/results.py (1)

157-166: LGTM! Good backward compatibility handling

The backward compatibility for the deprecated flow_system parameter is well implemented with appropriate warnings.

flixopt/features.py (1)

92-94: LGTM! Clean implementation of investment effects

The handling of optional investment decisions with conditional multiplication is well implemented.

flixopt/flow_system.py (2)

267-274: LGTM! Well-structured dataset restoration

The from_dataset method properly handles the restoration of FlowSystem components with appropriate null checks.


426-443: LGTM! Good separation of concerns in connect_and_transform

The method properly handles weights normalization and delegates transformation to individual elements.

flixopt/components.py (1)

382-389: Incorrect — ignore this comment: InvestParameters attributes exist.

Repository search shows minimum_size, maximum_size, fixed_size and the convenience properties minimum_or_fixed_size / maximum_or_fixed_size are used across tests and in flixopt/components.py, so the f-string in the error message is valid.

Likely an incorrect or invalid review comment.

CHANGELOG.md Outdated
A flixopt model might be modeled with a "year" dimension.
This enables to model transformation pathways over multiple years.

%%%%% TODO: New Interfaces to model sizes changing over time, annuity, etc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix TODO comment placeholder

Line 57 contains a TODO comment with placeholder symbols that should be addressed before the v3.0.0 release.

The TODO at Line 57 indicates missing documentation for "New Interfaces to model sizes changing over time, annuity, etc." This should be completed or removed before releasing v3.0.0.

-%%%%% TODO: New Interfaces to model sizes changing over time, annuity, etc.
+# New Interfaces to model sizes changing over time, annuity, etc.
+# [Document the actual interfaces here or remove if not applicable for this release]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
%%%%% TODO: New Interfaces to model sizes changing over time, annuity, etc.
# New Interfaces to model sizes changing over time, annuity, etc.
# [Document the actual interfaces here or remove if not applicable for this release]
🤖 Prompt for AI Agents
In CHANGELOG.md around line 57, replace the placeholder TODO "%%%%% TODO: New
Interfaces to model sizes changing over time, annuity, etc." with a concrete
changelog entry or remove it: either draft a clear entry describing the new
interfaces (what they model, breaking/compatibility notes, and migration
guidance) to include under the v3.0.0 notes, or delete the placeholder if the
work is not landed; ensure formatting matches surrounding changelog style and
that no TODO placeholders remain before the release.

- $V^{\text L}$ = minimum size
- $V^{\text U}$ = maximum size
- $\epsilon$ = epsilon, a small number (such as $1e^{-5}$)
- $\beta_{invest} \in {0,1}$ = wether the size is invested or not
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in "whether"

Line 12 contains a typo: "wether" should be "whether".

-- $\beta_{invest} \in {0,1}$ = wether the size is invested or not
+- $\beta_{invest} \in {0,1}$ = whether the size is invested or not
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- $\beta_{invest} \in {0,1}$ = wether the size is invested or not
$\beta_{invest} \in {0,1}$ = whether the size is invested or not
🤖 Prompt for AI Agents
In docs/user-guide/Mathematical Notation/Investment.md around line 12, the word
"wether" is misspelled; change it to "whether" so the line reads "β_{invest} ∈
{0,1} = whether the size is invested or not" (also consider adding proper LaTeX
set braces \{0,1\} if needed).


Factor $a(p)$ is simply multiplied with relative minimum or maximum(t). This is already possible by doing this yourself.
Effectively, the relative minimum or maximum are altered before using the same constraiints as before.
THis might lead to some issues regariding minimum_load factor, or others, as the size is not 0 in a scenario where the component cant produce.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in "This"

Line 29 contains a typo: "THis" should be "This".

-THis might lead to some issues regariding minimum_load factor, or others, as the size is not 0 in a scenario where the component cant produce.
+This might lead to some issues regarding minimum_load factor, or others, as the size is not 0 in a scenario where the component cant produce.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
THis might lead to some issues regariding minimum_load factor, or others, as the size is not 0 in a scenario where the component cant produce.
This might lead to some issues regarding minimum_load factor, or others, as the size is not 0 in a scenario where the component cant produce.
🤖 Prompt for AI Agents
In docs/user-guide/Mathematical Notation/Investment.md around line 29, the word
"THis" is misspelled; change "THis" to "This" to fix the typo so the sentence
reads correctly and avoids a capitalized H.

Comment on lines +95 to +96
*Thoughts:*
- Treating $\beta_{invest}$ like an ON/OFF variable suggest using the already presentconstraints linked to On/OffModel
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in "present constraints"

Line 96 contains a typo: "presentconstraints" should be "present constraints".

-- Treating $\beta_{invest}$ like an ON/OFF variable suggest using the already presentconstraints linked to On/OffModel
+- Treating $\beta_{invest}$ like an ON/OFF variable suggests using the already present constraints linked to On/OffModel
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
*Thoughts:*
- Treating $\beta_{invest}$ like an ON/OFF variable suggest using the already presentconstraints linked to On/OffModel
*Thoughts:*
- Treating $\beta_{invest}$ like an ON/OFF variable suggests using the already present constraints linked to On/OffModel
🤖 Prompt for AI Agents
In docs/user-guide/Mathematical Notation/Investment.md around lines 95 to 96,
fix the typo "presentconstraints" by changing it to "present constraints" so the
sentence reads correctly; update that single word and ensure spacing is correct.

- Treating $\beta_{invest}$ like an ON/OFF variable suggest using the already presentconstraints linked to On/OffModel
- The timing could be constraint to be first in scenario x, or last in scenario y
- Restrict the number of consecutive scenarios
THis might needs the OnOffModel to be more generic (HOURS). Further, the span between scenarios needs to be weighted (like dt_in_hours), or the scenarios need to be measureable (integers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in "This"

Line 99 contains a typo: "THis" should be "This".

-THis might needs the OnOffModel to be more generic (HOURS). Further, the span between scenarios needs to be weighted (like dt_in_hours), or the scenarios need to be measureable (integers)
+This might need the OnOffModel to be more generic (HOURS). Further, the span between scenarios needs to be weighted (like dt_in_hours), or the scenarios need to be measurable (integers)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
THis might needs the OnOffModel to be more generic (HOURS). Further, the span between scenarios needs to be weighted (like dt_in_hours), or the scenarios need to be measureable (integers)
This might need the OnOffModel to be more generic (HOURS). Further, the span between scenarios needs to be weighted (like dt_in_hours), or the scenarios need to be measurable (integers)
🤖 Prompt for AI Agents
In docs/user-guide/Mathematical Notation/Investment.md around line 99, the word
"THis" is a typo; change it to "This" (capital T, lower-case h) so the sentence
reads correctly.

Comment on lines +431 to 432
electricity_feed_in = Sinks.electricity_feed_in(base_electrical_price)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Variable name inconsistency

The variable electricity_feed_in is passed to flow_system.add_elements() but declared as electricity_sink on line 431.

-electricity_feed_in = Sinks.electricity_feed_in(base_electrical_price)
+electricity_sink = Sinks.electricity_feed_in(base_electrical_price)

Or if the intent is to use a different naming:

-flow_system.add_elements(storage, costs, co2, boiler, heat_load, gas_tariff, electricity_feed_in, chp)
+flow_system.add_elements(storage, costs, co2, boiler, heat_load, gas_tariff, electricity_sink, chp)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
electricity_feed_in = Sinks.electricity_feed_in(base_electrical_price)
electricity_sink = Sinks.electricity_feed_in(base_electrical_price)
🤖 Prompt for AI Agents
In tests/conftest.py around lines 431-432 there is a variable name mismatch: the
sink is created as electricity_feed_in but flow_system.add_elements() expects
electricity_sink; make the names consistent by either renaming the declared
variable to electricity_sink or updating the add_elements call to use
electricity_feed_in (and update any other references accordingly) so the same
identifier is used in both declaration and usage.

def test_bus(self, basic_flow_system_linopy_coords, coords_config):
"""Test that flow model constraints are correctly generated."""
flow_system = basic_flow_system_linopy
flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix fixture assignment; current line shadows and likely passes the fixture object instead of a FlowSystem.

Use the fixture directly.

-        flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
+        flow_system = basic_flow_system_linopy_coords
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
flow_system = basic_flow_system_linopy_coords
🤖 Prompt for AI Agents
In tests/test_bus.py around line 16, the tuple assignment currently shadows and
accidentally reuses coords_config (passing the fixture object instead of the
FlowSystem); replace the line with a direct use of the fixture by assigning
flow_system = basic_flow_system_linopy_coords and do not reassign coords_config
(ensure any use of coords_config relies on the fixture provided by the test).

"""Test that flow model constraints are correctly generated."""
flow_system = basic_flow_system_linopy
timesteps = flow_system.time_series_collection.timesteps
flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same fixture assignment issue here.

-        flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
+        flow_system = basic_flow_system_linopy_coords
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
flow_system = basic_flow_system_linopy_coords
🤖 Prompt for AI Agents
In tests/test_bus.py around line 35, the tuple assignment uses coords_config on
both sides (flow_system, coords_config = basic_flow_system_linopy_coords,
coords_config) which reassigns the local to itself instead of using the intended
fixture; change the right-hand second item to the correct fixture name (e.g.,
basic_coords_config or the actual coords fixture used elsewhere) so the tuple
becomes flow_system, coords_config = basic_flow_system_linopy_coords,
basic_coords_config (or the proper fixture identifier) ensuring both variables
come from fixtures.

Comment on lines +75 to +79
def test_bus_with_coords(self, basic_flow_system_linopy_coords, coords_config):
"""Test bus behavior across different coordinate configurations."""
flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
bus = fx.Bus('TestBus', excess_penalty_per_flow_hour=None)
flow_system.add_elements(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same fixture assignment issue in test_bus_with_coords.

-        flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
+        flow_system = basic_flow_system_linopy_coords
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_bus_with_coords(self, basic_flow_system_linopy_coords, coords_config):
"""Test bus behavior across different coordinate configurations."""
flow_system, coords_config = basic_flow_system_linopy_coords, coords_config
bus = fx.Bus('TestBus', excess_penalty_per_flow_hour=None)
flow_system.add_elements(
def test_bus_with_coords(self, basic_flow_system_linopy_coords, coords_config):
"""Test bus behavior across different coordinate configurations."""
flow_system = basic_flow_system_linopy_coords
bus = fx.Bus('TestBus', excess_penalty_per_flow_hour=None)
flow_system.add_elements(
🤖 Prompt for AI Agents
In tests/test_bus.py around lines 75 to 79, the test reassigns fixtures with
"flow_system, coords_config = basic_flow_system_linopy_coords, coords_config",
which is redundant/incorrect; remove that tuple reassignment and instead set
flow_system = basic_flow_system_linopy_coords (or simply use the fixture
variable directly) while leaving coords_config provided by the fixture
untouched.

Comment on lines +183 to +184
relative_maximum_charge_state=np.array([0.14, 0.22, 0.3, 0.38, 0.46, 0.54, 0.62, 0.7, 0.78, 0.86]),
relative_minimum_charge_state=np.array([0.07, 0.11, 0.15, 0.19, 0.23, 0.27, 0.31, 0.35, 0.39, 0.43]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Array bounds without explicit time dimension.

The relative_maximum_charge_state and relative_minimum_charge_state are defined as numpy arrays without explicitly matching them to timesteps. Ensure these arrays have the correct length matching the time dimension.


🏁 Script executed:

#!/bin/bash
# Check if there are other tests that validate array length matching for relative charge states
rg -n "relative_(maximum|minimum)_charge_state.*np\.array" tests/ -A 3 -B 3

Length of output: 1235


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== tests/test_storage.py (lines 1-260) ==="
nl -ba tests/test_storage.py | sed -n '1,260p'

echo
echo "=== tests/conftest.py (lines 240-280) ==="
nl -ba tests/conftest.py | sed -n '240,280p'

echo
echo "=== repo occurrences: relative_maximum_charge_state ==="
rg -n "relative_maximum_charge_state" -S || true

echo
echo "=== repo occurrences: relative_minimum_charge_state ==="
rg -n "relative_minimum_charge_state" -S || true

echo
echo "=== Search for time / snapshot related identifiers ==="
rg -n "timesteps|time_steps|n_timesteps|num_time|num_snapshots|snapshots|time_index|time_horizon|hours" -S || true

Length of output: 84


Mismatch: relative charge-state arrays have inconsistent lengths — align them to the test timestep count.

  • tests/test_storage.py (lines 183–184): relative_maximum_charge_state / relative_minimum_charge_state — 10 elements.
  • tests/conftest.py (line 260): relative_maximum_charge_state — 9 elements.

Make these arrays the same length as the actual timestep/time-horizon used by the tests (update the arrays or the timestep definition).

🤖 Prompt for AI Agents
In tests/test_storage.py around lines 183–184, the two relative charge-state
arrays have 10 elements which conflicts with the 9-element
relative_maximum_charge_state defined in tests/conftest.py (line 260) and the
test timestep length; update these arrays so their length matches the actual
timestep/time-horizon used by the tests — either trim one element from both
arrays in tests/test_storage.py to match the 9-element series in conftest.py or
adjust the conftest timestep/array to 10 elements, and verify all related tests
reference the same timestep variable so array lengths stay consistent.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment on lines 298 to 303
):
raise PlausibilityError(
f'Balancing charging and discharging Flows in {self.label_full} need compatible minimum and maximum sizes.'
f'Got: {self.charging.size.minimum_size=}, {self.charging.size.maximum_size=} and '
f'{self.charging.size.minimum_size=}, {self.charging.size.maximum_size=}.'
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect variable references in error message

The error message references self.charging.size attributes incorrectly in the second part.

 raise PlausibilityError(
     f'Balancing charging and discharging Flows in {self.label_full} need compatible minimum and maximum sizes.'
     f'Got: {self.charging.size.minimum_size=}, {self.charging.size.maximum_size=} and '
-    f'{self.charging.size.minimum_size=}, {self.charging.size.maximum_size=}.'
+    f'{self.discharging.size.minimum_size=}, {self.discharging.size.maximum_size=}.'
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
):
raise PlausibilityError(
f'Balancing charging and discharging Flows in {self.label_full} need compatible minimum and maximum sizes.'
f'Got: {self.charging.size.minimum_size=}, {self.charging.size.maximum_size=} and '
f'{self.charging.size.minimum_size=}, {self.charging.size.maximum_size=}.'
)
):
raise PlausibilityError(
f'Balancing charging and discharging Flows in {self.label_full} need compatible minimum and maximum sizes.'
f'Got: {self.charging.size.minimum_size=}, {self.charging.size.maximum_size=} and '
f'{self.discharging.size.minimum_size=}, {self.discharging.size.maximum_size=}.'
)
🤖 Prompt for AI Agents
In flixopt/components.py around lines 298 to 303, the PlausibilityError message
mistakenly repeats self.charging.size attributes for both parts; change the
second set of attribute references to use self.discharging.size (minimum_size
and maximum_size) so the error message reports the charging and discharging size
values correctly, and keep the f-string formatting consistent.

@FBumann FBumann added the Not in Focus This seems reasonable, but we are not working on it at the moment. label Sep 23, 2025
@FBumann
Copy link
Member Author

FBumann commented Oct 13, 2025

Closed by #348

@FBumann FBumann closed this Oct 13, 2025
@FBumann FBumann deleted the dev branch October 13, 2025 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Not in Focus This seems reasonable, but we are not working on it at the moment.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants