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