diff --git a/bidscoin/bidsapps/skullstrip.py b/bidscoin/bidsapps/skullstrip.py index 799e4654..c5dd176f 100755 --- a/bidscoin/bidsapps/skullstrip.py +++ b/bidscoin/bidsapps/skullstrip.py @@ -16,7 +16,7 @@ from bidscoin import bcoin, bids, lsdirs, trackusage -def skullstrip(bidsdir: str, pattern: str, subjects: list, masked: str, output: list, force: bool, args: str, cluster: bool): +def skullstrip(bidsdir: str, pattern: str, subjects: list, masked: str, output: list, force: bool, args: str, cluster: str): """ :param bidsdir: The bids-directory with the subject data :param pattern: Globlike search pattern (relative to the subject/session folder) to select the images that need to be skullstripped, e.g. 'anat/*_T1w*' @@ -25,7 +25,7 @@ def skullstrip(bidsdir: str, pattern: str, subjects: list, masked: str, output: :param masked: Globlike search pattern (relative to the subject/session folder) to select additional images that need to be masked with the same mask, e.g. 'fmap/*_phasediff') :param output: One or two output strings that determine where the skullstripped + additional masked images are saved. Each output string can be the name of a BIDS datatype folder, such as 'anat', or of the derivatives folder, i.e. 'derivatives' (default). If the output string is the same as the datatype then the original images are replaced by the skullstripped images :param args: Additional arguments that are passed to synthstrip. See examples for usage - :param cluster: Use qsub to submit the skullstrip jobs to a high-performance compute (HPC) cluster + :param cluster: Use `torque` or `slurm` to submit the skullstrip jobs to a high-performance compute (HPC) cluster. Leave empty to run skullstrip on your local computer :return: """ @@ -112,8 +112,13 @@ def skullstrip(bidsdir: str, pattern: str, subjects: list, masked: str, output: maskimg = bidsdir/'derivatives'/'skullstrip'/subid/sesid/srcimg.parent.name/f"{srcent}_{derent}_mask{ext}" maskimg.parent.mkdir(parents=True, exist_ok=True) command = f"mri_synthstrip -i {srcimg} -o {outputimg} -m {maskimg} {args}" - if cluster: - command = f"qsub -l walltime=0:05:00,mem=8gb -N skullstrip_{subid}_{sesid} < tuple: return outputs, slices -def slicer_append(inputimage: Path, outlineimage: Path, mainopts: str, outputopts: str, sliceroutput: str, montage: Path, cluster: bool): +def slicer_append(inputimage: Path, outlineimage: Path, mainopts: str, outputopts: str, sliceroutput: str, montage: Path, cluster: str): """Run slicer and pngappend (locally or on the cluster) to create a montage of the sliced images""" # Create a workdir and the shell command @@ -86,12 +86,14 @@ def slicer_append(inputimage: Path, outlineimage: Path, mainopts: str, outputopt f"rm -r {workdir}" # Wrap the command - if cluster: - if inputimage.stat().st_size > 50 * 1024**2: - mem = '8gb' # Ask for more resources if we have a large (e.g. 4D) input image - else: - mem = '1gb' - command = f"qsub -l walltime=0:02:00,mem={mem} -N slicereport -e {tempfile.gettempdir()} -o {tempfile.gettempdir()}< 50 * 1024**2 else '1' # Ask for more resources if we have a large (e.g. 4D) input image + if cluster == 'torque': + command = f"qsub -l walltime=0:02:00,mem={mem}gb -N slicereport -e {tempfile.gettempdir()} -o {tempfile.gettempdir()} << EOF\n{command}\nEOF" + elif cluster == 'slurm': + command = f"sbatch --time=0:02:00 --mem={mem}G --job-name slicereport -o {tempfile.gettempdir()}/slurm-%j.out << EOF\n{command}\nEOF" + elif cluster: + LOGGER.error(f"Invalid cluster manager `{cluster}`") + exit(1) # Run the command LOGGER.bcdebug(f"Command: {command}") @@ -102,7 +104,7 @@ def slicer_append(inputimage: Path, outlineimage: Path, mainopts: str, outputopt sys.exit(process.returncode) -def slicereport(bidsdir: str, pattern: str, outlinepattern: str, outlineimage: str, subjects: list, reportdir: str, crossdirs: str, qccols: list, cluster: bool, options: list, outputs: list, suboptions: list, suboutputs: list): +def slicereport(bidsdir: str, pattern: str, outlinepattern: str, outlineimage: str, subjects: list, reportdir: str, crossdirs: str, qccols: list, cluster: str, options: list, outputs: list, suboptions: list, suboutputs: list): """ :param bidsdir: The bids-directory with the subject data :param pattern: Globlike search pattern to select the images in bidsdir to be reported, e.g. 'anat/*_T1w*' @@ -112,7 +114,7 @@ def slicereport(bidsdir: str, pattern: str, outlinepattern: str, outlineimage: s :param reportdir: The folder where the report is saved :param crossdirs: A (list of) folder(s) with cross-linked sub-reports :param qccols: Column names for creating an accompanying tsv-file to store QC-rating scores - :param cluster: Use qsub to submit the slicer jobs to a high-performance compute (HPC) cluster + :param cluster: Use `torque` or `slurm` to submit the slicer jobs to a high-performance compute (HPC) cluster. Leave empty to run slicer on your local computer :param options: Slicer main options :param outputs: Slicer output options :param suboptions: Slicer main options for creating the sub-reports (same as OPTIONS) diff --git a/bidscoin/cli/_skullstrip.py b/bidscoin/cli/_skullstrip.py index ae7b4083..4af58f11 100755 --- a/bidscoin/cli/_skullstrip.py +++ b/bidscoin/cli/_skullstrip.py @@ -32,6 +32,6 @@ class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescri parser.add_argument('-o','--output', help="One or two output strings that determine where the skullstripped + additional masked images are saved. Each output string can be the name of a BIDS datatype folder, such as 'anat', or of the derivatives folder, i.e. 'derivatives' (default). If the output string is the same as the datatype then the original images are replaced by the skullstripped images", nargs='+') parser.add_argument('-f','--force', help="Process images, regardless whether images have already been skullstripped (i.e. if {'SkullStripped': True} in the json sidecar file)", action='store_true') parser.add_argument('-a','--args', help="Additional arguments that are passed to synthstrip (NB: Use quotes and a leading space to prevent unintended argument parsing)", type=str, default='') - parser.add_argument('-c','--cluster', help='Use `qsub` to submit the skullstrip jobs to a high-performance compute (HPC) cluster. Can only be used if `--masked` is left empty', action='store_true') + parser.add_argument('-c','--cluster', help='Use `torque` or `slurm` to submit the skullstrip jobs to a high-performance compute (HPC) cluster. Can only be used if `--masked` is left empty', choices=['torque','slurm']) return parser diff --git a/bidscoin/cli/_slicereport.py b/bidscoin/cli/_slicereport.py index 3377ec1a..f1f58ee4 100755 --- a/bidscoin/cli/_slicereport.py +++ b/bidscoin/cli/_slicereport.py @@ -56,7 +56,7 @@ def get_parser(): parser.add_argument('-r','--reportfolder', help="The folder where the report is saved (default: bidsfolder/derivatives/slicereport)") parser.add_argument('-x','--xlinkfolder', help="A (list of) QC report folder(s) with cross-linkable sub-reports, e.g. bidsfolder/derivatives/mriqc", nargs='+') parser.add_argument('-q','--qcscores', help="Column names for creating an accompanying tsv-file to store QC-rating scores (default: rating_overall)", default=['rating_overall'], nargs='+') - parser.add_argument('-c','--cluster', help='Use `qsub` to submit the slicer jobs to a high-performance compute (HPC) cluster', action='store_true') + parser.add_argument('-c','--cluster', help='Use `torque` or `slurm` to submit the skullstrip jobs to a high-performance compute (HPC) cluster', choices=['torque','slurm']) parser.add_argument('--options', help='Main options of slicer (see below). (default: "s 1")', default=['s','1'], nargs='+') parser.add_argument('--outputs', help='Output options of slicer (see below). (default: "x 0.4 x 0.5 x 0.6 y 0.4 y 0.5 y 0.6 z 0.4 z 0.5 z 0.6")', default=['x','0.4','x','0.5','x','0.6','y','0.4','y','0.5','y','0.6','z','0.4','z','0.5','z','0.6'], nargs='+') parser.add_argument('--suboptions', help='Main options of slicer for creating the sub-reports (same as OPTIONS, see below). (default: OPTIONS)', nargs='+')