Repository: nipy/nipype

Issue:
ANTS interface: incorrect parameter passing in WarpTimeSeriesImageMultiTransform
### Summary
when using WarpTimeSeriesImageMultiTransform.inputs.invert_affine = [1], the parameter passed to the corresponding ANTS command is ['-i'], and should instead be '-i' (as in WarpImageMutiTransfrom) in resampling.py

### Actual behavior
The command fails to run or to write with .cmdline
(TypeError: sequence item 0: expected string or Unicode, list found)

### Expected behavior
In [8]: wimt.cmdline
Out[8]: u'WarpTimeSeriesImageMultiTransform 4 my_fixed_time_series.nii.gz my_fixed_time_series_warped.nii.gz -R my_moving_image.nii.gz -i synAffine.txt synInverseWarp.nii.gz'

### How to replicate the behavior
just run WarpTimeSeriesImageMultiTransform with .inputs.invert_affine = [1]

### Script/Workflow details
l.89:                         series += ['-i'], -> series += '-i',

### Platform details:

{'nibabel_version': '2.1.0', 'sys_executable': '/usr/bin/python', 'networkx_version': '1.11', 'numpy_version': '1.11.1rc1', 'sys_platform': 'linux2', 'sys_version': '2.7.12+ (default, Sep 17 2016, 12:08:02) \n[GCC 6.2.0 20160914]', 'commit_source': u'repository', 'commit_hash': '3f8c8b1', 'pkg_path': '/mnt/drive/pilou/Code/github/src/nipype/nipype', 'nipype_version': u'1.0.0-dev+g3f8c8b1', 'traits_version': '4.6.0', 'scipy_version': '0.17.1'}
1.0.0-dev+g3f8c8b1

### Execution environment

Choose one
My python environment outside container



Discussion:
Hi, thanks for reporting. I'm reading the code and I believe that changing `['-i']` to `-i` in [line 89](https://github.com/nipy/nipype/blob/master/nipype/interfaces/ants/resampling.py#L89) shouldn't help, since [line 91](https://github.com/nipy/nipype/blob/master/nipype/interfaces/ants/resampling.py#L89) would add a space between `-` and `i`. 

I might be wrong, never run this interface. Would you mind sharing the code for `wimt` so I can test it? The simplest examples I run didn't give me an error. 

What exactly do you get when you ask for `wimt.cmdline`? 
@chrisfilo : having problem with repeating the error, but wanted to ask if you really wanted to have `series += '-i'` (not `series += ['-i']`) in `WarpImageMultiTransform`, see [198](https://github.com/nipy/nipype/blob/master/nipype/interfaces/ants/resampling.py#L198)
I'm not 100% sure, but `series += ['-i']` does seem to make more sense.

Resolving patch:
diff --git a/nipype/interfaces/ants/resampling.py b/nipype/interfaces/ants/resampling.py
--- a/nipype/interfaces/ants/resampling.py
+++ b/nipype/interfaces/ants/resampling.py
@@ -43,9 +43,12 @@ class WarpTimeSeriesImageMultiTransformInputSpec(ANTSCommandInputSpec):
                                            desc='transformation file(s) to be applied',
                                            mandatory=True, copyfile=False)
     invert_affine = traits.List(traits.Int,
-                                desc=('List of Affine transformations to invert. '
+                                desc=('List of Affine transformations to invert.'
                                       'E.g.: [1,4,5] inverts the 1st, 4th, and 5th Affines '
-                                      'found in transformation_series'))
+                                      'found in transformation_series. Note that indexing '
+                                      'starts with 1 and does not include warp fields. Affine '
+                                      'transformations are distinguished '
+                                      'from warp fields by the word "affine" included in their filenames.'))
 
 
 class WarpTimeSeriesImageMultiTransformOutputSpec(TraitedSpec):
@@ -67,6 +70,14 @@ class WarpTimeSeriesImageMultiTransform(ANTSCommand):
     'WarpTimeSeriesImageMultiTransform 4 resting.nii resting_wtsimt.nii -R ants_deformed.nii.gz ants_Warp.nii.gz \
 ants_Affine.txt'
 
+    >>> wtsimt = WarpTimeSeriesImageMultiTransform()
+    >>> wtsimt.inputs.input_image = 'resting.nii'
+    >>> wtsimt.inputs.reference_image = 'ants_deformed.nii.gz'
+    >>> wtsimt.inputs.transformation_series = ['ants_Warp.nii.gz','ants_Affine.txt']
+    >>> wtsimt.inputs.invert_affine = [1] # # this will invert the 1st Affine file: ants_Affine.txt
+    >>> wtsimt.cmdline # doctest: +ALLOW_UNICODE
+    'WarpTimeSeriesImageMultiTransform 4 resting.nii resting_wtsimt.nii -R ants_deformed.nii.gz ants_Warp.nii.gz \
+-i ants_Affine.txt'
     """
 
     _cmd = 'WarpTimeSeriesImageMultiTransform'
@@ -81,13 +92,22 @@ def _format_arg(self, opt, spec, val):
         if opt == 'transformation_series':
             series = []
             affine_counter = 0
+            affine_invert = []
             for transformation in val:
                 if 'Affine' in transformation and \
                         isdefined(self.inputs.invert_affine):
                     affine_counter += 1
                     if affine_counter in self.inputs.invert_affine:
-                        series += ['-i'],
+                        series += ['-i']
+                        affine_invert.append(affine_counter)
                 series += [transformation]
+
+            if isdefined(self.inputs.invert_affine):
+                diff_inv = set(self.inputs.invert_affine) - set(affine_invert)
+                if diff_inv:
+                    raise Exceptions("Review invert_affine, not all indexes from invert_affine were used, "
+                                     "check the description for the full definition")
+
             return ' '.join(series)
         return super(WarpTimeSeriesImageMultiTransform, self)._format_arg(opt, spec, val)
 
@@ -168,7 +188,7 @@ class WarpImageMultiTransform(ANTSCommand):
     >>> wimt.inputs.reference_image = 'functional.nii'
     >>> wimt.inputs.transformation_series = ['func2anat_coreg_Affine.txt','func2anat_InverseWarp.nii.gz', \
     'dwi2anat_Warp.nii.gz','dwi2anat_coreg_Affine.txt']
-    >>> wimt.inputs.invert_affine = [1]
+    >>> wimt.inputs.invert_affine = [1]  # this will invert the 1st Affine file: 'func2anat_coreg_Affine.txt'
     >>> wimt.cmdline # doctest: +ALLOW_UNICODE
     'WarpImageMultiTransform 3 diffusion_weighted.nii diffusion_weighted_wimt.nii -R functional.nii \
 -i func2anat_coreg_Affine.txt func2anat_InverseWarp.nii.gz dwi2anat_Warp.nii.gz dwi2anat_coreg_Affine.txt'
@@ -190,14 +210,24 @@ def _format_arg(self, opt, spec, val):
         if opt == 'transformation_series':
             series = []
             affine_counter = 0
+            affine_invert = []
             for transformation in val:
                 if "affine" in transformation.lower() and \
                         isdefined(self.inputs.invert_affine):
                     affine_counter += 1
                     if affine_counter in self.inputs.invert_affine:
-                        series += '-i',
+                        series += ['-i']
+                        affine_invert.append(affine_counter)
                 series += [transformation]
+
+            if isdefined(self.inputs.invert_affine):
+                diff_inv = set(self.inputs.invert_affine) - set(affine_invert)
+                if diff_inv:
+                    raise Exceptions("Review invert_affine, not all indexes from invert_affine were used, "
+                                     "check the description for the full definition")
+
             return ' '.join(series)
+
         return super(WarpImageMultiTransform, self)._format_arg(opt, spec, val)
 
     def _list_outputs(self):


Test patch:
diff --git a/nipype/interfaces/ants/tests/test_resampling.py b/nipype/interfaces/ants/tests/test_resampling.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/ants/tests/test_resampling.py
@@ -0,0 +1,83 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:                                                                                                                 
+
+from nipype.interfaces.ants import WarpImageMultiTransform, WarpTimeSeriesImageMultiTransform
+import os
+import pytest
+
+
+@pytest.fixture()
+def change_dir(request):
+   orig_dir = os.getcwd()
+   filepath = os.path.dirname( os.path.realpath( __file__ ) )
+   datadir = os.path.realpath(os.path.join(filepath, '../../../testing/data'))
+   os.chdir(datadir)
+
+   def move2orig():
+      os.chdir(orig_dir)
+
+   request.addfinalizer(move2orig)
+
+
+@pytest.fixture()
+def create_wimt():
+   wimt = WarpImageMultiTransform()
+   wimt.inputs.input_image = 'diffusion_weighted.nii'
+   wimt.inputs.reference_image = 'functional.nii'
+   wimt.inputs.transformation_series = ['func2anat_coreg_Affine.txt','func2anat_InverseWarp.nii.gz', \
+                                           'dwi2anat_Warp.nii.gz','dwi2anat_coreg_Affine.txt']
+   return wimt
+
+def test_WarpImageMultiTransform(change_dir, create_wimt):
+   wimt = create_wimt
+   assert wimt.cmdline == 'WarpImageMultiTransform 3 diffusion_weighted.nii diffusion_weighted_wimt.nii -R functional.nii \
+func2anat_coreg_Affine.txt func2anat_InverseWarp.nii.gz dwi2anat_Warp.nii.gz dwi2anat_coreg_Affine.txt'
+
+
+def test_WarpImageMultiTransform_invaffine_1(change_dir, create_wimt):
+   wimt = create_wimt
+   wimt.inputs.invert_affine = [1]
+   assert wimt.cmdline == 'WarpImageMultiTransform 3 diffusion_weighted.nii diffusion_weighted_wimt.nii -R functional.nii \
+-i func2anat_coreg_Affine.txt func2anat_InverseWarp.nii.gz dwi2anat_Warp.nii.gz dwi2anat_coreg_Affine.txt'
+
+
+def test_WarpImageMultiTransform_invaffine_2(change_dir, create_wimt):
+   wimt = create_wimt
+   wimt.inputs.invert_affine = [2]
+   assert wimt.cmdline == 'WarpImageMultiTransform 3 diffusion_weighted.nii diffusion_weighted_wimt.nii -R functional.nii func2anat_coreg_Affine.txt func2anat_InverseWarp.nii.gz dwi2anat_Warp.nii.gz -i dwi2anat_coreg_Affine.txt'
+
+
+def test_WarpImageMultiTransform_invaffine_wrong(change_dir, create_wimt):
+   wimt = create_wimt
+   wimt.inputs.invert_affine = [3]
+   with pytest.raises(Exception):
+      assert wimt.cmdline
+
+
+@pytest.fixture()
+def create_wtsimt():
+   wtsimt = WarpTimeSeriesImageMultiTransform()
+   wtsimt.inputs.input_image = 'resting.nii'
+   wtsimt.inputs.reference_image = 'ants_deformed.nii.gz'
+   wtsimt.inputs.transformation_series = ['ants_Warp.nii.gz','ants_Affine.txt']
+   return wtsimt
+
+
+def test_WarpTimeSeriesImageMultiTransform(change_dir, create_wtsimt):
+   wtsimt = create_wtsimt
+   assert wtsimt.cmdline == 'WarpTimeSeriesImageMultiTransform 4 resting.nii resting_wtsimt.nii \
+-R ants_deformed.nii.gz ants_Warp.nii.gz ants_Affine.txt'                                                                                            
+
+
+def test_WarpTimeSeriesImageMultiTransform_invaffine(change_dir, create_wtsimt):
+   wtsimt = create_wtsimt
+   wtsimt.inputs.invert_affine = [1]
+   assert wtsimt.cmdline == 'WarpTimeSeriesImageMultiTransform 4 resting.nii resting_wtsimt.nii \
+-R ants_deformed.nii.gz ants_Warp.nii.gz -i ants_Affine.txt'
+
+
+def test_WarpTimeSeriesImageMultiTransform_invaffine_wrong(change_dir, create_wtsimt):
+   wtsimt = create_wtsimt
+   wtsimt.inputs.invert_affine = [0]
+   with pytest.raises(Exception):
+      wtsimt.cmdline