diff --git a/docs/environment.yml b/docs/environment.yml index 3d770429b..a90962fd5 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -21,7 +21,7 @@ dependencies: - python-dateutil - pydot>=1.2.3 - cython -- nipype>=1.1.6 +- nipype>=1.1.7 - pip: - sphinx-argparse diff --git a/fmriprep/__about__.py b/fmriprep/__about__.py index f4527549a..2a52a7033 100644 --- a/fmriprep/__about__.py +++ b/fmriprep/__about__.py @@ -88,7 +88,7 @@ 'indexed_gzip>=0.8.8', 'nibabel>=2.2.1', 'nilearn', - 'nipype>=1.1.6', + 'nipype>=1.1.7', 'nitime', 'niworkflows>=0.5.1,<0.5.2', 'numpy', diff --git a/fmriprep/interfaces/patches.py b/fmriprep/interfaces/patches.py index 1790efbaf..64f1090ab 100644 --- a/fmriprep/interfaces/patches.py +++ b/fmriprep/interfaces/patches.py @@ -12,49 +12,104 @@ from numpy.linalg.linalg import LinAlgError from nipype.algorithms import confounds as nac +from nipype.interfaces.base import File +from nipype.interfaces.mixins import reporting -class RobustACompCor(nac.ACompCor): - """ - Runs aCompCor several times if it suddenly fails with - https://github.com/poldracklab/fmriprep/issues/776 +class RetryCompCorInputSpecMixin(reporting.ReportCapableInputSpec): + out_report = File('report.html', usedefault=True, hash_files=False, + desc='filename for warning HTML snippet') - """ +class RetryCompCorMixin(reporting.ReportCapableInterface): def _run_interface(self, runtime): + warn = self.inputs.failure_mode == 'NaN' + failures = 0 + save_exc = None while True: + success = True + # Identifiy success/failure in both error and NaN mode try: - runtime = super(RobustACompCor, self)._run_interface(runtime) + runtime = super()._run_interface(runtime) + if warn and self._is_allnans(): + success = False + except LinAlgError as exc: + success = False + save_exc = exc + + if success: break - except LinAlgError: - failures += 1 - if failures > 10: - raise - start = (failures - 1) * 10 - sleep(randint(start + 4, start + 10)) + + failures += 1 + if failures > 10: + if warn: + break + raise save_exc + start = (failures - 1) * 10 + sleep(randint(start + 4, start + 10)) return runtime + def _is_allnans(self): + import numpy as np + outputs = self._list_outputs() + components = np.loadtxt(outputs['components_file'], skiprows=1) + return np.isnan(components).all() + + def _generate_report(self): + snippet = ''.format(self._header) + if self._is_allnans(): + snippet = '''\ +
+ Warning: {} components could not be estimated, due to a linear algebra error. + While not definitive, this may be an indication of a poor mask. + Please inspect the {} contours above to ensure that they are located + in the white matter/CSF. +
+'''.format(self._header, 'magenta' if self._header[0] == 'a' else 'blue') + + with open(self._out_report, 'w') as fobj: + fobj.write(snippet) -class RobustTCompCor(nac.TCompCor): + +class RobustACompCorInputSpec(RetryCompCorInputSpecMixin, nac.CompCorInputSpec): + pass + + +class RobustACompCorOutputSpec(reporting.ReportCapableOutputSpec, nac.CompCorOutputSpec): + pass + + +class RobustACompCor(RetryCompCorMixin, nac.ACompCor): """ - Runs tCompCor several times if it suddenly fails with - https://github.com/poldracklab/fmriprep/issues/940 + Runs aCompCor several times if it suddenly fails with + https://github.com/poldracklab/fmriprep/issues/776 + + Warns by default, rather than failing, on linear algebra errors. + https://github.com/poldracklab/fmriprep/issues/1433 """ + input_spec = RobustACompCorInputSpec + output_spec = RobustACompCorOutputSpec - def _run_interface(self, runtime): - failures = 0 - while True: - try: - runtime = super(RobustTCompCor, self)._run_interface(runtime) - break - except LinAlgError: - failures += 1 - if failures > 10: - raise - start = (failures - 1) * 10 - sleep(randint(start + 4, start + 10)) - return runtime +class RobustTCompCorInputSpec(RetryCompCorInputSpecMixin, nac.TCompCorInputSpec): + pass + + +class RobustTCompCorOutputSpec(reporting.ReportCapableOutputSpec, nac.TCompCorOutputSpec): + pass + + +class RobustTCompCor(RetryCompCorMixin, nac.TCompCor): + """ + Runs tCompCor several times if it suddenly fails with + https://github.com/poldracklab/fmriprep/issues/776 + + Warns by default, rather than failing, on linear algebra errors. + https://github.com/poldracklab/fmriprep/issues/1433 + + """ + input_spec = RobustTCompCorInputSpec + output_spec = RobustTCompCorOutputSpec diff --git a/fmriprep/viz/config.json b/fmriprep/viz/config.json index 235ab5ce7..72eafdea0 100644 --- a/fmriprep/viz/config.json +++ b/fmriprep/viz/config.json @@ -98,6 +98,16 @@ "title": "ROIs in BOLD space", "description": "Brain mask calculated on the BOLD signal (red contour), along with the masks used for a/tCompCor.