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.
- pyconvexity/__init__.py +226 -0
- pyconvexity/_version.py +1 -0
- pyconvexity/core/__init__.py +60 -0
- pyconvexity/core/database.py +485 -0
- pyconvexity/core/errors.py +106 -0
- pyconvexity/core/types.py +400 -0
- pyconvexity/data/README.md +101 -0
- pyconvexity/data/__init__.py +17 -0
- pyconvexity/data/loaders/__init__.py +3 -0
- pyconvexity/data/loaders/cache.py +213 -0
- pyconvexity/data/schema/01_core_schema.sql +420 -0
- pyconvexity/data/schema/02_data_metadata.sql +120 -0
- pyconvexity/data/schema/03_validation_data.sql +506 -0
- pyconvexity/data/sources/__init__.py +5 -0
- pyconvexity/data/sources/gem.py +442 -0
- pyconvexity/io/__init__.py +26 -0
- pyconvexity/io/excel_exporter.py +1226 -0
- pyconvexity/io/excel_importer.py +1381 -0
- pyconvexity/io/netcdf_exporter.py +197 -0
- pyconvexity/io/netcdf_importer.py +1833 -0
- pyconvexity/models/__init__.py +195 -0
- pyconvexity/models/attributes.py +730 -0
- pyconvexity/models/carriers.py +159 -0
- pyconvexity/models/components.py +611 -0
- pyconvexity/models/network.py +503 -0
- pyconvexity/models/results.py +148 -0
- pyconvexity/models/scenarios.py +234 -0
- pyconvexity/solvers/__init__.py +29 -0
- pyconvexity/solvers/pypsa/__init__.py +24 -0
- pyconvexity/solvers/pypsa/api.py +460 -0
- pyconvexity/solvers/pypsa/batch_loader.py +307 -0
- pyconvexity/solvers/pypsa/builder.py +675 -0
- pyconvexity/solvers/pypsa/constraints.py +405 -0
- pyconvexity/solvers/pypsa/solver.py +1509 -0
- pyconvexity/solvers/pypsa/storage.py +2048 -0
- pyconvexity/timeseries.py +330 -0
- pyconvexity/validation/__init__.py +25 -0
- pyconvexity/validation/rules.py +312 -0
- pyconvexity-0.4.3.dist-info/METADATA +47 -0
- pyconvexity-0.4.3.dist-info/RECORD +42 -0
- pyconvexity-0.4.3.dist-info/WHEEL +5 -0
- 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
|
+
]
|