Skip to content

Commit 3ed57d5

Browse files
authoredDec 8, 2016
Extensions to Matpower Format Parsing (#53)
* adding a mixture of spaces and tabs to unit tests * adding parsing of arbitrary mpc constant assignment values * adding parsing of arbitrary mpc matrixes * adding ability to name matrix columns * adding ability to extend matpower matrixes * adding line splitter that is aware of matpower strings, extending data input tools to matlab cell arrays * adding some basic documentation for data formats, adding notes to change log
1 parent 5954a1e commit 3ed57d5

File tree

7 files changed

+597
-35
lines changed

7 files changed

+597
-35
lines changed
 

‎CHANGELOG.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ PowerModels.jl Change Log
22
=================
33

44
### staged
5-
- Increased robustness of matlab parsing
6-
- Added support for reading matpower bus_names
5+
- Multiple improvements to Matlab file parsing
6+
- Added support matlab cell arrays
7+
- Added support for matpower bus_names
8+
- Added ability for reading non-standard matpower data elements
79

810
### v0.2.2
911
- Added Transmission Network Expansion Planning (tnep) problem.

‎DATA.md

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# PowerModels Data Format
2+
3+
## Network Data Dictionary
4+
5+
Internally PowerModels utilizes a dictionary to store network data.
6+
The dictionary uses strings as key values so this data structure can be serialized to JSON for algorithmic data exchange.
7+
The data dictionary organization and key names are designed to be consistent with the [Matpower](http://www.pserc.cornell.edu/matpower/) file format and should be familiar to power system researchers.
8+
9+
The network data dictionary structure is roughly as follows,
10+
```
11+
{
12+
"name":<string>,
13+
"version":"2",
14+
"baseMVA":<float>,
15+
"bus":[
16+
{
17+
"index":<int>,
18+
"bus_type":<int>,
19+
"pd":<float>,
20+
"qd":<float>,
21+
...
22+
},
23+
...
24+
],
25+
"gen":[
26+
{
27+
"index":<int>,
28+
"gen_bus":<int>,
29+
"pg":<float>,
30+
"qg":<float>,
31+
...
32+
},
33+
...
34+
],
35+
"branch":[
36+
{
37+
"index":<int>,
38+
"f_bus":<int>,
39+
"t_bus":<int>,
40+
"br_r":<int>,
41+
...
42+
},
43+
...
44+
],
45+
"gencost":[
46+
{
47+
"index":<int>,
48+
"model":<int>,
49+
"startup":<float>,
50+
"shutdown":<float>,
51+
...
52+
},
53+
...
54+
]
55+
}
56+
```
57+
58+
For a detailed list of all possible parameters please refer to the specification document provided with [Matpower](http://www.pserc.cornell.edu/matpower/). In addition to the traditional Matpower parameters every network component in the PowerModels dictionary has an `index` parameter, which can be used to uniquely identify that network element.
59+
60+
It is also important to note that although the Matpower format contains values in mixed units, during PowerModels data setup phase all of the data values are converted to per-unit and radian values.
61+
62+
63+
## Matpower Data Files
64+
65+
The data exchange via JSON files is ideal for building algorithms, however it is hard to for humans to read and process.
66+
To that end PowerModels also has extensive support for parsing Matpower network files in the `.m` format.
67+
68+
69+
### User Extensions
70+
71+
In addition to parsing the standard Matpower parameters, PowerModels also supports extending the standard Matpower format in a number of ways as illustrated by the following examples. In these examples a JSON document fragments are used to indicate the structure of the PowerModel dictionary.
72+
73+
#### Single Values
74+
Single values are added to the root of the dictionary as follows,
75+
```
76+
mpc.const_float = 4.56
77+
```
78+
becomes,
79+
```
80+
{
81+
"const_float": 4.56
82+
}
83+
```
84+
85+
#### Nonstandard Matrices
86+
Nonstandard matrices can be added as follows,
87+
```
88+
mpc.areas = [
89+
1 1;
90+
2 3;
91+
];
92+
```
93+
becomes,
94+
```
95+
{
96+
"areas":[
97+
{
98+
"index":1,
99+
"col_1":1,
100+
"col_2":1
101+
},
102+
{
103+
"index":1,
104+
"col_1":2,
105+
"col_2":3
106+
}
107+
]
108+
}
109+
```
110+
111+
#### Column Names
112+
Column names can be given to nonstandard matrices using the following special comment,
113+
```
114+
%column_names% area refbus
115+
mpc.areas_named = [
116+
4 5;
117+
5 6;
118+
];
119+
```
120+
becomes,
121+
```
122+
{
123+
"areas":[
124+
{
125+
"index":1,
126+
"area":4,
127+
"refbus":5
128+
},
129+
{
130+
"index":1,
131+
"area":5,
132+
"refbus":6
133+
}
134+
]
135+
}
136+
```
137+
138+
#### Standard Matrix Extentions
139+
Finally, if a nonstandard matrix's name extends a current Matpower matrix name with an underscore, then its values will be merged with the original Matpower component data. Note that this feature requires that the nonstandard matrix has column names and has the same number of rows as the original matrix (similar to the `gencost` matrix in the Matpower format). For example,
140+
```
141+
%column_names% rate_i rate_p
142+
mpc.branch_limit = [
143+
50.2 45;
144+
36 60.1;
145+
12 30;
146+
];
147+
148+
```
149+
becomes,
150+
```
151+
{
152+
"branch":[
153+
{
154+
"index":1,
155+
...(all pre existing fields)...
156+
"rate_i":50.2,
157+
"rate_p":45
158+
},
159+
{
160+
"index":2,
161+
...(all pre existing fields)...
162+
"rate_i":36,
163+
"rate_p":60.1
164+
},
165+
{
166+
"index":3,
167+
...(all pre existing fields)...
168+
"rate_i":12,
169+
"rate_p":30
170+
}
171+
]
172+
}
173+
```
174+
175+

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ This enables the definition of a wide variety of power network formulations and
2424
* QC Relaxation (W+L-space)
2525

2626
**Network Data Formats**
27-
* Matpower ".m" files
27+
* Matpower ".m" files (see DATA.md for details)
2828

2929

3030
## Installation

‎src/io/matpower.jl

+241-29
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#########################################################################
66

77

8+
89
function parse_cell(lines, index)
910
return parse_matlab_data(lines, index, '{', '}')
1011
end
@@ -21,8 +22,10 @@ function parse_matlab_data(lines, index, start_char, end_char)
2122
assert(contains(lines[index+line_count], "="))
2223
matrix_assignment = split(lines[index+line_count], '%')[1]
2324
matrix_assignment = strip(matrix_assignment)
25+
26+
assert(contains(matrix_assignment, "mpc."))
2427
matrix_assignment_parts = split(matrix_assignment, '=')
25-
matrix_name = strip(matrix_assignment_parts[1])
28+
matrix_name = strip(replace(matrix_assignment_parts[1], "mpc.", ""))
2629

2730
matrix_assignment_rhs = ""
2831
if length(matrix_assignment_parts) > 1
@@ -61,24 +64,76 @@ function parse_matlab_data(lines, index, start_char, end_char)
6164
matrix_body_rows = split(matrix_body, ';')
6265
matrix_body_rows = matrix_body_rows[1:(length(matrix_body_rows)-1)]
6366

64-
maxtrix = []
67+
matrix = []
6568
for row in matrix_body_rows
6669
row_items = split_line(strip(row))
67-
push!(maxtrix, row_items)
70+
#println(row_items)
71+
push!(matrix, row_items)
6872
if columns < 0
6973
columns = length(row_items)
7074
elseif columns != length(row_items)
7175
error("matrix parsing error, inconsistent number of items in each row\n$(row)")
7276
end
7377
end
7478

75-
return Dict("name" => matrix_name, "data" => maxtrix, "line_count" => line_count)
79+
matrix_dict = Dict("name" => matrix_name, "data" => matrix, "line_count" => line_count)
80+
81+
if index > 1 && contains(lines[index-1], "%column_names%")
82+
column_names_string = lines[index-1]
83+
column_names_string = replace(column_names_string, "%column_names%", "")
84+
column_names = split(column_names_string)
85+
if length(matrix[1]) != length(column_names)
86+
error("column name parsing error, data rows $(length(matrix[1])), column names $(length(column_names)) \n$(column_names)")
87+
end
88+
if any([column_name == "index" for column_name in column_names])
89+
error("column name parsing error, \"index\" is a reserved column name \n$(column_names)")
90+
end
91+
matrix_dict["column_names"] = column_names
92+
end
93+
94+
return matrix_dict
7695
end
7796

97+
98+
single_quote_expr = r"\'((\\.|[^\'])*?)\'"
99+
78100
function split_line(mp_line)
79-
if contains(mp_line, "'")
80-
# TODO fix this so that it will split a string escaping single quoted strings
81-
return strip(mp_line, '\'')
101+
if ismatch(single_quote_expr, mp_line)
102+
# splits a string on white space while escaping text quoted with "'"
103+
# note that quotes will be stripped later, when data typing occurs
104+
105+
#println(mp_line)
106+
tokens = []
107+
while length(mp_line) > 0 && ismatch(single_quote_expr, mp_line)
108+
#println(mp_line)
109+
m = match(single_quote_expr, mp_line)
110+
111+
if m.offset > 1
112+
push!(tokens, mp_line[1:m.offset-1])
113+
end
114+
push!(tokens, replace(m.match, "\\'", "'")) # replace escaped quotes
115+
116+
mp_line = mp_line[m.offset+length(m.match):end]
117+
end
118+
if length(mp_line) > 0
119+
push!(tokens, mp_line)
120+
end
121+
#println(tokens)
122+
123+
items = []
124+
for token in tokens
125+
if contains(token, "'")
126+
push!(items, strip(token))
127+
else
128+
for parts in split(token)
129+
push!(items, strip(parts))
130+
end
131+
end
132+
end
133+
#println(items)
134+
135+
#return [strip(mp_line, '\'')]
136+
return items
82137
else
83138
return split(mp_line)
84139
end
@@ -110,6 +165,53 @@ function extract_assignment(string)
110165
return strip(value)
111166
end
112167

168+
function extract_mpc_assignment(string)
169+
assert(contains(string, "mpc."))
170+
statement = split(string, ';')[1]
171+
statement = replace(statement, "mpc.", "")
172+
name, value = split(statement, '=')
173+
name = strip(name)
174+
value = type_value(strip(value))
175+
176+
return (name, value)
177+
end
178+
179+
180+
# Attempts to determine the type of a string extracted from a matlab file
181+
function type_value(value_string)
182+
value_string = strip(value_string)
183+
184+
if contains(value_string, "'") # value is a string
185+
value = strip(value_string, '\'')
186+
else
187+
# if value is a float
188+
if contains(value_string, ".") || contains(value_string, "e")
189+
value = parse(Float64, value_string)
190+
else # otherwise assume it is an int
191+
value = parse(Int, value_string)
192+
end
193+
end
194+
195+
return value
196+
end
197+
198+
# Attempts to determine the type of an array of strings extracted from a matlab file
199+
function type_array(string_array)
200+
value_string = [strip(value_string) for value_string in string_array]
201+
202+
if any([contains(value_string, "'") for value_string in string_array])
203+
value_array = [strip(value_string, '\'') for value_string in string_array]
204+
else
205+
if any([contains(value_string, ".") || contains(value_string, "e") for value_string in string_array])
206+
value_array = [parse(Float64, value_string) for value_string in string_array]
207+
else # otherwise assume it is an int
208+
value_array = [parse(Int, value_string) for value_string in string_array]
209+
end
210+
end
211+
212+
return value_array
213+
end
214+
113215

114216
function parse_matpower(file_string)
115217
data_string = readstring(open(file_string))
@@ -187,6 +289,11 @@ function parse_matpower_data(data_string)
187289
parsed_matrixes = []
188290
parsed_cells = []
189291

292+
case = Dict{AbstractString,Any}(
293+
"dcline" => [],
294+
"gencost" => []
295+
)
296+
190297
last_index = length(data_lines)
191298
index = 1
192299
while index <= last_index
@@ -199,10 +306,13 @@ function parse_matpower_data(data_string)
199306

200307
if contains(line, "function mpc")
201308
name = extract_assignment(line)
309+
case["name"] = name
202310
elseif contains(line, "mpc.version")
203-
version = extract_assignment(line)
311+
version = extract_mpc_assignment(line)[2]
312+
case["version"] = version
204313
elseif contains(line, "mpc.baseMVA")
205-
baseMVA = parse(Float64, extract_assignment(line))
314+
baseMVA = extract_mpc_assignment(line)[2]
315+
case["baseMVA"] = baseMVA
206316
elseif contains(line, "[")
207317
matrix = parse_matrix(data_lines, index)
208318
push!(parsed_matrixes, matrix)
@@ -211,22 +321,33 @@ function parse_matpower_data(data_string)
211321
cell = parse_cell(data_lines, index)
212322
push!(parsed_cells, cell)
213323
index = index + cell["line_count"]-1
324+
elseif contains(line, "mpc.")
325+
name, value = extract_mpc_assignment(line)
326+
case[name] = value
327+
info("extending matpower format with value named: $(name)")
214328
end
215329
index += 1
216330
end
217331

218-
case = Dict{AbstractString,Any}(
219-
"name" => name,
220-
"version" => version,
221-
"baseMVA" => baseMVA,
222-
"dcline" => [],
223-
"gencost" => []
224-
)
332+
if !haskey(case, "name")
333+
warn(string("no case name found in matpower file. The file seems to be missing \"function mpc = ...\""))
334+
case["name"] = "no_name_found"
335+
end
336+
337+
if !haskey(case, "version")
338+
warn(string("no case version found in matpower file. The file seems to be missing \"mpc.version = ...\""))
339+
case["version"] = "unknown"
340+
end
341+
342+
if !haskey(case, "baseMVA")
343+
warn(string("no baseMVA found in matpower file. The file seems to be missing \"mpc.baseMVA = ...\""))
344+
case["baseMVA"] = 1.0
345+
end
225346

226347
for parsed_matrix in parsed_matrixes
227348
#println(parsed_matrix)
228349

229-
if parsed_matrix["name"] == "mpc.bus"
350+
if parsed_matrix["name"] == "bus"
230351
buses = []
231352

232353
for bus_row in parsed_matrix["data"]
@@ -259,7 +380,7 @@ function parse_matpower_data(data_string)
259380

260381
case["bus"] = buses
261382

262-
elseif parsed_matrix["name"] == "mpc.gen"
383+
elseif parsed_matrix["name"] == "gen"
263384
gens = []
264385

265386
for (i, gen_row) in enumerate(parsed_matrix["data"])
@@ -299,7 +420,7 @@ function parse_matpower_data(data_string)
299420

300421
case["gen"] = gens
301422

302-
elseif parsed_matrix["name"] == "mpc.branch"
423+
elseif parsed_matrix["name"] == "branch"
303424
branches = []
304425

305426
for (i, branch_row) in enumerate(parsed_matrix["data"])
@@ -335,7 +456,7 @@ function parse_matpower_data(data_string)
335456

336457
case["branch"] = branches
337458

338-
elseif parsed_matrix["name"] == "mpc.gencost"
459+
elseif parsed_matrix["name"] == "gencost"
339460
gencost = []
340461

341462
for (i, gencost_row) in enumerate(parsed_matrix["data"])
@@ -356,7 +477,7 @@ function parse_matpower_data(data_string)
356477
error("incorrect Matpower file, the number of generator cost functions ($(length(case["gencost"]))) is inconsistent with the number of generators ($(length(case["gen"]))).\n")
357478
end
358479

359-
elseif parsed_matrix["name"] == "mpc.dcline"
480+
elseif parsed_matrix["name"] == "dcline"
360481
dclines = []
361482

362483
for (i, dcline_row) in enumerate(parsed_matrix["data"])
@@ -392,35 +513,126 @@ function parse_matpower_data(data_string)
392513
push!(dclines, dcline_data)
393514
end
394515

395-
396516
else
397-
println(parsed_matrix["name"])
398-
warn(string("unrecognized data matrix named \"", parsed_matrix["name"], "\" data was ignored."))
517+
name = parsed_matrix["name"]
518+
data = parsed_matrix["data"]
519+
520+
column_names = ["col_$(c)" for c in 1:length(data[1])]
521+
if haskey(parsed_matrix, "column_names")
522+
column_names = parsed_matrix["column_names"]
523+
end
524+
525+
typed_dict_data = build_typed_dict(data, column_names)
526+
527+
extend_case_data(case, name, typed_dict_data, haskey(parsed_matrix, "column_names"))
399528
end
400529
end
401530

402531

403532
for parsed_cell in parsed_cells
404533
#println(parsed_cell)
405-
if parsed_cell["name"] == "mpc.bus_name"
534+
if parsed_cell["name"] == "bus_name"
406535

407536
if length(parsed_cell["data"]) != length(case["bus"])
408537
error("incorrect Matpower file, the number of bus names ($(length(parsed_cell["data"]))) is inconsistent with the number of buses ($(length(case["bus"]))).\n")
409538
end
410539

411540
for (i, bus) in enumerate(case["bus"])
412-
bus["bus_name"] = parsed_cell["data"][i]
541+
# note striping the single quotes is not necessary in general, column typing takes care of this
542+
bus["bus_name"] = strip(parsed_cell["data"][i][1], '\'')
413543
#println(bus["bus_name"])
414544
end
415545
else
416-
println(parsed_cell["name"])
417-
warn(string("unrecognized data cell array named \"", parsed_cell["name"], "\" data was ignored."))
418-
end
546+
547+
name = parsed_cell["name"]
548+
data = parsed_cell["data"]
549+
550+
column_names = ["col_$(c)" for c in 1:length(data[1])]
551+
if haskey(parsed_cell, "column_names")
552+
column_names = parsed_cell["column_names"]
553+
end
554+
555+
typed_dict_data = build_typed_dict(data, column_names)
419556

557+
extend_case_data(case, name, typed_dict_data, haskey(parsed_cell, "column_names"))
558+
end
420559
end
421560

422561
#println("Case:")
423562
#println(case)
424563

425564
return case
426565
end
566+
567+
# takes a list of list of strings and turns it into a list of typed dictionaries
568+
function build_typed_dict(data, column_names)
569+
# TODO see if there is a more julia-y way of doing this
570+
rows = length(data)
571+
columns = length(data[1])
572+
573+
typed_columns = []
574+
for c in 1:columns
575+
column = [ data[r][c] for r in 1:rows ]
576+
#println(column)
577+
typed_column = type_array(column)
578+
#println(typed_column)
579+
push!(typed_columns, typed_column)
580+
end
581+
582+
typed_data = []
583+
for r in 1:rows
584+
data_dict = Dict{AbstractString,Any}()
585+
data_dict["index"] = r
586+
for c in 1:columns
587+
data_dict[column_names[c]] = typed_columns[c][r]
588+
end
589+
push!(typed_data, data_dict)
590+
end
591+
#println(typed_data)
592+
593+
return typed_data
594+
end
595+
596+
# extends a give case data with typed dictionary data
597+
function extend_case_data(case, name, typed_dict_data, has_column_names)
598+
matpower_matrix_names = ["bus", "gen", "branch", "dcline"]
599+
600+
if any([startswith(name, "$(mp_name)_") for mp_name in matpower_matrix_names])
601+
mp_name = "none"
602+
mp_matrix = "none"
603+
604+
for mp_name in matpower_matrix_names
605+
if startswith(name, "$(mp_name)_")
606+
mp_matrix = case[mp_name]
607+
break
608+
end
609+
end
610+
611+
if !has_column_names
612+
error("failed to extend the matpower matrix \"$(mp_name)\" with the matrix \"$(name)\" because it does not have column names.")
613+
end
614+
615+
if length(mp_matrix) != length(typed_dict_data)
616+
error("failed to extend the matpower matrix \"$(mp_name)\" with the matrix \"$(name)\" because they do not have the same number of rows, $(length(mp_matrix)) and $(length(typed_dict_data)) respectively.")
617+
end
618+
619+
info("extending matpower format by appending matrix \"$(name)\" onto \"$(mp_name)\"")
620+
for (i, row) in enumerate(mp_matrix)
621+
merge_row = typed_dict_data[i]
622+
assert(row["index"] == merge_row["index"])
623+
delete!(merge_row, "index")
624+
for key in keys(merge_row)
625+
if haskey(row, key)
626+
error("failed to extend the matpower matrix \"$(mp_name)\" with the matrix \"$(name)\" because they both share \"$(key)\" as a column name.")
627+
end
628+
row[key] = merge_row[key]
629+
end
630+
end
631+
632+
else
633+
# minus 1 for excluding added "index" key
634+
info("extending matpower format with data: $(name) $(length(typed_dict_data))x$(length(typed_dict_data[1])-1)")
635+
case[name] = typed_dict_data
636+
end
637+
end
638+

‎test/data/case2.m

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
% Case to test space based matlab matrix
22
% And other hard to parse cases
3+
34
function mpc = case2
45
mpc.version = '2';
56
mpc.baseMVA = 100.00;
67

78
mpc.bus = [
8-
1 3 0.00 0.00 0.00 0.00 1 1.0000 0.00000 20.00 1 1.100 0.900 0.00 0.00 0 0
9+
1 3 0.00 0.00 0.00 0.00 1 1.0000 0.00000 20.00 1 1.100 0.900 0.00 0.00 0 0
910
% comment in a matrix
10-
144 1 184.31 52.53 0.00 0.00 1 1.0000 0.00000 100.00 1 1.100 0.900 0.00 0.00 0 0
11+
144 1 184.31 52.53 0.00 0.00 1 1.0000 0.00000 100.00 1 1.100 0.900 0.00 0.00 0 0
1112
];
1213

1314
mpc.gen = [1 1098.17 140.74 952.77 -186.22 1.0400 2246.86 1 2042.60 0 612.78 2042.60 -186.22 952.77 -186.22 952.77 0 0 0 0 20.4260 0 0 0 0];

‎test/data/case3.m

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
% Case to test adding data to matpower file
2+
3+
function mpc = case3
4+
mpc.version = '2';
5+
mpc.baseMVA = 100.0;
6+
7+
mpc.bus = [
8+
1 3 110.0 40.0 0.0 0.0 1 1.10000 -0.00000 240.0 1 1.10000 0.90000;
9+
2 2 110.0 40.0 0.0 0.0 1 0.92617 7.25883 240.0 1 1.10000 0.90000;
10+
3 2 95.0 50.0 0.0 0.0 1 0.90000 -17.26710 240.0 2 1.10000 0.90000;
11+
];
12+
13+
mpc.gen = [
14+
1 148.067 54.697 1000.0 -1000.0 1.1 100.0 1 2000.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0;
15+
2 170.006 -8.791 1000.0 -1000.0 0.92617 100.0 1 2000.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0;
16+
3 0.0 -4.843 1000.0 -1000.0 0.9 100.0 1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0;
17+
];
18+
19+
mpc.gencost = [
20+
2 0.0 0.0 3 0.110000 5.000000 0.000000;
21+
2 0.0 0.0 3 0.085000 1.200000 0.000000;
22+
2 0.0 0.0 3 0.000000 0.000000 0.000000;
23+
];
24+
25+
mpc.branch = [
26+
1 3 0.065 0.62 0.45 9000.0 0.0 0.0 0.0 0.0 1 -30.0 30.0;
27+
3 2 0.025 0.75 0.7 50.0 0.0 0.0 0.0 0.0 1 -30.0 30.0;
28+
1 2 0.042 0.9 0.3 9000.0 0.0 0.0 0.0 0.0 1 -30.0 30.0;
29+
];
30+
31+
32+
% matpower data format extentions
33+
34+
% adding single values
35+
mpc.const_int = 123;
36+
mpc.const_float = 4.56
37+
mpc.const_str = 'a string';
38+
39+
40+
% adding extra matrix values
41+
42+
% generic table, comes in a matrix
43+
mpc.areas = [
44+
1 1;
45+
2 3;
46+
];
47+
48+
% named column table
49+
%column_names% area refbus
50+
mpc.areas_named = [
51+
4 5;
52+
5 6;
53+
];
54+
55+
% add two new columns to "branch" matrix
56+
%column_names% rate_i rate_p
57+
mpc.branch_limit = [
58+
50.2 45;
59+
36 60.1;
60+
12 30;
61+
];
62+
63+
64+
% adding extra cell values
65+
66+
mpc.areas_cells = {
67+
'Area 1' 123 987 'Slack \'Bus\' 1' 1.23 ;
68+
'Area 2' 456 987 'Slack Bus 3' 4.56 ;
69+
};
70+
71+
%column_names% area_name area area2 refbus_name refbus
72+
mpc.areas_named_cells = {
73+
'Area 1' 123 987 'Slack Bus 1' 1.23;
74+
'Area 2' 456 987 'Slack Bus 3' 4.56;
75+
};
76+
77+
%column_names% name number_id
78+
mpc.branch_names = {
79+
'Branch 1' 123;
80+
'Branch 2' 456;
81+
'Branch 3' 789;
82+
};
83+
84+
85+
86+

‎test/matpower.jl

+87-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@test isapprox(result["objective"], 204.96; atol = 1e-1)
77
end
88

9-
@testset "30-bus case json data" begin
9+
@testset "30-bus case matpower data" begin
1010
data = PowerModels.parse_file("../test/data/case30.m")
1111
result = run_opf(data, ACPPowerModel, ipopt_solver)
1212

@@ -48,3 +48,89 @@ end
4848
@test isapprox(result["objective"], 8075.1; atol = 1e0)
4949
end
5050
end
51+
52+
@testset "test matpower extentions parser" begin
53+
@testset "3-bus extended constants" begin
54+
data = PowerModels.parse_file("../test/data/case3.m")
55+
56+
@test data["const_int"] == 123
57+
@test data["const_float"] == 4.56
58+
@test data["const_str"] == "a string"
59+
end
60+
61+
@testset "3-bus extended matrix" begin
62+
data = PowerModels.parse_file("../test/data/case3.m")
63+
64+
@test haskey(data, "areas")
65+
@test data["areas"][1]["col_1"] == 1
66+
@test data["areas"][1]["col_2"] == 1
67+
@test data["areas"][2]["col_1"] == 2
68+
@test data["areas"][2]["col_2"] == 3
69+
end
70+
71+
@testset "3-bus extended named matrix" begin
72+
data = PowerModels.parse_file("../test/data/case3.m")
73+
74+
@test haskey(data, "areas_named")
75+
@test data["areas_named"][1]["area"] == 4
76+
@test data["areas_named"][1]["refbus"] == 5
77+
@test data["areas_named"][2]["area"] == 5
78+
@test data["areas_named"][2]["refbus"] == 6
79+
end
80+
81+
@testset "3-bus extended predefined matrix" begin
82+
data = PowerModels.parse_file("../test/data/case3.m")
83+
84+
@test haskey(data, "areas_named")
85+
@test data["branch"][1]["rate_i"] == 50.2
86+
@test data["branch"][1]["rate_p"] == 45
87+
@test data["branch"][2]["rate_i"] == 36
88+
@test data["branch"][2]["rate_p"] == 60.1
89+
@test data["branch"][3]["rate_i"] == 12
90+
@test data["branch"][3]["rate_p"] == 30
91+
end
92+
93+
94+
@testset "3-bus extended matrix from cell" begin
95+
data = PowerModels.parse_file("../test/data/case3.m")
96+
97+
@test haskey(data, "areas_cells")
98+
@test data["areas_cells"][1]["col_1"] == "Area 1"
99+
@test data["areas_cells"][1]["col_2"] == 123
100+
@test data["areas_cells"][1]["col_4"] == "Slack 'Bus' 1"
101+
@test data["areas_cells"][1]["col_5"] == 1.23
102+
@test data["areas_cells"][2]["col_1"] == "Area 2"
103+
@test data["areas_cells"][2]["col_2"] == 456
104+
@test data["areas_cells"][2]["col_4"] == "Slack Bus 3"
105+
@test data["areas_cells"][2]["col_5"] == 4.56
106+
end
107+
108+
@testset "3-bus extended named matrix from cell" begin
109+
data = PowerModels.parse_file("../test/data/case3.m")
110+
111+
@test haskey(data, "areas_named_cells")
112+
@test data["areas_named_cells"][1]["area_name"] == "Area 1"
113+
@test data["areas_named_cells"][1]["area"] == 123
114+
@test data["areas_named_cells"][1]["area2"] == 987
115+
@test data["areas_named_cells"][1]["refbus_name"] == "Slack Bus 1"
116+
@test data["areas_named_cells"][1]["refbus"] == 1.23
117+
@test data["areas_named_cells"][2]["area_name"] == "Area 2"
118+
@test data["areas_named_cells"][2]["area"] == 456
119+
@test data["areas_named_cells"][2]["area2"] == 987
120+
@test data["areas_named_cells"][2]["refbus_name"] == "Slack Bus 3"
121+
@test data["areas_named_cells"][2]["refbus"] == 4.56
122+
end
123+
124+
@testset "3-bus extended predefined matrix from cell" begin
125+
data = PowerModels.parse_file("../test/data/case3.m")
126+
127+
@test haskey(data, "areas_named")
128+
@test data["branch"][1]["name"] == "Branch 1"
129+
@test data["branch"][1]["number_id"] == 123
130+
@test data["branch"][2]["name"] == "Branch 2"
131+
@test data["branch"][2]["number_id"] == 456
132+
@test data["branch"][3]["name"] == "Branch 3"
133+
@test data["branch"][3]["number_id"] == 789
134+
end
135+
end
136+

0 commit comments

Comments
 (0)
Please sign in to comment.