Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REF: Split final boldref generation from BOLD-BOLD resampling, eliminate extra per-echo computation #2181

Merged
merged 17 commits into from
Aug 18, 2020

Conversation

tsalo
Copy link
Collaborator

@tsalo tsalo commented Jun 6, 2020

Closes #2175. It appears that dropping the tedana-based mask in #2109 led to un-joined echo-specific masks being used after echo combination throughout the workflow. This led to many redundant nodes in the workflow being created, including multiple runs of ICA-AROMA, although those nodes should be duplicates of one another except for the difference, if any, in masks.

Changes proposed in this pull request

  • Move final reference image creation step out of init_bold_preproc_trans_wf and into init_func_preproc_wf. The workflow is called final_boldref_wf.
  • Rename bold_reference_wf in init_func_preproc_wf to initial_boldref_wf.
  • Replace bold_files in join_echos JoinNode with reference native-space BOLD files from bold_bold_trans_wf.
  • Add skullstripped_bold_files to join_echos JoinNode, to combine skullstripped BOLD files from skullstrip_bold_wf and feed them to bold_t2s_wf. (These were the bold_files in join_echos before.)

Documentation that should be reviewed

None

Copy link
Member

@oesteban oesteban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we rebase this PR on the head of #1803? I think yielding until that one is merged will help clarify this.

@@ -11,3 +11,22 @@ def check_deps(workflow):
for node in workflow._get_all_nodes()
if (hasattr(node.interface, '_cmd') and
which(node.interface._cmd.split()[0]) is None))


def select_first(in_files):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use niworkflows.utils.connections.pop_file instead (requires nipreps/niworkflows#408 to be merged)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to use the first echo's brain mask, though? I am using it to fix the bug, but I don't know if it's the appropriate choice.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can also replace fmriprep.workflows.bold.resampling._first with pop_file as well.

@tsalo tsalo marked this pull request as ready for review June 9, 2020 14:44
Copy link
Member

@oesteban oesteban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I see the problem now. Because we run the bold resampling workflow as an iterable, we are generating one mask per echo, regardless of the changes in niworkflows to use the first echo only.

Although this looks like it fixes the problem, I believe that we are running some unnecessary stuff in the bold resampling workflow we should avoid (starting with the masking of each echo).

Please allow me some time to draft how the bold transform workflow could be improved and send a PR to your branch.

fmriprep/workflows/bold/base.py Show resolved Hide resolved
@effigies
Copy link
Member

Not to step on @oesteban's toes, but it looks like this would be good to get into the next release, if it's not too far from ready. I can plan to review tomorrow.

@tsalo Would you be willing to rebase on top of the refactors in #2239? Sorry to do this to you, but I think we've almost certainly interfered with your changes.

@effigies
Copy link
Member

Test failures are related to nipreps/niworkflows#556.

@effigies
Copy link
Member

Tests fixed.

Copy link
Member

@effigies effigies left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. The connections look good. I also see what @oesteban was saying, which is that we are calculating new masks for each echo, and throwing away all but the first:

https://github.com/poldracklab/fmriprep/blob/5d8fe4880cbdd2e2ac7fffb7db0740adbfdcedfe/fmriprep/workflows/bold/resampling.py#L518-L520

The simple solution is to remove this from init_bold_preproc_trans_wf, and put it directly into init_func_preproc_wf, connecting from init_bold_preproc_trans_wf.outputs.outputnode.bold or join_echos.outputs.bold_files. There might be a more clever way to do it without moving the workflow, but I'm not sure that cleverness would be worth it.

WDYT?

fmriprep/workflows/bold/base.py Outdated Show resolved Hide resolved
@tsalo
Copy link
Collaborator Author

tsalo commented Aug 17, 2020

The simple solution is to remove this from init_bold_preproc_trans_wf, and put it directly into init_func_preproc_wf, connecting from init_bold_preproc_trans_wf.outputs.outputnode.bold or join_echos.outputs.bold_files. There might be a more clever way to do it without moving the workflow, but I'm not sure that cleverness would be worth it.

To clarify, init_func_preproc_wf has an initial bold_reference_wf even before STC. As part of the reference image estimation, there is a quick HMC, but not STC or SDC, right?

Then, I think the one in init_bold_preproc_trans_wf is calculated after STC+HMC+SDC.

If so, then we could label these as "first pass" and "second pass" reference image estimation, correct?

In any case, I think that moving the second run of the reference workflow to init_func_preproc_wf makes sense.

@effigies
Copy link
Member

If so, then we could label these as "first pass" and "second pass" reference image estimation, correct?

Yes, that makes sense to me. I might call them initial_boldref_wf and final_boldref_wf, rather than leave ambiguity as to whether to expect a third pass, but as long as it's clear, I'm happy.

@tsalo
Copy link
Collaborator Author

tsalo commented Aug 17, 2020

Awesome! I think I can handle the refactor, but would it be better to have it here or in a separate PR?

Co-authored-by: Chris Markiewicz <[email protected]>
@effigies
Copy link
Member

I don't think it makes any difference to me. I would just have a look and see how much of this PR would need to be undone, and whether it makes sense to start fresh from master or keep going here.

@pep8speaks
Copy link

pep8speaks commented Aug 17, 2020

Hello @tsalo! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻

Comment last updated at 2020-08-18 13:39:55 UTC

@tsalo
Copy link
Collaborator Author

tsalo commented Aug 17, 2020

@effigies. Okay, I think I got it working. It did involve switching a lot of the stuff in the PR back, but I don't think that's too much of a problem. We'll see how the tests do.

@effigies
Copy link
Member

Crashed...

200817-20:37:33,217 nipype.workflow INFO:
	 [Node] Setting-up "fmriprep_wf.single_subject_02_wf.func_preproc_task_cuedSGT_run_01_echo_1_wf.bold_t2smap_wf.t2smap_node" in "/scratch/fmriprep_wf/single_subject_02_wf/func_preproc_task_cuedSGT_run_01_echo_1_wf/bold_t2smap_wf/t2smap_node".
exception calling callback for <Future at 0x7ff94ab8d0b8 state=finished raised FileNotFoundError>
concurrent.futures.process._RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/plugins/multiproc.py", line 67, in run_node
    result["result"] = node.run(updatehash=updatehash)
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py", line 486, in run
    self._get_hashval()
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py", line 538, in _get_hashval
    self._get_inputs()
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py", line 609, in _get_inputs
    self.set_input(key, deepcopy(output_value))
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py", line 302, in set_input
    setattr(self.inputs, parameter, deepcopy(val))
  File "/usr/local/miniconda/lib/python3.7/site-packages/traits/trait_types.py", line 2338, in validate
    self.error( object, name, value )
  File "/usr/local/miniconda/lib/python3.7/site-packages/traits/trait_handlers.py", line 172, in error
    value )
traits.trait_errors.TraitError: The 'in_files' trait of a T2SMapInputSpec instance must be a list of at least 3 items which are a pathlike object or string representing an existing file, but a value of '/scratch/fmriprep_wf/single_subject_02_wf/func_preproc_task_cuedSGT_run_01_echo_1_wf/skullstrip_bold_wf/_bold_file_..data..sub-02..func..sub-02_task-cuedSGT_run-01_echo-3_bold.nii.gz/apply_mask/vol0000_xform-00000_merged_masked.nii.gz' <class 'str'> was specified.

Error setting node input:
Node: t2smap_node
input: in_files
results_file: /scratch/fmriprep_wf/single_subject_02_wf/func_preproc_task_cuedSGT_run_01_echo_1_wf/join_echos/result_join_echos.pklz
value: /scratch/fmriprep_wf/single_subject_02_wf/func_preproc_task_cuedSGT_run_01_echo_1_wf/skullstrip_bold_wf/_bold_file_..data..sub-02..func..sub-02_task-cuedSGT_run-01_echo-3_bold.nii.gz/apply_mask/vol0000_xform-00000_merged_masked.nii.gz

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/miniconda/lib/python3.7/concurrent/futures/process.py", line 232, in _process_worker
    r = call_item.fn(*call_item.args, **call_item.kwargs)
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/plugins/multiproc.py", line 70, in run_node
    result["result"] = node.result
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py", line 217, in result
    op.join(self.output_dir(), "result_%s.pklz" % self.name)
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/engine/utils.py", line 291, in load_resultfile
    raise FileNotFoundError(results_file)
FileNotFoundError: /scratch/fmriprep_wf/single_subject_02_wf/func_preproc_task_cuedSGT_run_01_echo_1_wf/bold_t2smap_wf/t2smap_node/result_t2smap_node.pklz
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/miniconda/lib/python3.7/concurrent/futures/_base.py", line 324, in _invoke_callbacks
    callback(self)
  File "/usr/local/miniconda/lib/python3.7/site-packages/nipype/pipeline/plugins/multiproc.py", line 159, in _async_callback
    result = args.result()
  File "/usr/local/miniconda/lib/python3.7/concurrent/futures/_base.py", line 425, in result
    return self.__get_result()
  File "/usr/local/miniconda/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
FileNotFoundError: /scratch/fmriprep_wf/single_subject_02_wf/func_preproc_task_cuedSGT_run_01_echo_1_wf/bold_t2smap_wf/t2smap_node/result_t2smap_node.pklz

The issue isn't obvious to me. Could just be a cache that needs clearing.

@emdupre
Copy link
Collaborator

emdupre commented Aug 18, 2020

Could just be a cache that needs clearing.

I think it's a genuine error... The T2* workflow expects a list (with a minimum of 3 entries), but a string is being passed. I wonder if join_echos now needs another JoinField

@effigies
Copy link
Member

Good catch. JoinNodes.

@effigies
Copy link
Member

Okay, looks like this is working. Nice! I'll review for aesthetic/style stuff, and I think we can call this done.

Copy link
Member

@effigies effigies left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly suggestions to compact the diff. One verification.

Would you rather we merge by squashing or merge commit?

fmriprep/workflows/bold/base.py Outdated Show resolved Hide resolved
fmriprep/workflows/bold/base.py Show resolved Hide resolved
fmriprep/workflows/bold/base.py Outdated Show resolved Hide resolved
fmriprep/workflows/bold/base.py Outdated Show resolved Hide resolved
('outputnode.bold_mask', 'inputnode.bold_mask_native')]),
(bold_bold_trans_wf if not multiecho else bold_t2s_wf, outputnode, [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a semantic change, not just a reorganization. Was this something we were doing wrong?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, yes, I think it was wrong before, since it took the BOLD files from bold_bold_trans_wf for the native-space BOLD outputs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through, I agree with this change. If I'm reading it right, a native BOLD space derivative for MEEPI will be the optimal combination, which seems appropriate.

fmriprep/workflows/bold/base.py Outdated Show resolved Hide resolved
Thanks @effigies.

Co-authored-by: Chris Markiewicz <[email protected]>
@tsalo
Copy link
Collaborator Author

tsalo commented Aug 18, 2020

Generally speaking, I prefer squash and merge. It's a fairly cohesive set of changes, so the individual commits don't provide any extra information, IMHO. Especially given the pivot in approach.

@tsalo tsalo changed the title FIX: Use first echo's mask for multi-echo data FIX, REF: Move final reference image creation out of init_bold_preproc_trans_wf Aug 18, 2020
@tsalo tsalo changed the title FIX, REF: Move final reference image creation out of init_bold_preproc_trans_wf REF: Move final reference image creation out of init_bold_preproc_trans_wf Aug 18, 2020
Copy link
Member

@effigies effigies left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. We'll let the CI run through one more time to be safe, but I think we're good to go, and anybody with permissions should feel free to squash and merge.

@effigies effigies changed the title REF: Move final reference image creation out of init_bold_preproc_trans_wf REF: Split final boldref generation from BOLD-BOLD resampling, eliminate extra per-echo computation Aug 18, 2020
@tsalo tsalo merged commit 726584d into nipreps:master Aug 18, 2020
@tsalo tsalo mentioned this pull request Oct 4, 2021
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ME-EPI] Confirm that MELODIC is run on the optimally combined echo
5 participants