Skip to content

Conversation

@FBumann
Copy link
Member

@FBumann FBumann commented Dec 3, 2025

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

    • Direct optimization API: run optimizations via FlowSystem.optimize (also build_model/solve) and access results on flow_system.solution.
    • Component-level views: element.solution returns scoped results for individual components.
    • Time‑series clustering: flow_system.transform.cluster(...) to create clustered workflows.
  • Persistence

    • Save/load full systems and solution datasets via to_netcdf/from_netcdf and solution.to_netcdf.
  • Deprecation

    • Older Optimization/Results APIs now deprecated (migration docs and warnings added).

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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

Adds persistent solution storage to FlowSystem (xr.Dataset | None), serializes/deserializable solution data, exposes per-element solution views, introduces build_model/solve/optimize/transform accessors, and updates tests and docs to use the new FlowSystem-centered optimization API.

Changes

Cohort / File(s) Change Summary
FlowSystem core & serialization
flixopt/flow_system.py
Adds public `FlowSystem.solution: xr.Dataset
Accessors & transforms
flixopt/optimize_accessor.py, flixopt/transform_accessor.py
New OptimizeAccessor (callable to build+solve) and TransformAccessor (clustering: cluster(...), weight calculation). TransformAccessor stores _clustering_info on resulting FlowSystem.
Optimization & results deprecation plumbing
flixopt/optimization.py, flixopt/results.py
Deprecation warnings for older Optimization/Results classes; Optimization.solve now assigns self.flow_system.solution = self.model.solution. Results imports updated to include deprecation metadata.
Element model & serialization
flixopt/structure.py
Element ctor accepts optional _variable_names and _constraint_names; adds Element.solution property (filters FlowSystem.solution by element variables); introduces _populate_element_variable_names, extends _create_reference_structure and _resolve_reference_structure to include and restore these fields and nested Interface construction.
Configuration
flixopt/config.py
Adds Solving.compute_infeasibilities flag (default True) and includes it in CONFIG.to_dict().
Solution-related tests & test infra
tests/test_solution_persistence.py, tests/test_solution_and_plotting.py, tests/* and tests/deprecated/*
New/updated tests to validate solution persistence, per-element solutions, plotting with solution data, migration to FlowSystem.optimize API; many tests adapted from deprecated Optimization/Results flows; added pytest markers, warning filters, and deprecated test scaffolding.
Documentation & examples
docs/**, mkdocs.yml
Quick-start, user migration guide, and optimization/results docs updated to demonstrate FlowSystem.optimize/build_model/solve usage, solution access (flow_system.solution), and IO (to_netcdf/from_netcdf); migration doc added to nav.
Project config & test config
pyproject.toml, tests/conftest.py, tests/deprecated/conftest.py, tests/deprecated/__init__.py
Adds pytest marker deprecated_api, expands filterwarnings for DeprecationWarning, updates fixtures to use FlowSystem.build_model/optimize, and introduces deprecated test fixtures/helpers.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant FlowSystem
    participant OptimizeAccessor as Optimize
    participant Solver
    participant Storage as NetCDF

    User->>FlowSystem: build_model(normalize_weights?)
    FlowSystem->>FlowSystem: prepare & model (populate element variable names)
    User->>Optimize: __call__(solver, normalize_weights)
    Optimize->>FlowSystem: build_model(normalize_weights)
    Optimize->>Optimize: invoke FlowSystem.solve(solver)
    Optimize->>Solver: run solver on model
    Solver-->>Optimize: model.solution (xr.Dataset)
    Optimize->>FlowSystem: FlowSystem.solution = model.solution
    FlowSystem->>FlowSystem: set has_solution, rename time coord if needed
    User->>FlowSystem: to_netcdf(path) / to_dataset()
    FlowSystem->>Storage: serialize dataset (prefix solution|*, solution_time)
    User->>FlowSystem: from_netcdf(path)
    FlowSystem->>FlowSystem: load dataset, extract solution vars, rename solution_time→time
    User->>FlowSystem: components['X'].solution (Element.solution)
    FlowSystem->>FlowSystem: filter solution by element _variable_names
    FlowSystem-->>User: element-scoped xr.Dataset
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Attention areas:
    • flixopt/flow_system.py: solution serialization/deserialization, solution| variable prefix handling, time-coordinate renaming, and clustering constraint integration.
    • flixopt/structure.py: element variable/constraint name population, Element.solution filtering, and robust restore of nested Interface objects.
    • tests: ensure new tests correctly mock/expect solution Dataset shapes and that deprecated-test scaffolding does not mask regressions.
    • flixopt/transform_accessor.py: clustering weight computation and data aggregation correctness.

Possibly related PRs

Suggested reviewers

  • baumbude
  • PStange

Poem

🐰 A dataset hops into a chest,
Stored with names so neatly dressed.
Build, solve, save — then load anew,
Elements peek at their own view.
Hooray — the rabbit sings with glee, solutions safe and wild and free.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is a placeholder template with unchecked checkboxes and no substantive content. Required sections like actual description, specific type of change, related issues, and testing confirmation are all unfilled or generic. Replace placeholder text with actual description of the solution storage implementation, specify the type of change (e.g., 'New feature'), reference relevant issues, and confirm testing status by checking appropriate boxes.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'New solution storing interface' accurately describes the main objective: introducing a new solution storage and access interface for FlowSystem with xr.Dataset support, serialization, and element-level filtering.
Docstring Coverage ✅ Passed Docstring coverage is 84.81% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@FBumann
Copy link
Member Author

FBumann commented Dec 4, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 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: 1

🧹 Nitpick comments (1)
tests/test_solution_persistence.py (1)

170-178: Assertion on line 177 is always true.

The assertion assert len(boiler._constraint_names) >= 0 will always pass since list length is never negative. The comment suggests some elements might have no constraints, but this test doesn't verify constraints are actually populated when they should be.

Consider strengthening the test:

-        # Boiler should have some constraints
-        assert len(boiler._constraint_names) >= 0  # Some elements might have no constraints
+        # Verify constraint_names is a list (even if empty for some elements)
+        assert isinstance(boiler._constraint_names, list)
+        # For Boiler specifically, we expect some constraints
+        # (adjust if Boiler genuinely has no constraints in this config)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1cb20e and 0d167ff.

📒 Files selected for processing (4)
  • flixopt/flow_system.py (4 hunks)
  • flixopt/optimization.py (1 hunks)
  • flixopt/structure.py (4 hunks)
  • tests/test_solution_persistence.py (1 hunks)
🔇 Additional comments (10)
flixopt/optimization.py (1)

263-265: LGTM! Solution stored on FlowSystem after successful solve.

The placement is correct—after the status check (which raises on infeasibility) and before Results.from_optimization() is called. This ensures element-level solution access is available when results are constructed.

flixopt/flow_system.py (3)

205-206: LGTM! Solution attribute initialization.

Clean initialization of the solution storage attribute. Correctly set to None before any optimization is run.


547-565: LGTM! Solution serialization with proper namespace separation.

The approach is sound:

  • solution| prefix prevents name collisions with FlowSystem configuration variables
  • Renaming timesolution_time preserves the full solution (which may have extra timesteps for final states) without interfering with FlowSystem's time index
  • The has_solution flag enables clean conditional restoration

587-648: LGTM! Solution deserialization with proper validation.

The restoration logic correctly:

  • Separates solution variables by prefix
  • Rebuilds the solution dataset with original variable names
  • Reverses the solution_timetime rename
  • Uses defensive check (has_solution and solution_vars) to handle edge cases
tests/test_solution_persistence.py (1)

1-42: Good test structure with comprehensive coverage.

The test organization is clean with logical groupings:

  • TestSolutionOnFlowSystem: FlowSystem.solution lifecycle
  • TestSolutionOnElement: Element.solution property
  • TestVariableNamesPopulation: Post-modeling state
  • TestSolutionPersistence: Serialization roundtrips
  • TestFlowSystemFileIO: NetCDF persistence
  • TestNoSolutionPersistence / TestEdgeCases: Edge cases

The parametrized fixture with unique IDs for parallel execution is a good pattern.

flixopt/structure.py (5)

111-119: LGTM! Variable/constraint names population after modeling.

The implementation correctly:

  • Iterates over all elements via self.flow_system.values()
  • Checks for submodel is not None before accessing
  • Extracts variable/constraint names from the submodel's variables and constraints

This enables per-element solution access after the model is built.


736-750: LGTM! Enhanced deserialization for non-constructor attributes.

The approach cleanly separates constructor arguments from extra attributes (like _variable_names, _constraint_names) by introspecting the class signature. This allows Element-specific serialization fields to be restored even when subclasses don't include them in their constructors.


988-1008: LGTM! Element constructor extended for serialization support.

The optional _variable_names and _constraint_names parameters enable clean deserialization while defaulting to empty lists for fresh elements. The leading underscore convention appropriately signals these are internal/serialization-related.


1022-1037: LGTM! Element.solution property with proper validation.

The validation chain provides clear error messages for common issues:

  1. Element not linked to FlowSystem
  2. No solution available
  3. Element not modeled (no variable names)

The final indexing self._flow_system.solution[self._variable_names] will raise a KeyError if variable names don't exist in the solution, which is appropriate—this would indicate a mismatch between the modeled element and the loaded solution.


1039-1054: LGTM! Element serialization extended for variable/constraint names.

Clean implementation:

  • Extends parent via super()
  • Conditionally includes names only when non-empty (avoiding serialization bloat for un-modeled elements)
  • Pairs correctly with the enhanced _resolve_reference_structure for round-trip fidelity

@FBumann FBumann marked this pull request as ready for review December 4, 2025 18:09
@FBumann
Copy link
Member Author

FBumann commented Dec 4, 2025

https://github.com/coderabbitai review

…me values:

  # Old API (removed)
  sankey.flows(timestep=100)

  # New API
  sankey.flows(select={'time': '2023-01-01 12:00'})
  sankey.flows(select={'time': slice('2023-01-01', '2023-01-15')})
  1. Carrier attribute on flow_rates/flow_hours

  - Each flow variable now has a carrier attribute
  - Enables filtering: flow_system.statistics.flow_rates['Boiler(Steam)'].attrs['carrier']  → 'steam'

  2. Carrier filtering in sankey

  - New filter option: select={'carrier': 'heat'}
  - Example: fs.statistics.plot.sankey.flows(select={'carrier': 'steam'})

  3. Carrier colors in sankey (subtle/transparent)

  - Links: Colored by carrier with 40% opacity (e.g., rgba(255, 0, 0, 0.4))
  - Bus nodes: Colored by their carrier color

  4. Carrier colors in topology

  - topology.plot(): Links colored by carrier (subtle)
  - topology.infos(): Now includes carrier_color in edge data
  - topology.plot_legacy(): Edges colored by carrier

  5. Carrier-level balance plot

  # Show all heat production/consumption system-wide
  fs.statistics.plot.carrier_balance('heat')

  # Energy units
  fs.statistics.plot.carrier_balance('electricity', unit='flow_hours')

  - Positive: Inputs to carrier buses (production/sources)
  - Negative: Outputs from carrier buses (consumption/sinks)
  - Colors from component.color for each flow
  1. Centralized Hex Color Handling in color_processing.py
  2. Simplified Carrier Retrieval in flow_system.py
  3. Updated statistics_accessor.py
  4. Updated topology_accessor.py
Uses matplotlib.colors.to_rgba() under the hood, which handles all standard color formats that ma
  1. flow_system.py

  - Added self._topology: TopologyAccessor | None = None in __init__
  - Changed topology property to cache the accessor (like statistics)
  - Added self._topology = None in _invalidate_model() for invalidation on structure changes

  2. topology_accessor.py

  - Added cached color properties in __init__:
    - _carrier_colors
    - _component_colors
    - _bus_colors
  - Added three new properties:
    - carrier_colors - maps carrier name → hex color
    - component_colors - maps component label → hex color
    - bus_colors - maps bus label → hex color (from carrier)
  - Updated infos() and plot() to use cached colors

  3. statistics_accessor.py

  - Removed _carrier_colors from __init__
  - Changed carrier_colors to delegate to self._fs.topology.carrier_colors
  - Added component_colors and bus_colors properties (delegating to topology)
  - Updated _get_color_map_for_balance() to use cached component_colors
  - Updated carrier_balance() to use cached component_colors

  Architecture

  FlowSystem
      └── topology (cached TopologyAccessor)
              ├── carrier_colors   ─┐
              ├── component_colors  ├── Cached once, invalidated on structure change
              └── bus_colors       ─┘
      └── statistics (cached StatisticsAccessor)
              ├── carrier_colors   ─┐
              ├── component_colors  ├── Delegates to topology
              └── bus_colors       ─┘

  All 79 tests pass.
…created NaN values that caused a coordinate mismatch error.

  Better Solution: Instead of silently filling gaps, we now:

  1. Raise a clear error by default when resampling creates gaps, listing the affected variables
  2. Require explicit fill_gaps parameter if the user wants to proceed:
    - 'ffill' - forward fill (use previous value)
    - 'bfill' - backward fill (use next value)
    - 'interpolate' - linear interpolation
…ransformed (like after copy()), the add_elements method only invalidated if model is not None. After copy(), the model is None but _connected_and_transformed is True, so new elements' transform_data() was never called.
…to specify it (#517)

* Change default size to None. Raise if not set and Status is used

* Make maximum size mandatory (or fixed size). Update tests accordingly

* Adding maximum sizes for FLows which get Status variables (also if only through component status

* Update more tests

* BUGFIX: Minimum or foixed size in storages

* Added validation:

  5. Component with status_parameters now validates that all flows have explicit sizes

* In flixopt/elements.py - Flow._plausibility_checks():
  - relative_minimum > 0 requires size (lines 585-590)
  - relative_maximum < 1 requires size (lines 592-596)
  - load_factor_min requires size (lines 598-603)
  - load_factor_max requires size (lines 605-609)

* Added more validations

* Fix test

* Fix tests to specify size if needed

* Improve check verbosity

* Fix type hint

* Improve conftest.py
* Change default size to None. Raise if not set and Status is used

* Make maximum size mandatory (or fixed size). Update tests accordingly

* Adding maximum sizes for FLows which get Status variables (also if only through component status

* Update more tests

* BUGFIX: Minimum or foixed size in storages

* Added validation:

  5. Component with status_parameters now validates that all flows have explicit sizes

* In flixopt/elements.py - Flow._plausibility_checks():
  - relative_minimum > 0 requires size (lines 585-590)
  - relative_maximum < 1 requires size (lines 592-596)
  - load_factor_min requires size (lines 598-603)
  - load_factor_max requires size (lines 605-609)

* Added more validations

* Fix test

* Fix tests to specify size if needed

* Improve check verbosity

* Fix type hint

* Improve conftest.py

* Added more notebooks

* Improve notebooks

* Improve plotting of Piecewises

* Improve notebooks

* Streamline notebook

* Remove old notebook

* fix notebook

* fix notebook script

* Extract PlotResults class into separate module and reuse

* Improve notebook

* Add carrier units and effect units to accessors

* Add carrier units and effect units to notebooks

* Add carrier units and effect units to notebooks

* Decrease notebook font size

* Fix tests

* Fix docs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants