Skip to content

Commit 2d4a507

Browse files
FBumannclaude
andauthored
fix: allow expand() on clustered FlowSystem without a solution (#646)
* fix: allow expand() on clustered FlowSystem without a solution Previously, expand() required a solved FlowSystem, which prevented using it to inspect expanded data (e.g. clustering_data()) without solving first. Now the solution expansion is skipped when no solution is present. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: update test to expect expand() succeeds without solution Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 103be48 commit 2d4a507

2 files changed

Lines changed: 20 additions & 21 deletions

File tree

flixopt/transform_accessor.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -801,19 +801,20 @@ def expand_flow_system(self) -> FlowSystem:
801801

802802
# 2. Expand solution (with segment total correction for segmented systems)
803803
reduced_solution = self._fs.solution
804-
sol_coord_cache = {k: v for k, v in reduced_solution.coords.items()}
805-
sol_coord_names = set(sol_coord_cache)
806-
expanded_sol_vars = {}
807-
for name in reduced_solution.variables:
808-
if name in sol_coord_names:
809-
continue
810-
da = self._fast_get_da(reduced_solution, name, sol_coord_cache)
811-
expanded_sol_vars[name] = self.expand_dataarray(da, name, is_solution=True)
812-
expanded_fs._solution = xr.Dataset(expanded_sol_vars, attrs=reduced_solution.attrs)
813-
expanded_fs._solution = expanded_fs._solution.reindex(time=self._original_timesteps_extra)
814-
815-
# 3. Combine charge_state with SOC_boundary for intercluster storages
816-
self._combine_intercluster_charge_states(expanded_fs, reduced_solution)
804+
if reduced_solution is not None:
805+
sol_coord_cache = {k: v for k, v in reduced_solution.coords.items()}
806+
sol_coord_names = set(sol_coord_cache)
807+
expanded_sol_vars = {}
808+
for name in reduced_solution.variables:
809+
if name in sol_coord_names:
810+
continue
811+
da = self._fast_get_da(reduced_solution, name, sol_coord_cache)
812+
expanded_sol_vars[name] = self.expand_dataarray(da, name, is_solution=True)
813+
expanded_fs._solution = xr.Dataset(expanded_sol_vars, attrs=reduced_solution.attrs)
814+
expanded_fs._solution = expanded_fs._solution.reindex(time=self._original_timesteps_extra)
815+
816+
# 3. Combine charge_state with SOC_boundary for intercluster storages
817+
self._combine_intercluster_charge_states(expanded_fs, reduced_solution)
817818

818819
# Log expansion info
819820
has_periods = self._fs.periods is not None
@@ -1899,15 +1900,13 @@ def _validate_for_expansion(self) -> Clustering:
18991900
The Clustering object.
19001901
19011902
Raises:
1902-
ValueError: If FlowSystem wasn't created with cluster() or has no solution.
1903+
ValueError: If FlowSystem wasn't created with cluster().
19031904
"""
19041905

19051906
if self._fs.clustering is None:
19061907
raise ValueError(
19071908
'expand() requires a FlowSystem created with cluster(). This FlowSystem has no aggregation info.'
19081909
)
1909-
if self._fs.solution is None:
1910-
raise ValueError('FlowSystem has no solution. Run optimize() or solve() first.')
19111910

19121911
return self._fs.clustering
19131912

@@ -1932,7 +1931,6 @@ def expand(self) -> FlowSystem:
19321931
19331932
Raises:
19341933
ValueError: If the FlowSystem was not created with ``cluster()``.
1935-
ValueError: If the FlowSystem has no solution.
19361934
19371935
Examples:
19381936
Two-stage optimization with expansion:

tests/test_clustering/test_cluster_reduce_expand.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ def test_expand_withoutclustering_raises(solver_fixture, timesteps_2_days):
211211
fs.transform.expand()
212212

213213

214-
def test_expand_without_solution_raises(timesteps_8_days):
215-
"""Test that expand raises error if no solution."""
214+
def test_expand_without_solution(timesteps_8_days):
215+
"""Test that expand works without a solution (e.g. for inspecting clustering_data)."""
216216
fs = create_simple_system(timesteps_8_days)
217217

218218
fs_reduced = fs.transform.cluster(
@@ -221,8 +221,9 @@ def test_expand_without_solution_raises(timesteps_8_days):
221221
)
222222
# Don't optimize - no solution
223223

224-
with pytest.raises(ValueError, match='no solution'):
225-
fs_reduced.transform.expand()
224+
fs_expanded = fs_reduced.transform.expand()
225+
assert fs_expanded.solution is None
226+
assert len(fs_expanded.timesteps) == len(timesteps_8_days)
226227

227228

228229
# ==================== Multi-dimensional Tests ====================

0 commit comments

Comments
 (0)