pyconvexity 0.1.0__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.

@@ -0,0 +1,301 @@
1
+ """
2
+ Validation rules and operations for PyConvexity.
3
+
4
+ Provides validation logic for component attributes, data types, and timeseries alignment.
5
+ """
6
+
7
+ import sqlite3
8
+ import json
9
+ import logging
10
+ from typing import Dict, Any, Optional, List
11
+
12
+ from pyconvexity.core.types import (
13
+ ValidationRule, StaticValue, TimeseriesPoint, TimePeriod, TimeseriesValidationResult
14
+ )
15
+ from pyconvexity.core.errors import (
16
+ ValidationError, InvalidDataType
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def get_validation_rule(
23
+ conn: sqlite3.Connection,
24
+ component_type: str,
25
+ attribute_name: str
26
+ ) -> ValidationRule:
27
+ """
28
+ Get validation rule for a specific component type and attribute.
29
+
30
+ Args:
31
+ conn: Database connection
32
+ component_type: Type of component (e.g., "BUS", "GENERATOR")
33
+ attribute_name: Name of the attribute
34
+
35
+ Returns:
36
+ ValidationRule object with all validation information
37
+
38
+ Raises:
39
+ ValidationError: If no validation rule is found
40
+ """
41
+ cursor = conn.execute("""
42
+ SELECT component_type, attribute_name, data_type, unit, default_value, allowed_storage_types,
43
+ is_required, is_input, description
44
+ FROM attribute_validation_rules
45
+ WHERE component_type = ? AND attribute_name = ?
46
+ """, (component_type, attribute_name))
47
+
48
+ row = cursor.fetchone()
49
+ if not row:
50
+ raise ValidationError(f"No validation rule found for {component_type}.{attribute_name}")
51
+
52
+ allowed_storage_types = row[5]
53
+ allows_static = allowed_storage_types in ("static", "static_or_timeseries")
54
+ allows_timeseries = allowed_storage_types in ("timeseries", "static_or_timeseries")
55
+
56
+ # Parse default value
57
+ default_value = None
58
+ if row[4]: # default_value_string
59
+ default_value = parse_default_value(row[4])
60
+
61
+ return ValidationRule(
62
+ component_type=row[0],
63
+ attribute_name=row[1],
64
+ data_type=row[2],
65
+ unit=row[3],
66
+ default_value_string=row[4],
67
+ allowed_storage_types=allowed_storage_types,
68
+ allows_static=allows_static,
69
+ allows_timeseries=allows_timeseries,
70
+ is_required=bool(row[6]),
71
+ is_input=bool(row[7]),
72
+ description=row[8],
73
+ default_value=default_value
74
+ )
75
+
76
+
77
+ def list_validation_rules(
78
+ conn: sqlite3.Connection,
79
+ component_type: str
80
+ ) -> List[ValidationRule]:
81
+ """
82
+ List validation rules for a component type.
83
+
84
+ Args:
85
+ conn: Database connection
86
+ component_type: Type of component
87
+
88
+ Returns:
89
+ List of ValidationRule objects
90
+ """
91
+ cursor = conn.execute("""
92
+ SELECT component_type, attribute_name, data_type, unit, default_value, allowed_storage_types,
93
+ is_required, is_input, description
94
+ FROM attribute_validation_rules
95
+ WHERE component_type = ?
96
+ ORDER BY attribute_name
97
+ """, (component_type,))
98
+
99
+ rules = []
100
+ for row in cursor.fetchall():
101
+ allowed_storage_types = row[5]
102
+ allows_static = allowed_storage_types in ("static", "static_or_timeseries")
103
+ allows_timeseries = allowed_storage_types in ("timeseries", "static_or_timeseries")
104
+
105
+ # Parse default value
106
+ default_value = None
107
+ if row[4]: # default_value_string
108
+ default_value = parse_default_value(row[4])
109
+
110
+ rules.append(ValidationRule(
111
+ component_type=row[0],
112
+ attribute_name=row[1],
113
+ data_type=row[2],
114
+ unit=row[3],
115
+ default_value_string=row[4],
116
+ allowed_storage_types=allowed_storage_types,
117
+ allows_static=allows_static,
118
+ allows_timeseries=allows_timeseries,
119
+ is_required=bool(row[6]),
120
+ is_input=bool(row[7]),
121
+ description=row[8],
122
+ default_value=default_value
123
+ ))
124
+
125
+ return rules
126
+
127
+
128
+ def get_all_validation_rules(conn: sqlite3.Connection) -> Dict[str, Any]:
129
+ """
130
+ Get all validation rules from the database.
131
+ This replaces the need to load the entire JSON file into memory.
132
+
133
+ Args:
134
+ conn: Database connection
135
+
136
+ Returns:
137
+ Dictionary mapping component types to their validation rules
138
+ """
139
+ try:
140
+ cursor = conn.execute("""
141
+ SELECT component_type, attribute_name, data_type, unit, default_value, allowed_storage_types,
142
+ is_required, is_input, description
143
+ FROM attribute_validation_rules
144
+ """)
145
+
146
+ rules = {}
147
+ for row in cursor.fetchall():
148
+ component_type = row[0]
149
+ attribute_name = row[1]
150
+ data_type = row[2]
151
+ unit = row[3]
152
+ default_value = row[4]
153
+ allowed_storage_types = row[5]
154
+ is_required = bool(row[6])
155
+ is_input = bool(row[7])
156
+ description = row[8]
157
+
158
+ if component_type not in rules:
159
+ rules[component_type] = {}
160
+
161
+ rules[component_type][attribute_name] = {
162
+ 'data_type': data_type,
163
+ 'unit': unit,
164
+ 'default_value': default_value,
165
+ 'allowed_storage_types': allowed_storage_types,
166
+ 'is_required': is_required,
167
+ 'is_input': is_input,
168
+ 'description': description
169
+ }
170
+
171
+ return rules
172
+ except Exception as e:
173
+ logger.error(f"Error getting all validation rules: {e}")
174
+ return {}
175
+
176
+
177
+ def validate_static_value(value: StaticValue, rule: ValidationRule) -> None:
178
+ """
179
+ Validate static value against rule.
180
+
181
+ Args:
182
+ value: StaticValue to validate
183
+ rule: ValidationRule to validate against
184
+
185
+ Raises:
186
+ InvalidDataType: If value type doesn't match rule
187
+ """
188
+ value_type = value.data_type()
189
+
190
+ if value_type != rule.data_type:
191
+ raise InvalidDataType(expected=rule.data_type, actual=value_type)
192
+
193
+
194
+ def validate_timeseries_alignment(
195
+ conn: sqlite3.Connection,
196
+ network_id: int,
197
+ timeseries: List[TimeseriesPoint]
198
+ ) -> TimeseriesValidationResult:
199
+ """
200
+ Validate timeseries alignment with network periods.
201
+
202
+ Args:
203
+ conn: Database connection
204
+ network_id: Network ID
205
+ timeseries: List of timeseries points to validate
206
+
207
+ Returns:
208
+ TimeseriesValidationResult with validation details
209
+ """
210
+ # Get network time periods
211
+ from pyconvexity.models.network import get_network_time_periods
212
+ network_periods = get_network_time_periods(conn, network_id)
213
+ network_period_indices = {p.period_index for p in network_periods}
214
+
215
+ # Get provided period indices
216
+ provided_period_indices = {p.period_index for p in timeseries}
217
+
218
+ # Find missing and extra periods
219
+ missing_periods = list(network_period_indices - provided_period_indices)
220
+ extra_periods = list(provided_period_indices - network_period_indices)
221
+
222
+ is_valid = len(missing_periods) == 0 and len(extra_periods) == 0
223
+
224
+ return TimeseriesValidationResult(
225
+ is_valid=is_valid,
226
+ missing_periods=missing_periods,
227
+ extra_periods=extra_periods,
228
+ total_network_periods=len(network_periods),
229
+ provided_periods=len(timeseries)
230
+ )
231
+
232
+
233
+ def parse_default_value(s: str) -> Optional[StaticValue]:
234
+ """
235
+ Parse default value string.
236
+
237
+ Args:
238
+ s: String representation of default value
239
+
240
+ Returns:
241
+ StaticValue object or None if parsing fails
242
+ """
243
+ # Try to parse as JSON first
244
+ try:
245
+ value = json.loads(s)
246
+ if isinstance(value, float):
247
+ return StaticValue(value)
248
+ elif isinstance(value, int):
249
+ return StaticValue(value)
250
+ elif isinstance(value, bool):
251
+ return StaticValue(value)
252
+ elif isinstance(value, str):
253
+ return StaticValue(value)
254
+ else:
255
+ return None
256
+ except (json.JSONDecodeError, ValueError):
257
+ # Fallback to string
258
+ return StaticValue(s)
259
+
260
+
261
+ def get_attribute_setter_info(
262
+ conn: sqlite3.Connection,
263
+ component_type: str,
264
+ attribute_name: str,
265
+ ) -> Dict[str, Any]:
266
+ """
267
+ Get the appropriate function name for setting an attribute.
268
+
269
+ Args:
270
+ conn: Database connection
271
+ component_type: Type of component
272
+ attribute_name: Name of the attribute
273
+
274
+ Returns:
275
+ Dictionary with setter function information
276
+
277
+ Raises:
278
+ ValidationError: If attribute or data type is unknown
279
+ """
280
+ rule = get_validation_rule(conn, component_type, attribute_name)
281
+
282
+ function_name = {
283
+ "float": "set_float_attribute",
284
+ "int": "set_integer_attribute",
285
+ "boolean": "set_boolean_attribute",
286
+ "string": "set_string_attribute",
287
+ }.get(rule.data_type)
288
+
289
+ if not function_name:
290
+ raise ValidationError(f"Unknown data type: {rule.data_type}")
291
+
292
+ return {
293
+ "function_name": function_name,
294
+ "data_type": rule.data_type,
295
+ "allows_static": rule.allows_static,
296
+ "allows_timeseries": rule.allows_timeseries,
297
+ "is_required": rule.is_required,
298
+ "default_value": rule.default_value_string,
299
+ "unit": rule.unit,
300
+ "description": rule.description
301
+ }
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyconvexity
3
+ Version: 0.1.0
4
+ Summary: Python library for energy system modeling and optimization with PyPSA
5
+ Author-email: Convexity Team <info@convexity.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/bayesian-energy/convexity-js
8
+ Project-URL: Repository, https://github.com/bayesian-energy/convexity-js
9
+ Project-URL: Issues, https://github.com/bayesian-energy/convexity-js/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: pandas>=1.5.0
22
+ Requires-Dist: numpy>=1.21.0
23
+ Requires-Dist: pyarrow>=10.0.0
24
+ Provides-Extra: pypsa
25
+ Requires-Dist: pypsa>=0.25.0; extra == "pypsa"
26
+ Requires-Dist: networkx; extra == "pypsa"
27
+ Requires-Dist: scipy; extra == "pypsa"
28
+ Requires-Dist: xarray; extra == "pypsa"
29
+ Provides-Extra: excel
30
+ Requires-Dist: openpyxl>=3.0.0; extra == "excel"
31
+ Requires-Dist: xlsxwriter>=3.0.0; extra == "excel"
32
+ Provides-Extra: netcdf
33
+ Requires-Dist: netcdf4>=1.6.0; extra == "netcdf"
34
+ Requires-Dist: xarray>=2022.3.0; extra == "netcdf"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
37
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
38
+ Requires-Dist: black>=22.0.0; extra == "dev"
39
+ Requires-Dist: isort>=5.10.0; extra == "dev"
40
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
41
+ Requires-Dist: pre-commit>=2.20.0; extra == "dev"
42
+ Provides-Extra: all
43
+ Requires-Dist: pyconvexity[excel,netcdf,pypsa]; extra == "all"
44
+
45
+ # PyConvexity
46
+
47
+ **Energy system modeling library for optimization and analysis.**
48
+
49
+ PyConvexity provides the core functionality of the [Convexity](https://github.com/bayesian-energy/convexity-js) desktop application as a reusable, pip-installable Python library.
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install pyconvexity
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ```python
60
+ import pyconvexity as px
61
+
62
+ # Create a new energy system model database
63
+ px.create_database_with_schema("my_model.db")
64
+
65
+ # Create a network
66
+ with px.database_context("my_model.db") as conn:
67
+ network_req = px.CreateNetworkRequest(
68
+ name="My Energy Network",
69
+ description="Example renewable energy system",
70
+ start_time="2024-01-01 00:00:00",
71
+ end_time="2024-01-02 00:00:00",
72
+ time_resolution="H"
73
+ )
74
+ network_id = px.create_network(conn, network_req)
75
+
76
+ # Create carriers (energy types)
77
+ ac_carrier = px.create_carrier(conn, network_id, "AC")
78
+
79
+ # Create components
80
+ bus_id = px.create_component(
81
+ conn, network_id, "BUS", "Main Bus",
82
+ latitude=40.7128, longitude=-74.0060,
83
+ carrier_id=ac_carrier
84
+ )
85
+
86
+ # Set component attributes
87
+ px.set_static_attribute(conn, bus_id, "v_nom", px.StaticValue(230.0))
88
+
89
+ conn.commit()
90
+
91
+ print(f"✅ Created network {network_id} with bus {bus_id}")
92
+ ```
93
+
94
+ ## Features
95
+
96
+ - **Database Management**: SQLite-based energy system model storage
97
+ - **Component Modeling**: Buses, generators, loads, lines, storage, etc.
98
+ - **Time Series Support**: Efficient storage and retrieval of temporal data
99
+ - **Validation**: Built-in validation rules for energy system components
100
+ - **PyPSA Integration**: Compatible with PyPSA energy system modeling
101
+ - **Type Safety**: Full type hints and data validation
102
+
103
+ ## Core Concepts
104
+
105
+ ### Networks
106
+ Energy system models organized as networks with time periods and carriers.
107
+
108
+ ### Components
109
+ Physical and virtual elements: buses, generators, loads, transmission lines, storage units, etc.
110
+
111
+ ### Attributes
112
+ Component properties that can be static values or time series data.
113
+
114
+ ### Scenarios
115
+ Different parameter sets for the same network topology.
116
+
117
+ ## Documentation
118
+
119
+ - **Full Documentation**: [Convexity Documentation](https://github.com/bayesian-energy/convexity-js)
120
+ - **API Reference**: See docstrings in the code
121
+ - **Examples**: Check the `examples/` directory
122
+
123
+ ## Development
124
+
125
+ PyConvexity is developed as part of the [Convexity](https://github.com/bayesian-energy/convexity-js) project by [Bayesian Energy](https://bayesianenergy.com).
126
+
127
+ ## License
128
+
129
+ MIT License - see [LICENSE](https://github.com/bayesian-energy/convexity-js/blob/main/LICENSE) file for details.
130
+
131
+ ## Related Projects
132
+
133
+ - **[Convexity](https://github.com/bayesian-energy/convexity-js)**: Desktop application for energy system modeling
134
+ - **[PyPSA](https://pypsa.org/)**: Python for Power System Analysis
135
+ - **[Linopy](https://linopy.readthedocs.io/)**: Linear optimization with Python
@@ -0,0 +1,16 @@
1
+ pyconvexity/__init__.py,sha256=mEtafBwQ1A9s-hEv3o3XeXlJ7iFtbdS691wVUUw5miY,3303
2
+ pyconvexity/_version.py,sha256=5CA6z91JO_fS9ADhN6383Ps_rUNBx3K4gJNbVRJmeYY,133
3
+ pyconvexity/core/__init__.py,sha256=4SYAE4zqzGIRFSP4IoT7EzK-LCTB1HLe9EWhfi2aUmU,1253
4
+ pyconvexity/core/database.py,sha256=zqurVzPGuWei0jII1PTMAY-qaR-fr_A7uLxm__6KIlE,9276
5
+ pyconvexity/core/errors.py,sha256=HhrrOOEBJrzyB56_pmqh3NWvX6uHqWWNkdE5XM16rYI,2881
6
+ pyconvexity/core/types.py,sha256=eoVOAcDJWzjJKO9lYN7O17Us0XbahBpVBwv6uxdldh0,8508
7
+ pyconvexity/models/__init__.py,sha256=eVwf0ZTTEq1nM9M3NSMvj2yLPUOPNKMXv2A5GLT34-c,1470
8
+ pyconvexity/models/attributes.py,sha256=I6t1x7HX1gLw_lA6K87_GGmLAO34fpe4ZDX7_3LzMx4,13974
9
+ pyconvexity/models/components.py,sha256=K7QWelMVU_D18skvBZbap9dxP2AMS2116fcpmemkE6U,14629
10
+ pyconvexity/models/network.py,sha256=-itmot8StUdXogDpZUhGVIUC5uAEucYQ1LiTN1vPdA4,12923
11
+ pyconvexity/validation/__init__.py,sha256=_6SVqXkaDFqmagub_O064Zm_QIdBrOra-Gvvbo9vM4I,549
12
+ pyconvexity/validation/rules.py,sha256=bshO2Ibw8tBurg708Dmf79rIBoGV32t-jNHltjap9Pw,9323
13
+ pyconvexity-0.1.0.dist-info/METADATA,sha256=ltUWa115nNRTIAl4xbzFDjC2s9Q5LOhJcJHwMBS6ea4,4751
14
+ pyconvexity-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ pyconvexity-0.1.0.dist-info/top_level.txt,sha256=wFPEDXVaebR3JO5Tt3HNse-ws5aROCcxEco15d6j64s,12
16
+ pyconvexity-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ pyconvexity