Skip to content

Commit

Permalink
new branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominik Bach committed Aug 20, 2023
1 parent ec800a5 commit f71d554
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 150 deletions.
125 changes: 125 additions & 0 deletions src/pspm_check_model.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
function model = pspm_check_model(model, modeltype)

% General checks
if ~isstruct(model)
warning('ID:invalid_input', 'Model must be a struct.');
model = struct('invalid', 1);
return
else
model.invalid = 1;
if isempty(model)
warning('ID:invalid_input', 'Model is empty.');
return
end
end

% 1. Reject missing mandatory fields common to all models
if ~isfield(model, 'datafile')
warning('ID:invalid_input', 'No input data file specified.'); return;
elseif ~isfield(model, 'modelfile')
warning('ID:invalid_input', 'No output model file specified.'); return;
elseif ~isfield(model, 'timing')
warning('ID:invalid_input', 'No event onsets specified.'); return;
end

% 2. Reject wrong type of mandatory fields
if ~iscell(model.datafile) && ~ischar(model.datafile)
warning('ID:invalid_input', 'Input data must be a cell or string.'); return;
elseif ~ischar(model.modelfile)
warning('ID:invalid_input', 'Output model must be a string.'); return;
elseif ~ischar(model.timing) && ~iscell(model.timing) && ~isstruct(model.timing)
warning('ID:invalid_input', 'Event definition must be a string, cell array, or (for GLM) struct.'); return;
end

% 3. Fill missing fields common to all models, and accept only allowed values
if ~isfield(model, 'norm')
model.norm = 0;
elseif ~any(ismember(model.norm, [0, 1]))
warning('ID:invalid_input', 'Normalisation must be specified as 0 or 1.'); return;
end
if ~isfield(model, 'missing')
model.missing = cell(nFile, 1);
elseif ischar(model.missing) || isnumeric(model.missing)
model.missing = {model.missing};
elseif ~iscell(model.missing)
warning('ID:invalid_input',...
'Missing values must be a filename, matrix, or cell array of these.');
return
end

% 4. Check that data files, timing, and missing values are cell arrays with
% matching size
if ischar(model.datafile)
model.datafile = {model.datafile};
model.timing = {model.timing};
end

nFile = numel(model.datafile);
if nFile ~= numel(model.timing)
warning('ID:number_of_elements_dont_match',...
'Session numbers of data files and event definitions do not match.');
return
end
if nFile ~= numel(model.missing)
warning('ID:number_of_elements_dont_match',...
'Same number of data files and missing value definitions is needed.');
return
end

% GLM-specific checks
% -------------------------------------------------------------------------
if strcmpi(modeltype, 'glm')
% Reject missing mandatory fields
if ~isfield(model, 'timeunits')
warning('ID:invalid_input', 'No timeunits specified.'); return;
end
end


% DCM-specific check
% -------------------------------------------------------------------------
if strcmpi(modeltype, 'dcm')
if ~ischar(model.timing) && ~iscell(model.timing)
warning('ID:invalid_input', 'Event definition must be a string or cell array.'); return;
end
% Fill missing fields and detect wrong values
if ~isfield(model, 'channel')
model.channel = 'scr'; % this returns the last SCR channel
elseif ~isnumeric(model.channel) && ~strcmp(model.channel,'scr')
warning('ID:invalid_input', 'Channel number must be numeric.'); return;
end
if ~isfield(model, 'constrained')
model.constrained = 0;
elseif ~any(ismember(model.constrained, [0, 1]))
warning('ID:invalid_input', 'Constrained model must be specified as 0 or 1.'); return;
end
if ~isfield(model, 'substhresh')
model.substhresh = 2;
elseif ~isnumeric(model.substhresh)
warning('ID:invalid_input', 'Subsession threshold must be numeric.');
return;
end
end



% General checks that require GLM-specific checks in case of GLM
% -------------------------------------------------------------------------
if ~isfield(model, 'filter')
if strcmpi(modeltype, 'glm')
model.filter = settings.glm(modno).filter;
elseif strcmpi(modeltype, 'dcm')
model.filter = settings.dcm{1}.filter;
elseif ~isfield(model.filter, 'down') || ~isnumeric(model.filter.down)
warning('ID:invalid_input', 'Filter structure needs a numeric ''down'' field.'); return;
end


% 2.7 check filter --



try model.lasttrialcutoff; catch, model.lasttrialcutoff = 7; end


model.invalid = 0;
116 changes: 8 additions & 108 deletions src/pspm_dcm.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,82 +177,21 @@
warnings = {};

%% 2 Check input arguments & set defaults
% 2.1 check input
if nargin < 1
warning('ID:invalid_input', 'No data to work on.');
return
elseif nargin < 2
options = struct();
end
if ~isfield(model, 'datafile')
warning('ID:invalid_input', 'No input data file specified.'); return;
elseif ~isfield(model, 'modelfile')
warning('ID:invalid_input', 'No output model file specified.'); return;
elseif ~isfield(model, 'timing')
warning('ID:invalid_input', 'No event onsets specified.'); return;
end

% 2.2 check faulty input
if ~iscell(model.datafile) && ~ischar(model.datafile)
warning('ID:invalid_input', 'Input data must be a cell or string.'); return;
elseif ~ischar(model.modelfile)
warning('ID:invalid_input', 'Output model must be a string.'); return;
elseif ~ischar(model.timing) && ~iscell(model.timing)
warning('ID:invalid_input', 'Event definition must be a string or cell array.'); return;
end

% 2.3 get further input or set defaults --
% check data channel --
if ~isfield(model, 'channel')
model.channel = 'scr'; % this returns the last SCR channel
elseif ~isnumeric(model.channel) && ~strcmp(model.channel,'scr')
warning('ID:invalid_input', 'Channel number must be numeric.'); return;
end

% 2.4 check normalisation --
if ~isfield(model, 'norm')
model.norm = 0;
elseif ~any(ismember(model.norm, [0, 1]))
warning('ID:invalid_input', 'Normalisation must be specified as 0 or 1.'); return;
end

% 2.5 check constrained model --
if ~isfield(model, 'constrained')
model.constrained = 0;
elseif ~any(ismember(model.constrained, [0, 1]))
warning('ID:invalid_input', 'Constrained model must be specified as 0 or 1.'); return;
end

% 2.6 check substhresh --
if ~isfield(model, 'substhresh')
model.substhresh = 2;
elseif ~isnumeric(model.substhresh)
warning('ID:invalid_input', 'Subsession threshold must be numeric.');
return;
end
% 2.1 check missing input --
if nargin < 1; errmsg = 'Nothing to do.'; warning('ID:invalid_input', errmsg); return
elseif nargin < 2; options = struct(); end

% 2.7 check filter --
if ~isfield(model, 'filter')
model.filter = settings.dcm{1}.filter;
elseif ~isfield(model.filter, 'down') || ~isnumeric(model.filter.down)
warning('ID:invalid_input', 'Filter structure needs a numeric ''down'' field.'); return;
% 2.2 check model
model = pspm_check_model(model, 'dcm');
if model.invalid
return
end

if ~isstruct(options)
warning('ID:invalid_input', '''options'' must be a struct.');
return;
end


% 2.8 set and check options ---
% 2.3 check options (could be re-factored into pspm_options)
options = pspm_options(options, 'dcm');
if options.invalid
return
end

try model.lasttrialcutoff; catch, model.lasttrialcutoff = 7; end

% 2.9 check option fields --
% numeric fields
num_fields = {'depth', 'sfpre', 'sfpost', 'sffreq', 'sclpre', ...
'sclpost', 'aSCR_sigma_offset'};
Expand Down Expand Up @@ -289,44 +228,6 @@
return
end

if ischar(model.datafile)
model.datafile = {model.datafile};
model.timing = {model.timing};
end

nFile = numel(model.datafile);
if ~isfield(model, 'missing')
model.missing = cell(nFile, 1);
elseif ischar(model.missing) || isnumeric(model.missing)
model.missing = {model.missing};
elseif ~iscell(model.missing)
warning('ID:invalid_input',...
'Missing values must be a filename, matrix, or cell array of these.');
return
end
if nFile ~= numel(model.timing)
warning('ID:number_of_elements_dont_match',...
'Session numbers of data files and event definitions do not match.');
return
end
if nFile ~= numel(model.missing)
warning('ID:number_of_elements_dont_match',...
'Same number of data files and missing value definitions is needed.');
return
end

% 2.11 Fill model values
try model.aSCR; catch, model.aSCR = 0; end
try model.eSCR; catch, model.eSCR = 0; end
try model.meanSCR; catch, model.meanSCR = 0; end
% These parameters were set with default fallback values but will be
% determined later by processing
% try model.fixevents; catch, warning('model.fixevents not defined.'); end
% try model.flexevents; catch, warning('model.flexevents not defined.'); end
% try model.missing_data; catch, warning('model.missing_data not defined.'); end
% These parameters do not need to have a default value and will be
% determined later

%% 3 Check, get and prepare data

% split into subsessions
Expand All @@ -348,7 +249,6 @@
sr{iSn} = data{iSn}{end}.header.sr;
model.filter.sr = sr{iSn};


% load and check existing missing data (if defined)
if ~isempty(model.missing{iSn})
[~, missing{iSn}] = pspm_get_timing('missing', ...
Expand Down
25 changes: 4 additions & 21 deletions src/pspm_glm.m
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,9 @@
% 2.1 check missing input --
if nargin < 1; errmsg = 'Nothing to do.'; warning('ID:invalid_input', errmsg); return
elseif nargin < 2; options = struct(); end
model = pspm_check_model(model, 'glm');

fprintf('Computing GLM: %s ...\n', model.modelfile);
if ~isfield(model, 'datafile')
warning('ID:invalid_input', 'No input data file specified.'); return;
elseif ~isfield(model, 'modelfile')
warning('ID:invalid_input', 'No output model file specified.'); return;
elseif ~isfield(model, 'timeunits')
warning('ID:invalid_input', 'No timeunits specified.'); return;
end
% 2.2 check existing --
% whether field timing doesnt exist, field is emtpy or field is cell with empty entries
if ~isfield(model, 'timing') || isempty(model.timing) || ...
Expand All @@ -183,11 +178,7 @@
model.latency = 'fixed';
end
% 2.4 check faulty input --
if ~ischar(model.datafile) && ~iscell(model.datafile)
warning('ID:invalid_input', 'Input data must be a cell or string.'); return;
elseif ~ischar(model.modelfile)
warning('ID:invalid_input', 'Output model must be a string.'); return;
elseif ~ischar(model.timing) && ~iscell(model.timing) && ~isstruct(model.timing)
if ~ischar(model.timing) && ~iscell(model.timing) && ~isstruct(model.timing)
warning('ID:invalid_input', 'Event onsets must be a string, cell, or struct.'); return;
elseif ~ischar(model.timeunits) || ~ismember(model.timeunits, {'seconds', 'markers', 'samples','markervalues'})
warning('ID:invalid_input', 'Timeunits (%s) not recognised; only ''seconds'', ''markers'' and ''samples'' are supported', model.timeunits); return;
Expand Down Expand Up @@ -215,12 +206,7 @@
elseif ~isnumeric(model.channel) && ~ismember(model.channel, {settings.channeltypes.type})
warning('ID:invalid_input', 'Channel number must be numeric.'); return;
end
% 2.7 check normalisation --
if ~isfield(model, 'norm')
model.norm = 0;
elseif ~ismember(model.norm, [0, 1])
warning('ID:invalid_input', 'Normalisation must be specified as 0 or 1.'); return;
end

% 2.8 check mean centering --
if ~isfield(model,'centering')
model.centering = 1;
Expand Down Expand Up @@ -274,9 +260,6 @@
end

%% 4 check filter
if ~isfield(model, 'filter')
model.filter = settings.glm(modno).filter;
end
% 4.1 set default model.filter.down --
if strcmpi(model.filter.down, 'none') || ...
isnumeric(model.filter.down) && isnan(model.filter.down)
Expand Down
29 changes: 8 additions & 21 deletions src/pspm_sf.m
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,15 @@
end
%% 2 Check input
% 2.1 Check missing input --
if nargin<1
warning('ID:invalid_input', 'Nothing to do.'); return;
elseif nargin<2
options = struct();
end
if ~isfield(model, 'datafile')
warning('ID:invalid_input', 'No input data file specified.'); return;
elseif ~isfield(model, 'modelfile')
warning('ID:invalid_input', 'No output model file specified.'); return;
elseif ~isfield(model, 'timeunits')
warning('ID:invalid_input', 'No timeunits specified.'); return;
elseif ~isfield(model, 'timing') && ~strcmpi(model.timeunits, 'file')
warning('ID:invalid_input', 'No epochs specified.'); return;
end
if nargin < 1; errmsg = 'Nothing to do.'; warning('ID:invalid_input', errmsg); return
elseif nargin < 2; options = struct(); end
model = pspm_check_model(model, 'sf');




% 2.2 Check faulty input --
if ~ischar(model.datafile) && ~iscell(model.datafile)
warning('ID:invalid_input', 'Input data must be a cell or string.'); return;
elseif ~ischar(model.modelfile) && ~iscell(model.modelfile)
warning('ID:invalid_input', 'Output model must be a string.'); return;
elseif ~ischar(model.timing) && ~iscell(model.timing) && ~isnumeric(model.timing)
warning('ID:invalid_input', 'Event onsets must be a string, cell, or struct.'); return;
elseif ~ischar(model.timeunits) || ~ismember(model.timeunits, {'seconds', 'markers', 'samples', 'whole'})
if ~ischar(model.timeunits) || ~ismember(model.timeunits, {'seconds', 'markers', 'samples', 'whole'})
warning('ID:invalid_input',...
'Timeunits (%s) not recognised; ',...
'only ''seconds'', ''markers'', ''samples'' and ''whole'' are supported',...
Expand Down

0 comments on commit f71d554

Please sign in to comment.