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
0001 function [data, warns] = psse_parse_section(warns, records, sections, s, verbose, label, template) 0002 %PSSE_PARSE_SECTION Parses the data from a section of a PSS/E RAW data file 0003 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX, ... 0004 % VERBOSE, LABEL, TEMPLATE) 0005 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX, ... 0006 % VERBOSE, LABEL) 0007 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX, ... 0008 % VERBOSE) 0009 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, SECTIONS, SIDX) 0010 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, VERBOSE, LABEL, ... 0011 % TEMPLATE) 0012 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, VERBOSE, LABEL) 0013 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS, VERBOSE) 0014 % [DATA, WARNINGS] = PSSE_PARSE_SECTION(WARNINGS, RECORDS) 0015 % 0016 % Inputs: 0017 % WARNINGS : cell array of strings containing accumulated 0018 % warning messages 0019 % RECORDS : a cell array of strings returned by PSSE_READ 0020 % SECTIONS : a struct array returned by PSSE_READ 0021 % SIDX : (optional) index if the section to be read 0022 % if included, the RECORD indices are taken from 0023 % SECTIONS(SIDX), otherwise use all RECORDS 0024 % VERBOSE : 1 to display progress info, 0 (default) otherwise 0025 % LABEL : (optional) name for the section, to be compared with 0026 % the section name typically found in the 0027 % END OF <LABEL> DATA comment at the end of each section 0028 % TEMPLATE : (optional) string of characters indicating how to 0029 % interpret the type of the corresponding column, options 0030 % are as follows: 0031 % d, f or g : integer floating point number to be converted 0032 % via SSCANF with %d, %f or %g, respectively. 0033 % D, F or G : integer floating point number, possibly enclosed 0034 % in single or double quotes, to be converted via 0035 % SSCANF with %d, %f or %g, respectively. 0036 % c or s : character or string, possibly enclosed in single 0037 % or double quotes, which are stripped from the string 0038 % Note: Data columns in RECORDS that have no valid corresponding 0039 % entry in TEMPLATE (beyond end of TEMPLATE, or a character 0040 % other than those listed, e.g. '.') are returned in DATA.txt 0041 % with no conversion. TEMPLATE entries for which there is 0042 % no corresponding column in RECORDS are returned as NaN and 0043 % empty, respectively, in DATA.num and DATA.txt. 0044 % 0045 % Output: 0046 % DATA : a struct with two fields: 0047 % num : matrix containing the numeric data for the section, for 0048 % columns with no numeric data, num contain NaNs. 0049 % txt : a cell array containing the non-numeric (char/string) 0050 % data for the section, for columns with numeric data, 0051 % txt entries are empty 0052 % WARNINGS : cell array of strings containing updated accumulated 0053 % warning messages 0054 % 0055 % See also PSSE2MPC, PSSE_PARSE 0056 0057 % MATPOWER 0058 % Copyright (c) 2014-2016, Power Systems Engineering Research Center (PSERC) 0059 % by Ray Zimmerman, PSERC Cornell 0060 % 0061 % This file is part of MATPOWER. 0062 % Covered by the 3-clause BSD License (see LICENSE file for details). 0063 % See https://matpower.org for more info. 0064 0065 %% defaults 0066 if nargin < 3 0067 have_sections = 0; 0068 verbose = 0; 0069 template = ''; 0070 elseif isstruct(sections) 0071 have_sections = 1; 0072 if nargin < 7 0073 template = ''; 0074 if nargin < 6 0075 label = ''; 0076 if nargin < 5 0077 verbose = 0; 0078 else 0079 error('psse_parse_section: too few input arguments'); 0080 end 0081 end 0082 end 0083 else 0084 have_sections = 0; 0085 if nargin >= 5 0086 template = verbose; 0087 else 0088 template = ''; 0089 end 0090 if nargin >= 4 0091 label = s; 0092 else 0093 label = ''; 0094 end 0095 verbose = sections; 0096 end 0097 0098 %% get relevant records, check section name 0099 nt = length(template); 0100 if have_sections 0101 nr = sections(s).last - sections(s).first + 1; 0102 recs = records(sections(s).first:sections(s).last); 0103 if ~isempty(sections(s).name) && ~strcmpi(label, sections(s).name) 0104 warns{end+1} = sprintf('Section label mismatch, found ''%s'', expected ''%s''', ... 0105 sections(s).name, upper(label)); 0106 if verbose 0107 fprintf('----- WARNING: Found section labeled: ''%s''\n', sections(s).name); 0108 fprintf('----- Expected section labeled: ''%s''\n', upper(label)); 0109 end 0110 end 0111 else 0112 nr = length(records); 0113 recs = records; 0114 end 0115 if verbose 0116 spacers = repmat('.', 1, 42-length(label)); 0117 fprintf('Parsing %6d lines of %s data %s', nr, label, spacers); 0118 end 0119 0120 if nr 0121 %% set up regexp to parse cols, comments of each record 0122 delim = '\s*(,|\s)\s*'; %% general delimiter 0123 repeatdelim = '\s*,\s*|\t'; %% delimiter that allows repeated delimiters 0124 non_quote_field = '[^''",\s/]+'; 0125 single_quote_field = '''([^'']|'''')*'''; 0126 double_quote_field = '"([^"]|"")*"'; 0127 any_field = sprintf('(?<col>%s|%s|%s)', non_quote_field, single_quote_field, double_quote_field); 0128 pat = sprintf('%s%s|%s|%s|(?<comment>/.*)?', any_field, delim, repeatdelim, any_field); 0129 % pat = '(?<col>[^''",\s/]+|''([^'']|'''')*''|"([^"]|"")*")\s*(,|\s)\s*|\s*,\s*|\t|(?<col>[^''",\s/]+|''([^'']|'''')*''|"([^"]|"")*")|(?<comment>/.*)?'; 0130 0131 %% set up functions for use with cellfun 0132 if have_feature('octave') && have_feature('octave', 'vnum') < 4.003 0133 parser = @(ln){{regexp(ln, pat, 'names')}}; %% parse cols, comments of each rec 0134 numcols = @(ss)length(ss{1}.col); %% number of columns in each record 0135 else 0136 parser = @(ln){regexp(ln, pat, 'names')}; %% parse cols, comments of each rec 0137 numcols = @(ss)length(ss); %% number of columns in each record 0138 end 0139 0140 %% parse the table into cell array of structs (with col, comment fields) 0141 dd = cellfun(parser, recs); 0142 0143 % %% extract possible comments 0144 % if nargout > 2 0145 % % extract_comment = @(n){n(end).comment}; 0146 % if have_feature('octave') && have_feature('octave', 'vnum') < 4.003 0147 % comment = cellfun(@(n){n{1}.comment(end)}, dd); 0148 % else 0149 % comment = cellfun(@(n){n(end).comment}, dd); 0150 % end 0151 % end 0152 0153 %% find max number of columns 0154 nc = cellfun(numcols, dd); %% number of columns 0155 ncmax = max(nc); 0156 ncmin = min(nc); 0157 0158 %% extract data by column 0159 % nc = length(dd{1}); 0160 % if nc && isempty(dd{1}(nc).col) %% comment present 0161 % nc = nc - 1; %% reduce number of columns by 1 to discard 0162 % end 0163 data.num = NaN(nr, max(ncmax, nt)); 0164 data.txt = cell(nr, max(ncmax, nt)); 0165 for c = 1:ncmax 0166 %% template for conversion? 0167 if c <= nt 0168 t = template(c); 0169 else 0170 t = ''; 0171 end 0172 if have_feature('octave') && have_feature('octave', 'vnum') < 4.003 %% running under Octave 0173 switch t 0174 case {'d', 'f', 'g', 'D', 'F', 'G'} %% numeric data 0175 if t == upper(t) %% possibly quoted 0176 xc_fcn = @(n)extract_col_qnum_octave(n, c, lower(t)); 0177 else %% not quoted (more efficient) 0178 xc_fcn = @(n)extract_col_num_octave(n, c, t); 0179 end 0180 case {'s', 'c'} 0181 xc_fcn = @(n){extract_col_dequote_octave(n, c)}; 0182 otherwise 0183 if c <= ncmin 0184 xc_fcn = @(n)n{1}.col(c); 0185 else 0186 xc_fcn = @(n){extract_col_octave(n, c)}; 0187 end 0188 end 0189 else %% running under MATLAB (or Octave 4.3 or later) 0190 switch t 0191 case {'d', 'f', 'g', 'D', 'F', 'G'} %% numeric data 0192 if t == upper(t) %% possibly quoted 0193 xc_fcn = @(n)extract_col_qnum(n, c, lower(t)); 0194 else %% not quoted (more efficient) 0195 xc_fcn = @(n)extract_col_num(n, c, t); 0196 end 0197 case {'s', 'c'} 0198 xc_fcn = @(n){extract_col_dequote(n, c)}; 0199 otherwise 0200 if c <= ncmin 0201 xc_fcn = @(n){n(c).col}; 0202 else 0203 xc_fcn = @(n){extract_col(n, c)}; 0204 end 0205 end 0206 end 0207 switch upper(t) 0208 case {'D', 'F', 'G'} 0209 data.num(:, c) = cellfun(xc_fcn, dd); 0210 otherwise 0211 data.txt(:, c) = cellfun(xc_fcn, dd); 0212 end 0213 end 0214 else 0215 data.num = NaN(nr, nt); 0216 data.txt = cell(nr, nt); 0217 end 0218 if verbose 0219 fprintf(' done.\n'); 0220 % if have_sections 0221 % fprintf('%s\n', upper(label)); 0222 % fprintf('%s\n', sections(s).name); 0223 % end 0224 end 0225 0226 %%--------------------------------------------------------------------- 0227 function str = extract_col(n, c) 0228 if c <= length(n) 0229 str = n(c).col; 0230 else 0231 str = ''; 0232 end 0233 0234 %%--------------------------------------------------------------------- 0235 function str = extract_col_octave(n, c) 0236 if c <= length(n{1}.col) 0237 str = n{1}.col{c}; 0238 else 0239 str = ''; 0240 end 0241 0242 %%--------------------------------------------------------------------- 0243 function str = extract_col_dequote(n, c) 0244 if c <= length(n) 0245 str = n(c).col; 0246 if ~isempty(str) && (str(1) == '''' || str(1) == '"') 0247 str = str(2:end-1); 0248 end 0249 else 0250 str = ''; 0251 end 0252 0253 %%--------------------------------------------------------------------- 0254 function str = extract_col_dequote_octave(n, c) 0255 if c <= length(n{1}.col) 0256 str = n{1}.col{c}; 0257 if ~isempty(str) && (str(1) == '''' || str(1) == '"') 0258 str = str(2:end-1); 0259 end 0260 else 0261 str = ''; 0262 end 0263 0264 %%--------------------------------------------------------------------- 0265 function num = extract_col_num(n, c, t) 0266 if c <= length(n) && ~isempty(n(c).col) 0267 num = sscanf(n(c).col, ['%' t]); 0268 else 0269 num = NaN; 0270 end 0271 0272 %%--------------------------------------------------------------------- 0273 function num = extract_col_num_octave(n, c, t) 0274 if c <= length(n{1}.col) && ~isempty(n{1}.col{c}) 0275 num = sscanf(n{1}.col{c}, ['%' t]); 0276 else 0277 num = NaN; 0278 end 0279 0280 %%--------------------------------------------------------------------- 0281 function num = extract_col_qnum(n, c, t) 0282 if c <= length(n) 0283 str = n(c).col; 0284 if isempty(str) 0285 num = NaN; 0286 elseif str(1) == '''' || str(1) == '"' 0287 str = str(2:end-1); 0288 end 0289 num = sscanf(str, ['%' t]); 0290 else 0291 num = NaN; 0292 end 0293 0294 %%--------------------------------------------------------------------- 0295 function num = extract_col_qnum_octave(n, c, t) 0296 if c <= length(n{1}.col) 0297 str = n{1}.col{c}; 0298 if isempty(str) 0299 num = NaN; 0300 elseif str(1) == '''' || str(1) == '"' 0301 str = str(2:end-1); 0302 end 0303 num = sscanf(str, ['%' t]); 0304 else 0305 num = NaN; 0306 end