-
Notifications
You must be signed in to change notification settings - Fork 150
/
psse_parse_section.m
308 lines (291 loc) · 11.2 KB
/
psse_parse_section.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
function [data, warns] = psse_parse_section(warns, records, sections, s, verbose, label, template)
% psse_parse_section - Parses the data from a section of a PSS/E RAW data file.
% ::
%
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX, ...
% VERBOSE, LABEL, TEMPLATE)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX, ...
% VERBOSE, LABEL)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX, ...
% VERBOSE)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, VERBOSE, LABEL, ...
% TEMPLATE)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, VERBOSE, LABEL)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, VERBOSE)
% [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS)
%
% Inputs:
% WARNINGS : cell array of strings containing accumulated
% warning messages
% RECORDS : a cell array of strings returned by PSSE_READ
% SECTIONS : a struct array returned by PSSE_READ
% SIDX : (optional) index if the section to be read
% if included, the RECORD indices are taken from
% SECTIONS(SIDX), otherwise use all RECORDS
% VERBOSE : 1 to display progress info, 0 (default) otherwise
% LABEL : (optional) name for the section, to be compared with
% the section name typically found in the
% END OF <LABEL> DATA comment at the end of each section
% TEMPLATE : (optional) string of characters indicating how to
% interpret the type of the corresponding column, options
% are as follows:
% d, f or g : integer floating point number to be converted
% via SSCANF with %d, %f or %g, respectively.
% D, F or G : integer floating point number, possibly enclosed
% in single or double quotes, to be converted via
% SSCANF with %d, %f or %g, respectively.
% c or s : character or string, possibly enclosed in single
% or double quotes, which are stripped from the string
% Note: Data columns in RECORDS that have no valid corresponding
% entry in TEMPLATE (beyond end of TEMPLATE, or a character
% other than those listed, e.g. '.') are returned in DATA.txt
% with no conversion. TEMPLATE entries for which there is
% no corresponding column in RECORDS are returned as NaN and
% empty, respectively, in DATA.num and DATA.txt.
%
% Output:
% DATA : a struct with two fields:
% num : matrix containing the numeric data for the section, for
% columns with no numeric data, num contain NaNs.
% txt : a cell array containing the non-numeric (char/string)
% data for the section, for columns with numeric data,
% txt entries are empty
% WARNINGS : cell array of strings containing updated accumulated
% warning messages
%
% See also psse2mpc, psse_parse.
% MATPOWER
% Copyright (c) 2014-2024, Power Systems Engineering Research Center (PSERC)
% by Ray Zimmerman, PSERC Cornell
%
% This file is part of MATPOWER.
% Covered by the 3-clause BSD License (see LICENSE file for details).
% See https://matpower.org for more info.
%% defaults
if nargin < 3
have_sections = 0;
verbose = 0;
template = '';
elseif isstruct(sections)
have_sections = 1;
if nargin < 7
template = '';
if nargin < 6
label = '';
if nargin < 5
verbose = 0;
else
error('psse_parse_section: too few input arguments');
end
end
end
else
have_sections = 0;
if nargin >= 5
template = verbose;
else
template = '';
end
if nargin >= 4
label = s;
else
label = '';
end
verbose = sections;
end
%% get relevant records, check section name
nt = length(template);
if have_sections
nr = sections(s).last - sections(s).first + 1;
recs = records(sections(s).first:sections(s).last);
if ~isempty(sections(s).name) && ~strcmpi(label, sections(s).name)
warns{end+1} = sprintf('Section label mismatch, found ''%s'', expected ''%s''', ...
sections(s).name, upper(label));
if verbose
fprintf('----- WARNING: Found section labeled: ''%s''\n', sections(s).name);
fprintf('----- Expected section labeled: ''%s''\n', upper(label));
end
end
else
nr = length(records);
recs = records;
end
if verbose
spacers = repmat('.', 1, 42-length(label));
fprintf('Parsing %6d lines of %s data %s', nr, label, spacers);
end
if nr
%% set up regexp to parse cols, comments of each record
delim = '\s*(,|\s)\s*'; %% general delimiter
repeatdelim = '\s*,\s*|\t'; %% delimiter that allows repeated delimiters
non_quote_field = '[^''",\s/]+';
single_quote_field = '''([^'']|'''')*''';
double_quote_field = '"([^"]|"")*"';
any_field = sprintf('(?<col>%s|%s|%s)', non_quote_field, single_quote_field, double_quote_field);
pat = sprintf('%s%s|%s|%s|(?<comment>/.*)?', any_field, delim, repeatdelim, any_field);
% pat = '(?<col>[^''",\s/]+|''([^'']|'''')*''|"([^"]|"")*")\s*(,|\s)\s*|\s*,\s*|\t|(?<col>[^''",\s/]+|''([^'']|'''')*''|"([^"]|"")*")|(?<comment>/.*)?';
%% set up functions for use with cellfun
if have_feature('octave') && have_feature('octave', 'vnum') < 4.003
parser = @(ln){{regexp(ln, pat, 'names')}}; %% parse cols, comments of each rec
numcols = @(ss)length(ss{1}.col); %% number of columns in each record
else
parser = @(ln){regexp(ln, pat, 'names')}; %% parse cols, comments of each rec
numcols = @(ss)length(ss); %% number of columns in each record
end
%% parse the table into cell array of structs (with col, comment fields)
dd = cellfun(parser, recs);
% %% extract possible comments
% if nargout > 2
% % extract_comment = @(n){n(end).comment};
% if have_feature('octave') && have_feature('octave', 'vnum') < 4.003
% comment = cellfun(@(n){n{1}.comment(end)}, dd);
% else
% comment = cellfun(@(n){n(end).comment}, dd);
% end
% end
%% find max number of columns
nc = cellfun(numcols, dd); %% number of columns
ncmax = max(nc);
ncmin = min(nc);
%% extract data by column
% nc = length(dd{1});
% if nc && isempty(dd{1}(nc).col) %% comment present
% nc = nc - 1; %% reduce number of columns by 1 to discard
% end
data.num = NaN(nr, max(ncmax, nt));
data.txt = cell(nr, max(ncmax, nt));
for c = 1:ncmax
%% template for conversion?
if c <= nt
t = template(c);
else
t = '';
end
if have_feature('octave') && have_feature('octave', 'vnum') < 4.003 %% running under Octave
switch t
case {'d', 'f', 'g', 'D', 'F', 'G'} %% numeric data
if t == upper(t) %% possibly quoted
xc_fcn = @(n)extract_col_qnum_octave(n, c, lower(t));
else %% not quoted (more efficient)
xc_fcn = @(n)extract_col_num_octave(n, c, t);
end
case {'s', 'c'}
xc_fcn = @(n){extract_col_dequote_octave(n, c)};
otherwise
if c <= ncmin
xc_fcn = @(n)n{1}.col(c);
else
xc_fcn = @(n){extract_col_octave(n, c)};
end
end
else %% running under MATLAB (or Octave 4.3 or later)
switch t
case {'d', 'f', 'g', 'D', 'F', 'G'} %% numeric data
if t == upper(t) %% possibly quoted
xc_fcn = @(n)extract_col_qnum(n, c, lower(t));
else %% not quoted (more efficient)
xc_fcn = @(n)extract_col_num(n, c, t);
end
case {'s', 'c'}
xc_fcn = @(n){extract_col_dequote(n, c)};
otherwise
if c <= ncmin
xc_fcn = @(n){n(c).col};
else
xc_fcn = @(n){extract_col(n, c)};
end
end
end
switch upper(t)
case {'D', 'F', 'G'}
data.num(:, c) = cellfun(xc_fcn, dd);
otherwise
data.txt(:, c) = cellfun(xc_fcn, dd);
end
end
else
data.num = NaN(nr, nt);
data.txt = cell(nr, nt);
end
if verbose
fprintf(' done.\n');
% if have_sections
% fprintf('%s\n', upper(label));
% fprintf('%s\n', sections(s).name);
% end
end
%%---------------------------------------------------------------------
function str = extract_col(n, c)
if c <= length(n)
str = n(c).col;
else
str = '';
end
%%---------------------------------------------------------------------
function str = extract_col_octave(n, c)
if c <= length(n{1}.col)
str = n{1}.col{c};
else
str = '';
end
%%---------------------------------------------------------------------
function str = extract_col_dequote(n, c)
if c <= length(n)
str = n(c).col;
if ~isempty(str) && (str(1) == '''' || str(1) == '"')
str = str(2:end-1);
end
else
str = '';
end
%%---------------------------------------------------------------------
function str = extract_col_dequote_octave(n, c)
if c <= length(n{1}.col)
str = n{1}.col{c};
if ~isempty(str) && (str(1) == '''' || str(1) == '"')
str = str(2:end-1);
end
else
str = '';
end
%%---------------------------------------------------------------------
function num = extract_col_num(n, c, t)
if c <= length(n) && ~isempty(n(c).col)
num = sscanf(n(c).col, ['%' t]);
else
num = NaN;
end
%%---------------------------------------------------------------------
function num = extract_col_num_octave(n, c, t)
if c <= length(n{1}.col) && ~isempty(n{1}.col{c})
num = sscanf(n{1}.col{c}, ['%' t]);
else
num = NaN;
end
%%---------------------------------------------------------------------
function num = extract_col_qnum(n, c, t)
if c <= length(n)
str = n(c).col;
if isempty(str)
num = NaN;
elseif str(1) == '''' || str(1) == '"'
str = str(2:end-1);
end
num = sscanf(str, ['%' t]);
else
num = NaN;
end
%%---------------------------------------------------------------------
function num = extract_col_qnum_octave(n, c, t)
if c <= length(n{1}.col)
str = n{1}.col{c};
if isempty(str)
num = NaN;
elseif str(1) == '''' || str(1) == '"'
str = str(2:end-1);
end
num = sscanf(str, ['%' t]);
else
num = NaN;
end