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-2015 by Power System Engineering Research Center (PSERC) 0059 % by Ray Zimmerman, PSERC Cornell 0060 % 0061 % $Id: psse_parse_section.m 2644 2015-03-11 19:34:22Z ray $ 0062 % 0063 % This file is part of MATPOWER. 0064 % Covered by the 3-clause BSD License (see LICENSE file for details). 0065 % See http://www.pserc.cornell.edu/matpower/ for more info. 0066 0067 %% defaults 0068 if nargin < 3 0069 have_sections = 0; 0070 verbose = 0; 0071 template = ''; 0072 elseif isstruct(sections) 0073 have_sections = 1; 0074 if nargin < 7 0075 template = ''; 0076 if nargin < 6 0077 label = ''; 0078 if nargin < 5 0079 verbose = 0; 0080 else 0081 error('psse_parse_section: too few input arguments'); 0082 end 0083 end 0084 end 0085 else 0086 have_sections = 0; 0087 if nargin >= 5 0088 template = verbose; 0089 else 0090 template = ''; 0091 end 0092 if nargin >= 4 0093 label = s; 0094 else 0095 label = ''; 0096 end 0097 verbose = sections; 0098 end 0099 0100 %% get relevant records, check section name 0101 nt = length(template); 0102 if have_sections 0103 nr = sections(s).last - sections(s).first + 1; 0104 recs = records(sections(s).first:sections(s).last); 0105 if ~isempty(sections(s).name) && ~strcmpi(label, sections(s).name) 0106 warns{end+1} = sprintf('Section label mismatch, found ''%s'', expected ''%s''', ... 0107 sections(s).name, upper(label)); 0108 if verbose 0109 fprintf('----- WARNING: Found section labeled: ''%s''\n', sections(s).name); 0110 fprintf('----- Expected section labeled: ''%s''\n', upper(label)); 0111 end 0112 end 0113 else 0114 nr = length(records); 0115 recs = records; 0116 end 0117 if verbose 0118 spacers = repmat('.', 1, 42-length(label)); 0119 fprintf('Parsing %6d lines of %s data %s', nr, label, spacers); 0120 end 0121 0122 if nr 0123 %% set up regexp to parse cols, comments of each record 0124 delim = '\s*(,|\s)\s*'; %% general delimiter 0125 repeatdelim = '\s*,\s*|\t'; %% delimiter that allows repeated delimiters 0126 non_quote_field = '[^''",\s/]+'; 0127 single_quote_field = '''([^'']|'''')*'''; 0128 double_quote_field = '"([^"]|"")*"'; 0129 any_field = sprintf('(?<col>%s|%s|%s)', non_quote_field, single_quote_field, double_quote_field); 0130 pat = sprintf('%s%s|%s|%s|(?<comment>/.*)?', any_field, delim, repeatdelim, any_field); 0131 % pat = '(?<col>[^''",\s/]+|''([^'']|'''')*''|"([^"]|"")*")\s*(,|\s)\s*|\s*,\s*|\t|(?<col>[^''",\s/]+|''([^'']|'''')*''|"([^"]|"")*")|(?<comment>/.*)?'; 0132 0133 %% set up functions for use with cellfun 0134 if have_fcn('octave') 0135 parser = @(ln){{regexp(ln, pat, 'names')}}; %% parse cols, comments of each rec 0136 numcols = @(ss)length(ss{1}.col); %% number of columns in each record 0137 else 0138 parser = @(ln){regexp(ln, pat, 'names')}; %% parse cols, comments of each rec 0139 numcols = @(ss)length(ss); %% number of columns in each record 0140 end 0141 0142 %% parse the table into cell array of structs (with col, comment fields) 0143 dd = cellfun(parser, recs); 0144 0145 % %% extract possible comments 0146 % if nargout > 2 0147 % % extract_comment = @(n){n(end).comment}; 0148 % if have_fcn('octave') 0149 % comment = cellfun(@(n){n{1}.comment(end)}, dd); 0150 % else 0151 % comment = cellfun(@(n){n(end).comment}, dd); 0152 % end 0153 % end 0154 0155 %% find max number of columns 0156 nc = cellfun(numcols, dd); %% number of columns 0157 ncmax = max(nc); 0158 ncmin = min(nc); 0159 0160 %% extract data by column 0161 % nc = length(dd{1}); 0162 % if nc && isempty(dd{1}(nc).col) %% comment present 0163 % nc = nc - 1; %% reduce number of columns by 1 to discard 0164 % end 0165 data.num = NaN(nr, max(ncmax, nt)); 0166 data.txt = cell(nr, max(ncmax, nt)); 0167 for c = 1:ncmax 0168 %% template for conversion? 0169 if c <= nt 0170 t = template(c); 0171 else 0172 t = ''; 0173 end 0174 if have_fcn('octave') %% running under Octave 0175 switch t 0176 case {'d', 'f', 'g', 'D', 'F', 'G'} %% numeric data 0177 if t == upper(t) %% possibly quoted 0178 xc_fcn = @(n)extract_col_qnum_octave(n, c, lower(t)); 0179 else %% not quoted (more efficient) 0180 xc_fcn = @(n)extract_col_num_octave(n, c, t); 0181 end 0182 case {'s', 'c'} 0183 xc_fcn = @(n){extract_col_dequote_octave(n, c)}; 0184 otherwise 0185 if c <= ncmin 0186 xc_fcn = @(n)n{1}.col(c); 0187 else 0188 xc_fcn = @(n){extract_col_octave(n, c)}; 0189 end 0190 end 0191 else %% running under Matlab 0192 switch t 0193 case {'d', 'f', 'g', 'D', 'F', 'G'} %% numeric data 0194 if t == upper(t) %% possibly quoted 0195 xc_fcn = @(n)extract_col_qnum(n, c, lower(t)); 0196 else %% not quoted (more efficient) 0197 xc_fcn = @(n)extract_col_num(n, c, t); 0198 end 0199 case {'s', 'c'} 0200 xc_fcn = @(n){extract_col_dequote(n, c)}; 0201 otherwise 0202 if c <= ncmin 0203 xc_fcn = @(n){n(c).col}; 0204 else 0205 xc_fcn = @(n){extract_col(n, c)}; 0206 end 0207 end 0208 end 0209 switch upper(t) 0210 case {'D', 'F', 'G'} 0211 data.num(:, c) = cellfun(xc_fcn, dd); 0212 otherwise 0213 data.txt(:, c) = cellfun(xc_fcn, dd); 0214 end 0215 end 0216 else 0217 data.num = NaN(nr, nt); 0218 data.txt = cell(nr, nt); 0219 end 0220 if verbose 0221 fprintf(' done.\n'); 0222 % if have_sections 0223 % fprintf('%s\n', upper(label)); 0224 % fprintf('%s\n', sections(s).name); 0225 % end 0226 end 0227 0228 %%--------------------------------------------------------------------- 0229 function str = extract_col(n, c) 0230 if c <= length(n) 0231 str = n(c).col; 0232 else 0233 str = ''; 0234 end 0235 0236 %%--------------------------------------------------------------------- 0237 function str = extract_col_octave(n, c) 0238 if c <= length(n{1}.col) 0239 str = n{1}.col{c}; 0240 else 0241 str = ''; 0242 end 0243 0244 %%--------------------------------------------------------------------- 0245 function str = extract_col_dequote(n, c) 0246 if c <= length(n) 0247 str = n(c).col; 0248 if ~isempty(str) && (str(1) == '''' || str(1) == '"') 0249 str = str(2:end-1); 0250 end 0251 else 0252 str = ''; 0253 end 0254 0255 %%--------------------------------------------------------------------- 0256 function str = extract_col_dequote_octave(n, c) 0257 if c <= length(n{1}.col) 0258 str = n{1}.col{c}; 0259 if ~isempty(str) && (str(1) == '''' || str(1) == '"') 0260 str = str(2:end-1); 0261 end 0262 else 0263 str = ''; 0264 end 0265 0266 %%--------------------------------------------------------------------- 0267 function num = extract_col_num(n, c, t) 0268 if c <= length(n) && ~isempty(n(c).col) 0269 num = sscanf(n(c).col, ['%' t]); 0270 else 0271 num = NaN; 0272 end 0273 0274 %%--------------------------------------------------------------------- 0275 function num = extract_col_num_octave(n, c, t) 0276 if c <= length(n{1}.col) && ~isempty(n{1}.col{c}) 0277 num = sscanf(n{1}.col{c}, ['%' t]); 0278 else 0279 num = NaN; 0280 end 0281 0282 %%--------------------------------------------------------------------- 0283 function num = extract_col_qnum(n, c, t) 0284 if c <= length(n) 0285 str = n(c).col; 0286 if isempty(str) 0287 num = NaN; 0288 elseif str(1) == '''' || str(1) == '"' 0289 str = str(2:end-1); 0290 end 0291 num = sscanf(str, ['%' t]); 0292 else 0293 num = NaN; 0294 end 0295 0296 %%--------------------------------------------------------------------- 0297 function num = extract_col_qnum_octave(n, c, t) 0298 if c <= length(n{1}.col) 0299 str = n{1}.col{c}; 0300 if isempty(str) 0301 num = NaN; 0302 elseif str(1) == '''' || str(1) == '"' 0303 str = str(2:end-1); 0304 end 0305 num = sscanf(str, ['%' t]); 0306 else 0307 num = NaN; 0308 end