pyconvexity 0.1.0__py3-none-any.whl → 0.1.2__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.
@@ -0,0 +1,443 @@
1
+ -- ============================================================================
2
+ -- PYPSA ATTRIBUTE VALIDATION RULES
3
+ -- Complete set of validation rules for all component types
4
+ -- Based on PyPSA component attribute definitions
5
+ -- Updated to use simplified 5-group system: basic, power, economics, control, other
6
+ -- Version 2.2.0
7
+ -- ============================================================================
8
+
9
+ -- Clear any existing validation rules
10
+ DELETE FROM attribute_validation_rules;
11
+
12
+ -- ============================================================================
13
+ -- BUS ATTRIBUTES
14
+ -- ============================================================================
15
+
16
+ -- BUS attributes from buses.csv (PyPSA reference) - Updated to simplified groups
17
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name, to_save) VALUES
18
+ -- Input attributes for BUS
19
+ ('BUS', 'v_nom', 'Nominal Voltage', 'float', 'kV', '1', 'static', FALSE, TRUE, 'Nominal voltage', 0, NULL, 'power', FALSE),
20
+ ('BUS', 'type', 'Bus Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for bus type. Not yet implemented.', NULL, NULL, 'basic', FALSE),
21
+ ('BUS', 'x', 'X Coordinate', 'float', 'n/a', '0', 'static', FALSE, TRUE, 'Position (e.g. longitude); the Spatial Reference System Identifier (SRID) is set in network.srid.', NULL, NULL, 'other', FALSE),
22
+ ('BUS', 'y', 'Y Coordinate', 'float', 'n/a', '0', 'static', FALSE, TRUE, 'Position (e.g. latitude); the Spatial Reference System Identifier (SRID) is set in network.srid.', NULL, NULL, 'other', FALSE),
23
+ ('BUS', 'carrier', 'Energy Carrier', 'string', 'n/a', 'AC', 'static', FALSE, TRUE, 'Energy carrier: can be "AC" or "DC" for electrical buses, or "heat" or "gas".', NULL, NULL, 'basic', FALSE),
24
+ ('BUS', 'unit', 'Unit', 'string', 'n/a', 'None', 'static', FALSE, TRUE, 'Unit of the bus'' carrier if the implicitly assumed unit ("MW") is inappropriate (e.g. "t/h", "MWh_th/h"). Only descriptive. Does not influence any PyPSA functions.', NULL, NULL, 'basic', FALSE),
25
+ ('BUS', 'v_mag_pu_set', 'Voltage Magnitude Setpoint', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Voltage magnitude set point, per unit of v_nom.', 0, NULL, 'power', FALSE),
26
+ ('BUS', 'v_mag_pu_min', 'Min Voltage Magnitude', 'float', 'per unit', '0', 'static', FALSE, TRUE, 'Minimum desired voltage, per unit of v_nom. This is a placeholder attribute and is not currently used by any PyPSA functions.', 0, NULL, 'power', FALSE),
27
+ ('BUS', 'v_mag_pu_max', 'Max Voltage Magnitude', 'float', 'per unit', 'inf', 'static', FALSE, TRUE, 'Maximum desired voltage, per unit of v_nom. This is a placeholder attribute and is not currently used by any PyPSA functions.', 0, NULL, 'power', FALSE),
28
+ -- Output attributes for BUS
29
+ ('BUS', 'control', 'Control Strategy', 'string', 'n/a', 'PQ', 'static', FALSE, FALSE, 'P,Q,V control strategy for PF, must be "PQ", "PV" or "Slack". Note that this attribute is an output inherited from the controls of the generators attached to the bus; setting it directly on the bus will not have any effect.', NULL, NULL, 'control', FALSE),
30
+ ('BUS', 'generator', 'Slack Generator', 'string', 'n/a', 'n/a', 'static', FALSE, FALSE, 'Name of slack generator attached to slack bus.', NULL, NULL, 'control', FALSE),
31
+ ('BUS', 'sub_network', 'Sub-Network', 'string', 'n/a', 'n/a', 'static', FALSE, FALSE, 'Name of connected sub-network to which bus belongs. This attribute is set by PyPSA in the function network.determine_network_topology(); do not set it directly by hand.', NULL, NULL, 'other', FALSE),
32
+ ('BUS', 'p', 'Active Power', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus (positive if net generation at bus)', NULL, NULL, 'power', TRUE),
33
+ ('BUS', 'q', 'Reactive Power', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power (positive if net generation at bus)', NULL, NULL, 'power', TRUE),
34
+ ('BUS', 'v_mag_pu', 'Voltage Magnitude', 'float', 'per unit', '1', 'timeseries', FALSE, FALSE, 'Voltage magnitude, per unit of v_nom', NULL, NULL, 'power', TRUE),
35
+ ('BUS', 'v_ang', 'Voltage Angle', 'float', 'radians', '0', 'timeseries', FALSE, FALSE, 'Voltage angle', NULL, NULL, 'power', TRUE),
36
+ ('BUS', 'marginal_price', 'Marginal Price', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Locational marginal price from LOPF from power balance constraint', NULL, NULL, 'economics', TRUE);
37
+
38
+ -- ============================================================================
39
+ -- GENERATOR ATTRIBUTES
40
+ -- ============================================================================
41
+
42
+ -- GENERATOR attributes from generators.csv (PyPSA reference) - Updated to simplified groups
43
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
44
+ -- Input attributes for GENERATOR
45
+ ('GENERATOR', 'control', 'Control Strategy', 'string', 'n/a', 'PQ', 'static', FALSE, TRUE, 'P,Q,V control strategy for PF, must be "PQ", "PV" or "Slack".', NULL, NULL, 'control'),
46
+ ('GENERATOR', 'type', 'Generator Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for generator type. Not yet implemented.', NULL, NULL, 'basic'),
47
+ ('GENERATOR', 'p_nom', 'Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'Nominal power for limits in optimization.', 0, NULL, 'power'),
48
+ ('GENERATOR', 'p_nom_mod', 'Nominal Power Module', 'float', 'MW', '0', 'static', FALSE, TRUE, 'Nominal power of the generator module.', 0, NULL, 'power'),
49
+ ('GENERATOR', 'p_nom_extendable', 'Extendable Capacity', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow capacity p_nom to be extended in optimization.', NULL, NULL, 'power'),
50
+ ('GENERATOR', 'p_nom_min', 'Min Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'If p_nom is extendable in optimization, set its minimum value.', 0, NULL, 'power'),
51
+ ('GENERATOR', 'p_nom_max', 'Max Nominal Power', 'float', 'MW', 'inf', 'static', FALSE, TRUE, 'If p_nom is extendable in optimization, set its maximum value (e.g. limited by technical potential).', 0, NULL, 'power'),
52
+ ('GENERATOR', 'p_min_pu', 'Min Capacity Factor', 'float', 'per unit', '0', 'static_or_timeseries', FALSE, TRUE, 'The minimum output for each snapshot per unit of p_nom for the optimization (e.g. for variable renewable generators this can change due to weather conditions and compulsory feed-in; for conventional generators it represents a minimal dispatch). Note that if comittable is False and p_min_pu > 0, this represents a must-run condition.', 0, NULL, 'power'),
53
+ ('GENERATOR', 'p_max_pu', 'Max Capacity Factor', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'The maximum output for each snapshot per unit of p_nom for the optimization (e.g. for variable renewable generators this can change due to weather conditions; for conventional generators it represents a maximum dispatch).', 0, 1, 'power'),
54
+ ('GENERATOR', 'p_set', 'Active Power Setpoint', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, 'active power set point (for PF)', NULL, NULL, 'power'),
55
+ ('GENERATOR', 'e_sum_min', 'Min Energy Sum', 'float', 'MWh', '-inf', 'static', FALSE, TRUE, 'The minimum total energy produced during a single optimization horizon.', NULL, NULL, 'power'),
56
+ ('GENERATOR', 'e_sum_max', 'Max Energy Sum', 'float', 'MWh', 'inf', 'static', FALSE, TRUE, 'The maximum total energy produced during a single optimization horizon.', NULL, NULL, 'power'),
57
+ ('GENERATOR', 'q_set', 'Reactive Power Setpoint', 'float', 'MVar', '0', 'static_or_timeseries', FALSE, TRUE, 'reactive power set point (for PF)', NULL, NULL, 'power'),
58
+ ('GENERATOR', 'sign', 'Power Sign', 'float', 'n/a', '1', 'static', FALSE, TRUE, 'power sign', NULL, NULL, 'power'),
59
+ ('GENERATOR', 'carrier', 'Energy Carrier', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Prime mover energy carrier (e.g. coal, gas, wind, solar); required for global constraints on primary energy in optimization', NULL, NULL, 'basic'),
60
+ ('GENERATOR', 'marginal_cost', 'Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost of production of 1 MWh.', 0, NULL, 'economics'),
61
+ ('GENERATOR', 'marginal_cost_quadratic', 'Quadratic Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Quadratic marginal cost of production of 1 MWh.', 0, NULL, 'economics'),
62
+ ('GENERATOR', 'capital_cost', 'Capital Cost', 'float', 'currency/MW', '0', 'static', FALSE, TRUE, 'Fixed period costs of extending p_nom by 1 MW, including periodized investment costs and periodic fixed O&M costs (e.g. annuitized investment costs).', 0, NULL, 'economics'),
63
+ ('GENERATOR', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether to consider the component in basic functionality or not', NULL, NULL, 'basic'),
64
+ ('GENERATOR', 'build_year', 'Build Year', 'int', 'year', '0', 'static', FALSE, TRUE, 'build year', 0, 3000, 'economics'),
65
+ ('GENERATOR', 'lifetime', 'Lifetime', 'float', 'years', 'inf', 'static', FALSE, TRUE, 'lifetime', 0, NULL, 'economics'),
66
+ ('GENERATOR', 'efficiency', 'Efficiency', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Ratio between primary energy and electrical energy, e.g. takes value 0.4 MWh_elec/MWh_thermal for gas. This is required for global constraints on primary energy in optimization.', 0, 1, 'power'),
67
+ ('GENERATOR', 'committable', 'Committable', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Use unit commitment (only possible if p_nom is not extendable).', NULL, NULL, 'control'),
68
+ ('GENERATOR', 'start_up_cost', 'Start-up Cost', 'float', 'currency', '0', 'static', FALSE, TRUE, 'Cost to start up the generator. Only read if committable is True.', 0, NULL, 'control'),
69
+ ('GENERATOR', 'shut_down_cost', 'Shutdown Cost', 'float', 'currency', '0', 'static', FALSE, TRUE, 'Cost to shut down the generator. Only read if committable is True.', 0, NULL, 'control'),
70
+ ('GENERATOR', 'stand_by_cost', 'Stand-by Cost', 'float', 'currency/h', '0', 'static_or_timeseries', FALSE, TRUE, 'Stand-by cost for operating the generator at null power output.', 0, NULL, 'control'),
71
+ ('GENERATOR', 'min_up_time', 'Min Up Time', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Minimum number of snapshots for status to be 1. Only read if committable is True.', 0, NULL, 'control'),
72
+ ('GENERATOR', 'min_down_time', 'Min Down Time', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Minimum number of snapshots for status to be 0. Only read if committable is True.', 0, NULL, 'control'),
73
+ ('GENERATOR', 'up_time_before', 'Up Time Before', 'int', 'snapshots', '1', 'static', FALSE, TRUE, 'Number of snapshots that the generator was online before network.snapshots start. Only read if committable is True and min_up_time is non-zero.', 0, NULL, 'control'),
74
+ ('GENERATOR', 'down_time_before', 'Down Time Before', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Number of snapshots that the generator was offline before network.snapshots start. Only read if committable is True and min_down_time is non-zero.', 0, NULL, 'control'),
75
+ ('GENERATOR', 'ramp_limit_up', 'Ramp Up Limit', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximum active power increase from one snapshot to the next, per unit of the nominal power. Ignored if 1.', NULL, NULL, 'control'),
76
+ ('GENERATOR', 'ramp_limit_down', 'Ramp Down Limit', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximum active power decrease from one snapshot to the next, per unit of the nominal power. Ignored if 1.', NULL, NULL, 'control'),
77
+ ('GENERATOR', 'ramp_limit_start_up', 'Ramp Up at Start', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Maximum active power increase at start up, per unit of the nominal power. Only read if committable is True.', NULL, NULL, 'control'),
78
+ ('GENERATOR', 'ramp_limit_shut_down', 'Ramp Down at Shutdown', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Maximum active power decrease at shut down, per unit of the nominal power. Only read if committable is True.', NULL, NULL, 'control'),
79
+ ('GENERATOR', 'weight', 'Weight', 'float', 'n/a', '1', 'static', FALSE, TRUE, 'Weighting of a generator. Only used for network clustering.', NULL, NULL, 'other'),
80
+ -- Output attributes for GENERATOR (from PyPSA generators.csv lines 38-46)
81
+ ('GENERATOR', 'p', 'Active Power', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus (positive if net generation)', NULL, NULL, 'power'),
82
+ ('GENERATOR', 'q', 'Reactive Power', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power (positive if net generation)', NULL, NULL, 'power'),
83
+ ('GENERATOR', 'p_nom_opt', 'Optimised Nominal Power', 'float', 'MW', '0', 'static', FALSE, FALSE, 'Optimised nominal power.', 0, NULL, 'power'),
84
+ ('GENERATOR', 'status', 'Status', 'float', 'n/a', '1', 'timeseries', FALSE, FALSE, 'Status (1 is on, 0 is off). Only outputted if committable is True.', NULL, NULL, 'control'),
85
+ ('GENERATOR', 'mu_upper', 'Shadow Price Upper', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper p_nom limit', NULL, NULL, 'economics'),
86
+ ('GENERATOR', 'mu_lower', 'Shadow Price Lower', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower p_nom limit', NULL, NULL, 'economics'),
87
+ ('GENERATOR', 'mu_p_set', 'Shadow Price P Set', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of fixed power generation p_set', NULL, NULL, 'economics'),
88
+ ('GENERATOR', 'mu_ramp_limit_up', 'Shadow Price Ramp Up', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper ramp up limit', NULL, NULL, 'economics'),
89
+ ('GENERATOR', 'mu_ramp_limit_down', 'Shadow Price Ramp Down', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower ramp down limit', NULL, NULL, 'economics');
90
+
91
+ -- ============================================================================
92
+ -- LOAD ATTRIBUTES
93
+ -- ============================================================================
94
+
95
+ -- LOAD attributes from loads.csv (PyPSA reference) - Updated to simplified groups
96
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
97
+ -- Input attributes for LOAD
98
+ ('LOAD', 'carrier', 'Energy Carrier', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Energy carrier: can be "AC" or "DC" for electrical buses, or "heat" or "gas".', NULL, NULL, 'basic'),
99
+ ('LOAD', 'type', 'Load Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for load type. Not yet implemented.', NULL, NULL, 'basic'),
100
+ ('LOAD', 'p_set', 'Active Power Demand', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, 'Active power consumption (positive if the load is consuming power).', NULL, NULL, 'power'),
101
+ ('LOAD', 'q_set', 'Reactive Power Demand', 'float', 'MVar', '0', 'static_or_timeseries', FALSE, TRUE, 'Reactive power consumption (positive if the load is inductive).', NULL, NULL, 'power'),
102
+ ('LOAD', 'sign', 'Power Sign', 'float', 'n/a', '-1', 'static', FALSE, TRUE, 'power sign (opposite sign to generator)', NULL, NULL, 'power'),
103
+ ('LOAD', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether to consider the component in common functionalities or not', NULL, NULL, 'basic'),
104
+ -- Output attributes for LOAD (PyPSA load outputs)
105
+ ('LOAD', 'p', 'Active Power', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power consumption (positive if consuming)', NULL, NULL, 'power'),
106
+ ('LOAD', 'q', 'Reactive Power', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power consumption (positive if consuming)', NULL, NULL, 'power');
107
+
108
+ -- ============================================================================
109
+ -- LINE ATTRIBUTES
110
+ -- ============================================================================
111
+
112
+ -- LINE attributes from lines.csv (PyPSA reference) - Updated to simplified groups
113
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
114
+ -- Input attributes for LINE
115
+ ('LINE', 'type', 'Line Type', 'string', 'n/a', '', 'static', FALSE, TRUE, 'Name of line standard type. If this is not an empty string "", then the line standard type impedance parameters are multiplied with the line length and divided/multiplied by num_parallel to compute x, r, etc. This will override any values set in r, x, and b. If the string is empty, PyPSA will simply read r, x, etc.', NULL, NULL, 'basic'),
116
+ ('LINE', 'x', 'Series Reactance', 'float', 'Ohm', '0', 'static', FALSE, TRUE, 'Series reactance, must be non-zero for AC branch in linear power flow. If the line has series inductance L in Henries then x = 2πfL where f is the frequency in Hertz. Series impedance z = r + jx must be non-zero for the non-linear power flow. Ignored if type defined.', 0, NULL, 'power'),
117
+ ('LINE', 'r', 'Series Resistance', 'float', 'Ohm', '0', 'static', FALSE, TRUE, 'Series resistance, must be non-zero for DC branch in linear power flow. Series impedance z = r + jx must be non-zero for the non-linear power flow. Ignored if type defined.', 0, NULL, 'power'),
118
+ ('LINE', 'g', 'Shunt Conductance', 'float', 'Siemens', '0', 'static', FALSE, TRUE, 'Shunt conductivity. Shunt admittance is y = g + jb.', 0, NULL, 'power'),
119
+ ('LINE', 'b', 'Shunt Susceptance', 'float', 'Siemens', '0', 'static', FALSE, TRUE, 'Shunt susceptance. If the line has shunt capacitance C in Farads then b = 2πfC where f is the frequency in Hertz. Shunt admittance is y = g + jb. Ignored if type defined.', NULL, NULL, 'power'),
120
+ ('LINE', 's_nom', 'Nominal Apparent Power', 'float', 'MVA', '0', 'static', FALSE, TRUE, 'Limit of apparent power which can pass through branch.', 0, NULL, 'power'),
121
+ ('LINE', 's_nom_mod', 'Nominal Power Module', 'float', 'MVA', '0', 'static', FALSE, TRUE, 'Unit size of line expansion of s_nom.', 0, NULL, 'power'),
122
+ ('LINE', 's_nom_extendable', 'Extendable Capacity', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow capacity s_nom to be extended in OPF.', NULL, NULL, 'power'),
123
+ ('LINE', 's_nom_min', 'Min Nominal Power', 'float', 'MVA', '0', 'static', FALSE, TRUE, 'If s_nom is extendable in OPF, set its minimum value.', 0, NULL, 'power'),
124
+ ('LINE', 's_nom_max', 'Max Nominal Power', 'float', 'MVA', 'inf', 'static', FALSE, TRUE, 'If s_nom is extendable in OPF, set its maximum value (e.g. limited by potential).', 0, NULL, 'power'),
125
+ ('LINE', 's_max_pu', 'Max Power Factor', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'The maximum allowed absolute flow per unit of s_nom for the OPF (e.g. can be set <1 to approximate n-1 factor, or can be time-varying to represent weather-dependent dynamic line rating for overhead lines).', 0, 1, 'power'),
126
+ ('LINE', 'capital_cost', 'Capital Cost', 'float', 'currency/MVA', '0', 'static', FALSE, TRUE, 'Fixed period costs of extending s_nom by 1 MVA, including periodized investment costs and periodic fixed O&M costs (e.g. annuitized investment costs).', 0, NULL, 'economics'),
127
+ ('LINE', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether to consider the component in basic functionality or not', NULL, NULL, 'basic'),
128
+ ('LINE', 'build_year', 'Build Year', 'int', 'year', '0', 'static', FALSE, TRUE, 'build year', 0, 3000, 'economics'),
129
+ ('LINE', 'lifetime', 'Lifetime', 'float', 'years', 'inf', 'static', FALSE, TRUE, 'lifetime', 0, NULL, 'economics'),
130
+ ('LINE', 'length', 'Line Length', 'float', 'km', '0', 'static', FALSE, TRUE, 'Length of line used when "type" is set, also useful for calculating the capital cost.', 0, NULL, 'power'),
131
+ ('LINE', 'carrier', 'Energy Carrier', 'string', 'n/a', '', 'static', FALSE, TRUE, 'Type of current, "AC" is the only valid value', NULL, NULL, 'basic'),
132
+ ('LINE', 'terrain_factor', 'Terrain Factor', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Terrain factor for increasing capital cost.', 0, NULL, 'power'),
133
+ ('LINE', 'num_parallel', 'Number of Parallel Lines', 'float', 'n/a', '1', 'static', FALSE, TRUE, 'When "type" is set, this is the number of parallel lines (can also be fractional). If "type" is empty "" this value is ignored.', 1, NULL, 'power'),
134
+ ('LINE', 'v_ang_min', 'Min Voltage Angle Diff', 'float', 'Degrees', '-inf', 'static', FALSE, TRUE, 'Minimum voltage angle difference across the line. This is a placeholder attribute and is not currently used by any PyPSA functions.', NULL, NULL, 'power'),
135
+ ('LINE', 'v_ang_max', 'Max Voltage Angle Diff', 'float', 'Degrees', 'inf', 'static', FALSE, TRUE, 'Maximum voltage angle difference across the line. This is a placeholder attribute and is not currently used by any PyPSA functions.', NULL, NULL, 'power'),
136
+ -- Output attributes for LINE (PyPSA line outputs)
137
+ ('LINE', 'p0', 'Active Power Bus0', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus0 (positive if power flows from bus0 to bus1)', NULL, NULL, 'power'),
138
+ ('LINE', 'p1', 'Active Power Bus1', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus1 (positive if power flows from bus1 to bus0)', NULL, NULL, 'power'),
139
+ ('LINE', 'q0', 'Reactive Power Bus0', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power at bus0', NULL, NULL, 'power'),
140
+ ('LINE', 'q1', 'Reactive Power Bus1', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power at bus1', NULL, NULL, 'power'),
141
+ ('LINE', 's_nom_opt', 'Optimised Apparent Power', 'float', 'MVA', '0', 'static', FALSE, FALSE, 'Optimised apparent power limit.', 0, NULL, 'power'),
142
+ ('LINE', 'mu_upper', 'Shadow Price Upper', 'float', 'currency/MVA', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper s_nom limit', NULL, NULL, 'economics'),
143
+ ('LINE', 'mu_lower', 'Shadow Price Lower', 'float', 'currency/MVA', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower s_nom limit', NULL, NULL, 'economics'),
144
+ ('LINE', 'sub_network', 'Sub-Network', 'string', 'n/a', 'n/a', 'static', FALSE, FALSE, 'Name of connected sub-network to which line belongs. This attribute is set by PyPSA.', NULL, NULL, 'other'),
145
+ ('LINE', 'x_pu', 'Per Unit Reactance', 'float', 'per unit', '0', 'static', FALSE, FALSE, 'Per unit series reactance calculated by PyPSA from x and bus.v_nom', NULL, NULL, 'power'),
146
+ ('LINE', 'r_pu', 'Per Unit Resistance', 'float', 'per unit', '0', 'static', FALSE, FALSE, 'Per unit series resistance calculated by PyPSA from r and bus.v_nom', NULL, NULL, 'power'),
147
+ ('LINE', 'g_pu', 'Per Unit Conductance', 'float', 'per unit', '0', 'static', FALSE, FALSE, 'Per unit shunt conductivity calculated by PyPSA from g and bus.v_nom', NULL, NULL, 'power'),
148
+ ('LINE', 'b_pu', 'Per Unit Susceptance', 'float', 'per unit', '0', 'static', FALSE, FALSE, 'Per unit shunt susceptance calculated by PyPSA from b and bus.v_nom', NULL, NULL, 'power'),
149
+ ('LINE', 'x_pu_eff', 'Effective Per Unit Reactance', 'float', 'per unit', '0', 'static', FALSE, FALSE, 'Effective per unit series reactance for linear power flow', NULL, NULL, 'power'),
150
+ ('LINE', 'r_pu_eff', 'Effective Per Unit Resistance', 'float', 'per unit', '0', 'static', FALSE, FALSE, 'Effective per unit series resistance for linear power flow', NULL, NULL, 'power');
151
+
152
+ -- ============================================================================
153
+ -- LINK ATTRIBUTES
154
+ -- ============================================================================
155
+
156
+ -- LINK attributes from links.csv (PyPSA reference) - Updated to simplified groups
157
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
158
+ -- Input attributes for LINK
159
+ ('LINK', 'type', 'Link Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for link type. Not yet implemented.', NULL, NULL, 'basic'),
160
+ ('LINK', 'carrier', 'Energy Carrier', 'string', 'n/a', '', 'static', FALSE, TRUE, 'Energy carrier transported by the link: can be "DC" for electrical HVDC links, or "heat" or "gas" etc.', NULL, NULL, 'basic'),
161
+ ('LINK', 'efficiency', 'Transfer Efficiency', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Efficiency of power transfer from bus0 to bus1. (Can be time-dependent to represent temperature-dependent Coefficient of Performance of a heat pump from an electric to a heat bus.)', 0, 1, 'power'),
162
+ ('LINK', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether to consider the component in basic functionality or not', NULL, NULL, 'basic'),
163
+ ('LINK', 'build_year', 'Build Year', 'int', 'year', '0', 'static', FALSE, TRUE, 'build year', 0, 3000, 'economics'),
164
+ ('LINK', 'lifetime', 'Lifetime', 'float', 'years', 'inf', 'static', FALSE, TRUE, 'lifetime', 0, NULL, 'economics'),
165
+ ('LINK', 'p_nom', 'Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'Limit of active power which can pass through link.', 0, NULL, 'power'),
166
+ ('LINK', 'p_nom_mod', 'Nominal Power Module', 'float', 'MW', '0', 'static', FALSE, TRUE, 'Limit of active power of the link module.', 0, NULL, 'power'),
167
+ ('LINK', 'p_nom_extendable', 'Extendable Capacity', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow capacity p_nom to be extended.', NULL, NULL, 'power'),
168
+ ('LINK', 'p_nom_min', 'Min Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'If p_nom is extendable, set its minimum value.', 0, NULL, 'power'),
169
+ ('LINK', 'p_nom_max', 'Max Nominal Power', 'float', 'MW', 'inf', 'static', FALSE, TRUE, 'If p_nom is extendable, set its maximum value (e.g. limited by potential).', 0, NULL, 'power'),
170
+ ('LINK', 'p_set', 'Power Setpoint', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, 'The dispatch set point for p0 of the link in PF.', NULL, NULL, 'power'),
171
+ ('LINK', 'p_min_pu', 'Min Capacity Factor', 'float', 'per unit of p_nom', '0', 'static_or_timeseries', FALSE, TRUE, 'Minimal dispatch (can also be negative) per unit of p_nom for the link.', NULL, NULL, 'power'),
172
+ ('LINK', 'p_max_pu', 'Max Capacity Factor', 'float', 'per unit of p_nom', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximal dispatch (can also be negative) per unit of p_nom for the link.', NULL, NULL, 'power'),
173
+ ('LINK', 'capital_cost', 'Capital Cost', 'float', 'currency/MW', '0', 'static', FALSE, TRUE, 'Fixed period costs of extending p_nom by 1 MW, including periodized investment costs and periodic fixed O&M costs (e.g. annuitized investment costs).', 0, NULL, 'economics'),
174
+ ('LINK', 'marginal_cost', 'Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost of transfering 1 MWh (before efficiency losses) from bus0 to bus1. NB: marginal cost only makes sense if p_max_pu >= 0.', 0, NULL, 'economics'),
175
+ ('LINK', 'marginal_cost_quadratic', 'Quadratic Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Quadratic marginal cost for transferring 1 MWh (before efficiency losses) from bus0 to bus1.', 0, NULL, 'economics'),
176
+ ('LINK', 'stand_by_cost', 'Stand-by Cost', 'float', 'currency/h', '0', 'static_or_timeseries', FALSE, TRUE, 'Stand-by cost for operating the link at null power flow.', 0, NULL, 'control'),
177
+ ('LINK', 'length', 'Link Length', 'float', 'km', '0', 'static', FALSE, TRUE, 'Length of line, useful for calculating the capital cost.', 0, NULL, 'power'),
178
+ ('LINK', 'terrain_factor', 'Terrain Factor', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Terrain factor for increasing capital cost.', 0, NULL, 'power'),
179
+ ('LINK', 'committable', 'Committable', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Use unit commitment (only possible if p_nom is not extendable).', NULL, NULL, 'control'),
180
+ ('LINK', 'start_up_cost', 'Start-up Cost', 'float', 'currency', '0', 'static', FALSE, TRUE, 'Cost to start up the link. Only read if committable is True.', 0, NULL, 'control'),
181
+ ('LINK', 'shut_down_cost', 'Shutdown Cost', 'float', 'currency', '0', 'static', FALSE, TRUE, 'Cost to shut down the link. Only read if committable is True.', 0, NULL, 'control'),
182
+ ('LINK', 'min_up_time', 'Min Up Time', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Minimum number of snapshots for status to be 1. Only read if committable is True.', 0, NULL, 'control'),
183
+ ('LINK', 'min_down_time', 'Min Down Time', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Minimum number of snapshots for status to be 0. Only read if committable is True.', 0, NULL, 'control'),
184
+ ('LINK', 'up_time_before', 'Up Time Before', 'int', 'snapshots', '1', 'static', FALSE, TRUE, 'Number of snapshots that the link was online before network.snapshots start. Only read if committable is True and min_up_time is non-zero.', 0, NULL, 'control'),
185
+ ('LINK', 'down_time_before', 'Down Time Before', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Number of snapshots that the link was offline before network.snapshots start. Only read if committable is True and min_down_time is non-zero.', 0, NULL, 'control'),
186
+ ('LINK', 'ramp_limit_up', 'Ramp Up Limit', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximum increase from one snapshot to the next, per unit of the bus0 unit. Ignored if 1.', NULL, NULL, 'control'),
187
+ ('LINK', 'ramp_limit_down', 'Ramp Down Limit', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximum decrease from one snapshot to the next, per unit of the bus0 unit. Ignored if 1.', NULL, NULL, 'control'),
188
+ ('LINK', 'ramp_limit_start_up', 'Ramp Up at Start', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Maximumincrease at start up, per unit of bus0 unit. Only read if committable is True.', NULL, NULL, 'control'),
189
+ -- Output attributes for LINK (PyPSA link outputs)
190
+ ('LINK', 'p0', 'Active Power Bus0', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus0 (positive if power flows from bus0 to bus1)', NULL, NULL, 'power'),
191
+ ('LINK', 'p1', 'Active Power Bus1', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus1 (positive if power flows from bus1 to bus0)', NULL, NULL, 'power'),
192
+ ('LINK', 'p_nom_opt', 'Optimised Nominal Power', 'float', 'MW', '0', 'static', FALSE, FALSE, 'Optimised nominal power.', 0, NULL, 'power'),
193
+ ('LINK', 'status', 'Status', 'float', 'n/a', '1', 'timeseries', FALSE, FALSE, 'Status (1 is on, 0 is off). Only outputted if committable is True.', NULL, NULL, 'control'),
194
+ ('LINK', 'mu_upper', 'Shadow Price Upper', 'float', 'currency/MW', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper p_nom limit', NULL, NULL, 'economics'),
195
+ ('LINK', 'mu_lower', 'Shadow Price Lower', 'float', 'currency/MW', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower p_nom limit', NULL, NULL, 'economics'),
196
+ ('LINK', 'mu_p_set', 'Shadow Price P Set', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of fixed power transmission p_set', NULL, NULL, 'economics'),
197
+ ('LINK', 'mu_ramp_limit_up', 'Shadow Price Ramp Up', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper ramp up limit', NULL, NULL, 'economics'),
198
+ ('LINK', 'mu_ramp_limit_down', 'Shadow Price Ramp Down', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower ramp down limit', NULL, NULL, 'economics');
199
+
200
+ -- ============================================================================
201
+ -- UNMET_LOAD ATTRIBUTES
202
+ -- ============================================================================
203
+
204
+ -- UNMET_LOAD attributes - same as GENERATOR but with specific defaults and restrictions - Updated to simplified groups
205
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
206
+ -- Input attributes for UNMET_LOAD (same as GENERATOR)
207
+ ('UNMET_LOAD', 'control', 'Control Strategy', 'string', 'n/a', 'PQ', 'static', FALSE, TRUE, 'P,Q,V control strategy for PF, must be "PQ", "PV" or "Slack".', NULL, NULL, 'control'),
208
+ ('UNMET_LOAD', 'type', 'Unmet Load Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for generator type. Not yet implemented.', NULL, NULL, 'basic'),
209
+ ('UNMET_LOAD', 'p_nom', 'Nominal Power', 'float', 'MW', '10000000', 'static', FALSE, TRUE, 'Nominal power for limits in OPF. Set very high for unmet load.', 0, NULL, 'power'),
210
+ ('UNMET_LOAD', 'p_nom_extendable', 'Extendable Capacity', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow capacity p_nom to be extended in OPF.', NULL, NULL, 'power'),
211
+ ('UNMET_LOAD', 'p_nom_min', 'Min Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'If p_nom is extendable in OPF, set its minimum value.', 0, NULL, 'power'),
212
+ ('UNMET_LOAD', 'p_nom_max', 'Max Nominal Power', 'float', 'MW', 'inf', 'static', FALSE, TRUE, 'If p_nom is extendable in OPF, set its maximum value (e.g. limited by potential).', 0, NULL, 'power'),
213
+ ('UNMET_LOAD', 'p_min_pu', 'Min Capacity Factor', 'float', 'per unit', '0', 'static_or_timeseries', FALSE, TRUE, 'The minimum output for each snapshot per unit of p_nom for the OPF.', 0, 1, 'power'),
214
+ ('UNMET_LOAD', 'p_max_pu', 'Max Capacity Factor', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'The maximum output for each snapshot per unit of p_nom for the OPF.', 0, 1, 'power'),
215
+ ('UNMET_LOAD', 'p_set', 'Active Power Setpoint', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, 'active power set point (for PF)', NULL, NULL, 'power'),
216
+ ('UNMET_LOAD', 'q_set', 'Reactive Power Setpoint', 'float', 'MVar', '0', 'static_or_timeseries', FALSE, TRUE, 'reactive power set point (for PF)', NULL, NULL, 'power'),
217
+ ('UNMET_LOAD', 'sign', 'Power Sign', 'float', 'n/a', '1', 'static', FALSE, TRUE, 'power sign', NULL, NULL, 'power'),
218
+ ('UNMET_LOAD', 'carrier', 'Energy Carrier', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Prime mover energy carrier (e.g. coal, gas, wind, solar); required for global constraints on primary energy in OPF', NULL, NULL, 'basic'),
219
+ ('UNMET_LOAD', 'marginal_cost', 'Marginal Cost (Penalty)', 'float', 'currency/MWh', '100000000', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost of production of 1 MWh. Set very high for unmet load penalty.', 0, NULL, 'economics'),
220
+ ('UNMET_LOAD', 'build_year', 'Build Year', 'int', 'year', '0', 'static', FALSE, TRUE, 'Year when generator can be built in the optimize branch.', NULL, NULL, 'economics'),
221
+ ('UNMET_LOAD', 'lifetime', 'Lifetime', 'float', 'years', 'inf', 'static', FALSE, TRUE, 'Expected lifetime in years.', 0, NULL, 'economics'),
222
+ ('UNMET_LOAD', 'capital_cost', 'Capital Cost', 'float', 'currency/MW', '0', 'static_or_timeseries', FALSE, TRUE, 'Capital cost of extending p_nom by 1 MW.', 0, NULL, 'economics'),
223
+ ('UNMET_LOAD', 'efficiency', 'Efficiency', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Ratio between primary energy and electrical energy. Use as energy consumption if negative.', NULL, NULL, 'power'),
224
+ ('UNMET_LOAD', 'committable', 'Committable', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow the unit to be turned on or off with start up costs.', NULL, NULL, 'control'),
225
+ ('UNMET_LOAD', 'start_up_cost', 'Start-up Cost', 'float', 'currency', '0', 'static', FALSE, TRUE, 'Start up cost when unit goes from offline to online status.', 0, NULL, 'control'),
226
+ ('UNMET_LOAD', 'shut_down_cost', 'Shutdown Cost', 'float', 'currency', '0', 'static', FALSE, TRUE, 'Shut down cost when unit goes from online to offline status.', 0, NULL, 'control'),
227
+ ('UNMET_LOAD', 'min_up_time', 'Min Up Time', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Minimum number of snapshots for status to be 1. Only read if committable is True.', 0, NULL, 'control'),
228
+ ('UNMET_LOAD', 'min_down_time', 'Min Down Time', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Minimum number of snapshots for status to be 0. Only read if committable is True.', 0, NULL, 'control'),
229
+ ('UNMET_LOAD', 'up_time_before', 'Up Time Before', 'int', 'snapshots', '1', 'static', FALSE, TRUE, 'Number of snapshots that the generator was online before network.snapshots start. Only read if committable is True and min_up_time is non-zero.', 0, NULL, 'control'),
230
+ ('UNMET_LOAD', 'down_time_before', 'Down Time Before', 'int', 'snapshots', '0', 'static', FALSE, TRUE, 'Number of snapshots that the generator was offline before network.snapshots start. Only read if committable is True and min_down_time is non-zero.', 0, NULL, 'control'),
231
+ ('UNMET_LOAD', 'ramp_limit_up', 'Ramp Up Limit', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximum increase from one snapshot to the next, per unit of p_nom. Ignored if 1.', NULL, NULL, 'control'),
232
+ ('UNMET_LOAD', 'ramp_limit_down', 'Ramp Down Limit', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximum decrease from one snapshot to the next, per unit of p_nom. Ignored if 1.', NULL, NULL, 'control'),
233
+ ('UNMET_LOAD', 'ramp_limit_start_up', 'Ramp Up at Start', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Maximum increase at start up, per unit of p_nom. Only read if committable is True.', NULL, NULL, 'control'),
234
+ ('UNMET_LOAD', 'ramp_limit_shut_down', 'Ramp Down at Shutdown', 'float', 'per unit', '1', 'static', FALSE, TRUE, 'Maximum decrease at shut down, per unit of p_nom. Only read if committable is True.', NULL, NULL, 'control'),
235
+ ('UNMET_LOAD', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Switch to enable/disable this unmet load component.', NULL, NULL, 'basic'),
236
+ -- Output attributes for UNMET_LOAD (same as GENERATOR since PyPSA treats them as generators)
237
+ ('UNMET_LOAD', 'p', 'Active Power', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus (positive if net generation)', NULL, NULL, 'power'),
238
+ ('UNMET_LOAD', 'q', 'Reactive Power', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power (positive if net generation)', NULL, NULL, 'power'),
239
+ ('UNMET_LOAD', 'p_nom_opt', 'Optimised Nominal Power', 'float', 'MW', '0', 'static', FALSE, FALSE, 'Optimised nominal power.', 0, NULL, 'power'),
240
+ ('UNMET_LOAD', 'status', 'Status', 'float', 'n/a', '1', 'timeseries', FALSE, FALSE, 'Status (1 is on, 0 is off). Only outputted if committable is True.', NULL, NULL, 'control'),
241
+ ('UNMET_LOAD', 'mu_upper', 'Shadow Price Upper', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper p_nom limit', NULL, NULL, 'economics'),
242
+ ('UNMET_LOAD', 'mu_lower', 'Shadow Price Lower', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower p_nom limit', NULL, NULL, 'economics'),
243
+ ('UNMET_LOAD', 'mu_p_set', 'Shadow Price P Set', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of fixed power generation p_set', NULL, NULL, 'economics'),
244
+ ('UNMET_LOAD', 'mu_ramp_limit_up', 'Shadow Price Ramp Up', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper ramp up limit', NULL, NULL, 'economics'),
245
+ ('UNMET_LOAD', 'mu_ramp_limit_down', 'Shadow Price Ramp Down', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower ramp down limit', NULL, NULL, 'economics');
246
+
247
+ -- ============================================================================
248
+ -- STORAGE_UNIT ATTRIBUTES
249
+ -- ============================================================================
250
+
251
+ -- STORAGE_UNIT attributes from storage_units.csv (PyPSA reference) - Updated to simplified groups
252
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
253
+ -- Input attributes for STORAGE_UNIT
254
+ ('STORAGE_UNIT', 'control', 'Control Strategy', 'string', 'n/a', 'PQ', 'static', FALSE, TRUE, 'P,Q,V control strategy for PF, must be "PQ", "PV" or "Slack".', NULL, NULL, 'control'),
255
+ ('STORAGE_UNIT', 'type', 'Storage Unit Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for storage unit type. Not yet implemented.', NULL, NULL, 'basic'),
256
+ ('STORAGE_UNIT', 'p_nom', 'Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'Nominal power for limits in OPF.', 0, NULL, 'power'),
257
+ ('STORAGE_UNIT', 'p_nom_mod', 'Nominal Power Module', 'float', 'MW', '0', 'static', FALSE, TRUE, 'Nominal power of the storage unit module.', 0, NULL, 'power'),
258
+ ('STORAGE_UNIT', 'p_nom_extendable', 'Extendable Capacity', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow capacity p_nom to be extended in OPF.', NULL, NULL, 'power'),
259
+ ('STORAGE_UNIT', 'p_nom_min', 'Min Nominal Power', 'float', 'MW', '0', 'static', FALSE, TRUE, 'If p_nom is extendable in OPF, set its minimum value.', 0, NULL, 'power'),
260
+ ('STORAGE_UNIT', 'p_nom_max', 'Max Nominal Power', 'float', 'MW', 'inf', 'static', FALSE, TRUE, 'If p_nom is extendable in OPF, set its maximum value (e.g. limited by potential).', 0, NULL, 'power'),
261
+ ('STORAGE_UNIT', 'p_min_pu', 'Min Capacity Factor', 'float', 'per unit', '-1', 'static_or_timeseries', FALSE, TRUE, 'The minimum output for each snapshot per unit of p_nom for the OPF (negative sign implies storing mode withdrawing power from bus).', -1, 1, 'power'),
262
+ ('STORAGE_UNIT', 'p_max_pu', 'Max Capacity Factor', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'The maximum output for each snapshot per unit of p_nom for the OPF.', -1, 1, 'power'),
263
+ ('STORAGE_UNIT', 'p_set', 'Active Power Setpoint', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, 'active power set point (for PF)', NULL, NULL, 'power'),
264
+ ('STORAGE_UNIT', 'q_set', 'Reactive Power Setpoint', 'float', 'MVar', '0', 'static_or_timeseries', FALSE, TRUE, 'reactive power set point (for PF)', NULL, NULL, 'power'),
265
+ ('STORAGE_UNIT', 'sign', 'Power Sign', 'float', 'n/a', '1', 'static', FALSE, TRUE, 'power sign', NULL, NULL, 'power'),
266
+ ('STORAGE_UNIT', 'carrier', 'Energy Carrier', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Prime mover energy carrier (e.g. coal, gas, wind, solar); required for global constraints on primary energy in OPF', NULL, NULL, 'basic'),
267
+ ('STORAGE_UNIT', 'spill_cost', 'Spill Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Cost of spilling 1 MWh.', 0, NULL, 'economics'),
268
+ ('STORAGE_UNIT', 'marginal_cost', 'Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost of production of 1 MWh.', 0, NULL, 'economics'),
269
+ ('STORAGE_UNIT', 'marginal_cost_quadratic', 'Quadratic Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Quadratic marginal cost of production (discharge) of 1 MWh.', 0, NULL, 'economics'),
270
+ ('STORAGE_UNIT', 'marginal_cost_storage', 'Storage Marginal Cost', 'float', 'currency/MWh/h', '0', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost of energy storage of 1 MWh for one hour.', 0, NULL, 'economics'),
271
+ ('STORAGE_UNIT', 'capital_cost', 'Capital Cost', 'float', 'currency/MW', '0', 'static', FALSE, TRUE, 'Fixed period costs of extending p_nom by 1 MW, including periodized investment costs and periodic fixed O&M costs (e.g. annuitized investment costs).', 0, NULL, 'economics'),
272
+ ('STORAGE_UNIT', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether to consider the component in basic functionality or not', NULL, NULL, 'basic'),
273
+ ('STORAGE_UNIT', 'build_year', 'Build Year', 'int', 'year', '0', 'static', FALSE, TRUE, 'build year', 0, 3000, 'economics'),
274
+ ('STORAGE_UNIT', 'lifetime', 'Lifetime', 'float', 'years', 'inf', 'static', FALSE, TRUE, 'lifetime', 0, NULL, 'economics'),
275
+ ('STORAGE_UNIT', 'state_of_charge_initial', 'Initial State of Charge', 'float', 'MWh', '0', 'static', FALSE, TRUE, 'State of charge before the snapshots in the OPF.', 0, NULL, 'power'),
276
+ ('STORAGE_UNIT', 'state_of_charge_initial_per_period', 'Initial SOC per Period', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch: if True, then state of charge at the beginning of an investment period is set to state_of_charge_initial', NULL, NULL, 'power'),
277
+ ('STORAGE_UNIT', 'state_of_charge_set', 'State of Charge Setpoint', 'float', 'MWh', '1', 'static_or_timeseries', FALSE, TRUE, 'State of charge set points for snapshots in the OPF.', NULL, NULL, 'power'),
278
+ ('STORAGE_UNIT', 'cyclic_state_of_charge', 'Cyclic State of Charge', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch: if True, then state_of_charge_initial is ignored and the initial state of charge is set to the final state of charge for the group of snapshots in the OPF (soc[-1] = soc[len(snapshots)-1]).', NULL, NULL, 'power'),
279
+ ('STORAGE_UNIT', 'cyclic_state_of_charge_per_period', 'Cyclic SOC per Period', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Switch: if True, then the cyclic constraints are applied to each period (first snapshot level if multiindexed) separately.', NULL, NULL, 'power'),
280
+ ('STORAGE_UNIT', 'max_hours', 'Max Storage Hours', 'float', 'hours', '1', 'static', FALSE, TRUE, 'Maximum state of charge capacity in terms of hours at full output capacity p_nom', 0, NULL, 'power'),
281
+ ('STORAGE_UNIT', 'efficiency_store', 'Storage Efficiency', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Efficiency of storage on the way into the storage.', 0, 1, 'power'),
282
+ ('STORAGE_UNIT', 'efficiency_dispatch', 'Dispatch Efficiency', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Efficiency of storage on the way out of the storage.', 0, 1, 'power'),
283
+ ('STORAGE_UNIT', 'standing_loss', 'Standing Loss', 'float', 'per unit', '0', 'static_or_timeseries', FALSE, TRUE, 'Losses per hour to state of charge.', 0, 1, 'power'),
284
+ ('STORAGE_UNIT', '1low', 'Inflow', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, '1low to the state of charge, e.g. due to river 1low in hydro reservoir.', NULL, NULL, 'power'),
285
+ -- Output attributes for STORAGE_UNIT (PyPSA storage unit outputs)
286
+ ('STORAGE_UNIT', 'p', 'Active Power', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus (positive if discharging)', NULL, NULL, 'power'),
287
+ ('STORAGE_UNIT', 'q', 'Reactive Power', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power at bus', NULL, NULL, 'power'),
288
+ ('STORAGE_UNIT', 'state_of_charge', 'State of Charge', 'float', 'MWh', '0', 'timeseries', FALSE, FALSE, 'State of charge of storage unit', 0, NULL, 'power'),
289
+ ('STORAGE_UNIT', 'p_nom_opt', 'Optimised Nominal Power', 'float', 'MW', '0', 'static', FALSE, FALSE, 'Optimised nominal power.', 0, NULL, 'power'),
290
+ ('STORAGE_UNIT', 'spill', 'Spill', 'float', 'MWh', '0', 'timeseries', FALSE, FALSE, 'Spillage of storage unit', 0, NULL, 'power'),
291
+ ('STORAGE_UNIT', 'status', 'Status', 'float', 'n/a', '1', 'timeseries', FALSE, FALSE, 'Status (1 is on, 0 is off). Only outputted if committable is True.', NULL, NULL, 'control'),
292
+ ('STORAGE_UNIT', 'mu_upper', 'Shadow Price Upper', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper p_nom limit', NULL, NULL, 'economics'),
293
+ ('STORAGE_UNIT', 'mu_lower', 'Shadow Price Lower', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower p_nom limit', NULL, NULL, 'economics'),
294
+ ('STORAGE_UNIT', 'p_dispatch', 'Active Power Dispatch', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power dispatch at bus', NULL, NULL, 'power'),
295
+ ('STORAGE_UNIT', 'p_store', 'Active Power Charging', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power charging at bus', NULL, NULL, 'power'),
296
+ ('STORAGE_UNIT', 'mu_state_of_charge_set', 'Shadow Price SOC Set', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of fixed state of charge state_of_charge_set', NULL, NULL, 'economics'),
297
+ ('STORAGE_UNIT', 'mu_energy_balance', 'Shadow Price Energy Balance', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of storage consistency equations', NULL, NULL, 'economics');
298
+
299
+ -- ============================================================================
300
+ -- STORE ATTRIBUTES
301
+ -- ============================================================================
302
+
303
+ -- STORE attributes from stores.csv (PyPSA reference) - Updated to simplified groups
304
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
305
+ -- Input attributes for STORE
306
+ ('STORE', 'type', 'Store Type', 'string', 'n/a', 'n/a', 'static', FALSE, TRUE, 'Placeholder for store type. Not yet implemented.', NULL, NULL, 'basic'),
307
+ ('STORE', 'carrier', 'Energy Carrier', 'string', 'n/a', '', 'static', FALSE, TRUE, 'Energy carrier of the Store, e.g. "heat" or "gas".', NULL, NULL, 'basic'),
308
+ ('STORE', 'e_nom', 'Nominal Energy Capacity', 'float', 'MWh', '0', 'static', FALSE, TRUE, 'Nominal energy capacity.', 0, NULL, 'power'),
309
+ ('STORE', 'e_nom_mod', 'Nominal Energy Module', 'float', 'MWh', '0', 'static', FALSE, TRUE, 'Nominal energy capacity of the store module.', 0, NULL, 'power'),
310
+ ('STORE', 'e_nom_extendable', 'Extendable Energy Capacity', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch to allow capacity e_nom to be extended in OPF.', NULL, NULL, 'power'),
311
+ ('STORE', 'e_nom_min', 'Min Energy Capacity', 'float', 'MWh', '0', 'static', FALSE, TRUE, 'If e_nom is extendable in OPF, set its minimum value.', 0, NULL, 'power'),
312
+ ('STORE', 'e_nom_max', 'Max Energy Capacity', 'float', 'MWh', 'inf', 'static', FALSE, TRUE, 'If e_nom is extendable in OPF, set its maximum value (e.g. limited by technical potential).', 0, NULL, 'power'),
313
+ ('STORE', 'e_min_pu', 'Min Energy Factor', 'float', 'per unit', '0', 'static_or_timeseries', FALSE, TRUE, 'Minimal value of e relative to e_nom for the OPF.', 0, 1, 'power'),
314
+ ('STORE', 'e_max_pu', 'Max Energy Factor', 'float', 'per unit', '1', 'static_or_timeseries', FALSE, TRUE, 'Maximal value of e relative to e_nom for the OPF.', 0, 1, 'power'),
315
+ ('STORE', 'e_initial', 'Initial Energy', 'float', 'MWh', '0', 'static', FALSE, TRUE, 'Energy before the snapshots in the OPF.', 0, NULL, 'power'),
316
+ ('STORE', 'e_initial_per_period', 'Initial Energy per Period', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch: if True, then at the beginning of each investment period e is set to e_initial', NULL, NULL, 'power'),
317
+ ('STORE', 'e_cyclic', 'Cyclic Energy', 'boolean', 'n/a', 'False', 'static', FALSE, TRUE, 'Switch: if True, then e_initial is ignored and the initial energy is set to the final energy for the group of snapshots in the OPF.', NULL, NULL, 'power'),
318
+ ('STORE', 'e_cyclic_per_period', 'Cyclic Energy per Period', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Switch: if True, then the cyclic constraints are applied to each period (first snapshot level if multiindexed) separately.', NULL, NULL, 'power'),
319
+ ('STORE', 'p_set', 'Active Power Setpoint', 'float', 'MW', '0', 'static_or_timeseries', FALSE, TRUE, 'active power set point (for PF)', NULL, NULL, 'power'),
320
+ ('STORE', 'q_set', 'Reactive Power Setpoint', 'float', 'MVar', '0', 'static_or_timeseries', FALSE, TRUE, 'reactive power set point (for PF)', NULL, NULL, 'power'),
321
+ ('STORE', 'sign', 'Power Sign', 'float', 'n/a', '1', 'static', FALSE, TRUE, 'power sign', NULL, NULL, 'power'),
322
+ ('STORE', 'marginal_cost', 'Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost applied to both charging and discharging of 1 MWh.', 0, NULL, 'economics'),
323
+ ('STORE', 'marginal_cost_quadratic', 'Quadratic Marginal Cost', 'float', 'currency/MWh', '0', 'static_or_timeseries', FALSE, TRUE, 'Quadratic marginal cost of applied to charging and discharging of 1 MWh.', 0, NULL, 'economics'),
324
+ ('STORE', 'marginal_cost_storage', 'Storage Marginal Cost', 'float', 'currency/MWh/h', '0', 'static_or_timeseries', FALSE, TRUE, 'Marginal cost of energy storage of 1 MWh for one hour.', 0, NULL, 'economics'),
325
+ ('STORE', 'capital_cost', 'Capital Cost', 'float', 'currency/MWh', '0', 'static', FALSE, TRUE, 'Fixed period costs of extending e_nom by 1 MWh, including periodized investment costs and periodic fixed O&M costs (e.g. annuitized investment costs).', 0, NULL, 'economics'),
326
+ ('STORE', 'standing_loss', 'Standing Loss', 'float', 'per unit', '0', 'static_or_timeseries', FALSE, TRUE, 'Losses per hour to energy.', 0, 1, 'power'),
327
+ ('STORE', 'active', 'Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether to consider the component in basic functionality or not', NULL, NULL, 'basic'),
328
+ ('STORE', 'build_year', 'Build Year', 'int', 'year', '0', 'static', FALSE, TRUE, 'build year', 0, 3000, 'economics'),
329
+ ('STORE', 'lifetime', 'Lifetime', 'float', 'years', 'inf', 'static', FALSE, TRUE, 'lifetime', 0, NULL, 'economics'),
330
+ -- Output attributes for STORE (PyPSA store outputs)
331
+ ('STORE', 'p', 'Active Power', 'float', 'MW', '0', 'timeseries', FALSE, FALSE, 'active power at bus (positive if withdrawing energy)', NULL, NULL, 'power'),
332
+ ('STORE', 'q', 'Reactive Power', 'float', 'MVar', '0', 'timeseries', FALSE, FALSE, 'reactive power at bus', NULL, NULL, 'power'),
333
+ ('STORE', 'e', 'Energy', 'float', 'MWh', '0', 'timeseries', FALSE, FALSE, 'Energy stored in store', 0, NULL, 'power'),
334
+ ('STORE', 'e_nom_opt', 'Optimised Energy Capacity', 'float', 'MWh', '0', 'static', FALSE, FALSE, 'Optimised energy capacity.', 0, NULL, 'power'),
335
+ ('STORE', 'mu_upper', 'Shadow Price Upper', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of upper energy limit', NULL, NULL, 'economics'),
336
+ ('STORE', 'mu_lower', 'Shadow Price Lower', 'float', 'currency/MWh', '0', 'timeseries', FALSE, FALSE, 'Shadow price of lower energy limit', NULL, NULL, 'economics');
337
+
338
+ -- ============================================================================
339
+ -- CONSTRAINT ATTRIBUTES
340
+ -- ============================================================================
341
+
342
+ -- CONSTRAINT attributes for Python code block constraints
343
+ INSERT INTO attribute_validation_rules (component_type, attribute_name, display_name, data_type, unit, default_value, allowed_storage_types, is_required, is_input, description, min_value, max_value, group_name) VALUES
344
+ -- Input attributes for CONSTRAINT
345
+ ('CONSTRAINT', 'constraint_code', 'Constraint Code', 'string', 'n/a', '', 'static', TRUE, TRUE, 'Python code block defining constraint logic', NULL, NULL, 'basic'),
346
+ ('CONSTRAINT', 'description', 'Description', 'string', 'n/a', '', 'static', FALSE, TRUE, 'Human-readable description of the constraint', NULL, NULL, 'basic'),
347
+ ('CONSTRAINT', 'is_active', 'Is Active', 'boolean', 'n/a', 'True', 'static', FALSE, TRUE, 'Whether constraint is active and should be applied', NULL, NULL, 'basic'),
348
+ ('CONSTRAINT', 'priority', 'Priority', 'int', 'n/a', '0', 'static', FALSE, TRUE, 'Execution priority (lower numbers execute first)', NULL, NULL, 'basic');
349
+
350
+ -- ============================================================================
351
+ -- COMPONENT CARRIER VALIDATION TRIGGERS
352
+ -- ============================================================================
353
+
354
+ -- Bus carrier validation - buses can only use AC, DC, heat, or gas carriers
355
+ CREATE TRIGGER validate_bus_carrier
356
+ BEFORE INSERT ON components
357
+ FOR EACH ROW
358
+ WHEN NEW.component_type = 'BUS' AND NEW.carrier_id IS NOT NULL
359
+ BEGIN
360
+ SELECT CASE
361
+ WHEN NOT EXISTS (
362
+ SELECT 1 FROM carriers
363
+ WHERE id = NEW.carrier_id
364
+ AND name IN ('AC', 'DC', 'heat', 'gas')
365
+ AND network_id = NEW.network_id
366
+ ) THEN
367
+ RAISE(ABORT, 'Buses can only use AC, DC, heat, or gas carriers')
368
+ END;
369
+ END;
370
+
371
+ -- Bus carrier validation for updates
372
+ CREATE TRIGGER validate_bus_carrier_update
373
+ BEFORE UPDATE OF carrier_id ON components
374
+ FOR EACH ROW
375
+ WHEN NEW.component_type = 'BUS' AND NEW.carrier_id IS NOT NULL AND NEW.carrier_id != OLD.carrier_id
376
+ BEGIN
377
+ SELECT CASE
378
+ WHEN NOT EXISTS (
379
+ SELECT 1 FROM carriers
380
+ WHERE id = NEW.carrier_id
381
+ AND name IN ('AC', 'DC', 'heat', 'gas')
382
+ AND network_id = NEW.network_id
383
+ ) THEN
384
+ RAISE(ABORT, 'Buses can only use AC, DC, heat, or gas carriers')
385
+ END;
386
+ END;
387
+
388
+ -- Line carrier validation - lines can only use AC carriers (PyPSA specification)
389
+ CREATE TRIGGER validate_line_carrier
390
+ BEFORE INSERT ON components
391
+ FOR EACH ROW
392
+ WHEN NEW.component_type = 'LINE' AND NEW.carrier_id IS NOT NULL
393
+ BEGIN
394
+ SELECT CASE
395
+ WHEN NOT EXISTS (
396
+ SELECT 1 FROM carriers
397
+ WHERE id = NEW.carrier_id
398
+ AND name = 'AC'
399
+ AND network_id = NEW.network_id
400
+ ) THEN
401
+ RAISE(ABORT, 'Lines can only use AC carriers')
402
+ END;
403
+ END;
404
+
405
+ -- Line carrier validation for updates
406
+ CREATE TRIGGER validate_line_carrier_update
407
+ BEFORE UPDATE OF carrier_id ON components
408
+ FOR EACH ROW
409
+ WHEN NEW.component_type = 'LINE' AND NEW.carrier_id IS NOT NULL AND NEW.carrier_id != OLD.carrier_id
410
+ BEGIN
411
+ SELECT CASE
412
+ WHEN NOT EXISTS (
413
+ SELECT 1 FROM carriers
414
+ WHERE id = NEW.carrier_id
415
+ AND name = 'AC'
416
+ AND network_id = NEW.network_id
417
+ ) THEN
418
+ RAISE(ABORT, 'Lines can only use AC carriers')
419
+ END;
420
+ END;
421
+
422
+ -- ============================================================================
423
+ -- NOTE: Bus connections are stored in connectivity JSON field, not as attributes
424
+ -- PyPSA export will resolve connectivity JSON to bus names during export process
425
+ -- ============================================================================
426
+
427
+ -- ============================================================================
428
+ -- VALIDATION COMPLETION
429
+ -- ============================================================================
430
+
431
+ -- Update schema version to indicate validation rules are populated
432
+ UPDATE system_metadata
433
+ SET value = '2.2.0', updated_at = CURRENT_TIMESTAMP
434
+ WHERE key = 'schema_version';
435
+
436
+ INSERT OR REPLACE INTO system_metadata (key, value, description)
437
+ VALUES ('validation_rules_version', '2.2.0', 'PyPSA validation rules version');
438
+
439
+ INSERT OR REPLACE INTO system_metadata (key, value, description)
440
+ VALUES ('validation_rules_count', (SELECT COUNT(*) FROM attribute_validation_rules), 'Total number of validation rules');
441
+
442
+ -- Set database user version for tracking
443
+ PRAGMA user_version = 3;