Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c402d57
First iteration
FBumann Sep 12, 2025
e707d0a
First iteration
FBumann Sep 12, 2025
3863102
Few tests with actual prices instead of amounts
FBumann Sep 12, 2025
ca6424f
Update to simply use amounts rather than prices
FBumann Sep 12, 2025
69791d7
Improve docstring
FBumann Sep 12, 2025
10635f8
Complete Test
FBumann Sep 12, 2025
a0a5785
ruff check
FBumann Sep 12, 2025
3a88a59
Add new feature to IO test
FBumann Sep 12, 2025
c4db3b7
Add Test that checks for optional shares Modeling
FBumann Sep 12, 2025
12fdae7
Improve docstring
FBumann Sep 12, 2025
bcca8a2
Improve docstring
FBumann Sep 12, 2025
1914b70
Update Changelog
FBumann Sep 12, 2025
ed8a8f2
Improve docstrings and add warning for Bugfix
FBumann Sep 12, 2025
cc25934
Update CHANGELOG.md
FBumann Sep 12, 2025
6d73883
Moce docstrinf from init to class
FBumann Sep 12, 2025
5ef56a5
Update docstring of LinearConverter
FBumann Sep 12, 2025
d5e8e24
Move docstring to class
FBumann Sep 12, 2025
14505bb
Fix corrupted test
FBumann Sep 12, 2025
dd6ea15
Merge branch 'main' into feature/300-piecewiseeffects-for-flows
FBumann Sep 12, 2025
fb13157
Merge branch 'main' into feature/300-piecewiseeffects-for-flows
FBumann Sep 12, 2025
f7658dd
Merge remote-tracking branch 'origin/feature/300-piecewiseeffects-for…
FBumann Sep 12, 2025
380907e
Merge branch 'main' into feature/300-piecewiseeffects-for-flows
FBumann Sep 12, 2025
ca1a9f0
ruff format and lint
FBumann Sep 12, 2025
109c6ab
Merge branch 'main' into feature/300-piecewiseeffects-for-flows
FBumann Sep 13, 2025
454fe19
Update Changelog
FBumann Sep 13, 2025
bd40414
Update Changelog
FBumann Sep 13, 2025
fc71d85
Allow rc releases
FBumann Sep 13, 2025
ffd6b92
Merge remote-tracking branch 'origin/main' into feature/300-piecewise…
FBumann Sep 13, 2025
5ae6b05
Merge branch 'main' into feature/300-piecewiseeffects-for-flows
FBumann Sep 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 50 additions & 3 deletions .github/workflows/python-app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,58 @@ jobs:
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Extract release notes
- name: Check if pre-release
id: prerelease
run: |
if [[ "${{ github.ref }}" =~ (alpha|beta|rc) ]]; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
echo "✅ Detected pre-release"
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
echo "✅ Detected stable release"
fi

Comment on lines +145 to +155
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Unify and harden pre-release detection; drive GH Release flag from this output

  • Use GITHUB_REF_NAME (already provided by Actions) instead of the full ref.
  • Tighten the match to cover both PEP 440 (e.g., 2.2.0rc1) and hyphenated semver (e.g., 2.2.0-rc.1).
  • Make this the single source of truth and consume it in the GH release step to avoid drift.
-      - name: Check if pre-release
+      - name: Check if pre-release
         id: prerelease
         run: |
-          if [[ "${{ github.ref }}" =~ (alpha|beta|rc) ]]; then
+          # Use ref name (e.g., v2.2.0, v2.2.0rc1, v2.2.0-rc.1)
+          REF="${GITHUB_REF_NAME}"
+          if [[ "$REF" =~ (alpha|beta|rc) ]] || [[ "$REF" =~ -?(a|b|rc)[0-9]+ ]]; then
             echo "is_prerelease=true" >> $GITHUB_OUTPUT
             echo "✅ Detected pre-release"
           else
             echo "is_prerelease=false" >> $GITHUB_OUTPUT
             echo "✅ Detected stable release"
           fi

Follow-up (in “Create GitHub Release” step): set prerelease: ${{ steps.prerelease.outputs.is_prerelease == 'true' }} so both places use the same signal.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Check if pre-release
id: prerelease
run: |
if [[ "${{ github.ref }}" =~ (alpha|beta|rc) ]]; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
echo "✅ Detected pre-release"
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
echo "✅ Detected stable release"
fi
- name: Check if pre-release
id: prerelease
run: |
# Use ref name (e.g., v2.2.0, v2.2.0rc1, v2.2.0-rc.1)
REF="${GITHUB_REF_NAME}"
if [[ "$REF" =~ (alpha|beta|rc) ]] || [[ "$REF" =~ -?(a|b|rc)[0-9]+ ]]; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
echo "✅ Detected pre-release"
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
echo "✅ Detected stable release"
fi
🤖 Prompt for AI Agents
.github/workflows/python-app.yaml around lines 145-155: the prerelease detection
currently inspects the full ref and uses a loose match; replace it to read
GITHUB_REF_NAME (already provided by Actions), test against a tightened regex
that matches both PEP 440 (e.g. 2.2.0rc1) and hyphenated semver pre-release
labels (e.g. 2.2.0-rc.1), and emit a single boolean output is_prerelease
(true/false) to GITHUB_OUTPUT as the canonical signal; then in the "Create
GitHub Release" step consume that output with prerelease: ${{
steps.prerelease.outputs.is_prerelease == 'true' }} so the GH release flag is
driven by this single source of truth.

- name: Create release notes
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "Extracting release notes for version: $VERSION"
python scripts/extract_release_notes.py $VERSION > current_release_notes.md
echo "Creating release notes for version: $VERSION"

if [[ "${{ steps.prerelease.outputs.is_prerelease }}" == "true" ]]; then
echo "📝 Generating pre-release notes"
cat > current_release_notes.md << EOF
# Pre-release $VERSION

This is a pre-release version for testing and feedback.

## Installation
\`\`\`bash
pip install flixopt==$VERSION --pre
\`\`\`

## What's Changed
See the [unreleased section](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md#unreleased) in the changelog for upcoming features and changes.

## Feedback
Please report any issues or feedback on [GitHub Issues](https://github.com/flixOpt/flixopt/issues).
EOF
else
Comment on lines +161 to +179
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Heredoc indentation makes headings render as code blocks in release notes

YAML indentation is persisted inside the heredoc, so “# Pre-release …” is emitted with 10 leading spaces and shows up as a code block on GitHub. Strip the indent or use a tab-stripping heredoc.

Minimal fix (strip the 10-space indent after write):

           if [[ "${{ steps.prerelease.outputs.is_prerelease }}" == "true" ]]; then
             echo "📝 Generating pre-release notes"
-            cat > current_release_notes.md << EOF
+            cat > current_release_notes.md << EOF
           # Pre-release $VERSION

           This is a pre-release version for testing and feedback.

           ## Installation
           \`\`\`bash
           pip install flixopt==$VERSION --pre
           \`\`\`

           ## What's Changed
           See the [unreleased section](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md#unreleased) in the changelog for upcoming features and changes.

           ## Feedback
           Please report any issues or feedback on [GitHub Issues](https://github.com/flixOpt/flixopt/issues).
           EOF
+          # Remove YAML indentation (10 spaces) to render Markdown headings correctly
+          sed -i 's/^          //' current_release_notes.md

Alternative (no sed): use a tab-indented heredoc with stripping:

-            cat > current_release_notes.md << EOF
+            cat > current_release_notes.md <<- EOF

…and prefix all heredoc content lines with a single tab instead of spaces.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [[ "${{ steps.prerelease.outputs.is_prerelease }}" == "true" ]]; then
echo "📝 Generating pre-release notes"
cat > current_release_notes.md << EOF
# Pre-release $VERSION
This is a pre-release version for testing and feedback.
## Installation
\`\`\`bash
pip install flixopt==$VERSION --pre
\`\`\`
## What's Changed
See the [unreleased section](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md#unreleased) in the changelog for upcoming features and changes.
## Feedback
Please report any issues or feedback on [GitHub Issues](https://github.com/flixOpt/flixopt/issues).
EOF
else
if [[ "${{ steps.prerelease.outputs.is_prerelease }}" == "true" ]]; then
echo "📝 Generating pre-release notes"
cat > current_release_notes.md << EOF
# Pre-release $VERSION
This is a pre-release version for testing and feedback.
## Installation
\`\`\`bash
pip install flixopt==$VERSION --pre
\`\`\`
## What's Changed
See the [unreleased section](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md#unreleased) in the changelog for upcoming features and changes.
## Feedback
Please report any issues or feedback on [GitHub Issues](https://github.com/flixOpt/flixopt/issues).
EOF
# Remove YAML indentation (10 spaces) to render Markdown headings correctly
sed -i 's/^ //' current_release_notes.md
else
🤖 Prompt for AI Agents
In .github/workflows/python-app.yaml around lines 158-176 the heredoc is
indented so the generated release notes include leading spaces (rendering
headings as code); fix by using a tab-stripping heredoc (change the opener to
<<-EOF and prefix each heredoc line with a single tab) so YAML indentation is
preserved but the written file has no leading spaces, or if you prefer a minimal
patch keep the current heredoc and run a strip step after writing (e.g., sed to
remove the 10-space prefix from current_release_notes.md) to remove the unwanted
indentation.

echo "📝 Extracting release notes from changelog"
if python scripts/extract_release_notes.py "$VERSION" > current_release_notes.md 2>/dev/null; then
echo "✅ Successfully extracted release notes"
else
echo "⚠️ No release notes found, using fallback"
cat > current_release_notes.md << EOF
# Release $VERSION

See the [full changelog](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md) for details.
EOF
fi
fi

echo "Generated release notes:"
echo "========================"
cat current_release_notes.md
echo "========================"

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Take Care: The CI will automatically append a "Whats CHanged" section to the cha
This contains all COmmits, PR's and Contributers.
Therefore, the Changelog should focus on the user-facing changes.

Template:
----
## [Unreleased] - ????-??-??

### Changed
Expand All @@ -24,8 +26,22 @@ Therefore, the Changelog should focus on the user-facing changes.

### Development

----
Upcoming Release:

## [2.2.0] - 2025-09-13
THis release introduces a new interface `PiecewiseEffectsPerFlowHour` to model non-linear relations between flow rates and effects.
This greatly enhances Model flexibility.

### Fixed
- LinearConverter with `PiecewiseConversion` allowed flows to reach 0 values, even though they didnt have `OnOffParameters` nor `PiecewiseConversion` actually containing 0 in its `Piece`s.

### Added
- Added new Interface `PiecewiseEffectsPerFlowHour` to model non-linear relations between flow rates and effects.

Until here -->


## [2.1.7] - 2025-09-13

This update is a maintenance release to improve Code Quality, CI and update the dependencies.
Expand Down
1 change: 1 addition & 0 deletions flixopt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
Piecewise,
PiecewiseConversion,
PiecewiseEffects,
PiecewiseEffectsPerFlowHour,
SegmentedCalculation,
Sink,
Source,
Expand Down
11 changes: 10 additions & 1 deletion flixopt/commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@
from .effects import Effect
from .elements import Bus, Flow
from .flow_system import FlowSystem
from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects
from .interface import (
InvestParameters,
OnOffParameters,
Piece,
Piecewise,
PiecewiseConversion,
PiecewiseEffects,
PiecewiseEffectsPerFlowHour,
)

__all__ = [
'TimeSeriesData',
Expand Down Expand Up @@ -48,4 +56,5 @@
'results',
'linear_converters',
'solvers',
'PiecewiseEffectsPerFlowHour',
]
129 changes: 112 additions & 17 deletions flixopt/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,119 @@

@register_class_for_io
class LinearConverter(Component):
"""
Converts input-Flows into output-Flows via linear conversion factors
"""Convert input flows into output flows using linear or piecewise linear conversion factors.

This component models conversion equipment where input flows are transformed
into output flows with fixed or variable conversion ratios, such as:

- Heat pumps and chillers with variable efficiency
- Power plants with fuel-to-electricity conversion
- Chemical processes with multiple inputs/outputs
- Pumps and compressors
- Combined heat and power (CHP) plants

Args:
label: Unique identifier for the component in the FlowSystem.
inputs: List of input Flow objects that feed into the converter.
outputs: List of output Flow objects produced by the converter.
on_off_parameters: Controls binary on/off behavior of the converter.
When specified, the component can be completely turned on or off, affecting
all connected flows. This creates binary variables in the optimization.
For better performance, consider using OnOffParameters on individual flows instead.
conversion_factors: Linear conversion ratios between flows as time series data.
List of dictionaries mapping flow labels to their conversion factors.
Mutually exclusive with piecewise_conversion.
piecewise_conversion: Piecewise linear conversion relationships between flows.
Enables modeling of variable efficiency or discrete operating modes.
Mutually exclusive with conversion_factors.
meta_data: Additional information stored with the component.
Saved in results but not used internally. Use only Python native types.

Warning:
When using `piecewise_conversion` without `on_off_parameters`, flow rates cannot
reach zero unless explicitly defined with zero-valued pieces (e.g., `fx.Piece(0, 0)`).
This prevents unintended zero flows and maintains mathematical consistency.

To allow zero flow rates:

- Add `on_off_parameters` to enable complete shutdown, or
- Include explicit zero pieces in your `piecewise_conversion` definition

This behavior was clarified in v2.1.7 to prevent optimization edge cases.

Examples:
Simple heat pump with fixed COP:

```python
heat_pump = fx.LinearConverter(
label='heat_pump',
inputs=[electricity_flow],
outputs=[heat_flow],
conversion_factors=[
{
'electricity_flow': 1.0, # 1 kW electricity input
'heat_flow': 3.5, # 3.5 kW heat output (COP=3.5)
}
],
)
```

Variable efficiency heat pump:

```python
heat_pump = fx.LinearConverter(
label='variable_heat_pump',
inputs=[electricity_flow],
outputs=[heat_flow],
piecewise_conversion=fx.PiecewiseConversion(
{
'electricity_flow': fx.Piecewise(
[
fx.Piece(0, 10), # Allow zero to 10 kW input
fx.Piece(10, 25), # Higher load operation
]
),
'heat_flow': fx.Piecewise(
[
fx.Piece(0, 35), # COP=3.5 at low loads
fx.Piece(35, 75), # COP=3.0 at high loads
]
),
}
),
)
```

Combined heat and power plant:

```python
chp_plant = fx.LinearConverter(
label='chp_plant',
inputs=[natural_gas_flow],
outputs=[electricity_flow, heat_flow],
conversion_factors=[
{
'natural_gas_flow': 1.0, # 1 MW fuel input
'electricity_flow': 0.4, # 40% electrical efficiency
'heat_flow': 0.45, # 45% thermal efficiency
}
],
on_off_parameters=fx.OnOffParameters(
min_on_hours=4, # Minimum 4-hour operation
min_off_hours=2, # Minimum 2-hour downtime
),
)
```

Note:
Either `conversion_factors` or `piecewise_conversion` must be specified, but not both.
The component automatically handles the mathematical relationships between all
connected flows according to the specified conversion ratios.

See Also:
PiecewiseConversion: For variable efficiency modeling
OnOffParameters: For binary on/off control
Flow: Input and output flow definitions
"""

def __init__(
Expand All @@ -39,21 +149,6 @@ def __init__(
piecewise_conversion: Optional[PiecewiseConversion] = None,
meta_data: Optional[Dict] = None,
):
"""
Args:
label: The label of the Element. Used to identify it in the FlowSystem
inputs: The input Flows
outputs: The output Flows
on_off_parameters: Information about on and off state of LinearConverter.
Component is On/Off, if all connected Flows are On/Off. This induces an On-Variable (binary) in all Flows!
If possible, use OnOffParameters in a single Flow instead to keep the number of binary variables low.
See class OnOffParameters.
conversion_factors: linear relation between flows.
Either 'conversion_factors' or 'piecewise_conversion' can be used!
piecewise_conversion: Define a piecewise linear relation between flow rates of different flows.
Either 'conversion_factors' or 'piecewise_conversion' can be used!
meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
"""
super().__init__(label, inputs, outputs, on_off_parameters, meta_data=meta_data)
self.conversion_factors = conversion_factors or []
self.piecewise_conversion = piecewise_conversion
Expand Down
24 changes: 22 additions & 2 deletions flixopt/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from .config import CONFIG
from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeriesCollection
from .effects import EffectValuesUser
from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel
from .interface import InvestParameters, OnOffParameters
from .features import InvestmentModel, OnOffModel, PiecewiseEffectsPerFlowHourModel, PreventSimultaneousUsageModel
from .interface import InvestParameters, OnOffParameters, PiecewiseEffectsPerFlowHour
from .structure import Element, ElementModel, SystemModel, register_class_for_io

if TYPE_CHECKING:
Expand Down Expand Up @@ -159,6 +159,7 @@ def __init__(
relative_minimum: NumericDataTS = 0,
relative_maximum: NumericDataTS = 1,
effects_per_flow_hour: Optional[EffectValuesUser] = None,
piecewise_effects_per_flow_hour: Optional[PiecewiseEffectsPerFlowHour] = None,
on_off_parameters: Optional[OnOffParameters] = None,
flow_hours_total_max: Optional[Scalar] = None,
flow_hours_total_min: Optional[Scalar] = None,
Expand All @@ -180,6 +181,7 @@ def __init__(
def: :math:`load\_factor:= sumFlowHours/ (nominal\_val \cdot \Delta t_{tot})`
load_factor_max: maximal load factor (see minimal load factor)
effects_per_flow_hour: operational costs, costs per flow-"work"
piecewise_effects_per_flow_hour: piecewise relation between flow hours and effects
on_off_parameters: If present, flow can be "off", i.e. be zero (only relevant if relative_minimum > 0)
Therefore a binary var "on" is used. Further, several other restrictions and effects can be modeled
through this On/Off State (See OnOffParameters)
Expand Down Expand Up @@ -207,6 +209,7 @@ def __init__(
self.load_factor_max = load_factor_max
# self.positive_gradient = TimeSeries('positive_gradient', positive_gradient, self)
self.effects_per_flow_hour = effects_per_flow_hour if effects_per_flow_hour is not None else {}
self.piecewise_effects_per_flow_hour = piecewise_effects_per_flow_hour
self.flow_hours_total_max = flow_hours_total_max
self.flow_hours_total_min = flow_hours_total_min
self.on_off_parameters = on_off_parameters
Expand Down Expand Up @@ -248,6 +251,8 @@ def transform_data(self, flow_system: 'FlowSystem'):
self.effects_per_flow_hour = flow_system.create_effect_time_series(
self.label_full, self.effects_per_flow_hour, 'per_flow_hour'
)
if self.piecewise_effects_per_flow_hour is not None:
self.piecewise_effects_per_flow_hour.transform_data(flow_system, self.label_full)
if self.on_off_parameters is not None:
self.on_off_parameters.transform_data(flow_system, self.label_full)
if isinstance(self.size, InvestParameters):
Expand Down Expand Up @@ -398,6 +403,21 @@ def _create_shares(self):
target='operation',
)

if self.element.piecewise_effects_per_flow_hour is not None:
self.piecewise_effects = self.add(
PiecewiseEffectsPerFlowHourModel(
model=self._model,
label_of_element=self.label_of_element,
piecewise_origin=(
self.flow_rate.name,
self.element.piecewise_effects_per_flow_hour.piecewise_flow_rate,
),
piecewise_shares=self.element.piecewise_effects_per_flow_hour.piecewise_shares,
zero_point=self.on_off.on if self.on_off is not None else False,
),
)
self.piecewise_effects.do_modeling()

Comment on lines +406 to +420
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Zero-point handling risks infeasibility when On/Off is absent.

Passing False forces “exactly one segment active,” disallowing zero unless 0 is inside a piece. That can make otherwise valid flows (min=0) infeasible. Recommend allowing zero by default when no On/Off is present.

-                    zero_point=self.on_off.on if self.on_off is not None else False,
+                    zero_point=self.on_off.on if self.on_off is not None else True,

Would you prefer a Flow-level flag (e.g., piecewise_zero_allowed_when_off: bool = True) to make this explicit?


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo "Listing flixopt module files (top-level):"
ls -la flixopt || true
echo
echo "Search for Piecewise* classes / identifiers..."
rg -n --hidden --no-ignore-vcs "PiecewiseEffectsPerFlowHourModel" -S || true
rg -n --hidden --no-ignore-vcs "PiecewiseEffects" -S || true
rg -n --hidden --no-ignore-vcs "piecewise_effects_per_flow_hour" -S || true
rg -n --hidden --no-ignore-vcs "piecewise" -S || true
echo
echo "Search for 'zero_point' usages:"
rg -n --hidden --no-ignore-vcs "zero_point" -S || true
echo
echo "Show flixopt/elements.py around the snippet (lines ~390-430):"
if [ -f flixopt/elements.py ]; then
  nl -ba flixopt/elements.py | sed -n '390,430p' || true
else
  echo "flixopt/elements.py not found"
fi
echo
echo "Search for classes with 'Piecewise' in their name (to find model implementation):"
rg -n --hidden --no-ignore-vcs "class .*Piecewise" -S || true
echo
echo "Search for constructor parameters that mention zero_point (def __init__ ... zero_point):"
rg -n --hidden --no-ignore-vcs "def __init__\([^\)]*zero_point" -S || true
echo
echo "Search for 'on_off' near piecewise-related code:"
rg -n --hidden --no-ignore-vcs "on_off" -S || true

Length of output: 53460


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== features.py 820-920 ==="
sed -n '820,920p' flixopt/features.py || true

echo
echo "=== features.py 1040-1140 ==="
sed -n '1040,1140p' flixopt/features.py || true

echo
echo "=== elements.py 392-422 ==="
sed -n '392,422p' flixopt/elements.py || true

echo
echo "=== components.py 528-540 ==="
sed -n '528,540p' flixopt/components.py || true

echo
echo "=== tests/test_linear_converter.py 420-440 ==="
sed -n '420,440p' tests/test_linear_converter.py || true

Length of output: 11083


Allow zero when On/Off is absent — fix in PiecewiseModel, not by passing True at call sites

Confirmed: call sites (e.g. flixopt/elements.py) pass False when no On/Off; PiecewiseModel.do_modeling treats non-True/non-variable as sum(insides) == 1, forcing a segment and making flows with min=0 infeasible. Switching call-sites to True would create an unconstrained binary zero_point (undesirable).

Recommended minimal fix: in flixopt/features.py -> PiecewiseModel.do_modeling change the else branch from
sign, rhs = '=', 1
to
sign, rhs = '<=', 1
so zero (no active segment) is allowed by default. Keep call-sites as zero_point=self.on_off.on if present. Review related call-sites: flixopt/elements.py, flixopt/components.py, flixopt/linear_converters.py.

Would you prefer an explicit Flow-level flag (e.g. piecewise_zero_allowed_when_off: bool) instead of changing the default?

def _create_bounds_for_load_factor(self):
# TODO: Add Variable load_factor for better evaluation?

Expand Down
Loading