Skip to content

Commit fcb9e30

Browse files
committed
fix: move filtering and rounding before early return in case data_only=True
1 parent 95d79af commit fcb9e30

2 files changed

Lines changed: 40 additions & 36 deletions

File tree

flixopt/statistics_accessor.py

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,13 @@ def balance(
15521552

15531553
ds = _apply_selection(ds, select)
15541554

1555+
# Round to avoid numerical noise (tiny negative values from solver precision)
1556+
if round_decimals is not None:
1557+
ds = ds.round(round_decimals)
1558+
1559+
# Filter out variables below threshold
1560+
ds = _filter_small_variables(ds, threshold)
1561+
15551562
# Build color kwargs: bus balance → component colors, component balance → carrier colors
15561563
color_by: Literal['component', 'carrier'] = 'component' if is_bus else 'carrier'
15571564
color_kwargs = self._build_color_kwargs(colors, list(ds.data_vars), color_by)
@@ -1560,13 +1567,6 @@ def balance(
15601567
if data_only:
15611568
return PlotResult(data=ds, figure=go.Figure())
15621569

1563-
# Round to avoid numerical noise (tiny negative values from solver precision)
1564-
if round_decimals is not None:
1565-
ds = ds.round(round_decimals)
1566-
1567-
# Filter out variables below threshold
1568-
ds = _filter_small_variables(ds, threshold)
1569-
15701570
# Sort for consistent plotting order
15711571
ds = _sort_dataset(ds)
15721572

@@ -1708,20 +1708,20 @@ def carrier_balance(
17081708

17091709
ds = _apply_selection(ds, select)
17101710

1711-
# Build color kwargs with component colors (flows colored by their parent component)
1712-
color_kwargs = self._build_color_kwargs(colors, list(ds.data_vars), color_by='component')
1713-
1714-
# Early return for data_only mode (skip figure creation for performance)
1715-
if data_only:
1716-
return PlotResult(data=ds, figure=go.Figure())
1717-
17181711
# Round to avoid numerical noise (tiny negative values from solver precision)
17191712
if round_decimals is not None:
17201713
ds = ds.round(round_decimals)
17211714

17221715
# Filter out variables below threshold
17231716
ds = _filter_small_variables(ds, threshold)
17241717

1718+
# Build color kwargs with component colors (flows colored by their parent component)
1719+
color_kwargs = self._build_color_kwargs(colors, list(ds.data_vars), color_by='component')
1720+
1721+
# Early return for data_only mode (skip figure creation for performance)
1722+
if data_only:
1723+
return PlotResult(data=ds, figure=go.Figure())
1724+
17251725
# Sort for consistent plotting order
17261726
ds = _sort_dataset(ds)
17271727

@@ -1887,13 +1887,13 @@ def flows(
18871887

18881888
ds = _apply_selection(ds, select)
18891889

1890+
# Filter out variables below threshold
1891+
ds = _filter_small_variables(ds, threshold)
1892+
18901893
# Early return for data_only mode (skip figure creation for performance)
18911894
if data_only:
18921895
return PlotResult(data=ds, figure=go.Figure())
18931896

1894-
# Filter out variables below threshold
1895-
ds = _filter_small_variables(ds, threshold)
1896-
18971897
# Sort for consistent plotting order
18981898
ds = _sort_dataset(ds)
18991899

@@ -1956,13 +1956,13 @@ def sizes(
19561956
valid_labels = [lbl for lbl in ds.data_vars if float(ds[lbl].max()) < max_size]
19571957
ds = ds[valid_labels]
19581958

1959+
# Filter out variables below threshold
1960+
ds = _filter_small_variables(ds, threshold)
1961+
19591962
# Early return for data_only mode (skip figure creation for performance)
19601963
if data_only:
19611964
return PlotResult(data=ds, figure=go.Figure())
19621965

1963-
# Filter out variables below threshold
1964-
ds = _filter_small_variables(ds, threshold)
1965-
19661966
if not ds.data_vars:
19671967
fig = go.Figure()
19681968
else:
@@ -2051,13 +2051,13 @@ def duration_curve(
20512051

20522052
result_ds = ds.fxstats.to_duration_curve(normalize=normalize)
20532053

2054+
# Filter out variables below threshold
2055+
result_ds = _filter_small_variables(result_ds, threshold)
2056+
20542057
# Early return for data_only mode (skip figure creation for performance)
20552058
if data_only:
20562059
return PlotResult(data=result_ds, figure=go.Figure())
20572060

2058-
# Filter out variables below threshold
2059-
result_ds = _filter_small_variables(result_ds, threshold)
2060-
20612061
# Sort for consistent plotting order
20622062
result_ds = _sort_dataset(result_ds)
20632063

@@ -2180,13 +2180,13 @@ def effects(
21802180
else:
21812181
raise ValueError(f"'by' must be one of 'component', 'contributor', 'time', or None, got {by!r}")
21822182

2183+
# Filter out variables below threshold
2184+
ds = _filter_small_variables(ds, threshold)
2185+
21832186
# Early return for data_only mode (skip figure creation for performance)
21842187
if data_only:
21852188
return PlotResult(data=ds, figure=go.Figure())
21862189

2187-
# Filter out variables below threshold
2188-
ds = _filter_small_variables(ds, threshold)
2189-
21902190
# Sort for consistent plotting order
21912191
ds = _sort_dataset(ds)
21922192

@@ -2262,13 +2262,13 @@ def charge_states(
22622262

22632263
ds = _apply_selection(ds, select)
22642264

2265+
# Filter out variables below threshold
2266+
ds = _filter_small_variables(ds, threshold)
2267+
22652268
# Early return for data_only mode (skip figure creation for performance)
22662269
if data_only:
22672270
return PlotResult(data=ds, figure=go.Figure())
22682271

2269-
# Filter out variables below threshold
2270-
ds = _filter_small_variables(ds, threshold)
2271-
22722272
# Sort for consistent plotting order
22732273
ds = _sort_dataset(ds)
22742274

@@ -2367,10 +2367,6 @@ def storage(
23672367
# Apply selection
23682368
ds = _apply_selection(ds, select)
23692369

2370-
# Early return for data_only mode (skip figure creation for performance)
2371-
if data_only:
2372-
return PlotResult(data=ds, figure=go.Figure())
2373-
23742370
# Separate flow data from charge_state
23752371
flow_labels = [lbl for lbl in ds.data_vars if lbl != 'charge_state']
23762372
flow_ds = ds[flow_labels]
@@ -2383,6 +2379,12 @@ def storage(
23832379
# Filter out flow variables below threshold
23842380
flow_ds = _filter_small_variables(flow_ds, threshold)
23852381

2382+
# Early return for data_only mode (skip figure creation for performance)
2383+
if data_only:
2384+
result_ds = flow_ds.copy()
2385+
result_ds['charge_state'] = charge_da
2386+
return PlotResult(data=result_ds, figure=go.Figure())
2387+
23862388
# Sort for consistent plotting order
23872389
flow_ds = _sort_dataset(flow_ds)
23882390

tests/test_comparison.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,14 @@ def test_balance_returns_plot_result(self, optimized_base, optimized_with_chp):
359359
assert isinstance(result.data, xr.Dataset)
360360

361361
def test_balance_includes_all_flows(self, optimized_base, optimized_with_chp):
362-
"""balance() includes flows from both systems."""
362+
"""balance() includes flows from both systems (with non-zero values)."""
363363
comp = fx.Comparison([optimized_base, optimized_with_chp])
364364
result = comp.statistics.plot.balance('Heat', show=False)
365365

366-
# Should include CHP flow even though it's only in one system
367-
assert 'CHP(Q_th_chp)' in result.data
366+
# Should include flows that have non-zero values in at least one system
367+
# Note: CHP is not used (all zeros) in this test, so it's correctly filtered out
368+
# The Boiler flow is present in both systems
369+
assert 'Boiler(Q_th)' in result.data
368370

369371
def test_balance_data_has_case_dimension(self, optimized_base, optimized_with_chp):
370372
"""balance() data has 'case' dimension."""

0 commit comments

Comments
 (0)