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

[SCHEMA] Start drafting validation rules for ASL data #1060

Merged
merged 17 commits into from
Apr 15, 2022
259 changes: 259 additions & 0 deletions src/schema/rules/sidecars/asl_validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# Rules for ASL data that are not defined in tables.
---

# 157
ASLLabelingDurationNiftiLength:
code: LABELING_DURATION_LENGTH_NOT_MATCHING_NIFTI
description: |
The number of values for 'LabelingDuration' for this file does not match the 4th dimension of the NIfTI header.
'LabelingDuration' is the total duration of the labeling pulse train, in seconds,
corresponding to the temporal width of the labeling bolus for `(P)CASL`.
In case all control-label volumes (or deltam or CBF) have the same `LabelingDuration`, a scalar must be
specified.
In case the control-label volumes (or deltam or cbf) have a different `LabelingDuration`,
an array of numbers must be specified, for which any `m0scan` in the timeseries has a `LabelingDuration` of
zero.
In case an array of numbers is provided, its length should be equal to the number of volumes specified in
`*_aslcontext.tsv`. Corresponds to DICOM Tag 0018,9258 `ASL Pulse Train Duration`.
level: error
selectors:
- suffix == "asl"
- sidecar contains "LabelingDuration"
- type(sidecar.LabelingDuration) == "array"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry for my sparse appearance at the meetings. So do we have a grammar and interpreter already? (Since in general looks like python but this absent contains operand throws me off, not sure why not to use available in with swap of the arguments)

Copy link
Collaborator

@effigies effigies Apr 12, 2022

Choose a reason for hiding this comment

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

In YAML:

- "LabelingDuration" in sidecar

raises an error because of YAML parsing rules. But we also don't insist that people always use < instead of > so it seemed reasonable to have both directions.

checks:
- data.shape[3] == sidecar.LabelingDuration.size

# 165
ASLContextConsistent:
code: ASLCONTEXT_TSV_NOT_CONSISTENT
description: |
The number of volumes in the '*_aslcontext.tsv' for this file does not match the number of
values in the NIfTI header.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
checks:
- data.shape[3] == associated["aslcontext"].shape[0]
tsalo marked this conversation as resolved.
Show resolved Hide resolved

# 168
ASLFlipAngleNiftiLength:
code: FLIP_ANGLE_NOT_MATCHING_NIFTI
description: |
The number of values for 'FlipAngle' for this file does not match the 4th dimension of the NIfTI header.
'FlipAngle' is the flip angle (FA) for the acquisition, specified in degrees.
Corresponds to: DICOM Tag 0018, 1314 `Flip Angle`.
The data type number may apply to files from any MRI modality concerned with a single value for this field,
or to the files in a file collection where the value of this field is iterated using the flip entity.
The data type array provides a value for each volume in a 4D dataset and should only be used when the
volume timing is critical for interpretation of the data, such as in ASL or variable flip angle fMRI sequences.
level: error
selectors:
- suffix == "asl"
- sidecar contains "FlipAngle"
- type(sidecar.FlipAngle) == "array"
checks:
- data.shape[3] == sidecar.FlipAngle.size

# 172
ASLFlipAngleASLContextLength:
code: FLIP_ANGLE_NOT_MATCHING_ASLCONTEXT_TSV
description: |
The number of values for 'FlipAngle' for this file does not match the number of volumes in the
'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
'FlipAngle' is the flip angle (FA) for the acquisition, specified in degrees.
Corresponds to: DICOM Tag 0018, 1314 `Flip Angle`.
The data type number may apply to files from any MRI modality concerned with a single value for this field,
or to the files in a file collection where the value of this field is iterated using the flip entity.
The data type array provides a value for each volume in a 4D dataset and should only be used when the volume
timing is critical for interpretation of the data, such as in ASL or variable flip angle fMRI sequences.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar contains "FlipAngle"
- type(sidecar.FlipAngle) == "array"
checks:
- associated["aslcontext"].shape[0] == sidecar.FlipAngle.size

# 173
ASLPostLabelingDelayNiftiLength:
code: POST_LABELING_DELAY_NOT_MATCHING_NIFTI
description: |
The number of values for 'PostLabelingDelay' for this file does not match the 4th dimension of the NIfTI
header.
'PostLabelingDelay' is the time, in seconds, after the end of the labeling (for (P)CASL) or middle of the
labeling pulse (for PASL) until the middle of the excitation pulse applied to the imaging slab
(for 3D acquisition) or first slice (for 2D acquisition).
Can be a number (for a single-PLD time series) or an array of numbers (for multi-PLD and Look-Locker).
In the latter case, the array of numbers contains the PLD of each volume (i.e. each 'control' and 'label')
in the acquisition order. Any image within the time-series without a PLD (e.g. an 'm0scan') is indicated by a
zero.
Based on DICOM Tags 0018,9079 Inversion Times and 0018,0082 InversionTime.
level: error
selectors:
- suffix == "asl"
- sidecar contains "PostLabelingDelay"
- type(sidecar.PostLabelingDelay) == "array"
checks:
- data.shape[3] == sidecar.PostLabelingDelay.size

# 174
ASLPostLabelingDelayASLContextLength:
code: POST_LABELING_DELAY_NOT_MATCHING_ASLCONTEXT_TSV
description: |
The number of values for 'PostLabelingDelay' for this file does not match the number of volumes
in the 'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
'PostLabelingDelay' is the time, in seconds, after the end of the labeling (for (P)CASL) or
middle of the labeling pulse (for PASL) until the middle of the excitation pulse applied to
the imaging slab (for 3D acquisition) or first slice (for 2D acquisition).
Can be a number (for a single-PLD time series) or an array of numbers (for multi-PLD and Look-Locker).
In the latter case, the array of numbers contains the PLD of each volume (i.e. each 'control' and 'label')
in the acquisition order.
Any image within the time-series without a PLD (e.g. an 'm0scan') is indicated by a zero.
Based on DICOM Tags 0018,9079 Inversion Times and 0018,0082 InversionTime.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar contains "PostLabelingDelay"
- type(sidecar.PostLabelingDelay) == "array"
checks:
- associated["aslcontext"].shape[0] == sidecar.PostLabelingDelay.size

# 175
ASLLabelingDurationASLContextLength:
code: LABELLING_DURATION_NOT_MATCHING_ASLCONTEXT_TSV
description: |
The number of values for 'LabelingDuration' for this file does not match the number of volumes
in the 'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
'LabelingDuration' is the total duration of the labeling pulse train, in seconds,
corresponding to the temporal width of the labeling bolus for `(P)CASL`.
In case all control-label volumes (or deltam or CBF) have the same `LabelingDuration`,
a scalar must be specified.
In case the control-label volumes (or deltam or cbf) have a different `LabelingDuration`,
an array of numbers must be specified, for which any `m0scan` in the timeseries has a
`LabelingDuration` of zero.
In case an array of numbers is provided, its length should be equal to the number of volumes
specified in `*_aslcontext.tsv`.
Corresponds to DICOM Tag 0018,9258 `ASL Pulse Train Duration`.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar contains "LabelingDuration"
- type(sidecar.LabelingDuration) == "array"
checks:
- associated["aslcontext"].shape[0] == sidecar.LabelingDuration.size

# 177
ASLRepetitionTimePreparationASLContextLength:
code: REPETITIONTIMEPREPARATION_NOT_MATCHING_ASLCONTEXT_TSV
description: |
The number of values of 'RepetitionTimePreparation' for this file does not match the number of
volumes in the 'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
'RepetitionTimePreparation' is the interval, in seconds, that it takes a preparation pulse block to
re-appear at the beginning of the succeeding (essentially identical) pulse sequence block.
The data type number may apply to files from any MRI modality concerned with a single value for this field.
The data type array provides a value for each volume in a 4D dataset and should only be used when the
volume timing is critical for interpretation of the data, such as in ASL.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar contains "RepetitionTimePreparation"
- type(sidecar.RepetitionTimePreparation) == "array"
checks:
- associated["aslcontext"].shape[0] == sidecar.RepetitionTimePreparation.size

# 180
ASLBackgroundSuppressionNumberPulses:
code: BACKGROUND_SUPPRESSION_PULSE_NUMBER_NOT_CONSISTENT
description: |
The 'BackgroundSuppressionNumberPulses' field is not consistent with the length of
'BackgroundSuppressionPulseTime'.
'BackgroundSuppressionNumberPulses' is the number of background suppression pulses used.
Note that this excludes any effect of background suppression pulses applied before the labeling.
level: warning
selectors:
- suffix == "asl"
- sidecar contains "BackgroundSuppressionNumberPulses"
- sidecar contains "BackgroundSuppressionPulseTime"
- type(sidecar.BackgroundSuppressionPulseTime) == "array"
checks:
- sidecar.BackgroundSuppressionPulseTime.size == sidecar.BackgroundSuppressionNumberPulses

# 181
ASLTotalAcquiredVolumesASLContextLength:
code: TOTAL_ACQUIRED_VOLUMES_NOT_CONSISTENT
description: |
The number of values for 'TotalAcquiredVolumes' for this file does not match number of
volumes in the 'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
'TotalAcquiredVolumes' is the original number of 3D volumes acquired for each volume defined in the
'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
level: warning
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar contains "TotalAcquiredVolumes"
checks:
- associated["aslcontext"].shape[0] == sidecar.TotalAcquiredVolumes

# 196
ASLEchoTimeASLContextLength:
code: ECHO_TIME_NOT_CONSISTENT
description: |
The number of values for 'EchoTime' for this file does not match number of volumes in the
'sub-<label>[_ses-<label>][_acq-<label>][_rec-<label>][_run-<index>]_aslcontext.tsv'.
'EchoTime' is the echo time (TE) for the acquisition, specified in seconds.
level: warning
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar contains "EchoTime"
- type(sidecar.EchoTime) == "array"
checks:
- associated["aslcontext"].shape[0] == sidecar.EchoTime.size

# 198
ASLM0TypeAbsentScan:
code: M0Type_SET_INCORRECTLY_TO_ABSENT
description: |
You defined M0Type as 'absent' while including a separate '*_m0scan.nii[.gz]' and
'*_m0scan.json', or defining the 'M0Estimate' field.
This is not allowed, please check that this field are filled correctly.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- associated contains "m0scan"
checks:
- sidecar.M0Type != "absent"

# 199
ASLM0TypeAbsentASLContext:
code: M0Type_SET_INCORRECTLY_TO_ABSENT_IN_ASLCONTEXT
description: |
You defined M0Type as 'absent' while including an m0scan volume within the '*_aslcontext.tsv'.
This is not allowed, please check that this field are filled correctly.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- associated["aslcontext"]["volume_type"] contains "m0scan"
tsalo marked this conversation as resolved.
Show resolved Hide resolved
checks:
- sidecar.M0Type != "absent"

# 202
ASLM0TypeIncorrect:
code: M0Type_SET_INCORRECTLY
description: |
M0Type was not defined correctly.
If 'M0Type' is equal to 'separate', the dataset should include a *_m0scan.nii[.gz] and *_m0scan.json file.
level: error
selectors:
- suffix == "asl"
- associated contains "aslcontext"
- sidecar.M0Type == "separate"
checks:
- associated contains "m0scan"
39 changes: 39 additions & 0 deletions src/schema/rules/sidecars/dwi_validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Rules for DWI data that are not defined in tables.
---

# 29
DWIVolumeCount:
code: VOLUME_COUNT_MISMATCH
description: |
The number of volumes in this scan does not match the number of volumes in the
corresponding .bvec and .bval files.
level: error
selectors:
- suffix == "dwi"
- associated contains "bval"
- associated contains "bvec"
checks:
- associated["bval"].shape[0] == data.shape[3]
- associated["bvec"].shape[0] == data.shape[3]

# 32
DWIMissingBvec:
code: DWI_MISSING_BVEC
description: |
DWI scans must have a corresponding .bvec file.
level: error
selectors:
- suffix == "dwi"
checks:
- associated contains "bvec"

# 33
DWIMissingBval:
code: DWI_MISSING_BVAL
description: |
DWI scans must have a corresponding .bval file.
level: error
selectors:
- suffix == "dwi"
effigies marked this conversation as resolved.
Show resolved Hide resolved
checks:
- associated contains "bval"
16 changes: 16 additions & 0 deletions src/schema/rules/sidecars/events_validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Rules for events data that are not defined in tables.
---

# 25
EventsMissing:
code: EVENTS_TSV_MISSING
description: |
Task scans should have a corresponding events.tsv file.
If this is a resting state scan you can ignore this warning or rename the task to include the word "rest".
level: warning # could be an error with the proper selectors, I think
selectors:
- entities contains "task"
- entities.task != "rest"
- entities.task not contains "rest" # Alternative for including the word "rest"
checks:
- associated contains "events"