diff --git a/CITATION.cff b/CITATION.cff index 81ab7453c..c7aa9481d 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -93,6 +93,10 @@ authors: affiliation: "University of Gent" orcid: "https://orcid.org/0000-0001-8279-6118" +- family-names: Huber + given-names: Daniel + affiliation: University of Innsbruck + - family-names: "Van Audenhaege" given-names: "Alice" affiliation: "Université catholique de Louvain" diff --git a/src/bids_model/createDefaultStatsModel.m b/src/bids_model/createDefaultStatsModel.m index 0fafae3c9..0142a7098 100644 --- a/src/bids_model/createDefaultStatsModel.m +++ b/src/bids_model/createDefaultStatsModel.m @@ -106,7 +106,7 @@ if strcmp(levelsToUpdate{i}, 'subject') bm.Nodes{idx}.GroupBy = {'subject', 'contrast'}; - elseif strcmp(levelsToUpdate{i}, 'subject') + elseif strcmp(levelsToUpdate{i}, 'dataset') bm.Nodes{idx}.GroupBy = {'contrast'}; end diff --git a/src/preproc/utils/getAcquisitionTime.m b/src/preproc/utils/getAcquisitionTime.m index be7914fb3..d07c2d4ba 100644 --- a/src/preproc/utils/getAcquisitionTime.m +++ b/src/preproc/utils/getAcquisitionTime.m @@ -18,8 +18,8 @@ acquisitionTime = computeAcquisitionTime(sliceOrder, repetitionTime); - % ceil to avoid making this too brittle - if any(sliceOrder > ceil(acquisitionTime * 100) / 100) + % add small time buffer (0.01 s) to avoid making this too brittle + if any(sliceOrder > acquisitionTime + 0.01) sliceOrder = bids.internal.create_unordered_list(num2str(sliceOrder)); msg = sprintf(['Acquisition time cannot be < to any slice timing value:\n\n', ... 'Current values:', ... diff --git a/src/stats/utils/endsWithRunNumber.m b/src/stats/utils/endsWithRunNumber.m new file mode 100644 index 000000000..cb6e77924 --- /dev/null +++ b/src/stats/utils/endsWithRunNumber.m @@ -0,0 +1,15 @@ +function value = endsWithRunNumber(contrastName) + % + % USAGE:: + % + % endsWithRunNumber(contrastName) + % + % + % Returns true if the contrast name ends with an underscore + % followed by some number. + % + + % (C) Copyright 2024 bidspm developers + result = regexp(contrastName, '_[0-9]+\${0,1}$', 'match'); + value = ~isempty(result); + return diff --git a/src/workflows/stats/bidsResults.m b/src/workflows/stats/bidsResults.m index 567423e40..449d1017e 100644 --- a/src/workflows/stats/bidsResults.m +++ b/src/workflows/stats/bidsResults.m @@ -361,16 +361,14 @@ % % Only necessary % if the user did not specify the run number in result.name - % by adding an "_[0-9]*" to indicate the run number to get this contrast + % by adding an "_[0-9]+" to indicate the run number to get this contrast % for example % % opt.result.name = 'listening_1' % - endsWithRunNumber = regexp(contrastName, '_[0-9]*\${0,1}$', 'match'); - if isempty(endsWithRunNumber) - tmp.name = [contrastName '_[0-9]*']; - else + tmp.name = [contrastName '_[0-9]+']; + if ~endsWithRunNumber(contrastName) tmp.name = contrastName; end @@ -392,21 +390,18 @@ contrastNb = getContrastNb(tmp, opt, SPM); - constrastsNamesList = {SPM.xCon(contrastNb).name}'; + contrastsNamesList = {SPM.xCon(contrastNb).name}'; - for j = 1:numel(constrastsNamesList) + for j = 1:numel(contrastsNamesList) result = opt.results(iRes); - result.name = constrastsNamesList{j}; + result.name = contrastsNamesList{j}; - if ~isRunLevel - % skip contrast with name ending in _[0-9]* as they are run level - % contrasts - endsWithRunNumber = regexp(result.name, '_[0-9]*$', 'match'); - if ~isempty(endsWithRunNumber) - continue - end + % skip contrast with name ending in _[0-9]+ + % as they are run level contrasts + if ~isRunLevel && endsWithRunNumber(result.name) + continue end result.space = opt.space; diff --git a/tests/data/models/model-default_smdl.json b/tests/data/models/model-default_smdl.json index 23461ae8e..4bd1c93ed 100644 --- a/tests/data/models/model-default_smdl.json +++ b/tests/data/models/model-default_smdl.json @@ -1,7 +1,7 @@ { - "Name": "default_rest_vislocalizer_vismotion_model", + "Name": "default_vislocalizer_model", "BIDSModelVersion": "1.0.0", - "Description": "default BIDS stats model for rest/vislocalizer/vismotion task", + "Description": "default BIDS stats model for vislocalizer task", "Input": { "task": [ "vislocalizer" @@ -61,8 +61,8 @@ }, "Software": { "SPM": { - "InclusiveMaskingThreshold": 0.8, - "SerialCorrelation": "FAST" + "SerialCorrelation": "FAST", + "InclusiveMaskingThreshold": 0.8 } } }, @@ -107,7 +107,7 @@ "Level": "Dataset", "Name": "dataset", "GroupBy": [ - "" + "contrast" ], "Model": { "X": [ diff --git a/tests/tests_bids_model/test_createDefaultStatsModel.m b/tests/tests_bids_model/test_createDefaultStatsModel.m index 15b32a896..e6df05624 100644 --- a/tests/tests_bids_model/test_createDefaultStatsModel.m +++ b/tests/tests_bids_model/test_createDefaultStatsModel.m @@ -14,6 +14,7 @@ function test_createDefaultStatsModel_basic() BIDS = getLayout(opt); + % opt.dir.derivatives = pwd; opt.dir.derivatives = tempName(); opt.space = {'IXI549Space'}; opt.taskName = {'vislocalizer'}; diff --git a/tests/tests_preproc/utils/test_getAcquisitionTime.m b/tests/tests_preproc/utils/test_getAcquisitionTime.m index c04ff06df..cddfadab0 100644 --- a/tests/tests_preproc/utils/test_getAcquisitionTime.m +++ b/tests/tests_preproc/utils/test_getAcquisitionTime.m @@ -77,3 +77,52 @@ function test_getAcquisitionTime_bug_967() getAcquisitionTime(sliceOrder, repetitionTime); end + +function test_openneuro_ds000224() + % test a less brittle version of getAcquisitionTime + + repetitionTime = 2.2; + + sliceOrder = [ + 1.105, ... + 0, ... + 1.1675, ... + 0.0625, ... + 1.2275, ... + 0.1225, ... + 1.29, ... + 0.185, ... + 1.35, ... + 0.245, ... + 1.4125, ... + 0.3075, ... + 1.4725, ... + 0.37, ... + 1.535, ... + 0.43, ... + 1.595, ... + 0.4925, ... + 1.6575, ... + 0.5525, ... + 1.7175, ... + 0.615, ... + 1.78, ... + 0.675, ... + 1.84, ... + 0.7375, ... + 1.9025, ... + 0.7975, ... + 1.965, ... + 0.86, ... + 2.025, ... + 0.92, ... + 2.0875, ... + 0.9825, ... + 2.1475, ... + 1.0425]; + + acquisitionTime = getAcquisitionTime(sliceOrder, repetitionTime); + + assertElementsAlmostEqual(acquisitionTime, 2.1389, 'absolute', 1e-4); + +end diff --git a/tests/tests_stats/utils/test_endsWithRunNumber.m b/tests/tests_stats/utils/test_endsWithRunNumber.m new file mode 100644 index 000000000..2ac162326 --- /dev/null +++ b/tests/tests_stats/utils/test_endsWithRunNumber.m @@ -0,0 +1,19 @@ +function test_suite = test_endsWithRunNumber %#ok<*STOUT> + % (C) Copyright 2022 bidspm developers + try % assignment of 'localfunctions' is necessary in Matlab >= 2016 + test_functions = localfunctions(); %#ok<*NASGU> + catch % no problem; early Matlab versions can use initTestSuite fine + end + initTestSuite; +end + +function test_endsWithRunNumber_basic() + + assertEqual(endsWithRunNumber('foo_1'), true); + assertEqual(endsWithRunNumber('foo_1_'), false); + assertEqual(endsWithRunNumber('foo_1_a'), false); + assertEqual(endsWithRunNumber('foo_'), false); + assertEqual(endsWithRunNumber('foo'), false); + assertEqual(endsWithRunNumber('foo1'), false); + +end