Skip to content

[ENH] Issue 3345: Adding FreeSurfer longitudinal interfaces #3529

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

Merged
merged 18 commits into from
Jul 5, 2023

Conversation

l-espana
Copy link
Contributor

Summary

Adds FreeSurfer longitudinal interfaces per issue #3345.

List of changes proposed in this PR (pull-request)

  • Add BaseReconAll to allow running recon-all with timepoints to create a base template.
  • Add LongReconAll to allow running recon-all for a session given a base template.

@codecov
Copy link

codecov bot commented Nov 30, 2022

Codecov Report

Patch coverage: 40.74% and project coverage change: -0.02 ⚠️

Comparison is base (a9e25e1) 63.20% compared to head (78d580b) 63.19%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3529      +/-   ##
==========================================
- Coverage   63.20%   63.19%   -0.02%     
==========================================
  Files         308      308              
  Lines       40770    40792      +22     
  Branches     5480     5645     +165     
==========================================
+ Hits        25770    25779       +9     
  Misses      14000    14000              
- Partials     1000     1013      +13     
Impacted Files Coverage Δ
nipype/interfaces/freesurfer/preprocess.py 63.88% <30.43%> (-0.54%) ⬇️
nipype/interfaces/freesurfer/longitudinal.py 93.33% <100.00%> (+0.18%) ⬆️

... and 11 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@l-espana l-espana marked this pull request as ready for review November 30, 2022 16:29
Copy link
Contributor

@ghisvail ghisvail left a comment

Choose a reason for hiding this comment

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

This looks very good. I like your proposal for separating the base and long interfaces from the main one, which avoids overcharging the main interface which is already quite complex.

I was wondering whether you had tested a complete Nipype workflow for FS longitudinal composed of all three tasks? I have made a suggestion which could improve composition.


>>> from nipype.interfaces.freesurfer.longitudinal import LongReconAll
>>> longrecon = LongReconAll()
>>> longrecon.inputs.long_id = ("ses-1","sub-template")
Copy link
Contributor

Choose a reason for hiding this comment

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

From a workflow construction perspective, providing long_id as tuple might make composition from all, base and then long interfaces more difficult.

In pydra-freesurfer, I have split it as longitudinal_timepoint_id and longitudinal_template_id and required the latter be specified if the former is. This way they are both exposed as separate input to the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, that's a very good point. I can separate those out. I tend to run each step separately, but I will make that change and test out a full workflow with all three steps when I can...should definitely make sure that works with this setup.

@l-espana
Copy link
Contributor Author

Update on this: I was able to successfully run a nipype workflow with all longitudinal steps. (Not sure if I should document the relevant pieces of that somewhere). I think I can remove the [WIP], not sure if I need to update anything else or if anybody else has comments, perhaps @0rC0? I know you had worked on this previously as well...

@l-espana l-espana changed the title [WIP][ENH] Issue 3345: Adding FreeSurfer longitudinal interfaces [ENH] Issue 3345: Adding FreeSurfer longitudinal interfaces Dec 14, 2022
@ghisvail
Copy link
Contributor

Update on this: I was able to successfully run a nipype workflow with all longitudinal steps.

That's great.

Not sure if I should document the relevant pieces of that somewhere

One suggestion would be to post a working snippet here with like all 3 instances of ReconAll, BaseReconAll and LongReconAll wired together?

@l-espana
Copy link
Contributor Author

This is hopefully enough to help people get started and should be close enough to what I actually ran...

from nipype.interfaces.freesurfer.preprocess import ReconAll
from nipype.interfaces.freesurfer.longitudinal import BaseReconAll, LongReconall
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
from niworkflows.utils.bids import collect_data

bids_dir = "."
subject_data = collect_data(bids_dir, "sub-1")[0]
freesurfer_dir = "derivatives/freesurfer"
wf = Workflow(name="longitudinal_freesurfer")

initrecon = pe.MapNode(interface=ReconAll(directive="all", subjects_dir=freesurfer_dir), 
    name="initrecon", iterfield=["T1_files", "subject_id"])
initrecon.inputs.subject_id = ["ses-1","ses-2"]
initrecon.inputs.T1_files = subject_data["t1w"]

baserecon = pe.Node(BaseReconAll(base_id="sub-1", directive="all", subjects_dir=freesurfer_dir), 
    name="baserecon")

longrecon = pe.MapNode(LongReconAll(directive="all", subjects_dir=freesurfer_dir), 
    name="longrecon", iterfield=["long_id"])
longrecon.inputs.long_id = ["ses-1","ses-2"]

wf.connect([
    (initrecon, baserecon, [("subject_id", "timepoints")]),
    (baserecon, longrecon, [("subject_id", "base_id")]),
])

.zenodo.json Outdated
Comment on lines 893 to 897
},
{
"affiliation": "Department of Neurosurgery, Medical College of Wisconsin",
"name": "Espana, Lezlie",
"orcid": "0000-0002-6466-4653"
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd suggest to modify the zenodo file within its own commit. Also, I believe you should keep Satra as last author.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh of course, my mistake.

Copy link
Contributor

Choose a reason for hiding this comment

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

No problem

Copy link
Contributor

@ghisvail ghisvail 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 initial proposal. This first iteration looks very good and your snippet shows very well how it can be all composed.

I have a few suggestions wrt to namings, constraints and some typos.

l-espana and others added 4 commits December 15, 2022 07:21
Co-authored-by: Ghislain Vaillant <ghisvail@users.noreply.github.com>
Co-authored-by: Ghislain Vaillant <ghisvail@users.noreply.github.com>
Co-authored-by: Ghislain Vaillant <ghisvail@users.noreply.github.com>
Co-authored-by: Ghislain Vaillant <ghisvail@users.noreply.github.com>
@ghisvail
Copy link
Contributor

ghisvail commented Dec 15, 2022

My suggestions did not include updates to the doctests, so they will likely fail. Could you update them please?

You will also need to refresh the test suite generated automatically from the specs.

@l-espana
Copy link
Contributor Author

Yes, working on that now. :)

@l-espana
Copy link
Contributor Author

l-espana commented Dec 15, 2022

I don't know that we can take out the subject_id from the input spec...We're inheriting from the ReconAll spec which has a default subject_id of "recon-all" so I think we need to override it. Unless there's another way to negate that?

Edit: Unless we turn off the "usedefault" in the original spec of course. Was just trying not to mess too much with the original.

@l-espana l-espana requested a review from ghisvail December 20, 2022 16:26
@ghisvail
Copy link
Contributor

@l-espana l-espana requested a review from ghisvail

I will have a look at it next week.

I don't know that we can take out the subject_id from the input spec

If we can't, then that may be a sign Base and Long specs should be merged into the main ReconAll with some appropriate xor logic. That's what I did for the Pydra interface and it worked.

@effigies
Copy link
Member

Please merge master to fix the tests.

@0rC0
Copy link
Contributor

0rC0 commented Dec 27, 2022

I was able to successfully run a nipype workflow with all longitudinal steps.
great! :-)

not sure if I need to update anything else or if anybody else has comments, perhaps @0rC0? I know you had worked on this previously as well...

It looks very good to me. At the beginning of the new year I can do some more tests.

@l-espana
Copy link
Contributor Author

l-espana commented Apr 5, 2023

Hi! Any chance we can get this reviewed?

@effigies
Copy link
Member

effigies commented Apr 5, 2023

@ghisvail Were you reviewing this? Or would you like me to?

Copy link
Contributor

@ghisvail ghisvail left a comment

Choose a reason for hiding this comment

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

One last suggestion to improve the robustness of the interface.

The rest looks good to me. Nice work 👏

@@ -927,6 +930,29 @@ class ReconAllInputSpec(CommandLineInputSpec):
)
flags = InputMultiPath(traits.Str, argstr="%s", desc="additional parameters")

# Longitudinal runs
Copy link
Contributor

Choose a reason for hiding this comment

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

We might need to introduce additional constraints to avoid using cross-sectional specific arguments with base and long modes.

I am thinking of the T1_files, T2_file and FLAIR_file parameters at least. These should probably get a requirement on subject_id, which would achieve what we want thanks to your xor constraints.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That didn't cross my mind but excellent thought. I've add some requires and will test some variations next week to make sure that works as expected.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good to me 👍

@l-espana l-espana requested a review from ghisvail April 12, 2023 19:38
Copy link
Contributor

@ghisvail ghisvail left a comment

Choose a reason for hiding this comment

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

Looks good to be merged. I have highlighted a few formatting nuggets which black might complain about.

Co-authored-by: Ghislain Vaillant <ghisvail@users.noreply.github.com>
@ghisvail
Copy link
Contributor

@effigies I don't have the privileges to merge this PR. Could you take care of it? 🙏

@effigies
Copy link
Member

Looks like CI is broken; apologies. @ghisvail If you have time to look into this, it would be appreciated. Otherwise I'll get to it this week.

@ghisvail
Copy link
Contributor

Looks like CI is broken; apologies. @ghisvail If you have time to look into this, it would be appreciated. Otherwise I'll get to it this week.

It looks like issues with CI started to surface on the last two merged PRs. CI complains that jobs no longer find black or pytest although they appear in the install step in the logs. The only reason I could think of is that the venv is no longer visible or activated between calls to the CI scripts (perhaps as a result of a change in GitHub Actions behaviour)?

Perhaps we could refactor CI to not use a dedicated venv? Maybe there are good reasons for it, I have just never seen it done in practice with GHA since each job is executed in a separate throwaway container anyway. It might also make things considerably simpler, as I find the current flow of CI scripts quite hard to follow. Let me know what you think.

@effigies
Copy link
Member

effigies commented Jul 5, 2023

Been a bit, but I got CI working (except CircleCI...) so hopefully any red checks you see are actionable.

@effigies effigies merged commit 68a7cd4 into nipy:master Jul 5, 2023
@effigies effigies mentioned this pull request Mar 20, 2024
6 tasks
@effigies effigies mentioned this pull request Oct 31, 2024
6 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.

4 participants