Convolution of fMRIPREP and behavioral regressors for FSL FEAT in Nipype

Hi there!

I’m writing a Nipype workflow for a first-level analysis in FSL on fMRIPREP preprocessed timeseries. Based on the many helpful suggestions in other threads I have added a number of fMRIPREP confounds as nuisance regressors to the design. Besides those, I would like to model single-trial behavioral regressors (such as trial-to-trial reaction-time variability), which are ‘resampled’ to the temporal resolution of the timeseries. I am unsure whether convolution of these regressors would be beneficial or not, so I would like to run the analysis with and without convolution. However, I get a little stuck on how to specify convolution settings for different (sets of) regressors in Nipype.

The FEAT/UserGuide describes that “if the original waveform is already in an appropriate form, e.g. sampled from the data itself”, convolution should be set to None. Does this indeed apply to the fMRIPREP confounds? If so, it is possible to specify different convolution settings for the behavioral and confound regressors? In Nipype, it seems to me this should perhaps be specified in the level1design.input.bases argument, but I’m a bit unsure how. Below the relevant part of my code.

To summarize:
(1) Is it correct that fMRIPrep confounds should not be convolved?
(2) Is it possible to set convolution to None for fMRIPRep confounds but to e.g., dgamma for the behavioral regressors? If yes, what would be the best way to do this? If no, what would be the best way to deal with this issue?

Any help/advice is very much appreciated! Thanks!

def get_regressors_func(events_file, behav_reg_file, confounds_file): 
    import pandas as pd
    from nipype.interfaces.base import Bunch   

    #task events in sec
    events = pd.read_csv(events_file, sep = ',', header = 1)
    onsets = {k:[] for k in ['stimulus', 'tap_right', 'tap_left', 'probe_onset']}
    durations = [[0],[0],[0],[0]]  #typically 0 for event-related designs
    
    for i in events.index:
        if events['stimulus'][i]=='stimulus':            
            onsets['stimulus'].append(events['time'][i])
        elif events['stimulus'][i]=='tap' and events['response'][i]=='b':
            onsets['tap_right'].append(events['time'][i])
        elif events['stimulus'][i]=='tap' and events['response'][i]=='e':
            onsets['tap_left'].append(events['time'][i])
        elif events['stimulus'][i]=='probe':
            onsets['probe_onset'].append(events['time'][i])
        else:
            print("SOMETHING GOES WRONG", i, events.loc[i])
            
    behav_reg = pd.read_csv(behav_reg_file, sep = '\t')
    behav_reg = behav_reg.fillna(0) #set NaN to 0 so featmodel won't crash
    confounds = pd.read_csv(confounds_file, sep = '\t') 
    confounds = confounds.fillna(0)  
    reg_names = ['BV','AE','BVxAE','CSF','WM','FD','X','Y','Z','RotX','RotY','RotZ'] + [col for col in confounds.columns if 'Cosine' in col]
    
    conditions = [k for k in onsets.keys() if len(onsets[k])>0]   #drop conditions without onsets
    subject_info = Bunch(conditions = conditions,
                        onsets = [[o for o in onsets[k]] for k in conditions],
                        durations = durations,
                        regressors = [list(behav_reg.BV),
                                      list(behav_reg.AE),
                                      list(behav_reg.BVxAE),
                                      list(confounds.CSF),                              
                                      list(confounds.WhiteMatter),                      
                                      list(confounds.FramewiseDisplacement),            
                                      list(confounds.X),                              
                                      list(confounds.Y),                                
                                      list(confounds.Z),                                
                                      list(confounds.RotX),                           
                                      list(confounds.RotY),
                                      list(confounds.RotZ)] + [list(confounds[col]) for col in confounds.columns if 'Cosine' in col],
                        regressor_names = reg_names)
    
    #contrasts
    cont1 = ['BVp', 'T', reg_names, [1] + [0] * (len(reg_names)-1)] 
    cont2 = ['BVn', 'T', reg_names, [-1] + [0] * (len(reg_names)-1)]
    contrasts = [cont1, cont2]
    
    return subject_info, contrasts

get_regressors = Node(interface = Function(function = get_regressors_func, input_names = ['events_file','behav_reg_file','confounds_file'], 
                                           output_names = ['output_bunch','contrasts']),
                      name = 'get_regressors')

modelspec = Node(interface = model.SpecifyModel(), name = 'modelspec')
modelspec.inputs.input_units = 'secs'
modelspec.inputs.time_repetition = TR
modelspec.inputs.high_pass_filter_cutoff = 100 

level1design = Node(interface = fsl.Level1Design(interscan_interval = TR), name = 'level1design')    
level1design.inputs.bases = {'dgamma':{'derivs': True}}
level1design.inputs.model_serial_correlations = False #exclude fsl autoregression

MANAGED BY INCF