valid_fwd_model

PURPOSE ^

[pass, err_str] = valid_fwd_model(fmdl, [type])

SYNOPSIS ^

function [pass, err_str] = valid_fwd_model(fmdl, type)

DESCRIPTION ^

 [pass, err_str] = valid_fwd_model(fmdl, [type])

 Confirms that a valid forward model structure exists or
 explain why a model is not valid.

 The model is assumed to be a fwd_model and all fields are
 checked unless type='rec_model'. A reconstruction model
 (rec_model) is only checked to confirm it has a valid mesh
 associated with it.

 If called without an output argument (nargout=0), will
 error out if invalid. Otherwise the function is silent,
 returning an explaination of failures in err_str.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function [pass, err_str] = valid_fwd_model(fmdl, type)
0002 % [pass, err_str] = valid_fwd_model(fmdl, [type])
0003 %
0004 % Confirms that a valid forward model structure exists or
0005 % explain why a model is not valid.
0006 %
0007 % The model is assumed to be a fwd_model and all fields are
0008 % checked unless type='rec_model'. A reconstruction model
0009 % (rec_model) is only checked to confirm it has a valid mesh
0010 % associated with it.
0011 %
0012 % If called without an output argument (nargout=0), will
0013 % error out if invalid. Otherwise the function is silent,
0014 % returning an explaination of failures in err_str.
0015 
0016 % (C) 2015 Alistair Boyle. License: GPL version 2 or version 3
0017 % $Id: valid_fwd_model.m 7067 2024-12-09 01:48:58Z aadler $
0018 
0019 if ischar(fmdl) && strcmp(fmdl,'UNIT_TEST'); do_unit_test; return; end
0020 
0021 pass = 1;
0022 err_str = '';
0023 
0024 if nargin < 2
0025    type = 'fwd_model';
0026 end
0027 if ~any(strcmp(type, {'fwd_model','rec_model'}))
0028    error('unexpected "type"');
0029 end
0030 
0031 % it's a struct with fields
0032 if ~isstruct(fmdl)
0033    pass = 0;
0034    err_str = [err_str '- not a struct\n'];
0035 end
0036 
0037 % required fields
0038 %      field         type
0039 f = {'name',        'char', ...
0040      'nodes',       'numeric', ... % uintX
0041      'elems',       'numeric', ... % uintX
0042      'gnd_node',    'numeric', ... % uintX
0043      'electrode',   'none_or_struct', ...
0044      'stimulation', 'none_or_struct', ...
0045      'solve',       'function', ...
0046      'system_mat',  'function_or_numeric', ...
0047      'jacobian',    'function_or_numeric', ...
0048      'normalize_measurements', 'numeric_or_logical', ... % logical
0049      'type',        'char'};
0050 %     'boundary',   'numeric', ... % uintX OPTIONAL
0051 %     'meas_select','numeric', ... % uintX OPTIONAL
0052 % reduced set of requirements for a rec_model
0053 if strcmp(type, 'rec_model')
0054    f = {'name',     'char', ...
0055         'nodes',    'numeric', ...
0056         'elems',    'numeric'};
0057 end
0058 for i=1:length(f)/2
0059    x=2*(i-1)+1;
0060    y=x+1;
0061 %  if strcmp(f{x},'normalize_measurements'); keyboard; end
0062    [pass,err_str]= validate_fmdl(fmdl,f{x},f{y},pass,err_str);
0063 end
0064 if ~strcmp(type, 'rec_model')
0065    % validate electrode struct
0066    nn = size(fmdl.nodes,1); % number of nodes
0067    if isfield(fmdl,'electrode'); % ok if not specified
0068        for i=1:length(fmdl.electrode)
0069           [pass, err_str] = valid_elec(fmdl.electrode(i), i, nn, pass, err_str);
0070        end
0071        nel = length(fmdl.electrode); % number of electrodes
0072    end
0073    % validate stimulation struct
0074    if isfield(fmdl,'stimulation'); % ok if not specified
0075        for i=1:length(fmdl.stimulation)
0076           [pass, err_str] = valid_stim(fmdl.stimulation(i), i, nel, pass, err_str);
0077        end
0078    end
0079 end
0080 % check for correct 'type'
0081 if ~any(strcmp(fmdl.type, {'fwd_model', 'rec_model'}))
0082    pass = 0;
0083    err_str = [err_str '- field "type" must be "fwd_model" or "rec_model"\n'];
0084 end
0085 
0086 % optional fields
0087 %      field       type
0088 f = {'boundary',   'numeric', ... % uintX
0089      'meas_select','logical'};
0090 for i=1:length(f)/2
0091    x=2*(i-1)+1;
0092    y=x+1;
0093    if isfield(fmdl, f{x}) && ~isa(fmdl.(f{x}), f{y})
0094       pass = 0;
0095       err_str = [err_str '- optional field "' f{x} '" is not a ' f{y} '\n'];
0096    end
0097 end
0098 
0099 % illegal fields (common typos, etc)
0100 %      field
0101 f = {'fwd_model', ...
0102      'rec_model', ... % no recursion
0103      'stim', ... % not short form
0104      'elec', ...
0105      'fmdl', ...
0106      'fwd_mdl', ...
0107      'cmdl', ...
0108      'rmdl', ...
0109      'rec_mdl', ...
0110      'electrodes', ... % not plural
0111      'stimulations', ...
0112      'node', ... % plural
0113      'elem'};
0114 for i=1:length(f)
0115    x=i;
0116    if isfield(fmdl, f{x})
0117       pass = 0;
0118       err_str = [err_str '- illegal field "' f{x} '" found\n'];
0119    end
0120 end
0121 
0122 % result
0123 if ~pass
0124    err_str = err_str(1:end-2); % drop last \n
0125 end
0126 if ( nargout == 0 ) && ~pass
0127    error(sprintf(['Reasons:\n' err_str]));
0128 end
0129 
0130 function [pass,err_str]= validate_fmdl(fmdl,f_x,f_y,pass,err_str);
0131    hasfield= isfield(fmdl, f_x);
0132    if strcmp(f_y(1:4),'none')
0133       if ~hasfield
0134           return
0135       else
0136           f_y(1:8) = ''; % remove 'none_or_'
0137       end
0138    end
0139 
0140    if ~hasfield
0141       pass = 0;
0142       err_str = [err_str '- missing required field: "' f_x '"\n'];
0143    elseif strcmp(f_y,'function')
0144       if ~isfunc(fmdl.(f_x))
0145          pass = 0;
0146          err_str = [err_str '- expected function (pointer or string): "' f_x '"\n'];
0147       end
0148    elseif strcmp(f_y,'function_or_numeric')
0149       if ~isfunc(fmdl.(f_x)) && ~isa(fmdl.(f_x), 'numeric')
0150          pass = 0;
0151          err_str = [err_str '- expected function (pointer or string) or a matrix: "' f_x '"\n'];
0152       end
0153    elseif strcmp(f_y,'numeric_or_logical')
0154       if ~isa(fmdl.(f_x), 'numeric') && ~isa(fmdl.(f_x),'logical')
0155          pass = 0;
0156          err_str = [err_str '- expected numerical or logical value: "' f_x '"\n'];
0157       end
0158    elseif ~isa(fmdl.(f_x), f_y)
0159       pass = 0;
0160       err_str = [err_str '- required field "' f_x '" is not a ' f_y '\n'];
0161    end
0162 
0163 function t=isfunc(f)
0164 t=isa(f, 'function_handle') || isa(f, 'char');
0165 
0166 function [pass, err_str] = valid_stim(stim, i, nel, pass, err_str)
0167 pass_local = 1;
0168 % required fields
0169 %      field         type
0170 f = {'stim_pattern', 'numeric', ...
0171      'meas_pattern', 'numeric'};
0172 for i=1:length(f)/2
0173    x=2*(i-1)+1;
0174    y=x+1;
0175    if ~isfield(stim, f{x})
0176       pass_local = 0;
0177       err_str = [err_str '- missing required field: "stimulation(' num2str(i) ').' f{x} '"\n'];
0178    elseif ~isa(stim.(f{x}), f{y})
0179       pass_local = 0;
0180       err_str = [err_str '- required field "stimulation(' num2str(i) ').' f{x} '" is not a ' f{y} '\n'];
0181    end
0182 end
0183 % optional fields
0184 %      field       type
0185 f = {'stimulation',  'char'};
0186 for i=1:length(f)/2
0187    x=2*(i-1)+1;
0188    y=x+1;
0189    if isfield(stim, f{x}) && ~isa(stim.(f{x}), f{y})
0190       pass_local = 0;
0191       err_str = [err_str '- optional field "stimulation(' num2str(i) ').' f{x} '" is not a ' f{y} '\n'];
0192    end
0193 end
0194 % we don't need to check further
0195 if ~pass_local
0196    pass = 0;
0197    return
0198 end
0199 clear pass_local;
0200 % we expect only stimulation, meas/stim_pattern fields
0201 len = length(fieldnames(stim));
0202 if (len > 3) || ((len > 2) && ~any(strcmp(fieldnames(stim),'stimulation')))
0203    pass = 0;
0204    err_str = [err_str '- extraneous fields in "stimulation(' num2str(i) ')\n'];
0205 end
0206 if size(stim.stim_pattern,1) > nel
0207    pass = 0;
0208    err_str = [err_str '- field "stimulation(' num2str(i) ').stim_pattern has more rows than electrodes in the model\n'];
0209 end
0210 if size(stim.meas_pattern,2) > nel
0211    pass = 0;
0212    err_str = [err_str '- field "stimulation(' num2str(i) ').meas_pattern has more columns than electrodes in the model\n'];
0213 end
0214 
0215 function [pass, err_str] = valid_elec(elec, i, nn, pass, err_str);
0216 pass_local = 1;
0217 % required fields
0218 %      field         type
0219 f = {'nodes',     'numeric', ...
0220      'z_contact', 'numeric'};
0221 for i=1:length(f)/2
0222    x=2*(i-1)+1;
0223    y=x+1;
0224    if ~isfield(elec, f{x})
0225       pass_local = 0;
0226       err_str = [err_str '- missing required field: "electrode(' num2str(i) ').' f{x} '"\n'];
0227    elseif ~isa(elec.(f{x}), f{y})
0228       pass_local = 0;
0229       err_str = [err_str '- required field "electrode(' num2str(i) ').' f{x} '" is not a ' f{y} '\n'];
0230    end
0231 end
0232 % we don't need to check further
0233 if ~pass_local
0234    pass = 0;
0235    return
0236 end
0237 clear pass_local;
0238 % check that 'nodes' are valid
0239 if any((elec.nodes > nn) | (elec.nodes < 1))
0240    pass = 0;
0241    err_str = [err_str '- field "electrode(' num2str(i) ').nodes do not exist on the model.nodes\n'];
0242 end
0243 
0244 function do_unit_test
0245    fmdl = mk_circ_tank(2,[],0);
0246    pass=1; 
0247    try   [pass, err_str] = valid_fwd_model(fmdl);
0248    catch pass=0; 
0249    end
0250    unit_test_cmp('valid_fwd_model 1',pass,1);
0251 
0252    fmdl= rmfield(fmdl,'nodes');
0253    try   [pass, err_str] = valid_fwd_model(fmdl);
0254    catch pass=0; 
0255    end
0256    unit_test_cmp('valid_fwd_model 2',pass,0);

Generated on Sun 29-Dec-2024 11:41:59 by m2html © 2005