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
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
-- ============================================================================
|
|
2
|
-
-- CORE ENERGY NETWORK SCHEMA
|
|
3
|
-
--
|
|
4
|
-
-- Optimized for
|
|
5
|
-
-- Version
|
|
2
|
+
-- CORE ENERGY NETWORK SCHEMA (SIMPLIFIED)
|
|
3
|
+
-- Single-network-per-file design for desktop SQLite
|
|
4
|
+
-- Optimized for fast timeseries access and simple Rust/Python API
|
|
5
|
+
-- Version 3.1.0 - Single network + Sparse scenarios + Raw timeseries
|
|
6
6
|
-- ============================================================================
|
|
7
7
|
|
|
8
8
|
-- ============================================================================
|
|
9
|
-
--
|
|
9
|
+
-- NETWORK METADATA
|
|
10
10
|
-- ============================================================================
|
|
11
11
|
|
|
12
|
-
--
|
|
13
|
-
CREATE TABLE
|
|
14
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
|
+
-- Network metadata - single row per database file
|
|
13
|
+
CREATE TABLE network_metadata (
|
|
15
14
|
name TEXT NOT NULL,
|
|
16
15
|
description TEXT,
|
|
17
16
|
|
|
@@ -20,55 +19,26 @@ CREATE TABLE networks (
|
|
|
20
19
|
time_end DATETIME NOT NULL,
|
|
21
20
|
time_interval TEXT NOT NULL, -- ISO 8601 duration (PT1H, PT30M, PT2H, etc.)
|
|
22
21
|
|
|
23
|
-
--
|
|
24
|
-
|
|
22
|
+
-- Network-level flags
|
|
23
|
+
locked BOOLEAN DEFAULT 0, -- Prevent accidental edits to base network
|
|
25
24
|
|
|
26
25
|
-- Metadata
|
|
27
26
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
28
27
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
-
created_by TEXT,
|
|
30
28
|
|
|
31
29
|
CONSTRAINT valid_time_range CHECK (time_end > time_start)
|
|
32
30
|
);
|
|
33
31
|
|
|
34
|
-
CREATE INDEX idx_networks_name ON networks(name);
|
|
35
|
-
CREATE INDEX idx_networks_created_at ON networks(created_at);
|
|
36
|
-
|
|
37
32
|
-- Network time periods - optimized storage using computed timestamps
|
|
38
|
-
-- Instead of storing 75k+ timestamp strings, we compute them from the time axis
|
|
39
|
-
-- This reduces storage from ~3.4MB to ~24 bytes per network
|
|
40
33
|
CREATE TABLE network_time_periods (
|
|
41
|
-
network_id INTEGER NOT NULL,
|
|
42
34
|
period_count INTEGER NOT NULL, -- Total number of periods (e.g., 8760 for hourly year)
|
|
43
35
|
start_timestamp INTEGER NOT NULL, -- Unix timestamp of first period
|
|
44
36
|
interval_seconds INTEGER NOT NULL, -- Seconds between periods (3600 for hourly)
|
|
45
37
|
|
|
46
|
-
PRIMARY KEY (network_id),
|
|
47
|
-
|
|
48
|
-
CONSTRAINT fk_time_periods_network
|
|
49
|
-
FOREIGN KEY (network_id) REFERENCES networks(id) ON DELETE CASCADE,
|
|
50
38
|
CONSTRAINT valid_period_count CHECK (period_count > 0),
|
|
51
39
|
CONSTRAINT valid_interval CHECK (interval_seconds > 0)
|
|
52
40
|
);
|
|
53
41
|
|
|
54
|
-
-- No additional indexes needed - primary key on network_id is sufficient
|
|
55
|
-
-- Timestamps are computed as: start_timestamp + (period_index * interval_seconds)
|
|
56
|
-
|
|
57
|
-
-- Network locks - prevents concurrent modifications
|
|
58
|
-
CREATE TABLE network_locks (
|
|
59
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
60
|
-
network_id INTEGER NOT NULL UNIQUE,
|
|
61
|
-
locked_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
62
|
-
locked_by TEXT NOT NULL, -- Process/user identifier
|
|
63
|
-
lock_reason TEXT NOT NULL, -- 'solving', 'importing', 'editing', etc.
|
|
64
|
-
|
|
65
|
-
CONSTRAINT fk_network_locks_network
|
|
66
|
-
FOREIGN KEY (network_id) REFERENCES networks(id) ON DELETE CASCADE
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
CREATE INDEX idx_network_locks_network ON network_locks(network_id);
|
|
70
|
-
CREATE INDEX idx_network_locks_reason ON network_locks(lock_reason);
|
|
71
|
-
|
|
72
42
|
-- ============================================================================
|
|
73
43
|
-- CARRIERS - ENERGY TYPES
|
|
74
44
|
-- ============================================================================
|
|
@@ -76,8 +46,7 @@ CREATE INDEX idx_network_locks_reason ON network_locks(lock_reason);
|
|
|
76
46
|
-- Carriers table - energy carriers (electricity, gas, heat, etc.)
|
|
77
47
|
CREATE TABLE carriers (
|
|
78
48
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
-
|
|
80
|
-
name TEXT NOT NULL,
|
|
49
|
+
name TEXT NOT NULL UNIQUE,
|
|
81
50
|
|
|
82
51
|
-- Carrier properties from PyPSA reference
|
|
83
52
|
co2_emissions REAL DEFAULT 0.0, -- tonnes/MWh
|
|
@@ -85,44 +54,32 @@ CREATE TABLE carriers (
|
|
|
85
54
|
nice_name TEXT, -- Display name
|
|
86
55
|
max_growth REAL DEFAULT NULL, -- MW - can be infinite
|
|
87
56
|
max_relative_growth REAL DEFAULT 0.0, -- MW
|
|
57
|
+
curtailable BOOLEAN DEFAULT FALSE, -- Whether the carrier can be curtailed
|
|
88
58
|
|
|
89
59
|
-- Metadata
|
|
90
60
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
91
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
92
|
-
|
|
93
|
-
CONSTRAINT fk_carriers_network
|
|
94
|
-
FOREIGN KEY (network_id) REFERENCES networks(id) ON DELETE CASCADE,
|
|
95
|
-
CONSTRAINT uq_carriers_network_name
|
|
96
|
-
UNIQUE (network_id, name)
|
|
61
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
97
62
|
);
|
|
98
63
|
|
|
99
|
-
CREATE INDEX idx_carriers_network ON carriers(network_id);
|
|
100
|
-
CREATE INDEX idx_carriers_name ON carriers(network_id, name);
|
|
101
|
-
|
|
102
64
|
-- ============================================================================
|
|
103
65
|
-- UNIFIED COMPONENT SYSTEM
|
|
104
66
|
-- ============================================================================
|
|
105
67
|
|
|
106
68
|
-- Components table - unified table for all network components
|
|
107
|
-
-- This is the single source of truth for component identity and relationships
|
|
108
69
|
CREATE TABLE components (
|
|
109
70
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
name TEXT NOT NULL,
|
|
71
|
+
component_type TEXT NOT NULL, -- 'BUS', 'GENERATOR', 'LOAD', 'LINE', 'LINK', 'STORAGE_UNIT', 'STORE', 'UNMET_LOAD', 'CONSTRAINT', 'TRANSFORMER', 'SHUNT_IMPEDANCE'
|
|
72
|
+
name TEXT NOT NULL UNIQUE,
|
|
113
73
|
|
|
114
|
-
-- Geographic location (optional
|
|
115
|
-
-- Lines and links are connections between buses and don't have their own coordinates
|
|
74
|
+
-- Geographic location (optional)
|
|
116
75
|
latitude REAL,
|
|
117
76
|
longitude REAL,
|
|
77
|
+
geometry TEXT, -- GeoJSON geometry (Point, LineString, Polygon, etc.)
|
|
118
78
|
|
|
119
|
-
-- Energy carrier reference
|
|
79
|
+
-- Energy carrier reference
|
|
120
80
|
carrier_id INTEGER,
|
|
121
81
|
|
|
122
|
-
-- Bus connections
|
|
123
|
-
-- For single connections (GENERATOR, LOAD, STORAGE_UNIT, STORE): use bus_id
|
|
124
|
-
-- For dual connections (LINE, LINK): use bus0_id and bus1_id
|
|
125
|
-
-- For buses: all are NULL (buses don't connect to other buses)
|
|
82
|
+
-- Bus connections
|
|
126
83
|
bus_id INTEGER, -- Single bus connection
|
|
127
84
|
bus0_id INTEGER, -- First bus for lines/links
|
|
128
85
|
bus1_id INTEGER, -- Second bus for lines/links
|
|
@@ -131,8 +88,6 @@ CREATE TABLE components (
|
|
|
131
88
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
132
89
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
133
90
|
|
|
134
|
-
CONSTRAINT fk_components_network
|
|
135
|
-
FOREIGN KEY (network_id) REFERENCES networks(id) ON DELETE CASCADE,
|
|
136
91
|
CONSTRAINT fk_components_carrier
|
|
137
92
|
FOREIGN KEY (carrier_id) REFERENCES carriers(id),
|
|
138
93
|
CONSTRAINT fk_components_bus
|
|
@@ -141,141 +96,178 @@ CREATE TABLE components (
|
|
|
141
96
|
FOREIGN KEY (bus0_id) REFERENCES components(id),
|
|
142
97
|
CONSTRAINT fk_components_bus1
|
|
143
98
|
FOREIGN KEY (bus1_id) REFERENCES components(id),
|
|
144
|
-
CONSTRAINT uq_components_network_name
|
|
145
|
-
UNIQUE (network_id, name),
|
|
146
99
|
CONSTRAINT valid_component_type
|
|
147
|
-
CHECK (component_type IN ('BUS', 'GENERATOR', 'LOAD', 'LINE', 'LINK', 'STORAGE_UNIT', 'STORE', 'UNMET_LOAD', 'CONSTRAINT'))
|
|
148
|
-
-- New constraint: carrier_id must be NOT NULL for all components except CONSTRAINT
|
|
149
|
-
CONSTRAINT valid_carrier_id
|
|
150
|
-
CHECK (component_type != 'CONSTRAINT' OR carrier_id IS NULL)
|
|
151
|
-
-- Note: UNMET_LOAD uniqueness per bus is enforced in backend logic since SQLite doesn't support partial unique constraints
|
|
100
|
+
CHECK (component_type IN ('BUS', 'GENERATOR', 'LOAD', 'LINE', 'LINK', 'STORAGE_UNIT', 'STORE', 'UNMET_LOAD', 'CONSTRAINT', 'TRANSFORMER', 'SHUNT_IMPEDANCE'))
|
|
152
101
|
);
|
|
153
102
|
|
|
154
|
-
--
|
|
155
|
-
CREATE INDEX idx_components_network ON components(network_id);
|
|
103
|
+
-- Essential indexes only
|
|
156
104
|
CREATE INDEX idx_components_type ON components(component_type);
|
|
157
105
|
CREATE INDEX idx_components_name ON components(name);
|
|
158
|
-
CREATE INDEX
|
|
159
|
-
CREATE INDEX
|
|
160
|
-
CREATE INDEX
|
|
161
|
-
CREATE INDEX idx_components_location ON components(latitude, longitude); -- For spatial queries
|
|
162
|
-
CREATE INDEX idx_components_bus ON components(bus_id); -- For bus connections
|
|
163
|
-
CREATE INDEX idx_components_bus0 ON components(bus0_id); -- For line/link connections
|
|
164
|
-
CREATE INDEX idx_components_bus1 ON components(bus1_id); -- For line/link connections
|
|
106
|
+
CREATE INDEX idx_components_bus ON components(bus_id);
|
|
107
|
+
CREATE INDEX idx_components_bus0 ON components(bus0_id);
|
|
108
|
+
CREATE INDEX idx_components_bus1 ON components(bus1_id);
|
|
165
109
|
|
|
166
110
|
-- ============================================================================
|
|
167
111
|
-- ATTRIBUTE VALIDATION SYSTEM
|
|
168
112
|
-- ============================================================================
|
|
169
113
|
|
|
170
|
-
-- Attribute validation rules table
|
|
171
|
-
-- This enforces PyPSA attribute definitions at the database level
|
|
114
|
+
-- Attribute validation rules table
|
|
172
115
|
CREATE TABLE attribute_validation_rules (
|
|
173
116
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
174
117
|
component_type TEXT NOT NULL,
|
|
175
118
|
attribute_name TEXT NOT NULL,
|
|
176
|
-
display_name TEXT,
|
|
119
|
+
display_name TEXT,
|
|
177
120
|
|
|
178
|
-
-- Validation rules
|
|
121
|
+
-- Validation rules
|
|
179
122
|
data_type TEXT NOT NULL, -- 'float', 'boolean', 'string', 'int'
|
|
180
|
-
unit TEXT,
|
|
181
|
-
default_value TEXT,
|
|
123
|
+
unit TEXT,
|
|
124
|
+
default_value TEXT,
|
|
182
125
|
allowed_storage_types TEXT NOT NULL, -- 'static', 'timeseries', 'static_or_timeseries'
|
|
183
126
|
is_required BOOLEAN DEFAULT FALSE,
|
|
184
|
-
is_input BOOLEAN DEFAULT TRUE,
|
|
127
|
+
is_input BOOLEAN DEFAULT TRUE,
|
|
185
128
|
description TEXT,
|
|
186
129
|
|
|
187
|
-
--
|
|
188
|
-
min_value REAL,
|
|
189
|
-
max_value REAL,
|
|
190
|
-
allowed_values TEXT,
|
|
191
|
-
|
|
192
|
-
-- Attribute grouping and storage control
|
|
193
|
-
group_name TEXT DEFAULT 'other', -- Attribute group: 'basic', 'power', 'economics', 'control', 'other'
|
|
194
|
-
to_save BOOLEAN DEFAULT TRUE, -- Whether to save this attribute in solve results
|
|
130
|
+
-- Constraints
|
|
131
|
+
min_value REAL,
|
|
132
|
+
max_value REAL,
|
|
133
|
+
allowed_values TEXT, -- JSON array
|
|
195
134
|
|
|
196
|
-
--
|
|
197
|
-
|
|
198
|
-
|
|
135
|
+
-- Grouping
|
|
136
|
+
group_name TEXT DEFAULT 'other',
|
|
137
|
+
to_save BOOLEAN DEFAULT TRUE,
|
|
199
138
|
|
|
200
|
-
-- Constraints
|
|
201
139
|
CONSTRAINT uq_validation_rule
|
|
202
140
|
UNIQUE (component_type, attribute_name),
|
|
203
141
|
CONSTRAINT valid_component_type_validation
|
|
204
|
-
CHECK (component_type IN ('BUS', 'GENERATOR', 'LOAD', 'LINE', 'LINK', 'STORAGE_UNIT', 'STORE', 'UNMET_LOAD', 'CONSTRAINT')),
|
|
142
|
+
CHECK (component_type IN ('BUS', 'GENERATOR', 'LOAD', 'LINE', 'LINK', 'STORAGE_UNIT', 'STORE', 'UNMET_LOAD', 'CONSTRAINT', 'TRANSFORMER', 'SHUNT_IMPEDANCE')),
|
|
205
143
|
CONSTRAINT valid_data_type
|
|
206
144
|
CHECK (data_type IN ('float', 'boolean', 'string', 'int')),
|
|
207
145
|
CONSTRAINT valid_allowed_storage_types
|
|
208
146
|
CHECK (allowed_storage_types IN ('static', 'timeseries', 'static_or_timeseries')),
|
|
209
147
|
CONSTRAINT valid_group_name
|
|
210
|
-
CHECK (group_name IN ('basic', '
|
|
148
|
+
CHECK (group_name IN ('basic', 'capacity', 'power_limits', 'energy', 'unit_commitment', 'ramping', 'costs', 'electrical'))
|
|
211
149
|
);
|
|
212
150
|
|
|
213
|
-
--
|
|
151
|
+
-- Essential indexes only
|
|
214
152
|
CREATE INDEX idx_validation_component_type ON attribute_validation_rules(component_type);
|
|
215
|
-
CREATE INDEX
|
|
216
|
-
CREATE INDEX idx_validation_input ON attribute_validation_rules(is_input);
|
|
217
|
-
CREATE INDEX idx_validation_required ON attribute_validation_rules(is_required);
|
|
153
|
+
CREATE INDEX idx_validation_lookup ON attribute_validation_rules(component_type, attribute_name);
|
|
218
154
|
|
|
219
155
|
-- ============================================================================
|
|
220
|
-
--
|
|
156
|
+
-- SCENARIOS - SPARSE OVERRIDE APPROACH
|
|
221
157
|
-- ============================================================================
|
|
222
158
|
|
|
223
|
-
--
|
|
224
|
-
--
|
|
159
|
+
-- Scenarios table - represents alternative scenarios
|
|
160
|
+
-- Base network has NO scenario (scenario_id = NULL in attributes)
|
|
161
|
+
-- Supports both deterministic what-if scenarios (probability = NULL) and stochastic scenarios (probability set)
|
|
162
|
+
CREATE TABLE scenarios (
|
|
163
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
164
|
+
name TEXT NOT NULL UNIQUE,
|
|
165
|
+
description TEXT,
|
|
166
|
+
probability REAL DEFAULT NULL, -- For stochastic optimization (NULL = deterministic what-if)
|
|
167
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
168
|
+
|
|
169
|
+
CONSTRAINT valid_probability
|
|
170
|
+
CHECK (probability IS NULL OR (probability >= 0 AND probability <= 1))
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
-- ============================================================================
|
|
174
|
+
-- UNIFIED COMPONENT ATTRIBUTES - SPARSE SCENARIOS + RAW TIMESERIES
|
|
175
|
+
-- ============================================================================
|
|
176
|
+
|
|
177
|
+
-- Component attributes - sparse scenario overrides
|
|
178
|
+
-- scenario_id = NULL → Base network (editable)
|
|
179
|
+
-- scenario_id = 1 → Scenario 1 (overrides base, read-only)
|
|
225
180
|
CREATE TABLE component_attributes (
|
|
226
181
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
227
182
|
component_id INTEGER NOT NULL,
|
|
228
183
|
attribute_name TEXT NOT NULL,
|
|
229
184
|
|
|
230
|
-
-- Scenario support -
|
|
231
|
-
scenario_id INTEGER
|
|
185
|
+
-- Scenario support - NULL = base network, non-NULL = scenario override
|
|
186
|
+
scenario_id INTEGER, -- NULLABLE!
|
|
232
187
|
|
|
233
|
-
-- Storage type
|
|
188
|
+
-- Storage type
|
|
234
189
|
storage_type TEXT NOT NULL CHECK (storage_type IN ('static', 'timeseries')),
|
|
235
190
|
|
|
236
|
-
--
|
|
237
|
-
static_value TEXT,
|
|
238
|
-
timeseries_data BLOB,
|
|
191
|
+
-- Value storage
|
|
192
|
+
static_value TEXT, -- JSON-encoded static value (all data types)
|
|
193
|
+
timeseries_data BLOB, -- Raw f32 array (NOT Parquet!)
|
|
239
194
|
|
|
240
|
-
-- Cached metadata
|
|
241
|
-
data_type TEXT NOT NULL,
|
|
242
|
-
unit TEXT,
|
|
243
|
-
is_input BOOLEAN DEFAULT TRUE,
|
|
195
|
+
-- Cached metadata
|
|
196
|
+
data_type TEXT NOT NULL,
|
|
197
|
+
unit TEXT,
|
|
198
|
+
is_input BOOLEAN DEFAULT TRUE,
|
|
244
199
|
|
|
245
200
|
-- Metadata
|
|
246
201
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
247
202
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
248
203
|
|
|
249
|
-
-- Constraints
|
|
250
204
|
CONSTRAINT fk_attributes_component
|
|
251
205
|
FOREIGN KEY (component_id) REFERENCES components(id) ON DELETE CASCADE,
|
|
252
206
|
CONSTRAINT fk_attributes_scenario
|
|
253
207
|
FOREIGN KEY (scenario_id) REFERENCES scenarios(id) ON DELETE CASCADE,
|
|
254
208
|
|
|
255
|
-
--
|
|
209
|
+
-- Storage validation
|
|
256
210
|
CONSTRAINT check_exactly_one_storage_type CHECK (
|
|
257
211
|
(storage_type = 'static' AND static_value IS NOT NULL AND timeseries_data IS NULL) OR
|
|
258
212
|
(storage_type = 'timeseries' AND static_value IS NULL AND timeseries_data IS NOT NULL)
|
|
259
213
|
),
|
|
260
214
|
|
|
261
|
-
--
|
|
215
|
+
-- Unique per component/attribute/scenario (NULL scenario counts as unique value)
|
|
262
216
|
CONSTRAINT uq_component_attribute_scenario
|
|
263
217
|
UNIQUE (component_id, attribute_name, scenario_id)
|
|
264
218
|
);
|
|
265
219
|
|
|
266
|
-
--
|
|
267
|
-
CREATE INDEX
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
CREATE INDEX idx_attributes_component_name ON component_attributes(component_id, attribute_name);
|
|
271
|
-
CREATE INDEX idx_attributes_data_type ON component_attributes(data_type);
|
|
272
|
-
CREATE INDEX idx_attributes_is_input ON component_attributes(is_input);
|
|
220
|
+
-- Essential indexes only
|
|
221
|
+
CREATE INDEX idx_attributes_lookup ON component_attributes(
|
|
222
|
+
component_id, attribute_name, scenario_id
|
|
223
|
+
);
|
|
273
224
|
CREATE INDEX idx_attributes_scenario ON component_attributes(scenario_id);
|
|
274
225
|
|
|
275
|
-
--
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
226
|
+
-- ============================================================================
|
|
227
|
+
-- SCENARIO CACHE - MATERIALIZED VIEW FOR FAST SCENARIO COUNTING
|
|
228
|
+
-- ============================================================================
|
|
229
|
+
|
|
230
|
+
-- Scenario cache - stores precomputed scenario counts and details
|
|
231
|
+
-- This is a materialized view that's kept in sync via application code
|
|
232
|
+
-- Updated transactionally whenever attribute values change
|
|
233
|
+
CREATE TABLE IF NOT EXISTS attribute_scenario_cache (
|
|
234
|
+
component_id INTEGER NOT NULL,
|
|
235
|
+
attribute_name TEXT NOT NULL,
|
|
236
|
+
|
|
237
|
+
-- Cached computed values
|
|
238
|
+
scenario_count INTEGER NOT NULL DEFAULT 1, -- Display count (includes synthetic base)
|
|
239
|
+
has_base_value BOOLEAN NOT NULL DEFAULT FALSE, -- TRUE if base network has value
|
|
240
|
+
has_scenario_values BOOLEAN NOT NULL DEFAULT FALSE, -- TRUE if any scenarios have values
|
|
241
|
+
|
|
242
|
+
-- Scenario details for dropdown (JSON array)
|
|
243
|
+
-- Format: [{scenario_id: 0, scenario_name: "Base", value: "123", has_value: true}, ...]
|
|
244
|
+
scenario_details TEXT,
|
|
245
|
+
|
|
246
|
+
-- Metadata
|
|
247
|
+
last_updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
248
|
+
|
|
249
|
+
PRIMARY KEY (component_id, attribute_name),
|
|
250
|
+
CONSTRAINT fk_scenario_cache_component
|
|
251
|
+
FOREIGN KEY (component_id) REFERENCES components(id) ON DELETE CASCADE
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
-- Index for fast component-based lookups
|
|
255
|
+
CREATE INDEX IF NOT EXISTS idx_scenario_cache_component
|
|
256
|
+
ON attribute_scenario_cache(component_id);
|
|
257
|
+
|
|
258
|
+
-- Index for bulk table loads
|
|
259
|
+
CREATE INDEX IF NOT EXISTS idx_scenario_cache_component_type
|
|
260
|
+
ON attribute_scenario_cache(component_id, attribute_name);
|
|
261
|
+
|
|
262
|
+
-- NOTES:
|
|
263
|
+
-- This cache is maintained by application code (Rust) in the same transaction
|
|
264
|
+
-- as attribute writes. This ensures ACID guarantees - the cache can never be
|
|
265
|
+
-- stale or inconsistent with the actual data.
|
|
266
|
+
--
|
|
267
|
+
-- Cache logic:
|
|
268
|
+
-- - If has_scenario_values: scenario_count = num_scenarios + 1 (always include base)
|
|
269
|
+
-- - If only has_base_value: scenario_count = 1 (no indicator shown)
|
|
270
|
+
-- - If neither: scenario_count = 0 (no values at all)
|
|
279
271
|
|
|
280
272
|
-- ============================================================================
|
|
281
273
|
-- VALIDATION TRIGGERS
|
|
@@ -311,37 +303,7 @@ BEGIN
|
|
|
311
303
|
SELECT RAISE(ABORT, 'Storage type not allowed for this attribute');
|
|
312
304
|
END;
|
|
313
305
|
|
|
314
|
-
-- Trigger to
|
|
315
|
-
CREATE TRIGGER validate_component_attribute_update
|
|
316
|
-
BEFORE UPDATE ON component_attributes
|
|
317
|
-
FOR EACH ROW
|
|
318
|
-
WHEN NOT EXISTS (
|
|
319
|
-
SELECT 1 FROM components c
|
|
320
|
-
JOIN attribute_validation_rules avr ON c.component_type = avr.component_type
|
|
321
|
-
WHERE c.id = NEW.component_id
|
|
322
|
-
AND avr.attribute_name = NEW.attribute_name
|
|
323
|
-
)
|
|
324
|
-
BEGIN
|
|
325
|
-
SELECT RAISE(ABORT, 'Attribute is not defined for this component type');
|
|
326
|
-
END;
|
|
327
|
-
|
|
328
|
-
-- Trigger to validate storage type on update
|
|
329
|
-
CREATE TRIGGER validate_storage_type_update
|
|
330
|
-
BEFORE UPDATE ON component_attributes
|
|
331
|
-
FOR EACH ROW
|
|
332
|
-
WHEN EXISTS (
|
|
333
|
-
SELECT 1 FROM components c
|
|
334
|
-
JOIN attribute_validation_rules avr ON c.component_type = avr.component_type
|
|
335
|
-
WHERE c.id = NEW.component_id
|
|
336
|
-
AND avr.attribute_name = NEW.attribute_name
|
|
337
|
-
AND avr.allowed_storage_types != 'static_or_timeseries'
|
|
338
|
-
AND avr.allowed_storage_types != NEW.storage_type
|
|
339
|
-
)
|
|
340
|
-
BEGIN
|
|
341
|
-
SELECT RAISE(ABORT, 'Storage type not allowed for this attribute');
|
|
342
|
-
END;
|
|
343
|
-
|
|
344
|
-
-- Trigger to update metadata timestamps
|
|
306
|
+
-- Trigger to update timestamps
|
|
345
307
|
CREATE TRIGGER update_component_attributes_timestamp
|
|
346
308
|
BEFORE UPDATE ON component_attributes
|
|
347
309
|
FOR EACH ROW
|
|
@@ -361,100 +323,38 @@ BEGIN
|
|
|
361
323
|
WHERE id = NEW.component_id;
|
|
362
324
|
END;
|
|
363
325
|
|
|
364
|
-
-- ============================================================================
|
|
365
|
-
-- COMPONENT GEOMETRIES - Optional GeoJSON geometries for spatial representation
|
|
366
|
-
-- ============================================================================
|
|
367
|
-
|
|
368
|
-
-- Component geometries - stores optional GeoJSON geometries for components
|
|
369
|
-
-- Enables real spatial representation (e.g., actual line routes, generator footprints)
|
|
370
|
-
CREATE TABLE component_geometries (
|
|
371
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
372
|
-
component_id INTEGER NOT NULL UNIQUE,
|
|
373
|
-
|
|
374
|
-
-- GeoJSON geometry stored as JSON text
|
|
375
|
-
-- Supports: Point, LineString, Polygon, MultiPolygon, MultiPoint, MultiLineString, GeometryCollection
|
|
376
|
-
geometry TEXT NOT NULL,
|
|
377
|
-
|
|
378
|
-
-- Cache the geometry type for faster queries and validation
|
|
379
|
-
geometry_type TEXT NOT NULL CHECK (geometry_type IN (
|
|
380
|
-
'Point', 'LineString', 'Polygon', 'MultiPolygon',
|
|
381
|
-
'MultiPoint', 'MultiLineString', 'GeometryCollection'
|
|
382
|
-
)),
|
|
383
|
-
|
|
384
|
-
-- Cache bounding box for spatial indexing and quick filtering
|
|
385
|
-
bbox_min_lng REAL,
|
|
386
|
-
bbox_min_lat REAL,
|
|
387
|
-
bbox_max_lng REAL,
|
|
388
|
-
bbox_max_lat REAL,
|
|
389
|
-
|
|
390
|
-
-- Metadata
|
|
391
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
392
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
393
|
-
|
|
394
|
-
CONSTRAINT fk_geometry_component
|
|
395
|
-
FOREIGN KEY (component_id) REFERENCES components(id) ON DELETE CASCADE
|
|
396
|
-
);
|
|
397
|
-
|
|
398
|
-
-- Indexes for efficient geometry queries
|
|
399
|
-
CREATE INDEX idx_component_geometries_component ON component_geometries(component_id);
|
|
400
|
-
CREATE INDEX idx_component_geometries_type ON component_geometries(geometry_type);
|
|
401
|
-
CREATE INDEX idx_component_geometries_bbox ON component_geometries(
|
|
402
|
-
bbox_min_lng, bbox_min_lat, bbox_max_lng, bbox_max_lat
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
-- Trigger to update timestamp on geometry changes
|
|
406
|
-
CREATE TRIGGER update_component_geometries_timestamp
|
|
407
|
-
BEFORE UPDATE ON component_geometries
|
|
408
|
-
FOR EACH ROW
|
|
409
|
-
BEGIN
|
|
410
|
-
UPDATE component_geometries
|
|
411
|
-
SET updated_at = CURRENT_TIMESTAMP
|
|
412
|
-
WHERE id = NEW.id;
|
|
413
|
-
END;
|
|
414
|
-
|
|
415
326
|
-- ============================================================================
|
|
416
327
|
-- NETWORK CONFIGURATION
|
|
417
328
|
-- ============================================================================
|
|
418
329
|
|
|
419
|
-
-- Network configuration parameters
|
|
420
|
-
-- Supports scenario-aware configuration with fallback to network defaults
|
|
330
|
+
-- Network configuration parameters with scenario support
|
|
421
331
|
CREATE TABLE network_config (
|
|
422
332
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
423
|
-
network_id INTEGER NOT NULL,
|
|
424
333
|
scenario_id INTEGER, -- NULL for network defaults
|
|
425
334
|
|
|
426
|
-
-- Parameter definition
|
|
427
335
|
param_name TEXT NOT NULL,
|
|
428
336
|
param_type TEXT NOT NULL,
|
|
429
337
|
param_value TEXT NOT NULL,
|
|
430
338
|
param_description TEXT,
|
|
431
339
|
|
|
432
|
-
-- Metadata
|
|
433
340
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
434
341
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
435
342
|
|
|
436
|
-
CONSTRAINT fk_network_config_network
|
|
437
|
-
FOREIGN KEY (network_id) REFERENCES networks(id) ON DELETE CASCADE,
|
|
438
343
|
CONSTRAINT fk_network_config_scenario
|
|
439
344
|
FOREIGN KEY (scenario_id) REFERENCES scenarios(id) ON DELETE CASCADE,
|
|
440
345
|
CONSTRAINT uq_network_config_param
|
|
441
|
-
UNIQUE (
|
|
346
|
+
UNIQUE (scenario_id, param_name),
|
|
442
347
|
CONSTRAINT valid_param_type
|
|
443
348
|
CHECK (param_type IN ('boolean', 'real', 'integer', 'string', 'json'))
|
|
444
349
|
);
|
|
445
350
|
|
|
446
|
-
|
|
447
|
-
CREATE INDEX idx_network_config_network ON network_config(network_id);
|
|
448
|
-
CREATE INDEX idx_network_config_scenario ON network_config(scenario_id);
|
|
449
|
-
CREATE INDEX idx_network_config_param ON network_config(param_name);
|
|
450
|
-
CREATE INDEX idx_network_config_network_param ON network_config(network_id, param_name);
|
|
451
|
-
CREATE INDEX idx_network_config_network_scenario_param ON network_config(network_id, scenario_id, param_name);
|
|
351
|
+
CREATE INDEX idx_network_config_lookup ON network_config(scenario_id, param_name);
|
|
452
352
|
|
|
453
353
|
-- ============================================================================
|
|
454
354
|
-- SYSTEM METADATA
|
|
455
355
|
-- ============================================================================
|
|
456
356
|
|
|
457
|
-
-- System metadata table for schema
|
|
357
|
+
-- System metadata table for schema version tracking and system-level settings
|
|
458
358
|
CREATE TABLE system_metadata (
|
|
459
359
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
460
360
|
key TEXT NOT NULL UNIQUE,
|
|
@@ -466,12 +366,12 @@ CREATE TABLE system_metadata (
|
|
|
466
366
|
|
|
467
367
|
CREATE INDEX idx_system_metadata_key ON system_metadata(key);
|
|
468
368
|
|
|
469
|
-
--
|
|
369
|
+
-- Initialize system metadata with schema version
|
|
470
370
|
INSERT INTO system_metadata (key, value, description)
|
|
471
|
-
VALUES ('schema_version', '
|
|
371
|
+
VALUES ('schema_version', '3.1.0', 'Database schema version');
|
|
472
372
|
|
|
473
|
-
|
|
474
|
-
|
|
373
|
+
-- ============================================================================
|
|
374
|
+
-- SCHEMA VERSION
|
|
375
|
+
-- ============================================================================
|
|
475
376
|
|
|
476
|
-
|
|
477
|
-
VALUES ('atomic_operations', 'enabled', 'Atomic database operations support');
|
|
377
|
+
PRAGMA user_version = 31; -- Schema version 3.1
|