flixopt 3.0.1__py3-none-any.whl → 6.0.0rc7__py3-none-any.whl

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.
Files changed (42) hide show
  1. flixopt/__init__.py +57 -49
  2. flixopt/carrier.py +159 -0
  3. flixopt/clustering/__init__.py +51 -0
  4. flixopt/clustering/base.py +1746 -0
  5. flixopt/clustering/intercluster_helpers.py +201 -0
  6. flixopt/color_processing.py +372 -0
  7. flixopt/comparison.py +819 -0
  8. flixopt/components.py +848 -270
  9. flixopt/config.py +853 -496
  10. flixopt/core.py +111 -98
  11. flixopt/effects.py +294 -284
  12. flixopt/elements.py +484 -223
  13. flixopt/features.py +220 -118
  14. flixopt/flow_system.py +2026 -389
  15. flixopt/interface.py +504 -286
  16. flixopt/io.py +1718 -55
  17. flixopt/linear_converters.py +291 -230
  18. flixopt/modeling.py +304 -181
  19. flixopt/network_app.py +2 -1
  20. flixopt/optimization.py +788 -0
  21. flixopt/optimize_accessor.py +373 -0
  22. flixopt/plot_result.py +143 -0
  23. flixopt/plotting.py +1177 -1034
  24. flixopt/results.py +1331 -372
  25. flixopt/solvers.py +12 -4
  26. flixopt/statistics_accessor.py +2412 -0
  27. flixopt/stats_accessor.py +75 -0
  28. flixopt/structure.py +954 -120
  29. flixopt/topology_accessor.py +676 -0
  30. flixopt/transform_accessor.py +2277 -0
  31. flixopt/types.py +120 -0
  32. flixopt-6.0.0rc7.dist-info/METADATA +290 -0
  33. flixopt-6.0.0rc7.dist-info/RECORD +36 -0
  34. {flixopt-3.0.1.dist-info → flixopt-6.0.0rc7.dist-info}/WHEEL +1 -1
  35. flixopt/aggregation.py +0 -382
  36. flixopt/calculation.py +0 -672
  37. flixopt/commons.py +0 -51
  38. flixopt/utils.py +0 -86
  39. flixopt-3.0.1.dist-info/METADATA +0 -209
  40. flixopt-3.0.1.dist-info/RECORD +0 -26
  41. {flixopt-3.0.1.dist-info → flixopt-6.0.0rc7.dist-info}/licenses/LICENSE +0 -0
  42. {flixopt-3.0.1.dist-info → flixopt-6.0.0rc7.dist-info}/top_level.txt +0 -0
flixopt/__init__.py CHANGED
@@ -2,68 +2,76 @@
2
2
  This module bundles all common functionality of flixopt and sets up the logging
3
3
  """
4
4
 
5
- import warnings
5
+ import logging
6
6
  from importlib.metadata import PackageNotFoundError, version
7
7
 
8
8
  try:
9
9
  __version__ = version('flixopt')
10
- except PackageNotFoundError:
10
+ except (PackageNotFoundError, TypeError):
11
11
  # Package is not installed (development mode without editable install)
12
12
  __version__ = '0.0.0.dev0'
13
13
 
14
- from .commons import (
15
- CONFIG,
16
- AggregatedCalculation,
17
- AggregationParameters,
18
- Bus,
19
- Effect,
20
- Flow,
21
- FlowSystem,
22
- FullCalculation,
23
- InvestParameters,
14
+ # Import commonly used classes and functions
15
+ # Register xarray accessors:
16
+ # - xr.Dataset.plotly / xr.DataArray.plotly (from xarray_plotly package)
17
+ # - xr.Dataset.fxstats (from stats_accessor)
18
+ import xarray_plotly as _xpx # noqa: F401
19
+
20
+ from . import clustering, linear_converters, plotting, results, solvers
21
+ from . import stats_accessor as _fxstats # noqa: F401
22
+ from .carrier import Carrier, CarrierContainer
23
+ from .comparison import Comparison
24
+ from .components import (
24
25
  LinearConverter,
25
- OnOffParameters,
26
- Piece,
27
- Piecewise,
28
- PiecewiseConversion,
29
- PiecewiseEffects,
30
- SegmentedCalculation,
31
26
  Sink,
32
27
  Source,
33
28
  SourceAndSink,
34
29
  Storage,
35
- TimeSeriesData,
36
30
  Transmission,
37
- change_logging_level,
38
- linear_converters,
39
- plotting,
40
- results,
41
- solvers,
42
31
  )
32
+ from .config import CONFIG
33
+ from .core import TimeSeriesData
34
+ from .effects import PENALTY_EFFECT_LABEL, Effect
35
+ from .elements import Bus, Flow
36
+ from .flow_system import FlowSystem
37
+ from .interface import InvestParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects, StatusParameters
38
+ from .optimization import Optimization, SegmentedOptimization
39
+ from .plot_result import PlotResult
43
40
 
44
- # === Runtime warning suppression for third-party libraries ===
45
- # These warnings are from dependencies and cannot be fixed by end users.
46
- # They are suppressed at runtime to provide a cleaner user experience.
47
- # These filters match the test configuration in pyproject.toml for consistency.
48
-
49
- # tsam: Time series aggregation library
50
- # - UserWarning: Informational message about minimal value constraints during clustering.
51
- warnings.filterwarnings('ignore', category=UserWarning, message='.*minimal value.*exceeds.*', module='tsam')
52
- # TODO: Might be able to fix it in flixopt?
53
-
54
- # linopy: Linear optimization library
55
- # - UserWarning: Coordinate mismatch warnings that don't affect functionality and are expected.
56
- warnings.filterwarnings(
57
- 'ignore', category=UserWarning, message='Coordinates across variables not equal', module='linopy'
58
- )
59
- # - FutureWarning: join parameter default will change in future versions
60
- warnings.filterwarnings(
61
- 'ignore',
62
- category=FutureWarning,
63
- message="In a future version of xarray the default value for join will change from join='outer' to join='exact'",
64
- module='linopy',
65
- )
41
+ __all__ = [
42
+ 'TimeSeriesData',
43
+ 'CONFIG',
44
+ 'Carrier',
45
+ 'CarrierContainer',
46
+ 'Comparison',
47
+ 'Flow',
48
+ 'Bus',
49
+ 'Effect',
50
+ 'PENALTY_EFFECT_LABEL',
51
+ 'Source',
52
+ 'Sink',
53
+ 'SourceAndSink',
54
+ 'Storage',
55
+ 'LinearConverter',
56
+ 'Transmission',
57
+ 'FlowSystem',
58
+ 'Optimization',
59
+ 'SegmentedOptimization',
60
+ 'InvestParameters',
61
+ 'StatusParameters',
62
+ 'Piece',
63
+ 'Piecewise',
64
+ 'PiecewiseConversion',
65
+ 'PiecewiseEffects',
66
+ 'PlotResult',
67
+ 'clustering',
68
+ 'plotting',
69
+ 'results',
70
+ 'linear_converters',
71
+ 'solvers',
72
+ ]
66
73
 
67
- # numpy: Core numerical library
68
- # - RuntimeWarning: Binary incompatibility warnings from compiled extensions (safe to ignore). numpy 1->2
69
- warnings.filterwarnings('ignore', category=RuntimeWarning, message='numpy\\.ndarray size changed')
74
+ # Initialize logger with default configuration (silent: WARNING level, NullHandler).
75
+ logger = logging.getLogger('flixopt')
76
+ logger.setLevel(logging.WARNING)
77
+ logger.addHandler(logging.NullHandler())
flixopt/carrier.py ADDED
@@ -0,0 +1,159 @@
1
+ """Carrier class for energy/material type definitions.
2
+
3
+ Carriers represent types of energy or materials that flow through buses,
4
+ such as electricity, heat, gas, or water. They provide consistent styling
5
+ and metadata across visualizations.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from .structure import ContainerMixin, Interface, register_class_for_io
11
+
12
+
13
+ @register_class_for_io
14
+ class Carrier(Interface):
15
+ """Definition of an energy or material carrier type.
16
+
17
+ Carriers represent the type of energy or material flowing through a Bus.
18
+ They provide consistent color, unit, and description across all visualizations
19
+ and can be shared between multiple buses of the same type.
20
+
21
+ Inherits from Interface to provide serialization capabilities.
22
+
23
+ Args:
24
+ name: Identifier for the carrier (e.g., 'electricity', 'heat', 'gas').
25
+ color: Hex color string for visualizations (e.g., '#FFD700').
26
+ unit: Unit string for display (e.g., 'kW', 'kW_th', 'm³/h').
27
+ description: Optional human-readable description.
28
+
29
+ Examples:
30
+ Creating custom carriers:
31
+
32
+ ```python
33
+ import flixopt as fx
34
+
35
+ # Define custom carriers
36
+ electricity = fx.Carrier('electricity', '#FFD700', 'kW', 'Electrical power')
37
+ district_heat = fx.Carrier('district_heat', '#FF6B6B', 'kW_th', 'District heating')
38
+ hydrogen = fx.Carrier('hydrogen', '#00CED1', 'kg/h', 'Hydrogen fuel')
39
+
40
+ # Register with FlowSystem
41
+ flow_system.add_carrier(electricity)
42
+ flow_system.add_carrier(district_heat)
43
+
44
+ # Use with buses (just reference by name)
45
+ elec_bus = fx.Bus('MainGrid', carrier='electricity')
46
+ heat_bus = fx.Bus('HeatingNetwork', carrier='district_heat')
47
+ ```
48
+
49
+ Using predefined carriers from CONFIG:
50
+
51
+ ```python
52
+ # Access built-in carriers
53
+ elec = fx.CONFIG.Carriers.electricity
54
+ heat = fx.CONFIG.Carriers.heat
55
+
56
+ # Use directly
57
+ bus = fx.Bus('Grid', carrier='electricity')
58
+ ```
59
+
60
+ Adding custom carriers to CONFIG:
61
+
62
+ ```python
63
+ # Add a new carrier globally
64
+ fx.CONFIG.Carriers.add(fx.Carrier('biogas', '#228B22', 'kW', 'Biogas'))
65
+
66
+ # Now available as
67
+ fx.CONFIG.Carriers.biogas
68
+ ```
69
+
70
+ Note:
71
+ Carriers are compared by name for equality, allowing flexible usage
72
+ patterns where the same carrier type can be referenced by name string
73
+ or Carrier object interchangeably.
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ name: str,
79
+ color: str = '',
80
+ unit: str = '',
81
+ description: str = '',
82
+ ) -> None:
83
+ """Initialize a Carrier.
84
+
85
+ Args:
86
+ name: Identifier for the carrier (normalized to lowercase).
87
+ color: Hex color string for visualizations.
88
+ unit: Unit string for display.
89
+ description: Optional human-readable description.
90
+ """
91
+ self.name = name.lower()
92
+ self.color = color
93
+ self.unit = unit
94
+ self.description = description
95
+
96
+ def transform_data(self, name_prefix: str = '') -> None:
97
+ """Transform data to match FlowSystem dimensions.
98
+
99
+ Carriers don't have time-series data, so this is a no-op.
100
+
101
+ Args:
102
+ name_prefix: Ignored for Carrier.
103
+ """
104
+ pass # Carriers have no data to transform
105
+
106
+ @property
107
+ def label(self) -> str:
108
+ """Label for container keying (alias for name)."""
109
+ return self.name
110
+
111
+ def __hash__(self):
112
+ return hash(self.name)
113
+
114
+ def __eq__(self, other):
115
+ if isinstance(other, Carrier):
116
+ return self.name == other.name
117
+ if isinstance(other, str):
118
+ return self.name == other.lower()
119
+ return False
120
+
121
+ def __repr__(self):
122
+ return f"Carrier('{self.name}', color='{self.color}', unit='{self.unit}')"
123
+
124
+ def __str__(self):
125
+ return self.name
126
+
127
+
128
+ class CarrierContainer(ContainerMixin['Carrier']):
129
+ """Container for Carrier objects.
130
+
131
+ Uses carrier.name for keying. Provides dict-like access to carriers
132
+ registered with a FlowSystem.
133
+
134
+ Examples:
135
+ ```python
136
+ # Access via FlowSystem
137
+ carriers = flow_system.carriers
138
+
139
+ # Dict-like access
140
+ elec = carriers['electricity']
141
+ 'heat' in carriers # True/False
142
+
143
+ # Iteration
144
+ for name in carriers:
145
+ print(name)
146
+ ```
147
+ """
148
+
149
+ def __init__(self, carriers: list[Carrier] | dict[str, Carrier] | None = None):
150
+ """Initialize a CarrierContainer.
151
+
152
+ Args:
153
+ carriers: Initial carriers to add.
154
+ """
155
+ super().__init__(elements=carriers, element_type_name='carriers')
156
+
157
+ def _get_label(self, carrier: Carrier) -> str:
158
+ """Extract name from Carrier for keying."""
159
+ return carrier.name
@@ -0,0 +1,51 @@
1
+ """
2
+ Time Series Aggregation Module for flixopt.
3
+
4
+ This module provides wrapper classes around tsam's clustering functionality:
5
+ - Clustering: Top-level class stored on FlowSystem after clustering
6
+ - ClusteringResults: Manages collection of tsam ClusteringResult objects (for IO)
7
+
8
+ Example usage:
9
+
10
+ # Cluster a FlowSystem to reduce timesteps
11
+ from tsam import ExtremeConfig
12
+
13
+ fs_clustered = flow_system.transform.cluster(
14
+ n_clusters=8,
15
+ cluster_duration='1D',
16
+ extremes=ExtremeConfig(method='new_cluster', max_value=['Demand|fixed_relative_profile']),
17
+ )
18
+
19
+ # Access clustering structure (available before AND after IO)
20
+ clustering = fs_clustered.clustering
21
+ print(f'Number of clusters: {clustering.n_clusters}')
22
+ print(f'Dims: {clustering.dims}') # e.g., ('period', 'scenario')
23
+ print(f'Coords: {clustering.coords}') # e.g., {'period': [2024, 2025]}
24
+
25
+ # Access tsam AggregationResult for detailed analysis
26
+ # NOTE: Only available BEFORE saving/loading. Lost after IO.
27
+ result = clustering.sel(period=2024, scenario='high')
28
+ result.cluster_representatives # DataFrame with aggregated time series
29
+ result.accuracy # AccuracyMetrics (rmse, mae)
30
+ result.plot.compare() # tsam's built-in comparison plot
31
+
32
+ # Iterate over all results (only before IO)
33
+ for key, result in clustering.items():
34
+ print(f'{key}: {result.n_clusters} clusters')
35
+
36
+ # Save and load - structure preserved, AggregationResult access lost
37
+ fs_clustered.to_netcdf('system.nc')
38
+ # Use include_original_data=False for smaller files (~38% reduction)
39
+ fs_clustered.to_netcdf('system.nc', include_original_data=False)
40
+
41
+ # Expand back to full resolution
42
+ fs_expanded = fs_clustered.transform.expand()
43
+ """
44
+
45
+ from .base import AggregationResults, Clustering, ClusteringResults
46
+
47
+ __all__ = [
48
+ 'ClusteringResults',
49
+ 'AggregationResults',
50
+ 'Clustering',
51
+ ]