pyconvexity 0.4.3__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 pyconvexity might be problematic. Click here for more details.

Files changed (42) hide show
  1. pyconvexity/__init__.py +226 -0
  2. pyconvexity/_version.py +1 -0
  3. pyconvexity/core/__init__.py +60 -0
  4. pyconvexity/core/database.py +485 -0
  5. pyconvexity/core/errors.py +106 -0
  6. pyconvexity/core/types.py +400 -0
  7. pyconvexity/data/README.md +101 -0
  8. pyconvexity/data/__init__.py +17 -0
  9. pyconvexity/data/loaders/__init__.py +3 -0
  10. pyconvexity/data/loaders/cache.py +213 -0
  11. pyconvexity/data/schema/01_core_schema.sql +420 -0
  12. pyconvexity/data/schema/02_data_metadata.sql +120 -0
  13. pyconvexity/data/schema/03_validation_data.sql +506 -0
  14. pyconvexity/data/sources/__init__.py +5 -0
  15. pyconvexity/data/sources/gem.py +442 -0
  16. pyconvexity/io/__init__.py +26 -0
  17. pyconvexity/io/excel_exporter.py +1226 -0
  18. pyconvexity/io/excel_importer.py +1381 -0
  19. pyconvexity/io/netcdf_exporter.py +197 -0
  20. pyconvexity/io/netcdf_importer.py +1833 -0
  21. pyconvexity/models/__init__.py +195 -0
  22. pyconvexity/models/attributes.py +730 -0
  23. pyconvexity/models/carriers.py +159 -0
  24. pyconvexity/models/components.py +611 -0
  25. pyconvexity/models/network.py +503 -0
  26. pyconvexity/models/results.py +148 -0
  27. pyconvexity/models/scenarios.py +234 -0
  28. pyconvexity/solvers/__init__.py +29 -0
  29. pyconvexity/solvers/pypsa/__init__.py +24 -0
  30. pyconvexity/solvers/pypsa/api.py +460 -0
  31. pyconvexity/solvers/pypsa/batch_loader.py +307 -0
  32. pyconvexity/solvers/pypsa/builder.py +675 -0
  33. pyconvexity/solvers/pypsa/constraints.py +405 -0
  34. pyconvexity/solvers/pypsa/solver.py +1509 -0
  35. pyconvexity/solvers/pypsa/storage.py +2048 -0
  36. pyconvexity/timeseries.py +330 -0
  37. pyconvexity/validation/__init__.py +25 -0
  38. pyconvexity/validation/rules.py +312 -0
  39. pyconvexity-0.4.3.dist-info/METADATA +47 -0
  40. pyconvexity-0.4.3.dist-info/RECORD +42 -0
  41. pyconvexity-0.4.3.dist-info/WHEEL +5 -0
  42. pyconvexity-0.4.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,234 @@
1
+ """
2
+ Scenario management operations for PyConvexity.
3
+
4
+ Provides operations for listing, querying, and managing scenarios.
5
+ """
6
+
7
+ import sqlite3
8
+ import logging
9
+ from typing import List, Optional
10
+ from dataclasses import dataclass
11
+
12
+ from pyconvexity.core.errors import ValidationError
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @dataclass
18
+ class Scenario:
19
+ """Represents a scenario (single network per database)."""
20
+
21
+ id: int
22
+ name: str
23
+ description: Optional[str]
24
+ probability: Optional[float] # For stochastic optimization
25
+ is_system_scenario: bool = False # System-reserved scenarios (like "Actual")
26
+ system_purpose: Optional[str] = None # 'actual' for actual/measured values
27
+ created_at: str = ""
28
+
29
+
30
+ # System scenario constants
31
+ ACTUAL_SCENARIO_PURPOSE = "actual"
32
+
33
+
34
+ def list_scenarios(
35
+ conn: sqlite3.Connection, include_system: bool = False
36
+ ) -> List[Scenario]:
37
+ """
38
+ List scenarios (single network per database).
39
+
40
+ Args:
41
+ conn: Database connection
42
+ include_system: If True, include system scenarios (like "Actual")
43
+
44
+ Returns:
45
+ List of Scenario objects ordered by creation date
46
+ """
47
+ if include_system:
48
+ query = """
49
+ SELECT id, name, description, probability, is_system_scenario, system_purpose, created_at
50
+ FROM scenarios
51
+ ORDER BY is_system_scenario ASC, created_at ASC
52
+ """
53
+ else:
54
+ query = """
55
+ SELECT id, name, description, probability, is_system_scenario, system_purpose, created_at
56
+ FROM scenarios
57
+ WHERE is_system_scenario = 0
58
+ ORDER BY created_at ASC
59
+ """
60
+
61
+ cursor = conn.execute(query)
62
+
63
+ scenarios = []
64
+ for row in cursor.fetchall():
65
+ scenarios.append(
66
+ Scenario(
67
+ id=row[0],
68
+ name=row[1],
69
+ description=row[2],
70
+ probability=row[3],
71
+ is_system_scenario=bool(row[4]),
72
+ system_purpose=row[5],
73
+ created_at=row[6],
74
+ )
75
+ )
76
+
77
+ return scenarios
78
+
79
+
80
+ def get_scenario_by_name(conn: sqlite3.Connection, name: str) -> Scenario:
81
+ """
82
+ Get a scenario by name (single network per database).
83
+
84
+ Args:
85
+ conn: Database connection
86
+ name: Scenario name
87
+
88
+ Returns:
89
+ Scenario object
90
+
91
+ Raises:
92
+ ValidationError: If scenario doesn't exist
93
+ """
94
+ cursor = conn.execute(
95
+ """
96
+ SELECT id, name, description, probability, is_system_scenario, system_purpose, created_at
97
+ FROM scenarios
98
+ WHERE name = ?
99
+ """,
100
+ (name,),
101
+ )
102
+
103
+ row = cursor.fetchone()
104
+ if not row:
105
+ raise ValidationError(f"Scenario '{name}' not found")
106
+
107
+ return Scenario(
108
+ id=row[0],
109
+ name=row[1],
110
+ description=row[2],
111
+ probability=row[3],
112
+ is_system_scenario=bool(row[4]),
113
+ system_purpose=row[5],
114
+ created_at=row[6],
115
+ )
116
+
117
+
118
+ def get_scenario_by_id(conn: sqlite3.Connection, scenario_id: int) -> Scenario:
119
+ """
120
+ Get a scenario by ID.
121
+
122
+ Args:
123
+ conn: Database connection
124
+ scenario_id: Scenario ID
125
+
126
+ Returns:
127
+ Scenario object
128
+
129
+ Raises:
130
+ ValidationError: If scenario doesn't exist
131
+ """
132
+ cursor = conn.execute(
133
+ """
134
+ SELECT id, name, description, probability, is_system_scenario, system_purpose, created_at
135
+ FROM scenarios
136
+ WHERE id = ?
137
+ """,
138
+ (scenario_id,),
139
+ )
140
+
141
+ row = cursor.fetchone()
142
+ if not row:
143
+ raise ValidationError(f"Scenario with ID {scenario_id} not found")
144
+
145
+ return Scenario(
146
+ id=row[0],
147
+ name=row[1],
148
+ description=row[2],
149
+ probability=row[3],
150
+ is_system_scenario=bool(row[4]),
151
+ system_purpose=row[5],
152
+ created_at=row[6],
153
+ )
154
+
155
+
156
+ # ============================================================================
157
+ # ACTUAL SCENARIO FUNCTIONS
158
+ # ============================================================================
159
+
160
+
161
+ def get_actual_scenario_id(conn: sqlite3.Connection) -> Optional[int]:
162
+ """
163
+ Get the Actual scenario ID (if it exists).
164
+
165
+ Args:
166
+ conn: Database connection
167
+
168
+ Returns:
169
+ Actual scenario ID or None if not found
170
+ """
171
+ cursor = conn.execute(
172
+ """
173
+ SELECT id FROM scenarios WHERE system_purpose = ?
174
+ """,
175
+ (ACTUAL_SCENARIO_PURPOSE,),
176
+ )
177
+
178
+ row = cursor.fetchone()
179
+ return row[0] if row else None
180
+
181
+
182
+ def get_or_create_actual_scenario(conn: sqlite3.Connection) -> int:
183
+ """
184
+ Get the Actual scenario ID, creating it if it doesn't exist.
185
+
186
+ Args:
187
+ conn: Database connection
188
+
189
+ Returns:
190
+ Actual scenario ID
191
+ """
192
+ scenario_id = get_actual_scenario_id(conn)
193
+ if scenario_id is not None:
194
+ return scenario_id
195
+
196
+ # Create the actual scenario
197
+ cursor = conn.execute(
198
+ """
199
+ INSERT INTO scenarios (name, description, is_system_scenario, system_purpose, created_at)
200
+ VALUES ('Actual', 'Actual/measured values for validation and comparison', 1, ?, datetime('now'))
201
+ """,
202
+ (ACTUAL_SCENARIO_PURPOSE,),
203
+ )
204
+
205
+ return cursor.lastrowid
206
+
207
+
208
+ def has_actual_value(
209
+ conn: sqlite3.Connection, component_id: int, attribute_name: str
210
+ ) -> bool:
211
+ """
212
+ Check if an actual value exists for a component attribute.
213
+
214
+ Args:
215
+ conn: Database connection
216
+ component_id: Component ID
217
+ attribute_name: Attribute name
218
+
219
+ Returns:
220
+ True if actual value exists
221
+ """
222
+ scenario_id = get_actual_scenario_id(conn)
223
+ if scenario_id is None:
224
+ return False
225
+
226
+ cursor = conn.execute(
227
+ """
228
+ SELECT COUNT(*) > 0 FROM component_attributes
229
+ WHERE component_id = ? AND attribute_name = ? AND scenario_id = ?
230
+ """,
231
+ (component_id, attribute_name, scenario_id),
232
+ )
233
+
234
+ return bool(cursor.fetchone()[0])
@@ -0,0 +1,29 @@
1
+ """
2
+ Solver module for PyConvexity.
3
+
4
+ Provides interfaces to various optimization solvers for energy system modeling.
5
+ """
6
+
7
+ # Try to import PyPSA solver with graceful fallback
8
+ try:
9
+ from pyconvexity.solvers.pypsa import (
10
+ solve_network,
11
+ build_pypsa_network,
12
+ solve_pypsa_network,
13
+ load_network_components,
14
+ apply_constraints,
15
+ store_solve_results,
16
+ )
17
+
18
+ __all__ = [
19
+ "solve_network",
20
+ "build_pypsa_network",
21
+ "solve_pypsa_network",
22
+ "load_network_components",
23
+ "apply_constraints",
24
+ "store_solve_results",
25
+ ]
26
+
27
+ except ImportError:
28
+ # PyPSA not available
29
+ __all__ = []
@@ -0,0 +1,24 @@
1
+ """
2
+ PyPSA solver integration for PyConvexity.
3
+
4
+ Provides high-level and low-level APIs for building PyPSA networks from database,
5
+ solving them, and storing results back to the database.
6
+ """
7
+
8
+ from pyconvexity.solvers.pypsa.api import (
9
+ solve_network,
10
+ build_pypsa_network,
11
+ solve_pypsa_network,
12
+ load_network_components,
13
+ apply_constraints,
14
+ store_solve_results,
15
+ )
16
+
17
+ __all__ = [
18
+ "solve_network",
19
+ "build_pypsa_network",
20
+ "solve_pypsa_network",
21
+ "load_network_components",
22
+ "apply_constraints",
23
+ "store_solve_results",
24
+ ]