Skip to content

feat: accept a tsam_xarray ClusteringResult in apply_clustering() - #777

Merged
FBumann merged 1 commit into
dependabot/pip/tsam-4.0.0from
feat/apply-clustering-from-tsam-result
Sep 8, 2026
Merged

feat: accept a tsam_xarray ClusteringResult in apply_clustering()#777
FBumann merged 1 commit into
dependabot/pip/tsam-4.0.0from
feat/apply-clustering-from-tsam-result

Conversation

@FBumann

@FBumann FBumann commented Sep 8, 2026

Copy link
Copy Markdown
Member

Note

Stacked on #766 — base is dependabot/pip/tsam-4.0.0. Review/merge that first; this diff shows only the changes on top of it.

Reusing a clustering computed outside flixopt worked, but only by importing Clustering and building the wrapper by hand — and guessing what original_timesteps should be:

from flixopt.clustering import Clustering

cr = tsam_xarray.load_clustering('clustering.json')
fs_clustered = fs.transform.apply_clustering(
    Clustering(clustering_result=cr, original_timesteps=fs.timesteps)
)

Change

transform.apply_clustering() now accepts a tsam_xarray ClusteringResult, or its to_dict() form (what lands in a JSON file), in addition to a Clustering:

cr = tsam_xarray.load_clustering('clustering.json')
fs_clustered = fs.transform.apply_clustering(cr)

original_timesteps defaults to the FlowSystem's own grid. That default is always correct for the case the method already requires, since it validates the target has n_original_clusters × timesteps_per_cluster timesteps — so the two can't disagree without the existing check firing first.

Passing original_timesteps alongside a Clustering raises ValueError rather than being ignored: a Clustering already carries its own timesteps, so accepting it silently would hide the mistake.

Passing a Clustering is unchanged — this is purely additive.

Tests

7 new tests in TestApplyClusteringFromClusteringResult covering each accepted input form, equivalence with the Clustering path, the original_timesteps default and override, the rejection case, and the cross-tool tsam_xarray.load_clustering() round-trip.

Docs

New "Reusing a Clustering" section in the clustering guide covering all three input forms, JSON persistence, and the time-grid constraint.


🤖 Generated with Claude Code

https://claude.ai/code/session_01SkyekJFFtk63wUj8W6v93E

Summary by CodeRabbit

  • New Features

    • Apply an existing clustering to another FlowSystem using a clustering object, result, or dictionary.
    • Reuse clusterings created outside the application or restored from JSON.
    • Automatically use the target FlowSystem’s time steps when appropriate, with options for specifying original time steps.
    • Clear validation errors are provided for incompatible time-step configurations or conflicting inputs.
  • Documentation

    • Added guidance and examples for reusing, persisting, and reloading clusterings.

Reusing a clustering computed outside flixopt meant importing `Clustering` and
constructing the wrapper by hand, guessing that `original_timesteps` should be
the target FlowSystem's own timesteps:

    Clustering(clustering_result=cr, original_timesteps=fs.timesteps)

`apply_clustering()` now takes a tsam_xarray `ClusteringResult` — or its
`to_dict()` form, which is what lands in a JSON file — directly, and defaults
`original_timesteps` to the FlowSystem's own grid. That default is always correct
for the case the method already requires, since it validates that the target has
`n_original_clusters * timesteps_per_cluster` timesteps.

`original_timesteps` is rejected when combined with a `Clustering`, which already
carries its own timesteps; silently ignoring it would hide the mistake.

Passing a `Clustering` is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkyekJFFtk63wUj8W6v93E
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 77c49237-96ad-407a-b9c2-c6cc41ac04d2

📥 Commits

Reviewing files that changed from the base of the PR and between 3fcf774 and 3ce8d9e.

📒 Files selected for processing (3)
  • docs/user-guide/optimization/clustering.md
  • flixopt/transform_accessor.py
  • tests/test_clustering/test_clustering_io.py

📝 Walkthrough

Walkthrough

apply_clustering() now accepts Clustering, ClusteringResult, and dictionary inputs. It supports explicit or default original timesteps, adds validation, documents reuse workflows, and tests direct, dictionary, and JSON-loaded clustering inputs.

Changes

Clustering reuse

Layer / File(s) Summary
Clustering input normalization
flixopt/transform_accessor.py
apply_clustering() accepts three clustering representations. It normalizes tsam_xarray inputs and validates original_timesteps usage.
Reuse validation and documentation
tests/test_clustering/test_clustering_io.py, docs/user-guide/optimization/clustering.md
Tests cover direct, dictionary, explicit-timestep, invalid-argument, and JSON-loaded inputs. The guide documents the same workflows and errors.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FlowSystem
  participant TransformAccessor
  participant Clustering
  FlowSystem->>TransformAccessor: apply_clustering(clustering, original_timesteps)
  TransformAccessor->>Clustering: normalize ClusteringResult or dict
  Clustering-->>TransformAccessor: reusable clustering
  TransformAccessor-->>FlowSystem: apply cluster assignments
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/apply-clustering-from-tsam-result

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.

@FBumann
FBumann merged commit e33918a into main Sep 8, 2026
13 of 14 checks passed
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.

1 participant