1
1
import math
2
2
from dataclasses import dataclass , field
3
+ from datetime import datetime
3
4
from typing import Tuple
5
+ from uuid import uuid4
4
6
5
7
import numpy as np
6
8
import pandapower as pp
7
9
import pandas as pd
8
10
from pypsdm .models .input .container .raw_grid import RawGridContainer
9
11
from pypsdm .models .input .create .participants import create_data
12
+ from pypsdm .models .input .create .grid_elements import create_nodes , create_nodes_data , create_lines , create_lines_data
10
13
11
14
12
15
@dataclass
@@ -25,9 +28,10 @@ def convert_grid(
25
28
net = RawGridContainer .empty (
26
29
)
27
30
28
- for uuid , bus in grid .bus .data .iterrows ():
29
- idx = convert_node (net , bus )
30
- uuid_idx .bus [uuid ] = idx # type: ignore
31
+ nodes = convert_nodes (grid .bus )
32
+
33
+ convert_lines (grid )
34
+
31
35
32
36
for uuid , line in grid .line .data .iterrows ():
33
37
idx = convert_line (net , line , uuid_idx .node )
@@ -41,48 +45,174 @@ def convert_grid(
41
45
42
46
return net , uuid_idx
43
47
48
+ def convert_nodes (grid ):
49
+ nodes_data = {}
44
50
45
- def convert_node (net : pp .pandapowerNet , node_data : pd .Series ):
46
- node_id = node_data ["id" ]
47
- vn_kv = node_data ["v_rated" ]
48
- xy_coords = [node_data ["longitude" ], node_data ["latitude" ]]
49
- subnet = node_data ["subnet" ]
50
- node_type = "b"
51
- return create_nodes_data (
51
+ df = grid .bus
52
52
53
- )
53
+ for idx , row in df .iterrows ():
54
+ # TODO SLACK?
54
55
56
+ # Get operation times
57
+ operates_from , operates_until = get_operation_times (row )
55
58
56
- def convert_line (net : pp .pandapowerNet , line_data : pd .Series , uuid_idx : dict ):
57
- line_id = line_data ["id" ]
58
- from_bus = uuid_idx [line_data ["node_a" ]]
59
- to_bus = uuid_idx [line_data ["node_b" ]]
60
- length_km = line_data ["length" ]
61
- r_ohm_per_km = line_data ["r" ]
62
- x_ohm_per_km = line_data ["x" ]
63
- g_us_per_km , c_nf_per_km = line_param_conversion (
64
- float (line_data ["b" ]), float (line_data ["g" ])
65
- )
66
- max_i_ka = line_data ["i_max" ] / 1000
67
- return pp .create_line_from_parameters (
68
- net ,
69
- name = line_id ,
70
- from_bus = from_bus ,
71
- to_bus = to_bus ,
72
- length_km = length_km ,
73
- r_ohm_per_km = r_ohm_per_km ,
74
- x_ohm_per_km = x_ohm_per_km ,
75
- c_nf_per_km = c_nf_per_km ,
76
- max_i_ka = max_i_ka ,
77
- g_us_per_km = g_us_per_km ,
78
- )
59
+ # Determine the voltage level (vlt_lvl or vn_kv)
60
+ if "vlt_lvl" in df .columns and not pd .isna (row ["vlt_lvl" ]):
61
+ volt_lvl = row ["vlt_lvl" ]
62
+ else :
63
+ volt_lvl = row ["vn_kv" ]
64
+
65
+ # Determine the subnet (subnet, zone, or fixed number)
66
+ if "subnet" in df .columns and not pd .isna (row ["subnet" ]):
67
+ subnet = row ["subnet" ]
68
+ elif "zone" in df .columns and not pd .isna (row ["zone" ]):
69
+ subnet = row ["zone" ]
70
+ else :
71
+ subnet = 101
72
+
73
+ node_data = create_nodes_data (
74
+ uuid = row ["uuid" ],
75
+ geo_position = None ,
76
+ id = row ["name" ],
77
+ subnet = subnet ,
78
+ v_rated = row ["vn_kv" ],
79
+ v_target = row ["vn_kv" ],
80
+ volt_lvl = volt_lvl ,
81
+ operates_from = operates_from ,
82
+ operates_until = operates_until ,
83
+ operator = None ,
84
+ slack = False
85
+ )
86
+ nodes_data [node_data .name ] = node_data
87
+
88
+ if row ["uuid" ] == '1f6e5dd7-9bd7-4bb5-9ff9-56000a6cd14b' :
89
+ print (node_data )
90
+
91
+
92
+ # if row["uuid"] == '798443cf-7d59-4e95-aaf5-955f65531bac':
93
+ # print(node_data)
94
+
95
+
96
+ return create_nodes (nodes_data )
97
+
98
+
99
+ def get_operation_times (row ):
100
+ if row ["in_service" ]:
101
+ operates_from = None
102
+ operates_until = None
103
+ else :
104
+ operates_from = datetime (1980 , 1 , 1 )
105
+ operates_until = datetime (1980 , 1 , 2 )
106
+
107
+ return operates_from , operates_until
108
+
109
+
110
+ def get_node_uuid (nodes , node_id ):
111
+ for uuid , node_data in nodes .items ():
112
+ if node_data ['id' ] == node_id :
113
+ return uuid
114
+ raise ValueError (f"No matching node found for id { node_id } " )
115
+
116
+ def get_v_target_for_node (nodes , node_uuid ):
117
+ return nodes [node_uuid ]['v_target' ]
79
118
80
119
81
- def line_param_conversion (b_us : float , g_us : float ):
82
- g_us_per_km = g_us
120
+ def line_param_conversion (c_nf_per_km : float , g_us_per_km : float ):
121
+ g_us = g_us_per_km
83
122
f = 50
84
- c_nf_per_km = b_us / (2 * np .pi * f * 1e-3 )
85
- return g_us_per_km , c_nf_per_km
123
+ b_us = c_nf_per_km * (2 * np .pi * f * 1e-3 )
124
+
125
+ return g_us , b_us
126
+
127
+ def convert_line_types (df , nodes ):
128
+ line_type_columns = ['r_ohm_per_km' , 'x_ohm_per_km' , 'c_nf_per_km' , 'g_us_per_km' , 'max_i_ka' ]
129
+ unique_line_types = df [line_type_columns ].drop_duplicates ().reset_index (drop = True )
130
+
131
+ line_types = {}
132
+
133
+ for idx , row in unique_line_types .iterrows ():
134
+ uuid = str (uuid4 ())
135
+
136
+ g_us , b_us = line_param_conversion (row ['c_nf_per_km' ], row ['g_us_per_km' ])
137
+
138
+ # Retrieve node_a and node_b UUIDs based on from_bus and to_bus
139
+ node_a_uuid = get_node_uuid (nodes , row ['from_bus' ])
140
+ node_b_uuid = get_node_uuid (nodes , row ['to_bus' ])
141
+
142
+ # Retrieve v_target for node_a and node_b
143
+ v_target_a = get_v_target_for_node (nodes , node_a_uuid )
144
+ v_target_b = get_v_target_for_node (nodes , node_b_uuid )
145
+
146
+ # Ensure v_target_a and v_target_b are the same
147
+ if v_target_a != v_target_b :
148
+ raise ValueError (f"v_target mismatch between node_a ({ v_target_a } ) and node_b ({ v_target_b } ) for line { row ['from_bus' ]} to { row ['to_bus' ]} " )
149
+
150
+
151
+ line_types [uuid ] = {
152
+ 'uuid' : uuid ,
153
+ 'r' : row ['r_ohm_per_km' ],
154
+ 'x' : row ['x_ohm_per_km' ],
155
+ 'b' : b_us ,
156
+ 'g' : g_us ,
157
+ 'i_max' : row ['max_i_ka' ] * 1000 ,
158
+ 'id' : f"line_type_{ idx + 1 } " ,
159
+ 'v_rated' : v_target_a ,
160
+ }
161
+
162
+ return line_types
163
+ def convert_lines (df , line_types , nodes ):
164
+ lines_data = []
165
+
166
+ for idx , row in df .iterrows ():
167
+ # Find the corresponding line type based on r, x, c, g, max_i_ka
168
+ line_type_uuid = None
169
+
170
+ for uuid , line_type_data in line_types .items ():
171
+ if (
172
+ line_type_data ['r' ] == row ['r_ohm_per_km' ] and
173
+ line_type_data ['x' ] == row ['x_ohm_per_km' ] and
174
+ line_type_data ['b' ] == row ['c_nf_per_km' ] and
175
+ line_type_data ['g' ] == row ['g_us_per_km' ] and
176
+ line_type_data ['i_max' ] == row ['max_i_ka' ]
177
+ ):
178
+ line_type_uuid = uuid
179
+ break
180
+
181
+ # If no matching line type is found, there might be an issue
182
+ if not line_type_uuid :
183
+ raise ValueError (f"No matching line type found for line { row ['name' ]} " )
184
+
185
+ # Retrieve node_a and node_b UUIDs based on from_bus and to_bus
186
+ node_a_uuid = get_node_uuid (nodes , row ['from_bus' ])
187
+ node_b_uuid = get_node_uuid (nodes , row ['to_bus' ])
188
+
189
+ # Set operates_from and operates_until based on in_service status
190
+ if row ['in_service' ]:
191
+ operates_from = None
192
+ operates_until = None
193
+ else :
194
+ operates_from = datetime (1980 , 1 , 1 )
195
+ operates_until = datetime (1980 , 12 , 31 )
196
+
197
+ # Create line data
198
+ line_data = {
199
+ 'uuid' : row ["uuid" ],
200
+ 'geo_position' : None ,
201
+ 'id' : row ['name' ],
202
+ 'length' : row ['length_km' ],
203
+ 'node_a' : node_a_uuid ,
204
+ 'node_b' : node_b_uuid ,
205
+ 'olm_characteristic' : "olm:{(0.0,1.0)}" ,
206
+ 'operates_from' : operates_from ,
207
+ 'operates_until' : operates_until ,
208
+ 'operator' : None ,
209
+ 'parallel_devices' : row ['parallel' ],
210
+ 'type' : line_type_uuid ,
211
+ }
212
+
213
+ lines_data .append (line_data )
214
+
215
+ return lines_data
86
216
87
217
88
218
def convert_transformer (net : pp .pandapowerNet , trafo_data : pd .Series , uuid_idx : dict ):
0 commit comments