flixopt 2.1.7__tar.gz → 2.2.0rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flixopt might be problematic. Click here for more details.

Files changed (101) hide show
  1. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/workflows/python-app.yaml +57 -7
  2. {flixopt-2.1.7 → flixopt-2.2.0rc2}/CHANGELOG.md +20 -1
  3. {flixopt-2.1.7/flixopt.egg-info → flixopt-2.2.0rc2}/PKG-INFO +1 -1
  4. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/__init__.py +1 -0
  5. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/commons.py +10 -1
  6. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/components.py +112 -17
  7. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/elements.py +22 -2
  8. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/features.py +70 -7
  9. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/interface.py +267 -8
  10. {flixopt-2.1.7 → flixopt-2.2.0rc2/flixopt.egg-info}/PKG-INFO +1 -1
  11. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/conftest.py +13 -1
  12. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_flow.py +161 -0
  13. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_linear_converter.py +1 -1
  14. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/CONTRIBUTING.md +0 -0
  15. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  16. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  17. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  18. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/ISSUE_TEMPLATE/general_issue.yml +0 -0
  19. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.github/pull_request_template.md +0 -0
  20. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.gitignore +0 -0
  21. {flixopt-2.1.7 → flixopt-2.2.0rc2}/.pre-commit-config.yaml +0 -0
  22. {flixopt-2.1.7 → flixopt-2.2.0rc2}/LICENSE +0 -0
  23. {flixopt-2.1.7 → flixopt-2.2.0rc2}/README.md +0 -0
  24. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/SUMMARY.md +0 -0
  25. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/contribute.md +0 -0
  26. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/examples/00-Minimal Example.md +0 -0
  27. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/examples/01-Basic Example.md +0 -0
  28. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/examples/02-Complex Example.md +0 -0
  29. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/examples/03-Calculation Modes.md +0 -0
  30. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/examples/index.md +0 -0
  31. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/faq/contribute.md +0 -0
  32. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/faq/index.md +0 -0
  33. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/getting-started.md +0 -0
  34. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/images/architecture_flixOpt-pre2.0.0.png +0 -0
  35. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/images/architecture_flixOpt.png +0 -0
  36. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/images/flixopt-icon.svg +0 -0
  37. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/index.md +0 -0
  38. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/javascripts/mathjax.js +0 -0
  39. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/Bus.md +0 -0
  40. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +0 -0
  41. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/Flow.md +0 -0
  42. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/LinearConverter.md +0 -0
  43. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/Piecewise.md +0 -0
  44. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/Storage.md +0 -0
  45. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/index.md +0 -0
  46. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/Mathematical Notation/others.md +0 -0
  47. {flixopt-2.1.7 → flixopt-2.2.0rc2}/docs/user-guide/index.md +0 -0
  48. {flixopt-2.1.7 → flixopt-2.2.0rc2}/examples/00_Minmal/minimal_example.py +0 -0
  49. {flixopt-2.1.7 → flixopt-2.2.0rc2}/examples/01_Simple/simple_example.py +0 -0
  50. {flixopt-2.1.7 → flixopt-2.2.0rc2}/examples/02_Complex/complex_example.py +0 -0
  51. {flixopt-2.1.7 → flixopt-2.2.0rc2}/examples/02_Complex/complex_example_results.py +0 -0
  52. {flixopt-2.1.7 → flixopt-2.2.0rc2}/examples/03_Calculation_types/Zeitreihen2020.csv +0 -0
  53. {flixopt-2.1.7 → flixopt-2.2.0rc2}/examples/03_Calculation_types/example_calculation_types.py +0 -0
  54. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/aggregation.py +0 -0
  55. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/calculation.py +0 -0
  56. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/config.py +0 -0
  57. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/config.yaml +0 -0
  58. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/core.py +0 -0
  59. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/effects.py +0 -0
  60. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/flow_system.py +0 -0
  61. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/io.py +0 -0
  62. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/linear_converters.py +0 -0
  63. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/network_app.py +0 -0
  64. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/plotting.py +0 -0
  65. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/results.py +0 -0
  66. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/solvers.py +0 -0
  67. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/structure.py +0 -0
  68. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt/utils.py +0 -0
  69. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt.egg-info/SOURCES.txt +0 -0
  70. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt.egg-info/dependency_links.txt +0 -0
  71. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt.egg-info/requires.txt +0 -0
  72. {flixopt-2.1.7 → flixopt-2.2.0rc2}/flixopt.egg-info/top_level.txt +0 -0
  73. {flixopt-2.1.7 → flixopt-2.2.0rc2}/mkdocs.yml +0 -0
  74. {flixopt-2.1.7 → flixopt-2.2.0rc2}/pics/architecture_flixOpt-pre2.0.0.png +0 -0
  75. {flixopt-2.1.7 → flixopt-2.2.0rc2}/pics/architecture_flixOpt.png +0 -0
  76. {flixopt-2.1.7 → flixopt-2.2.0rc2}/pics/flixOpt_plotting.jpg +0 -0
  77. {flixopt-2.1.7 → flixopt-2.2.0rc2}/pics/flixopt-icon.svg +0 -0
  78. {flixopt-2.1.7 → flixopt-2.2.0rc2}/pics/pics.pptx +0 -0
  79. {flixopt-2.1.7 → flixopt-2.2.0rc2}/pyproject.toml +0 -0
  80. {flixopt-2.1.7 → flixopt-2.2.0rc2}/renovate.json +0 -0
  81. {flixopt-2.1.7 → flixopt-2.2.0rc2}/scripts/extract_release_notes.py +0 -0
  82. {flixopt-2.1.7 → flixopt-2.2.0rc2}/scripts/gen_ref_pages.py +0 -0
  83. {flixopt-2.1.7 → flixopt-2.2.0rc2}/setup.cfg +0 -0
  84. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/__init__.py +0 -0
  85. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/ressources/Zeitreihen2020.csv +0 -0
  86. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/run_all_tests.py +0 -0
  87. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_bus.py +0 -0
  88. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_component.py +0 -0
  89. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_dataconverter.py +0 -0
  90. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_effect.py +0 -0
  91. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_examples.py +0 -0
  92. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_functional.py +0 -0
  93. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_integration.py +0 -0
  94. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_io.py +0 -0
  95. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_network_app.py +0 -0
  96. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_on_hours_computation.py +0 -0
  97. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_plots.py +0 -0
  98. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_results_plots.py +0 -0
  99. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_storage.py +0 -0
  100. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/test_timeseries.py +0 -0
  101. {flixopt-2.1.7 → flixopt-2.2.0rc2}/tests/todos.txt +0 -0
@@ -2,12 +2,15 @@ name: Python Package CI/CD
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [main, dev] # Added develop branch
6
- tags:
7
- - 'v*.*.*' # Trigger on semantic version tags
5
+ branches: [main] # Only main branch
6
+ tags: ['v*.*.*']
8
7
  pull_request:
9
- branches: [main, dev, 'feature/*', 'hotfix/*', 'fix/*']
8
+ branches: [main, dev]
10
9
  types: [opened, synchronize, reopened]
10
+ paths-ignore:
11
+ - 'docs/**'
12
+ - '*.md'
13
+ - 'README*'
11
14
  workflow_dispatch: # Allow manual triggering
12
15
 
13
16
  # Set permissions for security
@@ -139,11 +142,58 @@ jobs:
139
142
  with:
140
143
  python-version: ${{ env.PYTHON_VERSION }}
141
144
 
142
- - name: Extract release notes
145
+ - name: Check if pre-release
146
+ id: prerelease
147
+ run: |
148
+ if [[ "${{ github.ref }}" =~ (alpha|beta|rc) ]]; then
149
+ echo "is_prerelease=true" >> $GITHUB_OUTPUT
150
+ echo "✅ Detected pre-release"
151
+ else
152
+ echo "is_prerelease=false" >> $GITHUB_OUTPUT
153
+ echo "✅ Detected stable release"
154
+ fi
155
+
156
+ - name: Create release notes
143
157
  run: |
144
158
  VERSION=${GITHUB_REF#refs/tags/v}
145
- echo "Extracting release notes for version: $VERSION"
146
- python scripts/extract_release_notes.py $VERSION > current_release_notes.md
159
+ echo "Creating release notes for version: $VERSION"
160
+
161
+ if [[ "${{ steps.prerelease.outputs.is_prerelease }}" == "true" ]]; then
162
+ echo "📝 Generating pre-release notes"
163
+ cat > current_release_notes.md << EOF
164
+ # Pre-release $VERSION
165
+
166
+ This is a pre-release version for testing and feedback.
167
+
168
+ ## Installation
169
+ \`\`\`bash
170
+ pip install flixopt==$VERSION --pre
171
+ \`\`\`
172
+
173
+ ## What's Changed
174
+ See the [unreleased section](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md#unreleased) in the changelog for upcoming features and changes.
175
+
176
+ ## Feedback
177
+ Please report any issues or feedback on [GitHub Issues](https://github.com/flixOpt/flixopt/issues).
178
+ EOF
179
+ else
180
+ echo "📝 Extracting release notes from changelog"
181
+ if python scripts/extract_release_notes.py "$VERSION" > current_release_notes.md 2>/dev/null; then
182
+ echo "✅ Successfully extracted release notes"
183
+ else
184
+ echo "⚠️ No release notes found, using fallback"
185
+ cat > current_release_notes.md << EOF
186
+ # Release $VERSION
187
+
188
+ See the [full changelog](https://github.com/flixOpt/flixopt/blob/main/CHANGELOG.md) for details.
189
+ EOF
190
+ fi
191
+ fi
192
+
193
+ echo "Generated release notes:"
194
+ echo "========================"
195
+ cat current_release_notes.md
196
+ echo "========================"
147
197
 
148
198
  - name: Create GitHub Release
149
199
  uses: softprops/action-gh-release@v2
@@ -6,8 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  <!-- This text won't be rendered
9
+ Take Care: The CI will automatically append a "Whats CHanged" section to the changelog.
10
+ This contains all COmmits, PR's and Contributers.
11
+ Therefore, the Changelog should focus on the user-facing changes.
9
12
 
10
- ## [Unreleased]
13
+ Template:
14
+ ----
15
+ ## [Unreleased] - ????-??-??
11
16
 
12
17
  ### Changed
13
18
 
@@ -21,8 +26,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
26
 
22
27
  ### Development
23
28
 
29
+ ----
30
+ Upcoming Release:
31
+
32
+ ## [2.2.0] - 2025-09-13
33
+ THis release introduces a new interface `PiecewiseEffectsPerFlowHour` to model non-linear relations between flow rates and effects.
34
+ This greatly enhances Model flexibility.
35
+
36
+ ### Fixed
37
+ - LinearConverter with `PiecewiseConversion` allowed flows to reach 0 values, even though they didnt have `OnOffParameters` nor `PiecewiseConversion` actually containing 0 in its `Piece`s.
38
+
39
+ ### Added
40
+ - Added new Interface `PiecewiseEffectsPerFlowHour` to model non-linear relations between flow rates and effects.
41
+
24
42
  Until here -->
25
43
 
44
+
26
45
  ## [2.1.7] - 2025-09-13
27
46
 
28
47
  This update is a maintenance release to improve Code Quality, CI and update the dependencies.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flixopt
3
- Version: 2.1.7
3
+ Version: 2.2.0rc2
4
4
  Summary: Vector based energy and material flow optimization framework in Python.
5
5
  Author-email: "Chair of Building Energy Systems and Heat Supply, TU Dresden" <peter.stange@tu-dresden.de>, Felix Bumann <felixbumann387@gmail.com>, Felix Panitz <baumbude@googlemail.com>, Peter Stange <peter.stange@tu-dresden.de>
6
6
  Maintainer-email: Felix Bumann <felixbumann387@gmail.com>, Peter Stange <peter.stange@tu-dresden.de>
@@ -22,6 +22,7 @@ from .commons import (
22
22
  Piecewise,
23
23
  PiecewiseConversion,
24
24
  PiecewiseEffects,
25
+ PiecewiseEffectsPerFlowHour,
25
26
  SegmentedCalculation,
26
27
  Sink,
27
28
  Source,
@@ -18,7 +18,15 @@ from .core import TimeSeriesData
18
18
  from .effects import Effect
19
19
  from .elements import Bus, Flow
20
20
  from .flow_system import FlowSystem
21
- from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects
21
+ from .interface import (
22
+ InvestParameters,
23
+ OnOffParameters,
24
+ Piece,
25
+ Piecewise,
26
+ PiecewiseConversion,
27
+ PiecewiseEffects,
28
+ PiecewiseEffectsPerFlowHour,
29
+ )
22
30
 
23
31
  __all__ = [
24
32
  'TimeSeriesData',
@@ -48,4 +56,5 @@ __all__ = [
48
56
  'results',
49
57
  'linear_converters',
50
58
  'solvers',
59
+ 'PiecewiseEffectsPerFlowHour',
51
60
  ]
@@ -24,9 +24,119 @@ logger = logging.getLogger('flixopt')
24
24
 
25
25
  @register_class_for_io
26
26
  class LinearConverter(Component):
27
- """
28
- Converts input-Flows into output-Flows via linear conversion factors
27
+ """Convert input flows into output flows using linear or piecewise linear conversion factors.
28
+
29
+ This component models conversion equipment where input flows are transformed
30
+ into output flows with fixed or variable conversion ratios, such as:
31
+
32
+ - Heat pumps and chillers with variable efficiency
33
+ - Power plants with fuel-to-electricity conversion
34
+ - Chemical processes with multiple inputs/outputs
35
+ - Pumps and compressors
36
+ - Combined heat and power (CHP) plants
37
+
38
+ Args:
39
+ label: Unique identifier for the component in the FlowSystem.
40
+ inputs: List of input Flow objects that feed into the converter.
41
+ outputs: List of output Flow objects produced by the converter.
42
+ on_off_parameters: Controls binary on/off behavior of the converter.
43
+ When specified, the component can be completely turned on or off, affecting
44
+ all connected flows. This creates binary variables in the optimization.
45
+ For better performance, consider using OnOffParameters on individual flows instead.
46
+ conversion_factors: Linear conversion ratios between flows as time series data.
47
+ List of dictionaries mapping flow labels to their conversion factors.
48
+ Mutually exclusive with piecewise_conversion.
49
+ piecewise_conversion: Piecewise linear conversion relationships between flows.
50
+ Enables modeling of variable efficiency or discrete operating modes.
51
+ Mutually exclusive with conversion_factors.
52
+ meta_data: Additional information stored with the component.
53
+ Saved in results but not used internally. Use only Python native types.
54
+
55
+ Warning:
56
+ When using `piecewise_conversion` without `on_off_parameters`, flow rates cannot
57
+ reach zero unless explicitly defined with zero-valued pieces (e.g., `fx.Piece(0, 0)`).
58
+ This prevents unintended zero flows and maintains mathematical consistency.
59
+
60
+ To allow zero flow rates:
61
+
62
+ - Add `on_off_parameters` to enable complete shutdown, or
63
+ - Include explicit zero pieces in your `piecewise_conversion` definition
64
+
65
+ This behavior was clarified in v2.1.7 to prevent optimization edge cases.
66
+
67
+ Examples:
68
+ Simple heat pump with fixed COP:
69
+
70
+ ```python
71
+ heat_pump = fx.LinearConverter(
72
+ label='heat_pump',
73
+ inputs=[electricity_flow],
74
+ outputs=[heat_flow],
75
+ conversion_factors=[
76
+ {
77
+ 'electricity_flow': 1.0, # 1 kW electricity input
78
+ 'heat_flow': 3.5, # 3.5 kW heat output (COP=3.5)
79
+ }
80
+ ],
81
+ )
82
+ ```
83
+
84
+ Variable efficiency heat pump:
85
+
86
+ ```python
87
+ heat_pump = fx.LinearConverter(
88
+ label='variable_heat_pump',
89
+ inputs=[electricity_flow],
90
+ outputs=[heat_flow],
91
+ piecewise_conversion=fx.PiecewiseConversion(
92
+ {
93
+ 'electricity_flow': fx.Piecewise(
94
+ [
95
+ fx.Piece(0, 10), # Allow zero to 10 kW input
96
+ fx.Piece(10, 25), # Higher load operation
97
+ ]
98
+ ),
99
+ 'heat_flow': fx.Piecewise(
100
+ [
101
+ fx.Piece(0, 35), # COP=3.5 at low loads
102
+ fx.Piece(35, 75), # COP=3.0 at high loads
103
+ ]
104
+ ),
105
+ }
106
+ ),
107
+ )
108
+ ```
109
+
110
+ Combined heat and power plant:
111
+
112
+ ```python
113
+ chp_plant = fx.LinearConverter(
114
+ label='chp_plant',
115
+ inputs=[natural_gas_flow],
116
+ outputs=[electricity_flow, heat_flow],
117
+ conversion_factors=[
118
+ {
119
+ 'natural_gas_flow': 1.0, # 1 MW fuel input
120
+ 'electricity_flow': 0.4, # 40% electrical efficiency
121
+ 'heat_flow': 0.45, # 45% thermal efficiency
122
+ }
123
+ ],
124
+ on_off_parameters=fx.OnOffParameters(
125
+ min_on_hours=4, # Minimum 4-hour operation
126
+ min_off_hours=2, # Minimum 2-hour downtime
127
+ ),
128
+ )
129
+ ```
130
+
131
+ Note:
132
+ Either `conversion_factors` or `piecewise_conversion` must be specified, but not both.
133
+ The component automatically handles the mathematical relationships between all
134
+ connected flows according to the specified conversion ratios.
29
135
 
136
+ See Also:
137
+ PiecewiseConversion: For variable efficiency modeling
138
+ OnOffParameters: For binary on/off control
139
+ Flow: Input and output flow definitions
30
140
  """
31
141
 
32
142
  def __init__(
@@ -39,21 +149,6 @@ class LinearConverter(Component):
39
149
  piecewise_conversion: Optional[PiecewiseConversion] = None,
40
150
  meta_data: Optional[Dict] = None,
41
151
  ):
42
- """
43
- Args:
44
- label: The label of the Element. Used to identify it in the FlowSystem
45
- inputs: The input Flows
46
- outputs: The output Flows
47
- on_off_parameters: Information about on and off state of LinearConverter.
48
- Component is On/Off, if all connected Flows are On/Off. This induces an On-Variable (binary) in all Flows!
49
- If possible, use OnOffParameters in a single Flow instead to keep the number of binary variables low.
50
- See class OnOffParameters.
51
- conversion_factors: linear relation between flows.
52
- Either 'conversion_factors' or 'piecewise_conversion' can be used!
53
- piecewise_conversion: Define a piecewise linear relation between flow rates of different flows.
54
- Either 'conversion_factors' or 'piecewise_conversion' can be used!
55
- meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
56
- """
57
152
  super().__init__(label, inputs, outputs, on_off_parameters, meta_data=meta_data)
58
153
  self.conversion_factors = conversion_factors or []
59
154
  self.piecewise_conversion = piecewise_conversion
@@ -12,8 +12,8 @@ import numpy as np
12
12
  from .config import CONFIG
13
13
  from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeriesCollection
14
14
  from .effects import EffectValuesUser
15
- from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel
16
- from .interface import InvestParameters, OnOffParameters
15
+ from .features import InvestmentModel, OnOffModel, PiecewiseEffectsPerFlowHourModel, PreventSimultaneousUsageModel
16
+ from .interface import InvestParameters, OnOffParameters, PiecewiseEffectsPerFlowHour
17
17
  from .structure import Element, ElementModel, SystemModel, register_class_for_io
18
18
 
19
19
  if TYPE_CHECKING:
@@ -159,6 +159,7 @@ class Flow(Element):
159
159
  relative_minimum: NumericDataTS = 0,
160
160
  relative_maximum: NumericDataTS = 1,
161
161
  effects_per_flow_hour: Optional[EffectValuesUser] = None,
162
+ piecewise_effects_per_flow_hour: Optional[PiecewiseEffectsPerFlowHour] = None,
162
163
  on_off_parameters: Optional[OnOffParameters] = None,
163
164
  flow_hours_total_max: Optional[Scalar] = None,
164
165
  flow_hours_total_min: Optional[Scalar] = None,
@@ -180,6 +181,7 @@ class Flow(Element):
180
181
  def: :math:`load\_factor:= sumFlowHours/ (nominal\_val \cdot \Delta t_{tot})`
181
182
  load_factor_max: maximal load factor (see minimal load factor)
182
183
  effects_per_flow_hour: operational costs, costs per flow-"work"
184
+ piecewise_effects_per_flow_hour: piecewise relation between flow hours and effects
183
185
  on_off_parameters: If present, flow can be "off", i.e. be zero (only relevant if relative_minimum > 0)
184
186
  Therefore a binary var "on" is used. Further, several other restrictions and effects can be modeled
185
187
  through this On/Off State (See OnOffParameters)
@@ -207,6 +209,7 @@ class Flow(Element):
207
209
  self.load_factor_max = load_factor_max
208
210
  # self.positive_gradient = TimeSeries('positive_gradient', positive_gradient, self)
209
211
  self.effects_per_flow_hour = effects_per_flow_hour if effects_per_flow_hour is not None else {}
212
+ self.piecewise_effects_per_flow_hour = piecewise_effects_per_flow_hour
210
213
  self.flow_hours_total_max = flow_hours_total_max
211
214
  self.flow_hours_total_min = flow_hours_total_min
212
215
  self.on_off_parameters = on_off_parameters
@@ -248,6 +251,8 @@ class Flow(Element):
248
251
  self.effects_per_flow_hour = flow_system.create_effect_time_series(
249
252
  self.label_full, self.effects_per_flow_hour, 'per_flow_hour'
250
253
  )
254
+ if self.piecewise_effects_per_flow_hour is not None:
255
+ self.piecewise_effects_per_flow_hour.transform_data(flow_system, self.label_full)
251
256
  if self.on_off_parameters is not None:
252
257
  self.on_off_parameters.transform_data(flow_system, self.label_full)
253
258
  if isinstance(self.size, InvestParameters):
@@ -398,6 +403,21 @@ class FlowModel(ElementModel):
398
403
  target='operation',
399
404
  )
400
405
 
406
+ if self.element.piecewise_effects_per_flow_hour is not None:
407
+ self.piecewise_effects = self.add(
408
+ PiecewiseEffectsPerFlowHourModel(
409
+ model=self._model,
410
+ label_of_element=self.label_of_element,
411
+ piecewise_origin=(
412
+ self.flow_rate.name,
413
+ self.element.piecewise_effects_per_flow_hour.piecewise_flow_rate,
414
+ ),
415
+ piecewise_shares=self.element.piecewise_effects_per_flow_hour.piecewise_shares,
416
+ zero_point=self.on_off.on if self.on_off is not None else False,
417
+ ),
418
+ )
419
+ self.piecewise_effects.do_modeling()
420
+
401
421
  def _create_bounds_for_load_factor(self):
402
422
  # TODO: Add Variable load_factor for better evaluation?
403
423
 
@@ -12,7 +12,7 @@ import numpy as np
12
12
  from . import utils
13
13
  from .config import CONFIG
14
14
  from .core import NumericData, Scalar, TimeSeries
15
- from .interface import InvestParameters, OnOffParameters, Piecewise
15
+ from .interface import InvestParameters, OnOffParameters, Piece, Piecewise
16
16
  from .structure import Model, SystemModel
17
17
 
18
18
  logger = logging.getLogger('flixopt')
@@ -841,7 +841,7 @@ class PiecewiseModel(Model):
841
841
  label: str = '',
842
842
  ):
843
843
  """
844
- Modeling a Piecewise relation between miultiple variables.
844
+ Modeling a Piecewise relation between multiple variables.
845
845
  The relation is defined by a list of Pieces, which are assigned to the variables.
846
846
  Each Piece is a tuple of (start, end).
847
847
 
@@ -850,7 +850,9 @@ class PiecewiseModel(Model):
850
850
  label_of_element: The label of the parent (Element). Used to construct the full label of the model.
851
851
  label: The label of the model. Used to construct the full label of the model.
852
852
  piecewise_variables: The variables to which the Pieces are assigned.
853
- zero_point: A variable that can be used to define a zero point for the Piecewise relation. If None or False, no zero point is defined.
853
+ zero_point: A variable that can be used to define a zero point for the Piecewise relation.
854
+ If None or False, no zero point is defined. THis leads to 0 not being possible,
855
+ unless its its contained in a Piece.
854
856
  as_time_series: Whether the Piecewise relation is defined for a TimeSeries or a single variable.
855
857
  """
856
858
  super().__init__(model, label_of_element, label)
@@ -896,7 +898,7 @@ class PiecewiseModel(Model):
896
898
  # b) eq: -On(t) + Segment1.onSeg(t) + Segment2.onSeg(t) + ... = 0 zusätzlich kann alles auch Null sein
897
899
  if isinstance(self._zero_point, linopy.Variable):
898
900
  self.zero_point = self._zero_point
899
- rhs = self.zero_point
901
+ sign, rhs = '<=', self.zero_point
900
902
  elif self._zero_point is True:
901
903
  self.zero_point = self.add(
902
904
  self._model.add_variables(
@@ -904,13 +906,15 @@ class PiecewiseModel(Model):
904
906
  ),
905
907
  'zero_point',
906
908
  )
907
- rhs = self.zero_point
909
+ sign, rhs = '<=', self.zero_point
908
910
  else:
909
- rhs = 1
911
+ sign, rhs = '=', 1
910
912
 
911
913
  self.add(
912
914
  self._model.add_constraints(
913
- sum([piece.inside_piece for piece in self.pieces]) <= rhs,
915
+ sum([piece.inside_piece for piece in self.pieces]),
916
+ sign,
917
+ rhs,
914
918
  name=f'{self.label_full}|{variable.name}|single_segment',
915
919
  ),
916
920
  f'{var_name}|single_segment',
@@ -1079,6 +1083,65 @@ class PiecewiseEffectsModel(Model):
1079
1083
  )
1080
1084
 
1081
1085
 
1086
+ class PiecewiseEffectsPerFlowHourModel(Model):
1087
+ def __init__(
1088
+ self,
1089
+ model: SystemModel,
1090
+ label_of_element: str,
1091
+ piecewise_origin: Tuple[str, Piecewise],
1092
+ piecewise_shares: Dict[str, Piecewise],
1093
+ zero_point: Optional[Union[bool, linopy.Variable]],
1094
+ label: str = 'PiecewiseEffectsPerFlowHour',
1095
+ ):
1096
+ super().__init__(model, label_of_element, label)
1097
+ assert len(piecewise_origin[1]) == len(list(piecewise_shares.values())[0]), (
1098
+ 'Piece length of variable_segments and share_segments must be equal'
1099
+ )
1100
+ self._zero_point = zero_point
1101
+ self._piecewise_origin = piecewise_origin
1102
+ self._piecewise_shares = piecewise_shares
1103
+
1104
+ self.shares: Dict[str, linopy.Variable] = {}
1105
+
1106
+ self.piecewise_model: Optional[PiecewiseModel] = None
1107
+
1108
+ def do_modeling(self):
1109
+ self.shares = {
1110
+ effect: self.add(
1111
+ self._model.add_variables(coords=self._model.coords, name=f'{self.label_full}|{effect}'), f'{effect}'
1112
+ )
1113
+ for effect in self._piecewise_shares
1114
+ }
1115
+
1116
+ piecewise_variables = {
1117
+ self._piecewise_origin[0]: self._piecewise_origin[1],
1118
+ **{
1119
+ self.shares[effect_label].name: self._piecewise_shares[effect_label]
1120
+ for effect_label in self._piecewise_shares
1121
+ },
1122
+ }
1123
+
1124
+ self.piecewise_model = self.add(
1125
+ PiecewiseModel(
1126
+ model=self._model,
1127
+ label_of_element=self.label_of_element,
1128
+ piecewise_variables=piecewise_variables,
1129
+ zero_point=self._zero_point,
1130
+ as_time_series=True,
1131
+ label='PiecewiseEffectsPerFlowHour',
1132
+ )
1133
+ )
1134
+
1135
+ self.piecewise_model.do_modeling()
1136
+
1137
+ # Shares
1138
+ self._model.effects.add_share_to_effects(
1139
+ name=self.label_of_element,
1140
+ expressions={effect: variable * self._model.hours_per_step for effect, variable in self.shares.items()},
1141
+ target='operation',
1142
+ )
1143
+
1144
+
1082
1145
  class PreventSimultaneousUsageModel(Model):
1083
1146
  """
1084
1147
  Prevents multiple Multiple Binary variables from being 1 at the same time