flixopt 2.1.11__py3-none-any.whl → 2.2.0__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.
Potentially problematic release.
This version of flixopt might be problematic. Click here for more details.
- flixopt/__init__.py +0 -2
- flixopt/calculation.py +2 -2
- flixopt/config.py +486 -193
- flixopt/elements.py +5 -5
- flixopt/features.py +6 -6
- flixopt/interface.py +4 -4
- {flixopt-2.1.11.dist-info → flixopt-2.2.0.dist-info}/METADATA +4 -4
- {flixopt-2.1.11.dist-info → flixopt-2.2.0.dist-info}/RECORD +11 -12
- flixopt/config.yaml +0 -10
- {flixopt-2.1.11.dist-info → flixopt-2.2.0.dist-info}/WHEEL +0 -0
- {flixopt-2.1.11.dist-info → flixopt-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {flixopt-2.1.11.dist-info → flixopt-2.2.0.dist-info}/top_level.txt +0 -0
flixopt/__init__.py
CHANGED
flixopt/calculation.py
CHANGED
|
@@ -91,13 +91,13 @@ class Calculation:
|
|
|
91
91
|
model.label_of_element: float(model.size.solution)
|
|
92
92
|
for component in self.flow_system.components.values()
|
|
93
93
|
for model in component.model.all_sub_models
|
|
94
|
-
if isinstance(model, InvestmentModel) and float(model.size.solution) >= CONFIG.
|
|
94
|
+
if isinstance(model, InvestmentModel) and float(model.size.solution) >= CONFIG.Modeling.epsilon
|
|
95
95
|
},
|
|
96
96
|
'Not invested': {
|
|
97
97
|
model.label_of_element: float(model.size.solution)
|
|
98
98
|
for component in self.flow_system.components.values()
|
|
99
99
|
for model in component.model.all_sub_models
|
|
100
|
-
if isinstance(model, InvestmentModel) and float(model.size.solution) < CONFIG.
|
|
100
|
+
if isinstance(model, InvestmentModel) and float(model.size.solution) < CONFIG.Modeling.epsilon
|
|
101
101
|
},
|
|
102
102
|
},
|
|
103
103
|
'Buses with excess': [
|
flixopt/config.py
CHANGED
|
@@ -1,168 +1,345 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
from
|
|
7
|
-
from
|
|
4
|
+
import warnings
|
|
5
|
+
from logging.handlers import RotatingFileHandler
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
from typing import Literal
|
|
8
9
|
|
|
9
10
|
import yaml
|
|
10
11
|
from rich.console import Console
|
|
11
12
|
from rich.logging import RichHandler
|
|
13
|
+
from rich.style import Style
|
|
14
|
+
from rich.theme import Theme
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def merge_configs(defaults: dict, overrides: dict) -> dict:
|
|
17
|
-
"""
|
|
18
|
-
Merge the default configuration with user-provided overrides.
|
|
19
|
-
Args:
|
|
20
|
-
defaults: Default configuration dictionary.
|
|
21
|
-
overrides: User configuration dictionary.
|
|
22
|
-
Returns:
|
|
23
|
-
Merged configuration dictionary.
|
|
24
|
-
"""
|
|
25
|
-
for key, value in overrides.items():
|
|
26
|
-
if isinstance(value, dict) and key in defaults and isinstance(defaults[key], dict):
|
|
27
|
-
# Recursively merge nested dictionaries
|
|
28
|
-
defaults[key] = merge_configs(defaults[key], value)
|
|
29
|
-
else:
|
|
30
|
-
# Override the default value
|
|
31
|
-
defaults[key] = value
|
|
32
|
-
return defaults
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def dataclass_from_dict_with_validation(cls, data: dict):
|
|
36
|
-
"""
|
|
37
|
-
Recursively initialize a dataclass from a dictionary.
|
|
38
|
-
"""
|
|
39
|
-
if not is_dataclass(cls):
|
|
40
|
-
raise TypeError(f'{cls} must be a dataclass')
|
|
41
|
-
|
|
42
|
-
# Get resolved type hints to handle postponed evaluation
|
|
43
|
-
type_hints = get_type_hints(cls)
|
|
44
|
-
|
|
45
|
-
# Build kwargs for the dataclass constructor
|
|
46
|
-
kwargs = {}
|
|
47
|
-
for field in fields(cls):
|
|
48
|
-
field_name = field.name
|
|
49
|
-
# Use resolved type from get_type_hints instead of field.type
|
|
50
|
-
field_type = type_hints.get(field_name, field.type)
|
|
51
|
-
field_value = data.get(field_name)
|
|
52
|
-
|
|
53
|
-
# If the field type is a dataclass and the value is a dict, recursively initialize
|
|
54
|
-
if is_dataclass(field_type) and isinstance(field_value, dict):
|
|
55
|
-
kwargs[field_name] = dataclass_from_dict_with_validation(field_type, field_value)
|
|
56
|
-
else:
|
|
57
|
-
kwargs[field_name] = field_value # Pass as-is if no special handling is needed
|
|
58
|
-
|
|
59
|
-
return cls(**kwargs)
|
|
16
|
+
__all__ = ['CONFIG', 'change_logging_level']
|
|
60
17
|
|
|
18
|
+
logger = logging.getLogger('flixopt')
|
|
61
19
|
|
|
62
|
-
@dataclass()
|
|
63
|
-
class ValidatedConfig:
|
|
64
|
-
def __setattr__(self, name, value):
|
|
65
|
-
if field := self.__dataclass_fields__.get(name):
|
|
66
|
-
# Get resolved type hints to handle postponed evaluation
|
|
67
|
-
type_hints = get_type_hints(self.__class__, include_extras=True)
|
|
68
|
-
field_type = type_hints.get(name, field.type)
|
|
69
|
-
if metadata := getattr(field_type, '__metadata__', None):
|
|
70
|
-
assert metadata[0](value), f'Invalid value passed to {name!r}: {value=}'
|
|
71
|
-
super().__setattr__(name, value)
|
|
72
20
|
|
|
21
|
+
# SINGLE SOURCE OF TRUTH - immutable to prevent accidental modification
|
|
22
|
+
_DEFAULTS = MappingProxyType(
|
|
23
|
+
{
|
|
24
|
+
'config_name': 'flixopt',
|
|
25
|
+
'logging': MappingProxyType(
|
|
26
|
+
{
|
|
27
|
+
'level': 'INFO',
|
|
28
|
+
'file': 'flixopt.log',
|
|
29
|
+
'rich': False,
|
|
30
|
+
'console': True,
|
|
31
|
+
'max_file_size': 10_485_760, # 10MB
|
|
32
|
+
'backup_count': 5,
|
|
33
|
+
'date_format': '%Y-%m-%d %H:%M:%S',
|
|
34
|
+
'format': '%(message)s',
|
|
35
|
+
'console_width': 120,
|
|
36
|
+
'show_path': False,
|
|
37
|
+
'colors': MappingProxyType(
|
|
38
|
+
{
|
|
39
|
+
'DEBUG': '\033[32m', # Green
|
|
40
|
+
'INFO': '\033[34m', # Blue
|
|
41
|
+
'WARNING': '\033[33m', # Yellow
|
|
42
|
+
'ERROR': '\033[31m', # Red
|
|
43
|
+
'CRITICAL': '\033[1m\033[31m', # Bold Red
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
}
|
|
47
|
+
),
|
|
48
|
+
'modeling': MappingProxyType(
|
|
49
|
+
{
|
|
50
|
+
'big': 10_000_000,
|
|
51
|
+
'epsilon': 1e-5,
|
|
52
|
+
'big_binary_bound': 100_000,
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
}
|
|
56
|
+
)
|
|
73
57
|
|
|
74
|
-
@dataclass
|
|
75
|
-
class LoggingConfig(ValidatedConfig):
|
|
76
|
-
level: Annotated[
|
|
77
|
-
Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
|
78
|
-
lambda level: level in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
|
79
|
-
]
|
|
80
|
-
file: Annotated[str, lambda file: isinstance(file, str)]
|
|
81
|
-
rich: Annotated[bool, lambda rich: isinstance(rich, bool)]
|
|
82
58
|
|
|
59
|
+
class CONFIG:
|
|
60
|
+
"""Configuration for flixopt library.
|
|
61
|
+
|
|
62
|
+
The CONFIG class provides centralized configuration for logging and modeling parameters.
|
|
63
|
+
All changes require calling ``CONFIG.apply()`` to take effect.
|
|
64
|
+
|
|
65
|
+
By default, logging outputs to both console and file ('flixopt.log').
|
|
66
|
+
|
|
67
|
+
Attributes:
|
|
68
|
+
Logging: Nested class containing all logging configuration options.
|
|
69
|
+
Colors: Nested subclass under Logging containing ANSI color codes for log levels.
|
|
70
|
+
Modeling: Nested class containing optimization modeling parameters.
|
|
71
|
+
config_name (str): Name of the configuration (default: 'flixopt').
|
|
72
|
+
|
|
73
|
+
Logging Attributes:
|
|
74
|
+
level (str): Logging level: 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'.
|
|
75
|
+
Default: 'INFO'
|
|
76
|
+
file (str | None): Log file path. Default: 'flixopt.log'.
|
|
77
|
+
Set to None to disable file logging.
|
|
78
|
+
console (bool): Enable console (stdout) logging. Default: True
|
|
79
|
+
rich (bool): Use Rich library for enhanced console output. Default: False
|
|
80
|
+
max_file_size (int): Maximum log file size in bytes before rotation.
|
|
81
|
+
Default: 10485760 (10MB)
|
|
82
|
+
backup_count (int): Number of backup log files to keep. Default: 5
|
|
83
|
+
date_format (str): Date/time format for log messages.
|
|
84
|
+
Default: '%Y-%m-%d %H:%M:%S'
|
|
85
|
+
format (str): Log message format string. Default: '%(message)s'
|
|
86
|
+
console_width (int): Console width for Rich handler. Default: 120
|
|
87
|
+
show_path (bool): Show file paths in log messages. Default: False
|
|
88
|
+
|
|
89
|
+
Colors Attributes:
|
|
90
|
+
DEBUG (str): ANSI color code for DEBUG level. Default: '\\033[32m' (green)
|
|
91
|
+
INFO (str): ANSI color code for INFO level. Default: '\\033[34m' (blue)
|
|
92
|
+
WARNING (str): ANSI color code for WARNING level. Default: '\\033[33m' (yellow)
|
|
93
|
+
ERROR (str): ANSI color code for ERROR level. Default: '\\033[31m' (red)
|
|
94
|
+
CRITICAL (str): ANSI color code for CRITICAL level. Default: '\\033[1m\\033[31m' (bold red)
|
|
95
|
+
|
|
96
|
+
Works with both Rich and standard console handlers.
|
|
97
|
+
Rich automatically converts ANSI codes using Style.from_ansi().
|
|
98
|
+
|
|
99
|
+
Common ANSI codes:
|
|
100
|
+
|
|
101
|
+
- '\\033[30m' - Black
|
|
102
|
+
- '\\033[31m' - Red
|
|
103
|
+
- '\\033[32m' - Green
|
|
104
|
+
- '\\033[33m' - Yellow
|
|
105
|
+
- '\\033[34m' - Blue
|
|
106
|
+
- '\\033[35m' - Magenta
|
|
107
|
+
- '\\033[36m' - Cyan
|
|
108
|
+
- '\\033[37m' - White
|
|
109
|
+
- '\\033[1m\\033[3Xm' - Bold color (replace X with color code 0-7)
|
|
110
|
+
- '\\033[2m\\033[3Xm' - Dim color (replace X with color code 0-7)
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
|
|
114
|
+
- Magenta: '\\033[35m'
|
|
115
|
+
- Bold cyan: '\\033[1m\\033[36m'
|
|
116
|
+
- Dim green: '\\033[2m\\033[32m'
|
|
117
|
+
|
|
118
|
+
Modeling Attributes:
|
|
119
|
+
big (int): Large number for optimization constraints. Default: 10000000
|
|
120
|
+
epsilon (float): Small tolerance value. Default: 1e-5
|
|
121
|
+
big_binary_bound (int): Upper bound for binary variable constraints.
|
|
122
|
+
Default: 100000
|
|
123
|
+
|
|
124
|
+
Examples:
|
|
125
|
+
Basic configuration::
|
|
126
|
+
|
|
127
|
+
from flixopt import CONFIG
|
|
128
|
+
|
|
129
|
+
CONFIG.Logging.console = True
|
|
130
|
+
CONFIG.Logging.level = 'DEBUG'
|
|
131
|
+
CONFIG.apply()
|
|
132
|
+
|
|
133
|
+
Configure log file rotation::
|
|
134
|
+
|
|
135
|
+
CONFIG.Logging.file = 'myapp.log'
|
|
136
|
+
CONFIG.Logging.max_file_size = 5_242_880 # 5 MB
|
|
137
|
+
CONFIG.Logging.backup_count = 3
|
|
138
|
+
CONFIG.apply()
|
|
139
|
+
|
|
140
|
+
Customize log colors::
|
|
141
|
+
|
|
142
|
+
CONFIG.Logging.Colors.INFO = '\\033[35m' # Magenta
|
|
143
|
+
CONFIG.Logging.Colors.DEBUG = '\\033[36m' # Cyan
|
|
144
|
+
CONFIG.Logging.Colors.ERROR = '\\033[1m\\033[31m' # Bold red
|
|
145
|
+
CONFIG.apply()
|
|
146
|
+
|
|
147
|
+
Use Rich handler with custom colors::
|
|
148
|
+
|
|
149
|
+
CONFIG.Logging.console = True
|
|
150
|
+
CONFIG.Logging.rich = True
|
|
151
|
+
CONFIG.Logging.console_width = 100
|
|
152
|
+
CONFIG.Logging.show_path = True
|
|
153
|
+
CONFIG.Logging.Colors.INFO = '\\033[36m' # Cyan
|
|
154
|
+
CONFIG.apply()
|
|
155
|
+
|
|
156
|
+
Load from YAML file::
|
|
157
|
+
|
|
158
|
+
CONFIG.load_from_file('config.yaml')
|
|
159
|
+
|
|
160
|
+
Example YAML config file:
|
|
161
|
+
|
|
162
|
+
.. code-block:: yaml
|
|
163
|
+
|
|
164
|
+
logging:
|
|
165
|
+
level: DEBUG
|
|
166
|
+
console: true
|
|
167
|
+
file: app.log
|
|
168
|
+
rich: true
|
|
169
|
+
max_file_size: 5242880 # 5MB
|
|
170
|
+
backup_count: 3
|
|
171
|
+
date_format: '%H:%M:%S'
|
|
172
|
+
console_width: 100
|
|
173
|
+
show_path: true
|
|
174
|
+
colors:
|
|
175
|
+
DEBUG: "\\033[36m" # Cyan
|
|
176
|
+
INFO: "\\033[32m" # Green
|
|
177
|
+
WARNING: "\\033[33m" # Yellow
|
|
178
|
+
ERROR: "\\033[31m" # Red
|
|
179
|
+
CRITICAL: "\\033[1m\\033[31m" # Bold red
|
|
180
|
+
|
|
181
|
+
modeling:
|
|
182
|
+
big: 20000000
|
|
183
|
+
epsilon: 1e-6
|
|
184
|
+
big_binary_bound: 200000
|
|
185
|
+
|
|
186
|
+
Reset to defaults::
|
|
83
187
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
EPSILON: Annotated[float, lambda x: isinstance(x, float)]
|
|
88
|
-
BIG_BINARY_BOUND: Annotated[int, lambda x: isinstance(x, int)]
|
|
188
|
+
CONFIG.reset()
|
|
189
|
+
|
|
190
|
+
Export current configuration::
|
|
89
191
|
|
|
192
|
+
config_dict = CONFIG.to_dict()
|
|
193
|
+
import yaml
|
|
90
194
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
logging: LoggingConfig
|
|
95
|
-
modeling: ModelingConfig
|
|
195
|
+
with open('my_config.yaml', 'w') as f:
|
|
196
|
+
yaml.dump(config_dict, f)
|
|
197
|
+
"""
|
|
96
198
|
|
|
199
|
+
class Logging:
|
|
200
|
+
level: str = _DEFAULTS['logging']['level']
|
|
201
|
+
file: str | None = _DEFAULTS['logging']['file']
|
|
202
|
+
rich: bool = _DEFAULTS['logging']['rich']
|
|
203
|
+
console: bool = _DEFAULTS['logging']['console']
|
|
204
|
+
max_file_size: int = _DEFAULTS['logging']['max_file_size']
|
|
205
|
+
backup_count: int = _DEFAULTS['logging']['backup_count']
|
|
206
|
+
date_format: str = _DEFAULTS['logging']['date_format']
|
|
207
|
+
format: str = _DEFAULTS['logging']['format']
|
|
208
|
+
console_width: int = _DEFAULTS['logging']['console_width']
|
|
209
|
+
show_path: bool = _DEFAULTS['logging']['show_path']
|
|
210
|
+
|
|
211
|
+
class Colors:
|
|
212
|
+
DEBUG: str = _DEFAULTS['logging']['colors']['DEBUG']
|
|
213
|
+
INFO: str = _DEFAULTS['logging']['colors']['INFO']
|
|
214
|
+
WARNING: str = _DEFAULTS['logging']['colors']['WARNING']
|
|
215
|
+
ERROR: str = _DEFAULTS['logging']['colors']['ERROR']
|
|
216
|
+
CRITICAL: str = _DEFAULTS['logging']['colors']['CRITICAL']
|
|
217
|
+
|
|
218
|
+
class Modeling:
|
|
219
|
+
big: int = _DEFAULTS['modeling']['big']
|
|
220
|
+
epsilon: float = _DEFAULTS['modeling']['epsilon']
|
|
221
|
+
big_binary_bound: int = _DEFAULTS['modeling']['big_binary_bound']
|
|
222
|
+
|
|
223
|
+
config_name: str = _DEFAULTS['config_name']
|
|
97
224
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
225
|
+
@classmethod
|
|
226
|
+
def reset(cls):
|
|
227
|
+
"""Reset all configuration values to defaults."""
|
|
228
|
+
for key, value in _DEFAULTS['logging'].items():
|
|
229
|
+
if key == 'colors':
|
|
230
|
+
# Reset nested Colors class
|
|
231
|
+
for color_key, color_value in value.items():
|
|
232
|
+
setattr(cls.Logging.Colors, color_key, color_value)
|
|
233
|
+
else:
|
|
234
|
+
setattr(cls.Logging, key, value)
|
|
235
|
+
|
|
236
|
+
for key, value in _DEFAULTS['modeling'].items():
|
|
237
|
+
setattr(cls.Modeling, key, value)
|
|
238
|
+
|
|
239
|
+
cls.config_name = _DEFAULTS['config_name']
|
|
240
|
+
cls.apply()
|
|
102
241
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
242
|
+
@classmethod
|
|
243
|
+
def apply(cls):
|
|
244
|
+
"""Apply current configuration to logging system."""
|
|
245
|
+
# Convert Colors class attributes to dict
|
|
246
|
+
colors_dict = {
|
|
247
|
+
'DEBUG': cls.Logging.Colors.DEBUG,
|
|
248
|
+
'INFO': cls.Logging.Colors.INFO,
|
|
249
|
+
'WARNING': cls.Logging.Colors.WARNING,
|
|
250
|
+
'ERROR': cls.Logging.Colors.ERROR,
|
|
251
|
+
'CRITICAL': cls.Logging.Colors.CRITICAL,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_setup_logging(
|
|
255
|
+
default_level=cls.Logging.level,
|
|
256
|
+
log_file=cls.Logging.file,
|
|
257
|
+
use_rich_handler=cls.Logging.rich,
|
|
258
|
+
console=cls.Logging.console,
|
|
259
|
+
max_file_size=cls.Logging.max_file_size,
|
|
260
|
+
backup_count=cls.Logging.backup_count,
|
|
261
|
+
date_format=cls.Logging.date_format,
|
|
262
|
+
format=cls.Logging.format,
|
|
263
|
+
console_width=cls.Logging.console_width,
|
|
264
|
+
show_path=cls.Logging.show_path,
|
|
265
|
+
colors=colors_dict,
|
|
266
|
+
)
|
|
106
267
|
|
|
107
268
|
@classmethod
|
|
108
|
-
def
|
|
109
|
-
"""
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
default_config_path = os.path.join(os.path.dirname(__file__), 'config.yaml')
|
|
114
|
-
|
|
115
|
-
if user_config_file is None:
|
|
116
|
-
with open(default_config_path) as file:
|
|
117
|
-
new_config = yaml.safe_load(file)
|
|
118
|
-
elif not os.path.exists(user_config_file):
|
|
119
|
-
raise FileNotFoundError(f'Config file not found: {user_config_file}')
|
|
120
|
-
else:
|
|
121
|
-
with open(user_config_file) as user_file:
|
|
122
|
-
new_config = yaml.safe_load(user_file)
|
|
269
|
+
def load_from_file(cls, config_file: str | Path):
|
|
270
|
+
"""Load configuration from YAML file and apply it."""
|
|
271
|
+
config_path = Path(config_file)
|
|
272
|
+
if not config_path.exists():
|
|
273
|
+
raise FileNotFoundError(f'Config file not found: {config_file}')
|
|
123
274
|
|
|
124
|
-
|
|
125
|
-
|
|
275
|
+
with config_path.open() as file:
|
|
276
|
+
config_dict = yaml.safe_load(file)
|
|
277
|
+
cls._apply_config_dict(config_dict)
|
|
126
278
|
|
|
127
|
-
|
|
128
|
-
cls.logging = config_data.logging
|
|
129
|
-
cls.modeling = config_data.modeling
|
|
130
|
-
cls.config_name = config_data.config_name
|
|
279
|
+
cls.apply()
|
|
131
280
|
|
|
132
|
-
|
|
281
|
+
@classmethod
|
|
282
|
+
def _apply_config_dict(cls, config_dict: dict):
|
|
283
|
+
"""Apply configuration dictionary to class attributes."""
|
|
284
|
+
for key, value in config_dict.items():
|
|
285
|
+
if key == 'logging' and isinstance(value, dict):
|
|
286
|
+
for nested_key, nested_value in value.items():
|
|
287
|
+
if nested_key == 'colors' and isinstance(nested_value, dict):
|
|
288
|
+
# Handle nested colors under logging
|
|
289
|
+
for color_key, color_value in nested_value.items():
|
|
290
|
+
setattr(cls.Logging.Colors, color_key, color_value)
|
|
291
|
+
else:
|
|
292
|
+
setattr(cls.Logging, nested_key, nested_value)
|
|
293
|
+
elif key == 'modeling' and isinstance(value, dict):
|
|
294
|
+
for nested_key, nested_value in value.items():
|
|
295
|
+
setattr(cls.Modeling, nested_key, nested_value)
|
|
296
|
+
elif hasattr(cls, key):
|
|
297
|
+
setattr(cls, key, value)
|
|
133
298
|
|
|
134
299
|
@classmethod
|
|
135
300
|
def to_dict(cls):
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
301
|
+
"""Convert the configuration class into a dictionary for JSON serialization."""
|
|
302
|
+
return {
|
|
303
|
+
'config_name': cls.config_name,
|
|
304
|
+
'logging': {
|
|
305
|
+
'level': cls.Logging.level,
|
|
306
|
+
'file': cls.Logging.file,
|
|
307
|
+
'rich': cls.Logging.rich,
|
|
308
|
+
'console': cls.Logging.console,
|
|
309
|
+
'max_file_size': cls.Logging.max_file_size,
|
|
310
|
+
'backup_count': cls.Logging.backup_count,
|
|
311
|
+
'date_format': cls.Logging.date_format,
|
|
312
|
+
'format': cls.Logging.format,
|
|
313
|
+
'console_width': cls.Logging.console_width,
|
|
314
|
+
'show_path': cls.Logging.show_path,
|
|
315
|
+
'colors': {
|
|
316
|
+
'DEBUG': cls.Logging.Colors.DEBUG,
|
|
317
|
+
'INFO': cls.Logging.Colors.INFO,
|
|
318
|
+
'WARNING': cls.Logging.Colors.WARNING,
|
|
319
|
+
'ERROR': cls.Logging.Colors.ERROR,
|
|
320
|
+
'CRITICAL': cls.Logging.Colors.CRITICAL,
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
'modeling': {
|
|
324
|
+
'big': cls.Modeling.big,
|
|
325
|
+
'epsilon': cls.Modeling.epsilon,
|
|
326
|
+
'big_binary_bound': cls.Modeling.big_binary_bound,
|
|
327
|
+
},
|
|
328
|
+
}
|
|
154
329
|
|
|
155
330
|
|
|
156
331
|
class MultilineFormater(logging.Formatter):
|
|
332
|
+
"""Formatter that handles multi-line messages with consistent prefixes."""
|
|
333
|
+
|
|
334
|
+
def __init__(self, fmt=None, datefmt=None):
|
|
335
|
+
super().__init__(fmt=fmt, datefmt=datefmt)
|
|
336
|
+
|
|
157
337
|
def format(self, record):
|
|
158
338
|
message_lines = record.getMessage().split('\n')
|
|
159
|
-
|
|
160
|
-
# Prepare the log prefix (timestamp + log level)
|
|
161
339
|
timestamp = self.formatTime(record, self.datefmt)
|
|
162
|
-
log_level = record.levelname.ljust(8)
|
|
340
|
+
log_level = record.levelname.ljust(8)
|
|
163
341
|
log_prefix = f'{timestamp} | {log_level} |'
|
|
164
342
|
|
|
165
|
-
# Format all lines
|
|
166
343
|
first_line = [f'{log_prefix} {message_lines[0]}']
|
|
167
344
|
if len(message_lines) > 1:
|
|
168
345
|
lines = first_line + [f'{log_prefix} {line}' for line in message_lines[1:]]
|
|
@@ -173,96 +350,212 @@ class MultilineFormater(logging.Formatter):
|
|
|
173
350
|
|
|
174
351
|
|
|
175
352
|
class ColoredMultilineFormater(MultilineFormater):
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
'DEBUG': '\033[32m', # Green
|
|
179
|
-
'INFO': '\033[34m', # Blue
|
|
180
|
-
'WARNING': '\033[33m', # Yellow
|
|
181
|
-
'ERROR': '\033[31m', # Red
|
|
182
|
-
'CRITICAL': '\033[1m\033[31m', # Bold Red
|
|
183
|
-
}
|
|
353
|
+
"""Formatter that adds ANSI colors to multi-line log messages."""
|
|
354
|
+
|
|
184
355
|
RESET = '\033[0m'
|
|
185
356
|
|
|
357
|
+
def __init__(self, fmt=None, datefmt=None, colors=None):
|
|
358
|
+
super().__init__(fmt=fmt, datefmt=datefmt)
|
|
359
|
+
self.COLORS = (
|
|
360
|
+
colors
|
|
361
|
+
if colors is not None
|
|
362
|
+
else {
|
|
363
|
+
'DEBUG': '\033[32m',
|
|
364
|
+
'INFO': '\033[34m',
|
|
365
|
+
'WARNING': '\033[33m',
|
|
366
|
+
'ERROR': '\033[31m',
|
|
367
|
+
'CRITICAL': '\033[1m\033[31m',
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
|
|
186
371
|
def format(self, record):
|
|
187
372
|
lines = super().format(record).splitlines()
|
|
188
373
|
log_color = self.COLORS.get(record.levelname, self.RESET)
|
|
374
|
+
formatted_lines = [f'{log_color}{line}{self.RESET}' for line in lines]
|
|
375
|
+
return '\n'.join(formatted_lines)
|
|
189
376
|
|
|
190
|
-
# Create a formatted message for each line separately
|
|
191
|
-
formatted_lines = []
|
|
192
|
-
for line in lines:
|
|
193
|
-
formatted_lines.append(f'{log_color}{line}{self.RESET}')
|
|
194
377
|
|
|
195
|
-
|
|
378
|
+
def _create_console_handler(
|
|
379
|
+
use_rich: bool = False,
|
|
380
|
+
console_width: int = 120,
|
|
381
|
+
show_path: bool = False,
|
|
382
|
+
date_format: str = '%Y-%m-%d %H:%M:%S',
|
|
383
|
+
format: str = '%(message)s',
|
|
384
|
+
colors: dict[str, str] | None = None,
|
|
385
|
+
) -> logging.Handler:
|
|
386
|
+
"""Create a console (stdout) logging handler.
|
|
196
387
|
|
|
388
|
+
Args:
|
|
389
|
+
use_rich: If True, use RichHandler with color support.
|
|
390
|
+
console_width: Width of the console for Rich handler.
|
|
391
|
+
show_path: Show file paths in log messages (Rich only).
|
|
392
|
+
date_format: Date/time format string.
|
|
393
|
+
format: Log message format string.
|
|
394
|
+
colors: Dictionary of ANSI color codes for each log level.
|
|
197
395
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
396
|
+
Returns:
|
|
397
|
+
Configured logging handler (RichHandler or StreamHandler).
|
|
398
|
+
"""
|
|
399
|
+
if use_rich:
|
|
400
|
+
# Convert ANSI codes to Rich theme
|
|
401
|
+
if colors:
|
|
402
|
+
theme_dict = {}
|
|
403
|
+
for level, ansi_code in colors.items():
|
|
404
|
+
# Rich can parse ANSI codes directly!
|
|
405
|
+
try:
|
|
406
|
+
style = Style.from_ansi(ansi_code)
|
|
407
|
+
theme_dict[f'logging.level.{level.lower()}'] = style
|
|
408
|
+
except Exception:
|
|
409
|
+
# Fallback to default if parsing fails
|
|
410
|
+
pass
|
|
411
|
+
|
|
412
|
+
theme = Theme(theme_dict) if theme_dict else None
|
|
413
|
+
else:
|
|
414
|
+
theme = None
|
|
415
|
+
|
|
416
|
+
console = Console(width=console_width, theme=theme)
|
|
417
|
+
handler = RichHandler(
|
|
204
418
|
console=console,
|
|
205
419
|
rich_tracebacks=True,
|
|
206
420
|
omit_repeated_times=True,
|
|
207
|
-
show_path=
|
|
208
|
-
log_time_format=
|
|
209
|
-
)
|
|
210
|
-
rich_handler.setFormatter(logging.Formatter('%(message)s')) # Simplified formatting
|
|
211
|
-
|
|
212
|
-
return rich_handler
|
|
213
|
-
elif log_file is None:
|
|
214
|
-
# Regular Logger with custom formating enabled
|
|
215
|
-
file_handler = logging.StreamHandler()
|
|
216
|
-
file_handler.setFormatter(
|
|
217
|
-
ColoredMultilineFormater(
|
|
218
|
-
fmt='%(message)s',
|
|
219
|
-
datefmt='%Y-%m-%d %H:%M:%S',
|
|
220
|
-
)
|
|
421
|
+
show_path=show_path,
|
|
422
|
+
log_time_format=date_format,
|
|
221
423
|
)
|
|
222
|
-
|
|
424
|
+
handler.setFormatter(logging.Formatter(format))
|
|
223
425
|
else:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
426
|
+
handler = logging.StreamHandler()
|
|
427
|
+
handler.setFormatter(ColoredMultilineFormater(fmt=format, datefmt=date_format, colors=colors))
|
|
428
|
+
|
|
429
|
+
return handler
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _create_file_handler(
|
|
433
|
+
log_file: str,
|
|
434
|
+
max_file_size: int = 10_485_760,
|
|
435
|
+
backup_count: int = 5,
|
|
436
|
+
date_format: str = '%Y-%m-%d %H:%M:%S',
|
|
437
|
+
format: str = '%(message)s',
|
|
438
|
+
) -> RotatingFileHandler:
|
|
439
|
+
"""Create a rotating file handler to prevent huge log files.
|
|
233
440
|
|
|
441
|
+
Args:
|
|
442
|
+
log_file: Path to the log file.
|
|
443
|
+
max_file_size: Maximum size in bytes before rotation.
|
|
444
|
+
backup_count: Number of backup files to keep.
|
|
445
|
+
date_format: Date/time format string.
|
|
446
|
+
format: Log message format string.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
Configured RotatingFileHandler (without colors).
|
|
450
|
+
"""
|
|
451
|
+
handler = RotatingFileHandler(
|
|
452
|
+
log_file,
|
|
453
|
+
maxBytes=max_file_size,
|
|
454
|
+
backupCount=backup_count,
|
|
455
|
+
encoding='utf-8',
|
|
456
|
+
)
|
|
457
|
+
handler.setFormatter(MultilineFormater(fmt=format, datefmt=date_format))
|
|
458
|
+
return handler
|
|
234
459
|
|
|
235
|
-
|
|
460
|
+
|
|
461
|
+
def _setup_logging(
|
|
236
462
|
default_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] = 'INFO',
|
|
237
|
-
log_file: str | None =
|
|
463
|
+
log_file: str | None = None,
|
|
238
464
|
use_rich_handler: bool = False,
|
|
465
|
+
console: bool = False,
|
|
466
|
+
max_file_size: int = 10_485_760,
|
|
467
|
+
backup_count: int = 5,
|
|
468
|
+
date_format: str = '%Y-%m-%d %H:%M:%S',
|
|
469
|
+
format: str = '%(message)s',
|
|
470
|
+
console_width: int = 120,
|
|
471
|
+
show_path: bool = False,
|
|
472
|
+
colors: dict[str, str] | None = None,
|
|
239
473
|
):
|
|
240
|
-
"""
|
|
241
|
-
logger = logging.getLogger('flixopt') # Use a specific logger name for your package
|
|
242
|
-
logger.setLevel(get_logging_level_by_name(default_level))
|
|
243
|
-
# Clear existing handlers
|
|
244
|
-
if logger.hasHandlers():
|
|
245
|
-
logger.handlers.clear()
|
|
474
|
+
"""Internal function to setup logging - use CONFIG.apply() instead.
|
|
246
475
|
|
|
247
|
-
logger.
|
|
248
|
-
|
|
249
|
-
logger.addHandler(_get_logging_handler(log_file, use_rich_handler=False))
|
|
476
|
+
Configures the flixopt logger with console and/or file handlers.
|
|
477
|
+
If no handlers are configured, adds NullHandler (library best practice).
|
|
250
478
|
|
|
251
|
-
|
|
479
|
+
Args:
|
|
480
|
+
default_level: Logging level for the logger.
|
|
481
|
+
log_file: Path to log file (None to disable file logging).
|
|
482
|
+
use_rich_handler: Use Rich for enhanced console output.
|
|
483
|
+
console: Enable console logging.
|
|
484
|
+
max_file_size: Maximum log file size before rotation.
|
|
485
|
+
backup_count: Number of backup log files to keep.
|
|
486
|
+
date_format: Date/time format for log messages.
|
|
487
|
+
format: Log message format string.
|
|
488
|
+
console_width: Console width for Rich handler.
|
|
489
|
+
show_path: Show file paths in log messages (Rich only).
|
|
490
|
+
colors: ANSI color codes for each log level.
|
|
491
|
+
"""
|
|
492
|
+
logger = logging.getLogger('flixopt')
|
|
493
|
+
logger.setLevel(getattr(logging, default_level.upper()))
|
|
494
|
+
logger.propagate = False # Prevent duplicate logs
|
|
495
|
+
logger.handlers.clear()
|
|
496
|
+
|
|
497
|
+
if console:
|
|
498
|
+
logger.addHandler(
|
|
499
|
+
_create_console_handler(
|
|
500
|
+
use_rich=use_rich_handler,
|
|
501
|
+
console_width=console_width,
|
|
502
|
+
show_path=show_path,
|
|
503
|
+
date_format=date_format,
|
|
504
|
+
format=format,
|
|
505
|
+
colors=colors,
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if log_file:
|
|
510
|
+
logger.addHandler(
|
|
511
|
+
_create_file_handler(
|
|
512
|
+
log_file=log_file,
|
|
513
|
+
max_file_size=max_file_size,
|
|
514
|
+
backup_count=backup_count,
|
|
515
|
+
date_format=date_format,
|
|
516
|
+
format=format,
|
|
517
|
+
)
|
|
518
|
+
)
|
|
252
519
|
|
|
520
|
+
# Library best practice: NullHandler if no handlers configured
|
|
521
|
+
if not logger.handlers:
|
|
522
|
+
logger.addHandler(logging.NullHandler())
|
|
253
523
|
|
|
254
|
-
|
|
255
|
-
possible_logging_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
|
|
256
|
-
if level_name.upper() not in possible_logging_levels:
|
|
257
|
-
raise ValueError(f'Invalid logging level {level_name}')
|
|
258
|
-
else:
|
|
259
|
-
logging_level = getattr(logging, level_name.upper(), logging.WARNING)
|
|
260
|
-
return logging_level
|
|
524
|
+
return logger
|
|
261
525
|
|
|
262
526
|
|
|
263
527
|
def change_logging_level(level_name: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']):
|
|
528
|
+
"""
|
|
529
|
+
Change the logging level for the flixopt logger and all its handlers.
|
|
530
|
+
|
|
531
|
+
.. deprecated:: 2.1.11
|
|
532
|
+
Use ``CONFIG.Logging.level = level_name`` and ``CONFIG.apply()`` instead.
|
|
533
|
+
This function will be removed in version 3.0.0.
|
|
534
|
+
|
|
535
|
+
Parameters
|
|
536
|
+
----------
|
|
537
|
+
level_name : {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}
|
|
538
|
+
The logging level to set.
|
|
539
|
+
|
|
540
|
+
Examples
|
|
541
|
+
--------
|
|
542
|
+
>>> change_logging_level('DEBUG') # deprecated
|
|
543
|
+
>>> # Use this instead:
|
|
544
|
+
>>> CONFIG.Logging.level = 'DEBUG'
|
|
545
|
+
>>> CONFIG.apply()
|
|
546
|
+
"""
|
|
547
|
+
warnings.warn(
|
|
548
|
+
'change_logging_level is deprecated and will be removed in version 3.0.0. '
|
|
549
|
+
'Use CONFIG.Logging.level = level_name and CONFIG.apply() instead.',
|
|
550
|
+
DeprecationWarning,
|
|
551
|
+
stacklevel=2,
|
|
552
|
+
)
|
|
264
553
|
logger = logging.getLogger('flixopt')
|
|
265
|
-
logging_level =
|
|
554
|
+
logging_level = getattr(logging, level_name.upper())
|
|
266
555
|
logger.setLevel(logging_level)
|
|
267
556
|
for handler in logger.handlers:
|
|
268
557
|
handler.setLevel(logging_level)
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
# Initialize default config
|
|
561
|
+
CONFIG.apply()
|
flixopt/elements.py
CHANGED
|
@@ -248,7 +248,7 @@ class Flow(Element):
|
|
|
248
248
|
size: Flow capacity or nominal rating. Can be:
|
|
249
249
|
- Scalar value for fixed capacity
|
|
250
250
|
- InvestParameters for investment-based sizing decisions
|
|
251
|
-
- None to use large default value (CONFIG.
|
|
251
|
+
- None to use large default value (CONFIG.Modeling.big)
|
|
252
252
|
relative_minimum: Minimum flow rate as fraction of size.
|
|
253
253
|
Example: 0.2 means flow cannot go below 20% of rated capacity.
|
|
254
254
|
relative_maximum: Maximum flow rate as fraction of size (typically 1.0).
|
|
@@ -356,7 +356,7 @@ class Flow(Element):
|
|
|
356
356
|
`relative_maximum` for upper bounds on optimization variables.
|
|
357
357
|
|
|
358
358
|
Notes:
|
|
359
|
-
- Default size (CONFIG.
|
|
359
|
+
- Default size (CONFIG.Modeling.big) is used when size=None
|
|
360
360
|
- list inputs for previous_flow_rate are converted to NumPy arrays
|
|
361
361
|
- Flow direction is determined by component input/output designation
|
|
362
362
|
|
|
@@ -383,7 +383,7 @@ class Flow(Element):
|
|
|
383
383
|
meta_data: dict | None = None,
|
|
384
384
|
):
|
|
385
385
|
super().__init__(label, meta_data=meta_data)
|
|
386
|
-
self.size = CONFIG.
|
|
386
|
+
self.size = CONFIG.Modeling.big if size is None else size
|
|
387
387
|
self.relative_minimum = relative_minimum
|
|
388
388
|
self.relative_maximum = relative_maximum
|
|
389
389
|
self.fixed_relative_profile = fixed_relative_profile
|
|
@@ -455,11 +455,11 @@ class Flow(Element):
|
|
|
455
455
|
raise PlausibilityError(self.label_full + ': Take care, that relative_minimum <= relative_maximum!')
|
|
456
456
|
|
|
457
457
|
if (
|
|
458
|
-
self.size == CONFIG.
|
|
458
|
+
self.size == CONFIG.Modeling.big and self.fixed_relative_profile is not None
|
|
459
459
|
): # Default Size --> Most likely by accident
|
|
460
460
|
logger.warning(
|
|
461
461
|
f'Flow "{self.label}" has no size assigned, but a "fixed_relative_profile". '
|
|
462
|
-
f'The default size is {CONFIG.
|
|
462
|
+
f'The default size is {CONFIG.Modeling.big}. As "flow_rate = size * fixed_relative_profile", '
|
|
463
463
|
f'the resulting flow_rate will be very high. To fix this, assign a size to the Flow {self}.'
|
|
464
464
|
)
|
|
465
465
|
|
flixopt/features.py
CHANGED
|
@@ -143,7 +143,7 @@ class InvestmentModel(Model):
|
|
|
143
143
|
# eq2: P_invest >= isInvested * max(epsilon, investSize_min)
|
|
144
144
|
self.add(
|
|
145
145
|
self._model.add_constraints(
|
|
146
|
-
self.size >= self.is_invested * np.maximum(CONFIG.
|
|
146
|
+
self.size >= self.is_invested * np.maximum(CONFIG.Modeling.epsilon, self.parameters.minimum_size),
|
|
147
147
|
name=f'{self.label_full}|is_invested_lb',
|
|
148
148
|
),
|
|
149
149
|
'is_invested_lb',
|
|
@@ -304,7 +304,7 @@ class StateModel(Model):
|
|
|
304
304
|
# Constraint: on * lower_bound <= def_var
|
|
305
305
|
self.add(
|
|
306
306
|
self._model.add_constraints(
|
|
307
|
-
self.on * np.maximum(CONFIG.
|
|
307
|
+
self.on * np.maximum(CONFIG.Modeling.epsilon, lb) <= def_var, name=f'{self.label_full}|on_con1'
|
|
308
308
|
),
|
|
309
309
|
'on_con1',
|
|
310
310
|
)
|
|
@@ -314,7 +314,7 @@ class StateModel(Model):
|
|
|
314
314
|
else:
|
|
315
315
|
# Case for multiple defining variables
|
|
316
316
|
ub = sum(bound[1] for bound in self._defining_bounds) / nr_of_def_vars
|
|
317
|
-
lb = CONFIG.
|
|
317
|
+
lb = CONFIG.Modeling.epsilon # TODO: Can this be a bigger value? (maybe the smallest bound?)
|
|
318
318
|
|
|
319
319
|
# Constraint: on * epsilon <= sum(all_defining_variables)
|
|
320
320
|
self.add(
|
|
@@ -337,7 +337,7 @@ class StateModel(Model):
|
|
|
337
337
|
@property
|
|
338
338
|
def previous_states(self) -> np.ndarray:
|
|
339
339
|
"""Computes the previous states {0, 1} of defining variables as a binary array from their previous values."""
|
|
340
|
-
return StateModel.compute_previous_states(self._previous_values, epsilon=CONFIG.
|
|
340
|
+
return StateModel.compute_previous_states(self._previous_values, epsilon=CONFIG.Modeling.epsilon)
|
|
341
341
|
|
|
342
342
|
@property
|
|
343
343
|
def previous_on_states(self) -> np.ndarray:
|
|
@@ -603,14 +603,14 @@ class ConsecutiveStateModel(Model):
|
|
|
603
603
|
elif np.isscalar(binary_values) and not np.isscalar(hours_per_timestep):
|
|
604
604
|
return binary_values * hours_per_timestep[-1]
|
|
605
605
|
|
|
606
|
-
if np.isclose(binary_values[-1], 0, atol=CONFIG.
|
|
606
|
+
if np.isclose(binary_values[-1], 0, atol=CONFIG.Modeling.epsilon):
|
|
607
607
|
return 0
|
|
608
608
|
|
|
609
609
|
if np.isscalar(hours_per_timestep):
|
|
610
610
|
hours_per_timestep = np.ones(len(binary_values)) * hours_per_timestep
|
|
611
611
|
hours_per_timestep: np.ndarray
|
|
612
612
|
|
|
613
|
-
indexes_with_zero_values = np.where(np.isclose(binary_values, 0, atol=CONFIG.
|
|
613
|
+
indexes_with_zero_values = np.where(np.isclose(binary_values, 0, atol=CONFIG.Modeling.epsilon))[0]
|
|
614
614
|
if len(indexes_with_zero_values) == 0:
|
|
615
615
|
nr_of_indexes_with_consecutive_ones = len(binary_values)
|
|
616
616
|
else:
|
flixopt/interface.py
CHANGED
|
@@ -650,10 +650,10 @@ class InvestParameters(Interface):
|
|
|
650
650
|
fixed_size: When specified, creates a binary investment decision at exactly
|
|
651
651
|
this size. When None, allows continuous sizing between minimum and maximum bounds.
|
|
652
652
|
minimum_size: Lower bound for continuous sizing decisions. Defaults to a small
|
|
653
|
-
positive value (CONFIG.
|
|
653
|
+
positive value (CONFIG.Modeling.epsilon) to avoid numerical issues.
|
|
654
654
|
Ignored when fixed_size is specified.
|
|
655
655
|
maximum_size: Upper bound for continuous sizing decisions. Defaults to a large
|
|
656
|
-
value (CONFIG.
|
|
656
|
+
value (CONFIG.Modeling.big) representing unlimited capacity.
|
|
657
657
|
Ignored when fixed_size is specified.
|
|
658
658
|
optional: Controls whether investment is required. When True (default),
|
|
659
659
|
optimization can choose not to invest. When False, forces investment
|
|
@@ -833,8 +833,8 @@ class InvestParameters(Interface):
|
|
|
833
833
|
self.optional = optional
|
|
834
834
|
self.specific_effects: EffectValuesUserScalar = specific_effects or {}
|
|
835
835
|
self.piecewise_effects = piecewise_effects
|
|
836
|
-
self._minimum_size = minimum_size if minimum_size is not None else CONFIG.
|
|
837
|
-
self._maximum_size = maximum_size if maximum_size is not None else CONFIG.
|
|
836
|
+
self._minimum_size = minimum_size if minimum_size is not None else CONFIG.Modeling.epsilon
|
|
837
|
+
self._maximum_size = maximum_size if maximum_size is not None else CONFIG.Modeling.big # default maximum
|
|
838
838
|
|
|
839
839
|
def transform_data(self, flow_system: FlowSystem):
|
|
840
840
|
self.fix_effects = flow_system.effects.create_effect_values_dict(self.fix_effects)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flixopt
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
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>
|
|
@@ -53,10 +53,10 @@ Provides-Extra: dev
|
|
|
53
53
|
Requires-Dist: pytest==8.4.2; extra == "dev"
|
|
54
54
|
Requires-Dist: pytest-xdist==3.8.0; extra == "dev"
|
|
55
55
|
Requires-Dist: nbformat==5.10.4; extra == "dev"
|
|
56
|
-
Requires-Dist: ruff==0.13.
|
|
56
|
+
Requires-Dist: ruff==0.13.2; extra == "dev"
|
|
57
57
|
Requires-Dist: pre-commit==4.3.0; extra == "dev"
|
|
58
58
|
Requires-Dist: pyvis==0.3.2; extra == "dev"
|
|
59
|
-
Requires-Dist: tsam==2.3.
|
|
59
|
+
Requires-Dist: tsam==2.3.9; extra == "dev"
|
|
60
60
|
Requires-Dist: scipy==1.15.1; extra == "dev"
|
|
61
61
|
Requires-Dist: gurobipy==12.0.3; extra == "dev"
|
|
62
62
|
Requires-Dist: dash==3.0.0; extra == "dev"
|
|
@@ -65,7 +65,7 @@ Requires-Dist: dash-daq==0.6.0; extra == "dev"
|
|
|
65
65
|
Requires-Dist: networkx==3.0.0; extra == "dev"
|
|
66
66
|
Requires-Dist: werkzeug==3.0.0; extra == "dev"
|
|
67
67
|
Provides-Extra: docs
|
|
68
|
-
Requires-Dist: mkdocs-material==9.6.
|
|
68
|
+
Requires-Dist: mkdocs-material==9.6.20; extra == "docs"
|
|
69
69
|
Requires-Dist: mkdocstrings-python==1.18.2; extra == "docs"
|
|
70
70
|
Requires-Dist: mkdocs-table-reader-plugin==3.1.0; extra == "docs"
|
|
71
71
|
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "docs"
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
flixopt/__init__.py,sha256=
|
|
1
|
+
flixopt/__init__.py,sha256=WodgPpH8UHORIGh9EucATUTK6ct4ebPDBuSxymZgl3E,666
|
|
2
2
|
flixopt/aggregation.py,sha256=BFy2ngr0a0By5wfBZet9fX8X5ZPmkn1x6HMRaw1ZsTo,16892
|
|
3
|
-
flixopt/calculation.py,sha256=
|
|
3
|
+
flixopt/calculation.py,sha256=2Dfgpwjmg_dCz9jWHOUpEwIgzggvIPXhiIg8nGl1YMc,24476
|
|
4
4
|
flixopt/commons.py,sha256=ZNlUN1z-h9OGHPo-s-n5OLlJaoPZKVGcAdRyGKpMk4M,1256
|
|
5
5
|
flixopt/components.py,sha256=GhALvEkjGagm6iy2VxU3lVkUPlQOtsnFmu6ZsUDrcwM,54632
|
|
6
|
-
flixopt/config.py,sha256=
|
|
7
|
-
flixopt/config.yaml,sha256=imzAnnhcJhIfKNTTXFB5Td7Pvk5ARn5j720k-oGGRug,392
|
|
6
|
+
flixopt/config.py,sha256=Gy2k1J4A-TS01jILwgUy9xp38AEMawU_pk0_XseufKI,19889
|
|
8
7
|
flixopt/core.py,sha256=HnXnUXnAL8yqBExdsXGgwu5HnfWRH0omjjMKgp_SBKc,37824
|
|
9
8
|
flixopt/effects.py,sha256=HcalPMUaAfuzD2p1OeQ9nLY2vmHv_GxwyGLLeqNGNZ8,19435
|
|
10
|
-
flixopt/elements.py,sha256=
|
|
11
|
-
flixopt/features.py,sha256=
|
|
9
|
+
flixopt/elements.py,sha256=tzQVeh1z98CDv2nm8wiwxY_B2cKMLLZyYwGAoYsNLso,33535
|
|
10
|
+
flixopt/features.py,sha256=hX2485foIxzKQ6DMv73FlIXcSmVhe6n1taeLmfyXE-A,43891
|
|
12
11
|
flixopt/flow_system.py,sha256=tOOGtEzZFISL87lmxNOsYf9uiSfE9XLUzenDBsSf8ns,20895
|
|
13
|
-
flixopt/interface.py,sha256=
|
|
12
|
+
flixopt/interface.py,sha256=sBJJxVKIPrl3w8NQ-BRnr_1C65A5i1ZWispPdV6YeVM,47575
|
|
14
13
|
flixopt/io.py,sha256=huFIS1c_1qXR5LJBl1hUmaN_nCxqj0GC1MUqvzObMfA,11477
|
|
15
14
|
flixopt/linear_converters.py,sha256=rKa0AZlJHgDsPF_LVsvrhxhsmVTRanPE0NuKP7OICtg,22668
|
|
16
15
|
flixopt/network_app.py,sha256=LnVAlAgzL1BgMYLsJ20a62j6nQUmNccF1zo4ACUXzL4,29433
|
|
@@ -19,8 +18,8 @@ flixopt/results.py,sha256=zSOFbDV7gpvoEUvRvmlP5JUHe-QhOJOuGKgZUsFjiGQ,40701
|
|
|
19
18
|
flixopt/solvers.py,sha256=m38Smc22MJfHYMiqfNf1MA3OmvbTRm5OWS9nECkDdQk,2355
|
|
20
19
|
flixopt/structure.py,sha256=vyD1lc80NH3JLexKJuar9btgHhEbcNEmihCQkBWea8k,26254
|
|
21
20
|
flixopt/utils.py,sha256=a-YKR7C7HtD8dSIcxzlJTgryV5HMS7zELSXNYr_Lz9Q,1775
|
|
22
|
-
flixopt-2.
|
|
23
|
-
flixopt-2.
|
|
24
|
-
flixopt-2.
|
|
25
|
-
flixopt-2.
|
|
26
|
-
flixopt-2.
|
|
21
|
+
flixopt-2.2.0.dist-info/licenses/LICENSE,sha256=HKsZnbrM_3Rvnr_u9cWSG90cBsj5_slaqI_z_qcxnGI,1118
|
|
22
|
+
flixopt-2.2.0.dist-info/METADATA,sha256=yvVKc1LFjiGo8zDKAVQcaSsMzlrEDbXsn3nhoi4kg0o,8385
|
|
23
|
+
flixopt-2.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
flixopt-2.2.0.dist-info/top_level.txt,sha256=fanTzb9NylIXfv6Ic7spU97fVmRgGDPKvI_91tw4S3E,8
|
|
25
|
+
flixopt-2.2.0.dist-info/RECORD,,
|
flixopt/config.yaml
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# Default configuration of flixopt
|
|
2
|
-
config_name: flixopt # Name of the config file. This has no effect on the configuration itself.
|
|
3
|
-
logging:
|
|
4
|
-
level: INFO
|
|
5
|
-
file: flixopt.log
|
|
6
|
-
rich: false # logging output is formatted using rich. This is only advisable when using a proper terminal
|
|
7
|
-
modeling:
|
|
8
|
-
BIG: 10000000 # 1e notation not possible in yaml
|
|
9
|
-
EPSILON: 0.00001
|
|
10
|
-
BIG_BINARY_BOUND: 100000
|
|
File without changes
|
|
File without changes
|
|
File without changes
|