Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
2c6461b
Add dataset plot accessor
FBumann Jan 1, 2026
f574c0c
Add fxplot acessor showcase
FBumann Jan 1, 2026
edf89ef
The internal plot accessors now leverage the shared .fxplot implement…
FBumann Jan 1, 2026
2b7aa63
Fix notebook
FBumann Jan 1, 2026
8d50009
1. xlabel/ylabel parameters - Added to bar(), stacked_bar(), line()…
FBumann Jan 2, 2026
1e70c78
Fix faceting of pie
FBumann Jan 2, 2026
7be17d0
Improve auto dim handling
FBumann Jan 2, 2026
da72bb8
Improve notebook
FBumann Jan 2, 2026
31bfb85
Fix pie plot
FBumann Jan 2, 2026
450ec0e
Logic order changed:
FBumann Jan 2, 2026
2975238
Add x parameter and x_dim_priority config to fxplot
FBumann Jan 2, 2026
21350db
Add x parameter and smart dimension handling to fxplot
FBumann Jan 2, 2026
d1f1a39
Add x parameter and smart dimension handling to fxplot
FBumann Jan 2, 2026
9d40c82
Improve documentation
FBumann Jan 2, 2026
bd314e0
Fix notebook in docs
FBumann Jan 2, 2026
22702e0
1. heatmap kwarg merge order - Now uses **{**imshow_args, **imshow_…
FBumann Jan 3, 2026
ed33706
Improve docstrings
FBumann Jan 3, 2026
0c7965f
Update notebooks to not do file operations
FBumann Jan 3, 2026
3d257ab
Add Comparison class
FBumann Jan 3, 2026
64cb857
Add Release notes
FBumann Jan 3, 2026
3e3c617
Add Comparison class to all Notebooks
FBumann Jan 3, 2026
ff75419
Update comparison.py and add documentation
FBumann Jan 3, 2026
c490f25
⏺ The class went from ~560 lines to ~115 lines. Key simplifications:
FBumann Jan 3, 2026
64dc1f9
⏺ The class went from ~560 lines to ~115 lines. Key simplifications:
FBumann Jan 3, 2026
d5d4259
Minor bugfix
FBumann Jan 3, 2026
273b03c
Now all methods properly split kwargs and pass plotly_kwargs to the f…
FBumann Jan 3, 2026
a9e9c55
Now all methods properly split kwargs and pass plotly_kwargs to the f…
FBumann Jan 3, 2026
9160b24
Now all methods properly split kwargs and pass plotly_kwargs to the f…
FBumann Jan 3, 2026
85911e8
Improve documentation and improve CHANGELOG.md
FBumann Jan 3, 2026
0b8440b
Fix core dims
FBumann Jan 3, 2026
e4cd270
FIx CHangelog and change to v6.0.0
FBumann Jan 3, 2026
c20f94f
FIx CHangelog and change to v6.0.0
FBumann Jan 3, 2026
c6c9a75
FIx CHangelog and change to v6.0.0
FBumann Jan 3, 2026
3d8e600
Enhanced Clustering Control
FBumann Jan 4, 2026
0abdb00
Dimension renamed: original_period → original_cluster
FBumann Jan 4, 2026
21f96c2
Problem: Expanded FlowSystem from clustering didn't have the extra …
FBumann Jan 4, 2026
8ffd185
- 'variable' is treated as a special valid facet value (since it ex…
FBumann Jan 4, 2026
57c9cb1
Add variable and color to auto resolving in fxplot
FBumann Jan 4, 2026
df1fac1
Added 'variable' to both priority lists and updated the logic to tr…
FBumann Jan 4, 2026
829c342
Improve plotting, especially for clustering
FBumann Jan 4, 2026
ed80f89
Drop cluster index when expanding
FBumann Jan 4, 2026
3dc7eec
Fix storage expansion
FBumann Jan 4, 2026
6b0579f
Improve clustering
FBumann Jan 4, 2026
b2539d8
fix scatter plot faceting
FBumann Jan 4, 2026
e48ff17
⏺ Fixed the documentation in the notebook:
FBumann Jan 4, 2026
285e07b
1. Error handling for accuracyIndicators() - Added try/except with …
FBumann Jan 4, 2026
c126115
1. DataFrame truth ambiguity - Changed non_empty_metrics.get(first_…
FBumann Jan 4, 2026
1488721
Fix pie plot animation frame and add warnings for unassigned dims
FBumann Jan 4, 2026
ee788c9
Merge branch 'feature/aggregate-rework-v2' into feature/tsam-params
FBumann Jan 4, 2026
cf0dcfa
Merge branch 'feature/aggregate-rework-v2' into feature/tsam-params
FBumann Jan 4, 2026
329941d
Merge remote-tracking branch 'origin/feature/tsam-params' into featur…
FBumann Jan 4, 2026
96e2f32
Merge branch 'feature/aggregate-rework-v2' into feature/tsam-params
FBumann Jan 4, 2026
e18966b
Change logger warning to regular warning
FBumann Jan 5, 2026
87ce351
⏺ The centralized slot assignment system is now complete. Here's a su…
FBumann Jan 5, 2026
947ccd9
Add slot_order to config
FBumann Jan 5, 2026
b1336f6
Add new assign_slots() method
FBumann Jan 5, 2026
28bb631
Add new assign_slots() method
FBumann Jan 5, 2026
4f8407a
Fix heatmap and convert all to use fxplot
FBumann Jan 5, 2026
a4d4681
Fix heatmap
FBumann Jan 5, 2026
ae5655d
Fix heatmap
FBumann Jan 5, 2026
56b1838
Fix heatmap
FBumann Jan 5, 2026
56719e8
Fix heatmap
FBumann Jan 5, 2026
4e7810d
Merge branch 'feature/aggregate-rework-v2' into feature/tsam-params
FBumann Jan 5, 2026
37b51de
Merge remote-tracking branch 'origin/feature/tsam-params' into featur…
FBumann Jan 5, 2026
df825de
Merge remote-tracking branch 'origin/feature/tsam-params' into featur…
FBumann Jan 5, 2026
e613755
comparison.py:
FBumann Jan 5, 2026
c6da15f
Squeeze signleton dims in heatmap()
FBumann Jan 5, 2026
ae44659
Merge branch 'feature/tsam-params' into feature/comparison
FBumann Jan 5, 2026
e2f95c7
Merge branch 'feature/aggregate-rework-v2' into feature/comparison
FBumann Jan 5, 2026
e4c6510
Replaced print statements with class repr
FBumann Jan 5, 2026
a53efa1
1. 08a-aggregation.ipynb cell 16: Removed corrupted <cell_type>mark…
FBumann Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,33 @@ Until here -->

### ✨ Added

**FlowSystem Comparison**: New `Comparison` class for comparing multiple FlowSystems side-by-side:

```python
# Compare systems (uses FlowSystem.name by default)
comp = fx.Comparison([fs_base, fs_modified])

# Or with custom names
comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff'])

# Side-by-side plots (auto-facets by 'case' dimension)
comp.statistics.plot.balance('Heat')
comp.statistics.flow_rates.fxplot.line()

# Access combined data with 'case' dimension
comp.solution # xr.Dataset
comp.statistics.flow_rates # xr.Dataset

# Compute differences relative to a reference case
comp.diff() # vs first case
comp.diff('baseline') # vs named case
```

- Concatenates solutions and statistics from multiple FlowSystems with a `'case'` dimension
- Mirrors all `StatisticsAccessor` properties (`flow_rates`, `flow_hours`, `sizes`, `charge_states`, `temporal_effects`, `periodic_effects`, `total_effects`)
- Mirrors all `StatisticsPlotAccessor` methods (`balance`, `carrier_balance`, `flows`, `sizes`, `duration_curve`, `effects`, `charge_states`, `heatmap`, `storage`)
- Existing plotting infrastructure automatically handles faceting by `'case'`

**Time-Series Clustering**: Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution.

```python
Expand Down
33 changes: 10 additions & 23 deletions docs/notebooks/02-heat-system.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import xarray as xr\n",
"\n",
"import flixopt as fx\n",
Expand Down Expand Up @@ -281,12 +282,16 @@
"metadata": {},
"outputs": [],
"source": [
"total_costs = flow_system.solution['costs'].item()\n",
"total_heat = heat_demand.sum()\n",
"\n",
"print(f'Total operating costs: {total_costs:.2f} €')\n",
"print(f'Total heat delivered: {total_heat:.0f} kWh')\n",
"print(f'Average cost: {total_costs / total_heat * 100:.2f} ct/kWh')"
"pd.DataFrame(\n",
" {\n",
" 'Total operating costs [EUR]': flow_system.solution['costs'].item(),\n",
" 'Total heat delivered [kWh]': total_heat,\n",
" 'Average cost [ct/kWh]': flow_system.solution['costs'].item() / total_heat * 100,\n",
" },\n",
" index=['Value'],\n",
").T"
]
},
{
Expand Down Expand Up @@ -370,25 +375,7 @@
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.11"
}
},
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}
58 changes: 29 additions & 29 deletions docs/notebooks/03-investment-optimization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import xarray as xr\n",
"\n",
"import flixopt as fx\n",
Expand Down Expand Up @@ -229,9 +230,14 @@
"solar_size = flow_system.statistics.sizes['SolarCollectors(Heat)'].item()\n",
"tank_size = flow_system.statistics.sizes['BufferTank'].item()\n",
"\n",
"print(\n",
" f'Optimal sizes: Solar {solar_size:.0f} kW, Tank {tank_size:.0f} kWh (ratio: {tank_size / solar_size:.1f} kWh/kW)'\n",
")"
"pd.DataFrame(\n",
" {\n",
" 'Solar [kW]': solar_size,\n",
" 'Tank [kWh]': tank_size,\n",
" 'Ratio [kWh/kW]': tank_size / solar_size,\n",
" },\n",
" index=['Optimal Size'],\n",
").T"
]
},
{
Expand Down Expand Up @@ -274,8 +280,13 @@
"tank_invest = tank_size * TANK_COST_WEEKLY\n",
"gas_costs = total_costs - solar_invest - tank_invest\n",
"\n",
"print(\n",
" f'Weekly costs: Solar {solar_invest:.1f}€ ({solar_invest / total_costs * 100:.0f}%) + Tank {tank_invest:.1f}€ ({tank_invest / total_costs * 100:.0f}%) + Gas {gas_costs:.1f}€ ({gas_costs / total_costs * 100:.0f}%) = {total_costs:.1f}€'\n",
"pd.DataFrame(\n",
" {\n",
" 'Solar Investment': {'EUR': solar_invest, '%': solar_invest / total_costs * 100},\n",
" 'Tank Investment': {'EUR': tank_invest, '%': tank_invest / total_costs * 100},\n",
" 'Gas Costs': {'EUR': gas_costs, '%': gas_costs / total_costs * 100},\n",
" 'Total': {'EUR': total_costs, '%': 100.0},\n",
" }\n",
")"
]
},
Expand Down Expand Up @@ -334,14 +345,21 @@
"metadata": {},
"outputs": [],
"source": [
"# Gas-only scenario\n",
"# Gas-only scenario for comparison\n",
"total_demand = pool_demand.sum()\n",
"gas_only_cost = total_demand / 0.92 * GAS_PRICE # All heat from gas boiler\n",
"\n",
"savings = gas_only_cost - total_costs\n",
"print(\n",
" f'Solar saves {savings:.1f}€/week ({savings / gas_only_cost * 100:.0f}%) vs gas-only ({gas_only_cost:.1f}€) → {savings * 52:.0f}€/year'\n",
")"
"\n",
"pd.DataFrame(\n",
" {\n",
" 'Gas-only [EUR/week]': gas_only_cost,\n",
" 'With Solar [EUR/week]': total_costs,\n",
" 'Savings [EUR/week]': savings,\n",
" 'Savings [%]': savings / gas_only_cost * 100,\n",
" 'Savings [EUR/year]': savings * 52,\n",
" },\n",
" index=['Value'],\n",
").T"
]
},
{
Expand Down Expand Up @@ -406,25 +424,7 @@
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.11"
}
},
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}
73 changes: 52 additions & 21 deletions docs/notebooks/04-operational-constraints.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import xarray as xr\n",
"\n",
"import flixopt as fx\n",
Expand Down Expand Up @@ -84,8 +85,12 @@
"metadata": {},
"outputs": [],
"source": [
"# Visualize demand with fxplot\n",
"demand_ds = xr.Dataset({'Steam Demand': xr.DataArray(steam_demand, dims=['time'], coords={'time': timesteps})})\n",
"# Visualize the demand with fxplot\n",
"demand_ds = xr.Dataset(\n",
" {\n",
" 'Steam Demand [kW]': xr.DataArray(steam_demand, dims=['time'], coords={'time': timesteps}),\n",
" }\n",
")\n",
"demand_ds.fxplot.line(title='Factory Steam Demand')"
]
},
Expand All @@ -104,7 +109,7 @@
"metadata": {},
"outputs": [],
"source": [
"flow_system = fx.FlowSystem(timesteps)\n",
"flow_system = fx.FlowSystem(timesteps, name='Constrained')\n",
"\n",
"# Define and register custom carriers\n",
"flow_system.add_carriers(\n",
Expand Down Expand Up @@ -268,8 +273,12 @@
"startup_costs = total_startups * 50\n",
"gas_costs = total_costs - startup_costs\n",
"\n",
"print(\n",
" f'{total_startups} startups × 50€ = {startup_costs:.0f}€ startup + {gas_costs:.0f}€ gas = {total_costs:.0f}€ total'\n",
"pd.DataFrame(\n",
" {\n",
" 'Startups': {'Count': total_startups, 'EUR': startup_costs},\n",
" 'Gas': {'Count': '-', 'EUR': gas_costs},\n",
" 'Total': {'Count': '-', 'EUR': total_costs},\n",
" }\n",
")"
]
},
Expand Down Expand Up @@ -321,7 +330,7 @@
"outputs": [],
"source": [
"# Build unconstrained system\n",
"fs_unconstrained = fx.FlowSystem(timesteps)\n",
"fs_unconstrained = fx.FlowSystem(timesteps, name='Unconstrained')\n",
"fs_unconstrained.add_carriers(\n",
" fx.Carrier('gas', '#3498db', 'kW'),\n",
" fx.Carrier('steam', '#87CEEB', 'kW_th', 'Process steam'),\n",
Expand Down Expand Up @@ -351,14 +360,43 @@
"fs_unconstrained.optimize(fx.solvers.HighsSolver())\n",
"unconstrained_costs = fs_unconstrained.solution['costs'].item()\n",
"\n",
"constraint_overhead = (total_costs - unconstrained_costs) / unconstrained_costs * 100\n",
"print(f'Constraints add {constraint_overhead:.1f}% cost: {unconstrained_costs:.0f}€ → {total_costs:.0f}€')"
"pd.DataFrame(\n",
" {\n",
" 'Without Constraints': {'Cost [EUR]': unconstrained_costs},\n",
" 'With Constraints': {'Cost [EUR]': total_costs},\n",
" 'Overhead': {\n",
" 'Cost [EUR]': total_costs - unconstrained_costs,\n",
" '%': (total_costs - unconstrained_costs) / unconstrained_costs * 100,\n",
" },\n",
" }\n",
")"
]
},
{
"cell_type": "markdown",
"id": "24",
"metadata": {},
"source": [
"### Side-by-Side Comparison\n",
"\n",
"Use the `Comparison` class to visualize both systems together:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "25",
"metadata": {},
"outputs": [],
"source": [
"comp = fx.Comparison([fs_unconstrained, flow_system])\n",
"comp.statistics.plot.effects()"
]
},
{
"cell_type": "markdown",
"id": "26",
"metadata": {},
"source": [
"### Energy Flow Sankey\n",
"\n",
Expand All @@ -368,7 +406,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "25",
"id": "27",
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -377,7 +415,7 @@
},
{
"cell_type": "markdown",
"id": "26",
"id": "28",
"metadata": {},
"source": [
"## Key Concepts\n",
Expand Down Expand Up @@ -430,17 +468,10 @@
}
],
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.11"
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
Expand Down
Loading