qsiprep
qsiprep copied to clipboard
Error in connecting qsirecon nodes (0.20.1dev)
Summary
QSIRecon errors out upon beginning its workflow. My recon config involves using HSVS MSMT tractography and feeding that into pyafq, computing some different scalars, and running DSI Studio Autotrack
Additional details
- QSIPrep version: 0.20.1.dev17+g89ba85b.d20240305
- Singularity version: Apptainer 1.1.7
What were you trying to do?
Run QSIRecon
What did you expect to happen?
QSIRecon to start
What actually happened?
Running qsirecon version 0.20.1.dev17+g89ba85b.d20240305:
* BIDS dataset path: /om2/scratch/tmp/smeisler/fsub_proc/sub-01/data.
* Participant list: ['01'].
* Run identifier: 20240326-003332_51924ea7-3d11-44b0-92a2-4ddea93aff4c.
240326-00:33:35,108 nipype.workflow INFO:
found ['/om2/scratch/tmp/smeisler/fsub_proc/sub-01/data/derivatives/qsiprep/sub-01/dwi/sub-01_space-T1w_desc-preproc_dwi.nii.gz'] in /om2/scratch/tmp/smeisler/fsub_proc/sub-01/data/derivatives/qsiprep
240326-00:33:35,109 nipype.workflow WARNING:
A Non-gzipped input nifti file was found. Consider gzipping /om2/scratch/tmp/smeisler/fsub_proc/sub-01/data/derivatives/qsiprep/sub-01/anat/sub-01_desc-preproc_T1w.nii
240326-00:33:35,110 nipype.workflow INFO:
Found usable QSIPrep-preprocessed T1w image and mask.
240326-00:33:35,111 nipype.workflow INFO:
Found high-res anatomical data in preprocessed inputs for 01.
240326-00:33:35,112 nipype.workflow INFO:
Freesurfer directory /om2/scratch/tmp/smeisler/fsub_proc/sub-01/data/derivatives/freesurfer/sub-01 exists for 01
240326-00:33:35,114 nipype.workflow INFO:
FreeSurfer data will be used to create a HSVS 5tt image.
240326-00:33:35,115 nipype.workflow INFO:
HSVS 5tt imaged will be registered to the QSIPrep T1w image.
Process Process-36:
Traceback (most recent call last):
File "/usr/local/miniconda/lib/python3.10/multiprocessing/process.py", line 314, in _bootstrap
self.run()
File "/usr/local/miniconda/lib/python3.10/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/miniconda/lib/python3.10/site-packages/qsiprep/cli/run.py", line 1330, in build_recon_workflow
retval["workflow"] = init_qsirecon_wf(
File "/usr/local/miniconda/lib/python3.10/site-packages/qsiprep/workflows/recon/base.py", line 130, in init_qsirecon_wf
single_subject_wf = init_single_subject_wf(
File "/usr/local/miniconda/lib/python3.10/site-packages/qsiprep/workflows/recon/base.py", line 262, in init_single_subject_wf
anat_ingress_node, available_anatomical_data = init_highres_recon_anatomical_wf(
File "/usr/local/miniconda/lib/python3.10/site-packages/qsiprep/workflows/recon/anatomical.py", line 178, in init_highres_recon_anatomical_wf
workflow.connect([
File "/usr/local/miniconda/lib/python3.10/site-packages/nipype/pipeline/engine/workflows.py", line 183, in connect
raise Exception(
Exception: Trying to connect anat_ingress_wf.register_fs_to_qsiprep_wf:outputnode.brain to anat_ingress_wf.outputnode:brain but input 'brain' of node 'anat_ingress_wf.outputnode' is already
connected.
Reproducing the bug
Cmd
singularity run -e --containall -B ${scratch},${templateflow_dir} $qsiprep_IMG \
--participant_label ${subject:4} \
-w $scratch \
--output_resolution 1.25 \
--unringing-method mrdegibbs \
--recon-spec ${scratch}/recon_spec.json \
--freesurfer-input $scratch/data/derivatives/freesurfer/ \
--fs-license-file ${scratch}/license.txt \
--skip-odf-reports \
--notrack \
--mem_mb 239000 \
--nthreads 32 \
--omp-nthreads 16 \
$scratch/data/ $scratch/data/derivatives participant"
Recon config
{
"name": "mrtrix_multishell_msmt_hsvs_pyafq_tractometry",
"space": "T1w",
"atlases": [],
"anatomical": [
"mrtrix_5tt_hsvs"
],
"nodes": [{
"name": "msmt_csd",
"software": "MRTrix3",
"action": "csd",
"output_suffix": "msmtcsd",
"input": "qsiprep",
"parameters": {
"mtnormalize": true,
"response": {
"algorithm": "dhollander"
},
"fod": {
"algorithm": "msmt_csd",
"max_sh": [8, 8, 8]
}
}
},
{
"name": "track_ifod2",
"software": "MRTrix3",
"action": "tractography",
"output_suffix": "ifod2",
"input": "msmt_csd",
"parameters": {
"use_5tt": true,
"method_5tt": "hsvs",
"use_sift2": true,
"tckgen": {
"algorithm": "iFOD2",
"select": 1e7,
"max_length": 250,
"min_length": 30,
"power": 0.33,
"crop_at_gmwmi": true,
"backtrack": true,
"quiet": true
},
"sift2": {}
}
},
{
"name": "pyafq_tractometry",
"software": "pyAFQ",
"action": "pyafq_tractometry",
"input": "track_ifod2",
"output_suffix": "PYAFQ_TRACTOMETRY_ET",
"parameters": {
"use_external_tracking": true,
"export": "all",
"directions": "prob",
"max_angle": 30.0,
"sphere": "",
"seed_mask": "",
"seed_threshold": 0,
"n_seeds": 1,
"random_seeds": false,
"rng_seed": "",
"stop_mask": "",
"stop_threshold": 0,
"step_size": 0.5,
"min_length": 50,
"max_length": 250,
"odf_model": "CSD",
"tracker": "local",
"nb_points": false,
"nb_streamlines": false,
"seg_algo": "AFQ",
"reg_algo": "",
"clip_edges": false,
"parallel_segmentation": "{'n_jobs': -1, 'engine': 'joblib', 'backend': 'loky'}",
"progressive": true,
"greater_than": 50,
"rm_small_clusters": 50,
"model_clust_thr": 1.25,
"reduction_thr": 25,
"refine": false,
"pruning_thr": 12,
"b0_threshold": 50,
"prob_threshold": 0,
"roi_dist_tie_break": false,
"dist_to_waypoint": "",
"rng": "",
"return_idx": false,
"presegment_bundle_dict": "",
"presegment_kwargs": "{}",
"filter_by_endpoints": true,
"dist_to_atlas": 4,
"save_intermediates": "",
"n_points": 100,
"clean_rounds": 5,
"distance_threshold": 3,
"length_threshold": 4,
"min_sl": 20,
"stat": "mean",
"min_bval": "",
"max_bval": "",
"filter_b": true,
"robust_tensor_fitting": false,
"csd_response": "",
"csd_sh_order": "",
"csd_lambda_": 1,
"csd_tau": 0.1,
"gtol": 0.01,
"brain_mask_definition": "",
"bundle_info": "",
"reg_template_spec": "mni_T1",
"mapping_definition": "",
"reg_subject_spec": "power_map",
"profile_weights": "gauss",
"scalars": "['dki_fa', 'dki_md']",
"import_tract": "",
"sbv_lims_bundles": "[None, None]",
"volume_opacity_bundles": 0.3,
"n_points_bundles": 40,
"sbv_lims_indiv": "[None, None]",
"volume_opacity_indiv": 0.3,
"n_points_indiv": 40,
"viz_backend_spec": "plotly_no_gif",
"virtual_frame_buffer": false
}
},
{
"name": "fit_noddi",
"action": "fit_noddi",
"software": "AMICO",
"input": "qsiprep",
"output_suffix": "NODDI",
"parameters": {
"isExvivo": false,
"dPar": 1.7E-3,
"dIso": 3.0E-3
}
},
{
"name": "dki_recon",
"software": "Dipy",
"action": "DKI_reconstruction",
"input": "qsiprep",
"output_suffix": "DKI",
"parameters": {
"write_mif": false,
"write_fibgz": false
}
},
{
"name": "dsistudio_gqi",
"software": "DSI Studio",
"action": "reconstruction",
"input": "qsiprep",
"output_suffix": "gqi",
"parameters": {
"method": "gqi"
}
},
{
"name": "scalar_export",
"software": "DSI Studio",
"action": "export",
"input": "dsistudio_gqi",
"output_suffix": "gqiscalar"
},
{
"name": "autotrackgqi",
"software": "DSI Studio",
"action": "autotrack",
"input": "dsistudio_gqi",
"output_suffix": "AutoTrackGQI",
"parameters": {
"track_id": "Fasciculus,Cingulum,Aslant,Corticos,Thalamic_R,Reticular,Optic,Fornix,Corpus",
"tolerance": "22,26,30",
"track_voxel_ratio": 2.0,
"yield_rate": 0.000001
}
}
]
}
@mattcieslak
Can confirm this bug exists with the vanilla version of hsvs
workflows, both with v. 0.20.0
and the current unstable
tag.
@araikes do you have any data with an open enough license that I could use for CI testing this? I'm looking for qsiprep outputs and a freesurfer directory from the same subject
@mattcieslak I can send you HBN freesurfer data I have, would that work?
@mattcieslak,
I have exactly one dataset that I could conceivably use and make available but it would take me a little bit to get it processed as it's my "phantom" data.
@mattcieslak,
Working on making my dataset ready for sharing but I have question. With the current version of QSIPrep, what is the most correct way to set up the eddy_config.json
file for multiband? Historically, I specifying the slsspec file, but it seems like that's not needed now. My multiband factor is 3 with 69 slices.
In the newest version you can just set mporder to something greater than 0 in your eddy config json file and the slice timings will be passed forward from BIDS to eddy.
@mattcieslak, I have a dataset and can add you as a collaborator on openneuro. It's slowly uploading the QSIPrep outputs as well as freesurfer.
Unless I need to, I'm not trying to publish the dataset publicly at this moment but adding you as a collaborator should enable access. Feel free to email me ([email protected]) if there are questions or other needs as well as with your email address for adding you to the dataset.
@mattcieslak,
Nevermind... went ahead and hit publish. ds005134
has a single subject with both Freesurfer and QSIPrep run. I have tested the HSVS workflows on this particular dataset and they fail for the reason that @smeisler identified above.
Thank you @araikes !!
You're welcome.
Hi @smeisler, Has there been any resolution on this issue? I am currently encountering the same error (see #753) , and would greatly appreciate any advice. Thanks
Hi, @PandaFaye, I think I see the issue (from this file https://github.com/PennLINC/qsiprep/blob/master/qsiprep/workflows/recon/anatomical.py#L132C1-L134C81):
Lines 132-134:
workflow.connect([
(fs_source, outputnode, [
(field, field) for field in FS_FILES_TO_REGISTER])]) <--------
then lines 174-194
workflow.connect([
(anat_ingress_node, register_fs_to_qsiprep_wf, [
("t1_preproc", "inputnode.qsiprep_reference_image"),
("t1_brain_mask", "inputnode.qsiprep_reference_mask")]),
(fs_source, register_fs_to_qsiprep_wf, [
(field, "inputnode." + field) for field in FS_FILES_TO_REGISTER]),
(register_fs_to_qsiprep_wf, outputnode, [
("outputnode.fs_to_qsiprep_transform_mrtrix",
"fs_to_qsiprep_transform_mrtrix"),
("outputnode.fs_to_qsiprep_transform_itk",
"fs_to_qsiprep_transform_itk")] + [
("outputnode." + field, field) for field in FS_FILES_TO_REGISTER]), <--------
(create_5tt_hsvs, apply_header_to_5tt, [("out_file", "in_image")]),
(create_5tt_hsvs, ds_fs_5tt_hsvs, [("out_file", "in_file")]),
(register_fs_to_qsiprep_wf, apply_header_to_5tt, [
("outputnode.fs_to_qsiprep_transform_mrtrix", "transform_file")]),
(apply_header_to_5tt, outputnode, [
("out_image", "qsiprep_5tt_hsvs")]),
(apply_header_to_5tt, ds_qsiprep_5tt_hsvs, [("out_image", "in_file")]),
])
Looks like both of those lines will try to connect a file from FS_FILES_TO_REGISTER
(which includes brain
) to outputnode
, creating the conflict in the error message.
Perhaps the later connection is redundant and can be deleted, but I have not tested this. What do you think @mattcieslak?
@mattcieslak and @smeisler,
Can confirm that L132-134 and L184-185 create the conflict. My inclination would be that removing the latter is more sensible since it's not clear why the FS files are being mapped to the output node there. Removing either set allows the workflow to run.
Also, line 195 throws an error at that point, but that's because workflow.__desc__
doesn't yet have a value so you get a NoneType
+ str
error. Easily fixed by just changing to =
https://github.com/PennLINC/qsiprep/blob/934bb0cde31ae15f5a0ca4888d8bab8aed5f8e53/qsiprep/workflows/recon/anatomical.py#L195
And naturally... crashed out on me as soon as I said that... Two separate issues back to back:
Node: qsirecon_wf.sub-02_mrtrix_multishell_msmt_hsvs.anat_ingress_wf.ds_fs_5tt_hsvs
Working directory: /tmp/qsirecon_wf/sub-02_mrtrix_multishell_msmt_hsvs/anat_ingress_wf/ds_fs_5tt_hsvs
Node inputs:
atlas =
base_directory = /output
bundle =
bundles =
compress = <undefined>
desc = hsvs
extension = <undefined>
extra_values = <undefined>
fit =
in_file = ['/tmp/qsirecon_wf/sub-02_mrtrix_multishell_msmt_hsvs/anat_ingress_wf/create_5tt_hsvs/sub-02_5tt.mif']
keep_dtype = False
label =
mdp =
mfp =
model =
qsirecon_suffix = anat
source_file = <undefined>
space = fsnative
suffix = dseg
Traceback (most recent call last):
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/plugins/multiproc.py", line 344, in _send_procs_to_workers
self.procs[jobid].run(updatehash=updatehash)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/engine/nodes.py", line 527, in run
result = self._run_interface(execute=True)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/engine/nodes.py", line 645, in _run_interface
return self._run_command(execute)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/engine/nodes.py", line 722, in _run_command
result = self._interface.run(cwd=outdir, ignore_exception=True)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/interfaces/base/core.py", line 388, in run
self._check_mandatory_inputs()
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/interfaces/base/core.py", line 275, in _check_mandatory_inputs
raise ValueError(msg)
ValueError: ReconDerivativesDataSink requires a value for input 'source_file'. For a list of required inputs, see ReconDerivativesDataSink.help()
Node: qsirecon_wf.sub-02_mrtrix_multishell_msmt_hsvs.anat_ingress_wf.ds_qsiprep_5tt_hsvs
Working directory: /tmp/qsirecon_wf/sub-02_mrtrix_multishell_msmt_hsvs/anat_ingress_wf/ds_qsiprep_5tt_hsvs
Node inputs:
atlas = hsvs
base_directory = /output
bundle =
bundles =
compress = <undefined>
desc =
extension = <undefined>
extra_values = <undefined>
fit =
in_file = ['/tmp/qsirecon_wf/sub-02_mrtrix_multishell_msmt_hsvs/anat_ingress_wf/apply_header_to_5tt/sub-02_5tt_hdrxform.nii.gz']
keep_dtype = False
label =
mdp =
mfp =
model =
qsirecon_suffix = anat
source_file = <undefined>
space =
suffix = dseg
Traceback (most recent call last):
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/plugins/multiproc.py", line 344, in _send_procs_to_workers
self.procs[jobid].run(updatehash=updatehash)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/engine/nodes.py", line 527, in run
result = self._run_interface(execute=True)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/engine/nodes.py", line 645, in _run_interface
return self._run_command(execute)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/pipeline/engine/nodes.py", line 722, in _run_command
result = self._interface.run(cwd=outdir, ignore_exception=True)
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/interfaces/base/core.py", line 388, in run
self._check_mandatory_inputs()
File "/opt/conda/envs/qsiprep/lib/python3.10/site-packages/nipype/interfaces/base/core.py", line 275, in _check_mandatory_inputs
raise ValueError(msg)
ValueError: ReconDerivativesDataSink requires a value for input 'source_file'. For a list of required inputs, see ReconDerivativesDataSink.help()
@mattcieslak
Can confirm this bug exists with the vanilla version of
hsvs
workflows, both withv. 0.20.0
and the currentunstable
tag.
with version 0.21.4
too. I wonder hsvs
related recon_workflow, like mrtrix_multishell_msmt_ACT-hsvs
, works with which QSIPrep version.
Hi, @PandaFaye, I think I see the issue (from this file https://github.com/PennLINC/qsiprep/blob/master/qsiprep/workflows/recon/anatomical.py#L132C1-L134C81):
Lines 132-134:
workflow.connect([ (fs_source, outputnode, [ (field, field) for field in FS_FILES_TO_REGISTER])]) <--------
then lines 174-194
workflow.connect([ (anat_ingress_node, register_fs_to_qsiprep_wf, [ ("t1_preproc", "inputnode.qsiprep_reference_image"), ("t1_brain_mask", "inputnode.qsiprep_reference_mask")]), (fs_source, register_fs_to_qsiprep_wf, [ (field, "inputnode." + field) for field in FS_FILES_TO_REGISTER]), (register_fs_to_qsiprep_wf, outputnode, [ ("outputnode.fs_to_qsiprep_transform_mrtrix", "fs_to_qsiprep_transform_mrtrix"), ("outputnode.fs_to_qsiprep_transform_itk", "fs_to_qsiprep_transform_itk")] + [ ("outputnode." + field, field) for field in FS_FILES_TO_REGISTER]), <-------- (create_5tt_hsvs, apply_header_to_5tt, [("out_file", "in_image")]), (create_5tt_hsvs, ds_fs_5tt_hsvs, [("out_file", "in_file")]), (register_fs_to_qsiprep_wf, apply_header_to_5tt, [ ("outputnode.fs_to_qsiprep_transform_mrtrix", "transform_file")]), (apply_header_to_5tt, outputnode, [ ("out_image", "qsiprep_5tt_hsvs")]), (apply_header_to_5tt, ds_qsiprep_5tt_hsvs, [("out_image", "in_file")]), ])
Looks like both of those lines will try to connect a file from
FS_FILES_TO_REGISTER
(which includesbrain
) tooutputnode
, creating the conflict in the error message.Perhaps the later connection is redundant and can be deleted, but I have not tested this. What do you think @mattcieslak?
Thanks @smeisler, I noticed the conflict too. But @araikes haved tried removing the latter connection, and raised other errors. Any idea?
@araikes I'm having trouble finding this dataset on openneuro. It's not in the datalad openneuro superdataset. Do you have a direct link?
@araikes I'm having trouble finding this dataset on openneuro. It's not in the datalad openneuro superdataset. Do you have a direct link?
Apparently hitting publish didn't actually publish it 2 months ago... that's fun. It's published now and you can get it here: https://openneuro.org/datasets/ds005134/versions/1.0.0
Thanks!! I have a copy locally and am writing CI tests with it so we don't run into this again
Great. Let me know if I can be a further help
One question - do you know if all the symlinks from freesurfer made it to openneuro? I'm seeing a lh.pial.T1, lh.pial.T2 but no lh.pial symlink pointing to lh.pial.T1. Does this file exist in your local copy?
Interesting. Looks like the symlinks didn't get handled... Also not annoying at all...
Here's where things should point:
lrwxrwxrwx 1 adamraikes adamraikes 10 May 9 17:26 lh.pial -> lh.pial.T2
lrwxrwxrwx 1 adamraikes adamraikes 19 May 9 15:49 lh.white.H -> lh.white.preaparc.H
lrwxrwxrwx 1 adamraikes adamraikes 19 May 9 15:49 lh.white.K -> lh.white.preaparc.K
lrwxrwxrwx 1 adamraikes adamraikes 10 May 9 17:48 rh.pial -> rh.pial.T2
lrwxrwxrwx 1 adamraikes adamraikes 19 May 9 15:49 rh.white.H -> rh.white.preaparc.H
lrwxrwxrwx 1 adamraikes adamraikes 19 May 9 15:49 rh.white.K -> rh.white.preaparc.K
I'll make this note here as I'm thinking about it... I used the T2 to refine the pial boundary. I assume that as long as you look for e.g. lh.pial
and the linked file exists, it won't matter whether it's pointing to a T1 version or a T2 version (or a FLAIR version)
#784 should have fixed this issue. When you get a chance @araikes could you try out pennbbl/qsiprep:unstable on your data? Please reopen if you find a new problem