pyconvexity 0.4.0__py3-none-any.whl → 0.4.1__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 (43) hide show
  1. pyconvexity/__init__.py +87 -46
  2. pyconvexity/_version.py +1 -1
  3. pyconvexity/core/__init__.py +3 -5
  4. pyconvexity/core/database.py +111 -103
  5. pyconvexity/core/errors.py +16 -10
  6. pyconvexity/core/types.py +61 -54
  7. pyconvexity/data/__init__.py +0 -1
  8. pyconvexity/data/loaders/cache.py +65 -64
  9. pyconvexity/data/schema/01_core_schema.sql +134 -234
  10. pyconvexity/data/schema/02_data_metadata.sql +38 -168
  11. pyconvexity/data/schema/03_validation_data.sql +327 -264
  12. pyconvexity/data/sources/gem.py +169 -139
  13. pyconvexity/io/__init__.py +4 -10
  14. pyconvexity/io/excel_exporter.py +694 -480
  15. pyconvexity/io/excel_importer.py +817 -545
  16. pyconvexity/io/netcdf_exporter.py +66 -61
  17. pyconvexity/io/netcdf_importer.py +850 -619
  18. pyconvexity/models/__init__.py +109 -59
  19. pyconvexity/models/attributes.py +197 -178
  20. pyconvexity/models/carriers.py +70 -67
  21. pyconvexity/models/components.py +260 -236
  22. pyconvexity/models/network.py +202 -284
  23. pyconvexity/models/results.py +65 -55
  24. pyconvexity/models/scenarios.py +58 -88
  25. pyconvexity/solvers/__init__.py +5 -5
  26. pyconvexity/solvers/pypsa/__init__.py +3 -3
  27. pyconvexity/solvers/pypsa/api.py +150 -134
  28. pyconvexity/solvers/pypsa/batch_loader.py +165 -162
  29. pyconvexity/solvers/pypsa/builder.py +390 -291
  30. pyconvexity/solvers/pypsa/constraints.py +184 -162
  31. pyconvexity/solvers/pypsa/solver.py +968 -663
  32. pyconvexity/solvers/pypsa/storage.py +1377 -671
  33. pyconvexity/timeseries.py +63 -60
  34. pyconvexity/validation/__init__.py +14 -6
  35. pyconvexity/validation/rules.py +95 -84
  36. pyconvexity-0.4.1.dist-info/METADATA +46 -0
  37. pyconvexity-0.4.1.dist-info/RECORD +42 -0
  38. pyconvexity/data/schema/04_scenario_schema.sql +0 -122
  39. pyconvexity/data/schema/migrate_add_geometries.sql +0 -73
  40. pyconvexity-0.4.0.dist-info/METADATA +0 -138
  41. pyconvexity-0.4.0.dist-info/RECORD +0 -44
  42. {pyconvexity-0.4.0.dist-info → pyconvexity-0.4.1.dist-info}/WHEEL +0 -0
  43. {pyconvexity-0.4.0.dist-info → pyconvexity-0.4.1.dist-info}/top_level.txt +0 -0
@@ -13,180 +13,185 @@ from pyconvexity.solvers.pypsa import build_pypsa_network
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
+
16
17
  class NetCDFModelExporter:
17
18
  """Export network model to PyPSA NetCDF format"""
18
-
19
+
19
20
  def __init__(self):
20
21
  self.logger = logging.getLogger(__name__)
21
-
22
+
22
23
  def export_to_netcdf(
23
24
  self,
24
25
  db_path: str,
25
- network_id: int,
26
26
  output_path: str,
27
27
  scenario_id: Optional[int] = None,
28
- progress_callback: Optional[Callable[[int, str], None]] = None
28
+ progress_callback: Optional[Callable[[int, str], None]] = None,
29
29
  ) -> Dict[str, Any]:
30
30
  """
31
- Export network from database to PyPSA NetCDF format
32
-
31
+ Export network from database to PyPSA NetCDF format (single network per database).
32
+
33
33
  This method leverages the existing pyconvexity PyPSA infrastructure to build
34
34
  a network from the database and then export it to NetCDF format.
35
-
35
+
36
36
  Args:
37
37
  db_path: Path to the database file
38
- network_id: ID of the network to export
39
38
  output_path: Path where to save the NetCDF file
40
- scenario_id: Optional scenario ID (uses master scenario if None)
39
+ scenario_id: Optional scenario ID (NULL for base network)
41
40
  progress_callback: Optional callback for progress updates
42
-
41
+
43
42
  Returns:
44
43
  Dictionary with export results and statistics
45
44
  """
46
45
  try:
47
46
  if progress_callback:
48
47
  progress_callback(0, "Starting NetCDF export...")
49
-
48
+
50
49
  # Build PyPSA network from database using existing infrastructure
51
50
  if progress_callback:
52
51
  progress_callback(10, "Building PyPSA network from database...")
53
-
52
+
54
53
  network = build_pypsa_network(
55
54
  db_path=db_path,
56
- network_id=network_id,
57
55
  scenario_id=scenario_id,
58
- progress_callback=self._wrap_progress_callback(progress_callback, 10, 80)
56
+ progress_callback=self._wrap_progress_callback(
57
+ progress_callback, 10, 80
58
+ ),
59
59
  )
60
-
60
+
61
61
  if progress_callback:
62
62
  progress_callback(80, "Exporting to NetCDF...")
63
-
63
+
64
64
  # Export to NetCDF using PyPSA's built-in method
65
65
  network.export_to_netcdf(output_path)
66
-
66
+
67
67
  if progress_callback:
68
68
  progress_callback(100, "NetCDF export completed")
69
-
69
+
70
70
  # Get statistics
71
71
  stats = self._get_network_stats(network)
72
-
72
+
73
73
  return {
74
74
  "success": True,
75
75
  "message": f"Network exported to NetCDF: {output_path}",
76
76
  "output_path": output_path,
77
- "stats": stats
77
+ "stats": stats,
78
78
  }
79
-
79
+
80
80
  except Exception as e:
81
81
  self.logger.error(f"NetCDF export failed: {e}", exc_info=True)
82
82
  if progress_callback:
83
83
  progress_callback(None, f"Export failed: {str(e)}")
84
84
  raise
85
-
85
+
86
86
  def export_to_csv(
87
87
  self,
88
88
  db_path: str,
89
- network_id: int,
90
89
  output_directory: str,
91
90
  scenario_id: Optional[int] = None,
92
- progress_callback: Optional[Callable[[int, str], None]] = None
91
+ progress_callback: Optional[Callable[[int, str], None]] = None,
93
92
  ) -> Dict[str, Any]:
94
93
  """
95
- Export network from database to PyPSA CSV format
96
-
94
+ Export network from database to PyPSA CSV format (single network per database).
95
+
97
96
  Args:
98
97
  db_path: Path to the database file
99
- network_id: ID of the network to export
100
98
  output_directory: Directory where to save CSV files
101
- scenario_id: Optional scenario ID (uses master scenario if None)
99
+ scenario_id: Optional scenario ID (NULL for base network)
102
100
  progress_callback: Optional callback for progress updates
103
-
101
+
104
102
  Returns:
105
103
  Dictionary with export results and statistics
106
104
  """
107
105
  try:
108
106
  if progress_callback:
109
107
  progress_callback(0, "Starting CSV export...")
110
-
108
+
111
109
  # Ensure output directory exists
112
110
  Path(output_directory).mkdir(parents=True, exist_ok=True)
113
-
111
+
114
112
  # Build PyPSA network from database using existing infrastructure
115
113
  if progress_callback:
116
114
  progress_callback(10, "Building PyPSA network from database...")
117
-
115
+
118
116
  network = build_pypsa_network(
119
117
  db_path=db_path,
120
- network_id=network_id,
121
118
  scenario_id=scenario_id,
122
- progress_callback=self._wrap_progress_callback(progress_callback, 10, 80)
119
+ progress_callback=self._wrap_progress_callback(
120
+ progress_callback, 10, 80
121
+ ),
123
122
  )
124
-
123
+
125
124
  if progress_callback:
126
125
  progress_callback(80, "Exporting to CSV...")
127
-
126
+
128
127
  # Export to CSV using PyPSA's built-in method
129
128
  network.export_to_csv_folder(output_directory)
130
-
129
+
131
130
  if progress_callback:
132
131
  progress_callback(100, "CSV export completed")
133
-
132
+
134
133
  # Get statistics
135
134
  stats = self._get_network_stats(network)
136
-
135
+
137
136
  return {
138
137
  "success": True,
139
138
  "message": f"Network exported to CSV: {output_directory}",
140
139
  "output_directory": output_directory,
141
- "stats": stats
140
+ "stats": stats,
142
141
  }
143
-
142
+
144
143
  except Exception as e:
145
144
  self.logger.error(f"CSV export failed: {e}", exc_info=True)
146
145
  if progress_callback:
147
146
  progress_callback(None, f"Export failed: {str(e)}")
148
147
  raise
149
-
148
+
150
149
  def _wrap_progress_callback(
151
- self,
152
- callback: Optional[Callable[[int, str], None]],
153
- start_percent: int,
154
- end_percent: int
150
+ self,
151
+ callback: Optional[Callable[[int, str], None]],
152
+ start_percent: int,
153
+ end_percent: int,
155
154
  ) -> Optional[Callable[[int, str], None]]:
156
155
  """
157
156
  Wrap a progress callback to map progress from one range to another
158
-
157
+
159
158
  Args:
160
159
  callback: Original callback function
161
160
  start_percent: Starting percentage for the wrapped range
162
161
  end_percent: Ending percentage for the wrapped range
163
-
162
+
164
163
  Returns:
165
164
  Wrapped callback function or None if original callback is None
166
165
  """
167
166
  if callback is None:
168
167
  return None
169
-
168
+
170
169
  def wrapped_callback(progress: Optional[int], message: Optional[str]):
171
170
  if progress is not None:
172
171
  # Map progress from 0-100 to start_percent-end_percent
173
- mapped_progress = start_percent + (progress * (end_percent - start_percent) // 100)
172
+ mapped_progress = start_percent + (
173
+ progress * (end_percent - start_percent) // 100
174
+ )
174
175
  callback(mapped_progress, message)
175
176
  else:
176
177
  callback(progress, message)
177
-
178
+
178
179
  return wrapped_callback
179
-
180
- def _get_network_stats(self, network: 'pypsa.Network') -> Dict[str, int]:
180
+
181
+ def _get_network_stats(self, network: "pypsa.Network") -> Dict[str, int]:
181
182
  """Get component counts from the network"""
182
183
  return {
183
184
  "buses": len(network.buses),
184
- "generators": len(network.generators) if hasattr(network, 'generators') else 0,
185
- "loads": len(network.loads) if hasattr(network, 'loads') else 0,
186
- "lines": len(network.lines) if hasattr(network, 'lines') else 0,
187
- "links": len(network.links) if hasattr(network, 'links') else 0,
188
- "storage_units": len(network.storage_units) if hasattr(network, 'storage_units') else 0,
189
- "stores": len(network.stores) if hasattr(network, 'stores') else 0,
190
- "carriers": len(network.carriers) if hasattr(network, 'carriers') else 0,
191
- "snapshots": len(network.snapshots) if hasattr(network, 'snapshots') else 0,
185
+ "generators": (
186
+ len(network.generators) if hasattr(network, "generators") else 0
187
+ ),
188
+ "loads": len(network.loads) if hasattr(network, "loads") else 0,
189
+ "lines": len(network.lines) if hasattr(network, "lines") else 0,
190
+ "links": len(network.links) if hasattr(network, "links") else 0,
191
+ "storage_units": (
192
+ len(network.storage_units) if hasattr(network, "storage_units") else 0
193
+ ),
194
+ "stores": len(network.stores) if hasattr(network, "stores") else 0,
195
+ "carriers": len(network.carriers) if hasattr(network, "carriers") else 0,
196
+ "snapshots": len(network.snapshots) if hasattr(network, "snapshots") else 0,
192
197
  }