6
6
7
7
# This parser was develop using ANAREDE v09 user manual
8
8
9
- const _fban_1_dtypes = [(" FROM BUS" , Int64, 1 : 5 ), (" OPERATION" , Int64 , 7 ),
9
+ const _fban_1_dtypes = [(" FROM BUS" , Int64, 1 : 5 ), (" OPERATION" , Char , 7 ),
10
10
(" TO BUS" , Int64, 9 : 13 ), (" CIRCUIT" , Int64, 15 : 16 ), (" CONTROL MODE" , Char, 18 ),
11
11
(" MINIMUM VOLTAGE" , Float64, 20 : 23 , 20 ), (" MAXIMUM VOLTAGE" , Float64, 25 : 28 , 25 ),
12
12
(" CONTROLLED BUS" , Int64, 30 : 34 ), (" INITIAL REACTIVE INJECTION" , Float64, 36 : 41 ),
@@ -34,7 +34,7 @@ const _divided_sections = Dict("DBSH" => _dbsh_dtypes,
34
34
"""
35
35
A list of data file sections in the order that they appear in a PWF file
36
36
"""
37
- const _dbar_dtypes = [(" NUMBER" , Int64, 1 : 5 ), (" OPERATION" , Int64 , 6 ),
37
+ const _dbar_dtypes = [(" NUMBER" , Int64, 1 : 5 ), (" OPERATION" , Char , 6 ),
38
38
(" STATUS" , Char, 7 ), (" TYPE" , Int64, 8 ), (" BASE VOLTAGE GROUP" , String, 9 : 10 ),
39
39
(" NAME" , String, 11 : 22 ), (" VOLTAGE LIMIT GROUP" , String, 23 : 24 ),
40
40
(" VOLTAGE" , Float64, 25 : 28 , 25 ), (" ANGLE" , Float64, 29 : 32 ),
@@ -51,7 +51,7 @@ const _dbar_dtypes = [("NUMBER", Int64, 1:5), ("OPERATION", Int64, 6),
51
51
(" AGGREGATOR 9" , Int64, 106 : 108 ), (" AGGREGATOR 10" , Int64, 109 : 111 )]
52
52
53
53
const _dlin_dtypes = [(" FROM BUS" , Int64, 1 : 5 ), (" OPENING FROM BUS" , Char, 6 ),
54
- (" OPERATION" , Int64 , 8 ), (" OPENING TO BUS" , Char, 10 ), (" TO BUS" , Int64, 11 : 15 ),
54
+ (" OPERATION" , Char , 8 ), (" OPENING TO BUS" , Char, 10 ), (" TO BUS" , Int64, 11 : 15 ),
55
55
(" CIRCUIT" , Int64, 16 : 17 ), (" STATUS" , Char, 18 ), (" OWNER" , Char, 19 ),
56
56
(" RESISTANCE" , Float64, 21 : 26 , 24 ), (" REACTANCE" , Float64, 27 : 32 , 30 ),
57
57
(" SHUNT SUSCEPTANCE" , Float64, 33 : 38 , 35 ), (" TAP" , Float64, 39 : 43 , 40 ),
@@ -80,26 +80,26 @@ const _dger_dtypes = [("NUMBER", Int, 1:5), ("OPERATION", Char, 7),
80
80
(" ROTOR SERVICE FACTOR" , Float64, 46 : 49 ), (" CHARGE ANGLE" , Float64, 51 : 54 ),
81
81
(" MACHINE REACTANCE" , Float64, 56 : 60 ), (" NOMINAL APPARENT POWER" , Float64, 62 : 66 )]
82
82
83
- const _dshl_dtypes = [(" FROM BUS" , Int64, 1 : 5 ), (" OPERATION" , Int64 , 7 ),
83
+ const _dshl_dtypes = [(" FROM BUS" , Int64, 1 : 5 ), (" OPERATION" , Char , 7 ),
84
84
(" TO BUS" , Int64, 10 : 14 ), (" CIRCUIT" , Int64, 15 : 16 ), (" SHUNT FROM" , Float64, 18 : 23 ),
85
85
(" SHUNT TO" , Float64, 24 : 29 ), (" STATUS FROM" , String, 31 : 32 ), (" STATUS TO" , String, 34 : 35 )]
86
86
87
- const _dcba_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Int64 , 6 ), (" TYPE" , Int64, 8 ),
87
+ const _dcba_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Char , 6 ), (" TYPE" , Int64, 8 ),
88
88
(" POLARITY" , Char, 9 ), (" NAME" , String, 10 : 21 ), (" VOLTAGE LIMIT GROUP" , String, 22 : 23 ),
89
89
(" VOLTAGE" , Float64, 24 : 28 ), (" GROUND ELECTRODE" , Float64, 67 : 71 ), (" DC LINK" , Int64, 72 : 75 )]
90
90
91
- const _dcli_dtypes = [(" FROM BUS" , Int64, 1 : 4 ), (" OPERATION" , Int64 , 6 ), (" TO BUS" , Int64, 9 : 12 ),
91
+ const _dcli_dtypes = [(" FROM BUS" , Int64, 1 : 4 ), (" OPERATION" , Char , 6 ), (" TO BUS" , Int64, 9 : 12 ),
92
92
(" CIRCUIT" , Int64, 13 : 14 ), (" OWNER" , Char, 16 ), (" RESISTANCE" , Float64, 18 : 23 ),
93
93
(" INDUCTANCE" , Float64, 24 : 29 ), (" CAPACITY" , Float64, 61 : 64 )]
94
94
95
- const _dcnv_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Int64 , 6 ), (" AC BUS" , Int64, 8 : 12 ),
95
+ const _dcnv_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Char , 6 ), (" AC BUS" , Int64, 8 : 12 ),
96
96
(" DC BUS" , Int64, 14 : 17 ), (" NEUTRAL BUS" , Int64, 19 : 22 ), (" OPERATION MODE" , Char, 24 ),
97
97
(" BRIDGES" , Int64, 26 ), (" CURRENT" , Float64, 28 : 32 ), (" COMMUTATION REACTANCE" , Float64, 34 : 38 ),
98
98
(" SECONDARY VOLTAGE" , Float64, 40 : 44 ), (" TRANSFORMER POWER" , Float64, 46 : 50 ),
99
99
(" REACTOR RESISTANCE" , Float64, 52 : 56 ), (" REACTOR INDUCTANCE" , Float64, 58 : 62 ),
100
100
(" CAPACITANCE" , Float64, 64 : 68 ), (" FREQUENCY" , Float64, 70 : 71 )]
101
101
102
- const _dccv_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Int64 , 6 ), (" LOOSENESS" , Char, 8 ),
102
+ const _dccv_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Char , 6 ), (" LOOSENESS" , Char, 8 ),
103
103
(" INVERTER CONTROL MODE" , Char, 9 ), (" CONVERTER CONTROL TYPE" , Char, 10 ),
104
104
(" SPECIFIED VALUE" , Float64, 12 : 16 ), (" CURRENT MARGIN" , Float64,18 : 22 ),
105
105
(" MAXIMUM OVERCURRENT" , Float64, 24 : 28 ), (" CONVERTER ANGLE" , Float64, 30 : 34 ),
@@ -109,7 +109,7 @@ const _dccv_dtypes = [("NUMBER", Int64, 1:4), ("OPERATION", Int64, 6), ("LOOSENE
109
109
(" MINIMUM DC VOLTAGE FOR POWER CONTROL" , Float64, 63 : 66 , 63 ),
110
110
(" TAP HI MVAR MODE" , Float64, 68 : 72 ), (" TAP REDUCED VOLTAGE MODE" , Float64, 74 : 78 )]
111
111
112
- const _delo_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Int64 , 6 ), (" VOLTAGE" , Float64, 8 : 12 ),
112
+ const _delo_dtypes = [(" NUMBER" , Int64, 1 : 4 ), (" OPERATION" , Char , 6 ), (" VOLTAGE" , Float64, 8 : 12 ),
113
113
(" BASE" , Float64, 14 : 18 ), (" NAME" , String, 20 : 39 ), (" HI MVAR MODE" , Char, 41 ), (" STATUS" , Char, 43 )]
114
114
115
115
const _dcer_dtypes = [(" BUS" , Int, 1 : 5 ), (" OPERATION" , Char, 7 ), (" GROUP" , Int64, 9 : 10 ),
@@ -406,12 +406,16 @@ const _pwf_defaults = Dict("DBAR" => _default_dbar, "DLIN" => _default_dlin, "DC
406
406
407
407
const title_identifier = " TITU"
408
408
const end_section_identifier = " 99999"
409
+ const commented_line_identifier = ' ('
409
410
410
- function _remove_titles_from_file_lines (file_lines:: Vector{String} , section_titles_idx:: Vector{Int64} )
411
- remove_titles_idx = vcat (section_titles_idx, section_titles_idx .+ 1 )
412
- file_lines_without_titles_idx = setdiff (1 : length (file_lines), remove_titles_idx)
413
- file_lines = file_lines[file_lines_without_titles_idx]
414
- return file_lines
411
+ function _is_valid_line (line_number:: Int64 , file_lines:: Vector{String} )
412
+ line = file_lines[line_number]
413
+ if line_number == 1
414
+ return ! isempty (line) && ! startswith (line, commented_line_identifier) && line != title_identifier
415
+ else
416
+ previous_line = file_lines[line_number - 1 ]
417
+ return ! isempty (line) && ! startswith (line, commented_line_identifier) && line != title_identifier && previous_line != title_identifier
418
+ end
415
419
end
416
420
417
421
"""
@@ -422,43 +426,37 @@ element corresponds to a section, divided by the delimiter 99999.
422
426
"""
423
427
function _split_sections (io:: IO )
424
428
file_lines = readlines (io)
425
- filter! (x -> x != " " && x[1 ] != ' (' , file_lines) # Ignore commented and empty lines
426
429
file_lines = replace .(file_lines, repeat ([Char (65533 ) => ' ' ], length (file_lines)))
427
- sections = Vector {String}[]
430
+ sections = Dict {String, Vector{Int64}} ()
428
431
429
432
section_titles_idx = findall (line -> line == title_identifier, file_lines)
430
433
if ! isempty (section_titles_idx)
431
- last_section_title_idx = section_titles_idx[end ]: section_titles_idx[end ] + 1
432
- push! (sections, file_lines[last_section_title_idx])
434
+ sections[title_identifier] = [section_titles_idx[end ] + 1 ]
433
435
end
434
436
435
- file_lines = _remove_titles_from_file_lines (
436
- file_lines, section_titles_idx
437
- )
438
-
439
437
section_delim = vcat (
440
438
0 ,
441
439
findall (x -> x == end_section_identifier, file_lines)
442
440
)
443
441
444
442
num_sections = length (section_delim) - 1
443
+ num_lines = length (file_lines)
445
444
446
445
for i in 1 : num_sections
447
- section_begin_idx = section_delim[i] + 1
446
+ section_name_idx = filter (idx -> _is_valid_line (idx, file_lines), section_delim[i] + 1 : num_lines)[1 ]
447
+ section_name = file_lines[section_name_idx]
448
+
449
+ section_begin_idx = section_name_idx + 1
448
450
section_end_idx = section_delim[i + 1 ] - 1
449
451
450
- # Account for multiple sections in the same pwf
451
- section_i = findall (x -> x[1 ] == file_lines[section_begin_idx], sections)
452
- @assert length (section_i) < 2
453
- if length (section_i) == 0
454
- push! (sections, file_lines[section_begin_idx: section_end_idx])
455
- else
456
- section_i = section_i[1 ]
457
- sections[section_i] = vcat (sections[section_i], file_lines[section_begin_idx + 1 : section_end_idx])
458
- end
452
+ section_range = collect (section_begin_idx: section_end_idx)
453
+ filter! (idx -> _is_valid_line (idx, file_lines), section_range)
454
+
455
+ current = get (sections, section_name, Int64[])
456
+ sections[section_name] = vcat (current, section_range)
459
457
end
460
458
461
- return sections
459
+ return file_lines, sections
462
460
end
463
461
464
462
function _handle_implicit_decimal_point! (
475
473
Internal function. Parses a single line of data elements from a PWF file
476
474
and saves it into `data::Dict`.
477
475
"""
478
- function _parse_line_element! (data:: Dict{String, Any} , line:: String , section:: AbstractString )
479
-
476
+ function _parse_line_element! (data:: Dict{String, Any} , line_number:: Int64 , section:: AbstractString , file_lines:: Vector{String} )
477
+ line = file_lines[line_number]
478
+
480
479
line_length = _pwf_dtypes[section][end ][3 ][end ]
481
480
if length (line) < line_length
482
481
extra_characters_needed = line_length - length (line)
@@ -496,9 +495,10 @@ function _parse_line_element!(data::Dict{String, Any}, line::String, section::Ab
496
495
else
497
496
data[field] = element
498
497
end
499
- catch
498
+ catch message
500
499
if ! _needs_default (element)
501
- @warn " Could not parse $element to $dtype inside $section section, setting it as a String"
500
+ throw (Memento. error (_LOGGER, " Parsing error in line $line_number at section $section :
501
+ $field should be of type $dtype , received $element " ))
502
502
end
503
503
data[field] = element
504
504
end
@@ -507,22 +507,24 @@ function _parse_line_element!(data::Dict{String, Any}, line::String, section::Ab
507
507
508
508
end
509
509
510
- function _parse_line_element! (data:: Dict{String, Any} , lines :: Vector{String } , section:: AbstractString )
510
+ function _parse_line_element! (data:: Dict{String, Any} , lines_idx :: Vector{Int64 } , section:: AbstractString , file_lines :: Vector{String} )
511
511
512
512
mn_keys, mn_values, mn_type = _mnemonic_pairs[section]
513
513
514
- for line in lines
514
+ for line_number in lines_idx
515
+ line = file_lines[line_number]
515
516
for i in 1 : length (mn_keys)
516
517
k, v = mn_keys[i], mn_values[i]
517
518
if v[end ] <= length (line)
518
519
519
520
if mn_type != String && mn_type != Char
520
521
try
521
522
data[line[k]] = parse (mn_type, line[v])
522
- catch
523
+ catch message
523
524
if ! _needs_default (line[v])
524
- @warn " Could not parse $(line[v]) to $mn_type , setting it as a String"
525
- end
525
+ throw (Memento. error (_LOGGER, " Parsing error in line $line_number at section $section :
526
+ $field should be of type $dtype , received $element " ))
527
+ end
526
528
! _needs_default (line[k]) ? data[line[k]] = line[v] : nothing
527
529
end
528
530
else
@@ -539,42 +541,43 @@ end
539
541
Internal function. Parses a section containing a system component.
540
542
Returns a Vector of Dict, where each entry corresponds to a single element.
541
543
"""
542
- function _parse_section_element! (data:: Dict{String, Any} , section_lines :: Vector{String } , section:: AbstractString , idx:: Int64 = 1 )
544
+ function _parse_section_element! (data:: Dict{String, Any} , section_lines_idx :: Vector{Int64 } , section:: AbstractString , file_lines :: Vector{String} , idx:: Int64 = 1 )
543
545
544
546
if section == " DBAR"
545
- for line in section_lines[ 2 : end ]
547
+ for line_number in section_lines_idx
546
548
547
549
line_data = Dict {String, Any} ()
548
- _parse_line_element! (line_data, line , section)
550
+ _parse_line_element! (line_data, line_number , section, file_lines )
549
551
550
552
bus_i = line_data[" NUMBER" ]
551
553
data[" $bus_i " ] = line_data
552
554
end
553
555
554
556
else
555
- for line in section_lines[ 2 : end ]
557
+ for line_number in section_lines_idx
556
558
557
559
line_data = Dict {String, Any} ()
558
- _parse_line_element! (line_data, line , section)
560
+ _parse_line_element! (line_data, line_number , section, file_lines )
559
561
560
562
data[" $idx " ] = line_data
561
563
idx += 1
562
564
end
563
565
end
564
566
end
565
567
566
- function _parse_divided_section! (data:: Dict{String, Any} , section_lines:: Vector{String} , section:: String )
568
+ function _parse_divided_section! (data:: Dict{String, Any} , section_lines_idx:: Vector{Int64} , section:: String , file_lines:: Vector{String} )
569
+ section_lines = file_lines[section_lines_idx]
567
570
568
571
separator = _divided_sections[section][" separator" ]
569
- sub_titles_idx = vcat (1 , findall (x -> x == separator, section_lines))
572
+ sub_titles_idx = vcat (0 , findall (x -> x == separator, section_lines))
570
573
for (i, idx) in enumerate (sub_titles_idx)
571
574
572
575
if idx != sub_titles_idx[end ]
573
576
next_idx = sub_titles_idx[i + 1 ]
574
- _parse_section_element! (data, section_lines[ idx: idx + 1 ], _divided_sections[section][" first name" ], i)
577
+ _parse_section_element! (data, [section_lines_idx[ idx + 1 ]] , _divided_sections[section][" first name" ], file_lines , i)
575
578
576
579
rc = Dict {String, Any} ()
577
- _parse_section_element! (rc, section_lines [idx + 1 : next_idx - 1 ], _divided_sections[section][" second name" ], i)
580
+ _parse_section_element! (rc, section_lines_idx [idx + 2 : next_idx - 1 ], _divided_sections[section][" second name" ], file_lines , i)
578
581
579
582
group = _divided_sections[section][" subgroup" ]
580
583
data[" $i " ][group] = rc
@@ -589,24 +592,24 @@ end
589
592
Internal function. Receives an array of lines corresponding to a PWF section,
590
593
transforms it into a Dict and saves it into `data::Dict`.
591
594
"""
592
- function _parse_section! (data:: Dict{String, Any} , section_lines:: Vector{String} )
593
- section = section_lines[1 ]
595
+ function _parse_section! (data:: Dict{String, Any} , section:: String , section_lines_idx:: Vector{Int64} , file_lines:: Vector{String} )
594
596
section_data = Dict {String, Any} ()
595
597
596
598
if section == title_identifier
597
- section_data = section_lines[end ]
599
+ @assert length (section_lines_idx) == 1
600
+ section_data = file_lines[section_lines_idx[1 ]]
598
601
599
602
elseif section in keys (_mnemonic_pairs)
600
- _parse_line_element! (section_data, section_lines[ 2 : end ] , section)
603
+ _parse_line_element! (section_data, section_lines_idx , section, file_lines )
601
604
602
605
elseif section in keys (_pwf_dtypes)
603
- _parse_section_element! (section_data, section_lines , section)
606
+ _parse_section_element! (section_data, section_lines_idx , section, file_lines )
604
607
605
608
elseif section in keys (_divided_sections)
606
- _parse_divided_section! (section_data, section_lines , section)
609
+ _parse_divided_section! (section_data, section_lines_idx , section, file_lines )
607
610
608
611
else
609
- @ warn " Currently there is no support for $section parsing"
612
+ Memento . warn (_LOGGER, " Currently there is no support for $section parsing" )
610
613
section_data = nothing
611
614
end
612
615
data[section] = section_data
@@ -617,11 +620,11 @@ _needs_default(ch::Char) = ch == ' '
617
620
618
621
function _populate_defaults! (pwf_data:: Dict{String, Any} )
619
622
620
- @warn " Populating defaults"
623
+ Memento . info (_LOGGER, " Populating defaults" )
621
624
622
625
for (section, section_data) in pwf_data
623
626
if ! haskey (_pwf_defaults, section)
624
- @ warn " Parser doesn't have default values for section $(section) . "
627
+ Memento . warn (_LOGGER, " Parser doesn't have default values for section $(section) " )
625
628
else
626
629
if section in keys (_pwf_dtypes)
627
630
_populate_section_defaults! (pwf_data, section, section_data)
@@ -820,11 +823,11 @@ Internal function. Receives a pwf file as an IOStream and parses into a Dict.
820
823
"""
821
824
function _parse_pwf_data (data_io:: IO )
822
825
823
- sections = _split_sections (data_io)
826
+ file_lines, sections = _split_sections (data_io)
824
827
pwf_data = Dict {String, Any} ()
825
828
pwf_data[" name" ] = match (r" ^\< file\s [\/\\ ]*(?:.*[\/\\ ])*(.*)\. pwf\> $" , lowercase (data_io. name)). captures[1 ]
826
- for section in sections
827
- _parse_section! (pwf_data, section)
829
+ for (section_name, section) in sections
830
+ _parse_section! (pwf_data, section_name, section, file_lines )
828
831
end
829
832
_populate_defaults! (pwf_data)
830
833
0 commit comments