Skip to content

issues with afni.allineate w/ solution (and possible issue with genfile parameter) #2499

Closed
@vanandrew

Description

@vanandrew

Summary

Hi all. Just started learning nipype, and it's been pretty great so far. I've only run into issues with the afni.allineate interface. I was able to fix it on my own, but I thought I should post an issue here for awareness (plus I'm not sure how to do a pull request...)

There are 2 problems I've encountered. The first is that afni.allineate consistently fails the hash check when placed in a workflow and always reruns. I think this is because the hash_files parameter needs to be set to False.

out_file = File(
    desc='output file from 3dAllineate',
    argstr='-prefix %s',
    genfile=True,
    hash_files=False, # <-- when this is added it starts working again
    xor=['allcostx']) 

Referencing the docs, I think this is an appropriate change:

hash_files
To be used with inputs that are defining output filenames. When this flag is set to false any Nipype will not try to hash any files described by this input. This is useful to avoid rerunning when the specified output file already exists and has changed.

The second issue is that the prefix (out_file) argument does not get generated when used in a workflow. This issue is also referenced here: #2216, but with a temporary workaround.

My understanding is that when genfile is set to True, the _gen_filename() method for the interface gets called when the user does not specify a value. Looking at the code, the _gen_filename() does seems to implemented, but not correctly:

def _gen_filename(self, name): # this gets called when `out_file` is not defined
    if name == 'out_file':
        return self._list_outputs()[name] # <-- we are returning the value of `out_file`, which is undefined...
    return None

So I changed it to this:

def _gen_filename(self, name): # this gets called when `out_file` is not defined
    if name == 'out_file':
        return self._gen_fname(self.inputs.in_file,op.dirname(self.inputs.in_file),suffix='_allineate')
    return None

Which should define the out_file to be in the same location and have the same name as the in_file, but with the '_allineate' suffix.

When I run this, the problem seems to be fixed, as the prefix is now defined:

[Node] Running "3dallineate_orig" ("nipype.interfaces.afni.preprocess.Allineate"), a CommandLine Interface with command:
3dAllineate -source /home/vana/Projects/p3/tmp/P3/_subject_id_sub-CTS200/3dallineate_orig/orig_out.nii.gz -prefix /home/vana/Projects/p3/tmp/P3/_subject_id_sub-CTS200/3dallineate_orig/orig_out_allineate.nii.gz -1Dmatrix_save FSorig.XFM.FS2MPR.aff12.1D -overwrite -base /home/vana/Projects/p3/dataset/sub-CTS200/anat/sub-CTS200_T1w.nii.gz

But downstream nodes don't seem to notice that the out_file is actually defined now... (Note: I have a node whose in_file is connected to the out_file of the allineate node)

 [Node] Error on "P3.3drefit1" (/home/vana/Projects/p3/tmp/P3/_subject_id_sub-CTS200/3drefit1)
Traceback (most recent call last):
  File "./preproc.py", line 293, in <module>
    wf.run()
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/pipeline/engine/workflows.py", line 602, in run
    runner.run(execgraph, updatehash=updatehash, config=self.config)
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/pipeline/plugins/linear.py", line 44, in run
    node.run(updatehash=updatehash)
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 487, in run
    result = self._run_interface(execute=True)
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 571, in _run_interface
    return self._run_command(execute)
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/pipeline/engine/nodes.py", line 638, in _run_command
    cmd = self._interface.cmdline
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/interfaces/base/core.py", line 935, in cmdline
    self._check_mandatory_inputs()
  File "/home/vana/.local/lib/python3.6/site-packages/nipype/interfaces/base/core.py", line 389, in _check_mandatory_inputs
    raise ValueError(msg)
ValueError: Refit requires a value for input 'in_file'. For a list of required inputs, see Refit.help()

After looking at some of the fsl interfaces (and some trial-and-error), I figured out that the downstream nodes seem to be getting the information for their inputs from the _list_outputs method of the upstream node, and ignore _gen_filename. So when I do this:

def _gen_outfilename(self):
    out_file = self.inputs.out_file
    if not isdefined(out_file) and isdefined(self.inputs.in_file) and not isdefined(self.inputs.allcostx):
        out_file = op.abspath(self._gen_fname(self.inputs.in_file,op.dirname(self.inputs.in_file),suffix='_allineate'))
    return out_file

def _list_outputs(self):
    outputs = self.output_spec().get()

    outputs['out_file'] = self._gen_outfilename()

    # other stuff below this ...

def _gen_filename(self, name):
    if name == 'out_file':
        return self._gen_outfilename()
    return None

Everything works! But then the question becomes, what's the point of the genfile parameter if I have to define a method that checks if the input is defined anyway?

I'm not sure how to do a pull request to this repo, so I've attached my git patch below. (This is on nipype 1.0.1)
allineate.patch.tar.gz

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions