Basics

Install slurmjobs

R is an open-source statistical environment which can be easily modified to enhance its functionality via packages. slurmjobs is a R package available via the Bioconductor repository for packages. R can be installed on any operating system from CRAN after which you can install slurmjobs by using the following commands in your R session:

if (!requireNamespace("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
}

BiocManager::install("slurmjobs")

## Check that you have a valid Bioconductor installation
BiocManager::valid()

Required knowledge

slurmjobs is designed for interacting with the SLURM job scheduler, and assumes basic familiarity with terms like “job”, “task”, and “array”, as well as the sbatch command. Background knowledge about memory (such as virtual memory and resident set size (RSS)) is helpful but not critical in using this package.

If you are asking yourself the question “Where do I start using Bioconductor?” you might be interested in this blog post.

Asking for help

As package developers, we try to explain clearly how to use our packages and in which order to use the functions. But R and Bioconductor have a steep learning curve so it is critical to learn where to ask for help. The blog post quoted above mentions some but we would like to highlight the Bioconductor support site as the main resource for getting help: remember to use the slurmjobs tag and check the older posts. Other alternatives are available such as creating GitHub issues and tweeting. However, please note that if you want to receive help you should adhere to the posting guidelines. It is particularly critical that you provide a small reproducible example and your session information so package developers can track down the source of the error.

Citing slurmjobs

We hope that slurmjobs will be useful for your research. Please use the following information to cite the package and the overall approach. Thank you!

## Citation info
citation("slurmjobs")
#> To cite package 'slurmjobs' in publications use:
#> 
#>   LieberInstitute (2024). _slurmjobs: Helper Functions for SLURM Jobs_.
#>   doi:10.18129/B9.bioc.slurmjobs
#>   <https://doi.org/10.18129/B9.bioc.slurmjobs>,
#>   https://github.com/LieberInstitute/slurmjobs/slurmjobs - R package
#>   version 1.2.5, <http://www.bioconductor.org/packages/slurmjobs>.
#> 
#>   LieberInstitute (2024). "slurmjobs: Helper Functions for SLURM Jobs."
#>   _bioRxiv_. doi:10.1101/TODO <https://doi.org/10.1101/TODO>,
#>   <https://www.biorxiv.org/content/10.1101/TODO>.
#> 
#> To see these entries in BibTeX format, use 'print(<citation>,
#> bibtex=TRUE)', 'toBibtex(.)', or set
#> 'options(citation.bibtex.max=999)'.

Overview

slurmjobs provides helper functions for interacting with SLURM-managed high-performance-computing environments from R. It includes functions for creating submittable jobs (including array jobs), monitoring partitions, and extracting info about running or complete jobs. In addition to loading slurmjobs, we’ll be using dplyr to manipulate example data about jobs.

Creating Shell Scripts to sbatch

When processing data on a SLURM-managed system, primarily running R code, you’ll likely find yourself writing many “wrapper” shell scripts that can be submitted via sbatch to the job scheduler. This process requires precise SLURM-specific syntax and a large amount of repetition. job_single aims to reduce required configuration from the user to just a handful of options that tend to vary most often between shell scripts (e.g. memory, number of CPUs, time limit), and automate the rest of the shell-script-creation process.

Shell scripts created by job_single log key reproducibility information, such as the user, job ID, job name, node name, and when the job starts and ends.

#   With 'create_shell = FALSE', the contents of the potential shell script are
#   only printed to the screen
job_single(
    name = "my_shell_script", memory = "10G", cores = 2, create_shell = FALSE
)
#> 2024-09-20 18:18:36.664277 creating the logs directory at:  logs
#> #!/bin/bash
#> #SBATCH -p shared
#> #SBATCH --mem=10G
#> #SBATCH --job-name=my_shell_script
#> #SBATCH -c 2
#> #SBATCH -t 1-00:00:00
#> #SBATCH -o logs/my_shell_script.txt
#> #SBATCH -e logs/my_shell_script.txt
#> #SBATCH --mail-type=ALL
#> 
#> set -e
#> 
#> echo "**** Job starts ****"
#> date
#> 
#> echo "**** JHPCE info ****"
#> echo "User: ${USER}"
#> echo "Job id: ${SLURM_JOB_ID}"
#> echo "Job name: ${SLURM_JOB_NAME}"
#> echo "Node name: ${HOSTNAME}"
#> echo "Task id: ${SLURM_ARRAY_TASK_ID}"
#> 
#> ## Load the R module
#> module load conda_R/4.4
#> 
#> ## List current modules for reproducibility
#> module list
#> 
#> ## Edit with your job command
#> Rscript -e "options(width = 120); sessioninfo::session_info()"
#> 
#> echo "**** Job ends ****"
#> date
#> 
#> ## This script was made using slurmjobs version 1.2.5
#> ## available from http://research.libd.org/slurmjobs/

Similarly, we can specify task_num to create an array job– in this case, one with 10 tasks.

job_single(
    name = "my_array_job", memory = "5G", cores = 1, create_shell = FALSE,
    task_num = 10
)
#> 2024-09-20 18:18:36.736029 creating the logs directory at:  logs
#> #!/bin/bash
#> #SBATCH -p shared
#> #SBATCH --mem=5G
#> #SBATCH --job-name=my_array_job
#> #SBATCH -c 1
#> #SBATCH -t 1-00:00:00
#> #SBATCH -o logs/my_array_job.%a.txt
#> #SBATCH -e logs/my_array_job.%a.txt
#> #SBATCH --mail-type=ALL
#> #SBATCH --array=1-10%20
#> 
#> set -e
#> 
#> echo "**** Job starts ****"
#> date
#> 
#> echo "**** JHPCE info ****"
#> echo "User: ${USER}"
#> echo "Job id: ${SLURM_JOB_ID}"
#> echo "Job name: ${SLURM_JOB_NAME}"
#> echo "Node name: ${HOSTNAME}"
#> echo "Task id: ${SLURM_ARRAY_TASK_ID}"
#> 
#> ## Load the R module
#> module load conda_R/4.4
#> 
#> ## List current modules for reproducibility
#> module list
#> 
#> ## Edit with your job command
#> Rscript -e "options(width = 120); sessioninfo::session_info()"
#> 
#> echo "**** Job ends ****"
#> date
#> 
#> ## This script was made using slurmjobs version 1.2.5
#> ## available from http://research.libd.org/slurmjobs/

Another function, job_loop(), can be used to create more complex array jobs as compared with job_single(). It’s useful when looping through one or more variables with pre-defined values, and applying the same processing steps. The key difference is that rather than specifying task_num, you specify loops, a named list of variables to loop through. An array job then gets created that can directly refer to the values of these variables, rather than referring to just the array’s task ID.

job_loop(), unlike job_single(), also creates an R script. The idea is that the shell script invokes the R script internally, with a particular combination of variables. The getopt package is then used to read in this combination from the command line, so that each variable can be accessed by name in R. Let’s make that a bit more concrete.

#   'job_loop' returns a list containing the contents of the to-be-created shell
#   and R scripts. Let's take a look at the shell script first
script_pair <- job_loop(
    loops = list(region = c("DLPFC", "HIPPO"), feature = c("gene", "exon", "tx", "jxn")),
    name = "bsp2_test"
)
cat(script_pair[["shell"]], sep = "\n")
#> #!/bin/bash
#> #SBATCH -p shared
#> #SBATCH --mem=10G
#> #SBATCH --job-name=bsp2_test
#> #SBATCH -c 1
#> #SBATCH -t 1-00:00:00
#> #SBATCH -o /dev/null
#> #SBATCH -e /dev/null
#> #SBATCH --mail-type=ALL
#> #SBATCH --array=1-8%20
#> 
#> ## Define loops and appropriately subset each variable for the array task ID
#> all_region=(DLPFC HIPPO)
#> region=${all_region[$(( $SLURM_ARRAY_TASK_ID / 4 % 2 ))]}
#> 
#> all_feature=(gene exon tx jxn)
#> feature=${all_feature[$(( $SLURM_ARRAY_TASK_ID / 1 % 4 ))]}
#> 
#> ## Explicitly pipe script output to a log
#> log_path=logs/bsp2_test_${region}_${feature}_${SLURM_ARRAY_TASK_ID}.txt
#> 
#> {
#> set -e
#> 
#> echo "**** Job starts ****"
#> date
#> 
#> echo "**** JHPCE info ****"
#> echo "User: ${USER}"
#> echo "Job id: ${SLURM_JOB_ID}"
#> echo "Job name: ${SLURM_JOB_NAME}"
#> echo "Node name: ${HOSTNAME}"
#> echo "Task id: ${SLURM_ARRAY_TASK_ID}"
#> 
#> ## Load the R module
#> module load conda_R/4.4
#> 
#> ## List current modules for reproducibility
#> module list
#> 
#> ## Edit with your job command
#> Rscript bsp2_test.R --region ${region} --feature ${feature}
#> 
#> echo "**** Job ends ****"
#> date
#> 
#> } > $log_path 2>&1
#> 
#> ## This script was made using slurmjobs version 1.2.5
#> ## available from http://research.libd.org/slurmjobs/

First, note the line Rscript bsp2_test.R --region ${region} --feature ${feature}. Every task of the array job passes a unique combination of ${region} and ${feature} to R.

Notice also that logs from executing this shell script get named with each of the variables’ values in addition to the array task ID. For example, the log for the first task would be logs/DLPFC_gene_1.txt. Also, the array specifies 8 tasks total (the product of the number of regions and features).

Let’s also look at the R script.

cat(script_pair[["R"]], sep = "\n")
#> library(getopt)
#> library(sessioninfo)
#> 
#> # Import command-line parameters
#> spec <- matrix(
#>     c(
#>         c("region", "feature"),
#>         c("r", "f"),
#>         rep("1", 2),
#>         rep("character", 2),
#>         rep("Add variable description here", 2)
#>     ),
#>     ncol = 5
#> )
#> opt <- getopt(spec)
#> 
#> print("Using the following parameters:")
#> print(opt)
#> 
#> session_info()
#> 
#> ## This script was made using slurmjobs version 1.2.5
#> ## available from http://research.libd.org/slurmjobs/

The code related to getopt at the top of the script reads in the unique combination of variable values into a list called opt here. For example, one task of the array job might yield values for opt$region and opt$feature to be "DLPFC" and "gene", respectively.

Submitting and Resubmitting Jobs

Shell scripts created with job_single() or job_loop() may be submitted as batch jobs with sbatch (e.g. sbatch myscript.sh). Note no additional arguments to sbatch are required since all configuration is specified within the shell script.

The array_submit() helper function was also intended to make job submission easier. In particular, it addresses a common case where after a large array job was run, a handful of tasks fail (such as due to temporary file-system issues). array_submit() helps re-submit failed tasks.

Below we’ll create an example array job with job_single(), then do a dry run of array_submit() to demonstrate its basic usage.

job_single(
    name = "my_array_job", memory = "5G", cores = 1, create_shell = TRUE,
    task_num = 10
)
#> 2024-09-20 18:18:36.942671 creating the logs directory at:  logs
#> 2024-09-20 18:18:36.943735 creating the shell file my_array_job.sh
#> To submit the job use: sbatch my_array_job.sh

#   Suppose that tasks 3, 6, 7, and 8 failed
array_submit(name = "my_array_job", task_ids = c(3, 6:8), submit = FALSE)

While task_ids can be provided explicitly as above, the real convenience comes from the ability to run array_submit() without specifying task_ids. As long as the original array job was created with job_single() or job_loop() and submitted as-is (on the full set of tasks), array_submit() can automatically find the failed tasks by reading the shell script (my_array_job.sh), grabbing the original array job ID from the log, and internally calling job_report()).

#   Not run here, since we aren't on a SLURM cluster
array_submit(name = "my_array_job", submit = FALSE)

Monitoring Running Jobs

The job_info() function provides wrappers around the squeue and sstat utilities SLURM provides for monitoring specific jobs and how busy partitions are. The general idea is to provide the information output from squeue into a tibble, while retrieving memory-utilization information that ordinarily must be retrieved manually on a job-by-job basis with sstat -j [specific job ID].

On a SLURM system, you’d run job_info_df = job_info(user = NULL, partition = "shared") here, to get every user’s jobs running on the “shared” partition. We’ll load an example output directly here.

#   On a real SLURM system
print(job_info_df)
#> # A tibble: 100 × 11
#>    job_id max_rss_gb max_vmem_gb user  array_task_id name     partition  cpus
#>     <dbl>      <dbl>       <dbl> <chr>         <int> <chr>    <fct>     <int>
#>  1 222106         NA          NA user1            69 my_job_1 shared        2
#>  2 271213         NA          NA user1            37 my_job_2 shared        1
#>  3 280839         NA          NA user1            11 my_job_3 shared        2
#>  4 285265         NA          NA user1            31 my_job_3 shared        2
#>  5 285275         NA          NA user1            41 my_job_3 shared        2
#>  6 285276         NA          NA user1            42 my_job_3 shared        2
#>  7 285281         NA          NA user1            47 my_job_3 shared        2
#>  8 285282         NA          NA user1            48 my_job_3 shared        2
#>  9 301953         NA          NA user2           180 my_job_4 shared        2
#> 10 301954         NA          NA user2           440 my_job_5 shared        2
#> # ℹ 90 more rows
#> # ℹ 3 more variables: requested_mem_gb <dbl>, status <fct>,
#> #   wallclock_time <drtn>

The benefit to having this data in R, now, is to be able to trivially ask summarizing questions. First, “how much memory and how many CPUs am I currently using?” Knowing this answer can help ensure fair and civil use of shared computing resources, for example on a computing cluster.

job_info_df |>
    #   Or your username here
    filter(user == "user21") |>
    #   Get the number of CPUs requested and the memory requested in GB
    summarize(
        total_mem_req = sum(requested_mem_gb),
        total_cpus = sum(cpus)
    ) |>
    print()
#> # A tibble: 1 × 2
#>   total_mem_req total_cpus
#>           <dbl>      <int>
#> 1            50          2

Monitoring Partitions

Sometimes, it’s useful to know about the partitions as a whole rather than about specific jobs. partition_info() serves this purpose, and parses sinfo output into a tibble. We’ll load an example of the output from partition_info(partition = NULL, all_nodes = FALSE).

print(partition_df)
#> # A tibble: 5 × 7
#>   partition   free_cpus total_cpus prop_free_cpus free_mem_gb total_mem_gb
#>   <chr>           <int>      <int>          <dbl>       <dbl>        <dbl>
#> 1 partition_1        48         48          1            126.         128.
#> 2 partition_2       324        384          0.844       1050.        1643.
#> 3 partition_3        48         48          1            127.         128.
#> 4 partition_4       412       1024          0.402       2806.        4126.
#> 5 partition_5        76        128          0.594        519.        1000.
#> # ℹ 1 more variable: prop_free_mem_gb <dbl>

Since all_nodes was FALSE, there’s one row per partition, summarizing information across all nodes that compose each partition. Alternatively, set all_nodes to TRUE to yield one row per node.

With partition_df, let’s summarize how busy the cluster is as a whole, then rank partitions by amount of free memory.

#   Print the proportion of CPUs and memory available for the whole cluster
partition_df |>
    summarize(
        prop_free_cpus = sum(free_cpus) / sum(total_cpus),
        prop_free_mem_gb = sum(free_mem_gb) / sum(total_mem_gb)
    ) |>
    print()
#> # A tibble: 1 × 2
#>   prop_free_cpus prop_free_mem_gb
#>            <dbl>            <dbl>
#> 1          0.556            0.659

#   Now let's take the top 3 partitions by memory currently available
partition_df |>
    arrange(desc(free_mem_gb)) |>
    select(partition, free_mem_gb) |>
    slice_head(n = 3)
#> # A tibble: 3 × 2
#>   partition   free_mem_gb
#>   <chr>             <dbl>
#> 1 partition_4       2806.
#> 2 partition_2       1050.
#> 3 partition_5        519.

Analyzing Finished Jobs

The job_report() function returns in-depth information about a single queued, running, or finished job (including a single array job). It combines functionality from SLURM’s sstat and sacct to return a tibble for easy manipulation in R.

Suppose you have a workflow that operates as an array job, and you’d like to profile memory usage across the many tasks. Suppose we’ve done an initial trial, setting memory relatively high just to get the jobs running without issues. One use of job_report could be to determine a better memory request in a data-driven way– the better settings can then be run on the larger dataset after the initial test.

On an actual system with SLURM installed, you’d normally run something like job_df = job_report(slurm_job_id) for the slurm_job_id (character or integer) representing the small test. For convenience, we’ll start from the output of job_report as available in the slurmjobs package.

job_df <- readRDS(
    system.file("extdata", "job_report_df.rds", package = "slurmjobs")
)
print(job_df)
#> # A tibble: 10 × 12
#>    job_id user  name     partition  cpus requested_mem_gb max_rss_gb max_vmem_gb
#>     <int> <chr> <chr>    <fct>     <int>            <dbl>      <dbl>       <dbl>
#>  1 297332 user1 broken_… shared        2                5       0.04        0.04
#>  2 297333 user1 broken_… shared        2                5       0.48        0.48
#>  3 297334 user1 broken_… shared        2                5       0.61        0.61
#>  4 297335 user1 broken_… shared        2                5       0.04        0.04
#>  5 297336 user1 broken_… shared        2                5       1.15        1.15
#>  6 297337 user1 broken_… shared        2                5       1.38        1.38
#>  7 297338 user1 broken_… shared        2                5       0.04        0.04
#>  8 297339 user1 broken_… shared        2                5       0.04        0.04
#>  9 297340 user1 broken_… shared        2                5       0.04        0.04
#> 10 297331 user1 broken_… shared        2                5       1.16        1.16
#> # ℹ 4 more variables: array_task_id <int>, exit_code <int>,
#> #   wallclock_time <drtn>, status <fct>

Now let’s choose a better memory request:

stat_df <- job_df |>
    #   This example includes tasks that fail. We're only interested in memory
    #   for successfully completed tasks
    filter(status != "FAILED") |>
    summarize(
        mean_mem = mean(max_vmem_gb),
        std_mem = sd(max_vmem_gb),
        max_mem = max(max_vmem_gb)
    )

#   We could choose a new memory request as 3 standard deviations above the mean
#   of actual memory usage
new_limit <- stat_df$mean_mem + 3 * stat_df$std_mem

print(
    sprintf(
        "%.02fG is a better memory request than %.02fG, which was used before",
        new_limit,
        job_df$requested_mem_gb[1]
    )
)
#> [1] "2.12G is a better memory request than 5.00G, which was used before"

Reproducibility

The slurmjobs package (LieberInstitute, 2024) was made possible thanks to:

  • R (R Core Team, 2024)
  • BiocStyle (Oleś, 2023)
  • dplyr (Wickham, François, Henry, Müller, and Vaughan, 2023)
  • knitr (Xie, 2024)
  • RefManageR (McLean, 2017)
  • rmarkdown (Allaire, Xie, Dervieux, McPherson, Luraschi, Ushey, Atkins, Wickham, Cheng, Chang, and Iannone, 2024)
  • sessioninfo (Wickham, Chang, Flight, Müller, and Hester, 2021)
  • testthat (Wickham, 2011)

This package was developed using biocthis.

Code for creating the vignette

## Create the vignette
library("rmarkdown")
system.time(render("slurmjobs.Rmd", "BiocStyle::html_document"))

## Extract the R code
library("knitr")
knit("slurmjobs.Rmd", tangle = TRUE)

Date the vignette was generated.

#> [1] "2024-09-20 18:18:37 UTC"

Wallclock time spent generating the vignette.

#> Time difference of 2.008 secs

R session information.

#> ─ Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.3.3 (2024-02-29)
#>  os       Ubuntu 22.04.4 LTS
#>  system   x86_64, linux-gnu
#>  ui       X11
#>  language en
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       UTC
#>  date     2024-09-20
#>  pandoc   3.1.1 @ /usr/local/bin/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────
#>  package       * version date (UTC) lib source
#>  backports       1.4.1   2021-12-13 [1] RSPM (R 4.3.0)
#>  bibtex          0.5.1   2023-01-26 [1] RSPM (R 4.3.0)
#>  BiocManager     1.30.22 2023-08-08 [1] RSPM (R 4.3.0)
#>  BiocStyle     * 2.30.0  2023-10-24 [1] Bioconductor
#>  bookdown        0.39    2024-04-15 [1] RSPM (R 4.3.0)
#>  bslib           0.7.0   2024-03-29 [2] RSPM (R 4.3.0)
#>  cachem          1.0.8   2023-05-01 [2] RSPM (R 4.3.0)
#>  cli             3.6.2   2023-12-11 [2] RSPM (R 4.3.0)
#>  crayon          1.5.2   2022-09-29 [2] RSPM (R 4.3.0)
#>  desc            1.4.3   2023-12-10 [2] RSPM (R 4.3.0)
#>  digest          0.6.35  2024-03-11 [2] RSPM (R 4.3.0)
#>  dplyr         * 1.1.4   2023-11-17 [1] RSPM (R 4.3.0)
#>  evaluate        0.23    2023-11-01 [2] RSPM (R 4.3.0)
#>  fansi           1.0.6   2023-12-08 [2] RSPM (R 4.3.0)
#>  fastmap         1.1.1   2023-02-24 [2] RSPM (R 4.3.0)
#>  fs              1.6.3   2023-07-20 [2] RSPM (R 4.3.0)
#>  generics        0.1.3   2022-07-05 [1] RSPM (R 4.3.0)
#>  glue            1.7.0   2024-01-09 [2] RSPM (R 4.3.0)
#>  htmltools       0.5.8.1 2024-04-04 [2] RSPM (R 4.3.0)
#>  htmlwidgets     1.6.4   2023-12-06 [2] RSPM (R 4.3.0)
#>  httr            1.4.7   2023-08-15 [2] RSPM (R 4.3.0)
#>  jquerylib       0.1.4   2021-04-26 [2] RSPM (R 4.3.0)
#>  jsonlite        1.8.8   2023-12-04 [2] RSPM (R 4.3.0)
#>  knitcitations * 1.0.12  2021-01-10 [1] RSPM (R 4.3.0)
#>  knitr           1.46    2024-04-06 [2] RSPM (R 4.3.0)
#>  lifecycle       1.0.4   2023-11-07 [2] RSPM (R 4.3.0)
#>  lubridate       1.9.3   2023-09-27 [1] RSPM (R 4.3.0)
#>  magrittr        2.0.3   2022-03-30 [2] RSPM (R 4.3.0)
#>  memoise         2.0.1   2021-11-26 [2] RSPM (R 4.3.0)
#>  pillar          1.9.0   2023-03-22 [2] RSPM (R 4.3.0)
#>  pkgconfig       2.0.3   2019-09-22 [2] RSPM (R 4.3.0)
#>  pkgdown         2.0.9   2024-04-18 [2] RSPM (R 4.3.0)
#>  plyr            1.8.9   2023-10-02 [1] RSPM (R 4.3.0)
#>  purrr           1.0.2   2023-08-10 [2] RSPM (R 4.3.0)
#>  R6              2.5.1   2021-08-19 [2] RSPM (R 4.3.0)
#>  ragg            1.3.0   2024-03-13 [2] RSPM (R 4.3.0)
#>  Rcpp            1.0.12  2024-01-09 [2] RSPM (R 4.3.0)
#>  RefManageR    * 1.4.0   2022-09-30 [1] RSPM (R 4.3.0)
#>  rlang           1.1.3   2024-01-10 [2] RSPM (R 4.3.0)
#>  rmarkdown       2.26    2024-03-05 [2] RSPM (R 4.3.0)
#>  sass            0.4.9   2024-03-15 [2] RSPM (R 4.3.0)
#>  sessioninfo   * 1.2.2   2021-12-06 [2] RSPM (R 4.3.0)
#>  slurmjobs     * 1.2.5   2024-09-20 [1] local
#>  stringi         1.8.3   2023-12-11 [2] RSPM (R 4.3.0)
#>  stringr         1.5.1   2023-11-14 [2] RSPM (R 4.3.0)
#>  systemfonts     1.0.6   2024-03-07 [2] RSPM (R 4.3.0)
#>  textshaping     0.3.7   2023-10-09 [2] RSPM (R 4.3.0)
#>  tibble          3.2.1   2023-03-20 [2] RSPM (R 4.3.0)
#>  tidyselect      1.2.1   2024-03-11 [1] RSPM (R 4.3.0)
#>  timechange      0.3.0   2024-01-18 [1] RSPM (R 4.3.0)
#>  utf8            1.2.4   2023-10-22 [2] RSPM (R 4.3.0)
#>  vctrs           0.6.5   2023-12-01 [2] RSPM (R 4.3.0)
#>  withr           3.0.0   2024-01-16 [2] RSPM (R 4.3.0)
#>  xfun            0.43    2024-03-25 [2] RSPM (R 4.3.0)
#>  xml2            1.3.6   2023-12-04 [2] RSPM (R 4.3.0)
#>  yaml            2.3.8   2023-12-11 [2] RSPM (R 4.3.0)
#> 
#>  [1] /__w/_temp/Library
#>  [2] /usr/local/lib/R/site-library
#>  [3] /usr/local/lib/R/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Bibliography

This vignette was generated using BiocStyle (Oleś, 2023) with knitr (Xie, 2024) and rmarkdown (Allaire, Xie, Dervieux et al., 2024) running behind the scenes.

Citations made with RefManageR (McLean, 2017).

[1] J. Allaire, Y. Xie, C. Dervieux, et al. rmarkdown: Dynamic Documents for R. R package version 2.26. 2024. URL: https://github.com/rstudio/rmarkdown.

[2] LieberInstitute. slurmjobs: Helper Functions for SLURM Jobs. https://github.com/LieberInstitute/slurmjobs/slurmjobs - R package version 1.2.5. 2024. DOI: 10.18129/B9.bioc.slurmjobs. URL: http://www.bioconductor.org/packages/slurmjobs.

[3] M. W. McLean. “RefManageR: Import and Manage BibTeX and BibLaTeX References in R”. In: The Journal of Open Source Software (2017). DOI: 10.21105/joss.00338.

[4] A. Oleś. BiocStyle: Standard styles for vignettes and other Bioconductor documents. R package version 2.30.0. 2023. DOI: 10.18129/B9.bioc.BiocStyle. URL: https://bioconductor.org/packages/BiocStyle.

[5] R Core Team. R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing. Vienna, Austria, 2024. URL: https://www.R-project.org/.

[6] H. Wickham. “testthat: Get Started with Testing”. In: The R Journal 3 (2011), pp. 5–10. URL: https://journal.r-project.org/archive/2011-1/RJournal_2011-1_Wickham.pdf.

[7] H. Wickham, W. Chang, R. Flight, et al. sessioninfo: R Session Information. R package version 1.2.2, https://r-lib.github.io/sessioninfo/. 2021. URL: https://github.com/r-lib/sessioninfo#readme.

[8] H. Wickham, R. François, L. Henry, et al. dplyr: A Grammar of Data Manipulation. R package version 1.1.4, https://github.com/tidyverse/dplyr. 2023. URL: https://dplyr.tidyverse.org.

[9] Y. Xie. knitr: A General-Purpose Package for Dynamic Report Generation in R. R package version 1.46. 2024. URL: https://yihui.org/knitr/.