flixopt 2.2.0b0__py3-none-any.whl → 2.2.0rc2__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.

Files changed (48) hide show
  1. docs/examples/00-Minimal Example.md +1 -1
  2. docs/examples/01-Basic Example.md +1 -1
  3. docs/examples/02-Complex Example.md +1 -1
  4. docs/examples/index.md +1 -1
  5. docs/faq/contribute.md +26 -14
  6. docs/faq/index.md +1 -1
  7. docs/javascripts/mathjax.js +1 -1
  8. docs/user-guide/Mathematical Notation/Bus.md +1 -1
  9. docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +13 -13
  10. docs/user-guide/Mathematical Notation/Flow.md +1 -1
  11. docs/user-guide/Mathematical Notation/LinearConverter.md +2 -2
  12. docs/user-guide/Mathematical Notation/Piecewise.md +1 -1
  13. docs/user-guide/Mathematical Notation/Storage.md +1 -1
  14. docs/user-guide/Mathematical Notation/index.md +1 -1
  15. docs/user-guide/Mathematical Notation/others.md +1 -1
  16. docs/user-guide/index.md +2 -2
  17. flixopt/__init__.py +5 -0
  18. flixopt/aggregation.py +0 -1
  19. flixopt/calculation.py +40 -72
  20. flixopt/commons.py +10 -1
  21. flixopt/components.py +326 -154
  22. flixopt/core.py +459 -966
  23. flixopt/effects.py +67 -270
  24. flixopt/elements.py +76 -84
  25. flixopt/features.py +172 -154
  26. flixopt/flow_system.py +70 -99
  27. flixopt/interface.py +315 -147
  28. flixopt/io.py +27 -56
  29. flixopt/linear_converters.py +3 -3
  30. flixopt/network_app.py +755 -0
  31. flixopt/plotting.py +16 -34
  32. flixopt/results.py +108 -806
  33. flixopt/structure.py +11 -67
  34. flixopt/utils.py +9 -6
  35. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/METADATA +63 -42
  36. flixopt-2.2.0rc2.dist-info/RECORD +54 -0
  37. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/WHEEL +1 -1
  38. scripts/extract_release_notes.py +45 -0
  39. docs/release-notes/_template.txt +0 -32
  40. docs/release-notes/index.md +0 -7
  41. docs/release-notes/v2.0.0.md +0 -93
  42. docs/release-notes/v2.0.1.md +0 -12
  43. docs/release-notes/v2.1.0.md +0 -31
  44. docs/release-notes/v2.2.0.md +0 -55
  45. docs/user-guide/Mathematical Notation/Investment.md +0 -115
  46. flixopt-2.2.0b0.dist-info/RECORD +0 -59
  47. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/licenses/LICENSE +0 -0
  48. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/top_level.txt +0 -0
flixopt/io.py CHANGED
@@ -23,7 +23,7 @@ def replace_timeseries(obj, mode: Literal['name', 'stats', 'data'] = 'name'):
23
23
  return [replace_timeseries(v, mode) for v in obj]
24
24
  elif isinstance(obj, TimeSeries): # Adjust this based on the actual class
25
25
  if obj.all_equal:
26
- return obj.selected_data.values.max().item()
26
+ return obj.active_data.values[0].item()
27
27
  elif mode == 'name':
28
28
  return f'::::{obj.name}'
29
29
  elif mode == 'stats':
@@ -44,7 +44,7 @@ def insert_dataarray(obj, ds: xr.Dataset):
44
44
  return [insert_dataarray(v, ds) for v in obj]
45
45
  elif isinstance(obj, str) and obj.startswith('::::'):
46
46
  da = ds[obj[4:]]
47
- if 'time' in da.dims and da.isel(time=-1).isnull().any().item():
47
+ if da.isel(time=-1).isnull():
48
48
  return da.isel(time=slice(0, -1))
49
49
  return da
50
50
  else:
@@ -79,17 +79,15 @@ def _save_to_yaml(data, output_file='formatted_output.yaml'):
79
79
  output_file (str): Path to output YAML file
80
80
  """
81
81
  # Process strings to normalize all newlines and handle special patterns
82
- processed_data = _normalize_complex_data(data)
82
+ processed_data = _process_complex_strings(data)
83
83
 
84
84
  # Define a custom representer for strings
85
85
  def represent_str(dumper, data):
86
- # Use literal block style (|) for multi-line strings
86
+ # Use literal block style (|) for any string with newlines
87
87
  if '\n' in data:
88
- # Clean up formatting for literal block style
89
- data = data.strip() # Remove leading/trailing whitespace
90
88
  return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
91
89
 
92
- # Use quoted style for strings with special characters
90
+ # Use quoted style for strings with special characters to ensure proper parsing
93
91
  elif any(char in data for char in ':`{}[]#,&*!|>%@'):
94
92
  return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"')
95
93
 
@@ -99,80 +97,53 @@ def _save_to_yaml(data, output_file='formatted_output.yaml'):
99
97
  # Add the string representer to SafeDumper
100
98
  yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper)
101
99
 
102
- # Configure dumper options for better formatting
103
- class CustomDumper(yaml.SafeDumper):
104
- def increase_indent(self, flow=False, indentless=False):
105
- return super(CustomDumper, self).increase_indent(flow, False)
106
-
107
100
  # Write to file with settings that ensure proper formatting
108
101
  with open(output_file, 'w', encoding='utf-8') as file:
109
102
  yaml.dump(
110
103
  processed_data,
111
104
  file,
112
- Dumper=CustomDumper,
105
+ Dumper=yaml.SafeDumper,
113
106
  sort_keys=False, # Preserve dictionary order
114
107
  default_flow_style=False, # Use block style for mappings
115
- width=1000, # Set a reasonable line width
108
+ width=float('inf'), # Don't wrap long lines
116
109
  allow_unicode=True, # Support Unicode characters
117
- indent=2, # Set consistent indentation
118
110
  )
119
111
 
120
112
 
121
- def _normalize_complex_data(data):
113
+ def _process_complex_strings(data):
122
114
  """
123
- Recursively normalize strings in complex data structures.
124
-
125
- Handles dictionaries, lists, and strings, applying various text normalization
126
- rules while preserving important formatting elements.
115
+ Process dictionary data recursively with comprehensive string normalization.
116
+ Handles various types of strings and special formatting.
127
117
 
128
118
  Args:
129
- data: Any data type (dict, list, str, or primitive)
119
+ data: The data to process (dict, list, str, or other)
130
120
 
131
121
  Returns:
132
- Data with all strings normalized according to defined rules
122
+ Processed data with normalized strings
133
123
  """
134
124
  if isinstance(data, dict):
135
- return {key: _normalize_complex_data(value) for key, value in data.items()}
136
-
125
+ return {k: _process_complex_strings(v) for k, v in data.items()}
137
126
  elif isinstance(data, list):
138
- return [_normalize_complex_data(item) for item in data]
139
-
127
+ return [_process_complex_strings(item) for item in data]
140
128
  elif isinstance(data, str):
141
- return _normalize_string_content(data)
142
-
143
- else:
144
- return data
145
-
146
-
147
- def _normalize_string_content(text):
148
- """
149
- Apply comprehensive string normalization rules.
150
-
151
- Args:
152
- text: The string to normalize
129
+ # Step 1: Normalize line endings to \n
130
+ normalized = data.replace('\r\n', '\n').replace('\r', '\n')
153
131
 
154
- Returns:
155
- Normalized string with standardized formatting
156
- """
157
- # Standardize line endings
158
- text = text.replace('\r\n', '\n').replace('\r', '\n')
132
+ # Step 2: Handle escaped newlines with robust regex
133
+ normalized = re.sub(r'(?<!\\)\\n', '\n', normalized)
159
134
 
160
- # Convert escaped newlines to actual newlines (avoiding double-backslashes)
161
- text = re.sub(r'(?<!\\)\\n', '\n', text)
135
+ # Step 3: Handle unnecessary double backslashes
136
+ normalized = re.sub(r'\\\\(n)', r'\\\1', normalized)
162
137
 
163
- # Normalize double backslashes before specific escape sequences
164
- text = re.sub(r'\\\\([rtn])', r'\\\1', text)
138
+ # Step 4: Ensure proper formatting of "[time: N]:\n---------"
139
+ normalized = re.sub(r'(\[time: \d+\]):\s*\\?n', r'\1:\n', normalized)
165
140
 
166
- # Standardize constraint headers format
167
- text = re.sub(r'Constraint\s*`([^`]+)`\s*(?:\\n|[\s\n]*)', r'Constraint `\1`\n', text)
141
+ # Step 5: Ensure "Constraint `...`" patterns are properly formatted
142
+ normalized = re.sub(r'Constraint `([^`]+)`\\?n', r'Constraint `\1`\n', normalized)
168
143
 
169
- # Clean up ellipsis patterns
170
- text = re.sub(r'[\t ]*(\.\.\.)', r'\1', text)
171
-
172
- # Limit consecutive newlines (max 2)
173
- text = re.sub(r'\n{3,}', '\n\n', text)
174
-
175
- return text.strip()
144
+ return normalized
145
+ else:
146
+ return data
176
147
 
177
148
 
178
149
  def document_linopy_model(model: linopy.Model, path: pathlib.Path = None) -> Dict[str, str]:
@@ -165,7 +165,7 @@ class CoolingTower(LinearConverter):
165
165
  label,
166
166
  inputs=[P_el, Q_th],
167
167
  outputs=[],
168
- conversion_factors=[{P_el.label: -1, Q_th.label: specific_electricity_demand}],
168
+ conversion_factors=[{P_el.label: 1, Q_th.label: -specific_electricity_demand}],
169
169
  on_off_parameters=on_off_parameters,
170
170
  meta_data=meta_data,
171
171
  )
@@ -177,12 +177,12 @@ class CoolingTower(LinearConverter):
177
177
 
178
178
  @property
179
179
  def specific_electricity_demand(self):
180
- return self.conversion_factors[0][self.Q_th.label]
180
+ return -self.conversion_factors[0][self.Q_th.label]
181
181
 
182
182
  @specific_electricity_demand.setter
183
183
  def specific_electricity_demand(self, value):
184
184
  check_bounds(value, 'specific_electricity_demand', self.label_full, 0, 1)
185
- self.conversion_factors[0][self.Q_th.label] = value
185
+ self.conversion_factors[0][self.Q_th.label] = -value
186
186
 
187
187
 
188
188
  @register_class_for_io