General overview
Contents
General overview#
qunex_container
is an independent, self contained python3 compatible command that enables running commands against either Docker or Apptainer (Singularity) QuNex container. The command is available as part of the QuNex suite. To make use of the qunex_container
, you will have to save a copy of the command to a folder that is in your PATH
(e.g., /usr/local/bin
or /usr/bin
) or add the location of the qunex_container
to your PATH. You can refer to the QuNex quick start for details about how to download and install this script.
We suggest adding the following line to your .bash_profile
or similar shell initialization script:
export PATH=<path to folder with qunex_container>:$PATH
To be sure that you have the command available, you can inspect the PATH by running echo $PATH
or the result of which qunex_container
commands.
Once you have qunex_container
installed, you can run QuNex commands the same way you would run them inside the container or if QuNex was installed locally, with two differences. First, replace:
qunex <command> [<parameters>]
with
qunex_container <command> [<parameters>]
When running qunex_container
you will need to provide information about the location of the container you want to run the command against. To do this you just set the container
parameter. An example use in this case would be:
qunex_container hcp_freesurfer \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1,sub2,sub3" \
--sessionsfolder="/myStudy/sessions" \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--overwrite="yes"
In the example above some of the parameters are related to the qunex_container
command (e.g., container
) while others are related to the hcp_freesurfer
command (e.g., sessionids
, overwrite
, etc.). QuNex automatically distinguishes these parameters and properly propagates them to relevant commands.
The second possibility is to provide the information about the location of the container by setting an environmental variable QUNEXCONIMAGE
, possibly in your .bash_profile
or another environment script, e.g.:
For the Docker container:
export QUNEXCONIMAGE="gitlab.qunex.yale.edu:5002/qunex/qunexcontainer:<stable_container_tag>"
For the Apptainer container:
export QUNEXCONIMAGE="/<path_to_container_image>/<container image name>.sif"
If you want to run a set of commands within a container, instead of a single command, you can make use of the run_turnkey
QuNex commands. Please consult QuNex quick start and QuNex 'turnkey' workflow for a detailed description of this.
Another option is to write a script with commands you wish to run within the container and pass the script to the container using the script
parameter, e.g:
qunex_container \
--script="/myStudy/processing/myScript.sh" \
--container="/myApptainerContainers/qunexcontainer-latest.sif"
For more detailed information about the use of the qunex_container
command and how to pass additional settings for the container, consult the inline help by running qunex_container
.
Binding/mapping external folders or setting additional container parameters#
When using a singularity container you can bind external folders by using the bind
parameter, this is analog to the singularity's -B
flag. To bind multiple folders use a comma separated list:
qunex_container hcp_freesurfer \
--batchfile="/my_study/processing/batch.txt" \
--sessions="sub1,sub2,sub3" \
--sessionsfolder="/my_study/sessions" \
--bind="/my_data/data:/data,/my_data/scripts:/scripts"
--container="/my_singularity_containers/qunexcontainer-latest.sif" \
--overwrite="yes"
When using a docker container, QuNex automatically binds the folder you are currently in as /data
inside the container. To change this mapping you can use the dockeropt
parameter. The dockeropt
is string that lists the additional options to be used when running the Docker container. If the parameter is ommited, the content of QUNEXDOCKEROPT
environment variable will be used. If neither is specified, the container will mount the current as data
by specifying the following options -v "$(pwd)":/data
. The parameters are to be specified in a string exactly as they would be on a command line, e.g. to run in detached mode and mount a specific folder use
qunex_container hcp_freesurfer \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1,sub2,sub3" \
--sessionsfolder="/myStudy/sessions" \
--dockeropt="-d -v /data/host_directory/qx_study/:/my_study/" \
--container="gitlab.qunex.yale.edu:5002/qunex/qunexcontainer:<tag>" \
--overwrite="yes"
Using CUDA libraries with an Apptainer container#
When using CUDA (GPU processing) inside an Apptainer (Singularity) container, the --nv
flag is often required so CUDA drivers and libraries are properly setup. To achieve this, use the --nv
flag in the qunex_container
call:
qunex_container hcp_diffusion \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1" \
--sessionsfolder="/myStudy/sessions" \
--nv \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--overwrite="yes"
Also when scheduling you have to use partitions that have GPU nodes, these partitions usually have a _gpu
suffix in their name.
If you are using Apptainer (Singularity) and a CUDA version that is not the QuNex's default, you also need to provide QuNex with the path to the location of the local CUDA libraries:
qunex_container hcp_diffusion \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1" \
--sessionsfolder="/myStudy/sessions" \
--nv \
--cuda_path="/usr/local/cuda-11/" \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--overwrite="yes"
Checking QuNex environment status inside the container#
You can use the --env_status
flag to check whether everything is OK with the QuNex environment inside the container. This comes in handy when troubleshooting execution of various commands, the first thing one should check is if the QuNex environment is properly set up.
qunex_container \
--env_status \
--container="/myApptainerContainers/qunexcontainer-latest.sif"
Executing custom bash commands#
In the example above we executed the hcp_diffusion
command within the container by using the following command:
qunex_container hcp_diffusion \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1" \
--sessionsfolder="/myStudy/sessions" \
--nv \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--overwrite="yes"
Sometimes you would like to execute some additional bash commands, either before entering the container or when already inside the container but before executing the actual QuNex command (hcp_diffusion
in the example above). You can do that by using the bash_pre
and bash_post
parameters. Commands specified in the bash_pre
parameter will execute before entering the container while those specified in the bash_post
parameter will execute when entering the container, but before executing the actual QuNex command. In the example below we first move to a certain directory and load the singularity module (this is done via the bash_pre
parameter), next we enter the QuNex container, then we set the value of an environmental variable (FSL_FIXDIR
) via the bash_post
parameter, finally we execute the hcp_diffusion
command.
qunex_container hcp_diffusion \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1" \
--sessionsfolder="/myStudy/sessions" \
--nv \
--bash_pre="cd /studies/qunex/;module load singularity" \
--bash_post="export FSL_FIXDIR=/studies/qunex/fix" \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--overwrite="yes"
As you can see multiple bash commands can be chained together by using the semicolon character.
Executing custom bash scripts in the QuNex environment#
Inside the QuNex container you will find many neuroimaging tools that are fully installed and preconfigured. A common scenario is that one might need to use this tools directly for some processing or analysis since QuNex does not cover all the possible use cases for these tools. For this purpose you can use the qunex_container
script to execute a custom bash script within the container. To achieve this you simply need to prepare a bash script that includes the desired commands and make that script executable. Next, we pass the script to qunex_container
with the script
parameter.
For example, if I want to merge two images using fslmerge
I would prepare this script:
#!/bin/bash
flsmerge -t /data/mri_data/merged.nii.gz /data/mri_data/image1.nii.gz /data/mri_data/image2.nii.gz
Let's say the script is saved as /data/mri_scripts/flsmerge.sh
. To execute it using FSL in the QuNex container I would then run:
qunex_container \
--script="/data/mri_scripts/flsmerge.sh" \
--container="/data/qx_containers/qunex_suite-0.98.0.sif"
If I were to add the scheduler
parameter execution of my custom script will be scheduled on a HPC cluster.
Scheduling commands to run against the QuNex container on a cluster#
To make use of the parallelization in a cluster environment, qunex_container
can schedule running the specified QuNex command as individual jobs to multiple computer cluster nodes, so that each job will process only one, or a set of sessions (or subjects) in parallel with all the others. Currently PBS and SLURM scheduling systems are supported.
Scheduling is enabled by specification of additional parameters to the qunex_container
command. First, to use a scheduler, provide a scheduler settings string to the scheduler
parameter. The general information about the settings string is available as inline documentation by running qunex_container
, whereas more detailed information is available by running qunex_container ?scheduler
.
An example of scheduling qunex_container
to a single node:
qunex_container hcp_freesurfer \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1,sub2,sub3" \
--sessionsfolder="/myStudy/sessions" \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--scheduler="SLURM,time=2-00:00:00,cpus-per-task=3,mem-per-cpu=20GB,partition=week" \
--overwrite="yes"
This command will schedule the QuNex hcp_freesurfer
command to be run as a single job with three sessions to process.
To distribute commands over multiple jobs and nodes to run in parallel, you have to provide two parameters: 1. sessions
, and 2. parsessions
. The first parameter provides a list of sessions (subjects) that need to be processed, the second parameter provides information on how many sessions are to be run in a single job. qunex_container
will then schedule as many jobs as necessary to process all the sessions, so that at most N (N being equal to parsessions
) are scheduled per job. An example of scheduling multiple jobs is:
qunex_container hcp_freesurfer \
--batchfile="/myStudy/processing/batch.txt" \
--sessionsfolder="/myStudy/sessions" \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--parsessions="4" \
--scheduler="SLURM,time=2-00:00:00,cpus-per-task=4,mem-per-cpu=16GB,partition=week" \
--overwrite="yes"
If there are 202 sessions listed in the /myStudy/processing/batch.txt
file, then the upper command will schedule 51 jobs, each job running the hcp_freesurfer
command against the QuNex container. The first 50 jobs will run 4 sessions in parallel within a container. The last job will run the remaining 2 sessions in parallel within a container.
For more specific information, please see the qunex_container
inline help by running qunex_container
.
NOTE: Passing --sessions parameter values cleanly
In the case that --sessions parameter needs to be passed to a command, but not used to split up processing into multiple container calls (e.g., in import_dicom or import_bids commands), specify the sessions to pass to the call using the --csessions parameter. The value of that parameter will be passed on as --sessions parameter to the command.
Scheduling example#
qunex_container import_dicom \
--sessionsfolder="/Studies/MyStudy/sessions/" \
--csessions="ap9374" \
--masterinbox="none" \
--check="any" \
--overwrite="no" \
--container="/software/Apptainer/qunexcontainer-latest.sif" \
--scheduler="SLURM,time=0-00:20:00,cpus-per-task=1,mem-per-cpu=16GB,partition=day"
In the above case, only one container will be scheduled and within the container the import_dicom
command will be run with --sessions="ap9374"
.
Making changes to the QuNex container environment#
This section is for more advanced users that will sometime want to make some changes to the OS and QuNex environments inside the container. For example, through described methods you can force QuNex inside container to use libraries that are located outside of the container, on your system. Doing so, you can use versions of libraries and tools (e.g. HCP Pipelines, FSL, etc.) that are different from the ones officially packaged into the container. One option is to use the previously mentioned bash_post
parameter to execute bash code within the container. Some additional options that provide further flexibility are outlined below.
QuNex environment#
Once inside the container you can run qunex
commands. Try running qunex
to see the help output, or qunex_envstatus
to see the environment variables. For general QuNex usage, see General Usage Overview.
Of note, the environment script is run automatically when commands are run against the container using qunex_container
. When run, the script (/opt/qunex/env/qunex_environment.sh
) will reset the Apptainer shell environment and clear any prior environment variables such that it reflects the default environment state for QuNex within the container. This reset is especially important for Apptainer containers as they are "permeable" to inheriting user environment variables that were set prior to entering the Apptainer shell.
There might be situations where one would like to make changes to the Apptainer shell container environment. One possibility would be to change a specific setting regarding the behavior within the container. For instance, a user can change the default options set for running octave commands, which are specified in the QUNEXMCOMMAND
environment parameter. Another possibility is to use software and/or dependencies that are not explicitly part of the QuNex Container. For instance, a user can set an environment variable to point to a FreeSurfer installation outside of the QuNex Container default by changing the FREESURFERDIR
environmental variable once inside the Apptainer shell. Put differently, a user can dynamically change and redeclare environment variables once inside the Apptainer shell to a use specified path.
Specifically, there are three mechanisms available to change the environment variables used within the QuNex container, presented in the following three sections.
Option I: .qunex_container.rc file#
If .qunex_container.rc
file is present in the home directory, it will be sourced after the QuNex environment was reset. Notice that in the case of the Apptainer container, the home folder is the user's home folder on the host computer. In case of the Docker container the home folder is /root
folder within the container. To have .qunex_container.rc
present in the Docker home folder, the folder that contains the file on the host computer has to be mounted as the Docker container's /root
folder.
Option II: QUNEXCONTAINERENV variable#
If QUNEXCONTAINERENV
is set, then it is considered to hold the path to the environment script that should be sourced within the container. The script will be sourced after the environment is reset and after checking for the presence of the .qunex_container.rc
file. Again, there are differences between the Apptainer and the Docker container use. For Apptainer, the QUNEXCONTAINERENV
is set in the user environment on the host computer and the path specified is a path valid on the host computer. For the Docker container, the variable has to be passed using additional Docker options, the script has to be present in a filesystem mounted to the Docker container, and the path has to be correct from within the Docker container.
Option III: Use of con_ prefix#
After the environment was reset and the presence of .qunex_container.rc
and QUNEXCONTAINERENV
was tested, the environment script then checks for presence of valid QuNex environment variables prefixed with con_
, e.g. con_FREESURFERDIR
or con_FSLDIR
. If these variables are present, the environment variables they refer to (FREESURFERDIR
, FSLDIR
in the previous example) will be set to their value. The formula <ENVIRONMENT VARIABLE NAME>=${con_<ENVIRONMENT VARIABLE NAME>}
might in a specific environment result in:
export FREESURFERDIR=${con_FREESURFERDIR}
export FSLDIR=${con_FSLDIR}
Again, note that in the case of the Apptainer container, the variables need to be set in the host computer user environment and you should not use the --cleanenv
Apptainer flag which prevents host variables from propagating into the container. When running through qunex_container
you should use --nocleanenv
flag to achieve this. In the case of the Docker container, the variables need to be passed when the container is run.
Order of operation for setting the environment#
The environment will be adjusted in the following order:
.qunex_container.rc
QUNEXCONTAINERENV
con_<ENVIRONMENT VARIABLE NAME>
This means that if a specific variable, e.g. FSLDIR is set in .qunex_container.rc
, and then again in the script referenced by QUNEXCONTAINERENV
, the latter will overwrite the former. If it is again specified as con_FSLDIR
this value will overwrite any previous value.
Setting QuNex environment variables when using qunex_container
#
Container environment variables can also be specified using the --envars
parameter passed to qunex_container
command. Specifically, envars
parameter should be a pipe separated string of "<variable>=><value>|<variable>=><value>"
pairs, illustrated below:
Example call#
qunex_container hcp_freesurfer \
--batchfile="/myStudy/processing/batch.txt" \
--sessions="sub1,sub2,sub3" \
--sessionsfolder="/myStudy/sessions" \
--container="/myApptainerContainers/qunexcontainer-latest.sif" \
--envars="FSLDIR=>/usr/local/bin/fsl|FREESURFERDIR=>/opt/bin/freesurfer-6-modified"
Note that internally, qunex_container
makes use of con_*
mapping so the values specified in the qunex_container
--envars
parameter will have precedence over all other methods.