Skip to content

Commit 974c765

Browse files
authored
Merge pull request #30 from LAMPSPUC/handle-logs
Handle logs
2 parents 5e8a35e + 0fc5fb3 commit 974c765

File tree

10 files changed

+104
-85
lines changed

10 files changed

+104
-85
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "0.0.1"
44

55
[deps]
66
PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655"
7+
Memento = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9"
78

89
[compat]
910
PowerModels = "0.18"

src/PWF.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@ module PWF
22

33
# using packages
44
using PowerModels
5+
using Memento
6+
7+
# setting up Memento
8+
const _LOGGER = Memento.getlogger(@__MODULE__)
9+
10+
__init__() = Memento.register(_LOGGER)
11+
12+
function silence()
13+
Memento.info(_LOGGER, "Suppressing information and warning messages for the rest of this session. Use the Memento package for more fine-grained control of logging.")
14+
Memento.setlevel!(Memento.getlogger(PWF), "error")
15+
end
16+
17+
function logger_config!(level)
18+
Memento.config!(Memento.getlogger("PWF"), level)
19+
end
520

621
# include PWF parser file
722
include("pwf2dict.jl")

src/pwf2dict.jl

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
# This parser was develop using ANAREDE v09 user manual
88

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),
1010
("TO BUS", Int64, 9:13), ("CIRCUIT", Int64, 15:16), ("CONTROL MODE", Char, 18),
1111
("MINIMUM VOLTAGE", Float64, 20:23, 20), ("MAXIMUM VOLTAGE", Float64, 25:28, 25),
1212
("CONTROLLED BUS", Int64, 30:34), ("INITIAL REACTIVE INJECTION", Float64, 36:41),
@@ -34,7 +34,7 @@ const _divided_sections = Dict("DBSH" => _dbsh_dtypes,
3434
"""
3535
A list of data file sections in the order that they appear in a PWF file
3636
"""
37-
const _dbar_dtypes = [("NUMBER", Int64, 1:5), ("OPERATION", Int64, 6),
37+
const _dbar_dtypes = [("NUMBER", Int64, 1:5), ("OPERATION", Char, 6),
3838
("STATUS", Char, 7), ("TYPE", Int64, 8), ("BASE VOLTAGE GROUP", String, 9:10),
3939
("NAME", String, 11:22), ("VOLTAGE LIMIT GROUP", String, 23:24),
4040
("VOLTAGE", Float64, 25:28, 25), ("ANGLE", Float64, 29:32),
@@ -51,7 +51,7 @@ const _dbar_dtypes = [("NUMBER", Int64, 1:5), ("OPERATION", Int64, 6),
5151
("AGGREGATOR 9", Int64, 106:108), ("AGGREGATOR 10", Int64, 109:111)]
5252

5353
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),
5555
("CIRCUIT", Int64, 16:17), ("STATUS", Char, 18), ("OWNER", Char, 19),
5656
("RESISTANCE", Float64, 21:26, 24), ("REACTANCE", Float64, 27:32, 30),
5757
("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),
8080
("ROTOR SERVICE FACTOR", Float64, 46:49), ("CHARGE ANGLE", Float64, 51:54),
8181
("MACHINE REACTANCE", Float64, 56:60), ("NOMINAL APPARENT POWER", Float64, 62:66)]
8282

83-
const _dshl_dtypes = [("FROM BUS", Int64, 1:5), ("OPERATION", Int64, 7),
83+
const _dshl_dtypes = [("FROM BUS", Int64, 1:5), ("OPERATION", Char, 7),
8484
("TO BUS", Int64, 10:14), ("CIRCUIT", Int64, 15:16), ("SHUNT FROM", Float64, 18:23),
8585
("SHUNT TO", Float64, 24:29), ("STATUS FROM", String, 31:32), ("STATUS TO", String, 34:35)]
8686

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),
8888
("POLARITY", Char, 9), ("NAME", String, 10:21), ("VOLTAGE LIMIT GROUP", String, 22:23),
8989
("VOLTAGE", Float64, 24:28), ("GROUND ELECTRODE", Float64, 67:71), ("DC LINK", Int64, 72:75)]
9090

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),
9292
("CIRCUIT", Int64, 13:14), ("OWNER", Char, 16), ("RESISTANCE", Float64, 18:23),
9393
("INDUCTANCE", Float64, 24:29), ("CAPACITY", Float64, 61:64)]
9494

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),
9696
("DC BUS", Int64, 14:17), ("NEUTRAL BUS", Int64, 19:22), ("OPERATION MODE", Char, 24),
9797
("BRIDGES", Int64, 26), ("CURRENT", Float64, 28:32), ("COMMUTATION REACTANCE", Float64, 34:38),
9898
("SECONDARY VOLTAGE", Float64, 40:44), ("TRANSFORMER POWER", Float64, 46:50),
9999
("REACTOR RESISTANCE", Float64, 52:56), ("REACTOR INDUCTANCE", Float64, 58:62),
100100
("CAPACITANCE", Float64, 64:68), ("FREQUENCY", Float64, 70:71)]
101101

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),
103103
("INVERTER CONTROL MODE", Char, 9), ("CONVERTER CONTROL TYPE", Char, 10),
104104
("SPECIFIED VALUE", Float64, 12:16), ("CURRENT MARGIN", Float64,18:22),
105105
("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
109109
("MINIMUM DC VOLTAGE FOR POWER CONTROL", Float64, 63:66, 63),
110110
("TAP HI MVAR MODE", Float64, 68:72), ("TAP REDUCED VOLTAGE MODE", Float64, 74:78)]
111111

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),
113113
("BASE", Float64, 14:18), ("NAME", String, 20:39), ("HI MVAR MODE", Char, 41), ("STATUS", Char, 43)]
114114

115115
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
406406

407407
const title_identifier = "TITU"
408408
const end_section_identifier = "99999"
409+
const commented_line_identifier = '('
409410

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
415419
end
416420

417421
"""
@@ -422,43 +426,37 @@ element corresponds to a section, divided by the delimiter 99999.
422426
"""
423427
function _split_sections(io::IO)
424428
file_lines = readlines(io)
425-
filter!(x -> x != "" && x[1] != '(', file_lines) # Ignore commented and empty lines
426429
file_lines = replace.(file_lines, repeat([Char(65533) => ' '], length(file_lines)))
427-
sections = Vector{String}[]
430+
sections = Dict{String, Vector{Int64}}()
428431

429432
section_titles_idx = findall(line -> line == title_identifier, file_lines)
430433
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]
433435
end
434436

435-
file_lines = _remove_titles_from_file_lines(
436-
file_lines, section_titles_idx
437-
)
438-
439437
section_delim = vcat(
440438
0,
441439
findall(x -> x == end_section_identifier, file_lines)
442440
)
443441

444442
num_sections = length(section_delim) - 1
443+
num_lines = length(file_lines)
445444

446445
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
448450
section_end_idx = section_delim[i + 1] - 1
449451

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)
459457
end
460458

461-
return sections
459+
return file_lines, sections
462460
end
463461

464462
function _handle_implicit_decimal_point!(
@@ -475,8 +473,9 @@ end
475473
Internal function. Parses a single line of data elements from a PWF file
476474
and saves it into `data::Dict`.
477475
"""
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+
480479
line_length = _pwf_dtypes[section][end][3][end]
481480
if length(line) < line_length
482481
extra_characters_needed = line_length - length(line)
@@ -496,9 +495,10 @@ function _parse_line_element!(data::Dict{String, Any}, line::String, section::Ab
496495
else
497496
data[field] = element
498497
end
499-
catch
498+
catch message
500499
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"))
502502
end
503503
data[field] = element
504504
end
@@ -507,22 +507,24 @@ function _parse_line_element!(data::Dict{String, Any}, line::String, section::Ab
507507

508508
end
509509

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})
511511

512512
mn_keys, mn_values, mn_type = _mnemonic_pairs[section]
513513

514-
for line in lines
514+
for line_number in lines_idx
515+
line = file_lines[line_number]
515516
for i in 1:length(mn_keys)
516517
k, v = mn_keys[i], mn_values[i]
517518
if v[end] <= length(line)
518519

519520
if mn_type != String && mn_type != Char
520521
try
521522
data[line[k]] = parse(mn_type, line[v])
522-
catch
523+
catch message
523524
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
526528
!_needs_default(line[k]) ? data[line[k]] = line[v] : nothing
527529
end
528530
else
@@ -539,42 +541,43 @@ end
539541
Internal function. Parses a section containing a system component.
540542
Returns a Vector of Dict, where each entry corresponds to a single element.
541543
"""
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)
543545

544546
if section == "DBAR"
545-
for line in section_lines[2:end]
547+
for line_number in section_lines_idx
546548

547549
line_data = Dict{String, Any}()
548-
_parse_line_element!(line_data, line, section)
550+
_parse_line_element!(line_data, line_number, section, file_lines)
549551

550552
bus_i = line_data["NUMBER"]
551553
data["$bus_i"] = line_data
552554
end
553555

554556
else
555-
for line in section_lines[2:end]
557+
for line_number in section_lines_idx
556558

557559
line_data = Dict{String, Any}()
558-
_parse_line_element!(line_data, line, section)
560+
_parse_line_element!(line_data, line_number, section, file_lines)
559561

560562
data["$idx"] = line_data
561563
idx += 1
562564
end
563565
end
564566
end
565567

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]
567570

568571
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))
570573
for (i, idx) in enumerate(sub_titles_idx)
571574

572575
if idx != sub_titles_idx[end]
573576
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)
575578

576579
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)
578581

579582
group = _divided_sections[section]["subgroup"]
580583
data["$i"][group] = rc
@@ -589,24 +592,24 @@ end
589592
Internal function. Receives an array of lines corresponding to a PWF section,
590593
transforms it into a Dict and saves it into `data::Dict`.
591594
"""
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})
594596
section_data = Dict{String, Any}()
595597

596598
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]]
598601

599602
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)
601604

602605
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)
604607

605608
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)
607610

608611
else
609-
@warn "Currently there is no support for $section parsing"
612+
Memento.warn(_LOGGER, "Currently there is no support for $section parsing")
610613
section_data = nothing
611614
end
612615
data[section] = section_data
@@ -617,11 +620,11 @@ _needs_default(ch::Char) = ch == ' '
617620

618621
function _populate_defaults!(pwf_data::Dict{String, Any})
619622

620-
@warn "Populating defaults"
623+
Memento.info(_LOGGER, "Populating defaults")
621624

622625
for (section, section_data) in pwf_data
623626
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)")
625628
else
626629
if section in keys(_pwf_dtypes)
627630
_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.
820823
"""
821824
function _parse_pwf_data(data_io::IO)
822825

823-
sections = _split_sections(data_io)
826+
file_lines, sections = _split_sections(data_io)
824827
pwf_data = Dict{String, Any}()
825828
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)
828831
end
829832
_populate_defaults!(pwf_data)
830833

src/pwf2pm/branch.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ function _pwf2pm_DCSC_branch!(pm_data::Dict, pwf_data::Dict, branch::Dict; add_c
171171

172172
rep = findall(x -> x["f_bus"] == sub_data["f_bus"] && x["t_bus"] == sub_data["t_bus"] && x["circuit"] == sub_data["circuit"], pm_data["branch"])
173173
if length(rep) > 0
174-
@warn "Branch from $(sub_data["f_bus"]) to $(sub_data["t_bus"]) in circuit $(sub_data["circuit"]) is duplicated"
174+
Memento.warn(_LOGGER, "Branch from $(sub_data["f_bus"]) to $(sub_data["t_bus"]) in circuit $(sub_data["circuit"]) is duplicated")
175175
end
176176
idx = string(sub_data["index"])
177177
pm_data["branch"][idx] = sub_data

src/pwf2pm/bus.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ end
108108
function _pwf2pm_bus!(pm_data::Dict, pwf_data::Dict; add_control_data::Bool=false)
109109

110110
dict_dglt = haskey(pwf_data, "DGLT") ? _create_dict_dglt(pwf_data["DGLT"]) : nothing
111-
isa(dict_dglt, Dict) && length(dict_dglt) == 1 ? @warn("Only one limit voltage group definded, each bus will be considered as part of the group $(pwf_data["DGLT"]["1"]["GROUP"]), regardless of its defined group") : nothing
111+
isa(dict_dglt, Dict) && length(dict_dglt) == 1 ? Memento.warn(_LOGGER, "Only one limit voltage group definded, each bus will be considered as part of the group $(pwf_data["DGLT"]["1"]["GROUP"]), regardless of its defined group") : nothing
112112
dict_dgbt = haskey(pwf_data, "DGBT") ? _create_dict_dgbt(pwf_data["DGBT"]) : nothing
113-
isa(dict_dgbt, Dict) && length(dict_dgbt) == 1 ? @warn("Only one base voltage group definded, each bus will be considered as part of the group $(pwf_data["DGBT"]["1"]["GROUP"]), regardless of its defined group") : nothing
113+
isa(dict_dgbt, Dict) && length(dict_dgbt) == 1 ? Memento.warn(_LOGGER, "Only one base voltage group definded, each bus will be considered as part of the group $(pwf_data["DGBT"]["1"]["GROUP"]), regardless of its defined group") : nothing
114114

115115
pm_data["bus"] = Dict{String, Any}()
116116
if haskey(pwf_data, "DBAR")

src/pwf2pm/correct/anarede.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ function _pwf2pm_corrections_PQ!(pm_data::Dict, software::ANAREDE)
8282
# sum load power with the negative of generator power
8383
pm_data["load"][load_key[1]]["pd"] += - Pg
8484
pm_data["load"][load_key[1]]["qd"] += - Qg
85-
@warn "Active generator with QMIN = QMAX = 0 found in PQ bus $i. Adding generator power " *
86-
"to load power and changing generator status to off."
85+
Memento.warn(_LOGGER, "Active generator with QMIN = QMAX = 0 found in PQ bus $i. Adding generator power " *
86+
"to load power and changing generator status to off")
8787
end
8888
end
8989
end

0 commit comments

Comments
 (0)