Skip to content

Conversation

@FBumann
Copy link
Member

@FBumann FBumann commented Nov 20, 2025

Description

Rename Calculation -> Optimization

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

  • Breaking Changes

    • Core APIs renamed (Calculation→Optimization family; CalculationResults→Results family; Aggregation→Clustering) and many legacy property/kwarg aliases removed.
  • New Features

    • New Optimization strategies exposed: full, clustered and segmented workflows; clustering parameters available in the public API.
  • Deprecations

    • Old names remain as deprecation wrappers where noted; numerous legacy kwargs and deprecated properties were removed and will require callers to use the new names.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Warning

Rate limit exceeded

@FBumann has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 59 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between fb35178 and d933036.

📒 Files selected for processing (15)
  • flixopt/calculation.py (5 hunks)
  • flixopt/components.py (0 hunks)
  • flixopt/core.py (1 hunks)
  • flixopt/effects.py (0 hunks)
  • flixopt/elements.py (1 hunks)
  • flixopt/flow_system.py (2 hunks)
  • flixopt/interface.py (1 hunks)
  • flixopt/linear_converters.py (0 hunks)
  • flixopt/plotting.py (0 hunks)
  • flixopt/results.py (17 hunks)
  • pyproject.toml (0 hunks)
  • scripts/extract_changelog.py (0 hunks)
  • tests/test_integration.py (0 hunks)
  • tests/test_io.py (0 hunks)
  • tests/test_scenarios.py (0 hunks)

Walkthrough

Introduces a new optimization module and public classes (Optimization, ClusteredOptimization, SegmentedOptimization), renames Aggregation→Clustering and CalculationResults→Results, and preserves legacy names as deprecated wrappers. Removes many legacy **kwargs/deprecation shims across components, effects, converters, and tests.

Changes

Cohort / File(s) Summary
Public package exports
\flixopt/init.py``
Re-exports new optimization and clustering symbols; updates __all__ to include Optimization, ClusteredOptimization, SegmentedOptimization, ClusteringParameters, and deprecated aliases.
New optimization framework
\flixopt/optimization.py``
Adds _Optimization internals and public Optimization, ClusteredOptimization, SegmentedOptimization classes implementing full-horizon, clustering-based, and segmented optimization flows, modeling, solving, infeasible-save behavior, and results composition.
Backward-compatible Calculation wrappers
\flixopt/calculation.py``
Replaces legacy Calculation family with thin wrappers that inherit from new Optimization classes and emit deprecation warnings while delegating behavior.
Results rename & wrappers
\flixopt/results.py``
Renames CalculationResultsResults and SegmentedCalculationResultsSegmentedResults; adds deprecated wrapper aliases and updates factory methods (from_calculationfrom_optimization / from_file).
Clustering (formerly Aggregation)
\flixopt/clustering.py``
Renames Aggregation→Clustering (classes, parameters, model), updates internal names and logic, and adds deprecated alias wrappers (Aggregation*) that warn and delegate.
Remove legacy kwargs & deprecated accessors
\flixopt/components.py`, `flixopt/core.py`, `flixopt/effects.py`, `flixopt/elements.py`, `flixopt/interface.py`, `flixopt/linear_converters.py``
Drop **kwargs and centralized deprecated-kwarg handling from many constructors; remove numerous deprecated properties/accessors and related wrappers and warnings.
Tests updated/removed
\tests/test_flow_system_resample.py`, `test_deprecations.py`, `tests/test_invest_parameters_deprecation.py``
Adjust one test to expect new attribute (thermal_efficiency); remove large deprecation-related test modules.
Changelog
\CHANGELOG.md``
Documents breaking renames and deprecation timeline (Calculation→Optimization, Aggregation→Clustering, CalculationResults→Results, module renames) and notes that old names remain as deprecated wrappers.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DepWrapper as Deprecated Wrapper\n(Calculation*)
    participant CanonicalOpt as Optimization family
    participant FlowSystem
    participant Solver

    User->>DepWrapper: instantiate legacy class (e.g. FullCalculation)
    Note right of DepWrapper#lightblue: emit deprecation warning
    DepWrapper->>CanonicalOpt: delegate initialization & calls
    DepWrapper->>FlowSystem: copy/prepare flow system
    User->>DepWrapper: do_modeling() / solve()
    DepWrapper->>CanonicalOpt: delegate do_modeling()/solve()
    CanonicalOpt->>FlowSystem: build model
    CanonicalOpt->>Solver: run solve
    CanonicalOpt-->>User: store and return Results
Loading
sequenceDiagram
    participant User
    participant SegOpt as SegmentedOptimization
    participant SubCalc as Optimization (segment)
    participant Solver
    participant FlowSystem

    User->>SegOpt: __init__(flow_system, timesteps_per_segment, overlap, ...)
    SegOpt->>SegOpt: _calculate_timesteps_per_segment()
    SegOpt->>SegOpt: _create_sub_calculations()
    loop per segment i
        SegOpt->>SubCalc: do_modeling()
        SegOpt->>SegOpt: _transfer_start_values(i)
        SubCalc->>Solver: solve segment i
        SegOpt->>SegOpt: collect durations & segment Results
    end
    SegOpt-->>User: return SegmentedResults
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • Deprecation wrapper correctness and exact warning text in flixopt/calculation.py, flixopt/results.py, and flixopt/clustering.py.
    • SegmentedOptimization: overlap/timestep calculation, _transfer_start_values, and final results aggregation.
    • ClusteredOptimization: aggregation-weight calculation, validation of time-step consistency, and optional data-aggregation side effects.
    • Broad API removals: ensure removal of **kwargs and deprecated properties did not inadvertently break expected constructor behavior or typing.

Possibly related PRs

Suggested labels

v3.0.0

Suggested reviewers

  • baumbude
  • PStange

Poem

🐇 I hopped through modules, tidy and spry,
Old names whispered gently, "We won't die."
Optimization blooms, segments pass the hat,
Clustering hums where aggregation sat,
Results parade onward — a carrot for that! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Feature/rename calculation' accurately reflects the primary objective of renaming Calculation classes to Optimization throughout the codebase.
Description check ✅ Passed The PR description follows the template structure with all main sections present (Description, Type of Change, Related Issues, Testing, Checklist), though some items are incomplete or unchecked.

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.

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

Caution

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

⚠️ Outside diff range comments (2)
flixopt/results.py (2)

54-80: Results docstring and deprecation message still reference non‑existent OptimizationResults

The Results.__init__ docstring and the deprecation warning inside Results.__init__ both tell users to switch from CalculationResults to OptimizationResults, but the new public class name is Results, and CalculationResults is now just a thin deprecated subclass.

This is confusing for users and tooling and should be aligned with the actual API.

Suggested diff to align wording with the current classes:

@@
-    def __init__(
+    def __init__(
         self,
         solution: xr.Dataset,
@@
-    ):
-        """Initialize CalculationResults with optimization data.
-        Usually, this class is instantiated by the Calculation class, or by loading from file.
+    ):
+        """Initialize Results with optimization data.
+        Usually, this class is instantiated by an Optimization object via `Results.from_optimization()`
+        or by loading from file using `Results.from_file()`.
@@
-        Deprecated:
-            flow_system: Use flow_system_data instead.
-
-        Note:
-            CalculationResults is deprecated. Use OptimizationResults instead.
+        Deprecated:
+            flow_system: Use flow_system_data instead.
+
+        Note:
+            The legacy alias `CalculationResults` is deprecated. Use `Results` instead.
@@
-        # Deprecation warning for CalculationResults
-        if self.__class__.__name__ == 'CalculationResults':
-            warnings.warn(
-                'CalculationResults is deprecated and will be removed in a future version. '
-                'Use OptimizationResults instead.',
-                DeprecationWarning,
-                stacklevel=2,
-            )
+        # Deprecation warning for the legacy CalculationResults alias
+        if self.__class__.__name__ == 'CalculationResults':
+            warnings.warn(
+                'CalculationResults is deprecated and will be removed in a future version. '
+                'Use Results instead.',
+                DeprecationWarning,
+                stacklevel=2,
+            )

Also applies to: 212-227, 229-235, 1108-1138


2000-2057: SegmentedResults / SegmentedCalculationResults deprecation path is inconsistent and may warn twice

Two issues around SegmentedResults and the deprecated SegmentedCalculationResults alias:

  1. Conflicting names in warnings

    • In SegmentedResults.__init__ (Line 2150 and following), the deprecation message says “Use SegmentedOptimizationResults instead.” but there is no such class; the new public name is SegmentedResults.
    • In SegmentedCalculationResults.__init__ (Lines 2393-2401), the message correctly says “Use SegmentedResults instead.”

    This inconsistency will be confusing.

  2. Potential double warning for SegmentedCalculationResults

    Instantiating SegmentedCalculationResults will:

    • Trigger the warning in SegmentedCalculationResults.__init__.
    • Then call super().__init__, where SegmentedResults.__init__ checks self.__class__.__name__ == 'SegmentedCalculationResults' again and emits a second warning.
  3. Type hint still tied to CalculationResults

    SegmentedResults.__init__ currently takes segment_results: list[CalculationResults], but SegmentedResults.from_optimization() actually passes a list of Results instances ([calc.results for calc in optimization.sub_calculations]). Typing works at runtime, but the hint is stale.

Suggested diff to simplify the deprecation path and modernize the type hint:

@@
-    def __init__(
-        self,
-        segment_results: list[CalculationResults],
-        all_timesteps: pd.DatetimeIndex,
-        timesteps_per_segment: int,
-        overlap_timesteps: int,
-        name: str,
-        folder: pathlib.Path | None = None,
-    ):
-        # Deprecation warning for SegmentedCalculationResults
-        if self.__class__.__name__ == 'SegmentedCalculationResults':
-            warnings.warn(
-                'SegmentedCalculationResults is deprecated and will be removed in a future version. '
-                'Use SegmentedOptimizationResults instead.',
-                DeprecationWarning,
-                stacklevel=2,
-            )
-
-        self.segment_results = segment_results
+    def __init__(
+        self,
+        segment_results: list[Results],
+        all_timesteps: pd.DatetimeIndex,
+        timesteps_per_segment: int,
+        overlap_timesteps: int,
+        name: str,
+        folder: pathlib.Path | None = None,
+    ):
+        self.segment_results = segment_results
@@ class SegmentedCalculationResults(SegmentedResults):
-    def __init__(self, *args, **kwargs):
-        # Only warn if directly instantiating SegmentedCalculationResults (not subclasses)
-        if self.__class__.__name__ == 'SegmentedCalculationResults':
-            warnings.warn(
-                'SegmentedCalculationResults is deprecated and will be removed in a future version. '
-                'Use SegmentedResults instead.',
-                DeprecationWarning,
-                stacklevel=2,
-            )
-        super().__init__(*args, **kwargs)
+    def __init__(self, *args, **kwargs):
+        # Only warn if directly instantiating SegmentedCalculationResults (not subclasses)
+        if self.__class__.__name__ == 'SegmentedCalculationResults':
+            warnings.warn(
+                'SegmentedCalculationResults is deprecated and will be removed in a future version. '
+                'Use SegmentedResults instead.',
+                DeprecationWarning,
+                stacklevel=2,
+            )
+        super().__init__(*args, **kwargs)

This way:

  • Users see a single, consistent warning when using the deprecated alias.
  • The constructor type hint matches what from_optimization() actually passes.

Also applies to: 2142-2158, 2386-2417

🧹 Nitpick comments (2)
flixopt/optimization.py (2)

25-31: Redundant InvestmentModel import in _Optimization.main_results

InvestmentModel is imported at the module level (Line 30) and again inside main_results via from flixopt.features import InvestmentModel (Line 99). The inner import is redundant and slightly obscures where the dependency comes from.

You can safely rely on the existing top‑level import and drop the inner one.

-    @property
-    def main_results(self) -> dict[str, int | float | dict]:
-        from flixopt.features import InvestmentModel
-
+    @property
+    def main_results(self) -> dict[str, int | float | dict]:

Also applies to: 97-142


236-278: Optimization / SegmentedOptimization workflow and BC behavior look correct; only messages still mention “Calculation”

The core workflow changes look good:

  • Optimization.solve() auto‑calls do_modeling() when needed, uses _Solver.options, logs main results, and populates self.results = Results.from_optimization(self).
  • SegmentedOptimization.do_modeling_and_solve() builds per‑segment Optimization instances, solves them with optional progress bar suppression, aggregates durations, and sets self.results = SegmentedResults.from_optimization(self).

This preserves the old behavior while routing everything through the new Results/SegmentedResults containers.

The only minor nit is some log/exception texts still using “Calculation”, e.g.:

  • Line 257: CalculationResultsPaths
  • Line 607: 'Investments are not supported in Segmented Calculation!'

CalculationResultsPaths is a deliberate BC name, but the human‑readable message in _solve_single_segment() could be updated to match the new terminology (e.g. “SegmentedOptimization”) when you next touch this area.

Also applies to: 423-689

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50bed32 and bd25e91.

📒 Files selected for processing (4)
  • flixopt/__init__.py (3 hunks)
  • flixopt/calculation.py (5 hunks)
  • flixopt/optimization.py (1 hunks)
  • flixopt/results.py (15 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
flixopt/calculation.py (2)
flixopt/optimization.py (5)
  • ClusteredOptimization (280-420)
  • Optimization (170-277)
  • _Optimization (43-167)
  • SegmentedOptimization (423-746)
  • active_timesteps (157-163)
flixopt/aggregation.py (1)
  • AggregationParameters (239-292)
flixopt/optimization.py (8)
flixopt/aggregation.py (7)
  • Aggregation (41-236)
  • AggregationModel (295-389)
  • AggregationParameters (239-292)
  • labels_for_high_peaks (283-284)
  • labels_for_low_peaks (287-288)
  • cluster (83-107)
  • plot (144-178)
flixopt/config.py (3)
  • CONFIG (58-407)
  • Solving (155-177)
  • Plotting (179-213)
flixopt/core.py (6)
  • DataConverter (167-574)
  • TimeSeriesData (36-164)
  • drop_constant_arrays (604-634)
  • to_dataarray (410-524)
  • is_timeseries_data (132-134)
  • from_dataarray (117-129)
flixopt/features.py (1)
  • InvestmentModel (24-144)
flixopt/results.py (11)
  • flow_system (352-369)
  • Results (54-1105)
  • SegmentedResults (2000-2383)
  • objective (321-328)
  • size (1989-1997)
  • constraints (338-342)
  • constraints (1162-1170)
  • variables (331-335)
  • variables (1151-1159)
  • from_optimization (184-200)
  • from_optimization (2097-2113)
flixopt/flow_system.py (6)
  • FlowSystem (38-1616)
  • sel (1320-1348)
  • connect_and_transform (724-737)
  • coords (1117-1123)
  • _connect_network (984-1019)
  • flows (1085-1091)
flixopt/solvers.py (2)
  • _Solver (16-41)
  • options (34-36)
flixopt/io.py (5)
  • round_nested_floats (42-86)
  • CalculationResultsPaths (599-650)
  • document_linopy_model (473-524)
  • format_yaml_string (254-301)
  • suppress_output (999-1051)
flixopt/__init__.py (1)
flixopt/optimization.py (3)
  • ClusteredOptimization (280-420)
  • Optimization (170-277)
  • SegmentedOptimization (423-746)
flixopt/results.py (4)
flixopt/structure.py (6)
  • CompositeContainerMixin (1181-1357)
  • flow_system (327-345)
  • to_dataset (775-798)
  • to_netcdf (800-816)
  • variables (1497-1503)
  • constraints (1488-1494)
flixopt/flow_system.py (2)
  • to_dataset (539-551)
  • to_netcdf (612-626)
flixopt/optimization.py (2)
  • summary (144-154)
  • all_timesteps (745-746)
flixopt/io.py (2)
  • document_linopy_model (473-524)
  • load_json (94-110)
🔇 Additional comments (3)
flixopt/__init__.py (1)

18-19: Public API surface cleanly exposes new and deprecated classes

The module now re‑exports the new Optimization, ClusteredOptimization, and SegmentedOptimization classes while still exposing legacy FullCalculation, AggregatedCalculation, and SegmentedCalculation under a clearly marked deprecated section in __all__. This is a solid BC strategy and keeps the transition path obvious for users.

Also applies to: 35-37, 52-59

flixopt/calculation.py (2)

41-48: Deprecation helper and Calculation/FullCalculation wrappers look sound

_deprecation_warning centralizes messaging and the stacklevel=3 will correctly point at user call sites when these classes are instantiated via Calculation/FullCalculation. Both wrappers preserve the original constructor signature and delegate cleanly to _Optimization, so runtime behavior should match the new Optimization class aside from the warning.

Also applies to: 50-77


79-109: AggregatedCalculation / SegmentedCalculation wrappers preserve behavior with clear deprecation path

AggregatedCalculation and SegmentedCalculation mirror the parameter lists of ClusteredOptimization and SegmentedOptimization and simply forward to the new implementations after emitting a deprecation warning. The super calls are correctly ordered, so aggregation/segmentation behavior is preserved while steering users toward the new names via both docstrings and runtime warnings.

Also applies to: 111-144, 146-172, 174-174

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd25e91 and 1fac0cf.

📒 Files selected for processing (1)
  • CHANGELOG.md (1 hunks)
🔇 Additional comments (1)
CHANGELOG.md (1)

62-70: Address missing test verification before merge.

According to the PR objectives, both "I have tested my changes" and "Existing tests still pass" are unchecked. Given that this is a major refactoring affecting the public API with widespread class renames, merging without verification that existing tests pass introduces significant risk of breaking changes going undetected.

Please confirm that:

  1. All existing tests pass with these class renames
  2. Backward compatibility with deprecated wrappers has been tested
  3. Public API exports in __init__.py are correctly updated

CHANGELOG.md Outdated
Comment on lines 62 to 70
**Class renaming:**
- `Calculation` → `Optimization`
- `AggregatedCalculation` → `AggregatedOptimization`
- `SegmentedCalculation` → `SegmentedOptimization`
- `CalculationResults` → `Results`
- `SegmentedCalculationResults` → `SegmentedResults`
- Module: `calculation.py` → `optimization.py`
Old names remain available with deprecation warnings (removed in v5.0.0).
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 | 🟠 Major

🧩 Analysis chain

Verify class rename accuracy against implementation.

The changelog lists AggregatedOptimization as the new name, but the AI-generated summary references ClusteredOptimization (described as "with aggregation"). Additionally, the summary mentions FullCalculation as a backward-compatible wrapper, but this class isn't explicitly documented in the changelog's rename list.

Before merging, please confirm:

  1. Is the correct new name AggregatedOptimization or ClusteredOptimization?
  2. Is FullCalculation being renamed or remaining as a deprecated wrapper alongside the new names?
  3. Are there any other classes in the Calculation family not documented here?

To verify, you can inspect the actual implementation:


🏁 Script executed:

#!/bin/bash
# Verify the actual class names in the new optimization module
echo "=== Classes in flixopt/optimization.py ===" 
rg -nP '^\s*class\s+\w+' flixopt/optimization.py

echo "=== Deprecated wrappers in flixopt/calculation.py ==="
rg -nP '^\s*class\s+\w+' flixopt/calculation.py

echo "=== Results classes in flixopt/results.py ==="
rg -nP '^\s*class\s+(CalculationResults|Results|SegmentedCalculationResults|SegmentedResults)' flixopt/results.py

Length of output: 792


Correct the changelog: AggregatedCalculation renames to ClusteredOptimization, not AggregatedOptimization. Also, FullCalculation is missing from the rename list.

The implementation verification shows two issues in lines 62-70:

  1. Line 65 contains incorrect class name: The changelog states AggregatedCalculationAggregatedOptimization, but the actual implementation (optimization.py line 280) shows it renames to ClusteredOptimization. The old AggregatedCalculation inherits from _ClusteredOptimization, confirming the correct target name.

  2. Line 62-70 omits FullCalculation: The class FullCalculation (calculation.py line 79) exists in the codebase but is not listed in the changelog's rename documentation. While it remains unchanged as a deprecated wrapper, it should be explicitly documented to provide complete migration guidance.

Update the changelog to:

  • Change line 65 from AggregatedCalculationAggregatedOptimization to AggregatedCalculationClusteredOptimization
  • Add FullCalculation to the list with clarification that it remains available as a deprecated wrapper
🤖 Prompt for AI Agents
In CHANGELOG.md around lines 62 to 70, correct the rename mapping and add the
missing class: change the line that currently documents "AggregatedCalculation →
AggregatedOptimization" to "AggregatedCalculation → ClusteredOptimization" to
match the implementation, and insert an entry for "FullCalculation" indicating
it remains available as a deprecated wrapper (not renamed) so the list documents
this class for migration completeness.

…s to use "clustering" terminology for better consistency with the ClusteredOptimization class

  name.

  Changes Made:

  1. Module renamed:
    - aggregation.py → clustering.py (via git mv)
  2. Classes renamed:
    - Aggregation → Clustering
    - AggregationParameters → ClusteringParameters
    - AggregationModel → ClusteringModel
  3. Updated references in:
    - optimization.py: All imports, attributes, and method parameters
    - calculation.py: Import statement (for backward compat type hints)
    - __init__.py: Exports (includes both old and new names)
    - Module docstrings updated
  4. Attribute updates:
    - ClusteredOptimization.clustering_parameters
    - ClusteredOptimization.clustering
    - ClusteredOptimization.clustering_model
    - self.durations['clustering']
    - Label strings: 'Clustering' instead of 'Aggregation'
  5. Backward compatibility:
    - Added deprecated aliases in clustering.py for all old class names
    - Old names exported from __init__.py with deprecation warnings
    - All warnings point to v6.0.0 removal
  6. Changelog updated: Added all clustering renames to the breaking changes section
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: 5

Caution

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

⚠️ Outside diff range comments (2)
flixopt/clustering.py (1)

320-351: Penalty expression is non‑scalar and will break add_share_to_penalty.

In ClusteringModel.do_modeling, this block:

penalty = self.clustering_parameters.penalty_of_period_freedom
if (self.clustering_parameters.percentage_of_period_freedom > 0) and penalty != 0:
    for variable in self.variables_direct.values():
        self._model.effects.add_share_to_penalty('Clustering', variable * penalty)

passes a time‑indexed variable to add_share_to_penalty. Since add_share_to_penalty requires a scalar expression (expression.ndim == 0), variable * penalty will still have a time dimension and raise a TypeError whenever period freedom and a non‑zero penalty are used.

You likely want to penalize the total number of corrections per variable. That requires reducing over dimensions before adding the share, e.g.:

-        for variable in self.variables_direct.values():
-            self._model.effects.add_share_to_penalty('Clustering', variable * penalty)
+        for variable in self.variables_direct.values():
+            # Aggregate correction indicators to a scalar before adding as penalty share
+            penalty_expr = variable.sum()
+            self._model.effects.add_share_to_penalty('Clustering', penalty_expr * penalty)

(If .sum() still leaves dimensions, apply an additional reduction so the final expression passed in is truly scalar.)

Also applies to: 386-392

flixopt/results.py (1)

28-28: Update TYPE_CHECKING imports to reference the new Optimization classes.

The TYPE_CHECKING block imports the deprecated Calculation and SegmentedCalculation names from .calculation, but these are not actually used in any type hints throughout results.py. The actual type annotations use the new Optimization and SegmentedOptimization classes.

Update line 28 to import the new names instead, or remove the imports entirely if they're unused.

🧹 Nitpick comments (2)
flixopt/clustering.py (1)

320-327: Optional: make components_to_clusterize explicit and avoid truthiness checks.

Using if not self.components_to_clusterize: means an empty list will cluster all components, which is surprising if someone builds the list programmatically.

Consider treating None as “all components” and a list (even if empty) as “exactly these components”, e.g.:

-        if not self.components_to_clusterize:
-            components = self.flow_system.components.values()
-        else:
-            components = [component for component in self.components_to_clusterize]
+        if self.components_to_clusterize is None:
+            components = self.flow_system.components.values()
+        else:
+            components = list(self.components_to_clusterize)

This keeps behavior clear and avoids relying on list truthiness.

flixopt/results.py (1)

213-229: Consider clarifying instantiation patterns in docstring.

The docstring mentions that "this class is instantiated by an Optimization object via Results.from_optimization()", which is helpful. However, it could be slightly clearer that direct instantiation via __init__ is primarily for internal use or when loading from files.

Consider this enhancement:

     def __init__(
         self,
         solution: xr.Dataset,
         flow_system_data: xr.Dataset,
         name: str,
         summary: dict,
         folder: pathlib.Path | None = None,
         model: linopy.Model | None = None,
         **kwargs,  # To accept old "flow_system" parameter
     ):
         """Initialize Results with optimization data.
-        Usually, this class is instantiated by an Optimization object via `Results.from_optimization()`
-        or by loading from file using `Results.from_file()`.
+        
+        Note:
+            Typically, you should use the factory methods `Results.from_optimization()` 
+            or `Results.from_file()` instead of calling this constructor directly.
 
         Args:
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fac0cf and c7c8af6.

📒 Files selected for processing (6)
  • CHANGELOG.md (1 hunks)
  • flixopt/__init__.py (3 hunks)
  • flixopt/calculation.py (5 hunks)
  • flixopt/clustering.py (9 hunks)
  • flixopt/optimization.py (1 hunks)
  • flixopt/results.py (17 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
flixopt/optimization.py (8)
flixopt/clustering.py (8)
  • Clustering (44-239)
  • ClusteringModel (298-392)
  • ClusteringParameters (242-295)
  • do_modeling (320-350)
  • labels_for_high_peaks (286-287)
  • labels_for_low_peaks (290-291)
  • cluster (86-110)
  • plot (147-181)
flixopt/core.py (6)
  • DataConverter (167-574)
  • TimeSeriesData (36-164)
  • drop_constant_arrays (604-634)
  • to_dataarray (410-524)
  • is_timeseries_data (132-134)
  • from_dataarray (117-129)
flixopt/features.py (1)
  • InvestmentModel (24-144)
flixopt/results.py (11)
  • flow_system (353-370)
  • Results (55-1106)
  • SegmentedResults (2001-2375)
  • objective (322-329)
  • size (1990-1998)
  • constraints (339-343)
  • constraints (1163-1171)
  • variables (332-336)
  • variables (1152-1160)
  • from_optimization (185-201)
  • from_optimization (2098-2114)
flixopt/flow_system.py (6)
  • FlowSystem (38-1616)
  • sel (1320-1348)
  • connect_and_transform (724-737)
  • coords (1117-1123)
  • _connect_network (984-1019)
  • flows (1085-1091)
flixopt/solvers.py (2)
  • _Solver (16-41)
  • options (34-36)
flixopt/structure.py (3)
  • FlowSystemModel (81-274)
  • copy (934-946)
  • solution (156-179)
flixopt/io.py (4)
  • round_nested_floats (42-86)
  • CalculationResultsPaths (599-650)
  • document_linopy_model (473-524)
  • suppress_output (999-1051)
flixopt/calculation.py (2)
flixopt/optimization.py (5)
  • ClusteredOptimization (278-417)
  • Optimization (168-275)
  • _Optimization (43-165)
  • SegmentedOptimization (420-743)
  • active_timesteps (155-161)
flixopt/clustering.py (2)
  • AggregationParameters (415-420)
  • ClusteringParameters (242-295)
flixopt/results.py (4)
flixopt/structure.py (6)
  • CompositeContainerMixin (1181-1357)
  • flow_system (327-345)
  • to_dataset (775-798)
  • to_netcdf (800-816)
  • variables (1497-1503)
  • constraints (1488-1494)
flixopt/flow_system.py (2)
  • to_dataset (539-551)
  • to_netcdf (612-626)
flixopt/optimization.py (2)
  • summary (142-152)
  • all_timesteps (742-743)
flixopt/io.py (2)
  • document_linopy_model (473-524)
  • load_json (94-110)
flixopt/__init__.py (3)
flixopt/calculation.py (3)
  • AggregatedCalculation (112-144)
  • FullCalculation (80-109)
  • SegmentedCalculation (147-172)
flixopt/clustering.py (2)
  • AggregationParameters (415-420)
  • ClusteringParameters (242-295)
flixopt/optimization.py (3)
  • ClusteredOptimization (278-417)
  • Optimization (168-275)
  • SegmentedOptimization (420-743)
flixopt/clustering.py (2)
flixopt/structure.py (4)
  • Submodel (1360-1539)
  • FlowSystemModel (81-274)
  • variables_direct (1478-1480)
  • add_constraints (1395-1404)
flixopt/effects.py (1)
  • add_share_to_penalty (776-779)
⏰ 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). (4)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
🔇 Additional comments (2)
flixopt/calculation.py (1)

2-12: Deprecation wrappers for Calculation look consistent and low‑risk.*

The module‑level docs, _deprecation_warning helper, and the four wrapper classes correctly route to the new optimization bases while emitting clear deprecation warnings. Signatures and super‑calls match the underlying Optimization, ClusteredOptimization, and SegmentedOptimization constructors, so existing imports from flixopt.calculation should continue to work as intended.

Also applies to: 19-28, 42-49, 51-175

flixopt/optimization.py (1)

1-9: Optimization, ClusteredOptimization, and SegmentedOptimization are wired coherently to the existing modeling stack.

The new optimization module cleanly centralizes common behavior in _Optimization and exposes the three strategies with consistent modeling/solving flows, deprecation handling for active_timesteps, and sensible logging/error handling. The transition from the old Calculation classes to these Optimization types appears mechanically sound and aligns with the clustering and results infrastructure already in the codebase.

Also applies to: 43-276, 278-417, 420-743

Comment on lines +62 to +75
**Class and module renaming:**
- `FullCalculation` → `Optimization`
- `AggregatedCalculation` → `ClusteredOptimization`
- `SegmentedCalculation` → `SegmentedOptimization`
- `CalculationResults` → `Results`
- `SegmentedCalculationResults` → `SegmentedResults`
- `Aggregation` → `Clustering`
- `AggregationParameters` → `ClusteringParameters`
- `AggregationModel` → `ClusteringModel`
- Module: `calculation.py` → `optimization.py`
- Module: `aggregation.py` → `clustering.py`
Old names remain available with deprecation warnings (removed in v5.0.0).
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 | 🟡 Minor

Rename list matches implementation; consider also documenting Calculation → Optimization.

The class/module rename bullets now line up with the new API (AggregatedCalculation → ClusteredOptimization, FullCalculation → Optimization, results and aggregation/clustering mappings) and the deprecation behavior in code.

For completeness of the migration notes, consider adding an entry for the plain Calculation → Optimization rename as well, since flixopt/calculation.py still exposes Calculation as a deprecated wrapper.

🤖 Prompt for AI Agents
In CHANGELOG.md around lines 62 to 75, the rename list omits the plain
Calculation → Optimization mapping; add a bullet documenting that Calculation
was renamed to Optimization (and note that the old Calculation remains available
as a deprecated wrapper in flixopt/calculation.py and will be removed in v5.0.0)
so the migration notes fully cover all deprecated aliases.

Comment on lines +17 to 20
# Import old Calculation classes for backwards compatibility (deprecated)
from .calculation import AggregatedCalculation, FullCalculation, SegmentedCalculation
from .clustering import AggregationParameters, ClusteringParameters # AggregationParameters is deprecated
from .components import (
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 | 🟠 Major

Calculation is not re‑exported at package root, breaking top‑level backward compatibility.

This module promises that legacy Calculation* names remain available as deprecated wrappers, but __init__.py only imports/exports FullCalculation, AggregatedCalculation, and SegmentedCalculation. Code that previously did:

from flixopt import Calculation
# or
import flixopt as fx; fx.Calculation(...)

will now fail, even though flixopt.calculation.Calculation still exists.

To keep the public API backward compatible, consider:

-from .calculation import AggregatedCalculation, FullCalculation, SegmentedCalculation
+from .calculation import AggregatedCalculation, Calculation, FullCalculation, SegmentedCalculation
...
-    # Old Calculation classes (deprecated, for backwards compatibility)
-    'FullCalculation',
-    'AggregatedCalculation',
-    'SegmentedCalculation',
+    # Old Calculation classes (deprecated, for backwards compatibility)
+    'Calculation',
+    'FullCalculation',
+    'AggregatedCalculation',
+    'SegmentedCalculation',

This aligns the package surface with the deprecation story in flixopt/calculation.py and the changelog.

Also applies to: 52-60, 66-67

🤖 Prompt for AI Agents
In flixopt/__init__.py around lines 17 to 20, the legacy alias Calculation is
not re-exported which breaks top-level backward compatibility; import
Calculation from .calculation and add it to the module exports (and __all__ if
present) alongside FullCalculation, AggregatedCalculation, and
SegmentedCalculation so from flixopt import Calculation and attribute access
like flixopt.Calculation continue to work as before.

# Conflicts:
#	flixopt/core.py
#	flixopt/interface.py
#	test_deprecations.py
…calculation

# Conflicts:
#	flixopt/calculation.py
…calculation

# Conflicts:
#	flixopt/calculation.py
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

♻️ Duplicate comments (4)
flixopt/__init__.py (1)

18-20: Missing Calculation export breaks backwards compatibility.

The Calculation class exists in flixopt/calculation.py but is not imported or exported at the package level. Users who previously used from flixopt import Calculation or fx.Calculation(...) will encounter ImportError or AttributeError.

Apply this diff to restore backwards compatibility:

 # Import old Calculation classes for backwards compatibility (deprecated)
-from .calculation import AggregatedCalculation, FullCalculation, SegmentedCalculation
+from .calculation import AggregatedCalculation, Calculation, FullCalculation, SegmentedCalculation
 from .clustering import AggregationParameters, ClusteringParameters  # AggregationParameters is deprecated

And update __all__:

     'SegmentedOptimization',
     # Old Calculation classes (deprecated, for backwards compatibility)
+    'Calculation',
     'FullCalculation',
     'AggregatedCalculation',

Also applies to: 57-60

flixopt/results.py (3)

154-163: Update docstring to reference the new class name.

The docstring on line 155 still mentions "CalculationResults" but should reference "Results" to match the renamed class.

Apply this diff:

     @classmethod
     def from_file(cls, folder: str | pathlib.Path, name: str) -> Results:
-        """Load CalculationResults from saved files.
+        """Load Results from saved files.
 
         Args:
             folder: Directory containing saved files.

232-238: Remove duplicate deprecation warning check.

This deprecation warning check in Results.__init__ causes duplicate warnings when users instantiate CalculationResults:

  1. CalculationResults.__init__ (lines 1122-1127) warns when self.__class__.__name__ == 'CalculationResults'
  2. It calls super().__init__() which triggers this warning again (lines 232-238)
  3. Result: Users see the same deprecation warning twice

The correct pattern (as used in SegmentedResults) is to only warn in the deprecated wrapper class, not in the base class.

Apply this diff to remove the redundant check:

-        # Deprecation warning for the legacy CalculationResults alias
-        if self.__class__.__name__ == 'CalculationResults':
-            warnings.warn(
-                f'CalculationResults is deprecated and will be removed in v{DEPRECATION_REMOVAL_VERSION}. Use Results instead.',
-                DeprecationWarning,
-                stacklevel=2,
-            )
-
         # Handle potential old "flow_system" parameter for backward compatibility

2121-2130: Update docstring to reference the new class name.

The docstring on line 2122 still mentions "SegmentedCalculationResults" but should reference "SegmentedResults" to match the renamed class.

Apply this diff:

     @classmethod
     def from_file(cls, folder: str | pathlib.Path, name: str) -> SegmentedResults:
-        """Load SegmentedCalculationResults from saved files.
+        """Load SegmentedResults from saved files.
 
         Args:
             folder: Directory containing saved files.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7c8af6 and fb35178.

📒 Files selected for processing (15)
  • CHANGELOG.md (1 hunks)
  • flixopt/__init__.py (3 hunks)
  • flixopt/calculation.py (5 hunks)
  • flixopt/clustering.py (10 hunks)
  • flixopt/components.py (0 hunks)
  • flixopt/core.py (0 hunks)
  • flixopt/effects.py (0 hunks)
  • flixopt/elements.py (0 hunks)
  • flixopt/interface.py (0 hunks)
  • flixopt/linear_converters.py (0 hunks)
  • flixopt/optimization.py (1 hunks)
  • flixopt/results.py (17 hunks)
  • test_deprecations.py (0 hunks)
  • tests/test_flow_system_resample.py (1 hunks)
  • tests/test_invest_parameters_deprecation.py (0 hunks)
💤 Files with no reviewable changes (8)
  • flixopt/core.py
  • tests/test_invest_parameters_deprecation.py
  • flixopt/interface.py
  • test_deprecations.py
  • flixopt/elements.py
  • flixopt/components.py
  • flixopt/effects.py
  • flixopt/linear_converters.py
🧰 Additional context used
🧬 Code graph analysis (5)
flixopt/__init__.py (3)
flixopt/calculation.py (3)
  • AggregatedCalculation (115-147)
  • FullCalculation (83-112)
  • SegmentedCalculation (150-175)
flixopt/clustering.py (2)
  • AggregationParameters (418-423)
  • ClusteringParameters (245-298)
flixopt/optimization.py (3)
  • ClusteredOptimization (278-418)
  • Optimization (170-275)
  • SegmentedOptimization (421-744)
flixopt/clustering.py (2)
flixopt/structure.py (2)
  • variables_direct (1480-1482)
  • add_constraints (1397-1406)
flixopt/effects.py (1)
  • add_share_to_penalty (599-602)
flixopt/calculation.py (2)
flixopt/optimization.py (5)
  • ClusteredOptimization (278-418)
  • Optimization (170-275)
  • _Optimization (45-167)
  • SegmentedOptimization (421-744)
  • active_timesteps (157-163)
flixopt/clustering.py (2)
  • AggregationParameters (418-423)
  • ClusteringParameters (245-298)
flixopt/results.py (4)
flixopt/structure.py (6)
  • CompositeContainerMixin (1183-1359)
  • flow_system (329-347)
  • to_dataset (777-800)
  • to_netcdf (802-818)
  • variables (1499-1505)
  • constraints (1490-1496)
flixopt/flow_system.py (2)
  • to_dataset (541-553)
  • to_netcdf (614-628)
flixopt/optimization.py (2)
  • summary (144-154)
  • all_timesteps (743-744)
flixopt/io.py (2)
  • document_linopy_model (475-526)
  • load_json (96-112)
flixopt/optimization.py (6)
flixopt/clustering.py (8)
  • Clustering (46-242)
  • ClusteringModel (301-395)
  • ClusteringParameters (245-298)
  • do_modeling (323-353)
  • labels_for_high_peaks (289-290)
  • labels_for_low_peaks (293-294)
  • cluster (88-113)
  • plot (150-184)
flixopt/core.py (6)
  • DataConverter (127-534)
  • TimeSeriesData (36-124)
  • drop_constant_arrays (564-594)
  • to_dataarray (370-484)
  • is_timeseries_data (112-114)
  • from_dataarray (97-109)
flixopt/features.py (1)
  • InvestmentModel (24-144)
flixopt/results.py (10)
  • flow_system (355-374)
  • Results (57-1110)
  • SegmentedResults (2005-2379)
  • size (1994-2002)
  • constraints (341-345)
  • constraints (1167-1175)
  • variables (334-338)
  • variables (1156-1164)
  • from_optimization (187-203)
  • from_optimization (2102-2118)
flixopt/flow_system.py (6)
  • FlowSystem (40-1618)
  • sel (1322-1350)
  • connect_and_transform (726-739)
  • coords (1119-1125)
  • _connect_network (986-1021)
  • flows (1087-1093)
flixopt/io.py (1)
  • CalculationResultsPaths (601-652)
🪛 GitHub Actions: Python Package CI/CD
flixopt/calculation.py

[error] 33-33: F401 'typing.Any' imported but unused.

🪛 GitHub Check: lint
flixopt/calculation.py

[failure] 40-40: Ruff (F401)
flixopt/calculation.py:40:26: F401 .solvers._Solver imported but unused


[failure] 37-37: Ruff (F401)
flixopt/calculation.py:37:52: F401 .clustering.ClusteringParameters imported but unused


[failure] 33-33: Ruff (F401)
flixopt/calculation.py:33:35: F401 typing.Any imported but unused

🔇 Additional comments (11)
flixopt/clustering.py (3)

1-17: LGTM!

The module docstring and imports have been updated correctly to reflect the Clustering terminology. The addition of deprecation warning infrastructure is well-implemented.


46-395: LGTM!

The class renamings (Aggregation → Clustering, AggregationParameters → ClusteringParameters, AggregationModel → ClusteringModel) are implemented consistently. All internal references, attributes, and model labels have been updated to use the new clustering terminology.


398-431: LGTM!

The deprecation wrappers are well-implemented. Each deprecated class inherits from its renamed counterpart and emits a clear deprecation warning. The stacklevel=3 is appropriate for the call chain (user code → wrapper __init___create_deprecation_warning).

CHANGELOG.md (1)

62-75: LGTM!

The Breaking Changes section clearly documents all class and module renamings with a clear deprecation policy. The documentation aligns with the implementation across the codebase.

flixopt/calculation.py (2)

45-176: LGTM!

The deprecation wrappers are well-implemented. Each deprecated class properly inherits from its renamed counterpart, emits a clear deprecation warning, and delegates to the parent constructor. The pattern is consistent across all wrapper classes.


178-178: LGTM!

The __all__ export list correctly includes all deprecated wrapper classes for backwards compatibility.

flixopt/optimization.py (4)

1-43: LGTM!

The module docstring and imports are well-organized. The docstring correctly describes the three optimization types (Optimization, ClusteredOptimization, SegmentedOptimization) with clear explanations of their purposes.


45-168: LGTM!

The _Optimization base class provides a solid foundation for all optimization types. Key features:

  • Proper FlowSystem validation and duplication to prevent sharing across calculations
  • Clear deprecation handling for active_timesteps parameter
  • Comprehensive main_results property for solution summary
  • Good error handling (e.g., folder validation)

170-418: LGTM!

Both Optimization and ClusteredOptimization classes are well-implemented:

Optimization highlights:

  • Auto-modeling when calling solve() improves user experience
  • Infeasibility handling saves model state for debugging
  • fix_sizes() method enables staged optimization workflows

ClusteredOptimization highlights:

  • Proper validation of time step consistency for clustering
  • Clear error messages for unsupported features (scenarios)
  • Comprehensive aggregation weight calculation from dataset attributes

421-744: LGTM!

The SegmentedOptimization class is well-designed for handling large-scale problems:

Strengths:

  • Comprehensive docstring with usage examples and design considerations
  • Proper investment parameter detection and warning (critical limitation)
  • Flexible output modes (progress bar vs. detailed logging)
  • Robust value transfer mechanism between segments for continuity
  • Good balance between segment overlap and computational efficiency

Implementation quality:

  • Clean separation of concerns across helper methods
  • Proper error handling and validation
  • Duration tracking and aggregation across segments
flixopt/results.py (1)

57-149: Well-structured refactoring with proper backwards compatibility.

The renaming from CalculationResultsResults and SegmentedCalculationResultsSegmentedResults is cleanly implemented:

  • Deprecated wrapper classes properly inherit from the new base classes
  • Factory methods correctly renamed: from_calculation()from_optimization()
  • Legacy methods in wrappers appropriately redirect to new equivalents
  • Type hints consistently updated across _ElementResults, _NodeResults, FlowResults, and SegmentedResults
  • Documentation and examples updated to reference new class names

The deprecation strategy provides a smooth migration path for users while clearly signaling the API changes.

Also applies to: 187-203, 1113-1144, 2005-2118, 2382-2414

@FBumann
Copy link
Member Author

FBumann commented Nov 22, 2025

Continued in #484

@FBumann FBumann closed this Nov 22, 2025
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