-
Notifications
You must be signed in to change notification settings - Fork 9
Feature/flow system first #521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
# Conflicts: # flixopt/core.py # flixopt/interface.py # test_deprecations.py
# Conflicts: # flixopt/core.py # flixopt/effects.py # test_deprecations.py # tests/test_invest_parameters_deprecation.py
# Conflicts: # flixopt/core.py # flixopt/effects.py # test_deprecations.py # tests/test_invest_parameters_deprecation.py
# Conflicts: # flixopt/core.py # flixopt/effects.py # test_deprecations.py # tests/test_invest_parameters_deprecation.py
* Perfect! Here's the **final complete renaming table with Option A**:
## Parameters Class (`OnOffParameters` → `StatusParameters`)
| Current Name | Recommended Name | Rationale |
|--------------|------------------|-----------|
| `OnOffParameters` | **`StatusParameters`** | Aligns with PyPSA, clearer semantics |
| `effects_per_switch_on` | **`effects_per_startup`** | Standard UC terminology |
| `effects_per_running_hour` | **`effects_per_active_hour`** | Clear, concise, matches "active" state |
| `on_hours_total_min` | **`active_hours_min`** | Total (not consecutive) active hours |
| `on_hours_total_max` | **`active_hours_max`** | Total (not consecutive) active hours |
| `consecutive_on_hours_min` | **`min_uptime`** | Standard UC term (consecutive) |
| `consecutive_on_hours_max` | **`max_uptime`** | Standard UC term (consecutive) |
| `consecutive_off_hours_min` | **`min_downtime`** | Standard UC term (consecutive) |
| `consecutive_off_hours_max` | **`max_downtime`** | Standard UC term (consecutive) |
| `switch_on_total_max` | **`startup_limit`** | Clearer intent, matches "startup" |
| `force_switch_on` | **`force_startup_tracking`** | More explicit about what is forced |
## Model Class (`OnOffModel` → `StatusModel`)
### Class Name
| Current Name | Recommended Name |
|--------------|------------------|
| `OnOffModel` | **`StatusModel`** |
### Constructor Parameters
| Current Name | Recommended Name | Rationale |
|--------------|------------------|-----------|
| `on_variable` | **`status`** | Aligns with PyPSA and literature |
| `previous_states` | **`previous_status`** | Consistency with status variable |
### Variables (short_name in add_variables/expression_tracking_variable)
| Current Name | Recommended Name | Type | Notes |
|--------------|------------------|------|-------|
| `self.on` | **`self.status`** | Input variable | Main binary state variable |
| `'off'` | **Remove variable** | Binary variable | Replace with expression `1 - status` |
| `'switch\|on'` | **`'startup'`** | Binary variable | Startup event indicator |
| `'switch\|off'` | **`'shutdown'`** | Binary variable | Shutdown event indicator |
| `'switch\|count'` | **`'startup_count'`** | Integer variable | Number of startups |
| `'on_hours_total'` | **`'active_hours'`** | Continuous variable | Total active duration |
| `'consecutive_on_hours'` | **`'uptime'`** | Continuous variable | Consecutive active hours |
| `'consecutive_off_hours'` | **`'downtime'`** | Continuous variable | Consecutive inactive hours |
### Properties
| Current Name | Recommended Name | Returns | Meaning |
|--------------|------------------|---------|---------|
| `on_hours_total` | **`active_hours`** | `linopy.Variable` | Total active hours |
| `off` | **Remove property** | — | Use `1 - status` expression |
| `switch_on` | **`startup`** | `linopy.Variable \| None` | Startup events |
| `switch_off` | **`shutdown`** | `linopy.Variable \| None` | Shutdown events |
| `switch_on_nr` | **`startup_count`** | `linopy.Variable \| None` | Number of startups |
| `consecutive_on_hours` | **`uptime`** | `linopy.Variable \| None` | Consecutive active hours |
| `consecutive_off_hours` | **`downtime`** | `linopy.Variable \| None` | Consecutive inactive hours |
### Internal Methods
| Current Name | Recommended Name |
|--------------|------------------|
| `_get_previous_on_duration()` | **`_get_previous_uptime()`** |
| `_get_previous_off_duration()` | **`_get_previous_downtime()`** |
### Internal Properties/Flags (in parameters)
| Current Name | Recommended Name |
|--------------|------------------|
| `use_off` | **Remove** (use expression instead) |
| `use_switch_on` | **`use_startup_tracking`** |
| `use_consecutive_on_hours` | **`use_uptime_tracking`** |
| `use_consecutive_off_hours` | **`use_downtime_tracking`** |
## Constraint Names (short_name in add_constraints)
| Current Name | Recommended Name |
|--------------|------------------|
| `'complementary'` | **Remove** (no off variable) |
| `'on_hours_total'` | **`'active_hours'`** |
| `'switch\|on'`, `'switch\|off'` | **`'startup'`, `'shutdown'`** |
| `'switch\|count'` | **`'startup_count'`** |
| `'consecutive_on_hours'` | **`'uptime'`** |
| `'consecutive_off_hours'` | **`'downtime'`** |
## Complete Terminology Summary (Option A)
**State:**
- `status` (binary): 1 = active, 0 = inactive
**Events:**
- `startup` (binary): transition from inactive to active
- `shutdown` (binary): transition from active to inactive
**Durations:**
- `active_hours` (continuous): **total** hours in active state across time horizon
- `uptime` (continuous): **consecutive** hours currently active (UC standard)
- `downtime` (continuous): **consecutive** hours currently inactive (UC standard)
**Parameter Bounds:**
- `active_hours_min/max`: limits on **total** active hours
- `min_uptime/max_uptime`: limits on **consecutive** active hours (UC standard)
- `min_downtime/max_downtime`: limits on **consecutive** inactive hours (UC standard)
- `startup_limit`: maximum number of startup events
**Effects:**
- `effects_per_startup`: costs/impacts per startup event
- `effects_per_active_hour`: costs/impacts per active hour
This aligns perfectly with PyPSA and the unit commitment literature! 🎯
* Refactor tests and examples
* Refactor tests and examples
* Update CHANGELOG.md
* Python Docstrings Updated:
1. interface.py - Module docstring now references "Status decisions"
2. components.py - Updated all docstrings:
- status_parameters parameter descriptions
- Example code updated with new parameter names (effects_per_startup, min_uptime, startup_limit)
- Fixed incorrect "OnOff feature" docstring to "Investment feature"
- Updated TODO comment to reference StatusParameters
3. linear_converters.py - All docstrings updated:
- Import statement updated to StatusParameters
- All parameter descriptions updated
- All example code updated with new terminology
4. flow_system.py - Updated references from "consecutive_on_hours" to "uptime and downtime" and on_off_parameters to status_parameters
5. modeling.py - Updated docstring from "switch-on/off variables" to "state transition constraints for binary switching variables"
Documentation Markdown Files Updated:
1. Flow.md - All references updated:
- Links to StatusParameters
- "on/off state" → "active/inactive state"
- Parameter names updated
2. StatusParameters.md (renamed from OnOffParameters.md) - Comprehensive updates:
- Title changed to "StatusParameters"
- All terminology updated: on/off → active/inactive
- Mathematical notation updated: s^on/s^off → s^startup/s^shutdown
- Duration variables: d^on/d^off → d^uptime/d^downtime
- Parameter names updated in all examples
- All Python code examples updated with new API
3. Other modeling pattern docs - Updated all references to StatusParameters and active/inactive terminology
4. mkdocs.yml - Navigation updated to reference StatusParameters.md
All docstrings and documentation now consistently use the new Status terminology aligned with PyPSA and unit commitment standards!
* Update remaining mentions of old parameters
* ⏺ Perfect! I've addressed all the actionable review comments:
Changes Made:
1. Fixed error message in modeling.py
- Corrected ModelingPrimitives.state_transition_bounds() → BoundingPatterns.state_transition_bounds() in error message (flixopt/modeling.py:591)
2. Fixed Transmission type hint (flixopt/components.py:667)
- Changed status_parameters: StatusParameters = None → status_parameters: StatusParameters | None = None
3. Fixed absolute_losses=0 edge case (flixopt/components.py:768)
- Added np.any(self.element.absolute_losses != 0) check in create_transmission_equation to match the initialization logic
- This prevents AttributeError when absolute_losses is explicitly set to 0
4. Updated test assertion messages (tests/test_component.py)
- Changed "On does not work properly" → "Status does not work properly"
5. Fixed effects_per_startup type (examples/02_Complex/complex_example.py)
- Changed scalar effects_per_startup=0.01 → dict effects_per_startup={Costs.label: 0.01} in all 3 occurrences
- Now consistent with the StatusParameters API which expects a dict mapping effect names to values
6. Updated test_functional.py docstring
- Removed reference to non-existent TestStatus class
- Updated to accurately describe the status-related test functions
7. Consistent unbounded upper bounds (flixopt/features.py:191)
- Changed np.inf → None for unbounded active_hours_max
- Now consistent with FlowModel's total_flow_hours pattern
All changes maintain backward compatibility and align with the codebase's existing patterns. The documentation in index.md was already correct (BoundingPatterns is
the right class for state_transition_bounds).
* Changes Made:
1. CHANGELOG.md - Fixed parameter rename documentation (lines 89-90)
- Changed incorrect status_parameters → status_parameters
- To correct: on_off_parameters → status_parameters
2. CHANGELOG.md - Removed duplicate logger warning (line 803 in v2.1.0)
- Removed duplicate entry that was already documented in v2.0.1
- Fixed v2.0.1 entry to say on_off_parameters (the name at that time)
3. StatusParameters.md - Aligned flow bounds formulation (line 229)
- Updated summary to include max(ε, rel_lower) like the main text
- Now consistent: s(t) · P · max(ε, rel_lower) ≤ p(t) ≤ s(t) · P · rel_upper
4. features.py - Narrowed previous_status type hint (line 155)
- Changed from Numeric_TPS | None to xr.DataArray | None
- Added import xarray as xr (line 12)
- This accurately reflects that _get_previous_uptime() and _get_previous_downtime() use xarray APIs
All changes are verified to compile correctly and maintain consistency with the codebase patterns!
* Fixed Issues
1. Constraint naming in tests (tests/test_component.py:126-127, 158, 168,
338, 348):
- Updated test expectations from 'TestComponent|on|lb' and
'TestComponent|on|ub' to 'TestComponent|status|lb' and
'TestComponent|status|ub' to match the actual constraint names
2. Added 'off' property to StatusModel (flixopt/features.py:284-287):
- Added a new property that returns 1 - self.status for backward
compatibility with tests expecting an off attribute
3. Fixed deprecated parameter name (tests/test_functional.py:435):
- Changed force_switch_on=True to force_startup_tracking=True in
StatusParameters
4. Fixed property name (tests/test_functional.py:466):
- Changed switch_off to shutdown to match the actual property name in
StatusModel
* Delete mistakingly added files
* Delete mistakingly added files
* Final touches
* Final touches
* Replace off with inactive
* Rename low level parameetrs as well:
switch_on -> activate
switch_off -> deactivate
* Rename low level parameetrs as well:
switch_on -> activate
switch_off -> deactivate
state_variable -> state
* Rename low level parameetrs as well:
switch_on -> activate
switch_off -> deactivate
state_variable -> state
* Docstring Improvements Summary
✅ All Parameters Now Documented
Each primitive now has complete parameter documentation with:
- Clear description of what each parameter does
- Type expectations
- Default values where applicable
✅ Focused on Math & Parameters
Removed:
- Excessive examples at low level
- Use case lists that belong at higher levels
Enhanced:
- Mathematical formulations (using proper · symbol for multiplication)
- Clear behavior descriptions
- Precise return value documentation
Updated Functions:
ModelingPrimitives:
1. expression_tracking_variable
- All 6 parameters documented
- Clear math formulation
2. consecutive_duration_tracking
- All 9 parameters documented
- Explained Big-M value
- Clear what constraints are returned
3. mutual_exclusivity_constraint
- All 4 parameters documented
- Simplified, focused on math
BoundingPatterns:
4. basic_bounds
- All 4 parameters documented
- Concise formulation
5. bounds_with_state
- All 5 parameters documented
- Explained epsilon (ε) usage
6. scaled_bounds
- All 5 parameters documented
- Clear scaling relationship
7. scaled_bounds_with_state
- All 7 parameters documented
- Explained Big-M formulation
8. state_transition_bounds
- All 7 parameters documented
- Removed verbose examples, kept math focus
9. continuous_transition_bounds
- All 8 parameters documented
- Clear Big-M constraint explanation
Result
✅ All parameters documented
✅ Math-focused docstrings
✅ Consistent format across all primitives
✅ Tests still passing
The modeling primitives now have professional, complete documentation!
* Update docs
* Add missing type hints
* Fix bullet points
* Fix bullet points
* Re-apply changes from main
* Bugfix: Usage of old on_off_parameters
* Update CHANGELOG.md
* Update CHANGELOG.md
* Update CHANGELOG.md
* Fix typos
* Improve flagging of wether to create inactive varaible
* Improve default upper bound of active_hours
* Bugfix self._model.hours_per_step.sum('time').item() with scenarios/periods
* Fix test
* FIx names
* pdate the test assertions to expect upper=total_hours instead of upper=inf when active_hours_max is not
specified
* Empty
* Trigger CI
* Fix test
* Triggger CI
* Summary of Fixes
1. Return type annotation for consecutive_duration_tracking (flixopt/modeling.py:255):
- Changed from tuple[linopy.Variable, tuple[linopy.Constraint, linopy.Constraint, linopy.Constraint]]
- To tuple[dict[str, linopy.Variable], dict[str, linopy.Constraint]] to match the actual return value
2. Clarified inactive property docstring (flixopt/features.py:284-291):
- Replaced the confusing "deprecated" note with a clear explanation that:
- The variable is only created when downtime tracking is enabled
- Users should prefer 1 - status expression for general use
3. Fixed _get_previous_uptime docstring (flixopt/features.py:318-322):
- Clarified that it returns 0 when no previous status is provided (assumes previously inactive)
4. Fixed _get_previous_downtime docstring (flixopt/features.py:329-333):
- Clarified that it returns one timestep duration when no previous status is provided (assumes previously inactive)
5. No action needed for effects_per_startup without use_startup_tracking:
- Verified that use_startup_tracking already returns True when effects_per_startup has values (line 1258 in interface.py), so this is already
handled correctly
6. Test fixes (tests/test_flow.py):
- Updated three test assertions to use model.hours_per_step.sum('time') as the expected upper bound for active_hours when active_hours_max is
not specified
* Trigger CI
* | File | Changes |
|-------------------------|----------------------------------------------------------------------------------|
| flixopt/elements.py | Renamed attributes excess_input → virtual_supply, excess_output → virtual_demand |
| flixopt/optimization.py | Updated attribute access and result keys |
| tests/test_bus.py | Updated variable name strings in assertions |
| docs/.../Bus.md | Updated description of φ symbols |
The variable names in the optimization model are now:
- {BusName}|virtual_supply (was excess_input)
- {BusName}|virtual_demand (was excess_output)
* Renamed excess_penalty_per_flow_hour → imbalance_penalty_per_flow_hour
* rename excess_penalty to imbalance_penalty
* Change default to None
* Added self._validate_kwargs(kwargs) to catch typos and unexpected arguments
* Renamed with_excess → allows_imbalance
* Fix docstring
* 1. docs/user-guide/mathematical-notation/elements/Bus.md - Fixed three typos:
- "a imbalance_penalty_per_flow_hour" → "an imbalance_penalty_per_flow_hour"
- "usefull" → "useful"
- "ifeasiblity" → "infeasibility"
2. tests/test_bus.py - Updated comments to use the new imbalance terminology instead of the old "excess" terminology
3. flixopt/elements.py (BusModel) - Improved code clarity:
- Changed eq_bus_balance.lhs -= -self.virtual_supply + self.virtual_demand to the more readable eq_bus_balance.lhs += self.virtual_supply -
self.virtual_demand
- Added a comment explaining the equation: # Σ(inflows) + virtual_supply = Σ(outflows) + virtual_demand
- Combined the two separate add_share_to_effects calls into a single call with the combined expression (self.virtual_supply +
self.virtual_demand) * imbalance_penalty
All 12 bus tests pass with these changes.
* Flow.md - Fully restructured with:
- Tab-based organization (Core / Advanced / Patterns / Examples)
- Collapsible definition blocks
- Links to both Flow and FlowModel classes
- Updated docstrings with absolute URLs
2. Bus.md - Restructured with tab organization and dual class linking
3. Storage.md - Restructured with comprehensive examples and dual class linking
4. LinearConverter.md - Restructured with detailed examples including specialized converters
5. InvestParameters.md - Restructured with clear separation of core vs. advanced features
* Improve organization
* Improve organization
* Improve organization by using tables
* Improve organization by using tables and use eqref
* Add symbol to parameter mapping
* Changed to inline math
* Use propre constraints with numbering
* Move parameters into separate tab
* Reorder parameters
* : Use the columns "symbol" and "python name" in the variables tab
* Update Bus, Storage, and LinearConverter.md
* Update InvestParameters and OnOffParameters.md
* Update Piecewise.md
* Compact effects-penalty-objective.md
* Allow toc level 3
* Add toc to homepage
* Replace ustom css with mkdocs material stuff
* Revert some
* Revert some
* Remove layout css rule
* Show toc on homepage
* FIx broken link
* Add edit uri
* Hide bottom part
* Hide bottom part
* Restructure docs
* Show navigation in home
* Add Changelog fromating
* THighten CHANGELOG.md
* Simplify users.md
* Simplify models.md
* Shorten citing.md
* Shorten support.md
* Update CHANGELOG.md
* Simplify installation.md
* Simplify quick-start.md
* Updated FullCalculation → Optimization in documentation
Fixed mkdocs.yml navigation
Fixed broken link in support.md
* Fixed solver calls in docs
* Move files and restructure
* Delete old docs script
* Improve docs structure
* Imrpove Optimization Modes
* Imrpove Optimization Modes
* Rewrite the core concepts to be user facing
* Reorganize Mathematical Notation
* 1. Minimal variable names — Changed from words to symbols:
- penalty_rate → $c_\phi$
- relative_min → $p_{rel}^{min}$
- flow_hours → $h_f$
- loss → $\dot{c}_{loss}$
- etc.
2. Tabs for conditional constraints — Used === "Tab Name" syntax for:
- Bus.md: "Without Excess (Strict)" vs "With Excess (Soft)"
- Flow.md: "Standard (No On/Off)" vs "With On/Off" vs "Fixed Profile"
- Storage.md: "Fixed Initial" vs "Cyclic" vs "Final Bounds"
- LinearConverter.md: "Single Input/Output" vs "Multiple Outputs" vs "COP > 1" vs "Time-Varying"
- Effects.md: "Temporal (Operational)" vs "Periodic (Investment)" vs "Total"
3. Corrected Flow constraints — Clarified that:
- Without on/off parameters: flow cannot be zero if relative_minimum > 0
- With on/off parameters: flow can be zero (when off) OR within bounds (when on)
4. Cleaner structure — Removed redundant content, focused on essential formulas and examples
* The Flow.md now has four tabs for capacity bounds:
1. Fixed Size — Standard bounds without on/off
2. Fixed Size + On/Off — Can be zero when off
3. Variable Size — Investment decision on capacity
4. Variable Size + On/Off — Both investment and on/off, with big-M linearization for the bilinear term $s(t) \cdot P$
* InvestParameters.md:
- Story-driven intro with real-world examples
- Core concept: "Size as a Variable"
- Tabs for: Binary (Fixed Size) | Continuous (Size Range) | Mandatory
- Tabs for effects: Fixed | Specific | Retirement | Piecewise
- Minimal variable names: $P$, $s_{inv}$, $c_{fix}$, $c_{spec}$, etc.
- Cost annualization formula
OnOffParameters.md:
- Story-driven intro with real-world examples
- Core concept: "Binary State" with flow bound modification
- Tabs for state transitions: Switch Detection | Startup Costs | Running Costs
- Tabs for duration constraints: Min Run Time | Min Off Time | Max Run Time | Total Hours | Max Startups
- Minimal variable names: $s(t)$, $s^{on}(t)$, $s^{off}(t)$, $T_{on}^{min}$, etc.
Piecewise.md:
- Story-driven intro with ASCII diagram
- Core concept: Linear segments with weighted combinations
- Tabs for constraints: Single Piece Active | With Zero Point
- Tabs for piece patterns: Continuous (Touching) | Gap (Forbidden Region) | Zero Point
- Minimal variable names: $\beta_k$, $\lambda_0$, $\lambda_1$, etc.
- Practical examples for heat pumps, boilers, and investment
* Make OnOffParameters better
* Piecewise.md:
- Replaced the useless ASCII diagram with a more informative one showing:
- Actual axis labels (input/output)
- Numeric values on axes
- Two pieces with their connection point labeled
- Clear visual of how pieces connect at (50, 45)
- Shows the start/end points notation
* Add plotly chart
* Add custom javascript
* Remove charts plugin
* Add missing docs file
* Fix quick start
* Delete model.md
* Update citation
* Update license.md
* Simplify faq, support and troubleshooting.md
* Remove old workflow
* 1. Renamed OnOffParameters.md → StatusParameters.md
2. Updated all terminology:
- on_off_parameters → status_parameters
- OnOffParameters → StatusParameters
- effects_per_switch_on → effects_per_startup
- effects_per_running_hour → effects_per_active_hour
- consecutive_on_hours_min → min_uptime
- consecutive_on_hours_max → max_uptime
- consecutive_off_hours_min → min_downtime
- on_hours_min/max → active_hours_min/max
- switch_on_max → startup_limit
- switch_on/switch_off → startup/shutdown
- "on/off" language → "active/inactive" language
3. Updated references in Flow.md, LinearConverter.md, and effects-penalty-objective.md
* Remove Modeling patterns from docs
* Simplify docs
* Improve LinearConverter.md
* Improve Flow.md
* Improve effects-penalty-objective.md
* Improve InvestParameters.md
* Add durtaion constraints
* Update Piecewise stuff
* Update Piecewise stuff
* Update Piecewise stuff
* Combine effects and dimensions into one tab
* The dimension examples now correctly show how to assign them to FlowSystem using pd.Index:
* Update effects-and-dimensions.md
* Update effects-and-dimensions.md
* updated all reference tables across all Mathematical Notation pages to be consistent
* updated all reference tables across all Mathematical Notation pages to be consistent
* updated all reference tables across all Mathematical Notation pages to be consistent
…on directly on the flow_system with accessors for aggregated views and plots
…est_transmission_balanced and test_transmission_unbalanced. Removed the self-comparing assertions and consolidated them with the meaningful loss calculation tests. 2. flixopt/optimize_accessor.py - Removed the unused logging import and logger variable. 3. flixopt/config.py - Updated the docstring for compute_infeasibilities from "Whether to save model documentation on infeasibility" to "Whether to compute infeasibility analysis when the model is infeasible." 4. flixopt/transform_accessor.py - Changed if not dt_min == dt_max: to the more idiomatic if dt_min != dt_max:. 5. pyproject.toml - Added "ignore:SegmentedResults is deprecated:DeprecationWarning:flixopt" to the pytest filter warnings to handle the SegmentedResults deprecation warning in backward compatibility tests. Fix depprecation warings in tests
* Add planning doc
* Finalize planning
* Add plotting acessor
* Add plotting acessor
* Add tests
* Improve
* Improve
* Update docs
* Updated the plotting API so that .data always returns xarray DataArray or Dataset instead of pandas DataFrame.
* All .data now returns xr.Dataset consistently.
* Fixed Inconsistencies and Unused Parameters
* New Plot Accessors
results.plot.variable(pattern)
Plots the same variable type across multiple elements for easy comparison.
# All binary operation states across all components
results.plot.variable('on')
# All flow_rates, filtered to Boiler-related elements
results.plot.variable('flow_rate', include='Boiler')
# All storage charge states
results.plot.variable('charge_state')
# With aggregation
results.plot.variable('flow_rate', aggregate='sum')
Key features:
- Searches all elements for variables matching the pattern
- Filter with include/exclude on element names
- Supports aggregation, faceting, and animation
- Returns Dataset with element names as variables
results.plot.duration_curve(variables)
Plots load duration curves (sorted time series) showing utilization patterns.
# Single variable
results.plot.duration_curve('Boiler(Q_th)|flow_rate')
# Multiple variables
results.plot.duration_curve(['CHP|on', 'Boiler|on'])
# Normalized x-axis (0-100%)
results.plot.duration_curve('demand', normalize=True)
Key features:
- Sorts values from highest to lowest
- Shows how often each power level is reached
- normalize=True shows percentage of time on x-axis
- Returns Dataset with duration_hours or duration_pct dimension
* Fix duration curve
* Fix duration curve
* Fix duration curve
* Fix duration curve
* xr.apply_ufunc to sort along the time axis while preserving all other dimensions automatically
* ⏺ The example runs successfully. Now let me summarize the fixes:
Summary of Fixes
I addressed the actionable code review comments from CodeRabbitAI:
1. Documentation Issue - reshape parameter name ✓ (No fix needed)
The CodeRabbitAI comment was incorrect. The public API parameter in PlotAccessor.heatmap() is correctly named reshape (line 335). The reshape_time parameter exists in the lower-level heatmap_with_plotly function, but the documentation correctly shows the public API parameter.
2. Fixed simple_example.py (lines 129-130)
Problem: The example called balance() and duration_curve() without required arguments, which would cause TypeError at runtime.
Fix: Added the required arguments:
- optimization.results.plot.balance('Fernwärme') - specifying the bus to plot
- optimization.results.plot.duration_curve('Boiler(Q_th)|flow_rate') - specifying the variable to plot
3. Fixed variable collision in plot_accessors.py (lines 985-988)
Problem: When building the Dataset in the variable() method, using element names as keys could cause collisions if multiple variables from the same element matched the pattern (e.g., 'Boiler|flow_rate' and 'Boiler|flow_rate_max' would both map to 'Boiler', with the latter silently overwriting the former).
Fix: Changed to use the full variable names as keys instead of just element names:
ds = xr.Dataset({var_name: self._results.solution[var_name] for var_name in filtered_vars})
All tests pass (40 passed, 1 skipped) and the example runs successfully.
* make variable names public in results
* Fix sankey
* Fix effects()
* Fix effects
* Remove bargaps
* made faceting consistent across all plot methods:
| Method | facet_col | facet_row |
|------------------|-------------------------------------------|-----------------------------|
| balance() | 'scenario' | 'period' |
| heatmap() | 'scenario' | 'period' |
| storage() | 'scenario' | 'period' |
| flows() | 'scenario' | 'period' |
| effects() | 'scenario' | 'period' |
| variable() | 'scenario' | 'period' |
| duration_curve() | 'scenario' | 'period' (already had this) |
| compare() | N/A (uses its own mode='overlay'/'facet') | |
| sankey() | N/A (aggregates to single diagram) | |
Removed animate_by parameter from all methods
* Update storage method
* Remove mode parameter for simpli
| Method | Default mode |
|------------------|---------------------------------------------------|
| balance() | stacked_bar |
| storage() | stacked_bar (flows) + line (charge state overlay) |
| flows() | line |
| variable() | line |
| duration_curve() | line |
| effects() | bar |
* Make plotting_accessors.py more self contained
* Use faster to_long()
* Add 0-dim case
* sankey diagram now properly handles scenarios and periods:
Changes made:
1. Period aggregation with weights: Uses period_weights from flow_system to properly weight periods by their duration
2. Scenario aggregation with weights: Uses normalized scenario_weights to compute a weighted average across scenarios
3. Selection support: Users can filter specific scenarios/periods via select parameter before aggregation
Weighting logic:
- Periods (for aggregate='sum'): (da * period_weights).sum(dim='period') - this gives the total energy across all periods weighted by their duration
- Periods (for aggregate='mean'): (da * period_weights).sum(dim='period') / period_weights.sum() - weighted mean
- Scenarios: Always uses normalized weights (sum to 1) for weighted averaging, since scenarios represent probability-weighted alternatives
* Add colors to sankey
* Add sizes
* Add size filtering
* Include storage sizes
* Remove storage sizes
* Add charge state and status accessor
* Summary of Changes
1. Added new methods to PlotAccessor (plot_accessors.py)
charge_states() (line 658):
- Returns a Dataset with each storage's charge state as a variable
- Supports filtering with include/exclude parameters
- Default plot: line chart
on_states() (line 753):
- Returns a Dataset with each component's |status variable
- Supports filtering with include/exclude parameters
- Default plot: heatmap (good for binary data visualization)
2. Added data building helper functions (plot_accessors.py)
build_flow_rates(results) (line 315):
- Builds a DataArray containing flow rates for all flows
- Used internally by PlotAccessor methods
build_flow_hours(results) (line 333):
- Builds a DataArray containing flow hours for all flows
build_sizes(results) (line 347):
- Builds a DataArray containing sizes for all flows
_filter_dataarray_by_coord(da, **kwargs) (line 284):
- Helper for filtering DataArrays by coordinate values
3. Deprecated old Results methods (results.py)
The following methods now emit DeprecationWarning:
- results.flow_rates() → Use results.plot.flows(plot=False).data
- results.flow_hours() → Use results.plot.flows(unit='flow_hours', plot=False).data
- results.sizes() → Use results.plot.sizes(plot=False).data
4. Updated PlotAccessor methods to use new helpers
- flows() now uses build_flow_rates() / build_flow_hours() directly
- sizes() now uses build_sizes() directly
- sankey() now uses build_flow_hours() directly
This ensures the deprecation warnings only fire when users directly call the old methods, not when using the plot accessor
* 1. New methods added to PlotAccessor
- charge_states(): Returns Dataset with all storage charge states
- on_states(): Returns Dataset with all component status variables (heatmap display)
2. Data building helper functions (plot_accessors.py)
- build_flow_rates(results): Builds DataArray of flow rates
- build_flow_hours(results): Builds DataArray of flow hours
- build_sizes(results): Builds DataArray of sizes
- _filter_dataarray_by_coord(da, **kwargs): Filter helper
- _assign_flow_coords(da, results): Add flow coordinates
3. Caching in PlotAccessor
Added lazy-cached properties for expensive computations:
- _all_flow_rates - cached DataArray of all flow rates
- _all_flow_hours - cached DataArray of all flow hours
- _all_sizes - cached DataArray of all sizes
- _all_charge_states - cached Dataset of all storage charge states
- _all_status_vars - cached Dataset of all status variables
4. Deprecated methods in Results class
Added deprecation warnings to:
- results.flow_rates() → Use results.plot.flows(plot=False).data
- results.flow_hours() → Use results.plot.flows(unit='flow_hours', plot=False).data
- results.sizes() → Use results.plot.sizes(plot=False).data
5. Updated PlotAccessor methods to use cached properties
- flows() uses _all_flow_rates / _all_flow_hours
- sankey() uses _all_flow_hours
- sizes() uses _all_sizes
- charge_states() uses _all_charge_states
- on_states() uses _all_status_vars
* Move deprectated functionality into results.py instead of porting to the new module
* Revert to simply deprectae old methods without forwarding to new code
* Remove planning file
* Update plotting methods for new datasets
* 1. Renamed data properties in PlotAccessor to use all_ prefix:
- all_flow_rates - All flow rates as Dataset
- all_flow_hours - All flow hours as Dataset
- all_sizes - All flow sizes as Dataset
- all_charge_states - All storage charge states as Dataset
- all_on_states - All component on/off status as Dataset
2. Updated internal references - All usages in flows(), sankey(), sizes(), charge_states(), and on_states() methods now use the new names.
3. Updated deprecation messages in results.py to point to the new API:
- results.flow_rates() → results.plot.all_flow_rates
- results.flow_hours() → results.plot.all_flow_hours
- results.sizes() → results.plot.all_sizes
4. Updated docstring examples in PlotAccessor to use the new all_* names.
* Update deprecations messages
* Update deprecations messages
* Thsi seems much better.
* Updaet docstrings and variable name generation in plotting acessor
* Change __ to _ in private dataset caching
* Revert breaking io changes
* New solution storing interface
* Add new focused statistics and plot accessors
* Renamed all properties:
- all_flow_rates → flow_rates
- all_flow_hours → flow_hours
- all_sizes → sizes
- all_charge_states → charge_states
* Cache Statistics
* Invalidate caches
* Add effect related statistics
* Simplify statistics accessor to rely on flow_system directly instead of solution attrs
* Fix heatma fallback for 1D Data
* Add topology accessor
* All deprecation warnings in the codebase now consistently use the format will be removed in v{DEPRECATION_REMOVAL_VERSION}.
* Update tests
* created comprehensive documentation for all FlowSystem accessors
* Update results documentation
* Update results documentation
* Update effect statistics
* Update effect statistics
* Update effect statistics
* Add mkdocs plotly plugin
* Add section about custom constraints
* documentation updates:
docs/user-guide/results/index.md:
- Updated table to replace effects_per_component with temporal_effects, periodic_effects, total_effects, and effect_share_factors
- Fixed flow_hours['Boiler(Q_th)|flow_rate'] → flow_hours['Boiler(Q_th)']
- Fixed sizes['Boiler(Q_th)|size'] → sizes['Boiler(Q_th)']
- Replaced effects_per_component example with new effect properties and groupby examples
- Updated complete example to use total_effects
docs/user-guide/results-plotting.md:
- Fixed colors example from 'Boiler(Q_th)|flow_rate' → 'Boiler(Q_th)'
- Fixed duration_curve examples to use clean labels
docs/user-guide/migration-guide-v6.md:
- Added new "Statistics Accessor" section explaining the clean labels and new effect properties
* implemented the effects() method in StatisticsPlotAccessor at flixopt/statistics_accessor.py:1132-1258.
Summary of what was done:
1. Implemented effects() method in StatisticsPlotAccessor class that was missing but documented
- Takes aspect parameter: 'total', 'temporal', or 'periodic'
- Takes effect parameter to filter to a specific effect (e.g., 'costs', 'CO2')
- Takes by parameter: 'component' or 'time' for grouping
- Supports all standard plotting parameters: select, colors, facet_col, facet_row, show
- Returns PlotResult with both data and figure
2. Verified the implementation works with all parameter combinations:
- Default call: flow_system.statistics.plot.effects()
- Specific effect: flow_system.statistics.plot.effects(effect='costs')
- Temporal aspect: flow_system.statistics.plot.effects(aspect='temporal')
- Temporal by time: flow_system.statistics.plot.effects(aspect='temporal', by='time')
- Periodic aspect: flow_system.statistics.plot.effects(aspect='periodic')
* Remove intermediate plot accessor
* 1. pyproject.toml: Removed duplicate mkdocs-plotly-plugin>=0.1.3 entry (kept the exact pin ==0.1.3)
2. flixopt/plotting.py: Fixed dimension name consistency by using squeezed_data.name instead of data.name in the fallback heatmap logic
3. flixopt/statistics_accessor.py:
- Fixed _dataset_to_long_df() to only use coordinates that are actually present as columns after reset_index()
- Fixed the nested loop inefficiency with include_flows by pre-computing the flows list outside the loop
- (Previously fixed) Fixed asymmetric NaN handling in validation check
* _create_effects_dataset method in statistics_accessor.py was simplified:
1. Detect contributors from solution data variables instead of assuming they're only flows
- Uses regex pattern to find {contributor}->{effect}(temporal|periodic) variables
- Contributors can be flows OR components (e.g., components with effects_per_active_hour)
2. Exclude effect-to-effect shares
- Filters out contributors whose base name matches any effect label
- For example, costs(temporal) is excluded because costs is an effect label
- These intermediate shares are already included in the computation
3. Removed the unused _compute_effect_total method
- The new simplified implementation directly looks up shares from the solution
- Uses effect_share_factors for conversion between effects
4. Key insight from user: The solution already contains properly computed share values including all effect-to-effect conversions. The computation uses conversion
factors because derived effects (like Effect1 which shares 0.5 from costs) don't have direct {flow}->Effect1(temporal) variables - only the source effect shares exist
({flow}->costs(temporal)).
* Update docs
* Improve to_netcdf method
* Update examples
* Fix IIS computaion flag
* Fix examples
* Fix faceting in heatmap and use period as facet col everywhere
* Inline plotting methods to deprecate plotting.py (#508)
* Inline plotting methods to deprecate plotting.py
* Fix test
* Simplify Color Management
* ColorType is now defined in color_processing.py and imported into statistics_accessor.py.
* Fix ColorType typing
* statistics_accessor.py - Heatmap colors type safety (lines 121-148, 820-853)
- Changed _heatmap_figure() parameter type from colors: ColorType = None to colors: str | list[str] | None = None
- Changed heatmap() method parameter type similarly
- Updated docstrings to clarify that dicts are not supported for heatmaps since px.imshow's color_continuous_scale only accepts colorscale names or lists
2. statistics_accessor.py - Use configured qualitative colorscale (lines 284, 315)
- Updated _create_stacked_bar() to use CONFIG.Plotting.default_qualitative_colorscale as the default colorscale
- Updated _create_line() similarly
- This ensures user-configured CONFIG.Plotting.default_qualitative_colorscale affects all bar/line plots consistently
3. topology_accessor.py - Path type alignment (lines 219-222)
- Added normalization of path=False to None before calling _plot_network()
- This resolves the type mismatch where TopologyAccessor.plot() accepts bool | str | Path but _plot_network() only accepts str | Path | None
* fix usage if index name in aggregation plot
* Add planning doc
* Finalize planning
* Add plotting acessor
* Add plotting acessor
* Add tests
* Improve
* Improve
* Update docs
* Updated the plotting API so that .data always returns xarray DataArray or Dataset instead of pandas DataFrame.
* All .data now returns xr.Dataset consistently.
* Fixed Inconsistencies and Unused Parameters
* New Plot Accessors
results.plot.variable(pattern)
Plots the same variable type across multiple elements for easy comparison.
# All binary operation states across all components
results.plot.variable('on')
# All flow_rates, filtered to Boiler-related elements
results.plot.variable('flow_rate', include='Boiler')
# All storage charge states
results.plot.variable('charge_state')
# With aggregation
results.plot.variable('flow_rate', aggregate='sum')
Key features:
- Searches all elements for variables matching the pattern
- Filter with include/exclude on element names
- Supports aggregation, faceting, and animation
- Returns Dataset with element names as variables
results.plot.duration_curve(variables)
Plots load duration curves (sorted time series) showing utilization patterns.
# Single variable
results.plot.duration_curve('Boiler(Q_th)|flow_rate')
# Multiple variables
results.plot.duration_curve(['CHP|on', 'Boiler|on'])
# Normalized x-axis (0-100%)
results.plot.duration_curve('demand', normalize=True)
Key features:
- Sorts values from highest to lowest
- Shows how often each power level is reached
- normalize=True shows percentage of time on x-axis
- Returns Dataset with duration_hours or duration_pct dimension
* Fix duration curve
* Fix duration curve
* Fix duration curve
* Fix duration curve
* xr.apply_ufunc to sort along the time axis while preserving all other dimensions automatically
* ⏺ The example runs successfully. Now let me summarize the fixes:
Summary of Fixes
I addressed the actionable code review comments from CodeRabbitAI:
1. Documentation Issue - reshape parameter name ✓ (No fix needed)
The CodeRabbitAI comment was incorrect. The public API parameter in PlotAccessor.heatmap() is correctly named reshape (line 335). The reshape_time parameter exists in the lower-level heatmap_with_plotly function, but the documentation correctly shows the public API parameter.
2. Fixed simple_example.py (lines 129-130)
Problem: The example called balance() and duration_curve() without required arguments, which would cause TypeError at runtime.
Fix: Added the required arguments:
- optimization.results.plot.balance('Fernwärme') - specifying the bus to plot
- optimization.results.plot.duration_curve('Boiler(Q_th)|flow_rate') - specifying the variable to plot
3. Fixed variable collision in plot_accessors.py (lines 985-988)
Problem: When building the Dataset in the variable() method, using element names as keys could cause collisions if multiple variables from the same element matched the pattern (e.g., 'Boiler|flow_rate' and 'Boiler|flow_rate_max' would both map to 'Boiler', with the latter silently overwriting the former).
Fix: Changed to use the full variable names as keys instead of just element names:
ds = xr.Dataset({var_name: self._results.solution[var_name] for var_name in filtered_vars})
All tests pass (40 passed, 1 skipped) and the example runs successfully.
* make variable names public in results
* Fix sankey
* Fix effects()
* Fix effects
* Remove bargaps
* made faceting consistent across all plot methods:
| Method | facet_col | facet_row |
|------------------|-------------------------------------------|-----------------------------|
| balance() | 'scenario' | 'period' |
| heatmap() | 'scenario' | 'period' |
| storage() | 'scenario' | 'period' |
| flows() | 'scenario' | 'period' |
| effects() | 'scenario' | 'period' |
| variable() | 'scenario' | 'period' |
| duration_curve() | 'scenario' | 'period' (already had this) |
| compare() | N/A (uses its own mode='overlay'/'facet') | |
| sankey() | N/A (aggregates to single diagram) | |
Removed animate_by parameter from all methods
* Update storage method
* Remove mode parameter for simpli
| Method | Default mode |
|------------------|---------------------------------------------------|
| balance() | stacked_bar |
| storage() | stacked_bar (flows) + line (charge state overlay) |
| flows() | line |
| variable() | line |
| duration_curve() | line |
| effects() | bar |
* Make plotting_accessors.py more self contained
* Use faster to_long()
* Add 0-dim case
* sankey diagram now properly handles scenarios and periods:
Changes made:
1. Period aggregation with weights: Uses period_weights from flow_system to properly weight periods by their duration
2. Scenario aggregation with weights: Uses normalized scenario_weights to compute a weighted average across scenarios
3. Selection support: Users can filter specific scenarios/periods via select parameter before aggregation
Weighting logic:
- Periods (for aggregate='sum'): (da * period_weights).sum(dim='period') - this gives the total energy across all periods weighted by their duration
- Periods (for aggregate='mean'): (da * period_weights).sum(dim='period') / period_weights.sum() - weighted mean
- Scenarios: Always uses normalized weights (sum to 1) for weighted averaging, since scenarios represent probability-weighted alternatives
* Add colors to sankey
* Add sizes
* Add size filtering
* Include storage sizes
* Remove storage sizes
* Add charge state and status accessor
* Summary of Changes
1. Added new methods to PlotAccessor (plot_accessors.py)
charge_states() (line 658):
- Returns a Dataset with each storage's charge state as a variable
- Supports filtering with include/exclude parameters
- Default plot: line chart
on_states() (line 753):
- Returns a Dataset with each component's |status variable
- Supports filtering with include/exclude parameters
- Default plot: heatmap (good for binary data visualization)
2. Added data building helper functions (plot_accessors.py)
build_flow_rates(results) (line 315):
- Builds a DataArray containing flow rates for all flows
- Used internally by PlotAccessor methods
build_flow_hours(results) (line 333):
- Builds a DataArray containing flow hours for all flows
build_sizes(results) (line 347):
- Builds a DataArray containing sizes for all flows
_filter_dataarray_by_coord(da, **kwargs) (line 284):
- Helper for filtering DataArrays by coordinate values
3. Deprecated old Results methods (results.py)
The following methods now emit DeprecationWarning:
- results.flow_rates() → Use results.plot.flows(plot=False).data
- results.flow_hours() → Use results.plot.flows(unit='flow_hours', plot=False).data
- results.sizes() → Use results.plot.sizes(plot=False).data
4. Updated PlotAccessor methods to use new helpers
- flows() now uses build_flow_rates() / build_flow_hours() directly
- sizes() now uses build_sizes() directly
- sankey() now uses build_flow_hours() directly
This ensures the deprecation warnings only fire when users directly call the old methods, not when using the plot accessor
* 1. New methods added to PlotAccessor
- charge_states(): Returns Dataset with all storage charge states
- on_states(): Returns Dataset with all component status variables (heatmap display)
2. Data building helper functions (plot_accessors.py)
- build_flow_rates(results): Builds DataArray of flow rates
- build_flow_hours(results): Builds DataArray of flow hours
- build_sizes(results): Builds DataArray of sizes
- _filter_dataarray_by_coord(da, **kwargs): Filter helper
- _assign_flow_coords(da, results): Add flow coordinates
3. Caching in PlotAccessor
Added lazy-cached properties for expensive computations:
- _all_flow_rates - cached DataArray of all flow rates
- _all_flow_hours - cached DataArray of all flow hours
- _all_sizes - cached DataArray of all sizes
- _all_charge_states - cached Dataset of all storage charge states
- _all_status_vars - cached Dataset of all status variables
4. Deprecated methods in Results class
Added deprecation warnings to:
- results.flow_rates() → Use results.plot.flows(plot=False).data
- results.flow_hours() → Use results.plot.flows(unit='flow_hours', plot=False).data
- results.sizes() → Use results.plot.sizes(plot=False).data
5. Updated PlotAccessor methods to use cached properties
- flows() uses _all_flow_rates / _all_flow_hours
- sankey() uses _all_flow_hours
- sizes() uses _all_sizes
- charge_states() uses _all_charge_states
- on_states() uses _all_status_vars
* Move deprectated functionality into results.py instead of porting to the new module
* Revert to simply deprectae old methods without forwarding to new code
* Remove planning file
* Update plotting methods for new datasets
* 1. Renamed data properties in PlotAccessor to use all_ prefix:
- all_flow_rates - All flow rates as Dataset
- all_flow_hours - All flow hours as Dataset
- all_sizes - All flow sizes as Dataset
- all_charge_states - All storage charge states as Dataset
- all_on_states - All component on/off status as Dataset
2. Updated internal references - All usages in flows(), sankey(), sizes(), charge_states(), and on_states() methods now use the new names.
3. Updated deprecation messages in results.py to point to the new API:
- results.flow_rates() → results.plot.all_flow_rates
- results.flow_hours() → results.plot.all_flow_hours
- results.sizes() → results.plot.all_sizes
4. Updated docstring examples in PlotAccessor to use the new all_* names.
* Update deprecations messages
* Update deprecations messages
* Thsi seems much better.
* Updaet docstrings and variable name generation in plotting acessor
* Change __ to _ in private dataset caching
* Revert breaking io changes
* New solution storing interface
* Add new focused statistics and plot accessors
* Renamed all properties:
- all_flow_rates → flow_rates
- all_flow_hours → flow_hours
- all_sizes → sizes
- all_charge_states → charge_states
* Cache Statistics
* Invalidate caches
* Add effect related statistics
* Simplify statistics accessor to rely on flow_system directly instead of solution attrs
* Fix heatma fallback for 1D Data
* Add topology accessor
* All deprecation warnings in the codebase now consistently use the format will be removed in v{DEPRECATION_REMOVAL_VERSION}.
* Update tests
* created comprehensive documentation for all FlowSystem accessors
* Update results documentation
* Update results documentation
* Update effect statistics
* Update effect statistics
* Update effect statistics
* Add mkdocs plotly plugin
* Add section about custom constraints
* documentation updates:
docs/user-guide/results/index.md:
- Updated table to replace effects_per_component with temporal_effects, periodic_effects, total_effects, and effect_share_factors
- Fixed flow_hours['Boiler(Q_th)|flow_rate'] → flow_hours['Boiler(Q_th)']
- Fixed sizes['Boiler(Q_th)|size'] → sizes['Boiler(Q_th)']
- Replaced effects_per_component example with new effect properties and groupby examples
- Updated complete example to use total_effects
docs/user-guide/results-plotting.md:
- Fixed colors example from 'Boiler(Q_th)|flow_rate' → 'Boiler(Q_th)'
- Fixed duration_curve examples to use clean labels
docs/user-guide/migration-guide-v6.md:
- Added new "Statistics Accessor" section explaining the clean labels and new effect properties
* implemented the effects() method in StatisticsPlotAccessor at flixopt/statistics_accessor.py:1132-1258.
Summary of what was done:
1. Implemented effects() method in StatisticsPlotAccessor class that was missing but documented
- Takes aspect parameter: 'total', 'temporal', or 'periodic'
- Takes effect parameter to filter to a specific effect (e.g., 'costs', 'CO2')
- Takes by parameter: 'component' or 'time' for grouping
- Supports all standard plotting parameters: select, colors, facet_col, facet_row, show
- Returns PlotResult with both data and figure
2. Verified the implementation works with all parameter combinations:
- Default call: flow_system.statistics.plot.effects()
- Specific effect: flow_system.statistics.plot.effects(effect='costs')
- Temporal aspect: flow_system.statistics.plot.effects(aspect='temporal')
- Temporal by time: flow_system.statistics.plot.effects(aspect='temporal', by='time')
- Periodic aspect: flow_system.statistics.plot.effects(aspect='periodic')
* Remove intermediate plot accessor
* 1. pyproject.toml: Removed duplicate mkdocs-plotly-plugin>=0.1.3 entry (kept the exact pin ==0.1.3)
2. flixopt/plotting.py: Fixed dimension name consistency by using squeezed_data.name instead of data.name in the fallback heatmap logic
3. flixopt/statistics_accessor.py:
- Fixed _dataset_to_long_df() to only use coordinates that are actually present as columns after reset_index()
- Fixed the nested loop inefficiency with include_flows by pre-computing the flows list outside the loop
- (Previously fixed) Fixed asymmetric NaN handling in validation check
* _create_effects_dataset method in statistics_accessor.py was simplified:
1. Detect contributors from solution data variables instead of assuming they're only flows
- Uses regex pattern to find {contributor}->{effect}(temporal|periodic) variables
- Contributors can be flows OR components (e.g., components with effects_per_active_hour)
2. Exclude effect-to-effect shares
- Filters out contributors whose base name matches any effect label
- For example, costs(temporal) is excluded because costs is an effect label
- These intermediate shares are already included in the computation
3. Removed the unused _compute_effect_total method
- The new simplified implementation directly looks up shares from the solution
- Uses effect_share_factors for conversion between effects
4. Key insight from user: The solution already contains properly computed share values including all effect-to-effect conversions. The computation uses conversion
factors because derived effects (like Effect1 which shares 0.5 from costs) don't have direct {flow}->Effect1(temporal) variables - only the source effect shares exist
({flow}->costs(temporal)).
* Update docs
* Improve to_netcdf method
* Update examples
* Fix IIS computaion flag
* Fix examples
* Fix faceting in heatmap and use period as facet col everywhere
* Inline plotting methods to deprecate plotting.py
* Fix test
* Simplify Color Management
* ColorType is now defined in color_processing.py and imported into statistics_accessor.py.
* Fix ColorType typing
* Add color accessor
* Ensure io
* Add carrier class
* implemented Carrier as a proper Interface subclass with container support. Here's what was done:
1. Carrier class (flixopt/carrier.py)
- Now inherits from Interface for serialization capabilities
- Has transform_data() method (no-op since carriers have no time-series data)
- Has label property for container keying
- Maintains equality comparison with both Carrier objects and strings
2. CarrierContainer class (flixopt/carrier.py)
- Inherits from ContainerMixin['Carrier']
- Provides dict-like access with nice repr and error messages
- Uses carrier.name for keying
3. FlowSystem updates (flixopt/flow_system.py)
- _carriers is now a CarrierContainer instead of a plain dict
- carriers property returns the CarrierContainer
- add_carrier() uses the container's add() method
- Serialization updated to include carriers in to_dataset() and restore them in from_dataset()
4. Exports (flixopt/__init__.py)
- Both Carrier and CarrierContainer are now exported
* Inline plotting methods to deprecate plotting.py (#508)
* Inline plotting methods to deprecate plotting.py
* Fix test
* Simplify Color Management
* ColorType is now defined in color_processing.py and imported into statistics_accessor.py.
* Fix ColorType typing
* statistics_accessor.py - Heatmap colors type safety (lines 121-148, 820-853)
- Changed _heatmap_figure() parameter type from colors: ColorType = None to colors: str | list[str] | None = None
- Changed heatmap() method parameter type similarly
- Updated docstrings to clarify that dicts are not supported for heatmaps since px.imshow's color_continuous_scale only accepts colorscale names or lists
2. statistics_accessor.py - Use configured qualitative colorscale (lines 284, 315)
- Updated _create_stacked_bar() to use CONFIG.Plotting.default_qualitative_colorscale as the default colorscale
- Updated _create_line() similarly
- This ensures user-configured CONFIG.Plotting.default_qualitative_colorscale affects all bar/line plots consistently
3. topology_accessor.py - Path type alignment (lines 219-222)
- Added normalization of path=False to None before calling _plot_network()
- This resolves the type mismatch where TopologyAccessor.plot() accepts bool | str | Path but _plot_network() only accepts str | Path | None
* fix usage if index name in aggregation plot
* Add to docs
* Improve carrier colors and defaults
* Update default carriers and colors
* Update config
* Update config
* Move default carriers to config.py
* Change default carrier handling
* Add color handling
* Rmeove meta_data color handling
* Add carrierst to examples
* Improve plotting acessor
* Improve _resolve_variable_names
* Improve _resolve_variable_names
* Simplify coloring and remove color accessor
* Add connected_and_transformed handling
* Improve error message in container
* BUGFIX: Carrier from dataset
…or (#510) * Add planning doc * Finalize planning * Add plotting acessor * Add plotting acessor * Add tests * Improve * Improve * Update docs * Updated the plotting API so that .data always returns xarray DataArray or Dataset instead of pandas DataFrame. * All .data now returns xr.Dataset consistently. * Fixed Inconsistencies and Unused Parameters * New Plot Accessors results.plot.variable(pattern) Plots the same variable type across multiple elements for easy comparison. # All binary operation states across all components results.plot.variable('on') # All flow_rates, filtered to Boiler-related elements results.plot.variable('flow_rate', include='Boiler') # All storage charge states results.plot.variable('charge_state') # With aggregation results.plot.variable('flow_rate', aggregate='sum') Key features: - Searches all elements for variables matching the pattern - Filter with include/exclude on element names - Supports aggregation, faceting, and animation - Returns Dataset with element names as variables results.plot.duration_curve(variables) Plots load duration curves (sorted time series) showing utilization patterns. # Single variable results.plot.duration_curve('Boiler(Q_th)|flow_rate') # Multiple variables results.plot.duration_curve(['CHP|on', 'Boiler|on']) # Normalized x-axis (0-100%) results.plot.duration_curve('demand', normalize=True) Key features: - Sorts values from highest to lowest - Shows how often each power level is reached - normalize=True shows percentage of time on x-axis - Returns Dataset with duration_hours or duration_pct dimension * Fix duration curve * Fix duration curve * Fix duration curve * Fix duration curve * xr.apply_ufunc to sort along the time axis while preserving all other dimensions automatically * ⏺ The example runs successfully. Now let me summarize the fixes: Summary of Fixes I addressed the actionable code review comments from CodeRabbitAI: 1. Documentation Issue - reshape parameter name ✓ (No fix needed) The CodeRabbitAI comment was incorrect. The public API parameter in PlotAccessor.heatmap() is correctly named reshape (line 335). The reshape_time parameter exists in the lower-level heatmap_with_plotly function, but the documentation correctly shows the public API parameter. 2. Fixed simple_example.py (lines 129-130) Problem: The example called balance() and duration_curve() without required arguments, which would cause TypeError at runtime. Fix: Added the required arguments: - optimization.results.plot.balance('Fernwärme') - specifying the bus to plot - optimization.results.plot.duration_curve('Boiler(Q_th)|flow_rate') - specifying the variable to plot 3. Fixed variable collision in plot_accessors.py (lines 985-988) Problem: When building the Dataset in the variable() method, using element names as keys could cause collisions if multiple variables from the same element matched the pattern (e.g., 'Boiler|flow_rate' and 'Boiler|flow_rate_max' would both map to 'Boiler', with the latter silently overwriting the former). Fix: Changed to use the full variable names as keys instead of just element names: ds = xr.Dataset({var_name: self._results.solution[var_name] for var_name in filtered_vars}) All tests pass (40 passed, 1 skipped) and the example runs successfully. * make variable names public in results * Fix sankey * Fix effects() * Fix effects * Remove bargaps * made faceting consistent across all plot methods: | Method | facet_col | facet_row | |------------------|-------------------------------------------|-----------------------------| | balance() | 'scenario' | 'period' | | heatmap() | 'scenario' | 'period' | | storage() | 'scenario' | 'period' | | flows() | 'scenario' | 'period' | | effects() | 'scenario' | 'period' | | variable() | 'scenario' | 'period' | | duration_curve() | 'scenario' | 'period' (already had this) | | compare() | N/A (uses its own mode='overlay'/'facet') | | | sankey() | N/A (aggregates to single diagram) | | Removed animate_by parameter from all methods * Update storage method * Remove mode parameter for simpli | Method | Default mode | |------------------|---------------------------------------------------| | balance() | stacked_bar | | storage() | stacked_bar (flows) + line (charge state overlay) | | flows() | line | | variable() | line | | duration_curve() | line | | effects() | bar | * Make plotting_accessors.py more self contained * Use faster to_long() * Add 0-dim case * sankey diagram now properly handles scenarios and periods: Changes made: 1. Period aggregation with weights: Uses period_weights from flow_system to properly weight periods by their duration 2. Scenario aggregation with weights: Uses normalized scenario_weights to compute a weighted average across scenarios 3. Selection support: Users can filter specific scenarios/periods via select parameter before aggregation Weighting logic: - Periods (for aggregate='sum'): (da * period_weights).sum(dim='period') - this gives the total energy across all periods weighted by their duration - Periods (for aggregate='mean'): (da * period_weights).sum(dim='period') / period_weights.sum() - weighted mean - Scenarios: Always uses normalized weights (sum to 1) for weighted averaging, since scenarios represent probability-weighted alternatives * Add colors to sankey * Add sizes * Add size filtering * Include storage sizes * Remove storage sizes * Add charge state and status accessor * Summary of Changes 1. Added new methods to PlotAccessor (plot_accessors.py) charge_states() (line 658): - Returns a Dataset with each storage's charge state as a variable - Supports filtering with include/exclude parameters - Default plot: line chart on_states() (line 753): - Returns a Dataset with each component's |status variable - Supports filtering with include/exclude parameters - Default plot: heatmap (good for binary data visualization) 2. Added data building helper functions (plot_accessors.py) build_flow_rates(results) (line 315): - Builds a DataArray containing flow rates for all flows - Used internally by PlotAccessor methods build_flow_hours(results) (line 333): - Builds a DataArray containing flow hours for all flows build_sizes(results) (line 347): - Builds a DataArray containing sizes for all flows _filter_dataarray_by_coord(da, **kwargs) (line 284): - Helper for filtering DataArrays by coordinate values 3. Deprecated old Results methods (results.py) The following methods now emit DeprecationWarning: - results.flow_rates() → Use results.plot.flows(plot=False).data - results.flow_hours() → Use results.plot.flows(unit='flow_hours', plot=False).data - results.sizes() → Use results.plot.sizes(plot=False).data 4. Updated PlotAccessor methods to use new helpers - flows() now uses build_flow_rates() / build_flow_hours() directly - sizes() now uses build_sizes() directly - sankey() now uses build_flow_hours() directly This ensures the deprecation warnings only fire when users directly call the old methods, not when using the plot accessor * 1. New methods added to PlotAccessor - charge_states(): Returns Dataset with all storage charge states - on_states(): Returns Dataset with all component status variables (heatmap display) 2. Data building helper functions (plot_accessors.py) - build_flow_rates(results): Builds DataArray of flow rates - build_flow_hours(results): Builds DataArray of flow hours - build_sizes(results): Builds DataArray of sizes - _filter_dataarray_by_coord(da, **kwargs): Filter helper - _assign_flow_coords(da, results): Add flow coordinates 3. Caching in PlotAccessor Added lazy-cached properties for expensive computations: - _all_flow_rates - cached DataArray of all flow rates - _all_flow_hours - cached DataArray of all flow hours - _all_sizes - cached DataArray of all sizes - _all_charge_states - cached Dataset of all storage charge states - _all_status_vars - cached Dataset of all status variables 4. Deprecated methods in Results class Added deprecation warnings to: - results.flow_rates() → Use results.plot.flows(plot=False).data - results.flow_hours() → Use results.plot.flows(unit='flow_hours', plot=False).data - results.sizes() → Use results.plot.sizes(plot=False).data 5. Updated PlotAccessor methods to use cached properties - flows() uses _all_flow_rates / _all_flow_hours - sankey() uses _all_flow_hours - sizes() uses _all_sizes - charge_states() uses _all_charge_states - on_states() uses _all_status_vars * Move deprectated functionality into results.py instead of porting to the new module * Revert to simply deprectae old methods without forwarding to new code * Remove planning file * Update plotting methods for new datasets * 1. Renamed data properties in PlotAccessor to use all_ prefix: - all_flow_rates - All flow rates as Dataset - all_flow_hours - All flow hours as Dataset - all_sizes - All flow sizes as Dataset - all_charge_states - All storage charge states as Dataset - all_on_states - All component on/off status as Dataset 2. Updated internal references - All usages in flows(), sankey(), sizes(), charge_states(), and on_states() methods now use the new names. 3. Updated deprecation messages in results.py to point to the new API: - results.flow_rates() → results.plot.all_flow_rates - results.flow_hours() → results.plot.all_flow_hours - results.sizes() → results.plot.all_sizes 4. Updated docstring examples in PlotAccessor to use the new all_* names. * Update deprecations messages * Update deprecations messages * Thsi seems much better. * Updaet docstrings and variable name generation in plotting acessor * Change __ to _ in private dataset caching * Revert breaking io changes * New solution storing interface * Add new focused statistics and plot accessors * Renamed all properties: - all_flow_rates → flow_rates - all_flow_hours → flow_hours - all_sizes → sizes - all_charge_states → charge_states * Cache Statistics * Invalidate caches * Add effect related statistics * Simplify statistics accessor to rely on flow_system directly instead of solution attrs * Fix heatma fallback for 1D Data * Add topology accessor * All deprecation warnings in the codebase now consistently use the format will be removed in v{DEPRECATION_REMOVAL_VERSION}. * Update tests * created comprehensive documentation for all FlowSystem accessors * Update results documentation * Update results documentation * Update effect statistics * Update effect statistics * Update effect statistics * Add mkdocs plotly plugin * Add section about custom constraints * documentation updates: docs/user-guide/results/index.md: - Updated table to replace effects_per_component with temporal_effects, periodic_effects, total_effects, and effect_share_factors - Fixed flow_hours['Boiler(Q_th)|flow_rate'] → flow_hours['Boiler(Q_th)'] - Fixed sizes['Boiler(Q_th)|size'] → sizes['Boiler(Q_th)'] - Replaced effects_per_component example with new effect properties and groupby examples - Updated complete example to use total_effects docs/user-guide/results-plotting.md: - Fixed colors example from 'Boiler(Q_th)|flow_rate' → 'Boiler(Q_th)' - Fixed duration_curve examples to use clean labels docs/user-guide/migration-guide-v6.md: - Added new "Statistics Accessor" section explaining the clean labels and new effect properties * implemented the effects() method in StatisticsPlotAccessor at flixopt/statistics_accessor.py:1132-1258. Summary of what was done: 1. Implemented effects() method in StatisticsPlotAccessor class that was missing but documented - Takes aspect parameter: 'total', 'temporal', or 'periodic' - Takes effect parameter to filter to a specific effect (e.g., 'costs', 'CO2') - Takes by parameter: 'component' or 'time' for grouping - Supports all standard plotting parameters: select, colors, facet_col, facet_row, show - Returns PlotResult with both data and figure 2. Verified the implementation works with all parameter combinations: - Default call: flow_system.statistics.plot.effects() - Specific effect: flow_system.statistics.plot.effects(effect='costs') - Temporal aspect: flow_system.statistics.plot.effects(aspect='temporal') - Temporal by time: flow_system.statistics.plot.effects(aspect='temporal', by='time') - Periodic aspect: flow_system.statistics.plot.effects(aspect='periodic') * Remove intermediate plot accessor * 1. pyproject.toml: Removed duplicate mkdocs-plotly-plugin>=0.1.3 entry (kept the exact pin ==0.1.3) 2. flixopt/plotting.py: Fixed dimension name consistency by using squeezed_data.name instead of data.name in the fallback heatmap logic 3. flixopt/statistics_accessor.py: - Fixed _dataset_to_long_df() to only use coordinates that are actually present as columns after reset_index() - Fixed the nested loop inefficiency with include_flows by pre-computing the flows list outside the loop - (Previously fixed) Fixed asymmetric NaN handling in validation check * _create_effects_dataset method in statistics_accessor.py was simplified: 1. Detect contributors from solution data variables instead of assuming they're only flows - Uses regex pattern to find {contributor}->{effect}(temporal|periodic) variables - Contributors can be flows OR components (e.g., components with effects_per_active_hour) 2. Exclude effect-to-effect shares - Filters out contributors whose base name matches any effect label - For example, costs(temporal) is excluded because costs is an effect label - These intermediate shares are already included in the computation 3. Removed the unused _compute_effect_total method - The new simplified implementation directly looks up shares from the solution - Uses effect_share_factors for conversion between effects 4. Key insight from user: The solution already contains properly computed share values including all effect-to-effect conversions. The computation uses conversion factors because derived effects (like Effect1 which shares 0.5 from costs) don't have direct {flow}->Effect1(temporal) variables - only the source effect shares exist ({flow}->costs(temporal)). * Update docs * Improve to_netcdf method * Update examples * Fix IIS computaion flag * Fix examples * Fix faceting in heatmap and use period as facet col everywhere * Inline plotting methods to deprecate plotting.py * Fix test * Simplify Color Management * ColorType is now defined in color_processing.py and imported into statistics_accessor.py. * Fix ColorType typing * Add color accessor * Ensure io * Add carrier class * implemented Carrier as a proper Interface subclass with container support. Here's what was done: 1. Carrier class (flixopt/carrier.py) - Now inherits from Interface for serialization capabilities - Has transform_data() method (no-op since carriers have no time-series data) - Has label property for container keying - Maintains equality comparison with both Carrier objects and strings 2. CarrierContainer class (flixopt/carrier.py) - Inherits from ContainerMixin['Carrier'] - Provides dict-like access with nice repr and error messages - Uses carrier.name for keying 3. FlowSystem updates (flixopt/flow_system.py) - _carriers is now a CarrierContainer instead of a plain dict - carriers property returns the CarrierContainer - add_carrier() uses the container's add() method - Serialization updated to include carriers in to_dataset() and restore them in from_dataset() 4. Exports (flixopt/__init__.py) - Both Carrier and CarrierContainer are now exported * Inline plotting methods to deprecate plotting.py (#508) * Inline plotting methods to deprecate plotting.py * Fix test * Simplify Color Management * ColorType is now defined in color_processing.py and imported into statistics_accessor.py. * Fix ColorType typing * statistics_accessor.py - Heatmap colors type safety (lines 121-148, 820-853) - Changed _heatmap_figure() parameter type from colors: ColorType = None to colors: str | list[str] | None = None - Changed heatmap() method parameter type similarly - Updated docstrings to clarify that dicts are not supported for heatmaps since px.imshow's color_continuous_scale only accepts colorscale names or lists 2. statistics_accessor.py - Use configured qualitative colorscale (lines 284, 315) - Updated _create_stacked_bar() to use CONFIG.Plotting.default_qualitative_colorscale as the default colorscale - Updated _create_line() similarly - This ensures user-configured CONFIG.Plotting.default_qualitative_colorscale affects all bar/line plots consistently 3. topology_accessor.py - Path type alignment (lines 219-222) - Added normalization of path=False to None before calling _plot_network() - This resolves the type mismatch where TopologyAccessor.plot() accepts bool | str | Path but _plot_network() only accepts str | Path | None * fix usage if index name in aggregation plot * Add to docs * Improve carrier colors and defaults * Update default carriers and colors * Update config * Update config * Move default carriers to config.py * Change default carrier handling * Add color handling * Rmeove meta_data color handling * Add carrierst to examples * Improve plotting acessor * Improve _resolve_variable_names * Improve _resolve_variable_names * Simplify coloring and remove color accessor * Add connected_and_transformed handling * Improve error message in container * Methods moved to TransformAccessor (transform_accessor.py): - sel() - select by label - isel() - select by integer index - resample() - resample time dimension - Helper methods: _dataset_sel, _dataset_isel, _dataset_resample, _resample_by_dimension_groups 2. Solution is dropped: All transform methods return a new FlowSystem with no solution - the user must re-optimize the transformed system. 3. Deprecation warnings: The old flow_system.sel(), flow_system.isel(), and flow_system.resample() methods now emit deprecation warnings and forward to the new TransformAccessor methods. 4. Backward compatible: Existing code still works, just with deprecation warnings. * Documentation updated * Re-add _dataset_sel and other helper methods for proper deprectation. ALso fix new methods to be classmethods * BUGFIX: Carrier from dataset * Update docs
* fix: Link status parameters to flow_system
* ⏺ Summary
I've implemented the FlowSystem locking behavior as discussed. Here's what was done:
Core Concept
A FlowSystem is locked (read-only) when solution is not None.
New Features
1. is_locked property (flow_system.py:1127-1131)
- Returns True if the FlowSystem has a solution
2. reset() method (flow_system.py:1145-1168)
- Clears: solution, model, element submodels, variable/constraint names
- Returns self for method chaining
- Allows the FlowSystem to be modified and re-optimized
3. _invalidate_model() helper (flow_system.py:1133-1143)
- Called when adding elements/carriers to a FlowSystem with a model (but no solution)
- Clears model and element state
4. copy() method (flow_system.py:733-767)
- Creates a fresh FlowSystem copy without solution/model
- Supports copy.copy() and copy.deepcopy()
Behavior Changes
| Operation | Before Optimization | After build_model() | After optimize() |
|----------------|-------------------------------|------------------------------------|-------------------------------|
| add_elements() | ✓ Works | ⚠ Works, warns & invalidates model | ✗ RuntimeError |
| add_carriers() | ✓ Works | ⚠ Works, warns & invalidates model | ✗ RuntimeError |
| copy() | Returns copy without solution | Returns copy without solution | Returns copy without solution |
| reset() | No-op | Clears model | Clears solution & model |
Bug Fix
Fixed an issue where StatusParameters created during modeling (for prevent_simultaneous_flows and component-level status) weren't linked to the FlowSystem
(elements.py:957-964, components.py:731-732).
Tests
Added comprehensive tests in tests/test_flow_system_locking.py (28 tests) covering:
- is_locked property behavior
- add_elements() locking and invalidation
- add_carriers() locking and invalidation
- reset() method functionality
- copy() method functionality
- Loaded FlowSystem behavior
* Add invalidation tests
* Add link_to_flow_system method
* Add link_to_flow_system method
* New invalidate() method (flow_system.py:1232-1275)
A public method for manual model invalidation when modifying element attributes after connect_and_transform():
def invalidate(self) -> FlowSystem:
"""Invalidate the model to allow re-transformation after modifying elements."""
- Raises RuntimeError if FlowSystem has a solution (must call reset() first)
- Returns self for method chaining
- Useful when you need to modify after connect_and_transform() but before optimize()
2. Updated docstrings
connect_and_transform() - Added comprehensive docstring explaining:
- What steps it performs
- Warning that attributes become xarray DataArrays after transformation
- Note about idempotency and how to reset with invalidate()
_invalidate_model() - Clarified its role and relationship to public methods
3. New tests (test_flow_system_locking.py:285-409)
Added TestInvalidate class with 8 tests:
- test_invalidate_resets_connected_and_transformed
- test_invalidate_clears_model
- test_invalidate_raises_when_locked
- test_invalidate_returns_self
- test_invalidate_allows_retransformation
- test_modify_element_and_invalidate - full workflow with reset
- test_invalidate_needed_after_transform_before_optimize - pre-optimization modification
- test_reset_already_invalidates - confirms reset already handles invalidation
Key insight from testing
reset() already calls _invalidate_model(), so modifications after reset() automatically take effect on the next optimize(). The new invalidate() method is primarily for the case where:
1. You've called connect_and_transform() manually
2. Haven't optimized yet (no solution)
3. Need to modify element attributes
* Typo
* Update sankey figure building * fix: sankey * fix: effects sankey * Add sankey diagramm tests * Add sankey diagramm tests * Minor nitpicks * All three failing tests are now fixed. Here's a summary of the changes made: Summary of Fixes 1. test_statistics_sizes_includes_all_flows - Fixed Problem: The statistics.sizes property was returning storage capacity sizes (like Speicher) which are not flow labels. The test expected only flow sizes. Fix in flixopt/statistics_accessor.py (lines 419-427): - Modified sizes property to filter only flow sizes by checking if the variable name (minus |size suffix) matches a flow label in self._fs.flows.keys() 2. test_sankey_sizes_mode - Fixed Problem: The sankey diagram with mode='sizes' returned empty links because the test fixture (simple_flow_system) didn't have any flows with InvestParameters. Fix in tests/conftest.py (lines 268-272): - Modified Storage.simple() to use fx.InvestParameters(fixed_size=1e4, mandatory=True) on the charging flow, ensuring there's at least one flow with investment parameters for testing. 3. test_sankey_sizes_max_size_filter - Fixed Problem: The sankey method didn't have a max_size parameter, so passing it caused the parameter to be forwarded to plotly_kwargs and then to update_layout(), which failed with "Invalid property specified for object of type plotly.graph_objs.Layout: 'max_size'". Fix in flixopt/statistics_accessor.py (lines 1351, 1367, 1392-1395): - Added max_size: float | None = None parameter to the sankey method signature - Added filtering logic to apply max_size filter when in mode='sizes' * Add new sankey network topology plot
* Extract docs build into separate workflow * Fix workflow * docs deploy job uses the uploaded artifact
WalkthroughLarge API and documentation overhaul: renames OnOff→Status, introduces FlowSystem accessors (optimize/transform/statistics/topology), consolidates solution I/O (to_netcdf/from_netcdf), removes legacy Calculation/Results shims, adds carriers/colors/PlotResult, and adds many docs, notebooks, and CI for docs deployment. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant FlowSystem
participant OptimizeAccessor
participant Solver
participant StatisticsAccessor
participant PlotResult
User->>FlowSystem: flow_system.optimize(solver)
FlowSystem->>OptimizeAccessor: __call__(solver)
OptimizeAccessor->>FlowSystem: build_model(normalize_weights)
FlowSystem->>FlowSystem: create/attach model
OptimizeAccessor->>FlowSystem: solve(solver)
FlowSystem->>Solver: run solver on model
Solver-->>FlowSystem: solution stored
FlowSystem-->>User: return FlowSystem (solution accessible)
User->>FlowSystem: flow_system.statistics.plot.balance(bus)
FlowSystem->>StatisticsAccessor: request data
StatisticsAccessor->>FlowSystem: read solution dataset
StatisticsAccessor->>PlotResult: assemble data + figure
PlotResult-->>User: PlotResult (show / export)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Areas to focus on during review:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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). (5)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
mkdocs.yml (1)
269-273: Pipeline failure: Signature formatting requires Black or Ruff.The mkdocstrings configuration uses
separate_signature: truewhich requires either Black or Ruff to be installed. Add one of these to documentation dependencies:# In pyproject.toml or docs requirements +black # or ruffAlternatively, disable signature separation:
separate_signature: false
🧹 Nitpick comments (19)
scripts/format_changelog.py (1)
56-63: Make changelog path independent of current working directory (and optionally fail fast on missing file)Right now the script assumes it’s run from the repo root (
Path('docs/changelog.md')). That’s brittle if invoked from a different CWD (e.g. via a tool that runs from another directory).You can anchor the path to the script location instead:
def main(): """Process the changelog file.""" - changelog_path = Path('docs/changelog.md') + script_dir = Path(__file__).resolve().parent + repo_root = script_dir.parent # assumes this file lives in <repo>/scripts/ + changelog_path = repo_root / 'docs' / 'changelog.md' @@ - if not changelog_path.exists(): - print(f'❌ {changelog_path} not found') - return + if not changelog_path.exists(): + print(f'❌ {changelog_path} not found') + return # or `sys.exit(1)` if you want CI to fail when the changelog is missingIf this is intended for CI/pre‑release automation, consider using
sys.exit(1)instead ofreturnso a missing changelog fails the step explicitly.docs/home/license.md (1)
7-29: Add language identifier to the fenced code block.The license text code block is missing a language identifier, which causes a linting warning (MD040).
Apply this diff to add the language identifier:
-``` +```text MIT Licensedocs/home/citing.md (1)
15-15: Convert emphasized text to proper heading or regular text.The line uses emphasis (italics) for what appears to be a placeholder message. This triggers a linting warning (MD036).
Apply this diff to use regular text instead:
-*Coming soon: A list of academic publications that have used flixOpt* +Coming soon: A list of academic publications that have used flixOpt..github/workflows/tests.yaml (1)
7-7: Clarify the intent of the**pattern.The
**pattern in glob matching is semantically different from*—it matches zero or more characters including slashes (nested paths), while*matches any characters except slashes. For this repository's flat branch naming convention, both patterns currently produce identical behavior. However, the change is worth documenting to clarify intent, especially if the branch strategy may evolve to include slash-based naming conventions in the future.flixopt/config.py (1)
650-678: Consider including Carriers in serialization.The
to_dict()method excludesCarriersfrom serialization. This is likely intentional since Carrier objects aren't simple JSON-serializable values, but it means carrier customizations won't round-trip throughto_dict()/load_from_file().If carrier persistence is needed in the future, consider adding a serialization path for custom carrier colors/units.
docs/user-guide/building-models/index.md (1)
1-12: Update "On/Off" terminology to "Status" for consistency with StatusParameters refactor.Line 12 references "On/Off" but the PR is migrating to StatusParameters terminology. This should be updated to align with the broader API changes.
Suggested fix:
- Using advanced features (Investment, On/Off, Piecewise) + Using advanced features (Investment, Status, Piecewise)Please verify this aligns with the terminology shift described in the PR objectives and other docs pages.
docs/user-guide/results/index.md (1)
116-116: Minor: Consider using fenced code blocks consistently.The markdown linter suggests using fenced code blocks (triple backticks) instead of indented code blocks for consistency throughout the documentation.
Apply this change if standardizing code block formatting:
- - **Flows**: Individual flows with `effects_per_flow_hour` - - **Components**: Components with `effects_per_active_hour` or similar direct effects +- **Flows**: Individual flows with `effects_per_flow_hour` +- **Components**: Components with `effects_per_active_hour` or similar direct effectsexamples/05_Two-stage-optimization/two_stage_optimization.py (1)
141-147: Size equality check relies on floating-point rounding.The comparison uses
.round(5)which is reasonable, but consider usingxr.testing.assert_allcloseornp.allclosefor more robust floating-point comparison:- if (dispatch_sizes.round(5).to_dataarray() == sizing_sizes.round(5).to_dataarray()).all().item(): + if np.allclose(dispatch_sizes.to_dataarray(), sizing_sizes.to_dataarray(), rtol=1e-5):flixopt/topology_accessor.py (1)
430-438: Redundant ImportError instop_app()is inconsistent with user expectation.If a user calls
stop_app()without having started an app (because dependencies weren't installed), the error message about missing dependencies is confusing. The check at line 440 (if self._fs._network_app is None) already handles the "no app running" case. Consider removing the dependency check or making it a warning instead:- if not DASH_CYTOSCAPE_AVAILABLE: - raise ImportError( - f'Network visualization requires optional dependencies. ' - ... - ) + # No need to check dependencies - if app was started, dependencies existflixopt/statistics_accessor.py (3)
376-380: Consider clearing cached data when solution changes.The cached properties (
_flow_rates,_flow_hours, etc.) are never invalidated. Ifoptimize()is called multiple times on the sameFlowSystem, stale cached data could be returned.Consider adding a method to clear the cache or checking if the solution has changed:
def _clear_cache(self) -> None: """Clear all cached data. Call when solution changes.""" self._flow_rates = None self._flow_hours = None self._sizes = None self._charge_states = None self._effect_share_factors = None self._temporal_effects = None self._periodic_effects = None self._total_effects = None
717-728: Validation logging useslogger.criticalfor non-critical mismatch.The validation mismatch in effect totals is logged at
CRITICALlevel, but it doesn't raise an exception or halt execution. This seems like a data integrity warning rather than a critical system failure.Consider using
logger.warningorlogger.errorinstead:- logger.critical( + logger.warning( f'Results for {effect}({mode}) in effects_dataset doesnt match {label}\n{computed=}\n, {found=}' )
987-990: Complex set operation could be clearer.The set subtraction with
- {None}works but is less readable than filtering explicitly.- keep_dims = set(heatmap_dims) | {actual_facet, actual_animation} - {None} + keep_dims = set(heatmap_dims) | {d for d in [actual_facet, actual_animation] if d is not None}flixopt/effects.py (1)
240-246:link_to_flow_systemignores passed prefix.The method signature accepts a
prefixparameter but explicitly ignores it, always usingself.label_full. The docstring explains this behavior, but it could be confusing for callers.Consider either:
- Removing the
prefixparameter entirely if it's always ignored, or- Logging a warning if a non-empty prefix is passed but ignored
def link_to_flow_system(self, flow_system, prefix: str = '') -> None: """Link this effect to a FlowSystem. Note: Effects always use their label_full as prefix, ignoring any passed prefix. """ if prefix and prefix != self.label_full: logger.debug(f"Effect '{self.label}' ignoring passed prefix '{prefix}', using '{self.label_full}'") super().link_to_flow_system(flow_system, self.label_full)flixopt/features.py (2)
158-159: Documentation link may be outdated.The docstring references
OnOffParametersin the URL but the class has been renamed toStatusModeland usesStatusParameters. Consider updating the link to match the new naming.Mathematical Formulation: - See <https://flixopt.github.io/flixopt/latest/user-guide/mathematical-notation/features/OnOffParameters/> + See <https://flixopt.github.io/flixopt/latest/user-guide/mathematical-notation/features/StatusParameters/>
347-348: Consider clearer syntax for status inversion.The expression
self._previous_status * -1 + 1inverts the binary status. Using1 - self._previous_statusis more idiomatic and readable.- return ModelingUtilities.compute_consecutive_hours_in_state(self._previous_status * -1 + 1, hours_per_step) + return ModelingUtilities.compute_consecutive_hours_in_state(1 - self._previous_status, hours_per_step)flixopt/transform_accessor.py (1)
506-507: Handle empty dimension groups gracefully.When
dim_groupsis empty (no variables with time dimension), the code falls through to callresampleon an empty dataset. This edge case is handled but could be made explicit with an early return.if not dim_groups: - return getattr(time_dataset.resample(time=time, **kwargs), method)() + # No variables to resample - return as-is + return time_datasettests/deprecated/conftest.py (1)
343-345: Potential issue:np.random.seedusage.Using
np.random.seedin static methods affects global state, which could cause non-deterministic test behavior if tests run in parallel or in unexpected order. Consider usingnp.random.default_rng(seed)for isolated random state.@staticmethod def random_thermal(length=10, seed=42): - np.random.seed(seed) - return np.array([np.random.random() for _ in range(length)]) * 180 + rng = np.random.default_rng(seed) + return rng.random(length) * 180 @staticmethod def random_electrical(length=10, seed=42): - np.random.seed(seed) - return (np.array([np.random.random() for _ in range(length)]) + 0.5) / 1.5 * 50 + rng = np.random.default_rng(seed) + return (rng.random(length) + 0.5) / 1.5 * 50flixopt/flow_system.py (1)
162-173: PreserveFlowSystem.namewhen round‑tripping via datasets
__init__introduces the optionalnameattribute andto_netcdf/from_netcdfmap it to the filename, butfrom_dataset()does not currently restorenamefromds.attrs(even thoughto_dataset()stores all constructor parameters there). This meansFlowSystem.nameis lost on a dataset round‑trip while being preserved for NetCDF round‑trips.Consider wiring
namethrough when reconstructing:flow_system = cls( timesteps=ds.indexes['time'], periods=ds.indexes.get('period'), scenarios=ds.indexes.get('scenario'), @@ scenario_independent_sizes=reference_structure.get('scenario_independent_sizes', True), scenario_independent_flow_rates=reference_structure.get('scenario_independent_flow_rates', False), + name=reference_structure.get('name'), )Also applies to: 207-224, 229-231, 597-643
flixopt/optimization.py (1)
632-696: SegmentedOptimization deprecation path is clear; future migration can target new APIs
SegmentedOptimizationnow emits a deprecation warning and uses_initialize_optimization_common()for consistent setup. Internally it still constructs sub‑optimizations viaOptimization(...)andflow_system.sel(...), both of which are now deprecated but explicitly marked as such.For a future refactor (not required for this PR), consider:
- switching
flow_system.sel(...)toflow_system.transform.sel(...), and- eventually re‑implementing segmented solving on top of
FlowSystem.build_model()/solve()directly.Also applies to: 696-703
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (85)
-
.github/workflows/docs.yaml(1 hunks) -
.github/workflows/release.yaml(2 hunks) -
.github/workflows/tests.yaml(1 hunks) -
.gitignore(1 hunks) -
CHANGELOG.md(2 hunks) -
README.md(1 hunks) -
bot-test.txt(0 hunks) -
docs/getting-started.md(0 hunks) -
docs/home/citing.md(1 hunks) -
docs/home/installation.md(1 hunks) -
docs/home/license.md(1 hunks) -
docs/home/quick-start.md(1 hunks) -
docs/home/users.md(1 hunks) -
docs/index.md(4 hunks) -
docs/javascripts/plotly-instant.js(1 hunks) -
docs/roadmap.md(1 hunks) -
docs/stylesheets/extra.css(3 hunks) -
docs/user-guide/building-models/index.md(1 hunks) -
docs/user-guide/core-concepts.md(1 hunks) -
docs/user-guide/faq.md(1 hunks) -
docs/user-guide/index.md(1 hunks) -
docs/user-guide/mathematical-notation/dimensions.md(0 hunks) -
docs/user-guide/mathematical-notation/effects-and-dimensions.md(1 hunks) -
docs/user-guide/mathematical-notation/effects-penalty-objective.md(0 hunks) -
docs/user-guide/mathematical-notation/elements/Bus.md(1 hunks) -
docs/user-guide/mathematical-notation/elements/Flow.md(1 hunks) -
docs/user-guide/mathematical-notation/elements/LinearConverter.md(1 hunks) -
docs/user-guide/mathematical-notation/elements/Storage.md(1 hunks) -
docs/user-guide/mathematical-notation/features/InvestParameters.md(1 hunks) -
docs/user-guide/mathematical-notation/features/OnOffParameters.md(0 hunks) -
docs/user-guide/mathematical-notation/features/Piecewise.md(1 hunks) -
docs/user-guide/mathematical-notation/features/StatusParameters.md(1 hunks) -
docs/user-guide/mathematical-notation/index.md(1 hunks) -
docs/user-guide/mathematical-notation/modeling-patterns/bounds-and-states.md(0 hunks) -
docs/user-guide/mathematical-notation/modeling-patterns/duration-tracking.md(0 hunks) -
docs/user-guide/mathematical-notation/modeling-patterns/index.md(0 hunks) -
docs/user-guide/mathematical-notation/modeling-patterns/state-transitions.md(0 hunks) -
docs/user-guide/mathematical-notation/others.md(0 hunks) -
docs/user-guide/migration-guide-v6.md(1 hunks) -
docs/user-guide/optimization/index.md(1 hunks) -
docs/user-guide/recipes/index.md(3 hunks) -
docs/user-guide/recipes/plotting-custom-data.md(1 hunks) -
docs/user-guide/results-plotting.md(1 hunks) -
docs/user-guide/results/index.md(1 hunks) -
docs/user-guide/support.md(1 hunks) -
docs/user-guide/troubleshooting.md(1 hunks) -
examples/00_Minmal/minimal_example.py(1 hunks) -
examples/01_Simple/simple_example.py(2 hunks) -
examples/02_Complex/complex_example.py(7 hunks) -
examples/02_Complex/complex_example_results.py(1 hunks) -
examples/03_Optimization_modes/example_optimization_modes.py(6 hunks) -
examples/04_Scenarios/scenario_example.py(4 hunks) -
examples/05_Two-stage-optimization/two_stage_optimization.py(4 hunks) -
flixopt/__init__.py(3 hunks) -
flixopt/calculation.py(0 hunks) -
flixopt/carrier.py(1 hunks) -
flixopt/clustering.py(4 hunks) -
flixopt/color_processing.py(1 hunks) -
flixopt/components.py(18 hunks) -
flixopt/config.py(8 hunks) -
flixopt/core.py(1 hunks) -
flixopt/effects.py(5 hunks) -
flixopt/elements.py(29 hunks) -
flixopt/features.py(4 hunks) -
flixopt/flow_system.py(20 hunks) -
flixopt/interface.py(12 hunks) -
flixopt/linear_converters.py(25 hunks) -
flixopt/modeling.py(16 hunks) -
flixopt/optimization.py(7 hunks) -
flixopt/optimize_accessor.py(1 hunks) -
flixopt/plotting.py(2 hunks) -
flixopt/results.py(11 hunks) -
flixopt/statistics_accessor.py(1 hunks) -
flixopt/structure.py(12 hunks) -
flixopt/topology_accessor.py(1 hunks) -
flixopt/transform_accessor.py(1 hunks) -
mkdocs.yml(5 hunks) -
pyproject.toml(3 hunks) -
scripts/extract_changelog.py(0 hunks) -
scripts/format_changelog.py(1 hunks) -
scripts/gen_ref_pages.py(1 hunks) -
tests/conftest.py(10 hunks) -
tests/deprecated/__init__.py(1 hunks) -
tests/deprecated/conftest.py(1 hunks) -
tests/deprecated/test_bus.py(1 hunks)
💤 Files with no reviewable changes (12)
- docs/user-guide/mathematical-notation/modeling-patterns/bounds-and-states.md
- bot-test.txt
- docs/user-guide/mathematical-notation/dimensions.md
- docs/user-guide/mathematical-notation/modeling-patterns/duration-tracking.md
- docs/user-guide/mathematical-notation/modeling-patterns/state-transitions.md
- docs/getting-started.md
- docs/user-guide/mathematical-notation/others.md
- docs/user-guide/mathematical-notation/effects-penalty-objective.md
- docs/user-guide/mathematical-notation/features/OnOffParameters.md
- docs/user-guide/mathematical-notation/modeling-patterns/index.md
- flixopt/calculation.py
- scripts/extract_changelog.py
🧰 Additional context used
🧬 Code graph analysis (13)
flixopt/linear_converters.py (1)
flixopt/interface.py (1)
StatusParameters(1014-1265)
flixopt/topology_accessor.py (3)
flixopt/color_processing.py (1)
process_colors(112-180)flixopt/flow_system.py (2)
FlowSystem(47-2028)flows(1636-1642)flixopt/network_app.py (1)
flow_graph(117-180)
examples/02_Complex/complex_example.py (5)
flixopt/flow_system.py (2)
topology(1387-1413)to_netcdf(686-711)flixopt/elements.py (1)
Bus(185-305)flixopt/linear_converters.py (7)
Boiler(24-111)thermal_efficiency(105-106)thermal_efficiency(109-111)thermal_efficiency(201-202)thermal_efficiency(205-207)thermal_efficiency(512-513)thermal_efficiency(516-518)flixopt/carrier.py (1)
label(107-109)flixopt/topology_accessor.py (2)
start_app(378-421)plot(183-317)
examples/03_Optimization_modes/example_optimization_modes.py (6)
flixopt/results.py (2)
solution_without_overlap(2207-2220)flow_system(338-357)flixopt/structure.py (3)
flow_system(386-404)solution(168-206)solution(1084-1098)flixopt/flow_system.py (3)
solution(1201-1203)solution(1206-1209)topology(1387-1413)flixopt/elements.py (1)
Bus(185-305)flixopt/interface.py (1)
StatusParameters(1014-1265)flixopt/topology_accessor.py (1)
plot(183-317)
flixopt/config.py (1)
flixopt/carrier.py (1)
Carrier(14-125)
flixopt/carrier.py (2)
flixopt/structure.py (7)
ContainerMixin(1153-1303)Interface(304-1038)register_class_for_io(43-52)transform_data(322-335)_get_label(1188-1198)_get_label(1313-1315)_get_label(1325-1327)flixopt/flow_system.py (1)
carriers(1080-1082)
flixopt/results.py (2)
flixopt/structure.py (4)
variables(1649-1655)constraints(1640-1646)solution(168-206)solution(1084-1098)flixopt/flow_system.py (2)
solution(1201-1203)solution(1206-1209)
flixopt/__init__.py (4)
flixopt/carrier.py (2)
Carrier(14-125)CarrierContainer(128-159)flixopt/clustering.py (1)
ClusteringParameters(246-299)flixopt/flow_system.py (1)
FlowSystem(47-2028)flixopt/interface.py (1)
StatusParameters(1014-1265)
tests/conftest.py (2)
flixopt/interface.py (1)
StatusParameters(1014-1265)flixopt/flow_system.py (1)
build_model(1099-1130)
flixopt/features.py (2)
flixopt/interface.py (5)
InvestParameters(693-1010)StatusParameters(1014-1265)use_downtime_tracking(1249-1251)use_startup_tracking(1254-1265)use_uptime_tracking(1244-1246)flixopt/elements.py (3)
status(845-849)previous_status(864-877)previous_status(1024-1041)
flixopt/structure.py (1)
flixopt/flow_system.py (2)
FlowSystem(47-2028)_create_reference_structure(510-548)
flixopt/plotting.py (3)
flixopt/color_processing.py (1)
process_colors(112-180)flixopt/results.py (2)
colors(2147-2148)colors(2151-2155)flixopt/config.py (2)
CONFIG(181-854)Plotting(547-576)
flixopt/statistics_accessor.py (3)
flixopt/color_processing.py (1)
process_colors(112-180)flixopt/topology_accessor.py (1)
plot(183-317)flixopt/effects.py (1)
calculate_effect_share_factors(612-636)
🪛 GitHub Actions: Docs
docs/home/quick-start.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/javascripts/plotly-instant.js
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/faq.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/home/citing.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/troubleshooting.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/results-plotting.md
[warning] 1-1: mkdocs_autorefs: Could not find cross-reference target 'flixopt.plot_accessors.PlotResult'
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/roadmap.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/building-models/index.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/home/installation.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/support.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/home/users.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/optimization/index.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/index.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/home/license.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/stylesheets/extra.css
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/results/index.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/elements/LinearConverter.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/recipes/plotting-custom-data.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/effects-and-dimensions.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/core-concepts.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/migration-guide-v6.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
.github/workflows/docs.yaml
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
mkdocs.yml
[error] 1-1: mkdocs build --strict failed. Aborted with 3 warnings in strict mode. Command: mkdocs build --strict. Warnings: mkdocstrings formatting signatures requires either Black or Ruff to be installed; griffe: No type or annotation for parameter 'carrier' and it does not appear in the function signature; mkdocs_autorefs: Could not find cross-reference target 'flixopt.plot_accessors.PlotResult'.
docs/user-guide/recipes/index.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/elements/Storage.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/features/StatusParameters.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/elements/Flow.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/elements/Bus.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/features/InvestParameters.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/index.md
[warning] 1-1: Doc file 'index.md' contains unrecognized relative links; left as is.
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
docs/user-guide/mathematical-notation/features/Piecewise.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
flixopt/flow_system.py
[warning] 1003-1003: No type or annotation for parameter 'carrier'. Parameter 'carrier' does not appear in the function signature.
docs/user-guide/index.md
[warning] 1-1: Formatting/signature tooling requires Black or Ruff to be installed (mkdocstrings/formatting step not fully available).
🪛 LanguageTool
docs/user-guide/results-plotting.md
[style] ~3-~3: Consider a different adjective to strengthen your wording.
Context: ...ss to common plots while still allowing deep customization. ## The Plot Accessor A...
(DEEP_PROFOUND)
docs/user-guide/building-models/index.md
[style] ~2-~2: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 664 characters long)
Context: # Building Models !!! note "Under Development" This secti...
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/results/index.md
[style] ~188-~188: Using many exclamation marks might seem excessive (in this case: 6 exclamation marks for a text that’s 2701 characters long)
Context: ...ne flow_system.topology.stop_app() ``` !!! note "Optional Dependencies" The in...
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/core-concepts.md
[style] ~17-~17: Consider an alternative for the overused word “exactly”.
Context: ...hile meeting all heat demands. This is exactly the kind of problem flixOpt solves. Let...
(EXACTLY_PRECISELY)
[style] ~98-~98: Using many exclamation marks might seem excessive (in this case: 18 exclamation marks for a text that’s 7150 characters long)
Context: ...imize), others can be constraints. !!! example "In our heating system" - *...
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/migration-guide-v6.md
[style] ~398-~398: Using many exclamation marks might seem excessive (in this case: 16 exclamation marks for a text that’s 6313 characters long)
Context: ... success "Welcome to the new flixopt API! 🎉"
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/mathematical-notation/elements/Flow.md
[style] ~18-~18: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 1352 characters long)
Context: ...e_minimum=0.3) # → 30 ≤ p(t) ≤ 100 ``` !!! warning "Cannot be zero" With `rela...
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/mathematical-notation/elements/Bus.md
[style] ~59-~59: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 1616 characters long)
Context: ...e5 # High penalty for imbalance ) ``` !!! tip "Debugging" If you see a `virtu...
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/mathematical-notation/features/Piecewise.md
[style] ~29-~29: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 2049 characters long)
Context: ...ve (
(EN_EXCESSIVE_EXCLAMATION)
🪛 markdownlint-cli2 (0.18.1)
docs/home/citing.md
15-15: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
docs/home/license.md
7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
docs/user-guide/results/index.md
116-116: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
docs/user-guide/mathematical-notation/elements/LinearConverter.md
62-62: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
75-75: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
docs/user-guide/mathematical-notation/effects-and-dimensions.md
22-22: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
33-33: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
43-43: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
51-51: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
59-59: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
68-68: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
78-78: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
93-93: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
105-105: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
119-119: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
138-138: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
144-144: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
152-152: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
160-160: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
172-172: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
182-182: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
191-191: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
201-201: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
209-209: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
217-217: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
225-225: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
233-233: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
273-273: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
282-282: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
292-292: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
309-309: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
327-327: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
343-343: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
358-358: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
379-379: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
docs/user-guide/migration-guide-v6.md
165-165: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
202-202: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
255-255: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
303-303: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
docs/user-guide/mathematical-notation/elements/Storage.md
44-44: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
50-50: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
58-58: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
71-71: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
79-79: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
93-93: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
docs/user-guide/mathematical-notation/elements/Flow.md
28-28: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
49-49: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
68-68: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
82-82: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
99-99: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
109-109: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
docs/user-guide/mathematical-notation/features/InvestParameters.md
30-30: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
42-42: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
53-53: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
70-70: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
82-82: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
94-94: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
106-106: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
⏰ 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.12)
- GitHub Check: test (3.10)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
| ### 2. Define your time horizon | ||
|
|
||
| ```python | ||
| # 24h period with hourly timesteps | ||
| timesteps = pd.date_range('2024-01-01', periods=24, freq='h') | ||
| ``` | ||
|
|
||
| ### 2. Set Up the Flow System | ||
|
|
||
| ```python | ||
| # Create the flow system | ||
| flow_system = fx.FlowSystem(timesteps) | ||
|
|
||
| # Define an effect to minimize (costs) | ||
| costs = fx.Effect('costs', 'EUR', 'Minimize total system costs', is_objective=True) | ||
| flow_system.add_elements(costs) | ||
| ``` | ||
|
|
||
| ### 4. Add Components |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix step numbering inconsistency.
Steps are incorrectly numbered with two "### 2." sections and missing "### 3.":
- Line 25: "### 2. Define your time horizon"
- Line 32: "### 2. Set Up the Flow System" (should be "### 3.")
- Line 43: "### 4. Add Components" (should stay as-is after fixing above)
### 2. Define your time horizon
```python
# 24h period with hourly timesteps
timesteps = pd.date_range('2024-01-01', periods=24, freq='h')-### 2. Set Up the Flow System
+### 3. Set Up the Flow System
<details>
<summary>🤖 Prompt for AI Agents</summary>
In docs/home/quick-start.md around lines 25 to 43, the step headings are
misnumbered—there are two "### 2." headings and no "### 3."; update the second
"### 2. Set Up the Flow System" to "### 3. Set Up the Flow System" so the
sequence reads "### 2. Define your time horizon", "### 3. Set Up the Flow
System", then "### 4. Add Components"; ensure no other headings are affected and
keep surrounding content unchanged.
</details>
<!-- fingerprinting:phantom:medusa:ocelot -->
<!-- This is an auto-generated comment by CodeRabbit -->
| Default: equal weights, normalized to sum to 1. | ||
|
|
||
| === "Period Weights" | ||
|
|
||
| Computed automatically from period index (interval sizes): | ||
|
|
||
| ```python | ||
| periods = pd.Index([2020, 2025, 2030]) | ||
| # → weights: [5, 5, 5] (5-year intervals) | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify status variable naming (CHP|on vs Component(Flow)|status).
Here you use model.variables['CHP|on'] for a binary on/off variable, while the migration guide’s naming table describes status variables as Component(Flow)|status / startup / shutdown. If the v6 API still exposes CHP|on, it’d be good to mention that explicitly in the naming-convention docs; if not, this example should be updated (e.g., to something like Boiler(Q_th)|status or whatever the canonical status variable is now).
Right now the discrepancy is likely to trip up users trying to reproduce this minimum-runtime pattern.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
182-182: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
🤖 Prompt for AI Agents
In docs/user-guide/mathematical-notation/effects-and-dimensions.md around lines
178 to 187, the example uses model.variables['CHP|on'] which conflicts with the
migration guide naming (Component(Flow)|status / startup / shutdown); update the
example to use the current canonical status variable name (e.g.,
Boiler(Q_th)|status or ComponentName(Flow)|status) or explicitly note that v6
still exposes the legacy 'CHP|on' name if that is true. Modify the text so it
either replaces 'CHP|on' with the canonical status variable used in v6 examples,
or adds a short parenthetical clarifying legacy compatibility and pointing
readers to the migration naming table.
| ```python | ||
| # Boiler | ||
| boiler = fx.linear_converters.Boiler( | ||
| label='boiler', eta=0.9, | ||
| Q_th=fx.Flow(label='heat', bus=heat_bus, size=100), | ||
| Q_fu=fx.Flow(label='fuel', bus=gas_bus), | ||
| ) | ||
|
|
||
| # Heat Pump | ||
| hp = fx.linear_converters.HeatPump( | ||
| label='hp', COP=3.5, | ||
| P_el=fx.Flow(label='el', bus=elec_bus, size=100), | ||
| Q_th=fx.Flow(label='heat', bus=heat_bus), | ||
| ) | ||
|
|
||
| # CHP | ||
| chp = fx.linear_converters.CHP( | ||
| label='chp', eta_el=0.35, eta_th=0.50, | ||
| P_el=fx.Flow(...), Q_th=fx.Flow(...), Q_fu=fx.Flow(...), | ||
| ) | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find the linear_converters module
fd -t f "linear_converters.py" | head -20Repository: flixOpt/flixopt
Length of output: 87
🏁 Script executed:
# Also check the example file structure
fd -t f "simple_example.py" | head -20Repository: flixOpt/flixopt
Length of output: 95
🏁 Script executed:
# List files in the docs directory to confirm the file exists
find docs -name "LinearConverter.md" -type fRepository: flixOpt/flixopt
Length of output: 124
🏁 Script executed:
# Get the actual Boiler, CHP, HeatPump class signatures from linear_converters.py
# First check the file size
wc -l flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 91
🏁 Script executed:
# Read the linear_converters.py file to find the class definitions
cat -n flixopt/linear_converters.py | head -150Repository: flixOpt/flixopt
Length of output: 6720
🏁 Script executed:
# Search for Boiler class definition
rg -A 20 "^class Boiler" flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 1080
🏁 Script executed:
# Search for CHP class definition
rg -A 20 "^class CHP" flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 1325
🏁 Script executed:
# Search for HeatPump class definition
rg -A 20 "^class HeatPump" flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 2667
🏁 Script executed:
# Check the simple_example.py to see actual parameter names used
cat -n examples/01_Simple/simple_example.py | grep -A 10 -B 2 "Boiler\|CHP\|HeatPump" | head -60Repository: flixOpt/flixopt
Length of output: 1711
🏁 Script executed:
# Read the exact doc file lines 75-95
sed -n '75,95p' docs/user-guide/mathematical-notation/elements/LinearConverter.mdRepository: flixOpt/flixopt
Length of output: 574
🏁 Script executed:
# Search for any property decorators that might alias the parameter names in linear_converters.py
rg "@property" flixopt/linear_converters.py -A 3Repository: flixOpt/flixopt
Length of output: 870
🏁 Script executed:
# Check HeatPump's __init__ signature more completely
rg -A 15 "class HeatPump\(LinearConverter\):" flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 981
🏁 Script executed:
# Let me check if there's any backward compatibility or alias support by looking at the __init__ method signatures more carefully
rg -A 25 "def __init__" flixopt/linear_converters.py | grep -A 25 "class HeatPump" | head -40Repository: flixOpt/flixopt
Length of output: 41
🏁 Script executed:
# Check HeatPump __init__ full signature
sed -n '300,350p' flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 2369
🏁 Script executed:
# Search for HeatPump class and its init more precisely
rg -B 5 -A 30 "class HeatPump\(" flixopt/linear_converters.pyRepository: flixOpt/flixopt
Length of output: 1733
Update convenience class examples in lines 75–95 to match actual parameter names.
The examples here use outdated parameter names that will cause runtime errors if copied verbatim:
Current doc (incorrect):
Boiler(..., eta=0.9, Q_th=..., Q_fu=...)HeatPump(..., COP=3.5, P_el=..., Q_th=...)CHP(..., eta_el=0.35, eta_th=0.50, P_el=..., Q_th=..., Q_fu=...)
Actual signatures in flixopt/linear_converters.py:
Boiler(thermal_efficiency, fuel_flow, thermal_flow)HeatPump(cop, electrical_flow, thermal_flow)← Note: lowercasecopCHP(thermal_efficiency, electrical_efficiency, fuel_flow, electrical_flow, thermal_flow)
The working example in examples/01_Simple/simple_example.py confirms these are the correct parameter names. No aliases exist for the old short names. Update the three examples to use the actual parameter names as shown in the working example.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
75-75: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
🤖 Prompt for AI Agents
In docs/user-guide/mathematical-notation/elements/LinearConverter.md around
lines 75 to 95, the convenience class examples use outdated parameter names
(eta, COP, Q_th, Q_fu, P_el) that don't match the actual signatures; update the
Boiler, HeatPump, and CHP examples to call the constructors with the real
parameter names: Boiler(thermal_efficiency, fuel_flow, thermal_flow),
HeatPump(cop, electrical_flow, thermal_flow) using lowercase 'cop', and
CHP(thermal_efficiency, electrical_efficiency, fuel_flow, electrical_flow,
thermal_flow), preserving the same Flow(...) arguments but passing them in the
correct order/with correct parameter names to match flixopt/linear_converters.py
and the examples/01_Simple/simple_example.py.
| def heatmap_with_plotly_v2( | ||
| data: xr.DataArray, | ||
| colors: ColorType | None = None, | ||
| title: str = '', | ||
| facet_col: str | None = None, | ||
| animation_frame: str | None = None, | ||
| facet_col_wrap: int | None = None, | ||
| **imshow_kwargs: Any, | ||
| ) -> go.Figure: | ||
| """ | ||
| Plot a heatmap using Plotly's imshow. | ||
| Data should be prepared with dims in order: (y_axis, x_axis, [facet_col], [animation_frame]). | ||
| Use reshape_data_for_heatmap() to prepare time-series data before calling this. | ||
| Args: | ||
| data: DataArray with 2-4 dimensions. First two are heatmap axes. | ||
| colors: Colorscale name ('viridis', 'plasma', etc.). | ||
| title: Plot title. | ||
| facet_col: Dimension name for subplot columns (3rd dim). | ||
| animation_frame: Dimension name for animation (4th dim). | ||
| facet_col_wrap: Max columns before wrapping (only if < n_facets). | ||
| **imshow_kwargs: Additional args for px.imshow. | ||
| Returns: | ||
| Plotly Figure object. | ||
| """ | ||
| if data.size == 0: | ||
| return go.Figure() | ||
|
|
||
| colors = colors or CONFIG.Plotting.default_sequential_colorscale | ||
| facet_col_wrap = facet_col_wrap or CONFIG.Plotting.default_facet_cols | ||
|
|
||
| imshow_args: dict[str, Any] = { | ||
| 'img': data, | ||
| 'color_continuous_scale': colors, | ||
| 'title': title, | ||
| **imshow_kwargs, | ||
| } | ||
|
|
||
| if facet_col and facet_col in data.dims: | ||
| imshow_args['facet_col'] = facet_col | ||
| if facet_col_wrap < data.sizes[facet_col]: | ||
| imshow_args['facet_col_wrap'] = facet_col_wrap | ||
|
|
||
| if animation_frame and animation_frame in data.dims: | ||
| imshow_args['animation_frame'] = animation_frame | ||
|
|
||
| return px.imshow(**imshow_args) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check all references to heatmap_with_plotly_v2 in the codebase
echo "=== All references to heatmap_with_plotly_v2 ==="
rg -n "heatmap_with_plotly_v2" --type=py
echo ""
echo "=== Check if it's in __all__ or exported ==="
rg -n "__all__" flixopt/plotting.py
echo ""
echo "=== Check __init__.py for exports ==="
find . -name "__init__.py" -path "*/flixopt/*" | head -5 | xargs -I {} sh -c "echo '--- {} ---' && cat {}"Repository: flixOpt/flixopt
Length of output: 3867
🏁 Script executed:
#!/bin/bash
# Check for heatmap_with_plotly function
echo "=== heatmap_with_plotly function (non-v2) ==="
rg -n "^def heatmap_with_plotly\b" --type=py -A 20
echo ""
echo "=== Check for other _v2 functions in plotting.py ==="
rg -n "def \w+_v2\(" flixopt/plotting.py
echo ""
echo "=== Check for _v2 pattern in entire codebase ==="
rg -n "_v2" --type=py | head -20Repository: flixOpt/flixopt
Length of output: 2155
🏁 Script executed:
#!/bin/bash
# Get the complete test for heatmap_with_plotly_v2
echo "=== Complete test_heatmap_with_plotly_v2 ==="
sed -n '405,420p' tests/test_solution_and_plotting.py
echo ""
echo "=== Check reshape_data_for_heatmap function ==="
rg -n "^def reshape_data_for_heatmap" --type=py -A 15
echo ""
echo "=== Check if heatmap_with_plotly_v2 is documented in docstrings or comments ==="
rg -B 5 "heatmap_with_plotly_v2" flixopt/plotting.py | head -30Repository: flixOpt/flixopt
Length of output: 2113
Clarify the purpose and naming of heatmap_with_plotly_v2.
This function provides a low-level heatmap interface for pre-processed data, but the naming and documentation lack clarity:
-
Non-standard naming:
_v2is not a naming pattern used elsewhere in the project. The function is not a versioned replacement but rather a complementary lower-level utility distinct fromheatmap_with_plotly. -
Unclear relationship to main API: The docstring mentions that data should be prepared with
reshape_data_for_heatmap(), but doesn't explain when to use this function versusheatmap_with_plotly, which handles reshaping internally. -
Consider a more descriptive name: Options like
heatmap_imshoworheatmap_plotly_imshowwould better communicate that this is a simplified, direct wrapper aroundpx.imshowfor already-shaped data, making the API more self-documenting.
flixopt/statistics_accessor.py
Outdated
|
|
||
| for flow in self._fs.flows.values(): | ||
| # Get bus label (could be string or Bus object) | ||
| bus_label = flow.bus | ||
| comp_label = flow.component.label_full | ||
|
|
||
| # start/end filtering based on flow direction | ||
| if flow.is_input_in_component: | ||
| # Flow goes: bus -> component, so start=bus, end=component | ||
| if starts and bus_label not in starts: | ||
| continue | ||
| if ends and comp_label not in ends: | ||
| continue | ||
| else: | ||
| # Flow goes: component -> bus, so start=component, end=bus | ||
| if starts and comp_label not in starts: | ||
| continue | ||
| if ends and bus_label not in ends: | ||
| continue | ||
|
|
||
| if components and comp_label not in components: | ||
| continue | ||
| matching_labels.append(flow.label_full) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find the Flow class definition
rg -n "class Flow" --type py -A 10Repository: flixOpt/flixopt
Length of output: 4402
🏁 Script executed:
# Check the specific lines mentioned in the comment (1279-1287)
sed -n '1275,1290p' flixopt/statistics_accessor.pyRepository: flixOpt/flixopt
Length of output: 557
🏁 Script executed:
# Also check lines around 1056-1078 to see the full context
sed -n '1050,1085p' flixopt/statistics_accessor.pyRepository: flixOpt/flixopt
Length of output: 1661
🏁 Script executed:
# Search for how flow.component is used throughout the file
rg -n "flow\.component" flixopt/statistics_accessor.py -B 2 -A 2Repository: flixOpt/flixopt
Length of output: 579
🏁 Script executed:
# Find the Flow class and check its component attribute
sed -n '322,500p' flixopt/elements.py | grep -n -A 2 -B 2 "component"Repository: flixOpt/flixopt
Length of output: 983
🏁 Script executed:
# Search for component property definition in Flow class
rg -n "@property" flixopt/elements.py | head -20Repository: flixOpt/flixopt
Length of output: 311
🏁 Script executed:
# Look at Flow class __init__ to see component definition
sed -n '322,600p' flixopt/elements.pyRepository: flixOpt/flixopt
Length of output: 13507
Remove .label_full from flow.component at line 1060.
In the Flow class, component is defined as a string attribute (self.component: str = 'UnknownComponent'), not a Component object. Line 1060 incorrectly calls .label_full on it (comp_label = flow.component.label_full), which will fail since strings don't have that method. Line 1280 correctly treats it as a string (comp_label = flow.component). Change line 1060 to comp_label = flow.component to match the actual type.
🤖 Prompt for AI Agents
In flixopt/statistics_accessor.py around lines 1056 to 1078, comp_label is
incorrectly set using flow.component.label_full even though Flow.component is a
string; change it to comp_label = flow.component (remove .label_full), keep the
rest of the start/end and components checks the same so they compare strings
consistently, and ensure matching_labels.append(flow.label_full) remains
unchanged.
| if not self._fs.connected_and_transformed: | ||
| self._fs.connect_and_transform() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent access to connected_and_transformed state.
Line 159 and 227 use the public property self._fs.connected_and_transformed, while line 414 uses the private attribute self._fs._connected_and_transformed. Additionally, line 415 calls _connect_network() instead of connect_and_transform().
This inconsistency could cause subtle bugs if the public property has additional logic. Consider using the public API consistently:
- if not self._fs._connected_and_transformed:
- self._fs._connect_network()
+ if not self._fs.connected_and_transformed:
+ self._fs.connect_and_transform()Also applies to: 227-228, 414-415
🤖 Prompt for AI Agents
In flixopt/topology_accessor.py around lines 159-160, 227-228 and 414-415, the
code inconsistently reads the FS state via the private attribute
_connected_and_transformed and calls the private method _connect_network();
replace all uses of self._fs._connected_and_transformed with the public property
self._fs.connected_and_transformed and replace calls to
self._fs._connect_network() with the public API
self._fs.connect_and_transform(), ensuring the public property is checked before
calling the public method to preserve any encapsulation or side effects.
* Reverted the time index handling to keep the original solution with charge_state including the extra timestep. All other variables now have NaN on the final timestep
instead of being truncated.
Changes Made:
1. flixopt/structure.py:191-206 - Removed the code that:
- Extracted final charge states into separate |final variables
- Reindexed the solution to regular timesteps
2. tests/test_solution_and_plotting.py:78-80 - Removed the assertion expecting the Speicher|charge_state|final variable
3. tests/test_integration.py:168-178 - Updated the charge_state expectation to include the extra timestep value (10.0 at the end)
4. tests/conftest.py:695-718 and tests/deprecated/conftest.py:694-717 - Updated the assert_almost_equal_numeric function to handle the extra timestep by trimming
arrays when the extra values are NaN (for non-charge_state variables)
Result:
- charge_state variables now include len(timesteps) + 1 values, with the final value being the actual final charge state
- All other time-indexed variables have len(timesteps) + 1 values with NaN at the final timestep (due to xarray coordinate merging)
- All 1329 tests pass
* Verify that time index always has extra timestep in solution!
* fixup test
* Update new tests for nan as last timestep
* Update some comments and add warnings in tests for nan handling
* Update docstring
* bugfix: revert
Improve example notebooks Fix resampling Re-Connect FLow_system when loading from dataset
* Change default size to None. Raise if not set and Status is used * Make maximum size mandatory (or fixed size). Update tests accordingly * Adding maximum sizes for FLows which get Status variables (also if only through component status * Update more tests * BUGFIX: Minimum or foixed size in storages * Added validation: 5. Component with status_parameters now validates that all flows have explicit sizes * In flixopt/elements.py - Flow._plausibility_checks(): - relative_minimum > 0 requires size (lines 585-590) - relative_maximum < 1 requires size (lines 592-596) - load_factor_min requires size (lines 598-603) - load_factor_max requires size (lines 605-609) * Added more validations * Fix test * Fix tests to specify size if needed * Improve check verbosity * Fix type hint * Improve conftest.py * Added more notebooks * Improve notebooks * Improve plotting of Piecewises * Improve notebooks * Streamline notebook * Remove old notebook * fix notebook * fix notebook script * Extract PlotResults class into separate module and reuse * Improve notebook * Add carrier units and effect units to accessors * Add carrier units and effect units to notebooks * Add carrier units and effect units to notebooks * Decrease notebook font size * Fix tests * Fix docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 17
♻️ Duplicate comments (3)
docs/user-guide/index.md (1)
68-68: API Reference link issue already flagged.This broken link to
../api-reference/index.mdwas identified in a previous review. See the past review comments for details and proposed fixes.docs/user-guide/migration-guide-v5.md (1)
23-27: Inconsistent deprecation status between sections.Line 27 states the
Optimizationclass "is removed", but the deprecation timeline at lines 415-416 shows bothOptimizationandResultsas "deprecated" in v5.0.0, and the warning box at lines 418-422 says they "will be removed in a future version."This creates confusion: is
Optimizationremoved or deprecated in v5.0.0?Suggested fix - align the messaging:
-The `Optimization` class is removed. Use `FlowSystem.optimize()` directly. +The `Optimization` class is **deprecated** and will be removed in v6.0.0. Use `FlowSystem.optimize()` directly.Or alternatively, update the timeline table to reflect that it's removed:
| v4.x | `Optimization` and `Results` classes available | -| v5.0.0 | `Optimization` and `Results` deprecated, new API available | +| v5.0.0 | `Optimization` removed, `Results` deprecated, new API available |flixopt/flow_system.py (1)
1134-1134: Example in docstring uses incorrect method name.The example calls
fs.add_carrier(biogas)but the method isadd_carriers. This was already flagged in a past review.- fs.add_carrier(biogas) + fs.add_carriers(biogas)
🧹 Nitpick comments (21)
docs/stylesheets/extra.css (3)
508-518: Conflicting guidance: comment says “remove this rule” but rule is still applied (hero-section).
Line 509-510 says to remove a rule due to TOC conflict, yet.hero-sectionis still being constrained/centered (Line 511-518). Either delete the obsolete comment or actually gate/disable the conflicting rule so future edits don’t “fix” the wrong thing.-/* Center home page content when navigation is hidden */ -/* Remove this rule as it conflicts with TOC layout */ +/* Home page hero-section: centered and width-constrained */
766-784:color-swatchutility: consider sizing/contrast and inline alignment edge cases.
1em × 1em+ border/shadow is fine, but if swatches are used next to small text (you set several areas to ~0.55–0.75rem), it can become tiny; considermin-width/min-heightorinline-flex+vertical-align: text-bottomfor better baseline alignment.
952-973: Use a more specific selector to target only social icons in the footer.The current rule hides
.md-footer-metaentirely. In this project's Material theme setup, this container holds only the social media links (GitHub, PyPI) fromextra.social. Using a more specific selector like.md-footer-meta__inner .md-socialwould clarify the intent and provide better specificity if footer metadata is ever needed elsewhere.-.md-footer-meta { - display: none; -} +/* Hide only the social media icons in footer */ +.md-footer-meta__inner .md-social { + display: none; +}flixopt/color_processing.py (1)
18-67: TheColorType“docstring” is a no-op string literal; consider moving it into the module docstring or using Sphinx-style variable docs.
As written, the triple-quoted block afterColorType = ...is just an unassigned string statement; many linters treat this as dead code, and doc tooling may not associate it withColorType.@@ -# Type alias for flexible color input -ColorType = str | list[str] | dict[str, str] -"""Flexible color specification type supporting multiple input formats for visualization. +"""Simplified color handling for visualization. + +ColorType +--------- +Flexible color specification type supporting multiple input formats for visualization. @@ -""" +""" + +# Type alias for flexible color input +ColorType = str | list[str] | dict[str, str].github/workflows/docs.yaml (1)
3-23: PR docs build trigger likely too narrow (misses flixopt/ changes on PRs).**
Onpull_requestyou only trigger fordocs/**andmkdocs.yml. If API/docstrings are pulled fromflixopt/**(or notebooks import code), doc builds can break without CI catching it.docs/notebooks/05-multi-carrier-system.ipynb (1)
167-173: Brittle result access via hard-coded labels ('CHP(P_el)','GridBuy(Electricity)').
This will break easily if labeling/formatting changes. Prefer programmatic discovery (e.g., by element/flow objects) if the library supports it.Also applies to: 281-310
docs/user-guide/building-models/choosing-components.md (1)
39-44: Snippets omit required surrounding context (Effects / FlowSystem) — consider adding minimal “full example” once.
Many snippets useeffects_per_flow_hourwithout showing effect creation and objective selection. A single short “complete runnable” example near the top would reduce confusion.Also applies to: 66-78, 120-150
docs/notebooks/10-transmission.ipynb (1)
248-271: Avoid relying on stringly-typedsolution[...]keys for flows/sizes.
'Pipe_AB(from_A)|flow_rate'/'...|size'is brittle. If there’s a supported accessor viastatisticsor element/flow references, that will survive renames.Also applies to: 317-337
flixopt/io.py (1)
732-828: Add tests for legacy dataset conversion, specifically for conversion_factors and piecewises structures.Tests exist for basic conversion and nested attrs with
__class__renames and StatusParameters field renames. However, there's no explicit test coverage for the behavior that flow labels insideconversion_factorsandpiecewisesstructures should NOT be renamed (e.g.,Q_fu,P_elkeys should remain unchanged inside these structures). The implementation correctly handles this viaskip_flow_renames, but add a test case to verify this behavior doesn't regress.docs/notebooks/06a-time-varying-parameters.ipynb (1)
196-196: Consider splitting large code cell for readability.The code cell at line 196 contains the entire FlowSystem construction and optimization in a single cell (carriers, buses, effects, components, and optimization call). For educational notebooks, consider splitting this into:
- FlowSystem and carrier setup
- Adding buses and effects
- Adding components (Source, Converter, Sink)
- Running optimization
This would make it easier for learners to understand each step and would allow for intermediate inspection points.
Example structure:
# Cell 1: Create FlowSystem and carriers flow_system = fx.FlowSystem(timesteps) flow_system.add_carriers( fx.Carrier('electricity', '#f1c40f', 'kW'), fx.Carrier('heat', '#e74c3c', 'kW'), ) # Cell 2: Add buses and effects flow_system.add_elements( fx.Bus('Electricity', carrier='electricity'), fx.Bus('Heat', carrier='heat'), fx.Effect('costs', '€', 'Operating Costs', is_standard=True, is_objective=True), ) # Cell 3: Add components flow_system.add_elements( fx.Source('Grid', ...), fx.LinearConverter('HeatPump', ...), # Key: time-varying COP here fx.Sink('Building', ...), ) # Cell 4: Optimize flow_system.optimize(fx.solvers.HighsSolver())docs/user-guide/results-plotting.md (1)
350-354: Wording/link mismatch: “Carriers” links toCarrier.
Either change the visible text to singular, or link to the container type if that’s what you intend.-[`Carriers`][flixopt.carrier.Carrier] define energy or material types with associated colors. Built-in carriers are available in `CONFIG.Carriers`: +[`Carrier`][flixopt.carrier.Carrier] defines an energy or material type with an associated color. Built-in carriers are available in `CONFIG.Carriers`:docs/notebooks/03-investment-optimization.ipynb (1)
70-96: Clarifyfixed_relative_profilesemantics (relative vs absolute) in the narrative.
Right now the notebook feeds kW-ish absolute values as a “relative profile” (withsize=1), which can confuse readers new to flixOpt. A short note like “we usesize=1so the profile is interpreted in kW for this example” would help.Also applies to: 169-175
docs/notebooks/02-heat-system.ipynb (1)
45-78: Implicit unit assumption:sum(kW)⇒kWhonly because timestep is hourly.
Given this is a teaching notebook, consider a one-liner noting “hourly resolution” is whyheat_demand.sum()is energy.Also applies to: 253-265
docs/notebooks/01-quickstart.ipynb (1)
106-132: Optional: add carriers (or a note) to align with later color/plotting docs.
Since later guides emphasize carrier-based colors, quickstart could either setfx.Bus(..., carrier=...)or explicitly say it’s omitted for minimalism.docs/user-guide/core-concepts.md (1)
249-265: Inconsistent API usage:solve()vsoptimize().The advanced section uses
flow_system.solve()on line 264, but earlier examples (line 144) useflow_system.optimize(). The migration guide also showsoptimize()as the primary method. This inconsistency could confuse users.Based on the pattern shown earlier in this file and in the migration guide, the two-step workflow should use
solve()afterbuild_model(), which appears correct. However, clarify thatoptimize()=build_model()+solve()for users who may be confused.Consider adding a brief comment to clarify the relationship:
# Build the model (without solving) flow_system.build_model() # Access the linopy model model = flow_system.model # Add custom constraints using linopy API model.add_constraints(...) -# Then solve +# Then solve (note: optimize() = build_model() + solve()) flow_system.solve(fx.solvers.HighsSolver())flixopt/components.py (2)
519-546: Review capacity-related validation logic for clarity.The validation logic at lines 519-546 skips capacity checks when
capacity_in_flow_hours is None, then performs complex checks for investment-related scenarios. Consider the following:
Lines 531-545: The validation for
initial_charge_stateconstraining investment decisions is complex. The error messages are helpful, but the logic could benefit from inline comments explaining the constraint derivation.Potential edge case: When
capacity_in_flow_hoursisNonebutinitial_charge_stateis numeric and non-zero, should that be flagged as inconsistent? Currently, this is allowed.Consider adding clarifying comments:
# Skip capacity-related checks if capacity is None (unbounded) if self.capacity_in_flow_hours is not None: # Use new InvestParameters methods to get capacity bounds if isinstance(self.capacity_in_flow_hours, InvestParameters): minimum_capacity = self.capacity_in_flow_hours.minimum_or_fixed_size maximum_capacity = self.capacity_in_flow_hours.maximum_or_fixed_size else: maximum_capacity = self.capacity_in_flow_hours minimum_capacity = self.capacity_in_flow_hours + # Initial charge state should not constrain investment decision. + # The initial value must be feasible across the entire investment range: + # - If initial > (min_cap * rel_max), the optimizer is forced to increase capacity + # - If initial < (max_cap * rel_min), the optimizer is forced to decrease capacity min_initial_at_max_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=0) max_initial_at_min_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=0)
763-767: Review dynamic StatusParameters creation for absolute losses.When
absolute_lossesis non-zero, StatusParameters are dynamically created and linked for flows that don't already have them. This automatic behavior might be unexpected to users.Consider whether this should be documented more prominently in the Transmission docstring, as it introduces binary variables that affect computational performance.
Add a note in the Transmission docstring (around line 679) explicitly stating:
- Absolute losses force the creation of binary on/inactive variables, which increases + Absolute losses automatically add StatusParameters to flows that don't have them, creating binary on/off variables. This increases computational complexity but enables realistic modeling of equipment with standby power consumption.flixopt/elements.py (3)
264-276: Review deprecated kwarg handling and carrier lowercasing.Lines 270-273 handle a deprecated
excess_penalty_per_flow_hourparameter, and line 274 lowercases the carrier string. A few observations:
Line 274:
self.carrier = carrier.lower() if carrier else None- This silently lowercases user input. While this may be intentional for case-insensitive matching, it could be surprising if users expect case preservation.Lines 270-273: The
_handle_deprecated_kwargcall suggests backward compatibility, but the implementation and deprecation warnings aren't visible in this file.Consider adding a note in the Bus docstring about carrier name normalization:
carrier: Name of the energy/material carrier type (e.g., 'electricity', 'heat', 'gas'). Carriers are registered via ``flow_system.add_carrier()`` or available as predefined defaults in CONFIG.Carriers. Used for automatic color assignment in plots. + Carrier names are normalized to lowercase for case-insensitive matching.
611-622: Review warning logic for status_parameters combinations.Two warnings are issued for potentially problematic configurations:
Lines 611-615: Warning when both
fixed_relative_profileandstatus_parametersare used. The message states the flow can be "switched active and inactive, effectively differing from the fixed_flow_rate." This might be confusing—does it mean the profile can be scaled down to zero, or that the profile itself can be violated?Lines 617-622: Warning when
relative_minimum > 0withoutstatus_parameters. This suggests the flow can never be inactive (flow_rate = 0), which is correct.Consider clarifying the first warning message:
if self.fixed_relative_profile is not None and self.status_parameters is not None: logger.warning( - f'Flow {self.label_full} has both a fixed_relative_profile and status_parameters.' - f'This will allow the flow to be switched active and inactive, effectively differing from the fixed_flow_rate.' + f'Flow {self.label_full} has both a fixed_relative_profile and status_parameters. ' + f'The status can switch the flow off (flow_rate = 0), allowing deviations from the fixed profile. ' + f'Consider removing status_parameters if the profile must always be followed exactly.' )
1006-1020: Review dynamic StatusParameters creation in ComponentModel.Similar to the Transmission component, ComponentModel dynamically creates StatusParameters for flows when:
- The component has
status_parameters(lines 1006-1012)- Flows are in
prevent_simultaneous_flows(lines 1014-1020)This automatic creation:
- Is convenient for users
- Introduces binary variables that may not be obvious
- Happens during modeling rather than initialization
Consider whether this behavior should be documented in the Component docstring to make users aware that binary variables will be added to their flows.
Would you like me to draft updated docstring text for the Component class that explains this automatic StatusParameters propagation behavior?
flixopt/flow_system.py (1)
820-820: Redundant local import ofjson.
jsonis already imported at module level (line 7). Remove the local import.- import json - from flixopt.io import convert_old_dataset, load_dataset_from_netcdf
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (48)
.github/workflows/docs.yaml(1 hunks).pre-commit-config.yaml(1 hunks)docs/examples/00-Minimal Example.md(0 hunks)docs/examples/01-Basic Example.md(0 hunks)docs/examples/02-Complex Example.md(0 hunks)docs/examples/03-Optimization Modes.md(0 hunks)docs/examples/04-Scenarios.md(0 hunks)docs/examples/05-Two-stage-optimization.md(0 hunks)docs/examples/index.md(0 hunks)docs/home/citing.md(1 hunks)docs/home/installation.md(1 hunks)docs/home/license.md(1 hunks)docs/home/quick-start.md(1 hunks)docs/index.md(3 hunks)docs/notebooks/01-quickstart.ipynb(1 hunks)docs/notebooks/02-heat-system.ipynb(1 hunks)docs/notebooks/03-investment-optimization.ipynb(1 hunks)docs/notebooks/04-operational-constraints.ipynb(1 hunks)docs/notebooks/05-multi-carrier-system.ipynb(1 hunks)docs/notebooks/06a-time-varying-parameters.ipynb(1 hunks)docs/notebooks/07-scenarios-and-periods.ipynb(1 hunks)docs/notebooks/08-large-scale-optimization.ipynb(1 hunks)docs/notebooks/10-transmission.ipynb(1 hunks)docs/notebooks/data/generate_example_systems.py(1 hunks)docs/notebooks/index.md(1 hunks)docs/overrides/main.html(1 hunks)docs/stylesheets/extra.css(3 hunks)docs/user-guide/building-models/choosing-components.md(1 hunks)docs/user-guide/building-models/index.md(1 hunks)docs/user-guide/core-concepts.md(1 hunks)docs/user-guide/index.md(1 hunks)docs/user-guide/migration-guide-v5.md(1 hunks)docs/user-guide/optimization/index.md(1 hunks)docs/user-guide/results-plotting.md(1 hunks)docs/user-guide/results/index.md(1 hunks)docs/user-guide/support.md(1 hunks)examples/05_Two-stage-optimization/two_stage_optimization.py(5 hunks)flixopt/__init__.py(3 hunks)flixopt/clustering.py(4 hunks)flixopt/color_processing.py(2 hunks)flixopt/components.py(23 hunks)flixopt/config.py(9 hunks)flixopt/effects.py(6 hunks)flixopt/elements.py(27 hunks)flixopt/features.py(4 hunks)flixopt/flow_system.py(24 hunks)flixopt/interface.py(12 hunks)flixopt/io.py(2 hunks)
💤 Files with no reviewable changes (7)
- docs/examples/00-Minimal Example.md
- docs/examples/03-Optimization Modes.md
- docs/examples/02-Complex Example.md
- docs/examples/index.md
- docs/examples/05-Two-stage-optimization.md
- docs/examples/04-Scenarios.md
- docs/examples/01-Basic Example.md
✅ Files skipped from review due to trivial changes (3)
- docs/notebooks/index.md
- docs/home/installation.md
- docs/user-guide/support.md
🚧 Files skipped from review as they are similar to previous changes (4)
- docs/home/citing.md
- docs/home/license.md
- docs/home/quick-start.md
- docs/user-guide/optimization/index.md
🧰 Additional context used
🧬 Code graph analysis (5)
docs/notebooks/data/generate_example_systems.py (8)
flixopt/flow_system.py (7)
FlowSystem(48-2204)add_elements(1063-1111)scenario_weights(1834-1841)scenario_weights(1844-1865)to_netcdf(719-744)solution(1345-1362)solution(1365-1368)flixopt/carrier.py (1)
Carrier(14-125)flixopt/elements.py (2)
Bus(196-316)Flow(333-647)flixopt/effects.py (1)
Effect(34-306)flixopt/linear_converters.py (2)
Boiler(24-111)HeatPump(211-302)flixopt/interface.py (4)
PiecewiseConversion(243-607)Piecewise(86-239)Piece(29-82)InvestParameters(956-1279)flixopt/structure.py (4)
scenario_weights(220-243)to_netcdf(894-921)solution(170-209)solution(1109-1123)flixopt/solvers.py (1)
HighsSolver(67-89)
flixopt/config.py (1)
flixopt/carrier.py (1)
Carrier(14-125)
flixopt/components.py (4)
flixopt/interface.py (12)
InvestParameters(956-1279)StatusParameters(1283-1534)link_to_flow_system(231-235)link_to_flow_system(463-467)link_to_flow_system(817-822)link_to_flow_system(1170-1174)transform_data(79-82)transform_data(237-239)transform_data(469-471)transform_data(824-827)transform_data(1176-1239)transform_data(1487-1510)flixopt/elements.py (8)
link_to_flow_system(113-122)link_to_flow_system(284-291)link_to_flow_system(523-532)transform_data(124-129)transform_data(293-296)transform_data(534-565)label_full(637-638)status(890-894)flixopt/structure.py (8)
link_to_flow_system(354-391)flow_system(394-412)prefix(346-348)_sub_prefix(350-352)transform_data(330-343)_fit_coords(414-427)label_full(1105-1106)label_full(1651-1652)flixopt/core.py (1)
PlausibilityError(22-25)
flixopt/effects.py (2)
flixopt/elements.py (7)
link_to_flow_system(113-122)link_to_flow_system(284-291)link_to_flow_system(523-532)label_full(637-638)transform_data(124-129)transform_data(293-296)transform_data(534-565)flixopt/structure.py (8)
link_to_flow_system(354-391)flow_system(394-412)prefix(346-348)label_full(1105-1106)label_full(1651-1652)transform_data(330-343)_fit_coords(414-427)_fit_effect_coords(429-447)
flixopt/clustering.py (2)
flixopt/plot_result.py (3)
PlotResult(20-143)show(55-58)update(60-73)flixopt/topology_accessor.py (1)
plot(289-455)
🪛 LanguageTool
docs/user-guide/core-concepts.md
[style] ~17-~17: Consider an alternative for the overused word “exactly”.
Context: ...hile meeting all heat demands. This is exactly the kind of problem flixOpt solves. Let...
(EXACTLY_PRECISELY)
[style] ~98-~98: Using many exclamation marks might seem excessive (in this case: 18 exclamation marks for a text that’s 7151 characters long)
Context: ...imize), others can be constraints. !!! example "In our heating system" - *...
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/migration-guide-v5.md
[style] ~428-~428: Using many exclamation marks might seem excessive (in this case: 19 exclamation marks for a text that’s 6900 characters long)
Context: ... success "Welcome to the new flixopt API! 🎉"
(EN_EXCESSIVE_EXCLAMATION)
docs/user-guide/results-plotting.md
[style] ~3-~3: Consider a different adjective to strengthen your wording.
Context: ...ss to common plots while still allowing deep customization. ## The Plot Accessor A...
(DEEP_PROFOUND)
docs/user-guide/results/index.md
[style] ~188-~188: Using many exclamation marks might seem excessive (in this case: 6 exclamation marks for a text that’s 2702 characters long)
Context: ...ne flow_system.topology.stop_app() ``` !!! note "Optional Dependencies" The in...
(EN_EXCESSIVE_EXCLAMATION)
🪛 markdownlint-cli2 (0.18.1)
docs/user-guide/migration-guide-v5.md
165-165: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
202-202: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
257-257: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
284-284: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
332-332: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
⏰ 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). (5)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.10)
- GitHub Check: Build documentation
🔇 Additional comments (51)
docs/overrides/main.html (1)
1-11: Good template structure and safe Jinja2 usage.The template correctly extends the base template, uses conditional rendering to guard against undefined
nb_url, and leverages{{ super() }}to preserve parent block content. The auto-escaping of Jinja2 variables protects against XSS risks.docs/stylesheets/extra.css (2)
596-602:architecture-sectionnow centered + max-width: good, but ensure it doesn’t double-constrain nested content.
Withmax-width: 1200pxon.architecture-sectionplus global.md-content__inner { max-width: 1300px !important; }, confirm the section renders as intended (no awkward side padding/line lengths) across breakpoints.
786-947: [Rewritten review comment]
[Classification tag]docs/notebooks/05-multi-carrier-system.ipynb (1)
103-107: Nice: deterministic randomness.
Seeding before adding noise keeps the notebook outputs stable.docs/user-guide/building-models/choosing-components.md (1)
289-306: StatusParameters field names need verification against current API.The code snippet uses
effects_per_switch_on,on_hours_min, andoff_hours_min, but these may be outdated. Verify against the current StatusParameters definition in the codebase and update to the correct field names if they have been renamed (e.g., toeffects_per_startup,active_hours_min,min_downtime).examples/05_Two-stage-optimization/two_stage_optimization.py (3)
43-46: LGTM! Carrier parameters improve visualization.Adding carrier parameters to Bus constructors enables automatic color assignment in plots, improving the user experience. This is a clean enhancement that aligns with the new Carrier system introduced in the PR.
62-62: LGTM! Improved terminology for state-based parameters.The rename from
OnOffParameterstoStatusParametersand the parameter updates (effects_per_startup,min_uptime,min_downtime) make the API more intuitive and aligned with standard industry terminology.Also applies to: 69-69
138-148: LGTM! Simplified solution access pattern.The shift to accessing solutions directly via
flow_system.solutionand usingflow_system.statistics.sizesprovides a cleaner, more intuitive API compared to the previousresults.solutionpattern. The size comparison logic usingto_dataarray()is appropriate for xarray-based validation.flixopt/config.py (2)
33-33: LGTM! Reasonable deprecation timeline.Setting
DEPRECATION_REMOVAL_VERSIONto'6.0.0'for the v5.0.0 release provides users with a full major version cycle to migrate away from deprecated APIs, following good deprecation policy practices.
598-606: Class-level import is unconventional but not problematic.The
from .carrier import Carrierimport at line 598 is placed inside the class definition because theCarrierclass is needed both for type annotations (: Carrier) and for creating instances immediately after. Sincecarrier.pyhas no imports fromconfig.py, there is no circular import risk.The import could be moved to the module level for consistency with Python conventions, but the current placement is functional and poses no issues with type checking or IDE support. The pattern is acceptable as-is.
flixopt/interface.py (3)
1283-1286: LGTM! StatusParameters terminology is more intuitive.The rename from
OnOffParameterstoStatusParametersbetter reflects the purpose of modeling equipment operational states. The updated parameter names (effects_per_startup,min_uptime,min_downtime, etc.) are clearer and more aligned with standard terminology in unit commitment problems.
1177-1182: LGTM! Helpful validation for InvestParameters.The new validation ensuring either
fixed_sizeormaximum_sizeis set prevents silent failures and provides a clear, actionable error message. This improves the user experience by catching configuration errors early in the model setup process.
473-607: LGTM! Well-implemented visualization for PiecewiseConversion.The
plot()method provides comprehensive visualization capabilities:
- Proper validation of FlowSystem connection state
- Smart handling of time-varying vs static data
- Good use of faceting for multiple flows
- Appropriate colorscale application
- Returns PlotResult for further customization
The implementation follows the patterns established in the plotting module and provides valuable debugging/analysis capabilities.
docs/user-guide/building-models/index.md (1)
1-378: LGTM! Excellent educational documentation.This comprehensive guide provides:
- Clear step-by-step model building process
- Well-organized component selection guidance
- Practical patterns and examples
- Helpful debugging tips
- Good use of tables and structured information
The examples correctly use the updated API (carriers, StatusParameters, etc.) and align with the broader changes in this PR.
docs/user-guide/results/index.md (1)
1-283: LGTM! Comprehensive results analysis documentation.This guide provides excellent coverage of:
- Multiple access patterns (solution, statistics, element-specific)
- Clear property descriptions with examples
- Visualization methods and customization
- Network topology visualization options
- Save/load workflows
- xarray integration patterns
The examples correctly demonstrate the new FlowSystem-centric API and statistics accessor patterns introduced in this PR.
docs/notebooks/06a-time-varying-parameters.ipynb (1)
1-339: LGTM! Excellent educational notebook on time-varying parameters.This notebook effectively demonstrates:
- Real-world use case (temperature-dependent heat pump COP)
- Data preparation and visualization
- Time-varying conversion factors using arrays
- Result analysis and validation
- Clear explanation of when to use this approach vs alternatives
The code examples correctly use the updated API and provide good learning value.
docs/notebooks/03-investment-optimization.ipynb (2)
18-33: Nice: clean notebook state (no outputs, null execution counts) + deterministic randomness.
This is ideal for docs notebooks in-repo.
169-175: No API issues found—parameter names and accessor are correct.The notebook correctly uses
effects_of_investment_per_size(current parameter name, as confirmed inInvestParameters.__init__) andstatistics.sizes(confirmed as a standard accessor). The deprecation mapping inio.pyshows these are the current names; older names likespecific_effectsmap toeffects_of_investment_per_size. No updates needed.docs/notebooks/01-quickstart.ipynb (1)
99-133: Tutorial flow matches the new API surface (FlowSystem-first +statistics.plot).
Reads cleanly and is consistent with the rest of the docs set.docs/user-guide/results-plotting.md (1)
21-25: ThePlotResultcross-reference target is valid. The classPlotResultexists atflixopt/plot_result.py:20and the mkdocs_autorefs target[flixopt.plot_result.PlotResult]will resolve correctly in the docs build.docs/notebooks/02-heat-system.ipynb (1)
147-153: No action needed—fx.Storage()parameter names match the v5 API.Both
capacity_in_flow_hoursandminimal_final_charge_stateare correct and will not cause failures. The parameters are properly defined in the current Storage class signature and used consistently throughout the codebase.docs/user-guide/core-concepts.md (2)
1-17: Well-structured introduction to core concepts.The scenario-driven approach using a district heating example is excellent for onboarding users. The framing clearly establishes the problem domain and optimization goal.
38-43: Verify the link to Color Management section exists.Line 43 references
results-plotting.md#color-management. Ensure this anchor exists in the target file.docs/notebooks/08-large-scale-optimization.ipynb (3)
23-34: Unused import:xarrayappears unused.The
xrimport (xarray) at line 29 is used later in the notebook for creating the profiles Dataset (line 106), so this is actually correct.
136-136: Thebuild_systemfunction is well-designed for demonstrating optimization techniques.The function creates a realistic district heating system with investment optimization. Good use of:
- Carriers for semantic organization
imbalance_penalty_per_flow_hourto prevent infeasibilities with fixed sizes- Investment parameters on multiple components
One minor observation: the function is defined inline in a single cell (line 136 contains the entire function). This is fine for a notebook but makes the code harder to read in the raw JSON format.
362-397: Documentation tables and workflow guidance are clear and actionable.The "When to Use Each Technique" table and code examples provide excellent guidance for users deciding between optimization approaches.
docs/user-guide/migration-guide-v5.md (3)
161-194: Excellent variable naming convention documentation.The pattern reference tables clearly document the new naming convention. This will significantly help users migrate their code.
249-276: Good migration path for old result files with clear limitations.The
FlowSystem.from_old_results()method and its documented limitations provide a pragmatic migration path while setting appropriate expectations.
382-407: Transform accessor migration is clearly documented.The side-by-side comparison of old vs new patterns and the important note about solution being dropped are helpful for users.
docs/notebooks/data/generate_example_systems.py (5)
19-24: Robust fallback for notebook execution context.The try/except block handling
NameErrorwhen__file__is unavailable (e.g., in mkdocs-jupyter) is a good defensive pattern.
26-83: Well-structured simple system example.The
create_simple_systemfunction creates a realistic heating system with appropriate parameters. Good use of seeded random for reproducibility.
159-187: CHP with piecewise efficiency is a valuable advanced example.The piecewise conversion definition demonstrates varying efficiency at different load points, which is a common real-world characteristic. The
status_parametersusage aligns with the PR's rename fromon_off_parameters.
232-314: Multiperiod system demonstrates advanced features well.The
create_multiperiod_systemfunction effectively demonstrates:
- Period definitions with rising gas prices
- Scenario definitions with weighted probabilities
- DataFrame-based demand profiles matching scenario columns
- Investment optimization within multiperiod context
317-341: No action needed. Thesolution["objective"]key is valid and correctly used. The solution Dataset explicitly includes an"objective"key (set instructure.pyline 173), which is distinct from effect labels. The access patternfs.solution["objective"].item()matches the established usage throughout the codebase.docs/notebooks/07-scenarios-and-periods.ipynb (4)
1-8: Clear introduction to multi-dimensional planning.The notebook introduction effectively frames the learning objectives: periods, scenarios, scenario weights, and multi-dimensional data handling.
96-134: Scenario-dependent demand profiles are well-constructed.Good use of:
- Seeded random for reproducibility
- Clipping to ensure realistic bounds
- DataFrame with scenario column names matching the scenario Index
432-443: Potential shape mismatch in CHP size comparison.Line 440 uses
chp_size.max("scenario")to get a scalar for comparison, but line 442 computes(chp_size - chp_size_mild)wherechp_sizemay have scenario dimension andchp_size_mildis from a single-scenario system. xarray should handle broadcasting, but the output message may be confusing if dimensions differ.Consider explicitly selecting matching dimensions or clarifying the comparison:
# More explicit comparison both_scenarios_size = chp_size.max("scenario").item() mild_only_size = chp_size_mild.item() print(f'CHP size (both scenarios): {both_scenarios_size:.2f} kW') print(f'CHP size (mild only): {mild_only_size:.2f} kW') print(f'\nPlanning for uncertainty adds {both_scenarios_size - mild_only_size:.2f} kW capacity')
466-526: Comprehensive "Key Concepts" summary section.The summary effectively recaps:
- Multi-dimensional FlowSystem initialization
- Dimension-varying parameter shapes
- Expected cost minimization formula
- Selection methods for analysis
This provides excellent reference material for users.
flixopt/__init__.py (1)
17-18: LGTM! Public API surface updates are well-structured.The additions of Carrier, CarrierContainer, StatusParameters, and PlotResult to the public API are properly imported and exported through
__all__. These changes align with the PR's goal of introducing a Carrier-based visualization system and Status-based terminology.Also applies to: 32-32, 34-34, 39-40, 56-56, 62-62
flixopt/clustering.py (1)
146-212: LGTM! Plot method successfully migrated to PlotResult pattern.The updated signature and implementation properly:
- Returns PlotResult for method chaining (
.show(),.to_html(), etc.)- Builds an xarray Dataset with both 'original' and 'aggregated' data for export
- Respects
CONFIG.Plotting.default_showwhenshow=None- Removes the save parameter in favor of PlotResult's
.to_html()and.to_image()methodsThis aligns with the PlotResult pattern shown in flixopt/plot_result.py and flixopt/topology_accessor.py.
flixopt/effects.py (3)
240-245: LGTM! Consistent with link_to_flow_system pattern.The new
link_to_flow_systemmethod correctly usesself.label_fullas the prefix (as documented: "Elements use their label_full as prefix by default"). This aligns with the pattern shown in flixopt/elements.py and flixopt/structure.py.
247-290: LGTM! Transform data now uses internal prefix.The signature change from
transform_data(self, name_prefix: str = '')totransform_data(self)is correct. The method now usesself.prefix(set bylink_to_flow_system) for all coordinate naming, which is consistent with the broader refactoring across Interface-based classes.
484-493: Effect objects are no longer accepted in effect specifications—use label strings instead.The helper function now raises
TypeErrorif an Effect object is passed instead of a label string. This is an intentional breaking change reflected in the error message: "Effect objects are no longer accepted when specifying EffectValues. Use the label string instead."flixopt/features.py (1)
152-348: LGTM! Consistent OnOff → Status terminology migration.The renaming from
OnOffModeltoStatusModeland associated parameters/properties is thorough and consistent:
- Constructor:
on_off_parameters→parameters: StatusParameters,on_variable→status,previous_states→previous_status- Properties:
on_hours_total→active_hours,off→inactive,switch_on→startup,switch_off→shutdown,switch_on_nr→startup_count,consecutive_on_hours→uptime,consecutive_off_hours→downtime- Internal flags:
use_off→use_downtime_tracking,use_switch_on→use_startup_tracking,use_consecutive_on_hours→use_uptime_trackingThe logic is preserved while updating the terminology to be more intuitive (active/inactive vs on/off).
flixopt/components.py (1)
388-388: Good addition of unbounded storage support with proper validation.The Storage now supports
capacity_in_flow_hours=Nonefor unbounded capacity, with validation ensuring:
relative_minimum_charge_state > 0requires a capacity (Line 497-501)relative_maximum_charge_state < 1requires a capacity (Line 502-506)relative_minimum/maximum_final_charge_staterequire a capacity (Lines 507-516)This correctly prevents nonsensical configurations where relative bounds would have no meaning without a defined capacity.
Also applies to: 496-516
flixopt/elements.py (2)
141-150: Good validation for status_parameters requiring flow sizes.The new validation correctly enforces that all flows must have explicit sizes when
status_parametersare used on a Component. The error message clearly explains that sizes are "required for big-M constraints."
572-622: Comprehensive validation logic for Flow size requirements.The validation correctly enforces size requirements for various Flow configurations:
- Lines 573-577:
status_parametersrequires size (for big-M constraints)- Lines 579-583:
fixed_relative_profilerequires size (flow_rate = size × profile)- Lines 586-596: Relative bounds require size when non-default values are used
- Lines 599-609: Load factor constraints require size
The error messages are clear and explain why the size is needed. This is excellent defensive programming.
flixopt/flow_system.py (4)
848-882: LGTM! Clean implementation of copy semantics.The
copy()method safely handles the solution state using try/finally, ensuring the original solution is restored even if serialization fails. Both__copy__and__deepcopy__correctly delegate tocopy(), which is appropriate since FlowSystem contains complex interdependent state that requires full re-serialization.
1344-1376: LGTM! Solution property with proper cache invalidation.The solution setter correctly invalidates the
_statisticscache, ensuring stale statistics are not returned after a new solution is set. Theis_lockedproperty provides a clean way to check if the FlowSystem can be modified.
1378-1423: LGTM! Well-designed lifecycle management.The separation between
_invalidate_model()(internal),reset()(clears solution + invalidates), andinvalidate()(requires no solution) provides clear semantics. The_invalidate_model()method properly clears all element submodels and accessor caches.
1520-1545: LGTM! Statistics accessor with proper caching.The lazy initialization pattern with cache invalidation on solution change is well-implemented. The cache is cleared via the solution setter, ensuring consistency.
docs/index.md
Outdated
| <a href="home/installation/" class="md-button md-button--primary">🚀 Get Started</a> | ||
| <a href="notebooks/" class="md-button">💡 View Examples</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify existence of main documentation targets
DOCS_PATHS=(
"docs/home/installation.md"
"docs/home/installation/index.md"
"docs/notebooks/index.md"
"docs/api-reference/index.md"
"docs/user-guide/recipes/index.md"
"docs/user-guide/mathematical-notation/index.md"
"docs/roadmap.md"
"docs/roadmap/index.md"
)
for path in "${DOCS_PATHS[@]}"; do
if [ -f "$path" ]; then
echo "✓ $path"
else
echo "✗ $path (not found)"
fi
done
# Also check the actual docs directory structure
echo ""
echo "=== Full docs directory structure ==="
find docs -type f -name "*.md" | head -50Repository: flixOpt/flixopt
Length of output: 1602
Fix broken documentation link targets.
The following documentation links in docs/index.md reference files that do not exist:
home/installation/(lines 16, 27, 128) — expectsdocs/home/installation/index.mdbut onlydocs/home/installation.mdexists; usehome/installation.mdor create the directory structureapi-reference/(line 39) — directory and file do not exist; either create the documentation or remove the linkroadmap/(line 57) — expectsdocs/roadmap/index.mdbut onlydocs/roadmap.mdexists; useroadmap.mdor create the directory structure
The links to notebooks/, user-guide/recipes/, and user-guide/mathematical-notation/ are valid.
🤖 Prompt for AI Agents
In docs/index.md around lines 16–17 (also affecting references at lines 27, 39,
57, and 128), fix broken link targets that point to non-existent directories:
update href="home/installation/" to href="home/installation.md" (or create
docs/home/installation/index.md), change href="roadmap/" to href="roadmap.md"
(or add docs/roadmap/index.md), and either add the missing api-reference
documentation under docs/api-reference/index.md or remove the api-reference
link; leave valid links (notebooks/, user-guide/recipes/,
user-guide/mathematical-notation/) unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
CHANGELOG.md (2)
295-308: Removed items are comprehensive but list is very long—consider adding a categorized reference table.The removal section covers ~12 classes, ~11 properties, and multiple parameters. While complete, the list is dense and hard to scan during migration. A quick reference table (Old → New → Migration Path) would improve user experience.
Consider adding a condensed reference table after line 295 with three columns:
- Removed Item
- Replacement
- Usage Example
This would make it easier for users to find specific items they're migrating without reading the full narrative.
383-395: Migration checklist is well-structured but misses a few nuances.The checklist is a good high-level guide, but some tasks lack depth:
- "Replace Optimization class" (line 387): Doesn't mention
SegmentedOptimization→SegmentedOptimizationalias change- "Update Bus excess parameters" (line 391): Doesn't clarify the strict balance default change (lines 250–255)
- "Update deprecated parameters" (line 392): Table reference is helpful but could benefit from a "priority" hint (e.g., "Most critical are...")
Enhance line 391 to clarify the strict balance behavior:
| Update Bus imbalance behavior | Review default `allows_imbalance=False` (strict balance); add explicit penalties if needed |
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
CHANGELOG.md(2 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md
66-66: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
96-96: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
113-113: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
141-141: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
159-159: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
172-172: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
187-187: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
213-213: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
259-259: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
⏰ 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). (5)
- GitHub Check: test (3.12)
- GitHub Check: test (3.10)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: Build documentation
🔇 Additional comments (5)
CHANGELOG.md (5)
226-248: OnOffParameters renaming table is comprehensive and clear.The terminology update from OnOff to Status is well-documented with a complete mapping table. The transition from operation-centric language (on/off) to state-centric terminology (status/startup/shutdown) is clear and aligns well with industry standards (PyPSA, unit commitment terminology).
54-61: [Upcoming] section header and migration guide link are well-positioned.The v5.0.0 summary clearly articulates the major paradigm shift (FlowSystem-centric) and prominently links to the migration guide. This is excellent for user adoption.
66-83: All code examples in the CHANGELOG are accurate and match the v5.0 API implementation. TheOptimizeAccessorclass implements__call__to support the documented syntaxflow_system.optimize(solver), and all referenced accessors (statistics, topology, transform), PlotResult class, and CONFIG.Carriers structure exist with the documented APIs.
271-271: The description default change from None to empty string is safe with no identified side effects.No code in the codebase relies on None-specific checks for
effect.description. The field is purely informational metadata without conditional logic or validation that would break from this change. Both None and empty string are falsy, making them functionally equivalent in any truthy/falsy context.
66-66: No changes needed. The CHANGELOG.md file uses fenced code blocks (python, etc.) consistently throughout, which aligns with markdownlint's default MD046 rule (style: 'consistent'`). Fenced blocks are the modern Markdown standard and are already the correct style for this project.
| **Bus imbalance terminology and default changed**: | ||
| - `excess_penalty_per_flow_hour` → `imbalance_penalty_per_flow_hour` | ||
| - Default changed from `1e5` to `None` (strict balance) | ||
| - `with_excess` → `allows_imbalance` | ||
| - `excess_input` → `virtual_supply` | ||
| - `excess_output` → `virtual_demand` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
git ls-files | head -20Repository: flixOpt/flixopt
Length of output: 527
🏁 Script executed:
# Check the CHANGELOG.md file size and read the relevant sections
wc -l CHANGELOG.md
echo "=== Lines 50-70 (migration guide area) ==="
sed -n '50,70p' CHANGELOG.md
echo ""
echo "=== Lines 240-260 (Bus imbalance terminology change) ==="
sed -n '240,260p' CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 1839
🏁 Script executed:
# Find the Bus class definition
find . -type f -name "*.py" | head -20
echo ""
echo "=== Searching for Bus class definition ==="
rg "class Bus" --type py -A 5 | head -50Repository: flixOpt/flixopt
Length of output: 2442
🏁 Script executed:
# Search for imbalance_penalty_per_flow_hour and excess_penalty_per_flow_hour
echo "=== Searching for imbalance_penalty_per_flow_hour ==="
rg "imbalance_penalty_per_flow_hour" --type py -B 2 -A 2 | head -40
echo ""
echo "=== Searching for excess_penalty_per_flow_hour ==="
rg "excess_penalty_per_flow_hour" --type py -B 2 -A 2 | head -40
echo ""
echo "=== Searching for allows_imbalance ==="
rg "allows_imbalance" --type py -B 2 -A 2 | head -40Repository: flixOpt/flixopt
Length of output: 5171
🏁 Script executed:
# Search for error messages related to bus imbalance or balance
echo "=== Searching for bus balance validation errors ==="
rg "balance|imbalance" --type py -i | grep -i "error\|raise\|warning" | head -30
echo ""
echo "=== Searching for Bus validation functions ==="
rg "def.*validate.*bus\|def.*check.*bus" --type py -i -A 3 | head -50Repository: flixOpt/flixopt
Length of output: 398
🏁 Script executed:
# Find main Python modules
fd "\.py$" --type f -d 2 | head -30Repository: flixOpt/flixopt
Length of output: 740
🏁 Script executed:
# List directory structure
ls -la
echo ""
# Look for Python source code directory
find . -maxdepth 3 -type d -name "src" -o -name "lib" -o -name "balmorel" -o -name "power*" | head -10Repository: flixOpt/flixopt
Length of output: 1437
🏁 Script executed:
# Search for parameter definition with defaults
echo "=== Looking for Bus class with parameter defaults ==="
rg "imbalance_penalty_per_flow_hour\s*[:=]" --type py -B 3 -A 1
echo ""
echo "=== Looking for excess_penalty_per_flow_hour defaults ==="
rg "excess_penalty_per_flow_hour\s*[:=]" --type py -B 3 -A 1Repository: flixOpt/flixopt
Length of output: 8069
🏁 Script executed:
# Search in all files, not just Python
echo "=== All references to imbalance_penalty_per_flow_hour ==="
rg "imbalance_penalty_per_flow_hour" -A 2 -B 2 | head -60
echo ""
echo "=== All references to excess_penalty_per_flow_hour ==="
rg "excess_penalty_per_flow_hour" -A 2 -B 2 | head -60Repository: flixOpt/flixopt
Length of output: 7992
🏁 Script executed:
# Get full CHANGELOG content
cat CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 50373
🏁 Script executed:
# Count Python files
find . -name "*.py" -type f | wc -l
echo ""
# List all files in repo
ls -la
echo ""
# Check if CHANGELOG exists
file CHANGELOG.md
wc -l CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 1519
🏁 Script executed:
# Use git to list all files
git ls-filesRepository: flixOpt/flixopt
Length of output: 7770
🏁 Script executed:
# Read CHANGELOG.md with line numbers
cat -n CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 50373
🏁 Script executed:
# Extract lines 40-80 to see the migration guide area
sed -n '40,80p' CHANGELOG.md | cat -n
echo ""
echo "=== Lines 240-270 (Bus imbalance terminology) ==="
sed -n '240,270p' CHANGELOG.md | cat -nRepository: flixOpt/flixopt
Length of output: 3041
🏁 Script executed:
# Get full CHANGELOG content and display with line numbers
head -300 CHANGELOG.md | tail -100 | cat -nRepository: flixOpt/flixopt
Length of output: 4715
🏁 Script executed:
# Just print the file with a simpler method
python3 << 'EOF'
with open('CHANGELOG.md', 'r') as f:
lines = f.readlines()
print(f"Total lines: {len(lines)}\n")
# Show lines 50-80 (migration guide area)
print("=== Lines 50-80 (migration guide area) ===")
for i in range(49, min(80, len(lines))):
print(f"{i+1:3d}: {lines[i]}", end='')
print("\n=== Lines 240-270 (Bus imbalance section) ===")
for i in range(239, min(270, len(lines))):
print(f"{i+1:3d}: {lines[i]}", end='')
EOFRepository: flixOpt/flixopt
Length of output: 145
🏁 Script executed:
# Display the entire CHANGELOG file
wc -l CHANGELOG.md
head -100 CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 3573
🏁 Script executed:
# Search for migration-related content
grep -n "migration\|restore\|upgrade\|backward\|breaking" CHANGELOG.md -i
echo ""
echo "=== Search for excess_penalty ==="
grep -n "excess_penalty\|imbalance_penalty" CHANGELOG.md
echo ""
echo "=== Search for allows_imbalance ==="
grep -n "allows_imbalance\|with_excess" CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 8979
🏁 Script executed:
# Get line count and show if file exists
stat CHANGELOG.md
echo ""
# Try to display from line 1 to 100
sed -n '1,100p' CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 3616
🏁 Script executed:
# Final attempt to read the entire file
cat CHANGELOG.md | head -n 300Repository: flixOpt/flixopt
Length of output: 10715
🏁 Script executed:
# Verify file exists and get its size
ls -lh CHANGELOG.md
echo "---"
# Try with less paging disabled
cat CHANGELOG.md 2>&1 | head -200
echo "---"
# Try wc to get size
wc -c CHANGELOG.md
wc -l CHANGELOG.mdRepository: flixOpt/flixopt
Length of output: 6866
🌐 Web query:
Bus imbalance terminology excess_penalty_per_flow_hour imbalance_penalty_per_flow_hour CHANGELOG
💡 Result:
Summary: I could not find any CHANGELOG or release note that renames excess_penalty_per_flow_hour to imbalance_penalty_per_flow_hour. The Bus API/docs still use excess_penalty_per_flow_hour (default 1e5). Sources: flixopt Bus API docs. [1][2]
Sources
- flixOpt commons — Bus class (shows excess_penalty_per_flow_hour parameter). [1]
- flixopt elements API — Bus documentation (same parameter and description). [2]
🌐 Web query:
"imbalance_penalty_per_flow_hour" OR "excess_penalty_per_flow_hour" Bus
💡 Result:
I couldn't find any web results for the exact parameters "imbalance_penalty_per_flow_hour" or "excess_penalty_per_flow_hour". Where did you see those names (codebase, paper, config file, library)? Tell me the source or context (project/language/tool) and I’ll search that specifically.
🏁 Script executed:
# Check git history to understand what project this is
git log --oneline -n 5 2>/dev/null || echo "No git history available"
echo ""
# Check if there's a README or any documentation
find . -maxdepth 1 -type f | grep -E "\.(md|txt|rst)$"
echo ""
# Try to read CHANGELOG with a hex dump to see if there's an encoding issue
xxd CHANGELOG.md | head -20Repository: flixOpt/flixopt
Length of output: 174
🏁 Script executed:
# Since we can't read the file easily, let's analyze what we know from the review comment itself
# The review shows this code snippet (lines 250-255):
cat << 'EOF'
**Bus imbalance terminology and default changed**:
- `excess_penalty_per_flow_hour` → `imbalance_penalty_per_flow_hour`
- Default changed from `1e5` to `None` (strict balance)
- `with_excess` → `allows_imbalance`
- `excess_input` → `virtual_supply`
- `excess_output` → `virtual_demand`
EOF
# This is WHAT the CHANGELOG says. Now we need to verify the claims about:
# 1. Whether the migration guide (lines 58-60) actually addresses this
# 2. Whether error messages are clear (can't verify without source)
# 3. Whether the change impacts are accurately assessed
# Let's try one more direct read
python3 -c "with open('CHANGELOG.md') as f: content = f.read(); print('File size:', len(content), 'chars'); print('First 500 chars:'); print(content[:500])"Repository: flixOpt/flixopt
Length of output: 145
🏁 Script executed:
python3 << 'SCRIPT'
import os
try:
with open('CHANGELOG.md', 'r', encoding='utf-8') as f:
lines = f.readlines()
print(f"Total lines: {len(lines)}\n")
# Show lines around 58-60 (migration guide claim)
print("=" * 60)
print("LINES 50-70 (Migration guide area):")
print("=" * 60)
start = max(0, 49)
end = min(len(lines), 71)
for i in range(start, end):
print(f"{i+1:4d}: {lines[i]}", end='')
print("\n" + "=" * 60)
print("LINES 240-270 (Bus imbalance terminology):")
print("=" * 60)
start = max(0, 239)
end = min(len(lines), 271)
for i in range(start, end):
print(f"{i+1:4d}: {lines[i]}", end='')
except FileNotFoundError:
print("CHANGELOG.md not found!")
except Exception as e:
print(f"Error: {e}")
SCRIPTRepository: flixOpt/flixopt
Length of output: 145
Default change to strict balance requires clear upgrade guidance inline in CHANGELOG.
The breaking change is accurately documented: the default for imbalance_penalty_per_flow_hour changed from 1e5 (allowing imbalance with penalty) to None (requiring strict balance). However, lines 58–60 reference an external migration guide link—they do not contain the inline restoration instructions mentioned in the review.
The migration checklist at line 391 states "Update Bus excess parameters | Use imbalance_penalty_per_flow_hour" but does not provide the critical example: users upgrading from v4.x who relied on the old default must now explicitly set both imbalance_penalty_per_flow_hour and allows_imbalance=True. Add a dedicated subsection in the CHANGELOG's Breaking Changes for Bus parameters with concrete examples of restoring old behavior.
🤖 Prompt for AI Agents
In CHANGELOG.md around lines 250–255, the breaking-change note about bus
imbalance defaults is missing inline upgrade instructions; add a dedicated "Bus
parameters (breaking change)" subsection under Breaking Changes that shows exact
migration steps and examples to restore previous behavior (e.g., for users
upgrading from v4.x: set imbalance_penalty_per_flow_hour=1e5 and
allows_imbalance=True, and show the old→new name mappings:
excess_penalty_per_flow_hour → imbalance_penalty_per_flow_hour, with_excess →
allows_imbalance, excess_input → virtual_supply, excess_output →
virtual_demand). Also update the migration checklist near line 391 to replace
the terse "Use `imbalance_penalty_per_flow_hour`" with a one-line example
demonstrating both keys set to restore legacy behavior.
| **Storage charge_state changes**: | ||
| - `charge_state` no longer has an extra timestep | ||
| - Final charge state is now a separate variable: `charge_state|final` | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storage charge_state structural change needs migration example.
The change to remove the extra timestep from charge_state and introduce a separate charge_state|final variable is breaking and potentially subtle. Users extracting charge state time series may be caught off guard by the dimensional change.
Add a brief migration example in the migration checklist or a new subsection showing:
- Old:
charge_statehad an extra timestep (length: timesteps + 1) - New:
charge_stateis aligned with timesteps; final state is incharge_state|final
Example code would help users update their result processing code.
🤖 Prompt for AI Agents
In CHANGELOG.md around lines 267–270, add a short migration subsection that
shows the structural change and a minimal before/after example: state that Old
charge_state had length timesteps+1 while New charge_state has length timesteps
with the final state moved to charge_state|final; include a concise “Old”
description of how users previously extracted the full timeseries from
charge_state and an “New” description showing they must now read charge_state
for the timestep-aligned series and read charge_state|final for the final value,
plus a one-line note advising to update any code that assumed the extra
timestep.
There was a problem hiding this 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 (6)
flixopt/io.py (1)
682-731: Past issue addressed - value renames now use renamed key with fallback.The fix from the past review has been implemented. Lines 714-717 now correctly look up value renames by the renamed key first (
new_key), falling back to the original key if not found:vr_key = new_key if new_key in value_renames else keyflixopt/config.py (1)
788-825: Past issue addressed - docstring now matches implementation.The docstring at line 796 now correctly states "Enables solver console output and main results logging", which matches the implementation at lines 822-823. The comment at line 818 ("Light logging - SUCCESS level without too much noise") also aligns with the behavior.
flixopt/flow_system.py (3)
533-536: Past issue addressed - safe pop with default value.The
pop('scenario_weights', None)now correctly handles the case where the key doesn't exist.
1288-1340: LGTM - Solve method with proper error handling.The solve method correctly checks for model existence, handles infeasibility with configurable diagnostics, and stores the solution. The typo "Sucessfully" has been fixed to "Successfully" at line 1332.
1577-1597: Past issue addressed - deprecation warning now emitted.The
plot_networkmethod now properly emits aDeprecationWarningbefore delegating totopology.plot_legacy().docs/user-guide/migration-guide-v5.md (1)
411-422: Deprecation timeline and warning are consistent.The timeline correctly shows the progression from v4.x (available) → v5.0.0 (deprecated) and the warning box accurately reflects that these classes will be removed "in a future version" (implying v6.0.0 based on line 27). This addresses the past review concern about inconsistent wording.
🧹 Nitpick comments (1)
flixopt/flow_system.py (1)
1812-1822: Minor: storages property creates a new container on each access.Unlike
flowswhich is cached in_flows_cache,storagescreates a newElementContainerevery call. If accessed frequently in loops, consider caching similar toflows.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.github/workflows/docs.yaml(1 hunks).pre-commit-config.yaml(1 hunks)docs/index.md(3 hunks)docs/user-guide/index.md(1 hunks)docs/user-guide/migration-guide-v5.md(1 hunks)flixopt/config.py(9 hunks)flixopt/flow_system.py(24 hunks)flixopt/io.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- .pre-commit-config.yaml
- .github/workflows/docs.yaml
- docs/user-guide/index.md
🧰 Additional context used
🧬 Code graph analysis (1)
flixopt/config.py (3)
flixopt/carrier.py (1)
Carrier(14-125)tests/conftest.py (3)
electricity(85-86)heat(89-90)gas(93-94)tests/deprecated/conftest.py (3)
electricity(88-89)heat(92-93)gas(96-97)
🪛 LanguageTool
docs/user-guide/migration-guide-v5.md
[style] ~428-~428: Using many exclamation marks might seem excessive (in this case: 19 exclamation marks for a text that’s 6937 characters long)
Context: ... success "Welcome to the new flixopt API! 🎉"
(EN_EXCESSIVE_EXCLAMATION)
🪛 markdownlint-cli2 (0.18.1)
docs/user-guide/migration-guide-v5.md
165-165: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
202-202: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
257-257: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
284-284: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
332-332: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
⏰ 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). (5)
- GitHub Check: test (3.10)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: Build documentation
🔇 Additional comments (14)
docs/index.md (1)
16-17: All documentation links are correctly configured.The files
docs/home/installation.mdanddocs/roadmap.mdexist and the links at lines 16–17, 27, 57, and 128 correctly reference them as relative markdown file paths.flixopt/io.py (3)
600-667: LGTM - Comprehensive backwards-compatibility mappings.The
PARAMETER_RENAMESdictionary provides a thorough mapping from old parameter names to new ones, covering effects, flows, components, classes, and status parameters. The organization with inline comments makes maintenance easier.
669-680: LGTM - Value renames and context keys are well-defined.The
VALUE_RENAMESand flow label reference keys (_FLOW_LABEL_REFERENCE_KEYS,_FLOW_PARAMETER_KEYS) correctly separate value transformations from key renames and handle the special case where flow labels should not be renamed inside certain contexts likepiecewises.
733-829: LGTM - Clean migration utilities with good documentation.The
convert_old_datasetandconvert_old_netcdffunctions provide a straightforward migration path with clear docstrings and usage examples. The in-place modification semantics are documented appropriately.flixopt/config.py (4)
23-33: LGTM - Public exports and deprecation version constant added.The
DEPRECATION_REMOVAL_VERSIONconstant centralizes version management for deprecation warnings, making it easier to update when planning future releases.
174-175: LGTM - New solving default added.The
compute_infeasibilitiesdefault is properly added to the immutable_DEFAULTSdictionary.
634-642: LGTM - Carriers reset is consistent with class definition.The reset logic correctly recreates the same Carrier instances defined in the class body, maintaining consistency.
578-606: No circular import risk from this import.The
from .carrier import Carrierimport inside the class body executes at module load time, butcarrier.pydoes not import fromconfig.py, so there is no circular dependency to be concerned about.Likely an incorrect or invalid review comment.
flixopt/flow_system.py (5)
579-623: LGTM - Solution and carriers serialization in to_dataset.The solution variables are properly namespaced with
'solution|'prefix and the time coordinate is renamed to'solution_time'to avoid conflicts. Carriers are JSON-serialized into attrs.
699-716: LGTM - Solution and carriers restoration in from_dataset.The deserialization correctly reverses the serialization: solution variables are extracted by prefix, time coordinate is renamed back, and carriers are restored from JSON attrs.
846-888: LGTM - Copy semantics exclude solution state.The
copy()method correctly creates a clean copy without the solution, enabling independent modifications. The__copy__and__deepcopy__methods delegate appropriately.
1073-1086: Good defensive check for locked FlowSystem.The
is_lockedcheck prevents modifications after optimization, with a clear error message guiding users to callreset()first. The invalidation of model and topology caches when adding elements is appropriate.
1376-1396: LGTM - _invalidate_model cleans up all cached state.The method properly clears the model, topology cache, and element submodels/variable names when structure changes. This prevents stale state issues.
docs/user-guide/migration-guide-v5.md (1)
382-407: LGTM - Transform accessor deprecation clearly documented.The migration path from
flow_system.sel/isel/resample()toflow_system.transform.sel/isel/resample()is clearly explained with the important note that solutions are dropped on transformation.
* Updated CHangelog and migration guide
* The rolling_horizon method is now implemented on OptimizeAccessor:
API:
segments = flow_system.optimize.rolling_horizon(
solver,
horizon=100, # Timesteps per segment
overlap=0, # Lookahead timesteps
nr_of_previous_values=1, # State transfer depth
)
flow_system.solution # Combined solution (overlaps trimmed)
segments # List[FlowSystem] with individual segment solutions
Key features implemented:
1. Segment calculation - Divides timesteps into overlapping windows
2. State transfer - Transfers storage charge states and flow rates between segments
3. Solution combining - Trims overlaps and concatenates time-indexed variables
4. Effect recalculation - Correctly sums costs from combined per-timestep values
5. Investment validation - Raises error if InvestParameters are used
6. Progress bar - Shows solving progress with tqdm
Location: flixopt/optimize_accessor.py:91-399
* Summary: Rolling Horizon Optimization
The optimize.rolling_horizon() method has been implemented in flixopt/optimize_accessor.py with the following features:
API
segments = flow_system.optimize.rolling_horizon(
solver,
horizon=100, # Timesteps per segment (excluding overlap)
overlap=0, # Additional lookahead timesteps
nr_of_previous_values=1, # For uptime/downtime tracking
)
Key Features
1. Segment creation: Divides time horizon into overlapping segments using transform.isel()
2. State transfer: Transfers storage charge states and previous flow rates between segments
3. Solution combining: Trims overlaps from segment solutions and concatenates
4. Effect recomputation: Computes total effect values from combined per-timestep values (no re-solve needed)
5. Investment check: Raises error if InvestParameters are detected (not supported)
Implementation Details
- Uses _calculate_segment_indices() to compute start/end indices
- Uses _transfer_state() to pass storage charge states and flow rates to next segment
- Uses _combine_solutions() to merge segment solutions and recompute effect totals
- Progress bar shows segment progress via tqdm
* Add output supression
* Improve output supression
* Improve log message
* Improve solution concatenation
* Improve solution concatenation
* Improve solution concatenation
* Adding notebook for rolling
* Update migration guide
* Improve notebook
* Speed up notebook
* Improve notebook
* Improve notebook
* reorganize notebooks
* Update CHANGELOG.md
* Fix docs
* Fix docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
CHANGELOG.md (2)
268-273: Bus imbalance breaking change needs inline migration example.The terminology and default changes are documented, but users upgrading from v4.x who relied on the old permissive default (
1e5) need explicit guidance. Consider adding a brief example showing how to restore the old behavior.This echoes the past review comment. Add a brief inline example:
# To restore v4.x permissive balance behavior: bus = fx.Bus('MyBus', allows_imbalance=True, imbalance_penalty_per_flow_hour=1e5)
285-288: Storage charge_state structural change needs migration guidance.The dimensional change (removing the extra timestep and introducing
charge_state|final) is subtle but breaking for users parsing charge state arrays. A brief migration note would help.This echoes the past review comment. Consider adding:
**Migration**: If your code expected `charge_state` to have length `timesteps + 1`, update it to read `charge_state` (length: timesteps) and `charge_state|final` separately.docs/notebooks/10-transmission.ipynb (1)
396-399: Python version metadata mismatch with repo standard.The notebook metadata specifies Python 3.10.0, but the repository and other notebooks use Python 3.11. This should be aligned for consistency.
This echoes the past review comment. Update the metadata:
"language_info": { "name": "python", - "version": "3.10.0" + "version": "3.11.0" }
🧹 Nitpick comments (4)
docs/user-guide/migration-guide-v5.md (1)
435-446: Deprecation timeline could be clearer about removal versions.The timeline table (Lines 437-440) states that
OptimizationandResultsare "deprecated" in v5.0.0, but doesn't specify when they will be removed. The warning box says "will be removed in a future version" which is vague. Consider adding a concrete removal version (e.g., v6.0.0) for better migration planning.| Version | Status | |---------|--------| | v4.x | `Optimization` and `Results` classes available | -| v5.0.0 | `Optimization` and `Results` deprecated, new API available | +| v5.0.0 | `Optimization` and `Results` deprecated, new API available | +| v6.0.0 | `Optimization` and `Results` classes removed |docs/notebooks/08a-aggregation.ipynb (3)
42-64: Consider adding error handling and path robustness.The relative path
../../examples/resources/Zeitreihen2020.csvmay fail if users run the notebook from different directories. Additionally, there's no validation that the file exists or that the requested date range is available in the data.Consider adding:
- A check for file existence with a helpful error message
- Validation that the date range exists in the loaded data
- Optionally, use
Path(__file__).parentor document the expected working directoryExample improvement:
from pathlib import Path # Construct robust path data_file = Path(__file__).parent.parent.parent / 'examples' / 'resources' / 'Zeitreihen2020.csv' if not data_file.exists(): raise FileNotFoundError(f"Data file not found: {data_file}\nPlease ensure you're running from the repository root.") data = pd.read_csv(data_file, index_col=0, parse_dates=True).sort_index() # Validate date range try: data = data['2020-01-01':'2020-01-31 23:45:00'] except KeyError: raise ValueError("Required date range not available in data")
242-248: Consider adding error handling for optimization failures.The code assumes optimization succeeds and directly accesses
fs_resampled.solution["costs"]. If the solver fails (infeasible model, timeout, etc.), this will raise an unclear error.For a tutorial notebook, consider adding basic error handling to provide helpful feedback:
# Optimize resampled system start = timeit.default_timer() result = fs_resampled.optimize(solver) time_resampled = timeit.default_timer() - start if not result.success: # or however the API indicates failure print(f'Optimization failed: {result.message}') else: print(f'\nResampled optimization: {time_resampled:.2f} seconds') print(f'Cost: {fs_resampled.solution["costs"].item():.2f} €')
1-7: Consider clarifying clustering mention in introduction.The introduction (line 7) lists "Clustering: Identify typical periods" as one of the techniques introduced, but the notebook doesn't demonstrate clustering—it only mentions it in passing later. This might create an expectation gap for users.
Consider either:
- Removing clustering from the introduction list, or
- Adding a brief note: "Clustering (introduced conceptually, requires
tsampackage—see Further Reading)"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
CHANGELOG.md(2 hunks)README.md(1 hunks)docs/notebooks/07-scenarios-and-periods.ipynb(1 hunks)docs/notebooks/08a-aggregation.ipynb(1 hunks)docs/notebooks/10-transmission.ipynb(1 hunks)docs/notebooks/index.md(1 hunks)docs/user-guide/migration-guide-v5.md(1 hunks)docs/user-guide/support.md(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- docs/user-guide/support.md
- README.md
- docs/notebooks/index.md
🧰 Additional context used
🪛 LanguageTool
docs/user-guide/migration-guide-v5.md
[style] ~452-~452: Using many exclamation marks might seem excessive (in this case: 19 exclamation marks for a text that’s 7067 characters long)
Context: ... success "Welcome to the new flixopt API! 🎉"
(EN_EXCESSIVE_EXCLAMATION)
🪛 markdownlint-cli2 (0.18.1)
docs/user-guide/migration-guide-v5.md
165-165: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
202-202: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
257-257: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
284-284: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
356-356: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
CHANGELOG.md
66-66: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
96-96: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
113-113: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
141-141: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
159-159: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
172-172: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
190-190: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
205-205: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
231-231: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
277-277: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
⏰ 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). (5)
- GitHub Check: test (3.10)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: Build documentation
🔇 Additional comments (22)
docs/user-guide/migration-guide-v5.md (3)
1-61: Well-structured migration guide with clear examples.The introduction and Optimization API section provide clear side-by-side comparisons that will help users migrate. The two-step alternative for pre-solve model access is a helpful addition.
71-158: Results access patterns are comprehensive and well-documented.The variable naming convention section (Lines 161-195) provides an excellent reference table. The examples cover the main use cases (effects, flow rates, investment variables, status variables, storage variables).
306-349: Rolling horizon API documented correctly after previous fix.The
flow_system.optimize.rolling_horizon()method is properly documented with appropriate parameters (horizon,overlap). This addresses the previous review comment about the missingsegment()method.CHANGELOG.md (2)
54-61: Clear summary and migration guide reference for v5.0.0.The summary effectively communicates the major architectural shift to a FlowSystem-centric API, and the tip box properly links to the detailed migration guide.
401-414: Migration checklist is comprehensive and actionable.The checklist provides a clear task list for users upgrading. It appropriately covers the major API changes and references the detailed sections above.
docs/notebooks/07-scenarios-and-periods.ipynb (6)
23-31: Clean setup with proper configuration.The imports and
fx.CONFIG.notebook()call follow the recommended pattern for Jupyter notebooks.
63-78: Proper use ofstrict=Falsein zip for Python compatibility.Good practice using
strict=Falseexplicitly for thezip()calls, ensuring compatibility and clarity.
201-201: FlowSystem construction follows new API correctly.The FlowSystem initialization with
timesteps,periods,scenarios, andscenario_weightsparameters is correct. Theadd_carriers()method usage demonstrates the new Carrier system properly.
217-288: Component definitions are well-structured and demonstrate key features.The example effectively shows:
- Period-varying parameters (
effects_per_flow_hour=gas_pricesarray)- Investment optimization with
InvestParameters- Scenario-varying demand via DataFrame with scenario columns
This is a valuable pedagogical example.
432-443: Sensitivity comparison logic has a potential issue with dimensionality.Line 440 uses
chp_size.max("scenario")butchp_size_mildwas computed from a single-scenario FlowSystem and may not have a scenario dimension. The comparison(chp_size - chp_size_mild)could fail if dimensions don't align.Verify that the subtraction works correctly when
chp_sizehas a scenario dimension butchp_size_milddoes not. xarray should broadcast correctly, but this depends on the actual structure. Consider making the comparison more explicit:print(f'Planning for uncertainty adds {(chp_size.max("scenario") - chp_size_mild).round(2).values} kW capacity')
469-469: Comprehensive Key Concepts section with accurate documentation.The markdown cell provides excellent reference material covering multi-dimensional FlowSystem usage, dimension-varying parameters, and selection methods. The mathematical notation for scenario optimization is clear.
docs/notebooks/10-transmission.ipynb (5)
34-43: Clean setup with proper configuration.The imports and
fx.CONFIG.notebook()call follow the recommended pattern. Thexarrayimport is appropriate for the later visualization code.
138-139: Unidirectional transmission example is well-constructed.The example clearly demonstrates:
- Multi-bus setup for two sites
- Carrier registration with the new API
- Transmission with relative losses
- LinearConverter usage for boilers
The code follows the new FlowSystem-centric API correctly.
231-232: Bidirectional transmission example demonstrates key constraints.Good demonstration of:
in1/out1andin2/out2for bidirectional flowprevent_simultaneous_flows_in_both_directions=Trueconstraint- Time-varying electricity prices
322-323: Investment optimization example correctly uses InvestParameters.The example shows:
InvestParametersas thesizeparameter for capacity optimizationbalanced=Truefor symmetric capacity- Cost calculation based on weekly amortization
This is a practical example for infrastructure sizing.
365-366: Key Concepts section provides excellent reference documentation.The markdown clearly explains:
- Transmission component structure with code example
- Loss types with formulas
- Bidirectional vs unidirectional configurations
- Investment optimization pattern
docs/notebooks/08a-aggregation.ipynb (6)
18-33: LGTM!The imports and notebook configuration are appropriate for a tutorial demonstrating optimization techniques with time-series data.
101-206: LGTM!The system builder function is well-structured and properly parameterized, making it reusable for different time resolutions and profiles—essential for the aggregation techniques demonstrated later in the notebook.
269-299: LGTM!The two-stage optimization workflow is well-implemented: sizing with resampled data (fast) followed by full-resolution dispatch with fixed sizes. This demonstrates the proper use of the new
transform.fix_sizes()API.Note: Error handling for optimization failures would improve robustness here as well (same concern as the resampling section).
313-326: LGTM!The full optimization provides an appropriate baseline for comparing the aggregation techniques. Using
.copy()correctly preserves the original system for subsequent comparisons.
337-387: LGTM!The results comparison table effectively quantifies the speed vs. accuracy trade-offs. The relative metrics (cost gap and speedup) are correctly calculated and provide valuable insights for users deciding which technique to apply.
399-437: LGTM!The visualizations effectively demonstrate the differences between optimization approaches using the new
statistics.plotAPI. Comparing heat balance plots and showing the Sankey diagram helps users understand the energy flow patterns.
|
|
||
| # Load FlowSystem with solution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent variable name between migration guide and changelog.
Line 241 shows flow_system.solution['costs(total)'].item() but the actual variable name appears to be just 'costs' based on line 90 (flow_system.solution['costs'].item()) and the CHANGELOG examples. The (total) suffix seems incorrect.
# New (v5.0)
flow_system.optimize(solver)
-costs = flow_system.solution['costs(total)'].item()
+costs = flow_system.solution['costs'].item()Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In docs/user-guide/migration-guide-v5.md around lines 241-242, the migration
guide uses flow_system.solution['costs(total)'].item() which is inconsistent
with line 90 and the CHANGELOG that use flow_system.solution['costs'].item();
update the guide to use flow_system.solution['costs'].item() (remove the
"(total)" suffix) so the variable name matches the rest of the docs and
examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
CHANGELOG.md (2)
268-273: Add explicit inline migration snippet for Bus imbalance rename/default (currently too easy to miss).
This matches earlier review feedback: you document the rename and default change, but the Migration Checklist still only says “Useimbalance_penalty_per_flow_hour” without showing that users likely need to set bothallows_imbalance=Trueand a penalty to retain v4 behavior.Also applies to: 410-411
285-288: Storagecharge_statestructural change needs a before/after example.
This matches earlier review feedback: the breaking shape change is stated, but the checklist doesn’t show how to update common result-extraction code (timestep-aligned series vs final value incharge_state|final).Also applies to: 401-414
🧹 Nitpick comments (1)
CHANGELOG.md (1)
19-20: Changelog header format is inconsistent with your own rule (## [VERSION] - YYYY-MM-DD).
Line 19 asks to keep## [VERSION] - YYYY-MM-DD, but the new entry is## [Upcoming] - v5.0.0(no date, and “Upcoming” isn’t a version). Consider either:
## [5.0.0] - YYYY-MM-DD(and optionally an “Unreleased/Upcoming” section separately), or## [Unreleased]per Keep a Changelog, with5.0.0added once dated.Also applies to: 54-56
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
CHANGELOG.md(2 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md
66-66: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
96-96: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
113-113: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
141-141: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
159-159: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
172-172: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
190-190: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
205-205: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
231-231: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
277-277: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
⏰ 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). (5)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.10)
- GitHub Check: Build documentation
There was a problem hiding this 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
📒 Files selected for processing (1)
flixopt/flow_system.py(24 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
flixopt/flow_system.py (4)
flixopt/topology_accessor.py (2)
TopologyAccessor(111-579)infos(242-287)flixopt/transform_accessor.py (2)
TransformAccessor(26-703)sel(202-242)flixopt/carrier.py (2)
Carrier(14-125)label(107-109)flixopt/io.py (1)
convert_old_dataset(733-778)
⏰ 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). (5)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: test (3.10)
- GitHub Check: Build documentation
🔇 Additional comments (3)
flixopt/flow_system.py (3)
518-539: LGTM! Scenario metadata helper is consistent with time/period patterns.The
_update_scenario_metadatamethod correctly handles scenario weight cleanup and follows the same pattern as_update_time_metadataand_update_period_metadata. The safepop('scenario_weights', None)avoids KeyError.
580-718: LGTM! Solution and carrier serialization is well-designed.The dataset serialization correctly:
- Prefixes solution variables with
'solution|'to avoid conflicts with config variables- Renames time coordinates to
'solution_time'to preserve the full solution independently- Stores carriers as JSON metadata and restores them
- Reconnects the network post-load (line 716) to populate bus inputs/outputs
1242-1341: LGTM! New lifecycle methods and accessor pattern are well-designed.The refactored API is clean and extensible:
- Accessor pattern organizes functionality (optimize, transform, statistics, topology) with appropriate caching
- Locking mechanism (
is_lockedchecks inadd_elements/add_carriers) prevents modifications after optimization, improving safety- Lifecycle separation (
build_model→solve) enables model inspection before solving- Cache invalidation is correct (statistics cleared when solution changes line 1367, topology cleared in
_invalidate_modelline 1393)- Clustering integration in
build_model(lines 1270-1271) supports time aggregation workflows- Infeasibility handling in
solve(lines 1321-1334) with configurable extraction is helpful for debuggingAlso applies to: 1369-1466, 1468-1577
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
flixopt/flow_system.py (1)
1017-1037: Carrier key casing is inconsistent (can break lookups and auto-registration).
_register_missing_carriers()usesbus.carrieras-is for membership and as the key, butget_carrier()/flow_carriersnormalize with.lower(). This can lead to “registered but not found” (or duplicated entries) when users pass mixed-case carrier names.def _register_missing_carriers(self) -> None: """Auto-register carriers from CONFIG for buses that reference unregistered carriers.""" for bus in self.buses.values(): - if bus.carrier and bus.carrier not in self._carriers: + if not bus.carrier: + continue + carrier_key = bus.carrier.lower() + if carrier_key not in self._carriers: # Try to get from CONFIG defaults - default_carrier = getattr(CONFIG.Carriers, bus.carrier, None) + default_carrier = getattr(CONFIG.Carriers, carrier_key, None) or getattr(CONFIG.Carriers, bus.carrier, None) if default_carrier is not None: - self._carriers[bus.carrier] = default_carrier - logger.debug(f"Auto-registered carrier '{bus.carrier}' from CONFIG") + self._carriers[carrier_key] = default_carrier + logger.debug(f"Auto-registered carrier '{carrier_key}' from CONFIG")(Then you can drop repeated
.lower()calls elsewhere if you want, but this alone fixes the mismatch.)Also applies to: 1183-1194, 1218-1224
🧹 Nitpick comments (1)
flixopt/flow_system.py (1)
580-625: Harden carriers (de)serialization against malformed attrs.
json.loads(reference_structure['carriers'])will raise if the attr is missing/empty/corrupted. Given NetCDF files can be edited or partially written, a defensive parse makes this much more resilient.- if 'carriers' in reference_structure: - carriers_structure = json.loads(reference_structure['carriers']) - for carrier_data in carriers_structure.values(): - carrier = cls._resolve_reference_structure(carrier_data, {}) - flow_system._carriers.add(carrier) + carriers_raw = reference_structure.get('carriers') + if carriers_raw: + try: + carriers_structure = json.loads(carriers_raw) + except (TypeError, json.JSONDecodeError): + carriers_structure = {} + logger.warning("Ignoring malformed dataset attr 'carriers'") + for carrier_data in carriers_structure.values(): + carrier = cls._resolve_reference_structure(carrier_data, {}) + flow_system._carriers.add(carrier)Also applies to: 627-718
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
flixopt/flow_system.py(24 hunks)
⏰ 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). (5)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.10)
- GitHub Check: test (3.11)
- GitHub Check: Build documentation
🔇 Additional comments (3)
flixopt/flow_system.py (3)
1062-1111: Locking + invalidation lifecycle looks consistent and user-protective.
is_lockedgating inadd_elements()/add_carriers()+reset()/invalidate()is a clean model-edit workflow._invalidate_model()clearing_topologyand_flow_carriersaligns with the new caches and prevents stale derived state.Also applies to: 1112-1162, 1370-1467
1579-1642: Deprecation wrappers are consistent and point to the right migration targets.The wrappers emit
DeprecationWarningwithstacklevel=2and delegate to the accessor-based API, which should make v5→v6 transitions much smoother.Also applies to: 1986-2211
1695-1717: Linking elements to the FlowSystem +storagescache is a nice usability improvement.
link_to_flow_system()on add, plus a dedicatedstoragescontainer/cache (with invalidation on component/bus adds) reads straightforward and coherent with the existingflowscache behavior.Also applies to: 1816-1829
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
flixopt/flow_system.py (2)
1039-1062: Past review concern may still apply:labelvslabel_fullmismatch.Line 1049 collects
comp.labelbut line 1059-1060 uses it to indexself.components[label]. IfElementContaineris keyed bylabel_full, this works only whenlabel == label_full(true for top-level components, but potentially fragile).Consider using the component reference directly instead of re-looking it up:
- elements_without_colors = [comp.label for comp in self.components.values() if comp.color is None] + elements_without_colors = [comp for comp in self.components.values() if comp.color is None] if not elements_without_colors: return # Generate colors from the default colorscale colorscale = CONFIG.Plotting.default_qualitative_colorscale - color_mapping = process_colors(colorscale, elements_without_colors) + labels = [comp.label for comp in elements_without_colors] + color_mapping = process_colors(colorscale, labels) # Assign colors to elements - for label, color in color_mapping.items(): - self.components[label].color = color - logger.debug(f"Auto-assigned color '{color}' to component '{label}'") + for comp, color in zip(elements_without_colors, color_mapping.values()): + comp.color = color + logger.debug(f"Auto-assigned color '{color}' to component '{comp.label}'")
1276-1288: Consider adding defensive validation for_clustering_infostructure.As noted in a previous review, directly indexing
info['parameters'],info['clustering'], andinfo['components_to_clusterize']assumes the dict has the correct shape. While this is an internal method with controlled input fromtransform.cluster(), defensive validation would make debugging easier if the structure is malformed.def _add_clustering_constraints(self) -> None: """Add clustering constraints to the model.""" from .clustering import ClusteringModel info = self._clustering_info + required = {'parameters', 'clustering', 'components_to_clusterize'} + if missing := required - set(info or {}): + raise KeyError(f'_clustering_info missing required keys: {sorted(missing)}') + clustering_model = ClusteringModel(
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
flixopt/flow_system.py(24 hunks)
⏰ 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). (5)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: test (3.10)
- GitHub Check: Build documentation
🔇 Additional comments (15)
flixopt/flow_system.py (15)
7-46: LGTM!The new imports are well-organized: runtime dependencies at module level, type-only imports in
TYPE_CHECKINGblock. The_Solvertype hint correctly uses deferred import pattern.
213-238: LGTM!Good design choices: lazy initialization for caches (
_statistics,_topology,_flow_carriers), emptyCarrierContaineras default, and clear comments explaining each cache's purpose.
518-538: LGTM!The
pop('scenario_weights', None)pattern correctly handles the missing key case, addressing the previous review concern.
580-629: LGTM!Clean serialization design: solution variables get
solution|prefix to avoid conflicts, time coordinate renamed tosolution_timeto preserve full solution timeline, and carriers stored as JSON. The conditional rename on line 609 correctly handles solutions without time dimension.
705-722: LGTM!Solution restoration correctly reverses the serialization: renames
solution_timeback totime, restores carriers from JSON, and re-connects the network. The unconditionalconnect_and_transform()call ensures bus inputs/outputs are rebuilt since they're not stored in NetCDF.
852-885: LGTM!Excellent improvement over the previous approach. Using
to_dataset(include_solution=False)avoids mutating the original instance entirely, addressing the past review concern about thread safety and side effects.
1113-1162: LGTM!The docstring now correctly uses plural
carriersin the Args section and the example uses the correct method nameadd_carriers(). Good defensive programming withis_lockedcheck and model invalidation.
1322-1335: LGTM!Both previous review concerns are addressed:
- Termination condition now uses explicit tuple membership check
in ('infeasible', 'infeasible_or_unbounded')instead of substring matching- Typo fixed: "Successfully" (line 1334)
1378-1399: LGTM!Cache invalidation is now complete:
_flow_carriersis cleared (line 1395) alongside_topology, addressing the previous review concern about stale cache after structural changes.
1580-1600: LGTM!The
plot_network()method now properly emits aDeprecationWarningwithstacklevel=2, consistent with other deprecated methods in this file.
772-850: LGTM!Good migration utility for backward compatibility. The warning clearly documents limitations (solution variable names not renamed, advanced access may require original names). The JSON conversion for solution attrs (lines 845-847) ensures consistency with the new format.
1470-1578: LGTM!Good design: stateless accessors (
optimize,transform) create fresh instances, while stateful accessors (statistics,topology) are cached and invalidated appropriately. Thestatisticscache is cleared via thesolutionsetter, andtopologyis cleared in_invalidate_model().
1987-2211: LGTM!Consistent deprecation pattern: all legacy methods emit
DeprecationWarningwithstacklevel=2and cleanly delegate to the newTransformAccessorimplementations. Docstrings properly document the deprecation with the alternative to use.
1817-1829: LGTM!The
storagesproperty follows the same lazy-caching pattern asflows, with proper cache invalidation in_add_componentsand_add_buses. Sorting bylabel_full.lower()ensures reproducible ordering.
1696-1717: LGTM!Consistent pattern: all element types call
link_to_flow_system(self)before being added to their container. This establishes the bidirectional reference and ensures elements know their parent FlowSystem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
flixopt/flow_system.py (2)
1043-1066: Bug:_assign_element_colors()likely keys container incorrectly (labelvslabel_full).
This can throwKeyErroror assign colors to the wrong component ifElementContaineris keyed bylabel_full.def _assign_element_colors(self) -> None: @@ - elements_without_colors = [comp.label for comp in self.components.values() if comp.color is None] + elements_without_colors = [comp.label_full for comp in self.components.values() if comp.color is None] @@ - for label, color in color_mapping.items(): - self.components[label].color = color - logger.debug(f"Auto-assigned color '{color}' to component '{label}'") + for label_full, color in color_mapping.items(): + self.components[label_full].color = color + logger.debug(f"Auto-assigned color '{color}' to component '{label_full}'")
1247-1293: Guard_clustering_infoshape before indexing.
Currentlyinfo['parameters'], etc. can throwKeyErrordeep in optimization. Validate early with a clear error.def _add_clustering_constraints(self) -> None: """Add clustering constraints to the model.""" from .clustering import ClusteringModel - info = self._clustering_info + info = self._clustering_info or {} + required = {'parameters', 'clustering', 'components_to_clusterize'} + missing = required - set(info) + if missing: + raise KeyError(f'_clustering_info missing keys: {sorted(missing)}') clustering_model = ClusteringModel( model=self.model, clustering_parameters=info['parameters'], flow_system=self, clustering_data=info['clustering'], components_to_clusterize=info['components_to_clusterize'], )
🧹 Nitpick comments (3)
flixopt/flow_system.py (3)
1028-1042: Carrier auto-registration likely bypassesCarrierContainerinvariants.
Usingself._carriers[carrier_key] = default_carrierassumesCarrierContainerbehaves like a plain dict and that key normalization is correct. Preferself._carriers.add(default_carrier)(and ensure.add()normalizes name casing) to avoid mismatched keys vscarrier.name.
1168-1231: Carrier lookup/mapping API is useful; ensure label expectations are documented.
get_carrier()treats the argument as either a bus label or a flow label; consider explicitly stating it expectslabel_full(or supporting both) to avoid surprises.
1474-1583: Accessor exposure pattern is clean; consider caching consistency.
statistics/topologyare cached,optimize/transformaren’t—seems intentional; just ensure accessors are lightweight or make caching consistent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
flixopt/flow_system.py(23 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
flixopt/flow_system.py (4)
flixopt/topology_accessor.py (2)
TopologyAccessor(111-579)infos(242-287)flixopt/transform_accessor.py (5)
TransformAccessor(26-703)_dataset_sel(348-395)sel(202-242)isel(244-284)resample(286-343)flixopt/carrier.py (3)
Carrier(14-125)CarrierContainer(128-159)label(107-109)flixopt/io.py (2)
convert_old_dataset(733-778)load_dataset_from_netcdf(571-597)
⏰ 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). (5)
- GitHub Check: test (3.10)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: Build documentation
🔇 Additional comments (16)
flixopt/flow_system.py (16)
7-45: New imports/accessors/carriers wiring looks consistent.
No issues spotted with the added imports/types in this file.
164-240: State/caching initialization is clear; consider cache consistency aroundname.
namebeing mutable and overwritten on save/load is intentional per docstrings—just ensure callers don’t rely on a manually set name persisting acrossto_netcdf().
519-540: Scenario metadata update: safe pop is good.
dataset.attrs.pop('scenario_weights', None)correctly avoids KeyError for missing attrs and keeps scenario subsets consistent.
581-634:to_dataset(include_solution=...)design is solid; one edge-case to verify.
Renaming solutiontime→solution_timeis a good conflict-avoidance strategy; please verify behavior when solution has atimecoordinate but not atimedimension (and/or already hassolution_time).
635-728:from_dataset()eagerlyconnect_and_transform()can be surprising.
Reconnecting makes sense because bus inputs/outputs aren’t stored, but it also introduces side effects and can raise if the dataset is invalid. Worth confirming this is desired for all dataset loads (including “template” datasets).
729-775:to_netcdf()/from_netcdf()name derivation: verify base-class signature.
super().to_netcdf(path, compression, overwrite)assumes positional parameters match the base implementation; please confirmInterface.to_netcdf()has the same order/types.
776-855:from_old_results()migration path is pragmatic; watch attr normalization.
Converting dict attrs to JSON strings for specific keys is reasonable—just confirm downstream readers consistently expect JSON strings insolution.attrs[...](not dicts) after this path.
856-890:copy()semantics look good (no solution copied).
Usingto_dataset(include_solution=False)avoids mutating the original instance and keeps copying simple.
987-1027:connect_and_transform()lifecycle + integrity validation is a good improvement.
The explicit idempotency notes and early cross-element validation reduce late model-build failures.
1067-1116: Lockingadd_elements()when solution exists is a good guardrail.
The invalidation behavior when model exists / already transformed is also consistent with the new lifecycle.
1117-1167:add_carriers()locking + invalidation is consistent withadd_elements().
Docstring/example/signature alignment looks corrected.
1294-1347: Infeasibility handling is improved; verify termination-condition typing across linopy versions.
The explicit tuple check avoids substring pitfalls; please confirmself.model.termination_conditionis always a string (or handle enum values as well).
1348-1473: Solution/lock/reset/invalidate lifecycle looks coherent.
Reset clears solution (unlock) and invalidates, while invalidate blocks when locked—this is a clean mental model.
1584-1647: Deprecation wrappers are consistent and actionable.
ClearDeprecationWarningmessages + redirect to accessors is exactly what downstream users need.
1700-1722: Linking elements to FlowSystem + cache invalidation is correct.
Good to see_storages_cacheinvalidated alongside_flows_cache.
1821-1834:storagescached container is a nice usability improvement.
Sorting bylabel_fullfor reproducibility is a good touch.
There was a problem hiding this 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
🧹 Nitpick comments (1)
flixopt/flow_system.py (1)
724-726: Consider makingconnect_and_transform()optional infrom_dataset().Line 725 unconditionally calls
connect_and_transform(), which performs network connection, carrier registration, color assignment, and data transformation. If the caller plans to modify the FlowSystem before using it (e.g., adding more elements), this is wasted work.Consider either:
- Removing this call and documenting that callers should call it explicitly when needed
- Adding a
connectparameter to control this behaviorOption 1: Remove automatic call (simpler, matches
__init__behavior)# Restore carriers if present if 'carriers' in reference_structure: carriers_structure = json.loads(reference_structure['carriers']) for carrier_data in carriers_structure.values(): carrier = cls._resolve_reference_structure(carrier_data, {}) flow_system._carriers.add(carrier) - - # Reconnect network to populate bus inputs/outputs (not stored in NetCDF). - flow_system.connect_and_transform() return flow_systemUpdate docstring to note:
+ Note: + The returned FlowSystem is not connected. Call `connect_and_transform()` + or `build_model()` before optimizing.Option 2: Add parameter for control
@classmethod - def from_dataset(cls, ds: xr.Dataset) -> FlowSystem: + def from_dataset(cls, ds: xr.Dataset, connect: bool = True) -> FlowSystem: """ ... + Args: + ds: Dataset containing the FlowSystem data + connect: If True, call connect_and_transform() before returning """ ... - # Reconnect network to populate bus inputs/outputs (not stored in NetCDF). - flow_system.connect_and_transform() + if connect: + flow_system.connect_and_transform() return flow_system
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
flixopt/flow_system.py(23 hunks)
⏰ 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). (5)
- GitHub Check: test (3.10)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: Build documentation
🔇 Additional comments (5)
flixopt/flow_system.py (5)
519-539: LGTM: Safe attribute removal.The method correctly uses
.pop('scenario_weights', None)to safely remove the attribute when scenarios are not multi-valued, avoiding KeyError exceptions.
856-889: LGTM: Clean copy implementation without mutation.The copy methods correctly exclude the solution using
include_solution=Falseand avoid mutating the original instance. The__copy__and__deepcopy__implementations properly delegate to the maincopy()method.
1028-1067: LGTM: Carrier registration and color assignment.The new helper methods correctly:
- Try both original and lowercase carrier names when auto-registering from CONFIG
- Use
label_fullconsistently for element keying (avoiding KeyError risks)- Provide good debug logging for troubleshooting
1118-1232: LGTM: Carrier management with proper locking and caching.The carrier-related methods correctly:
- Check
is_lockedbefore allowing modifications- Invalidate caches appropriately (including
_flow_carriersin_invalidate_model)- Build the carrier mapping lazily with proper runtime checks
- Use consistent lowercase keying for carrier lookups
1997-2184: LGTM: Comprehensive deprecation strategy.The deprecated methods (
_dataset_sel,_dataset_isel,_dataset_resample,_resample_by_dimension_groups,sel,isel,resample) correctly:
- Emit clear deprecation warnings with version information
- Redirect to the new
TransformAccessorimplementations- Maintain backward compatibility during the transition period
| path = pathlib.Path(path) | ||
| # Set name from filename (without extension) | ||
| self.name = path.stem | ||
|
|
||
| super().to_netcdf(path, compression, overwrite) | ||
| logger.info(f'Saved FlowSystem to {path}') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to_netcdf() mutates self.name as a side effect.
Line 751 sets self.name = path.stem, which modifies the FlowSystem instance during a save operation. This is unexpected behavior for a serialization method.
Consider alternatives:
- Only set name if it's currently None (preserve existing names)
- Store the name in the dataset without mutating self (read-only save)
- Document this behavior prominently if it's intentional
Option 1: Preserve existing names (recommended)
path = pathlib.Path(path)
- # Set name from filename (without extension)
- self.name = path.stem
+ # Set name from filename only if not already set
+ if self.name is None:
+ self.name = path.stemOption 2: Store without mutating (more pure)
path = pathlib.Path(path)
- # Set name from filename (without extension)
- self.name = path.stem
-
- super().to_netcdf(path, compression, overwrite)
+ # Temporarily store filename as name for serialization
+ ds = self.to_dataset()
+ ds.attrs['name'] = path.stem
+ # Use Interface._save_dataset directly instead of super().to_netcdf()
+ self._save_dataset(ds, path, compression, overwrite)🤖 Prompt for AI Agents
In flixopt/flow_system.py around lines 749 to 754, to_netcdf() currently mutates
self.name by unconditionally assigning path.stem; change this so the method does
not unexpectedly modify the object: only set self.name = path.stem if self.name
is currently None (preserve existing names), otherwise leave self.name
untouched; alternatively, if you prefer not to modify the instance at all,
remove the assignment and ensure the filename is written into the saved dataset
metadata instead. Ensure tests cover both existing-name and no-name cases.
* drop python 3.10 support and add 3.14 * Exclude gurobi from py314 * scipy==1.16.3 in dev for python 3.14 * Fix tests without gurobi installed (py314)
Description
This PR collects and sanitizes the commit history for a clean merge into main for flixopt 5.0.0
Type of Change
Related Issues
Closes #(issue number)
Testing
Checklist
Summary by CodeRabbit
New Features
Documentation
Breaking Changes
✏️ Tip: You can customize this high-level summary in your review settings.