pyconvexity 0.3.8.post7__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.
- pyconvexity/__init__.py +87 -46
- pyconvexity/_version.py +1 -1
- pyconvexity/core/__init__.py +3 -5
- pyconvexity/core/database.py +111 -103
- pyconvexity/core/errors.py +16 -10
- pyconvexity/core/types.py +61 -54
- pyconvexity/data/__init__.py +0 -1
- pyconvexity/data/loaders/cache.py +65 -64
- pyconvexity/data/schema/01_core_schema.sql +134 -234
- pyconvexity/data/schema/02_data_metadata.sql +38 -168
- pyconvexity/data/schema/03_validation_data.sql +327 -264
- pyconvexity/data/sources/gem.py +169 -139
- pyconvexity/io/__init__.py +4 -10
- pyconvexity/io/excel_exporter.py +694 -480
- pyconvexity/io/excel_importer.py +817 -545
- pyconvexity/io/netcdf_exporter.py +66 -61
- pyconvexity/io/netcdf_importer.py +850 -619
- pyconvexity/models/__init__.py +109 -59
- pyconvexity/models/attributes.py +197 -178
- pyconvexity/models/carriers.py +70 -67
- pyconvexity/models/components.py +260 -236
- pyconvexity/models/network.py +202 -284
- pyconvexity/models/results.py +65 -55
- pyconvexity/models/scenarios.py +58 -88
- pyconvexity/solvers/__init__.py +5 -5
- pyconvexity/solvers/pypsa/__init__.py +3 -3
- pyconvexity/solvers/pypsa/api.py +150 -134
- pyconvexity/solvers/pypsa/batch_loader.py +165 -162
- pyconvexity/solvers/pypsa/builder.py +390 -291
- pyconvexity/solvers/pypsa/constraints.py +184 -162
- pyconvexity/solvers/pypsa/solver.py +968 -666
- pyconvexity/solvers/pypsa/storage.py +1377 -671
- pyconvexity/timeseries.py +63 -60
- pyconvexity/validation/__init__.py +14 -6
- pyconvexity/validation/rules.py +95 -84
- pyconvexity-0.4.1.dist-info/METADATA +46 -0
- pyconvexity-0.4.1.dist-info/RECORD +42 -0
- pyconvexity/data/__pycache__/__init__.cpython-313.pyc +0 -0
- pyconvexity/data/loaders/__pycache__/__init__.cpython-313.pyc +0 -0
- pyconvexity/data/loaders/__pycache__/cache.cpython-313.pyc +0 -0
- pyconvexity/data/schema/04_scenario_schema.sql +0 -122
- pyconvexity/data/schema/migrate_add_geometries.sql +0 -73
- pyconvexity/data/sources/__pycache__/__init__.cpython-313.pyc +0 -0
- pyconvexity/data/sources/__pycache__/gem.cpython-313.pyc +0 -0
- pyconvexity-0.3.8.post7.dist-info/METADATA +0 -138
- pyconvexity-0.3.8.post7.dist-info/RECORD +0 -49
- {pyconvexity-0.3.8.post7.dist-info → pyconvexity-0.4.1.dist-info}/WHEEL +0 -0
- {pyconvexity-0.3.8.post7.dist-info → pyconvexity-0.4.1.dist-info}/top_level.txt +0 -0
pyconvexity/timeseries.py
CHANGED
|
@@ -24,7 +24,7 @@ from pyconvexity.models.attributes import (
|
|
|
24
24
|
set_timeseries_attribute,
|
|
25
25
|
serialize_values_to_binary,
|
|
26
26
|
deserialize_values_from_binary,
|
|
27
|
-
get_timeseries_length_from_binary
|
|
27
|
+
get_timeseries_length_from_binary,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
|
|
@@ -32,6 +32,7 @@ from pyconvexity.models.attributes import (
|
|
|
32
32
|
# HIGH-LEVEL TIMESERIES API
|
|
33
33
|
# ============================================================================
|
|
34
34
|
|
|
35
|
+
|
|
35
36
|
def get_timeseries(
|
|
36
37
|
db_path: str,
|
|
37
38
|
component_id: int,
|
|
@@ -39,14 +40,14 @@ def get_timeseries(
|
|
|
39
40
|
scenario_id: Optional[int] = None,
|
|
40
41
|
start_index: Optional[int] = None,
|
|
41
42
|
end_index: Optional[int] = None,
|
|
42
|
-
max_points: Optional[int] = None
|
|
43
|
+
max_points: Optional[int] = None,
|
|
43
44
|
) -> Timeseries:
|
|
44
45
|
"""
|
|
45
46
|
Get timeseries data with efficient array-based format.
|
|
46
|
-
|
|
47
|
+
|
|
47
48
|
This is the main function for retrieving timeseries data. It returns
|
|
48
49
|
a Timeseries object with values as a flat array for maximum performance.
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
Args:
|
|
51
52
|
db_path: Path to the database file
|
|
52
53
|
component_id: Component ID
|
|
@@ -55,28 +56,33 @@ def get_timeseries(
|
|
|
55
56
|
start_index: Start index for range queries (optional)
|
|
56
57
|
end_index: End index for range queries (optional)
|
|
57
58
|
max_points: Maximum number of points for sampling (optional)
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
Returns:
|
|
60
61
|
Timeseries object with efficient array-based data
|
|
61
|
-
|
|
62
|
+
|
|
62
63
|
Example:
|
|
63
64
|
>>> ts = get_timeseries("model.db", component_id=123, attribute_name="p")
|
|
64
65
|
>>> print(f"Length: {ts.length}, Values: {ts.values[:5]}")
|
|
65
66
|
Length: 8760, Values: [100.5, 95.2, 87.3, 92.1, 88.7]
|
|
66
|
-
|
|
67
|
+
|
|
67
68
|
# Get a subset of the data
|
|
68
69
|
>>> ts_subset = get_timeseries("model.db", 123, "p", start_index=100, end_index=200)
|
|
69
70
|
>>> print(f"Subset length: {ts_subset.length}")
|
|
70
71
|
Subset length: 100
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
# Sample large datasets
|
|
73
74
|
>>> ts_sampled = get_timeseries("model.db", 123, "p", max_points=1000)
|
|
74
75
|
>>> print(f"Sampled from {ts.length} to {ts_sampled.length} points")
|
|
75
76
|
"""
|
|
76
77
|
with database_context(db_path, read_only=True) as conn:
|
|
77
78
|
return _get_timeseries(
|
|
78
|
-
conn,
|
|
79
|
-
|
|
79
|
+
conn,
|
|
80
|
+
component_id,
|
|
81
|
+
attribute_name,
|
|
82
|
+
scenario_id,
|
|
83
|
+
start_index,
|
|
84
|
+
end_index,
|
|
85
|
+
max_points,
|
|
80
86
|
)
|
|
81
87
|
|
|
82
88
|
|
|
@@ -84,23 +90,23 @@ def get_timeseries_metadata(
|
|
|
84
90
|
db_path: str,
|
|
85
91
|
component_id: int,
|
|
86
92
|
attribute_name: str,
|
|
87
|
-
scenario_id: Optional[int] = None
|
|
93
|
+
scenario_id: Optional[int] = None,
|
|
88
94
|
) -> TimeseriesMetadata:
|
|
89
95
|
"""
|
|
90
96
|
Get timeseries metadata without loading the full data.
|
|
91
|
-
|
|
97
|
+
|
|
92
98
|
This is useful for checking the size and properties of a timeseries
|
|
93
99
|
before deciding whether to load the full data.
|
|
94
|
-
|
|
100
|
+
|
|
95
101
|
Args:
|
|
96
102
|
db_path: Path to the database file
|
|
97
103
|
component_id: Component ID
|
|
98
104
|
attribute_name: Name of the attribute
|
|
99
105
|
scenario_id: Scenario ID (uses master scenario if None)
|
|
100
|
-
|
|
106
|
+
|
|
101
107
|
Returns:
|
|
102
108
|
TimeseriesMetadata with length and type information
|
|
103
|
-
|
|
109
|
+
|
|
104
110
|
Example:
|
|
105
111
|
>>> meta = get_timeseries_metadata("model.db", 123, "p")
|
|
106
112
|
>>> print(f"Length: {meta.length}, Type: {meta.data_type}, Unit: {meta.unit}")
|
|
@@ -115,31 +121,31 @@ def set_timeseries(
|
|
|
115
121
|
component_id: int,
|
|
116
122
|
attribute_name: str,
|
|
117
123
|
values: Union[List[float], np.ndarray, Timeseries],
|
|
118
|
-
scenario_id: Optional[int] = None
|
|
124
|
+
scenario_id: Optional[int] = None,
|
|
119
125
|
) -> None:
|
|
120
126
|
"""
|
|
121
127
|
Set timeseries data using efficient array-based format.
|
|
122
|
-
|
|
128
|
+
|
|
123
129
|
This is the main function for storing timeseries data. It accepts
|
|
124
130
|
various input formats and stores them efficiently in the database.
|
|
125
|
-
|
|
131
|
+
|
|
126
132
|
Args:
|
|
127
133
|
db_path: Path to the database file
|
|
128
134
|
component_id: Component ID
|
|
129
135
|
attribute_name: Name of the attribute
|
|
130
136
|
values: Timeseries values as list, numpy array, or Timeseries object
|
|
131
137
|
scenario_id: Scenario ID (uses master scenario if None)
|
|
132
|
-
|
|
138
|
+
|
|
133
139
|
Example:
|
|
134
140
|
# Set from a list
|
|
135
141
|
>>> values = [100.5, 95.2, 87.3, 92.1, 88.7]
|
|
136
142
|
>>> set_timeseries("model.db", 123, "p_set", values)
|
|
137
|
-
|
|
143
|
+
|
|
138
144
|
# Set from numpy array
|
|
139
145
|
>>> import numpy as np
|
|
140
146
|
>>> values = np.random.normal(100, 10, 8760) # Hourly data for a year
|
|
141
147
|
>>> set_timeseries("model.db", 123, "p_max_pu", values)
|
|
142
|
-
|
|
148
|
+
|
|
143
149
|
# Set from existing Timeseries object
|
|
144
150
|
>>> ts = get_timeseries("model.db", 456, "p")
|
|
145
151
|
>>> set_timeseries("model.db", 123, "p_set", ts)
|
|
@@ -153,30 +159,30 @@ def set_timeseries(
|
|
|
153
159
|
values_list = [float(v) for v in values]
|
|
154
160
|
else:
|
|
155
161
|
raise ValueError("values must be List[float], numpy.ndarray, or Timeseries")
|
|
156
|
-
|
|
162
|
+
|
|
157
163
|
with database_context(db_path) as conn:
|
|
158
|
-
set_timeseries_attribute(
|
|
164
|
+
set_timeseries_attribute(
|
|
165
|
+
conn, component_id, attribute_name, values_list, scenario_id
|
|
166
|
+
)
|
|
159
167
|
|
|
160
168
|
|
|
161
169
|
def get_multiple_timeseries(
|
|
162
|
-
db_path: str,
|
|
163
|
-
requests: List[dict],
|
|
164
|
-
max_points: Optional[int] = None
|
|
170
|
+
db_path: str, requests: List[dict], max_points: Optional[int] = None
|
|
165
171
|
) -> List[Timeseries]:
|
|
166
172
|
"""
|
|
167
173
|
Get multiple timeseries efficiently in a single database connection.
|
|
168
|
-
|
|
174
|
+
|
|
169
175
|
This is more efficient than calling get_timeseries multiple times
|
|
170
176
|
when you need to load many timeseries from the same database.
|
|
171
|
-
|
|
177
|
+
|
|
172
178
|
Args:
|
|
173
179
|
db_path: Path to the database file
|
|
174
180
|
requests: List of dicts with keys: component_id, attribute_name, scenario_id (optional)
|
|
175
181
|
max_points: Maximum number of points for sampling (applied to all)
|
|
176
|
-
|
|
182
|
+
|
|
177
183
|
Returns:
|
|
178
184
|
List of Timeseries objects in the same order as requests
|
|
179
|
-
|
|
185
|
+
|
|
180
186
|
Example:
|
|
181
187
|
>>> requests = [
|
|
182
188
|
... {"component_id": 123, "attribute_name": "p"},
|
|
@@ -187,19 +193,18 @@ def get_multiple_timeseries(
|
|
|
187
193
|
>>> print(f"Loaded {len(timeseries_list)} timeseries")
|
|
188
194
|
"""
|
|
189
195
|
results = []
|
|
190
|
-
|
|
196
|
+
|
|
191
197
|
with database_context(db_path, read_only=True) as conn:
|
|
192
198
|
for request in requests:
|
|
193
199
|
component_id = request["component_id"]
|
|
194
200
|
attribute_name = request["attribute_name"]
|
|
195
201
|
scenario_id = request.get("scenario_id")
|
|
196
|
-
|
|
202
|
+
|
|
197
203
|
ts = _get_timeseries(
|
|
198
|
-
conn, component_id, attribute_name, scenario_id,
|
|
199
|
-
None, None, max_points
|
|
204
|
+
conn, component_id, attribute_name, scenario_id, None, None, max_points
|
|
200
205
|
)
|
|
201
206
|
results.append(ts)
|
|
202
|
-
|
|
207
|
+
|
|
203
208
|
return results
|
|
204
209
|
|
|
205
210
|
|
|
@@ -207,16 +212,17 @@ def get_multiple_timeseries(
|
|
|
207
212
|
# UTILITY FUNCTIONS
|
|
208
213
|
# ============================================================================
|
|
209
214
|
|
|
215
|
+
|
|
210
216
|
def timeseries_to_numpy(timeseries: Timeseries) -> np.ndarray:
|
|
211
217
|
"""
|
|
212
218
|
Convert Timeseries to numpy array for scientific computing.
|
|
213
|
-
|
|
219
|
+
|
|
214
220
|
Args:
|
|
215
221
|
timeseries: Timeseries object
|
|
216
|
-
|
|
222
|
+
|
|
217
223
|
Returns:
|
|
218
224
|
numpy array with float32 dtype for memory efficiency
|
|
219
|
-
|
|
225
|
+
|
|
220
226
|
Example:
|
|
221
227
|
>>> ts = get_timeseries("model.db", 123, "p")
|
|
222
228
|
>>> arr = timeseries_to_numpy(ts)
|
|
@@ -229,53 +235,50 @@ def numpy_to_timeseries(
|
|
|
229
235
|
array: np.ndarray,
|
|
230
236
|
data_type: str = "float",
|
|
231
237
|
unit: Optional[str] = None,
|
|
232
|
-
is_input: bool = True
|
|
238
|
+
is_input: bool = True,
|
|
233
239
|
) -> Timeseries:
|
|
234
240
|
"""
|
|
235
241
|
Convert numpy array to Timeseries object.
|
|
236
|
-
|
|
242
|
+
|
|
237
243
|
Args:
|
|
238
244
|
array: numpy array of values
|
|
239
245
|
data_type: Data type string (default: "float")
|
|
240
246
|
unit: Unit string (optional)
|
|
241
247
|
is_input: Whether this is input data (default: True)
|
|
242
|
-
|
|
248
|
+
|
|
243
249
|
Returns:
|
|
244
250
|
Timeseries object
|
|
245
|
-
|
|
251
|
+
|
|
246
252
|
Example:
|
|
247
253
|
>>> import numpy as np
|
|
248
254
|
>>> arr = np.random.normal(100, 10, 8760)
|
|
249
255
|
>>> ts = numpy_to_timeseries(arr, unit="MW")
|
|
250
256
|
>>> print(f"Created timeseries with {ts.length} points")
|
|
251
257
|
"""
|
|
252
|
-
values = array.tolist() if hasattr(array,
|
|
258
|
+
values = array.tolist() if hasattr(array, "tolist") else list(array)
|
|
253
259
|
return Timeseries(
|
|
254
260
|
values=[float(v) for v in values],
|
|
255
261
|
length=len(values),
|
|
256
262
|
start_index=0,
|
|
257
263
|
data_type=data_type,
|
|
258
264
|
unit=unit,
|
|
259
|
-
is_input=is_input
|
|
265
|
+
is_input=is_input,
|
|
260
266
|
)
|
|
261
267
|
|
|
262
268
|
|
|
263
269
|
def validate_timeseries_alignment(
|
|
264
|
-
db_path: str,
|
|
265
|
-
network_id: int,
|
|
266
|
-
values: Union[List[float], np.ndarray, Timeseries]
|
|
270
|
+
db_path: str, values: Union[List[float], np.ndarray, Timeseries]
|
|
267
271
|
) -> dict:
|
|
268
272
|
"""
|
|
269
273
|
Validate that timeseries data aligns with network time periods.
|
|
270
|
-
|
|
274
|
+
|
|
271
275
|
Args:
|
|
272
276
|
db_path: Path to the database file
|
|
273
|
-
network_id: Network ID
|
|
274
277
|
values: Timeseries values to validate
|
|
275
|
-
|
|
278
|
+
|
|
276
279
|
Returns:
|
|
277
280
|
Dictionary with validation results
|
|
278
|
-
|
|
281
|
+
|
|
279
282
|
Example:
|
|
280
283
|
>>> values = [100.0] * 8760 # Hourly data for a year
|
|
281
284
|
>>> result = validate_timeseries_alignment("model.db", 1, values)
|
|
@@ -293,35 +296,35 @@ def validate_timeseries_alignment(
|
|
|
293
296
|
values_list = [float(v) for v in values]
|
|
294
297
|
else:
|
|
295
298
|
raise ValueError("values must be List[float], numpy.ndarray, or Timeseries")
|
|
296
|
-
|
|
299
|
+
|
|
297
300
|
with database_context(db_path, read_only=True) as conn:
|
|
298
301
|
# Get network time periods
|
|
299
302
|
from pyconvexity.models.network import get_network_time_periods
|
|
303
|
+
|
|
300
304
|
try:
|
|
301
|
-
time_periods = get_network_time_periods(conn
|
|
305
|
+
time_periods = get_network_time_periods(conn)
|
|
302
306
|
expected_length = len(time_periods)
|
|
303
307
|
actual_length = len(values_list)
|
|
304
|
-
|
|
308
|
+
|
|
305
309
|
is_valid = actual_length == expected_length
|
|
306
310
|
issues = []
|
|
307
|
-
|
|
311
|
+
|
|
308
312
|
if actual_length < expected_length:
|
|
309
313
|
issues.append(f"Missing {expected_length - actual_length} time periods")
|
|
310
314
|
elif actual_length > expected_length:
|
|
311
315
|
issues.append(f"Extra {actual_length - expected_length} time periods")
|
|
312
|
-
|
|
316
|
+
|
|
313
317
|
return {
|
|
314
318
|
"is_valid": is_valid,
|
|
315
319
|
"expected_length": expected_length,
|
|
316
320
|
"actual_length": actual_length,
|
|
317
|
-
"issues": issues
|
|
321
|
+
"issues": issues,
|
|
318
322
|
}
|
|
319
|
-
|
|
323
|
+
|
|
320
324
|
except Exception as e:
|
|
321
325
|
return {
|
|
322
326
|
"is_valid": False,
|
|
323
327
|
"expected_length": 0,
|
|
324
328
|
"actual_length": len(values_list),
|
|
325
|
-
"issues": [f"Failed to get network time periods: {e}"]
|
|
329
|
+
"issues": [f"Failed to get network time periods: {e}"],
|
|
326
330
|
}
|
|
327
|
-
|
|
@@ -5,13 +5,21 @@ Contains data validation rules and type checking functionality.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from pyconvexity.validation.rules import (
|
|
8
|
-
get_validation_rule,
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
get_validation_rule,
|
|
9
|
+
list_validation_rules,
|
|
10
|
+
get_all_validation_rules,
|
|
11
|
+
validate_static_value,
|
|
12
|
+
validate_timeseries_alignment,
|
|
13
|
+
parse_default_value,
|
|
14
|
+
get_attribute_setter_info,
|
|
11
15
|
)
|
|
12
16
|
|
|
13
17
|
__all__ = [
|
|
14
|
-
"get_validation_rule",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
18
|
+
"get_validation_rule",
|
|
19
|
+
"list_validation_rules",
|
|
20
|
+
"get_all_validation_rules",
|
|
21
|
+
"validate_static_value",
|
|
22
|
+
"validate_timeseries_alignment",
|
|
23
|
+
"parse_default_value",
|
|
24
|
+
"get_attribute_setter_info",
|
|
17
25
|
]
|