Repository: nipy/nipype

Issue:
[enhancement] MRCat interface for mrtrix3's mrcat function
### Summary
Would be nice to have an interface for mrtrix3's mrcat function as other alternatives (fslmerge etc.) can't handle the mif format.

### Expected behavior

`import nipype.interfaces.mrtrix3 as mrt`
`mrcat = mrt.MRCat()`
`mrcat.inputs.in_files = ['image1.mif','image2.mif']`
`mrcat.cmdline`
mrcat image1.mif image2.mif concatenated.mif
`mrcat.run()`

### Platform details:

<!-- Please run the following code from your shell and place the output between the triple ticks, below.
python -c "import nipype; from pprint import pprint; pprint(nipype.get_info())"
-->

```
{'commit_hash': 'aa64ccdfb',
 'commit_source': 'repository',
 'networkx_version': '2.6.3',
 'nibabel_version': '3.2.1',
 'nipype_version': '1.7.1-dev+gaa64ccdfb',
 'numpy_version': '1.21.3',
 'pkg_path': '/home/groot/Projects/nipype/nipype',
 'scipy_version': '1.7.1',
 'sys_executable': '/home/groot/Projects/dwiprep/venv/bin/python',
 'sys_platform': 'linux',
 'sys_version': '3.9.5 (default, May 19 2021, 11:32:47) \n[GCC 9.3.0]',
 'traits_version': '6.3.1'}
```

[ENH] Mrtrix3 enhancements
## Summary
Made some changes and additions to Mrtrix3's interfaces in the nipype platform.

Fixes #3400 and #3402 .

## List of changes proposed in this PR (pull-request)

- Added json and gradient table-related  kwargs to *mrconvert* interface.
- Added *mrcat*interface.
- Added *mrtransform* interface.

## Acknowledgment

- [] \(Mandatory\) I acknowledge that this contribution will be available under the Apache 2 license.



Discussion:

Resolving patch:
diff --git a/nipype/interfaces/mrtrix3/__init__.py b/nipype/interfaces/mrtrix3/__init__.py
--- a/nipype/interfaces/mrtrix3/__init__.py
+++ b/nipype/interfaces/mrtrix3/__init__.py
@@ -2,29 +2,32 @@
 # vi: set ft=python sts=4 ts=4 sw=4 et:
 # -*- coding: utf-8 -*-
 """MRTrix3 provides software tools to perform various types of diffusion MRI analyses."""
+from .connectivity import BuildConnectome, LabelConfig, LabelConvert
+from .preprocess import (
+    ACTPrepareFSL,
+    DWIBiasCorrect,
+    DWIDenoise,
+    DWIPreproc,
+    MRDeGibbs,
+    ReplaceFSwithFIRST,
+    ResponseSD,
+)
+from .reconst import ConstrainedSphericalDeconvolution, EstimateFOD, FitTensor
+from .tracking import Tractography
 from .utils import (
-    Mesh2PVE,
-    Generate5tt,
+    TCK2VTK,
     BrainMask,
-    TensorMetrics,
     ComputeTDI,
-    TCK2VTK,
-    MRMath,
+    DWIExtract,
+    Generate5tt,
+    Mesh2PVE,
+    MRCat,
     MRConvert,
+    MRMath,
     MRResize,
-    DWIExtract,
-    SHConv,
+    MRTransform,
     SH2Amp,
+    SHConv,
+    TensorMetrics,
+    TransformFSLConvert,
 )
-from .preprocess import (
-    ResponseSD,
-    ACTPrepareFSL,
-    ReplaceFSwithFIRST,
-    DWIPreproc,
-    DWIDenoise,
-    MRDeGibbs,
-    DWIBiasCorrect,
-)
-from .tracking import Tractography
-from .reconst import FitTensor, EstimateFOD, ConstrainedSphericalDeconvolution
-from .connectivity import LabelConfig, LabelConvert, BuildConnectome
diff --git a/nipype/interfaces/mrtrix3/base.py b/nipype/interfaces/mrtrix3/base.py
--- a/nipype/interfaces/mrtrix3/base.py
+++ b/nipype/interfaces/mrtrix3/base.py
@@ -79,6 +79,15 @@ class MRTrix3BaseInputSpec(CommandLineInputSpec):
         exists=True, argstr="-fslgrad %s %s", desc="bvecs file in FSL format"
     )
     in_bval = File(exists=True, desc="bvals file in FSL format")
+    out_bvec = File(
+        exists=False,
+        argstr="-export_grad_fsl %s %s",
+        desc="export bvec file in FSL format",
+    )
+    out_bval = File(
+        exists=False,
+        desc="export bval file in FSL format",
+    )
 
 
 class MRTrix3Base(CommandLine):
@@ -96,6 +105,8 @@ def _format_arg(self, name, trait_spec, value):
 
         if name == "in_bvec":
             return trait_spec.argstr % (value, self.inputs.in_bval)
+        if name == "out_bvec":
+            return trait_spec.argstr % (value, self.inputs.out_bval)
 
         return super(MRTrix3Base, self)._format_arg(name, trait_spec, value)
 
diff --git a/nipype/interfaces/mrtrix3/utils.py b/nipype/interfaces/mrtrix3/utils.py
--- a/nipype/interfaces/mrtrix3/utils.py
+++ b/nipype/interfaces/mrtrix3/utils.py
@@ -4,6 +4,7 @@
 
 import os.path as op
 
+from ...utils.filemanip import split_filename
 from ..base import (
     CommandLineInputSpec,
     CommandLine,
@@ -64,6 +65,101 @@ def _list_outputs(self):
         return outputs
 
 
+class MRCatInputSpec(MRTrix3BaseInputSpec):
+    in_files = traits.List(
+        File(exists=True),
+        argstr="%s",
+        position=-2,
+        mandatory=True,
+        desc="files to concatenate",
+    )
+
+    out_file = File(
+        "concatenated.mif",
+        argstr="%s",
+        mandatory=True,
+        position=-1,
+        usedefault=True,
+        desc="output concatenated image",
+    )
+
+    axis = traits.Int(
+        argstr="-axis %s",
+        desc="""specify axis along which concatenation should be performed. By default,
+     the program will use the last non-singleton, non-spatial axis of any of
+     the input images - in other words axis 3 or whichever axis (greater than
+     3) of the input images has size greater than one""",
+    )
+
+    datatype = traits.Enum(
+        "float32",
+        "float32le",
+        "float32be",
+        "float64",
+        "float64le",
+        "float64be",
+        "int64",
+        "uint64",
+        "int64le",
+        "uint64le",
+        "int64be",
+        "uint64be",
+        "int32",
+        "uint32",
+        "int32le",
+        "uint32le",
+        "int32be",
+        "uint32be",
+        "int16",
+        "uint16",
+        "int16le",
+        "uint16le",
+        "int16be",
+        "uint16be",
+        "cfloat32",
+        "cfloat32le",
+        "cfloat32be",
+        "cfloat64",
+        "cfloat64le",
+        "cfloat64be",
+        "int8",
+        "uint8",
+        "bit",
+        argstr="-datatype %s",
+        desc="specify output image data type",
+    )
+
+
+class MRCatOutputSpec(TraitedSpec):
+    out_file = File(exists=True, desc="the output concatenated image")
+
+
+class MRCat(CommandLine):
+    """
+    Concatenate several images into one
+
+
+    Example
+    -------
+
+    >>> import nipype.interfaces.mrtrix3 as mrt
+    >>> mrcat = mrt.MRCat()
+    >>> mrcat.inputs.in_files = ['dwi.mif','mask.mif']
+    >>> mrcat.cmdline                               # doctest: +ELLIPSIS
+    'mrcat dwi.mif mask.mif concatenated.mif'
+    >>> mrcat.run()                                 # doctest: +SKIP
+    """
+
+    _cmd = "mrcat"
+    input_spec = MRCatInputSpec
+    output_spec = MRCatOutputSpec
+
+    def _list_outputs(self):
+        outputs = self.output_spec().get()
+        outputs["out_file"] = op.abspath(self.inputs.out_file)
+        return outputs
+
+
 class Mesh2PVEInputSpec(CommandLineInputSpec):
     in_file = File(
         exists=True, argstr="%s", mandatory=True, position=-3, desc="input mesh"
@@ -173,11 +269,20 @@ def _list_outputs(self):
 
 class TensorMetricsInputSpec(CommandLineInputSpec):
     in_file = File(
-        exists=True, argstr="%s", mandatory=True, position=-1, desc="input DTI image"
+        exists=True,
+        argstr="%s",
+        mandatory=True,
+        position=-1,
+        desc="input DTI image",
     )
 
     out_fa = File(argstr="-fa %s", desc="output FA file")
     out_adc = File(argstr="-adc %s", desc="output ADC file")
+    out_ad = File(argstr="-ad %s", desc="output AD file")
+    out_rd = File(argstr="-rd %s", desc="output RD file")
+    out_cl = File(argstr="-cl %s", desc="output CL file")
+    out_cp = File(argstr="-cp %s", desc="output CP file")
+    out_cs = File(argstr="-cs %s", desc="output CS file")
     out_evec = File(argstr="-vector %s", desc="output selected eigenvector(s) file")
     out_eval = File(argstr="-value %s", desc="output selected eigenvalue(s) file")
     component = traits.List(
@@ -209,6 +314,11 @@ class TensorMetricsInputSpec(CommandLineInputSpec):
 class TensorMetricsOutputSpec(TraitedSpec):
     out_fa = File(desc="output FA file")
     out_adc = File(desc="output ADC file")
+    out_ad = File(desc="output AD file")
+    out_rd = File(desc="output RD file")
+    out_cl = File(desc="output CL file")
+    out_cp = File(desc="output CP file")
+    out_cs = File(desc="output CS file")
     out_evec = File(desc="output selected eigenvector(s) file")
     out_eval = File(desc="output selected eigenvalue(s) file")
 
@@ -580,10 +690,28 @@ class MRConvertInputSpec(MRTrix3BaseInputSpec):
         argstr="-scaling %s",
         desc="specify the data scaling parameter",
     )
+    json_import = File(
+        exists=True,
+        argstr="-json_import %s",
+        mandatory=False,
+        desc="import data from a JSON file into header key-value pairs",
+    )
+    json_export = File(
+        exists=False,
+        argstr="-json_export %s",
+        mandatory=False,
+        desc="export data from an image header key-value pairs into a JSON file",
+    )
 
 
 class MRConvertOutputSpec(TraitedSpec):
     out_file = File(exists=True, desc="output image")
+    json_export = File(
+        exists=True,
+        desc="exported data from an image header key-value pairs in a JSON file",
+    )
+    out_bvec = File(exists=True, desc="export bvec file in FSL format")
+    out_bval = File(exists=True, desc="export bvec file in FSL format")
 
 
 class MRConvert(MRTrix3Base):
@@ -610,9 +738,182 @@ class MRConvert(MRTrix3Base):
     def _list_outputs(self):
         outputs = self.output_spec().get()
         outputs["out_file"] = op.abspath(self.inputs.out_file)
+        if self.inputs.json_export:
+            outputs["json_export"] = op.abspath(self.inputs.json_export)
+        if self.inputs.out_bvec:
+            outputs["out_bvec"] = op.abspath(self.inputs.out_bvec)
+        if self.inputs.out_bval:
+            outputs["out_bval"] = op.abspath(self.inputs.out_bval)
+        return outputs
+
+
+class TransformFSLConvertInputSpec(MRTrix3BaseInputSpec):
+    in_file = File(
+        exists=True,
+        argstr="%s",
+        mandatory=True,
+        position=1,
+        desc="FLIRT input image",
+    )
+    reference = File(
+        exists=True,
+        argstr="%s",
+        mandatory=True,
+        position=2,
+        desc="FLIRT reference image",
+    )
+    in_transform = File(
+        exists=True,
+        argstr="%s",
+        mandatory=True,
+        position=0,
+        desc="FLIRT output transformation matrix",
+    )
+    out_transform = File(
+        "transform_mrtrix.txt",
+        argstr="%s",
+        mandatory=True,
+        position=-1,
+        usedefault=True,
+        desc="output transformed affine in mrtrix3's format",
+    )
+    flirt_import = traits.Bool(
+        True,
+        argstr="flirt_import",
+        mandatory=True,
+        usedefault=True,
+        position=-2,
+        desc="import transform from FSL's FLIRT.",
+    )
+
+
+class TransformFSLConvertOutputSpec(TraitedSpec):
+    out_transform = File(
+        exists=True, desc="output transformed affine in mrtrix3's format"
+    )
+
+
+class TransformFSLConvert(MRTrix3Base):
+    """
+    Perform conversion between FSL's transformation matrix format to mrtrix3's.
+    """
+
+    _cmd = "transformconvert"
+    input_spec = TransformFSLConvertInputSpec
+    output_spec = TransformFSLConvertOutputSpec
+
+    def _list_outputs(self):
+        outputs = self.output_spec().get()
+        outputs["out_transform"] = op.abspath(self.inputs.out_transform)
         return outputs
 
 
+class MRTransformInputSpec(MRTrix3BaseInputSpec):
+    in_files = InputMultiPath(
+        File(exists=True),
+        argstr="%s",
+        mandatory=True,
+        position=-2,
+        desc="Input images to be transformed",
+    )
+    out_file = File(
+        genfile=True,
+        argstr="%s",
+        position=-1,
+        desc="Output image",
+    )
+    invert = traits.Bool(
+        argstr="-inverse",
+        position=1,
+        desc="Invert the specified transform before using it",
+    )
+    linear_transform = File(
+        exists=True,
+        argstr="-linear %s",
+        position=1,
+        desc=(
+            "Specify a linear transform to apply, in the form of a 3x4 or 4x4 ascii file. "
+            "Note the standard reverse convention is used, "
+            "where the transform maps points in the template image to the moving image. "
+            "Note that the reverse convention is still assumed even if no -template image is supplied."
+        ),
+    )
+    replace_transform = traits.Bool(
+        argstr="-replace",
+        position=1,
+        desc="replace the current transform by that specified, rather than applying it to the current transform",
+    )
+    transformation_file = File(
+        exists=True,
+        argstr="-transform %s",
+        position=1,
+        desc="The transform to apply, in the form of a 4x4 ascii file.",
+    )
+    template_image = File(
+        exists=True,
+        argstr="-template %s",
+        position=1,
+        desc="Reslice the input image to match the specified template image.",
+    )
+    reference_image = File(
+        exists=True,
+        argstr="-reference %s",
+        position=1,
+        desc="in case the transform supplied maps from the input image onto a reference image, use this option to specify the reference. Note that this implicitly sets the -replace option.",
+    )
+    flip_x = traits.Bool(
+        argstr="-flipx",
+        position=1,
+        desc="assume the transform is supplied assuming a coordinate system with the x-axis reversed relative to the MRtrix convention (i.e. x increases from right to left). This is required to handle transform matrices produced by FSL's FLIRT command. This is only used in conjunction with the -reference option.",
+    )
+    quiet = traits.Bool(
+        argstr="-quiet",
+        position=1,
+        desc="Do not display information messages or progress status.",
+    )
+    debug = traits.Bool(argstr="-debug", position=1, desc="Display debugging messages.")
+
+
+class MRTransformOutputSpec(TraitedSpec):
+    out_file = File(exists=True, desc="the output image of the transformation")
+
+
+class MRTransform(MRTrix3Base):
+    """
+    Apply spatial transformations or reslice images
+
+    Example
+    -------
+
+    >>> MRxform = MRTransform()
+    >>> MRxform.inputs.in_files = 'anat_coreg.mif'
+    >>> MRxform.run()                                   # doctest: +SKIP
+    """
+
+    _cmd = "mrtransform"
+    input_spec = MRTransformInputSpec
+    output_spec = MRTransformOutputSpec
+
+    def _list_outputs(self):
+        outputs = self.output_spec().get()
+        outputs["out_file"] = self.inputs.out_file
+        if not isdefined(outputs["out_file"]):
+            outputs["out_file"] = op.abspath(self._gen_outfilename())
+        else:
+            outputs["out_file"] = op.abspath(outputs["out_file"])
+        return outputs
+
+    def _gen_filename(self, name):
+        if name == "out_file":
+            return self._gen_outfilename()
+        else:
+            return None
+
+    def _gen_outfilename(self):
+        _, name, _ = split_filename(self.inputs.in_files[0])
+        return name + "_MRTransform.mif"
+
+
 class MRMathInputSpec(MRTrix3BaseInputSpec):
     in_file = File(
         exists=True, argstr="%s", mandatory=True, position=-3, desc="input image"


Test patch:
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_BrainMask.py b/nipype/interfaces/mrtrix3/tests/test_auto_BrainMask.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_BrainMask.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_BrainMask.py
@@ -40,6 +40,13 @@ def test_BrainMask_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_ConstrainedSphericalDeconvolution.py b/nipype/interfaces/mrtrix3/tests/test_auto_ConstrainedSphericalDeconvolution.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_ConstrainedSphericalDeconvolution.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_ConstrainedSphericalDeconvolution.py
@@ -77,6 +77,13 @@ def test_ConstrainedSphericalDeconvolution_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         predicted_signal=dict(
             argstr="-predicted_signal %s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_DWIBiasCorrect.py b/nipype/interfaces/mrtrix3/tests/test_auto_DWIBiasCorrect.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_DWIBiasCorrect.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_DWIBiasCorrect.py
@@ -48,6 +48,13 @@ def test_DWIBiasCorrect_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_DWIDenoise.py b/nipype/interfaces/mrtrix3/tests/test_auto_DWIDenoise.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_DWIDenoise.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_DWIDenoise.py
@@ -55,6 +55,13 @@ def test_DWIDenoise_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_DWIExtract.py b/nipype/interfaces/mrtrix3/tests/test_auto_DWIExtract.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_DWIExtract.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_DWIExtract.py
@@ -46,6 +46,13 @@ def test_DWIExtract_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_DWIPreproc.py b/nipype/interfaces/mrtrix3/tests/test_auto_DWIPreproc.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_DWIPreproc.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_DWIPreproc.py
@@ -56,6 +56,13 @@ def test_DWIPreproc_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_EstimateFOD.py b/nipype/interfaces/mrtrix3/tests/test_auto_EstimateFOD.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_EstimateFOD.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_EstimateFOD.py
@@ -80,6 +80,13 @@ def test_EstimateFOD_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         predicted_signal=dict(
             argstr="-predicted_signal %s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_FitTensor.py b/nipype/interfaces/mrtrix3/tests/test_auto_FitTensor.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_FitTensor.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_FitTensor.py
@@ -47,6 +47,13 @@ def test_FitTensor_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_Generate5tt.py b/nipype/interfaces/mrtrix3/tests/test_auto_Generate5tt.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_Generate5tt.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_Generate5tt.py
@@ -45,6 +45,13 @@ def test_Generate5tt_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRCat.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRCat.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRCat.py
@@ -0,0 +1,80 @@
+# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
+from ..utils import MRCat
+
+
+def test_MRCat_inputs():
+    input_map = dict(
+        args=dict(
+            argstr="%s",
+        ),
+        axis=dict(
+            argstr="-axis %s",
+        ),
+        bval_scale=dict(
+            argstr="-bvalue_scaling %s",
+        ),
+        datatype=dict(
+            argstr="-datatype %s",
+        ),
+        environ=dict(
+            nohash=True,
+            usedefault=True,
+        ),
+        grad_file=dict(
+            argstr="-grad %s",
+            extensions=None,
+            xor=["grad_fsl"],
+        ),
+        grad_fsl=dict(
+            argstr="-fslgrad %s %s",
+            xor=["grad_file"],
+        ),
+        in_bval=dict(
+            extensions=None,
+        ),
+        in_bvec=dict(
+            argstr="-fslgrad %s %s",
+            extensions=None,
+        ),
+        in_files=dict(
+            argstr="%s",
+            mandatory=True,
+            position=-2,
+        ),
+        nthreads=dict(
+            argstr="-nthreads %d",
+            nohash=True,
+        ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
+        out_file=dict(
+            argstr="%s",
+            extensions=None,
+            mandatory=True,
+            position=-1,
+            usedefault=True,
+        ),
+    )
+    inputs = MRCat.input_spec()
+
+    for key, metadata in list(input_map.items()):
+        for metakey, value in list(metadata.items()):
+            assert getattr(inputs.traits()[key], metakey) == value
+
+
+def test_MRCat_outputs():
+    output_map = dict(
+        out_file=dict(
+            extensions=None,
+        ),
+    )
+    outputs = MRCat.output_spec()
+
+    for key, metadata in list(output_map.items()):
+        for metakey, value in list(metadata.items()):
+            assert getattr(outputs.traits()[key], metakey) == value
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRConvert.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRConvert.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_MRConvert.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRConvert.py
@@ -44,10 +44,27 @@ def test_MRConvert_inputs():
             mandatory=True,
             position=-2,
         ),
+        json_export=dict(
+            argstr="-json_export %s",
+            extensions=None,
+            mandatory=False,
+        ),
+        json_import=dict(
+            argstr="-json_import %s",
+            extensions=None,
+            mandatory=False,
+        ),
         nthreads=dict(
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
@@ -73,6 +90,15 @@ def test_MRConvert_inputs():
 
 def test_MRConvert_outputs():
     output_map = dict(
+        json_export=dict(
+            extensions=None,
+        ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            extensions=None,
+        ),
         out_file=dict(
             extensions=None,
         ),
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRDeGibbs.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRDeGibbs.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_MRDeGibbs.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRDeGibbs.py
@@ -59,6 +59,13 @@ def test_MRDeGibbs_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRMath.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRMath.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_MRMath.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRMath.py
@@ -48,6 +48,13 @@ def test_MRMath_inputs():
             mandatory=True,
             position=-2,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRResize.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRResize.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_MRResize.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRResize.py
@@ -49,6 +49,13 @@ def test_MRResize_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRTransform.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRTransform.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRTransform.py
@@ -0,0 +1,113 @@
+# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
+from ..utils import MRTransform
+
+
+def test_MRTransform_inputs():
+    input_map = dict(
+        args=dict(
+            argstr="%s",
+        ),
+        bval_scale=dict(
+            argstr="-bvalue_scaling %s",
+        ),
+        debug=dict(
+            argstr="-debug",
+            position=1,
+        ),
+        environ=dict(
+            nohash=True,
+            usedefault=True,
+        ),
+        flip_x=dict(
+            argstr="-flipx",
+            position=1,
+        ),
+        grad_file=dict(
+            argstr="-grad %s",
+            extensions=None,
+            xor=["grad_fsl"],
+        ),
+        grad_fsl=dict(
+            argstr="-fslgrad %s %s",
+            xor=["grad_file"],
+        ),
+        in_bval=dict(
+            extensions=None,
+        ),
+        in_bvec=dict(
+            argstr="-fslgrad %s %s",
+            extensions=None,
+        ),
+        in_files=dict(
+            argstr="%s",
+            mandatory=True,
+            position=-2,
+        ),
+        invert=dict(
+            argstr="-inverse",
+            position=1,
+        ),
+        linear_transform=dict(
+            argstr="-linear %s",
+            extensions=None,
+            position=1,
+        ),
+        nthreads=dict(
+            argstr="-nthreads %d",
+            nohash=True,
+        ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
+        out_file=dict(
+            argstr="%s",
+            extensions=None,
+            genfile=True,
+            position=-1,
+        ),
+        quiet=dict(
+            argstr="-quiet",
+            position=1,
+        ),
+        reference_image=dict(
+            argstr="-reference %s",
+            extensions=None,
+            position=1,
+        ),
+        replace_transform=dict(
+            argstr="-replace",
+            position=1,
+        ),
+        template_image=dict(
+            argstr="-template %s",
+            extensions=None,
+            position=1,
+        ),
+        transformation_file=dict(
+            argstr="-transform %s",
+            extensions=None,
+            position=1,
+        ),
+    )
+    inputs = MRTransform.input_spec()
+
+    for key, metadata in list(input_map.items()):
+        for metakey, value in list(metadata.items()):
+            assert getattr(inputs.traits()[key], metakey) == value
+
+
+def test_MRTransform_outputs():
+    output_map = dict(
+        out_file=dict(
+            extensions=None,
+        ),
+    )
+    outputs = MRTransform.output_spec()
+
+    for key, metadata in list(output_map.items()):
+        for metakey, value in list(metadata.items()):
+            assert getattr(outputs.traits()[key], metakey) == value
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_ResponseSD.py b/nipype/interfaces/mrtrix3/tests/test_auto_ResponseSD.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_ResponseSD.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_ResponseSD.py
@@ -68,6 +68,13 @@ def test_ResponseSD_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         wm_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_TensorMetrics.py b/nipype/interfaces/mrtrix3/tests/test_auto_TensorMetrics.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_TensorMetrics.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_TensorMetrics.py
@@ -29,10 +29,26 @@ def test_TensorMetrics_inputs():
         modulate=dict(
             argstr="-modulate %s",
         ),
+        out_ad=dict(
+            argstr="-ad %s",
+            extensions=None,
+        ),
         out_adc=dict(
             argstr="-adc %s",
             extensions=None,
         ),
+        out_cl=dict(
+            argstr="-cl %s",
+            extensions=None,
+        ),
+        out_cp=dict(
+            argstr="-cp %s",
+            extensions=None,
+        ),
+        out_cs=dict(
+            argstr="-cs %s",
+            extensions=None,
+        ),
         out_eval=dict(
             argstr="-value %s",
             extensions=None,
@@ -45,6 +61,10 @@ def test_TensorMetrics_inputs():
             argstr="-fa %s",
             extensions=None,
         ),
+        out_rd=dict(
+            argstr="-rd %s",
+            extensions=None,
+        ),
     )
     inputs = TensorMetrics.input_spec()
 
@@ -55,9 +75,21 @@ def test_TensorMetrics_inputs():
 
 def test_TensorMetrics_outputs():
     output_map = dict(
+        out_ad=dict(
+            extensions=None,
+        ),
         out_adc=dict(
             extensions=None,
         ),
+        out_cl=dict(
+            extensions=None,
+        ),
+        out_cp=dict(
+            extensions=None,
+        ),
+        out_cs=dict(
+            extensions=None,
+        ),
         out_eval=dict(
             extensions=None,
         ),
@@ -67,6 +99,9 @@ def test_TensorMetrics_outputs():
         out_fa=dict(
             extensions=None,
         ),
+        out_rd=dict(
+            extensions=None,
+        ),
     )
     outputs = TensorMetrics.output_spec()
 
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_Tractography.py b/nipype/interfaces/mrtrix3/tests/test_auto_Tractography.py
--- a/nipype/interfaces/mrtrix3/tests/test_auto_Tractography.py
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_Tractography.py
@@ -95,6 +95,13 @@ def test_Tractography_inputs():
             argstr="-nthreads %d",
             nohash=True,
         ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
         out_file=dict(
             argstr="%s",
             extensions=None,
diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_TransformFSLConvert.py b/nipype/interfaces/mrtrix3/tests/test_auto_TransformFSLConvert.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/mrtrix3/tests/test_auto_TransformFSLConvert.py
@@ -0,0 +1,93 @@
+# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
+from ..utils import TransformFSLConvert
+
+
+def test_TransformFSLConvert_inputs():
+    input_map = dict(
+        args=dict(
+            argstr="%s",
+        ),
+        bval_scale=dict(
+            argstr="-bvalue_scaling %s",
+        ),
+        environ=dict(
+            nohash=True,
+            usedefault=True,
+        ),
+        flirt_import=dict(
+            argstr="flirt_import",
+            mandatory=True,
+            position=-2,
+            usedefault=True,
+        ),
+        grad_file=dict(
+            argstr="-grad %s",
+            extensions=None,
+            xor=["grad_fsl"],
+        ),
+        grad_fsl=dict(
+            argstr="-fslgrad %s %s",
+            xor=["grad_file"],
+        ),
+        in_bval=dict(
+            extensions=None,
+        ),
+        in_bvec=dict(
+            argstr="-fslgrad %s %s",
+            extensions=None,
+        ),
+        in_file=dict(
+            argstr="%s",
+            extensions=None,
+            mandatory=True,
+            position=1,
+        ),
+        in_transform=dict(
+            argstr="%s",
+            extensions=None,
+            mandatory=True,
+            position=0,
+        ),
+        nthreads=dict(
+            argstr="-nthreads %d",
+            nohash=True,
+        ),
+        out_bval=dict(
+            extensions=None,
+        ),
+        out_bvec=dict(
+            argstr="-export_grad_fsl %s %s",
+            extensions=None,
+        ),
+        out_transform=dict(
+            argstr="%s",
+            extensions=None,
+            mandatory=True,
+            position=-1,
+            usedefault=True,
+        ),
+        reference=dict(
+            argstr="%s",
+            extensions=None,
+            mandatory=True,
+            position=2,
+        ),
+    )
+    inputs = TransformFSLConvert.input_spec()
+
+    for key, metadata in list(input_map.items()):
+        for metakey, value in list(metadata.items()):
+            assert getattr(inputs.traits()[key], metakey) == value
+
+
+def test_TransformFSLConvert_outputs():
+    output_map = dict(
+        out_transform=dict(
+            extensions=None,
+        ),
+    )
+    outputs = TransformFSLConvert.output_spec()
+
+    for key, metadata in list(output_map.items()):
+        for metakey, value in list(metadata.items()):
+            assert getattr(outputs.traits()[key], metakey) == value