qsiprep icon indicating copy to clipboard operation
qsiprep copied to clipboard

Error in connecting qsirecon nodes (0.20.1dev)

Open smeisler opened this issue 11 months ago • 16 comments

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
		}
	}
]
}

smeisler avatar Mar 26 '24 12:03 smeisler

@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 avatar Apr 25 '24 17:04 araikes

@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 avatar Apr 25 '24 18:04 mattcieslak

@mattcieslak I can send you HBN freesurfer data I have, would that work?

smeisler avatar Apr 25 '24 18:04 smeisler

@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.

araikes avatar Apr 25 '24 18:04 araikes

@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.

araikes avatar May 09 '24 19:05 araikes

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 avatar May 09 '24 19:05 mattcieslak

@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.

araikes avatar May 10 '24 19:05 araikes

@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.

araikes avatar May 14 '24 17:05 araikes

Thank you @araikes !!

mattcieslak avatar May 14 '24 18:05 mattcieslak

You're welcome.

araikes avatar May 15 '24 19:05 araikes

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

PandaFaye avatar May 27 '24 02:05 PandaFaye

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?

smeisler avatar May 27 '24 02:05 smeisler

@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

araikes avatar May 30 '24 22:05 araikes

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()

araikes avatar May 30 '24 22:05 araikes

@mattcieslak

Can confirm this bug exists with the vanilla version of hsvs workflows, both with v. 0.20.0 and the current unstable tag.

with version 0.21.4 too. I wonder hsvs related recon_workflow, like mrtrix_multishell_msmt_ACT-hsvs, works with which QSIPrep version.

PandaFaye avatar Jun 26 '24 02:06 PandaFaye

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?

Thanks @smeisler, I noticed the conflict too. But @araikes haved tried removing the latter connection, and raised other errors. Any idea?

PandaFaye avatar Jun 26 '24 02:06 PandaFaye

@araikes I'm having trouble finding this dataset on openneuro. It's not in the datalad openneuro superdataset. Do you have a direct link?

mattcieslak avatar Jul 13 '24 13:07 mattcieslak

@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

araikes avatar Jul 15 '24 20:07 araikes

Thanks!! I have a copy locally and am writing CI tests with it so we don't run into this again

mattcieslak avatar Jul 16 '24 13:07 mattcieslak

Great. Let me know if I can be a further help

araikes avatar Jul 16 '24 16:07 araikes

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?

mattcieslak avatar Jul 16 '24 18:07 mattcieslak

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

araikes avatar Jul 17 '24 23:07 araikes

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)

araikes avatar Jul 17 '24 23:07 araikes

#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

mattcieslak avatar Jul 18 '24 17:07 mattcieslak