Repository: nipreps/niworkflows
Issue: MELODIC fails but returns with exit code zero

This error has surfaced with MRIQC only this far, but I expect it to appear also for fMRIPrep with `--ica-aroma`.


Command line:
```
singularity exec /home/groups/russpold/singularity_images/poldracklab_mriqc_0.15.0-2019-04-05-a4441b87badc.simg melodic -i /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_bold_valid.nii.gz --nobet --nomask --no_mm -o /scratch/users/oesteban/mphagen/work-1105/melodic-ica -v --debug
```

This is the standard output (with both `-v` and `--debug`, empty otherwise):
```
Melodic Version 3.14
Melodic results will be in /scratch/users/oesteban/mphagen/work-1105/melodic-ica
START: setup
START: setup_misc
END: setup_misc
 after setup_misc START: setup_classic
START: process_file
 before reading file /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_b
old_validReading data file /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_r
un-1_bold_valid  ...  done
 after reading file /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_bo
ld_valid before est smoothness   Estimating data smoothness ...  done 
 after smoothness  before reshape  after reshape   Removing mean image ... before remmean  after remmean  done
 before VN   Normalising by voxel-wise variance ... in VN  done
END: process_file
 after process_file Excluding voxels with constant value ... done
  /scratch/users/oesteban/mphagen/work-1105/melodic-ica/alldat
Finished with exit code 0
```

Standard error:
```
     stdDev  0 x 0
An exception has been thrown
Logic error:- detected by Newmat: index error: requested index = 0
MatrixType = Rect   # Rows = 1; # Cols = 982
```

And finally, `log.txt`:
```
Melodic Version 3.14
melodic -i /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_bold_valid.
nii.gz --nobet --nomask --no_mm -o /scratch/users/oesteban/mphagen/work-1105/melodic-ica 
---------------------------------------------
Melodic results will be in /scratch/users/oesteban/mphagen/work-1105/melodic-ica
Reading data file /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_bold
_valid  ...  done
  Estimating data smoothness ...  done 
  Removing mean image ... done
  Normalising by voxel-wise variance ... done
Excluding voxels with constant value ... done
Melodic Version 3.14
melodic -i /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_bold_valid.
nii.gz --nobet --nomask --no_mm -o /scratch/users/oesteban/mphagen/work-1105/melodic-ica -v --debug 
---------------------------------------------
Melodic results will be in /scratch/users/oesteban/mphagen/work-1105/melodic-ica
Reading data file /scratch/users/oesteban/mphagen/work-1105/workflow_enumerator/funcMRIQC/_in_file_..oak..stanford..groups..russpold..data..uh2_aim2..BIDS_scans..sub-1105..func..sub-1105_task-motorSelectiveStop_run-1_bold.nii.gz/sanitize/sub-1105_task-motorSelectiveStop_run-1_bold
_valid  ...  done
  Estimating data smoothness ...  done 
  Removing mean image ... done
  Normalising by voxel-wise variance ... done
Excluding voxels with constant value ... done
  /scratch/users/oesteban/mphagen/work-1105/melodic-ica/alldat
```

Some [other people](https://www.jiscmail.ac.uk/cgi-bin/webadmin?A2=ind1803&L=fsl&D=0&P=145318) seem [to have hit this issue and MELODIC 3.15 _might_ solve the issue](https://www.mail-archive.com/hcp-users@humanconnectome.org/msg06937.html).

Changed files:
niworkflows/interfaces/segmentation.py

diff --git a/niworkflows/interfaces/segmentation.py b/niworkflows/interfaces/segmentation.py
index e43c7040..54504a84 100644
--- a/niworkflows/interfaces/segmentation.py
+++ b/niworkflows/interfaces/segmentation.py
@@ -9,7 +9,7 @@ ReportCapableInterfaces for segmentation tools
 from __future__ import absolute_import, division, print_function, unicode_literals
 import os
 
-from nipype.interfaces.base import File
+from nipype.interfaces.base import File, isdefined
 from nipype.interfaces import fsl, freesurfer
 from nipype.interfaces.mixins import reporting
 from . import report_base as nrc
@@ -106,37 +106,63 @@ class MELODICOutputSpecRPT(reporting.ReportCapableOutputSpec,
     pass
 
 
-class MELODICRPT(reporting.ReportCapableInterface, fsl.MELODIC):
+class MELODICRPT(fsl.MELODIC):
     input_spec = MELODICInputSpecRPT
     output_spec = MELODICOutputSpecRPT
+    _out_report = None
+
+    def __init__(self, generate_report=False, **kwargs):
+        super(MELODICRPT, self).__init__(**kwargs)
+        self.generate_report = generate_report
+
+    def _post_run_hook(self, runtime):
+        # Run _post_run_hook of super class
+        runtime = super(MELODICRPT, self)._post_run_hook(runtime)
+        # leave early if there's nothing to do
+        if not self.generate_report:
+            return runtime
+
+        NIWORKFLOWS_LOG.info('Generating report for MELODIC.')
+        _melodic_dir = runtime.cwd
+        if isdefined(self.inputs.out_dir):
+            _melodic_dir = self.inputs.out_dir
+        self._melodic_dir = os.path.abspath(_melodic_dir)
+
+        self._out_report = self.inputs.out_report
+        if not os.path.isabs(self._out_report):
+            self._out_report = os.path.abspath(os.path.join(runtime.cwd,
+                                                            self._out_report))
 
-    def _generate_report(self):
-        from niworkflows.viz.utils import plot_melodic_components
         mix = os.path.join(self._melodic_dir, "melodic_mix")
         if not os.path.exists(mix):
-            self._out_report = self.inputs.out_report.replace('svg', 'html')
+            NIWORKFLOWS_LOG.warning("MELODIC outputs not found, assuming it didn't converge.")
+            self._out_report = self._out_report.replace('.svg', '.html')
             snippet = '<h4>MELODIC did not converge, no output</h4>'
             with open(self._out_report, 'w') as fobj:
                 fobj.write(snippet)
-            return
+            return runtime
+
+        self._generate_report()
+        return runtime
+
+    def _list_outputs(self):
+        try:
+            outputs = super(MELODICRPT, self)._list_outputs()
+        except NotImplementedError:
+            outputs = {}
+        if self._out_report is not None:
+            outputs['out_report'] = self._out_report
+        return outputs
+
+    def _generate_report(self):
+        from niworkflows.viz.utils import plot_melodic_components
         plot_melodic_components(melodic_dir=self._melodic_dir,
                                 in_file=self.inputs.in_files[0],
                                 tr=self.inputs.tr_sec,
-                                out_file=self.inputs.out_report,
+                                out_file=self._out_report,
                                 compress=self.inputs.compress_report,
                                 report_mask=self.inputs.report_mask)
 
-    def _post_run_hook(self, runtime):
-        ''' generates a report showing nine slices, three per axis, of an
-        arbitrary volume of `in_files`, with the resulting segmentation
-        overlaid '''
-        outputs = self.aggregate_outputs(runtime=runtime)
-        self._melodic_dir = outputs.out_dir
-
-        NIWORKFLOWS_LOG.info('Generating report for MELODIC')
-
-        return super(MELODICRPT, self)._post_run_hook(runtime)
-
 
 class ICA_AROMAInputSpecRPT(nrc.SVGReportCapableInputSpec,
                             fsl.aroma.ICA_AROMAInputSpec):