Probably the most important chapter in this section is about how to handle error and crashes. Because at the beginning you will run into a few.
For example:
string
as input, where a float
value is expected or you try to specify a parameter that doesn't exist. Be sure to use the right input type
and input name.[func1.nii, func2.nii, func3.nii]
to a node that only expects one input file . MapNode
is your solution.*.nii.gz
? SPM cannot handle that. Nipype's Gunzip
interface can help.Important note about crashfiles
. Crashfiles
are only created when you run a workflow, not during building a workflow. If you have a typo in a folder path, because they didn't happen during runtime, but still during workflow building.
In [ ]:
from nipype import SelectFiles, Node, Workflow
from os.path import abspath as opap
from nipype.interfaces.fsl import MCFLIRT, IsotropicSmooth
# Create SelectFiles node
templates={'func': '{subject_id}/func/{subject_id}_task-flanker_run-1_bold.nii.gz'}
sf = Node(SelectFiles(templates),
name='selectfiles')
sf.inputs.base_directory = opap('/data/ds102')
sf.inputs.subject_id = 'sub-06'
# Create Motion Correction Node
mcflirt = Node(MCFLIRT(mean_vol=True,
save_plots=True),
name='mcflirt')
# Create Smoothing node
smooth = Node(IsotropicSmooth(fwhm=4),
name='smooth')
# Create a preprocessing workflow
wf = Workflow(name="preprocWF")
wf.base_dir = 'working_dir'
# Connect the three nodes to each other
wf.connect([(sf, mcflirt, [("func", "in_file")]),
(mcflirt, smooth, [("out_file", "in_file")])])
# Let's the workflow
wf.run()
Hidden, in the log file you can find the relevant information:
IOError: No files were found matching func template: /data/ds102/sub-06/func/sub-06_task-flanker_run-1_bold.nii.gz
Interface SelectFiles failed to run.
170301-13:04:17,458 workflow INFO:
***********************************
170301-13:04:17,460 workflow ERROR:
could not run node: preprocWF.selectfiles
170301-13:04:17,461 workflow INFO:
crashfile: /home/jovyan/work/notebooks/crash-20170301-130417-mnotter-selectfiles-45206d1b-73d9-4e03-a91e-437335577b8d.pklz
170301-13:04:17,462 workflow INFO:
This part tells you that it's an IOError
and that it looked for the file /data/ds102/sub-06/func/sub-06_task-flanker_run-1_bold.nii.gz
.
After the line ***********************************
, you can additional see, that it's the node preprocWF.selectfiles
that crasehd and that you can find a crashfile
to this crash under /home/jovyan/work/notebooks/
.
In [ ]:
!nipype_display_crash /home/jovyan/work/notebooks/crash-*selectfiles-*.pklz
In [ ]:
from nipype.interfaces.fsl import IsotropicSmooth
smooth = IsotropicSmooth(fwhm='4')
This will give you the error: TraitError
: The 'fwhm' trait of an IsotropicSmoothInput instance must be a float, but a value of '4' <type 'str'> was specified.
To make sure that you are using the right input types, just check the help
section of a given interface. There you can see fwhm: (a float)
.
In [ ]:
IsotropicSmooth.help()
In a similar way, you will also get an error message if the input type is correct but you have a type in the name:
TraitError: The 'output_type' trait of an IsotropicSmoothInput instance must be u'NIFTI_PAIR' or u'NIFTI_PAIR_GZ' or u'NIFTI_GZ' or u'NIFTI', but a value of 'NIFTIiii' <type 'str'> was specified.
In [ ]:
from nipype.interfaces.fsl import IsotropicSmooth
smooth = IsotropicSmooth(output_type='NIFTIiii')
As you an see in the MapNode example, if you try to feed an array as an input into a field that only expects a single file, you will get a TraitError
.
In [ ]:
from nipype.algorithms.misc import Gunzip
from nipype.pipeline.engine import Node
files = ['/data/ds102/sub-01/func/sub-01_task-flanker_run-1_bold.nii.gz',
'/data/ds102/sub-01/func/sub-01_task-flanker_run-2_bold.nii.gz']
gunzip = Node(Gunzip(), name='gunzip',)
gunzip.inputs.in_file = files
This can be solved by using a MapNode
:
In [ ]:
from nipype.pipeline.engine import MapNode
gunzip = MapNode(Gunzip(), name='gunzip', iterfield=['in_file'])
gunzip.inputs.in_file = files
Now, make sure that you specify files that actually exist, otherwise you can the same problem as in crash example 1, but this time labeled as TraitError
:
TraitError: Each element of the 'in_file' trait of a DynamicTraitedSpec instance must be an existing file name, but a value of '/data/ds102/sub-06/func/sub-06_task-flanker_run-1_bold.nii.gz' <type 'str'> was specified.
In [ ]:
files = ['/data/ds102/sub-06/func/sub-06_task-flanker_run-1_bold.nii.gz',
'/data/ds102/sub-06/func/sub-06_task-flanker_run-2_bold.nii.gz']
gunzip.inputs.in_file = files
By the way, not that those crashes don't create a crashfile
, because they didn't happen during runtime, but still during workflow building.
*.nii.gz
filesSPM12 cannot handle compressed NIfTI files (*nii.gz
). If you try to run the node nonetheless, it can give you different kind of problems:
*.nii.gz
filesSPM12 has a problem with handling *.nii.gz
files. For it a compressed functional image has no temporal dimension and therefore seems to be just a 3D file. So if we try to run the Realign
interface on a compressed file, we will get a weired UnicodeEncodeError
error.
In [ ]:
from nipype.interfaces.spm import Realign
realign = Realign(in_files='/data/ds102/sub-01/func/sub-01_task-flanker_run-1_bold.nii.gz')
realign.run()
But what does this UnicodeEncodeError
mean?
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf7' in position 7984: ordinal not in range(128)
Well, to find out, we need to dig a bit deeper and check the corresponding MATLAB script. Because every SPM interface creates an executable MATLAB script, either in the current location or in the folder of the node. So what's written in this script?
In [ ]:
!cat /home/jovyan/work/notebooks/pyscript_realign.m
All seems to be fine, right? It even detects that the functional image has a temporal dimension. So what's wrong with MATLAB? To find out, let's run the script directly in matlab ourselves...
In [ ]:
!/opt/spm12/run_spm12.sh /opt/mcr/v91/ batch pyscript_realign.m
Now, here's at least a hint. At the end of the output, we get the following lines:
Item 'Session', field 'val': Number of matching files (0) less than required (1).
MATLAB code threw an exception:
No executable modules, but still unresolved dependencies or incomplete module inputs.
It's not too clear from the output, but MATLAB tries to tell you, that it cannot read the compressed NIfTI files. Therefore, it doesn't find one single NIfTI file (0 matching files, required 1
).
Solve this issue by unzipping the compressed NIfTI file before giving it as an input to an SPM node. This can either be done by using the Gunzip
interface from Nipype or even better, if the input is coming from a FSL interface, most of them have an input filed output_type='NIFTI'
, that you can set to NIFIT.
In [ ]:
from nipype.interfaces.spm import Smooth
smooth = Smooth(in_files='/data/ds102/sub-01/anat/sub-01_T1w.nii.gz')
smooth.run()
As you can see, in this case you'll get the error:
FileNotFoundError: File/Directory '[u'/data/workflow/smooth/ssub-01_T1w.nii.gz']' not found for Smooth output 'smoothed_files'.
Interface Smooth failed to run.
It's easy to overlook the additional s
in front of the file name. The problem is, the error tells you that it cannot find the output file of smooth
, but doesn't tell you what the problem in MATLAB was.
And even if you run the MATLAB script yourself, you will get no hints. In this case, good luck...
...
------------------------------------------------------------------------
Running job #1
------------------------------------------------------------------------
Running 'Smooth'
Done 'Smooth'
Done
In [ ]:
!/opt/spm12/run_spm12.sh /opt/mcr/v91/ batch pyscript_smooth.m
Especially at the beginning, just after installation, you sometimes forgot to specify some environment variables. If you try to use an interface where the environment variables of the software are not specified, you'll errors, such as:
IOError: command 'mri_convert' could not be found on host mnotter
Interface MRIConvert failed to run.
In [ ]:
from nipype.interfaces.freesurfer import MRIConvert
convert = MRIConvert(in_file='/data/ds102/sub-01/anat/sub-01_T1w.nii.gz',
out_type='nii')
Or if you try to use SPM, but forgot to tell Nipype where to find it. If you forgot to tell the system where to find MATLAB (or MCR), than you will get same kind of error as above. But if you forgot to specify which SPM you want to use, you'll get the following RuntimeError
:
Standard error:
MATLAB code threw an exception:
SPM not in matlab path
You can solve this issue by specifying the path to your SPM version:
from nipype.interfaces.matlab import MatlabCommand
MatlabCommand.set_default_paths('/usr/local/MATLAB/R2017a/toolbox/spm12')
In [ ]:
from nipype.interfaces.spm import Realign
realign = Realign(register_to_mean=True)
realign.run()
This gives you the error:
ValueError: Realign requires a value for input 'in_files'. For a list of required inputs, see Realign.help()
As described by the error text, if we use the help()
function, we can actually see, which inputs are mandatory and which are optional.
In [ ]:
realign.help()
In [ ]:
from nipype.interfaces.afni import Despike
despike = Despike(in_file='../../ds102/sub-01/func/sub-01_task-flanker_run-1_bold.nii.gz',
output_type='NIFTI')
despike.run()
This results in the TraitError
:
TraitError: Cannot set the undefined 'output_type' attribute of a 'DespikeInputSpec' object.
So what went wrong? If you use the help()
function, you will see that the correct input filed is called outputtype
and not output_type
.
In [ ]:
from nipype import SelectFiles, Node, Workflow
from os.path import abspath as opap
from nipype.interfaces.fsl import MCFLIRT, IsotropicSmooth
# Create SelectFiles node
templates={'func': '{subject_id}/func/{subject_id}_task-flanker_run-1_bold.nii.gz'}
sf = Node(SelectFiles(templates),
name='selectfiles')
sf.inputs.base_directory = opap('/data/ds102')
sf.inputs.subject_id = 'sub-01'
# Create Motion Correction Node
mcflirt = Node(MCFLIRT(mean_vol=True,
save_plots=True),
name='mcflirt')
# Create Smoothing node
smooth = Node(IsotropicSmooth(fwhm=4),
name='smooth')
# Create a preprocessing workflow
wf = Workflow(name="preprocWF")
wf.base_dir = 'working_dir'
# Connect the three nodes to each other
wf.connect([(sf, mcflirt, [("func", "in_file")]),
(mcflirt, smooth, [("out_file", "in_file")])])
Now, let's create a new node and connect it to the already occupied input field in_file
of the smooth
node:
In [ ]:
# Create a new node
mcflirt_NEW = Node(MCFLIRT(mean_vol=True),
name='mcflirt_NEW')
# Connect it to an already connected input field
wf.connect([(mcflirt_NEW, smooth, [("out_file", "in_file")])])
This will lead to the error:
Exception:
Trying to connect preprocWF.mcflirt_NEW:out_file to preprocWF.smooth:in_file but input 'in_file' of node 'preprocWF.smooth' is already connected.